From ed959b50c6dd48d02112faa0ad2290947de89fff Mon Sep 17 00:00:00 2001 From: wes Date: Mon, 14 Nov 2016 18:37:05 -0500 Subject: [PATCH] switch to python3 --- src/archive.py | 8 +- src/goasearch.py | 14 +- src/mcmaster/classes.py | 25 +- src/mcmaster/spreadsheet.py | 2 +- src/mcmaster/sylla.py | 6 +- src/openlibrary.py | 6 +- src/predictions.py | 2 +- src/search.py | 12 +- src/textbookExceptions.py | 2 +- src/visualize.py | 4 +- src/website.py | 17 +- venv/bin/activate | 78 - venv/bin/activate.csh | 36 - venv/bin/activate.fish | 76 - venv/bin/activate_this.py | 34 - venv/bin/easy_install | 11 - venv/bin/easy_install-2.7 | 11 - venv/bin/flask | 11 - venv/bin/pip | 11 - venv/bin/pip2 | 11 - venv/bin/pip2.7 | 11 - venv/bin/python | 1 - venv/bin/python-config | 78 - venv/bin/python2 | Bin 6280 -> 0 bytes venv/bin/python2.7 | 1 - venv/bin/wheel | 11 - venv/include/python2.7 | 1 - venv/lib/python2.7/UserDict.py | 1 - venv/lib/python2.7/_abcoll.py | 1 - venv/lib/python2.7/_weakrefset.py | 1 - venv/lib/python2.7/abc.py | 1 - venv/lib/python2.7/codecs.py | 1 - venv/lib/python2.7/config | 1 - venv/lib/python2.7/copy_reg.py | 1 - venv/lib/python2.7/distutils/__init__.py | 101 - venv/lib/python2.7/distutils/distutils.cfg | 6 - venv/lib/python2.7/encodings | 1 - venv/lib/python2.7/fnmatch.py | 1 - venv/lib/python2.7/genericpath.py | 1 - venv/lib/python2.7/lib-dynload | 1 - venv/lib/python2.7/linecache.py | 1 - venv/lib/python2.7/locale.py | 1 - .../lib/python2.7/no-global-site-packages.txt | 0 venv/lib/python2.7/ntpath.py | 1 - venv/lib/python2.7/orig-prefix.txt | 1 - venv/lib/python2.7/os.py | 1 - venv/lib/python2.7/posixpath.py | 1 - venv/lib/python2.7/re.py | 1 - .../Flask-0.11.1.dist-info/DESCRIPTION.rst | 44 - .../Flask-0.11.1.dist-info/INSTALLER | 1 - .../Flask-0.11.1.dist-info/METADATA | 73 - .../Flask-0.11.1.dist-info/RECORD | 51 - .../Flask-0.11.1.dist-info/WHEEL | 6 - .../Flask-0.11.1.dist-info/entry_points.txt | 4 - .../Flask-0.11.1.dist-info/metadata.json | 1 - .../Flask-0.11.1.dist-info/top_level.txt | 1 - .../Jinja2-2.8.dist-info/DESCRIPTION.rst | 36 - .../Jinja2-2.8.dist-info/INSTALLER | 1 - .../Jinja2-2.8.dist-info/METADATA | 63 - .../site-packages/Jinja2-2.8.dist-info/RECORD | 54 - .../site-packages/Jinja2-2.8.dist-info/WHEEL | 6 - .../Jinja2-2.8.dist-info/entry_points.txt | 4 - .../Jinja2-2.8.dist-info/metadata.json | 1 - .../Jinja2-2.8.dist-info/top_level.txt | 1 - .../MarkupSafe-0.23.dist-info/DESCRIPTION.rst | 101 - .../MarkupSafe-0.23.dist-info/INSTALLER | 1 - .../MarkupSafe-0.23.dist-info/METADATA | 121 - .../MarkupSafe-0.23.dist-info/RECORD | 19 - .../MarkupSafe-0.23.dist-info/WHEEL | 5 - .../MarkupSafe-0.23.dist-info/metadata.json | 1 - .../MarkupSafe-0.23.dist-info/top_level.txt | 1 - .../DESCRIPTION.rst | 54 - .../Werkzeug-0.11.10.dist-info/INSTALLER | 1 - .../Werkzeug-0.11.10.dist-info/METADATA | 79 - .../Werkzeug-0.11.10.dist-info/RECORD | 94 - .../Werkzeug-0.11.10.dist-info/WHEEL | 6 - .../Werkzeug-0.11.10.dist-info/metadata.json | 1 - .../Werkzeug-0.11.10.dist-info/top_level.txt | 1 - .../click-6.6.dist-info/DESCRIPTION.rst | 3 - .../click-6.6.dist-info/INSTALLER | 1 - .../click-6.6.dist-info/METADATA | 16 - .../site-packages/click-6.6.dist-info/RECORD | 41 - .../site-packages/click-6.6.dist-info/WHEEL | 6 - .../click-6.6.dist-info/metadata.json | 1 - .../click-6.6.dist-info/top_level.txt | 1 - .../python2.7/site-packages/click/__init__.py | 98 - .../site-packages/click/_bashcomplete.py | 83 - .../python2.7/site-packages/click/_compat.py | 642 -- .../site-packages/click/_termui_impl.py | 547 -- .../site-packages/click/_textwrap.py | 38 - .../site-packages/click/_unicodefun.py | 119 - .../site-packages/click/_winconsole.py | 273 - .../lib/python2.7/site-packages/click/core.py | 1738 ----- .../site-packages/click/decorators.py | 304 - .../site-packages/click/exceptions.py | 201 - .../site-packages/click/formatting.py | 256 - .../python2.7/site-packages/click/globals.py | 48 - .../python2.7/site-packages/click/parser.py | 426 -- .../python2.7/site-packages/click/termui.py | 539 -- .../python2.7/site-packages/click/testing.py | 322 - .../python2.7/site-packages/click/types.py | 550 -- .../python2.7/site-packages/click/utils.py | 415 -- .../site-packages/dateutil/__init__.py | 2 - .../site-packages/dateutil/easter.py | 89 - .../site-packages/dateutil/parser.py | 1356 ---- .../site-packages/dateutil/relativedelta.py | 530 -- .../python2.7/site-packages/dateutil/rrule.py | 1612 ----- .../site-packages/dateutil/tz/__init__.py | 4 - .../site-packages/dateutil/tz/_common.py | 18 - .../python2.7/site-packages/dateutil/tz/tz.py | 979 --- .../site-packages/dateutil/tz/win.py | 330 - .../python2.7/site-packages/dateutil/tzwin.py | 2 - .../dateutil/zoneinfo/__init__.py | 101 - .../zoneinfo/dateutil-zoneinfo.tar.gz | Bin 156039 -> 0 bytes .../dateutil/zoneinfo/rebuild.py | 51 - .../python2.7/site-packages/easy_install.py | 5 - .../DESCRIPTION.rst | 130 - .../elasticsearch-2.3.0.dist-info/INSTALLER | 1 - .../elasticsearch-2.3.0.dist-info/METADATA | 155 - .../elasticsearch-2.3.0.dist-info/RECORD | 48 - .../elasticsearch-2.3.0.dist-info/WHEEL | 6 - .../metadata.json | 1 - .../elasticsearch-2.3.0.dist-info/pbr.json | 1 - .../top_level.txt | 1 - .../site-packages/elasticsearch/__init__.py | 25 - .../elasticsearch/client/__init__.py | 1279 ---- .../site-packages/elasticsearch/client/cat.py | 346 - .../elasticsearch/client/cluster.py | 154 - .../elasticsearch/client/indices.py | 998 --- .../elasticsearch/client/nodes.py | 91 - .../elasticsearch/client/snapshot.py | 169 - .../elasticsearch/client/utils.py | 87 - .../site-packages/elasticsearch/compat.py | 13 - .../elasticsearch/connection/__init__.py | 3 - .../elasticsearch/connection/base.py | 110 - .../elasticsearch/connection/http_requests.py | 88 - .../elasticsearch/connection/http_urllib3.py | 115 - .../elasticsearch/connection/pooling.py | 26 - .../elasticsearch/connection_pool.py | 248 - .../site-packages/elasticsearch/exceptions.py | 109 - .../elasticsearch/helpers/__init__.py | 355 -- .../elasticsearch/helpers/test.py | 60 - .../site-packages/elasticsearch/serializer.py | 77 - .../site-packages/elasticsearch/transport.py | 355 -- .../DESCRIPTION.rst | 238 - .../INSTALLER | 1 - .../METADATA | 265 - .../elasticsearch_dsl-2.0.0.dist-info/RECORD | 42 - .../elasticsearch_dsl-2.0.0.dist-info/WHEEL | 6 - .../metadata.json | 1 - .../pbr.json | 1 - .../top_level.txt | 1 - .../elasticsearch_dsl/__init__.py | 14 - .../site-packages/elasticsearch_dsl/aggs.py | 176 - .../elasticsearch_dsl/analysis.py | 121 - .../elasticsearch_dsl/connections.py | 96 - .../elasticsearch_dsl/document.py | 264 - .../elasticsearch_dsl/exceptions.py | 14 - .../elasticsearch_dsl/faceted_search.py | 284 - .../site-packages/elasticsearch_dsl/field.py | 256 - .../site-packages/elasticsearch_dsl/filter.py | 180 - .../elasticsearch_dsl/function.py | 86 - .../site-packages/elasticsearch_dsl/index.py | 91 - .../elasticsearch_dsl/mapping.py | 151 - .../site-packages/elasticsearch_dsl/query.py | 233 - .../site-packages/elasticsearch_dsl/result.py | 82 - .../site-packages/elasticsearch_dsl/search.py | 676 -- .../elasticsearch_dsl/serializer.py | 13 - .../site-packages/elasticsearch_dsl/utils.py | 396 -- .../python2.7/site-packages/flask/__init__.py | 49 - .../python2.7/site-packages/flask/__main__.py | 15 - .../python2.7/site-packages/flask/_compat.py | 88 - venv/lib/python2.7/site-packages/flask/app.py | 2006 ------ .../site-packages/flask/blueprints.py | 413 -- venv/lib/python2.7/site-packages/flask/cli.py | 482 -- .../python2.7/site-packages/flask/config.py | 258 - venv/lib/python2.7/site-packages/flask/ctx.py | 410 -- .../site-packages/flask/debughelpers.py | 155 - .../site-packages/flask/ext/__init__.py | 29 - .../python2.7/site-packages/flask/exthook.py | 143 - .../python2.7/site-packages/flask/globals.py | 61 - .../python2.7/site-packages/flask/helpers.py | 930 --- .../lib/python2.7/site-packages/flask/json.py | 272 - .../python2.7/site-packages/flask/logging.py | 90 - .../python2.7/site-packages/flask/sessions.py | 362 -- .../python2.7/site-packages/flask/signals.py | 56 - .../site-packages/flask/templating.py | 149 - .../python2.7/site-packages/flask/testing.py | 129 - .../python2.7/site-packages/flask/views.py | 149 - .../python2.7/site-packages/flask/wrappers.py | 204 - .../DESCRIPTION.rst | 3 - .../itsdangerous-0.24.dist-info/INSTALLER | 1 - .../itsdangerous-0.24.dist-info/METADATA | 16 - .../itsdangerous-0.24.dist-info/RECORD | 9 - .../itsdangerous-0.24.dist-info/WHEEL | 5 - .../itsdangerous-0.24.dist-info/metadata.json | 1 - .../itsdangerous-0.24.dist-info/top_level.txt | 1 - .../python2.7/site-packages/itsdangerous.py | 872 --- .../site-packages/jinja2/__init__.py | 70 - .../python2.7/site-packages/jinja2/_compat.py | 111 - .../site-packages/jinja2/_stringdefs.py | 132 - .../python2.7/site-packages/jinja2/bccache.py | 362 -- .../site-packages/jinja2/compiler.py | 1686 ----- .../site-packages/jinja2/constants.py | 32 - .../python2.7/site-packages/jinja2/debug.py | 350 - .../site-packages/jinja2/defaults.py | 43 - .../site-packages/jinja2/environment.py | 1213 ---- .../site-packages/jinja2/exceptions.py | 146 - .../lib/python2.7/site-packages/jinja2/ext.py | 636 -- .../python2.7/site-packages/jinja2/filters.py | 996 --- .../python2.7/site-packages/jinja2/lexer.py | 734 --- .../python2.7/site-packages/jinja2/loaders.py | 481 -- .../python2.7/site-packages/jinja2/meta.py | 103 - .../python2.7/site-packages/jinja2/nodes.py | 919 --- .../site-packages/jinja2/optimizer.py | 68 - .../python2.7/site-packages/jinja2/parser.py | 899 --- .../python2.7/site-packages/jinja2/runtime.py | 667 -- .../python2.7/site-packages/jinja2/sandbox.py | 367 -- .../python2.7/site-packages/jinja2/tests.py | 173 - .../python2.7/site-packages/jinja2/utils.py | 531 -- .../python2.7/site-packages/jinja2/visitor.py | 87 - .../lxml-3.6.0.dist-info/DESCRIPTION.rst | 51 - .../lxml-3.6.0.dist-info/INSTALLER | 1 - .../lxml-3.6.0.dist-info/METADATA | 87 - .../site-packages/lxml-3.6.0.dist-info/RECORD | 85 - .../site-packages/lxml-3.6.0.dist-info/WHEEL | 5 - .../lxml-3.6.0.dist-info/metadata.json | 1 - .../lxml-3.6.0.dist-info/top_level.txt | 1 - .../site-packages/lxml/ElementInclude.py | 223 - .../python2.7/site-packages/lxml/__init__.py | 20 - .../site-packages/lxml/_elementpath.py | 315 - .../python2.7/site-packages/lxml/builder.py | 246 - .../python2.7/site-packages/lxml/cssselect.py | 102 - .../site-packages/lxml/doctestcompare.py | 508 -- .../lib/python2.7/site-packages/lxml/etree.so | Bin 2020984 -> 0 bytes .../site-packages/lxml/html/ElementSoup.py | 10 - .../site-packages/lxml/html/__init__.py | 1923 ------ .../site-packages/lxml/html/_diffcommand.py | 87 - .../site-packages/lxml/html/_html5builder.py | 100 - .../site-packages/lxml/html/_setmixin.py | 52 - .../site-packages/lxml/html/builder.py | 133 - .../site-packages/lxml/html/clean.py | 732 --- .../python2.7/site-packages/lxml/html/defs.py | 137 - .../python2.7/site-packages/lxml/html/diff.py | 881 --- .../site-packages/lxml/html/formfill.py | 299 - .../site-packages/lxml/html/html5parser.py | 207 - .../site-packages/lxml/html/soupparser.py | 310 - .../site-packages/lxml/html/usedoctest.py | 13 - .../site-packages/lxml/includes/__init__.py | 0 .../site-packages/lxml/includes/c14n.pxd | 26 - .../site-packages/lxml/includes/config.pxd | 3 - .../site-packages/lxml/includes/dtdvalid.pxd | 18 - .../site-packages/lxml/includes/etree_defs.h | 373 -- .../lxml/includes/etreepublic.pxd | 234 - .../lxml/includes/htmlparser.pxd | 56 - .../lxml/includes/lxml-version.h | 3 - .../site-packages/lxml/includes/relaxng.pxd | 64 - .../lxml/includes/schematron.pxd | 34 - .../site-packages/lxml/includes/tree.pxd | 474 -- .../site-packages/lxml/includes/uri.pxd | 5 - .../site-packages/lxml/includes/xinclude.pxd | 22 - .../site-packages/lxml/includes/xmlerror.pxd | 850 --- .../site-packages/lxml/includes/xmlparser.pxd | 248 - .../site-packages/lxml/includes/xmlschema.pxd | 35 - .../site-packages/lxml/includes/xpath.pxd | 135 - .../site-packages/lxml/includes/xslt.pxd | 176 - .../lxml/isoschematron/__init__.py | 334 - .../resources/rng/iso-schematron.rng | 622 -- .../resources/xsl/RNG2Schtrn.xsl | 75 - .../resources/xsl/XSD2Schtrn.xsl | 77 - .../iso_abstract_expand.xsl | 296 - .../iso-schematron-xslt1/iso_dsdl_include.xsl | 1160 ---- .../iso_schematron_message.xsl | 55 - .../iso_schematron_skeleton_for_xslt1.xsl | 1796 ------ .../iso_svrl_for_xslt1.xsl | 588 -- .../xsl/iso-schematron-xslt1/readme.txt | 83 - .../python2.7/site-packages/lxml/lxml.etree.h | 219 - .../site-packages/lxml/lxml.etree_api.h | 230 - .../python2.7/site-packages/lxml/objectify.so | Bin 433736 -> 0 bytes .../site-packages/lxml/pyclasslookup.py | 3 - venv/lib/python2.7/site-packages/lxml/sax.py | 248 - .../site-packages/lxml/usedoctest.py | 13 - .../site-packages/markupsafe/__init__.py | 298 - .../site-packages/markupsafe/_compat.py | 26 - .../site-packages/markupsafe/_constants.py | 267 - .../site-packages/markupsafe/_native.py | 46 - .../site-packages/markupsafe/_speedups.c | 239 - .../site-packages/markupsafe/_speedups.so | Bin 13344 -> 0 bytes .../site-packages/markupsafe/tests.py | 179 - .../pip-8.1.2.dist-info/DESCRIPTION.rst | 36 - .../pip-8.1.2.dist-info/INSTALLER | 1 - .../pip-8.1.2.dist-info/METADATA | 65 - .../site-packages/pip-8.1.2.dist-info/RECORD | 483 -- .../site-packages/pip-8.1.2.dist-info/WHEEL | 6 - .../pip-8.1.2.dist-info/entry_points.txt | 5 - .../pip-8.1.2.dist-info/metadata.json | 1 - .../pip-8.1.2.dist-info/top_level.txt | 1 - .../python2.7/site-packages/pip/__init__.py | 319 - .../python2.7/site-packages/pip/__main__.py | 19 - .../site-packages/pip/_vendor/__init__.py | 106 - .../pip/_vendor/cachecontrol/__init__.py | 11 - .../pip/_vendor/cachecontrol/_cmd.py | 60 - .../pip/_vendor/cachecontrol/adapter.py | 117 - .../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 | 63 - .../pip/_vendor/cachecontrol/heuristics.py | 138 - .../pip/_vendor/cachecontrol/serialize.py | 190 - .../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 | 1102 ---- .../pip/_vendor/distlib/database.py | 1312 ---- .../pip/_vendor/distlib/index.py | 513 -- .../pip/_vendor/distlib/locators.py | 1264 ---- .../pip/_vendor/distlib/manifest.py | 367 -- .../pip/_vendor/distlib/markers.py | 190 - .../pip/_vendor/distlib/metadata.py | 1066 ---- .../pip/_vendor/distlib/resources.py | 350 - .../pip/_vendor/distlib/scripts.py | 384 -- .../site-packages/pip/_vendor/distlib/t32.exe | Bin 89088 -> 0 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 97792 -> 0 bytes .../site-packages/pip/_vendor/distlib/util.py | 1597 ----- .../pip/_vendor/distlib/version.py | 742 --- .../site-packages/pip/_vendor/distlib/w32.exe | Bin 85504 -> 0 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 94208 -> 0 bytes .../pip/_vendor/distlib/wheel.py | 978 --- .../pip/_vendor/html5lib/__init__.py | 25 - .../pip/_vendor/html5lib/constants.py | 3102 --------- .../pip/_vendor/html5lib/filters/__init__.py | 0 .../pip/_vendor/html5lib/filters/_base.py | 12 - .../filters/alphabeticalattributes.py | 20 - .../html5lib/filters/inject_meta_charset.py | 65 - .../pip/_vendor/html5lib/filters/lint.py | 90 - .../_vendor/html5lib/filters/optionaltags.py | 205 - .../pip/_vendor/html5lib/filters/sanitizer.py | 12 - .../_vendor/html5lib/filters/whitespace.py | 38 - .../pip/_vendor/html5lib/html5parser.py | 2724 -------- .../pip/_vendor/html5lib/ihatexml.py | 285 - .../pip/_vendor/html5lib/inputstream.py | 903 --- .../pip/_vendor/html5lib/sanitizer.py | 300 - .../_vendor/html5lib/serializer/__init__.py | 16 - .../html5lib/serializer/htmlserializer.py | 317 - .../pip/_vendor/html5lib/tokenizer.py | 1731 ----- .../_vendor/html5lib/treeadapters/__init__.py | 0 .../pip/_vendor/html5lib/treeadapters/sax.py | 44 - .../_vendor/html5lib/treebuilders/__init__.py | 76 - .../_vendor/html5lib/treebuilders/_base.py | 377 -- .../pip/_vendor/html5lib/treebuilders/dom.py | 227 - .../_vendor/html5lib/treebuilders/etree.py | 337 - .../html5lib/treebuilders/etree_lxml.py | 369 -- .../_vendor/html5lib/treewalkers/__init__.py | 147 - .../pip/_vendor/html5lib/treewalkers/_base.py | 200 - .../pip/_vendor/html5lib/treewalkers/dom.py | 43 - .../pip/_vendor/html5lib/treewalkers/etree.py | 136 - .../html5lib/treewalkers/genshistream.py | 69 - .../_vendor/html5lib/treewalkers/lxmletree.py | 201 - .../_vendor/html5lib/treewalkers/pulldom.py | 63 - .../pip/_vendor/html5lib/trie/__init__.py | 12 - .../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 | 103 - .../site-packages/pip/_vendor/ipaddress.py | 2417 ------- .../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/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 | 289 - .../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 | 2956 --------- .../pip/_vendor/progress/__init__.py | 123 - .../site-packages/pip/_vendor/progress/bar.py | 83 - .../pip/_vendor/progress/counter.py | 47 - .../pip/_vendor/progress/helpers.py | 91 - .../pip/_vendor/progress/spinner.py | 40 - .../site-packages/pip/_vendor/pyparsing.py | 3837 ----------- .../site-packages/pip/_vendor/re-vendor.py | 34 - .../pip/_vendor/requests/__init__.py | 89 - .../pip/_vendor/requests/adapters.py | 483 -- .../site-packages/pip/_vendor/requests/api.py | 149 - .../pip/_vendor/requests/auth.py | 242 - .../pip/_vendor/requests/cacert.pem | 5616 ----------------- .../pip/_vendor/requests/certs.py | 25 - .../pip/_vendor/requests/compat.py | 62 - .../pip/_vendor/requests/cookies.py | 493 -- .../pip/_vendor/requests/exceptions.py | 114 - .../pip/_vendor/requests/hooks.py | 34 - .../pip/_vendor/requests/models.py | 855 --- .../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 | 849 --- .../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 | 385 -- .../packages/ssl_match_hostname/__init__.py | 13 - .../ssl_match_hostname/_implementation.py | 105 - .../requests/packages/urllib3/poolmanager.py | 284 - .../requests/packages/urllib3/request.py | 151 - .../requests/packages/urllib3/response.py | 526 -- .../packages/urllib3/util/__init__.py | 46 - .../packages/urllib3/util/connection.py | 101 - .../requests/packages/urllib3/util/request.py | 72 - .../packages/urllib3/util/response.py | 74 - .../requests/packages/urllib3/util/retry.py | 294 - .../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 | 689 -- .../pip/_vendor/requests/status_codes.py | 91 - .../pip/_vendor/requests/structures.py | 106 - .../pip/_vendor/requests/utils.py | 728 --- .../site-packages/pip/_vendor/retrying.py | 267 - .../site-packages/pip/_vendor/six.py | 868 --- .../site-packages/pip/basecommand.py | 331 - .../python2.7/site-packages/pip/baseparser.py | 292 - .../python2.7/site-packages/pip/cmdoptions.py | 618 -- .../site-packages/pip/commands/__init__.py | 83 - .../site-packages/pip/commands/completion.py | 67 - .../site-packages/pip/commands/download.py | 136 - .../site-packages/pip/commands/freeze.py | 86 - .../site-packages/pip/commands/hash.py | 57 - .../site-packages/pip/commands/help.py | 35 - .../site-packages/pip/commands/install.py | 386 -- .../site-packages/pip/commands/list.py | 209 - .../site-packages/pip/commands/search.py | 145 - .../site-packages/pip/commands/show.py | 154 - .../site-packages/pip/commands/uninstall.py | 76 - .../site-packages/pip/commands/wheel.py | 204 - .../site-packages/pip/compat/__init__.py | 164 - .../site-packages/pip/compat/dictconfig.py | 565 -- .../site-packages/pip/compat/ordereddict.py | 129 - .../python2.7/site-packages/pip/download.py | 895 --- .../python2.7/site-packages/pip/exceptions.py | 239 - venv/lib/python2.7/site-packages/pip/index.py | 1048 --- .../python2.7/site-packages/pip/locations.py | 182 - .../site-packages/pip/models/__init__.py | 4 - .../site-packages/pip/models/index.py | 16 - .../site-packages/pip/operations/__init__.py | 0 .../site-packages/pip/operations/freeze.py | 114 - .../python2.7/site-packages/pip/pep425tags.py | 349 - .../site-packages/pip/req/__init__.py | 10 - .../site-packages/pip/req/req_file.py | 342 - .../site-packages/pip/req/req_install.py | 1183 ---- .../site-packages/pip/req/req_set.py | 756 --- .../site-packages/pip/req/req_uninstall.py | 195 - .../site-packages/pip/status_codes.py | 8 - .../site-packages/pip/utils/__init__.py | 860 --- .../site-packages/pip/utils/appdirs.py | 224 - .../site-packages/pip/utils/build.py | 42 - .../site-packages/pip/utils/deprecation.py | 76 - .../site-packages/pip/utils/encoding.py | 31 - .../site-packages/pip/utils/filesystem.py | 28 - .../site-packages/pip/utils/hashes.py | 92 - .../site-packages/pip/utils/logging.py | 130 - .../site-packages/pip/utils/outdated.py | 162 - .../pip/utils/setuptools_build.py | 6 - .../python2.7/site-packages/pip/utils/ui.py | 344 - .../site-packages/pip/vcs/__init__.py | 363 -- .../python2.7/site-packages/pip/vcs/bazaar.py | 116 - .../python2.7/site-packages/pip/vcs/git.py | 277 - .../site-packages/pip/vcs/mercurial.py | 103 - .../site-packages/pip/vcs/subversion.py | 249 - venv/lib/python2.7/site-packages/pip/wheel.py | 857 --- .../site-packages/pkg_resources/__init__.py | 2956 --------- .../pkg_resources/_vendor/__init__.py | 0 .../_vendor/packaging/__about__.py | 21 - .../_vendor/packaging/__init__.py | 14 - .../_vendor/packaging/_compat.py | 30 - .../_vendor/packaging/_structures.py | 68 - .../_vendor/packaging/markers.py | 287 - .../_vendor/packaging/requirements.py | 127 - .../_vendor/packaging/specifiers.py | 774 --- .../pkg_resources/_vendor/packaging/utils.py | 14 - .../_vendor/packaging/version.py | 393 -- .../pkg_resources/_vendor/pyparsing.py | 3805 ----------- .../pkg_resources/_vendor/six.py | 868 --- .../pkg_resources/extern/__init__.py | 71 - .../DESCRIPTION.rst | 5 - .../python_dateutil-2.5.3.dist-info/INSTALLER | 1 - .../python_dateutil-2.5.3.dist-info/METADATA | 30 - .../python_dateutil-2.5.3.dist-info/RECORD | 33 - .../python_dateutil-2.5.3.dist-info/WHEEL | 6 - .../metadata.json | 1 - .../top_level.txt | 1 - .../python_dateutil-2.5.3.dist-info/zip-safe | 1 - .../requests-2.10.0.dist-info/DESCRIPTION.rst | 1257 ---- .../requests-2.10.0.dist-info/INSTALLER | 1 - .../requests-2.10.0.dist-info/METADATA | 1286 ---- .../requests-2.10.0.dist-info/RECORD | 170 - .../requests-2.10.0.dist-info/WHEEL | 6 - .../requests-2.10.0.dist-info/metadata.json | 1 - .../requests-2.10.0.dist-info/top_level.txt | 1 - .../site-packages/requests/__init__.py | 89 - .../site-packages/requests/adapters.py | 483 -- .../python2.7/site-packages/requests/api.py | 149 - .../python2.7/site-packages/requests/auth.py | 242 - .../site-packages/requests/cacert.pem | 5616 ----------------- .../python2.7/site-packages/requests/certs.py | 25 - .../site-packages/requests/compat.py | 62 - .../site-packages/requests/cookies.py | 493 -- .../site-packages/requests/exceptions.py | 114 - .../python2.7/site-packages/requests/hooks.py | 34 - .../site-packages/requests/models.py | 855 --- .../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 | 849 --- .../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 | 385 -- .../packages/ssl_match_hostname/__init__.py | 13 - .../ssl_match_hostname/_implementation.py | 105 - .../requests/packages/urllib3/poolmanager.py | 284 - .../requests/packages/urllib3/request.py | 151 - .../requests/packages/urllib3/response.py | 526 -- .../packages/urllib3/util/__init__.py | 46 - .../packages/urllib3/util/connection.py | 101 - .../requests/packages/urllib3/util/request.py | 72 - .../packages/urllib3/util/response.py | 74 - .../requests/packages/urllib3/util/retry.py | 294 - .../requests/packages/urllib3/util/ssl_.py | 320 - .../requests/packages/urllib3/util/timeout.py | 242 - .../requests/packages/urllib3/util/url.py | 217 - .../site-packages/requests/sessions.py | 689 -- .../site-packages/requests/status_codes.py | 91 - .../site-packages/requests/structures.py | 106 - .../python2.7/site-packages/requests/utils.py | 728 --- .../DESCRIPTION.rst | 239 - .../setuptools-23.0.0.dist-info/INSTALLER | 1 - .../setuptools-23.0.0.dist-info/METADATA | 268 - .../setuptools-23.0.0.dist-info/RECORD | 135 - .../setuptools-23.0.0.dist-info/WHEEL | 6 - .../dependency_links.txt | 2 - .../entry_points.txt | 62 - .../setuptools-23.0.0.dist-info/metadata.json | 1 - .../setuptools-23.0.0.dist-info/top_level.txt | 3 - .../setuptools-23.0.0.dist-info/zip-safe | 1 - .../site-packages/setuptools/__init__.py | 169 - .../site-packages/setuptools/archive_util.py | 170 - .../site-packages/setuptools/cli-32.exe | Bin 65536 -> 0 bytes .../site-packages/setuptools/cli-64.exe | Bin 74752 -> 0 bytes .../site-packages/setuptools/cli.exe | Bin 65536 -> 0 bytes .../setuptools/command/__init__.py | 18 - .../site-packages/setuptools/command/alias.py | 80 - .../setuptools/command/bdist_egg.py | 471 -- .../setuptools/command/bdist_rpm.py | 43 - .../setuptools/command/bdist_wininst.py | 21 - .../setuptools/command/build_ext.py | 300 - .../setuptools/command/build_py.py | 269 - .../setuptools/command/develop.py | 196 - .../setuptools/command/easy_install.py | 2313 ------- .../setuptools/command/egg_info.py | 489 -- .../setuptools/command/install.py | 125 - .../setuptools/command/install_egg_info.py | 118 - .../setuptools/command/install_lib.py | 120 - .../setuptools/command/install_scripts.py | 60 - .../setuptools/command/launcher manifest.xml | 15 - .../setuptools/command/register.py | 10 - .../setuptools/command/rotate.py | 66 - .../setuptools/command/saveopts.py | 22 - .../site-packages/setuptools/command/sdist.py | 196 - .../setuptools/command/setopt.py | 150 - .../site-packages/setuptools/command/test.py | 207 - .../setuptools/command/upload.py | 38 - .../setuptools/command/upload_docs.py | 201 - .../site-packages/setuptools/depends.py | 217 - .../site-packages/setuptools/dist.py | 872 --- .../site-packages/setuptools/extension.py | 57 - .../setuptools/extern/__init__.py | 5 - .../site-packages/setuptools/gui-32.exe | Bin 65536 -> 0 bytes .../site-packages/setuptools/gui-64.exe | Bin 75264 -> 0 bytes .../site-packages/setuptools/gui.exe | Bin 65536 -> 0 bytes .../site-packages/setuptools/launch.py | 35 - .../site-packages/setuptools/lib2to3_ex.py | 58 - .../site-packages/setuptools/msvc9_support.py | 63 - .../site-packages/setuptools/package_index.py | 1072 ---- .../site-packages/setuptools/py26compat.py | 22 - .../site-packages/setuptools/py27compat.py | 15 - .../site-packages/setuptools/py31compat.py | 52 - .../site-packages/setuptools/sandbox.py | 496 -- .../setuptools/script (dev).tmpl | 5 - .../site-packages/setuptools/script.tmpl | 3 - .../site-packages/setuptools/site-patch.py | 76 - .../site-packages/setuptools/ssl_support.py | 243 - .../site-packages/setuptools/unicode_utils.py | 43 - .../site-packages/setuptools/utils.py | 11 - .../site-packages/setuptools/version.py | 6 - .../setuptools/windows_support.py | 29 - .../six-1.10.0.dist-info/DESCRIPTION.rst | 18 - .../six-1.10.0.dist-info/INSTALLER | 1 - .../six-1.10.0.dist-info/METADATA | 34 - .../site-packages/six-1.10.0.dist-info/RECORD | 9 - .../site-packages/six-1.10.0.dist-info/WHEEL | 6 - .../six-1.10.0.dist-info/metadata.json | 1 - .../six-1.10.0.dist-info/top_level.txt | 1 - venv/lib/python2.7/site-packages/six.py | 868 --- .../urllib3-1.16.dist-info/DESCRIPTION.rst | 863 --- .../urllib3-1.16.dist-info/INSTALLER | 1 - .../urllib3-1.16.dist-info/METADATA | 890 --- .../urllib3-1.16.dist-info/RECORD | 68 - .../urllib3-1.16.dist-info/WHEEL | 6 - .../urllib3-1.16.dist-info/metadata.json | 1 - .../urllib3-1.16.dist-info/pbr.json | 1 - .../urllib3-1.16.dist-info/top_level.txt | 1 - .../site-packages/urllib3/__init__.py | 96 - .../site-packages/urllib3/_collections.py | 324 - .../site-packages/urllib3/connection.py | 330 - .../site-packages/urllib3/connectionpool.py | 866 --- .../site-packages/urllib3/contrib/__init__.py | 0 .../urllib3/contrib/appengine.py | 231 - .../site-packages/urllib3/contrib/ntlmpool.py | 115 - .../urllib3/contrib/pyopenssl.py | 358 -- .../site-packages/urllib3/contrib/socks.py | 172 - .../site-packages/urllib3/exceptions.py | 209 - .../python2.7/site-packages/urllib3/fields.py | 178 - .../site-packages/urllib3/filepost.py | 94 - .../urllib3/packages/__init__.py | 5 - .../urllib3/packages/backports/__init__.py | 0 .../urllib3/packages/backports/makefile.py | 53 - .../urllib3/packages/ordered_dict.py | 259 - .../site-packages/urllib3/packages/six.py | 868 --- .../packages/ssl_match_hostname/__init__.py | 13 - .../ssl_match_hostname/_implementation.py | 105 - .../site-packages/urllib3/poolmanager.py | 367 -- .../site-packages/urllib3/request.py | 151 - .../site-packages/urllib3/response.py | 530 -- .../site-packages/urllib3/util/__init__.py | 46 - .../site-packages/urllib3/util/connection.py | 144 - .../site-packages/urllib3/util/request.py | 72 - .../site-packages/urllib3/util/response.py | 74 - .../site-packages/urllib3/util/retry.py | 300 - .../site-packages/urllib3/util/ssl_.py | 320 - .../site-packages/urllib3/util/timeout.py | 242 - .../site-packages/urllib3/util/url.py | 217 - .../site-packages/werkzeug/__init__.py | 152 - .../site-packages/werkzeug/_compat.py | 206 - .../site-packages/werkzeug/_internal.py | 414 -- .../site-packages/werkzeug/_reloader.py | 252 - .../werkzeug/contrib/__init__.py | 16 - .../site-packages/werkzeug/contrib/atom.py | 355 -- .../site-packages/werkzeug/contrib/cache.py | 795 --- .../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 | 339 - .../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 | 2690 -------- .../site-packages/werkzeug/debug/__init__.py | 466 -- .../site-packages/werkzeug/debug/console.py | 215 - .../site-packages/werkzeug/debug/repr.py | 281 - .../werkzeug/debug/shared/FONT_LICENSE | 96 - .../werkzeug/debug/shared/console.png | Bin 507 -> 0 bytes .../werkzeug/debug/shared/debugger.js | 205 - .../werkzeug/debug/shared/jquery.js | 5 - .../werkzeug/debug/shared/less.png | Bin 191 -> 0 bytes .../werkzeug/debug/shared/more.png | Bin 200 -> 0 bytes .../werkzeug/debug/shared/source.png | Bin 818 -> 0 bytes .../werkzeug/debug/shared/style.css | 142 - .../werkzeug/debug/shared/ubuntu.ttf | Bin 70220 -> 0 bytes .../site-packages/werkzeug/debug/tbtools.py | 556 -- .../site-packages/werkzeug/exceptions.py | 658 -- .../site-packages/werkzeug/filesystem.py | 66 - .../site-packages/werkzeug/formparser.py | 526 -- .../python2.7/site-packages/werkzeug/http.py | 1023 --- .../python2.7/site-packages/werkzeug/local.py | 416 -- .../site-packages/werkzeug/posixemulation.py | 106 - .../site-packages/werkzeug/routing.py | 1774 ------ .../site-packages/werkzeug/script.py | 318 - .../site-packages/werkzeug/security.py | 264 - .../site-packages/werkzeug/serving.py | 742 --- .../python2.7/site-packages/werkzeug/test.py | 896 --- .../site-packages/werkzeug/testapp.py | 230 - .../python2.7/site-packages/werkzeug/urls.py | 1004 --- .../site-packages/werkzeug/useragents.py | 195 - .../python2.7/site-packages/werkzeug/utils.py | 628 -- .../site-packages/werkzeug/wrappers.py | 1848 ------ .../python2.7/site-packages/werkzeug/wsgi.py | 1096 ---- .../wheel-0.29.0.dist-info/DESCRIPTION.rst | 325 - .../wheel-0.29.0.dist-info/INSTALLER | 1 - .../wheel-0.29.0.dist-info/LICENSE.txt | 22 - .../wheel-0.29.0.dist-info/METADATA | 357 -- .../wheel-0.29.0.dist-info/RECORD | 82 - .../wheel-0.29.0.dist-info/WHEEL | 6 - .../wheel-0.29.0.dist-info/entry_points.txt | 5 - .../wheel-0.29.0.dist-info/metadata.json | 1 - .../wheel-0.29.0.dist-info/top_level.txt | 1 - .../python2.7/site-packages/wheel/__init__.py | 2 - .../python2.7/site-packages/wheel/__main__.py | 17 - .../python2.7/site-packages/wheel/archive.py | 80 - .../site-packages/wheel/bdist_wheel.py | 453 -- .../site-packages/wheel/decorator.py | 19 - .../site-packages/wheel/egg2wheel.py | 73 - .../site-packages/wheel/eggnames.txt | 87 - .../python2.7/site-packages/wheel/install.py | 480 -- .../python2.7/site-packages/wheel/metadata.py | 317 - .../python2.7/site-packages/wheel/paths.py | 41 - .../site-packages/wheel/pep425tags.py | 169 - .../python2.7/site-packages/wheel/pkginfo.py | 44 - .../wheel/signatures/__init__.py | 106 - .../site-packages/wheel/signatures/djbec.py | 270 - .../wheel/signatures/ed25519py.py | 52 - .../site-packages/wheel/signatures/keys.py | 99 - .../site-packages/wheel/test/__init__.py | 1 - .../test/complex-dist/complexdist/__init__.py | 2 - .../wheel/test/complex-dist/setup.py | 30 - .../wheel/test/headers.dist/header.h | 0 .../wheel/test/headers.dist/headersdist.py | 0 .../wheel/test/headers.dist/setup.py | 16 - .../wheel/test/pydist-schema.json | 362 -- .../wheel/test/simple.dist/setup.py | 17 - .../test/simple.dist/simpledist/__init__.py | 0 .../test/test-1.0-py2.py3-none-win32.whl | Bin 5226 -> 0 bytes .../site-packages/wheel/test/test_basic.py | 176 - .../site-packages/wheel/test/test_install.py | 55 - .../site-packages/wheel/test/test_keys.py | 98 - .../site-packages/wheel/test/test_paths.py | 6 - .../site-packages/wheel/test/test_ranking.py | 43 - .../wheel/test/test_signatures.py | 47 - .../site-packages/wheel/test/test_tagopt.py | 176 - .../site-packages/wheel/test/test_tool.py | 25 - .../wheel/test/test_wheelfile.py | 142 - .../site-packages/wheel/tool/__init__.py | 360 -- .../lib/python2.7/site-packages/wheel/util.py | 167 - .../site-packages/wheel/wininst2wheel.py | 187 - venv/lib/python2.7/site.py | 758 --- venv/lib/python2.7/sre.py | 1 - venv/lib/python2.7/sre_compile.py | 1 - venv/lib/python2.7/sre_constants.py | 1 - venv/lib/python2.7/sre_parse.py | 1 - venv/lib/python2.7/stat.py | 1 - venv/lib/python2.7/types.py | 1 - venv/lib/python2.7/warnings.py | 1 - venv/pip-selfcheck.json | 1 - 858 files changed, 52 insertions(+), 217564 deletions(-) delete mode 100644 venv/bin/activate delete mode 100644 venv/bin/activate.csh delete mode 100644 venv/bin/activate.fish delete mode 100644 venv/bin/activate_this.py delete mode 100755 venv/bin/easy_install delete mode 100755 venv/bin/easy_install-2.7 delete mode 100755 venv/bin/flask delete mode 100755 venv/bin/pip delete mode 100755 venv/bin/pip2 delete mode 100755 venv/bin/pip2.7 delete mode 120000 venv/bin/python delete mode 100755 venv/bin/python-config delete mode 100755 venv/bin/python2 delete mode 120000 venv/bin/python2.7 delete mode 100755 venv/bin/wheel delete mode 120000 venv/include/python2.7 delete mode 120000 venv/lib/python2.7/UserDict.py delete mode 120000 venv/lib/python2.7/_abcoll.py delete mode 120000 venv/lib/python2.7/_weakrefset.py delete mode 120000 venv/lib/python2.7/abc.py delete mode 120000 venv/lib/python2.7/codecs.py delete mode 120000 venv/lib/python2.7/config delete mode 120000 venv/lib/python2.7/copy_reg.py delete mode 100644 venv/lib/python2.7/distutils/__init__.py delete mode 100644 venv/lib/python2.7/distutils/distutils.cfg delete mode 120000 venv/lib/python2.7/encodings delete mode 120000 venv/lib/python2.7/fnmatch.py delete mode 120000 venv/lib/python2.7/genericpath.py delete mode 120000 venv/lib/python2.7/lib-dynload delete mode 120000 venv/lib/python2.7/linecache.py delete mode 120000 venv/lib/python2.7/locale.py delete mode 100644 venv/lib/python2.7/no-global-site-packages.txt delete mode 120000 venv/lib/python2.7/ntpath.py delete mode 100644 venv/lib/python2.7/orig-prefix.txt delete mode 120000 venv/lib/python2.7/os.py delete mode 120000 venv/lib/python2.7/posixpath.py delete mode 120000 venv/lib/python2.7/re.py delete mode 100644 venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/entry_points.txt delete mode 100644 venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/entry_points.txt delete mode 100644 venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/click-6.6.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/click-6.6.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/click-6.6.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/click-6.6.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/click-6.6.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/click-6.6.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/click-6.6.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/click/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/click/_bashcomplete.py delete mode 100644 venv/lib/python2.7/site-packages/click/_compat.py delete mode 100644 venv/lib/python2.7/site-packages/click/_termui_impl.py delete mode 100644 venv/lib/python2.7/site-packages/click/_textwrap.py delete mode 100644 venv/lib/python2.7/site-packages/click/_unicodefun.py delete mode 100644 venv/lib/python2.7/site-packages/click/_winconsole.py delete mode 100644 venv/lib/python2.7/site-packages/click/core.py delete mode 100644 venv/lib/python2.7/site-packages/click/decorators.py delete mode 100644 venv/lib/python2.7/site-packages/click/exceptions.py delete mode 100644 venv/lib/python2.7/site-packages/click/formatting.py delete mode 100644 venv/lib/python2.7/site-packages/click/globals.py delete mode 100644 venv/lib/python2.7/site-packages/click/parser.py delete mode 100644 venv/lib/python2.7/site-packages/click/termui.py delete mode 100644 venv/lib/python2.7/site-packages/click/testing.py delete mode 100644 venv/lib/python2.7/site-packages/click/types.py delete mode 100644 venv/lib/python2.7/site-packages/click/utils.py delete mode 100644 venv/lib/python2.7/site-packages/dateutil/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/dateutil/easter.py delete mode 100644 venv/lib/python2.7/site-packages/dateutil/parser.py delete mode 100644 venv/lib/python2.7/site-packages/dateutil/relativedelta.py delete mode 100644 venv/lib/python2.7/site-packages/dateutil/rrule.py delete mode 100644 venv/lib/python2.7/site-packages/dateutil/tz/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/dateutil/tz/_common.py delete mode 100644 venv/lib/python2.7/site-packages/dateutil/tz/tz.py delete mode 100644 venv/lib/python2.7/site-packages/dateutil/tz/win.py delete mode 100644 venv/lib/python2.7/site-packages/dateutil/tzwin.py delete mode 100644 venv/lib/python2.7/site-packages/dateutil/zoneinfo/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz delete mode 100644 venv/lib/python2.7/site-packages/dateutil/zoneinfo/rebuild.py delete mode 100644 venv/lib/python2.7/site-packages/easy_install.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/pbr.json delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/client/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/client/cat.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/client/cluster.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/client/indices.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/client/nodes.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/client/snapshot.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/client/utils.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/compat.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/connection/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/connection/base.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/connection/http_requests.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/connection/http_urllib3.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/connection/pooling.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/connection_pool.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/exceptions.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/helpers/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/helpers/test.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/serializer.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch/transport.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/pbr.json delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/aggs.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/analysis.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/connections.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/document.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/exceptions.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/faceted_search.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/field.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/filter.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/function.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/index.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/mapping.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/query.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/result.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/search.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/serializer.py delete mode 100644 venv/lib/python2.7/site-packages/elasticsearch_dsl/utils.py delete mode 100644 venv/lib/python2.7/site-packages/flask/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/flask/__main__.py delete mode 100644 venv/lib/python2.7/site-packages/flask/_compat.py delete mode 100644 venv/lib/python2.7/site-packages/flask/app.py delete mode 100644 venv/lib/python2.7/site-packages/flask/blueprints.py delete mode 100644 venv/lib/python2.7/site-packages/flask/cli.py delete mode 100644 venv/lib/python2.7/site-packages/flask/config.py delete mode 100644 venv/lib/python2.7/site-packages/flask/ctx.py delete mode 100644 venv/lib/python2.7/site-packages/flask/debughelpers.py delete mode 100644 venv/lib/python2.7/site-packages/flask/ext/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/flask/exthook.py delete mode 100644 venv/lib/python2.7/site-packages/flask/globals.py delete mode 100644 venv/lib/python2.7/site-packages/flask/helpers.py delete mode 100644 venv/lib/python2.7/site-packages/flask/json.py delete mode 100644 venv/lib/python2.7/site-packages/flask/logging.py delete mode 100644 venv/lib/python2.7/site-packages/flask/sessions.py delete mode 100644 venv/lib/python2.7/site-packages/flask/signals.py delete mode 100644 venv/lib/python2.7/site-packages/flask/templating.py delete mode 100644 venv/lib/python2.7/site-packages/flask/testing.py delete mode 100644 venv/lib/python2.7/site-packages/flask/views.py delete mode 100644 venv/lib/python2.7/site-packages/flask/wrappers.py delete mode 100644 venv/lib/python2.7/site-packages/itsdangerous-0.24.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/itsdangerous-0.24.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/itsdangerous-0.24.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/itsdangerous-0.24.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/itsdangerous-0.24.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/itsdangerous-0.24.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/itsdangerous-0.24.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/itsdangerous.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/_compat.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/_stringdefs.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/bccache.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/compiler.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/constants.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/debug.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/defaults.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/environment.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/exceptions.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/ext.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/filters.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/lexer.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/loaders.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/meta.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/nodes.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/optimizer.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/parser.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/runtime.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/sandbox.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/tests.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/utils.py delete mode 100644 venv/lib/python2.7/site-packages/jinja2/visitor.py delete mode 100644 venv/lib/python2.7/site-packages/lxml-3.6.0.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/lxml-3.6.0.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/lxml-3.6.0.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/lxml-3.6.0.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/lxml-3.6.0.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/lxml-3.6.0.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/lxml-3.6.0.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/lxml/ElementInclude.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/_elementpath.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/builder.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/cssselect.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/doctestcompare.py delete mode 100755 venv/lib/python2.7/site-packages/lxml/etree.so delete mode 100644 venv/lib/python2.7/site-packages/lxml/html/ElementSoup.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/html/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/html/_diffcommand.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/html/_html5builder.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/html/_setmixin.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/html/builder.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/html/clean.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/html/defs.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/html/diff.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/html/formfill.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/html/html5parser.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/html/soupparser.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/html/usedoctest.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/c14n.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/config.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/dtdvalid.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/etree_defs.h delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/etreepublic.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/htmlparser.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/lxml-version.h delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/relaxng.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/schematron.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/tree.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/uri.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/xinclude.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/xmlerror.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/xmlparser.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/xmlschema.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/xpath.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/includes/xslt.pxd delete mode 100644 venv/lib/python2.7/site-packages/lxml/isoschematron/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/isoschematron/resources/rng/iso-schematron.rng delete mode 100644 venv/lib/python2.7/site-packages/lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl delete mode 100644 venv/lib/python2.7/site-packages/lxml/isoschematron/resources/xsl/XSD2Schtrn.xsl delete mode 100644 venv/lib/python2.7/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_abstract_expand.xsl delete mode 100644 venv/lib/python2.7/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_dsdl_include.xsl delete mode 100644 venv/lib/python2.7/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_message.xsl delete mode 100644 venv/lib/python2.7/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl delete mode 100644 venv/lib/python2.7/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl delete mode 100644 venv/lib/python2.7/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/readme.txt delete mode 100644 venv/lib/python2.7/site-packages/lxml/lxml.etree.h delete mode 100644 venv/lib/python2.7/site-packages/lxml/lxml.etree_api.h delete mode 100755 venv/lib/python2.7/site-packages/lxml/objectify.so delete mode 100644 venv/lib/python2.7/site-packages/lxml/pyclasslookup.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/sax.py delete mode 100644 venv/lib/python2.7/site-packages/lxml/usedoctest.py delete mode 100644 venv/lib/python2.7/site-packages/markupsafe/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/markupsafe/_compat.py delete mode 100644 venv/lib/python2.7/site-packages/markupsafe/_constants.py delete mode 100644 venv/lib/python2.7/site-packages/markupsafe/_native.py delete mode 100644 venv/lib/python2.7/site-packages/markupsafe/_speedups.c delete mode 100755 venv/lib/python2.7/site-packages/markupsafe/_speedups.so delete mode 100644 venv/lib/python2.7/site-packages/markupsafe/tests.py delete mode 100644 venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/entry_points.txt delete mode 100644 venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/pip/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/__main__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/_cmd.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/adapter.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/cache.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/compat.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/controller.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/filewrapper.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/heuristics.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/serialize.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/wrapper.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/colorama/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/colorama/ansi.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/colorama/ansitowin32.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/colorama/initialise.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/colorama/win32.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/colorama/winterm.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/misc.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/shutil.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/tarfile.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/compat.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/database.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/index.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/locators.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/manifest.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/markers.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/metadata.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/resources.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/scripts.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/t32.exe delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/t64.exe delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/util.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/version.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/w32.exe delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/w64.exe delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/distlib/wheel.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/constants.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/_base.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/lint.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/optionaltags.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/sanitizer.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/whitespace.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/html5parser.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/ihatexml.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/inputstream.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/sanitizer.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/serializer/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/serializer/htmlserializer.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/tokenizer.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/treeadapters/sax.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/_base.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/dom.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/etree.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/_base.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/dom.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/etree.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/genshistream.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/lxmletree.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/pulldom.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/trie/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/trie/_base.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/trie/datrie.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/trie/py.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/html5lib/utils.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/ipaddress.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/lockfile/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/lockfile/linklockfile.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/lockfile/mkdirlockfile.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/lockfile/pidlockfile.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/lockfile/sqlitelockfile.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/lockfile/symlinklockfile.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/packaging/__about__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/packaging/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/packaging/_compat.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/packaging/_structures.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/packaging/markers.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/packaging/requirements.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/packaging/specifiers.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/packaging/utils.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/packaging/version.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/pkg_resources/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/progress/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/progress/bar.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/progress/counter.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/progress/helpers.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/progress/spinner.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/pyparsing.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/re-vendor.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/adapters.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/api.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/auth.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/cacert.pem delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/certs.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/compat.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/cookies.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/exceptions.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/hooks.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/models.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/big5freq.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/big5prober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/chardetect.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/chardistribution.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/charsetgroupprober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/charsetprober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/codingstatemachine.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/compat.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/constants.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/cp949prober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/escprober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/escsm.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/eucjpprober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/euckrfreq.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/euckrprober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/euctwfreq.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/euctwprober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/gb2312freq.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/gb2312prober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/hebrewprober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/jisfreq.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/jpcntx.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/langbulgarianmodel.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/langcyrillicmodel.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/langgreekmodel.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/langhebrewmodel.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/langhungarianmodel.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/langthaimodel.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/latin1prober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/mbcharsetprober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/mbcsgroupprober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/mbcssm.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/sbcharsetprober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/sbcsgroupprober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/sjisprober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/universaldetector.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/chardet/utf8prober.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/_collections.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/connection.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/connectionpool.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/contrib/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/contrib/appengine.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/contrib/ntlmpool.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/contrib/socks.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/exceptions.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/fields.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/filepost.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/packages/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/packages/ordered_dict.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/packages/six.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/poolmanager.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/request.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/response.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/connection.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/request.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/response.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/retry.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/timeout.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/url.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/sessions.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/status_codes.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/structures.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/requests/utils.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/retrying.py delete mode 100644 venv/lib/python2.7/site-packages/pip/_vendor/six.py delete mode 100644 venv/lib/python2.7/site-packages/pip/basecommand.py delete mode 100644 venv/lib/python2.7/site-packages/pip/baseparser.py delete mode 100644 venv/lib/python2.7/site-packages/pip/cmdoptions.py delete mode 100644 venv/lib/python2.7/site-packages/pip/commands/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/commands/completion.py delete mode 100644 venv/lib/python2.7/site-packages/pip/commands/download.py delete mode 100644 venv/lib/python2.7/site-packages/pip/commands/freeze.py delete mode 100644 venv/lib/python2.7/site-packages/pip/commands/hash.py delete mode 100644 venv/lib/python2.7/site-packages/pip/commands/help.py delete mode 100644 venv/lib/python2.7/site-packages/pip/commands/install.py delete mode 100644 venv/lib/python2.7/site-packages/pip/commands/list.py delete mode 100644 venv/lib/python2.7/site-packages/pip/commands/search.py delete mode 100644 venv/lib/python2.7/site-packages/pip/commands/show.py delete mode 100644 venv/lib/python2.7/site-packages/pip/commands/uninstall.py delete mode 100644 venv/lib/python2.7/site-packages/pip/commands/wheel.py delete mode 100644 venv/lib/python2.7/site-packages/pip/compat/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/compat/dictconfig.py delete mode 100644 venv/lib/python2.7/site-packages/pip/compat/ordereddict.py delete mode 100644 venv/lib/python2.7/site-packages/pip/download.py delete mode 100644 venv/lib/python2.7/site-packages/pip/exceptions.py delete mode 100644 venv/lib/python2.7/site-packages/pip/index.py delete mode 100644 venv/lib/python2.7/site-packages/pip/locations.py delete mode 100644 venv/lib/python2.7/site-packages/pip/models/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/models/index.py delete mode 100644 venv/lib/python2.7/site-packages/pip/operations/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/operations/freeze.py delete mode 100644 venv/lib/python2.7/site-packages/pip/pep425tags.py delete mode 100644 venv/lib/python2.7/site-packages/pip/req/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/req/req_file.py delete mode 100644 venv/lib/python2.7/site-packages/pip/req/req_install.py delete mode 100644 venv/lib/python2.7/site-packages/pip/req/req_set.py delete mode 100644 venv/lib/python2.7/site-packages/pip/req/req_uninstall.py delete mode 100644 venv/lib/python2.7/site-packages/pip/status_codes.py delete mode 100644 venv/lib/python2.7/site-packages/pip/utils/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/utils/appdirs.py delete mode 100644 venv/lib/python2.7/site-packages/pip/utils/build.py delete mode 100644 venv/lib/python2.7/site-packages/pip/utils/deprecation.py delete mode 100644 venv/lib/python2.7/site-packages/pip/utils/encoding.py delete mode 100644 venv/lib/python2.7/site-packages/pip/utils/filesystem.py delete mode 100644 venv/lib/python2.7/site-packages/pip/utils/hashes.py delete mode 100644 venv/lib/python2.7/site-packages/pip/utils/logging.py delete mode 100644 venv/lib/python2.7/site-packages/pip/utils/outdated.py delete mode 100644 venv/lib/python2.7/site-packages/pip/utils/setuptools_build.py delete mode 100644 venv/lib/python2.7/site-packages/pip/utils/ui.py delete mode 100644 venv/lib/python2.7/site-packages/pip/vcs/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pip/vcs/bazaar.py delete mode 100644 venv/lib/python2.7/site-packages/pip/vcs/git.py delete mode 100644 venv/lib/python2.7/site-packages/pip/vcs/mercurial.py delete mode 100644 venv/lib/python2.7/site-packages/pip/vcs/subversion.py delete mode 100644 venv/lib/python2.7/site-packages/pip/wheel.py delete mode 100644 venv/lib/python2.7/site-packages/pkg_resources/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pkg_resources/_vendor/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/__about__.py delete mode 100644 venv/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/_compat.py delete mode 100644 venv/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/_structures.py delete mode 100644 venv/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/markers.py delete mode 100644 venv/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/requirements.py delete mode 100644 venv/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/specifiers.py delete mode 100644 venv/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/utils.py delete mode 100644 venv/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/version.py delete mode 100644 venv/lib/python2.7/site-packages/pkg_resources/_vendor/pyparsing.py delete mode 100644 venv/lib/python2.7/site-packages/pkg_resources/_vendor/six.py delete mode 100644 venv/lib/python2.7/site-packages/pkg_resources/extern/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/python_dateutil-2.5.3.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/python_dateutil-2.5.3.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/python_dateutil-2.5.3.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/python_dateutil-2.5.3.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/python_dateutil-2.5.3.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/python_dateutil-2.5.3.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/python_dateutil-2.5.3.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/python_dateutil-2.5.3.dist-info/zip-safe delete mode 100644 venv/lib/python2.7/site-packages/requests-2.10.0.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/requests-2.10.0.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/requests-2.10.0.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/requests-2.10.0.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/requests-2.10.0.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/requests-2.10.0.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/requests-2.10.0.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/requests/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/requests/adapters.py delete mode 100644 venv/lib/python2.7/site-packages/requests/api.py delete mode 100644 venv/lib/python2.7/site-packages/requests/auth.py delete mode 100644 venv/lib/python2.7/site-packages/requests/cacert.pem delete mode 100644 venv/lib/python2.7/site-packages/requests/certs.py delete mode 100644 venv/lib/python2.7/site-packages/requests/compat.py delete mode 100644 venv/lib/python2.7/site-packages/requests/cookies.py delete mode 100644 venv/lib/python2.7/site-packages/requests/exceptions.py delete mode 100644 venv/lib/python2.7/site-packages/requests/hooks.py delete mode 100644 venv/lib/python2.7/site-packages/requests/models.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/big5freq.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/big5prober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/chardetect.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/chardistribution.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/charsetgroupprober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/charsetprober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/codingstatemachine.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/compat.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/constants.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/cp949prober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/escprober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/escsm.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/eucjpprober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/euckrfreq.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/euckrprober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/euctwfreq.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/euctwprober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/gb2312freq.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/gb2312prober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/hebrewprober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/jisfreq.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/jpcntx.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/langbulgarianmodel.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/langcyrillicmodel.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/langgreekmodel.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/langhebrewmodel.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/langhungarianmodel.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/langthaimodel.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/latin1prober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/mbcharsetprober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/mbcsgroupprober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/mbcssm.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/sbcharsetprober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/sbcsgroupprober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/sjisprober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/universaldetector.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/chardet/utf8prober.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/_collections.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/connection.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/connectionpool.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/contrib/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/contrib/appengine.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/contrib/ntlmpool.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/contrib/pyopenssl.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/contrib/socks.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/exceptions.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/fields.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/filepost.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/packages/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/packages/ordered_dict.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/packages/six.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/poolmanager.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/request.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/response.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/util/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/util/connection.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/util/request.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/util/response.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/util/retry.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/util/ssl_.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/util/timeout.py delete mode 100644 venv/lib/python2.7/site-packages/requests/packages/urllib3/util/url.py delete mode 100644 venv/lib/python2.7/site-packages/requests/sessions.py delete mode 100644 venv/lib/python2.7/site-packages/requests/status_codes.py delete mode 100644 venv/lib/python2.7/site-packages/requests/structures.py delete mode 100644 venv/lib/python2.7/site-packages/requests/utils.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools-23.0.0.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/setuptools-23.0.0.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/setuptools-23.0.0.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/setuptools-23.0.0.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/setuptools-23.0.0.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/setuptools-23.0.0.dist-info/dependency_links.txt delete mode 100644 venv/lib/python2.7/site-packages/setuptools-23.0.0.dist-info/entry_points.txt delete mode 100644 venv/lib/python2.7/site-packages/setuptools-23.0.0.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/setuptools-23.0.0.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/setuptools-23.0.0.dist-info/zip-safe delete mode 100644 venv/lib/python2.7/site-packages/setuptools/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/archive_util.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/cli-32.exe delete mode 100644 venv/lib/python2.7/site-packages/setuptools/cli-64.exe delete mode 100644 venv/lib/python2.7/site-packages/setuptools/cli.exe delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/alias.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/bdist_egg.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/bdist_rpm.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/bdist_wininst.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/build_ext.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/build_py.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/develop.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/easy_install.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/egg_info.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/install.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/install_egg_info.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/install_lib.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/install_scripts.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/launcher manifest.xml delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/register.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/rotate.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/saveopts.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/sdist.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/setopt.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/test.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/upload.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/command/upload_docs.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/depends.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/dist.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/extension.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/extern/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/gui-32.exe delete mode 100644 venv/lib/python2.7/site-packages/setuptools/gui-64.exe delete mode 100644 venv/lib/python2.7/site-packages/setuptools/gui.exe delete mode 100644 venv/lib/python2.7/site-packages/setuptools/launch.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/lib2to3_ex.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/msvc9_support.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/package_index.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/py26compat.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/py27compat.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/py31compat.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/sandbox.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/script (dev).tmpl delete mode 100644 venv/lib/python2.7/site-packages/setuptools/script.tmpl delete mode 100644 venv/lib/python2.7/site-packages/setuptools/site-patch.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/ssl_support.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/unicode_utils.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/utils.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/version.py delete mode 100644 venv/lib/python2.7/site-packages/setuptools/windows_support.py delete mode 100644 venv/lib/python2.7/site-packages/six-1.10.0.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/six-1.10.0.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/six-1.10.0.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/six-1.10.0.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/six-1.10.0.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/six-1.10.0.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/six-1.10.0.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/six.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3-1.16.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/urllib3-1.16.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/urllib3-1.16.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/urllib3-1.16.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/urllib3-1.16.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/urllib3-1.16.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/urllib3-1.16.dist-info/pbr.json delete mode 100644 venv/lib/python2.7/site-packages/urllib3-1.16.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/urllib3/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/_collections.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/connection.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/connectionpool.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/contrib/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/contrib/appengine.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/contrib/ntlmpool.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/contrib/pyopenssl.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/contrib/socks.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/exceptions.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/fields.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/filepost.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/packages/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/packages/backports/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/packages/backports/makefile.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/packages/ordered_dict.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/packages/six.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/packages/ssl_match_hostname/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/packages/ssl_match_hostname/_implementation.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/poolmanager.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/request.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/response.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/util/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/util/connection.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/util/request.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/util/response.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/util/retry.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/util/ssl_.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/util/timeout.py delete mode 100644 venv/lib/python2.7/site-packages/urllib3/util/url.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/_compat.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/_internal.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/_reloader.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/contrib/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/contrib/atom.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/contrib/cache.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/contrib/fixers.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/contrib/iterio.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/contrib/jsrouting.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/contrib/limiter.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/contrib/lint.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/contrib/profiler.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/contrib/securecookie.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/contrib/sessions.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/contrib/testtools.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/contrib/wrappers.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/datastructures.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/debug/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/debug/console.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/debug/repr.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/debug/shared/FONT_LICENSE delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/debug/shared/console.png delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/debug/shared/debugger.js delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/debug/shared/jquery.js delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/debug/shared/less.png delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/debug/shared/more.png delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/debug/shared/source.png delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/debug/shared/style.css delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/debug/shared/ubuntu.ttf delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/debug/tbtools.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/exceptions.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/filesystem.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/formparser.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/http.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/local.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/posixemulation.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/routing.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/script.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/security.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/serving.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/test.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/testapp.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/urls.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/useragents.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/utils.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/wrappers.py delete mode 100644 venv/lib/python2.7/site-packages/werkzeug/wsgi.py delete mode 100644 venv/lib/python2.7/site-packages/wheel-0.29.0.dist-info/DESCRIPTION.rst delete mode 100644 venv/lib/python2.7/site-packages/wheel-0.29.0.dist-info/INSTALLER delete mode 100644 venv/lib/python2.7/site-packages/wheel-0.29.0.dist-info/LICENSE.txt delete mode 100644 venv/lib/python2.7/site-packages/wheel-0.29.0.dist-info/METADATA delete mode 100644 venv/lib/python2.7/site-packages/wheel-0.29.0.dist-info/RECORD delete mode 100644 venv/lib/python2.7/site-packages/wheel-0.29.0.dist-info/WHEEL delete mode 100644 venv/lib/python2.7/site-packages/wheel-0.29.0.dist-info/entry_points.txt delete mode 100644 venv/lib/python2.7/site-packages/wheel-0.29.0.dist-info/metadata.json delete mode 100644 venv/lib/python2.7/site-packages/wheel-0.29.0.dist-info/top_level.txt delete mode 100644 venv/lib/python2.7/site-packages/wheel/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/__main__.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/archive.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/bdist_wheel.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/decorator.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/egg2wheel.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/eggnames.txt delete mode 100644 venv/lib/python2.7/site-packages/wheel/install.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/metadata.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/paths.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/pep425tags.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/pkginfo.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/signatures/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/signatures/djbec.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/signatures/ed25519py.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/signatures/keys.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/complex-dist/complexdist/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/complex-dist/setup.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/headers.dist/header.h delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/headers.dist/headersdist.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/headers.dist/setup.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/pydist-schema.json delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/simple.dist/setup.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/simple.dist/simpledist/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/test-1.0-py2.py3-none-win32.whl delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/test_basic.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/test_install.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/test_keys.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/test_paths.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/test_ranking.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/test_signatures.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/test_tagopt.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/test_tool.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/test/test_wheelfile.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/tool/__init__.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/util.py delete mode 100644 venv/lib/python2.7/site-packages/wheel/wininst2wheel.py delete mode 100644 venv/lib/python2.7/site.py delete mode 120000 venv/lib/python2.7/sre.py delete mode 120000 venv/lib/python2.7/sre_compile.py delete mode 120000 venv/lib/python2.7/sre_constants.py delete mode 120000 venv/lib/python2.7/sre_parse.py delete mode 120000 venv/lib/python2.7/stat.py delete mode 120000 venv/lib/python2.7/types.py delete mode 120000 venv/lib/python2.7/warnings.py delete mode 100644 venv/pip-selfcheck.json diff --git a/src/archive.py b/src/archive.py index 88372d7..77c9ed9 100755 --- a/src/archive.py +++ b/src/archive.py @@ -1,6 +1,6 @@ -#! /usr/bin/python2 +#! /usr/bin/python3 -from urllib import quote +from urllib.parse import quote from json import loads, dumps import requests as req @@ -11,7 +11,7 @@ def searchIA(title, author): """ Do a search on The Internet Archive for a book """ - print "running a search" + print("running a search") requrl = searchUrl.format(quote(title + " " + author)) try: results = loads(req.get(requrl).text[9:][0:-1]) @@ -20,7 +20,7 @@ def searchIA(title, author): rownum = results["responseHeader"]["params"]["rows"] if rownum < 1: - print "Couldn't find results for %s %s" % (title, author) + print("Couldn't find results for %s %s" % (title, author)) return [] docs = results["response"]["docs"] urls = [] diff --git a/src/goasearch.py b/src/goasearch.py index b700fd8..4723b1e 100755 --- a/src/goasearch.py +++ b/src/goasearch.py @@ -1,4 +1,4 @@ -#! /usr/bin/python2 +#! /usr/bin/python3 from search import indexListing from textbookExceptions import UnIndexable from mcmaster.classes import allCourses @@ -6,15 +6,15 @@ from search import indexListing, createIndex from itertools import imap try: - print "Trying to create the index if it does not exist already" + print("Trying to create the index if it does not exist already") createIndex("oersearch") except Exception as e: - print e + print(e) -print "Downloading course info" +print("Downloading course info") for c in allCourses(): try: - print c - print indexListing(c) + print(c) + print(indexListing(c)) except UnIndexable as e: - print e + print(e) diff --git a/src/mcmaster/classes.py b/src/mcmaster/classes.py index 080fdc8..d1c17db 100755 --- a/src/mcmaster/classes.py +++ b/src/mcmaster/classes.py @@ -1,7 +1,7 @@ -#! /usr/bin/python2 +#! /usr/bin/python3 from sys import argv -from itertools import chain, islice, izip as zip +from itertools import chain, islice from re import search, sub from functools import total_ordering @@ -18,8 +18,13 @@ fall = "2169" spring_summer = "2175" winter = "2171" +def parseSem(sem): + if sem == "TBA": + return "TBA" + return sem[0] + sem[2:4] + sem[6] + # threading stuff -import Queue as q +import queue as q import threading as thd baseurl = "https://applicants.mcmaster.ca/psp/prepprd/EMPLOYEE/PSFT_LS/c/COMMUNITY_ACCESS.CLASS_SEARCH.GBL" @@ -124,10 +129,12 @@ class Section(dict): @property def sem(self): - if self._sem == fall: + if parseSem(self._sem) == fall: return "Fall" - elif self._sem == winter: + elif parseSem(self._sem) == winter: return "Winter" + elif parseSem(self._sem) == "TBA": + return "TBA" else: return "Spring/Summer" @@ -140,7 +147,7 @@ class Section(dict): assert len(self._day) == 2 day = self._day else: - day = [day[n:n+2] for n in xrange(0, len(day)-1, 2)] + day = [day[n:n+2] for n in range(0, len(day)-1, 2)] self._date = (day, timeparse(start), timeparse(end)) @@ -286,7 +293,7 @@ class MosReq(object): self.codes_ = list(chain.from_iterable( map((lambda l: self.getCodes(chr(l))), - xrange(65, 91)))) + range(65, 91)))) return self.codes_ def request(codes, lists, semester): @@ -320,7 +327,7 @@ class CourseInfo(object): lists = q.Queue() threads = [] thread = None - for i in xrange(self.threadcount): + for i in range(self.threadcount): thread = thd.Thread(group=None, target=request, args=(qcodes, lists, self.semester)) threads.append(thread) thread.start() @@ -355,4 +362,4 @@ def allCourses(): if __name__ == "__main__": for course in allCourses(): sys.stdout.write("%s, %s, %s, %s\n" % (course.title, course.code, course.dept, course.books)) - print course.sections + print(course.sections) diff --git a/src/mcmaster/spreadsheet.py b/src/mcmaster/spreadsheet.py index 85055e5..a034086 100755 --- a/src/mcmaster/spreadsheet.py +++ b/src/mcmaster/spreadsheet.py @@ -1,4 +1,4 @@ -#! /usr/bin/python2 +#! /usr/bin/python3 from classes import fallCourses import csv diff --git a/src/mcmaster/sylla.py b/src/mcmaster/sylla.py index 6347e70..3e98088 100755 --- a/src/mcmaster/sylla.py +++ b/src/mcmaster/sylla.py @@ -1,7 +1,7 @@ -#! /usr/bin/python2 +#! /usr/bin/python3 from sys import argv -from itertools import chain, islice, izip_longest, izip as zip +from itertools import chain, islice, zip_longest from re import search, sub from functools import total_ordering from re import sub @@ -34,7 +34,7 @@ class Book(object): def grouper(n, iterable, fillvalue=None): "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" args = [iter(iterable)] * n - return izip_longest(fillvalue=fillvalue, *args) + return zip_longest(fillvalue=fillvalue, *args) searchUrl = "https://campusstore.mcmaster.ca/cgi-mcm/ws/txsub.pl?wsDEPTG1=%s&wsDEPTDESC1=&wsCOURSEG1=%s&crit_cnt=1" diff --git a/src/openlibrary.py b/src/openlibrary.py index d558c21..1e02bc4 100755 --- a/src/openlibrary.py +++ b/src/openlibrary.py @@ -1,6 +1,6 @@ -#! /usr/bin/python2 +#! /usr/bin/python3 -from urllib import quote +from urllib.parse import quote from json import loads, dumps import requests as req @@ -9,7 +9,7 @@ import requests as req searchurl = 'http://openlibrary.org/search.json?author=%s&title=%s' def bookUrls(title, author): - print title, author + print(title, author) if ":" in title: title = title.split(":")[0] requrl = searchurl % (quote(author), quote(title)) diff --git a/src/predictions.py b/src/predictions.py index b770a0b..e377b88 100755 --- a/src/predictions.py +++ b/src/predictions.py @@ -1,4 +1,4 @@ -##! /usr/bin/python2 +##! /usr/bin/python3 from itertools import groupby, chain from sys import stdout from functools import partial diff --git a/src/search.py b/src/search.py index 3080506..7b560cf 100755 --- a/src/search.py +++ b/src/search.py @@ -1,4 +1,4 @@ -#! /usr/bin/python2 +#! /usr/bin/python3 import elasticsearch @@ -6,7 +6,7 @@ from elasticsearch_dsl import FacetedSearch, Search, Q from elasticsearch_dsl.aggs import Terms, DateHistogram from sys import exit, stderr from json import dumps, loads -from itertools import chain, imap +from itertools import chain from hashlib import sha1 from syslog import syslog @@ -77,9 +77,9 @@ def createIndex(name): """ indices = elasticsearch.client.IndicesClient(es) - print indices.create(name) + print(indices.create(name)) with open("../course.json", "r") as mapping: - print indices.put_mapping("course", loads(mapping.read()), name) + print(indices.put_mapping("course", loads(mapping.read()), name)) def indexListing(course): """ @@ -107,10 +107,10 @@ def indexListing(course): """ json_course = classToJSON(course) courseID = hashsec(json_course) - print es.index(index="oersearch", + print(es.index(index="oersearch", doc_type="course", id=courseID, - body=json_course) + body=json_course)) def termSearch(field): """ diff --git a/src/textbookExceptions.py b/src/textbookExceptions.py index 999ff3e..7e93fbb 100644 --- a/src/textbookExceptions.py +++ b/src/textbookExceptions.py @@ -1,4 +1,4 @@ -#! /usr/bin/python2 +#! /usr/bin/python3 class UnIndexable(Exception): def __init__(self, course): diff --git a/src/visualize.py b/src/visualize.py index b46a67d..3ec1098 100755 --- a/src/visualize.py +++ b/src/visualize.py @@ -1,4 +1,4 @@ -#! /usr/bin/python2 +#! /usr/bin/python3 from json import loads, load from re import sub, split @@ -92,6 +92,6 @@ def meanCosts(): return graph.render_table(style=True, transpose=True) for x in courses(): - print x + print(x) #print meanCosts() #print meanFacultyCosts() diff --git a/src/website.py b/src/website.py index c11b6d1..ee019a3 100755 --- a/src/website.py +++ b/src/website.py @@ -1,17 +1,16 @@ -#! /usr/bin/python2 +#! /usr/bin/python3 from functools import partial from flask import Blueprint, abort, Flask, render_template, flash, request, send_from_directory from flask_bootstrap import Bootstrap from flask_appconfig import AppConfig -from urllib import unquote from search import searchTerms from openlibrary import bookUrls from archive import searchIA -from urllib import quote, unquote +from urllib.parse import quote, unquote from json import dumps, loads from werkzeug.contrib.cache import MemcachedCache @@ -20,8 +19,8 @@ cache = MemcachedCache(['127.0.0.1:11211']) import os def predict(fieldtype, term): - print fieldtype - print term + print(fieldtype) + print(term) if not term: return "[]" else: @@ -90,7 +89,7 @@ def ClassSearch(configfile=None): @blueprint.route("/fc", methods=("GET", "POST")) def fc(): """ Filter Courses """ - print "trying to get courses" + print("trying to get courses") params = dict(request.args.items()) for key, val in params.iteritems(): if val in defaults: @@ -106,7 +105,7 @@ def ClassSearch(configfile=None): params = loads(dict(request.args.items())["data"]) except KeyError: return dumps("false") - print params + print(params) author = params["author"] title = params["title"] @@ -116,11 +115,11 @@ def ClassSearch(configfile=None): # Cache the result of the open library search openlib = cacheit("openlib"+title+author, lambda : bookUrls(title, author)) - print openlib + print(openlib) # cache the result of an internet archive search iarchive = cacheit("iarchive"+title+author, lambda : searchIA(title, author)) - print iarchive + print(iarchive) if not (any(openlib) or any(iarchive)): # We literally could not find ANYTHING diff --git a/venv/bin/activate b/venv/bin/activate deleted file mode 100644 index 625bdeb..0000000 --- a/venv/bin/activate +++ /dev/null @@ -1,78 +0,0 @@ -# This file must be used with "source bin/activate" *from bash* -# you cannot run it directly - -deactivate () { - unset -f pydoc >/dev/null 2>&1 - - # reset old environment variables - # ! [ -z ${VAR+_} ] returns true if VAR is declared at all - if ! [ -z "${_OLD_VIRTUAL_PATH+_}" ] ; then - PATH="$_OLD_VIRTUAL_PATH" - export PATH - unset _OLD_VIRTUAL_PATH - fi - if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then - PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" - export PYTHONHOME - unset _OLD_VIRTUAL_PYTHONHOME - fi - - # This should detect bash and zsh, which have a hash command that must - # be called to get it to forget past commands. Without forgetting - # past commands the $PATH changes we made may not be respected - if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ] ; then - hash -r 2>/dev/null - fi - - if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then - PS1="$_OLD_VIRTUAL_PS1" - export PS1 - unset _OLD_VIRTUAL_PS1 - fi - - unset VIRTUAL_ENV - if [ ! "${1-}" = "nondestructive" ] ; then - # Self destruct! - unset -f deactivate - fi -} - -# unset irrelevant variables -deactivate nondestructive - -VIRTUAL_ENV="/home/wes/MGOAL/venv" -export VIRTUAL_ENV - -_OLD_VIRTUAL_PATH="$PATH" -PATH="$VIRTUAL_ENV/bin:$PATH" -export PATH - -# unset PYTHONHOME if set -if ! [ -z "${PYTHONHOME+_}" ] ; then - _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" - unset PYTHONHOME -fi - -if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then - _OLD_VIRTUAL_PS1="$PS1" - if [ "x" != x ] ; then - PS1="$PS1" - else - PS1="(`basename \"$VIRTUAL_ENV\"`) $PS1" - fi - export PS1 -fi - -# Make sure to unalias pydoc if it's already there -alias pydoc 2>/dev/null >/dev/null && unalias pydoc - -pydoc () { - python -m pydoc "$@" -} - -# This should detect bash and zsh, which have a hash command that must -# be called to get it to forget past commands. Without forgetting -# past commands the $PATH changes we made may not be respected -if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ] ; then - hash -r 2>/dev/null -fi diff --git a/venv/bin/activate.csh b/venv/bin/activate.csh deleted file mode 100644 index 9c5cb83..0000000 --- a/venv/bin/activate.csh +++ /dev/null @@ -1,36 +0,0 @@ -# This file must be used with "source bin/activate.csh" *from csh*. -# You cannot run it directly. -# Created by Davide Di Blasi . - -alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc' - -# Unset irrelevant variables. -deactivate nondestructive - -setenv VIRTUAL_ENV "/home/wes/MGOAL/venv" - -set _OLD_VIRTUAL_PATH="$PATH" -setenv PATH "$VIRTUAL_ENV/bin:$PATH" - - - -if ("" != "") then - set env_name = "" -else - set env_name = `basename "$VIRTUAL_ENV"` -endif - -# Could be in a non-interactive environment, -# in which case, $prompt is undefined and we wouldn't -# care about the prompt anyway. -if ( $?prompt ) then - set _OLD_VIRTUAL_PROMPT="$prompt" - set prompt = "[$env_name] $prompt" -endif - -unset env_name - -alias pydoc python -m pydoc - -rehash - diff --git a/venv/bin/activate.fish b/venv/bin/activate.fish deleted file mode 100644 index 152dc7d..0000000 --- a/venv/bin/activate.fish +++ /dev/null @@ -1,76 +0,0 @@ -# This file must be used using `. bin/activate.fish` *within a running fish ( http://fishshell.com ) session*. -# Do not run it directly. - -function deactivate -d 'Exit virtualenv mode and return to the normal environment.' - # reset old environment variables - if test -n "$_OLD_VIRTUAL_PATH" - set -gx PATH $_OLD_VIRTUAL_PATH - set -e _OLD_VIRTUAL_PATH - end - - if test -n "$_OLD_VIRTUAL_PYTHONHOME" - set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME - set -e _OLD_VIRTUAL_PYTHONHOME - end - - if test -n "$_OLD_FISH_PROMPT_OVERRIDE" - # Set an empty local `$fish_function_path` to allow the removal of `fish_prompt` using `functions -e`. - set -l fish_function_path - - # Erase virtualenv's `fish_prompt` and restore the original. - functions -e fish_prompt - functions -c _old_fish_prompt fish_prompt - functions -e _old_fish_prompt - set -e _OLD_FISH_PROMPT_OVERRIDE - end - - set -e VIRTUAL_ENV - - if test "$argv[1]" != 'nondestructive' - # Self-destruct! - functions -e pydoc - functions -e deactivate - end -end - -# Unset irrelevant variables. -deactivate nondestructive - -set -gx VIRTUAL_ENV "/home/wes/MGOAL/venv" - -set -gx _OLD_VIRTUAL_PATH $PATH -set -gx PATH "$VIRTUAL_ENV/bin" $PATH - -# Unset `$PYTHONHOME` if set. -if set -q PYTHONHOME - set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME - set -e PYTHONHOME -end - -function pydoc - python -m pydoc $argv -end - -if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" - # Copy the current `fish_prompt` function as `_old_fish_prompt`. - functions -c fish_prompt _old_fish_prompt - - function fish_prompt - # Save the current $status, for fish_prompts that display it. - set -l old_status $status - - # Prompt override provided? - # If not, just prepend the environment name. - if test -n "" - printf '%s%s' "" (set_color normal) - else - printf '%s(%s) ' (set_color normal) (basename "$VIRTUAL_ENV") - end - - # Restore the original $status - echo "exit $old_status" | source - _old_fish_prompt - end - - set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" -end diff --git a/venv/bin/activate_this.py b/venv/bin/activate_this.py deleted file mode 100644 index f18193b..0000000 --- a/venv/bin/activate_this.py +++ /dev/null @@ -1,34 +0,0 @@ -"""By using execfile(this_file, dict(__file__=this_file)) you will -activate this virtualenv environment. - -This can be used when you must use an existing Python interpreter, not -the virtualenv bin/python -""" - -try: - __file__ -except NameError: - raise AssertionError( - "You must run this like execfile('path/to/activate_this.py', dict(__file__='path/to/activate_this.py'))") -import sys -import os - -old_os_path = os.environ.get('PATH', '') -os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + os.pathsep + old_os_path -base = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -if sys.platform == 'win32': - site_packages = os.path.join(base, 'Lib', 'site-packages') -else: - site_packages = os.path.join(base, 'lib', 'python%s' % sys.version[:3], 'site-packages') -prev_sys_path = list(sys.path) -import site -site.addsitedir(site_packages) -sys.real_prefix = sys.prefix -sys.prefix = base -# Move the added items to the front of the path: -new_sys_path = [] -for item in list(sys.path): - if item not in prev_sys_path: - new_sys_path.append(item) - sys.path.remove(item) -sys.path[:0] = new_sys_path diff --git a/venv/bin/easy_install b/venv/bin/easy_install deleted file mode 100755 index 130372c..0000000 --- a/venv/bin/easy_install +++ /dev/null @@ -1,11 +0,0 @@ -#!/home/wes/MGOAL/venv/bin/python2 - -# -*- coding: utf-8 -*- -import re -import sys - -from setuptools.command.easy_install import main - -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/venv/bin/easy_install-2.7 b/venv/bin/easy_install-2.7 deleted file mode 100755 index 130372c..0000000 --- a/venv/bin/easy_install-2.7 +++ /dev/null @@ -1,11 +0,0 @@ -#!/home/wes/MGOAL/venv/bin/python2 - -# -*- coding: utf-8 -*- -import re -import sys - -from setuptools.command.easy_install import main - -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/venv/bin/flask b/venv/bin/flask deleted file mode 100755 index c1f3137..0000000 --- a/venv/bin/flask +++ /dev/null @@ -1,11 +0,0 @@ -#!/home/wes/MGOAL/venv/bin/python2 - -# -*- coding: utf-8 -*- -import re -import sys - -from flask.cli import main - -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/venv/bin/pip b/venv/bin/pip deleted file mode 100755 index 90c3dd8..0000000 --- a/venv/bin/pip +++ /dev/null @@ -1,11 +0,0 @@ -#!/home/wes/MGOAL/venv/bin/python2 - -# -*- coding: utf-8 -*- -import re -import sys - -from pip import main - -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/venv/bin/pip2 b/venv/bin/pip2 deleted file mode 100755 index 90c3dd8..0000000 --- a/venv/bin/pip2 +++ /dev/null @@ -1,11 +0,0 @@ -#!/home/wes/MGOAL/venv/bin/python2 - -# -*- coding: utf-8 -*- -import re -import sys - -from pip import main - -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/venv/bin/pip2.7 b/venv/bin/pip2.7 deleted file mode 100755 index 90c3dd8..0000000 --- a/venv/bin/pip2.7 +++ /dev/null @@ -1,11 +0,0 @@ -#!/home/wes/MGOAL/venv/bin/python2 - -# -*- coding: utf-8 -*- -import re -import sys - -from pip import main - -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/venv/bin/python b/venv/bin/python deleted file mode 120000 index 551ab26..0000000 --- a/venv/bin/python +++ /dev/null @@ -1 +0,0 @@ -python2 \ No newline at end of file diff --git a/venv/bin/python-config b/venv/bin/python-config deleted file mode 100755 index d66c2e0..0000000 --- a/venv/bin/python-config +++ /dev/null @@ -1,78 +0,0 @@ -#!/home/wes/MGOAL/venv/bin/python - -import sys -import getopt -import sysconfig - -valid_opts = ['prefix', 'exec-prefix', 'includes', 'libs', 'cflags', - 'ldflags', 'help'] - -if sys.version_info >= (3, 2): - valid_opts.insert(-1, 'extension-suffix') - valid_opts.append('abiflags') -if sys.version_info >= (3, 3): - valid_opts.append('configdir') - - -def exit_with_usage(code=1): - sys.stderr.write("Usage: {0} [{1}]\n".format( - sys.argv[0], '|'.join('--'+opt for opt in valid_opts))) - sys.exit(code) - -try: - opts, args = getopt.getopt(sys.argv[1:], '', valid_opts) -except getopt.error: - exit_with_usage() - -if not opts: - exit_with_usage() - -pyver = sysconfig.get_config_var('VERSION') -getvar = sysconfig.get_config_var - -opt_flags = [flag for (flag, val) in opts] - -if '--help' in opt_flags: - exit_with_usage(code=0) - -for opt in opt_flags: - if opt == '--prefix': - print(sysconfig.get_config_var('prefix')) - - elif opt == '--exec-prefix': - print(sysconfig.get_config_var('exec_prefix')) - - elif opt in ('--includes', '--cflags'): - flags = ['-I' + sysconfig.get_path('include'), - '-I' + sysconfig.get_path('platinclude')] - if opt == '--cflags': - flags.extend(getvar('CFLAGS').split()) - print(' '.join(flags)) - - elif opt in ('--libs', '--ldflags'): - abiflags = getattr(sys, 'abiflags', '') - libs = ['-lpython' + pyver + abiflags] - libs += getvar('LIBS').split() - libs += getvar('SYSLIBS').split() - # add the prefix/lib/pythonX.Y/config dir, but only if there is no - # shared library in prefix/lib/. - if opt == '--ldflags': - if not getvar('Py_ENABLE_SHARED'): - libs.insert(0, '-L' + getvar('LIBPL')) - if not getvar('PYTHONFRAMEWORK'): - libs.extend(getvar('LINKFORSHARED').split()) - print(' '.join(libs)) - - elif opt == '--extension-suffix': - ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') - if ext_suffix is None: - ext_suffix = sysconfig.get_config_var('SO') - print(ext_suffix) - - elif opt == '--abiflags': - if not getattr(sys, 'abiflags', None): - exit_with_usage() - print(sys.abiflags) - - elif opt == '--configdir': - print(sysconfig.get_config_var('LIBPL')) diff --git a/venv/bin/python2 b/venv/bin/python2 deleted file mode 100755 index 4965985518ced32310196406e2cf04f73e6f6eb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6280 zcmeHLU2Ggz6~1eG<8@FWi*bvb=qqTQ!Z@bNdDofG8kT3aSJTAbuXIR`B2`X;h#{j;dCUf`rvju!93eX@gN)FXzmh zZ!?|5qT+!Udady}=ey^-_uT)x_tE`b2Py&qg;c5TRctL*yGTSb%mz_NL{yuqq5mIM z8`LVYgAV5AM_fU1)kA>8oYimMPRdr8PsR-xsP_L$sQ>`l$4^zG99D`@t3aZo^(YOwdV@Kbmt1nLc zL;LBcE>Csre{JE)tAE!XuIYIF;unu@_|@gFpS<+@%g@xUedC$Bd!AnX@T1>7SuSV= zrIMl=ll23aJ4n%Ccw~lj#N&b0_gBET5w3e3MURlwMfZyC74S*IKjw85{f{KqD6PEy z?-IP^;5WDH&xALquzFVJE9T=b2oI}usx7ROb|bw?>KYoMZ;C&1E|S9j%|8DZ9K7kK za?S}J@%g_^ILEI``AM=l{f0iqCk2;jtu7I+-=f~1i2tmNV|mY^d4GJ=9?E968+Y?` z-)6KbtF!x%l}P52gQ>io%yl2?NM|$2?s#81sVr-7IGeHZc06ZWma^_UVI2{_j&wYq zPv%|vEy;&RtwZrtMscO~P%aryaFwlW_p`Q}ititZTLY;~Je_)kYvU}bZJ4Q6G7-1q zl+l;ZyEs9a1U&om1!kwnWUH!fmsh; z9#g{SJUDZZ7CpFkNiTVD)XkhmiHCi8e?!teIMxW~nd1u~^K`H$q$~6BDLXi~#{tiU z&brLfu0F~tZBEet`i&9N*dF4{`PmYs&0k=fdvAW)v2P=LV6wEi$&vHFBXGc|-}snQ z-sHBcoy7~s%;Fp7_@5RJcXv$$pCqn1QFpr|+Fv2}xgXKLw+8Ar?&F;H1}ey$2(sug zw=9{(H_h=S?Zh1v_P46tuxoltv#x*dS&s5Xg~Y$WP7}L+OM>{z%s;bSX%-jFGjn^* zzZ{F95w*vh zXkq!;&f@jX;%i-7s5@v3&%9STw{^KxG9SNc>)S4&!@G)Cx{BBLQ3<8Be=x_-2Fy?V zwQ$88KNotSH~L_-H+n2;ot?bVU)N~do$|tO%zP|fjsl*4+C#kCE zNOqCzB6*Tx{WZx2l2tU6e6Dc{JaR+@M#6#hwbh}?Ky{dLzEdqxIoR9T@7DfGZ4&#0?y);3;!NQ>mOwy4$`)tY0PPJZMc(?U*s+}>F#$Ge`n$Pd$p zJ@Eh912`XX9-<%kK;Yz;CtSFUpAx%PB<`*9bEkYyTPOKApVy0xv#&{PoYTC=a6)EAj6K5Hvk1ZdhBN#l zLOv`2@JR^-aZia~jY!o0PRVT#!Hz3!OHTWa3!k=O$437ziXD^ue~Dd=Tgv}mi}LSS zU&YT$hnz>pNIvdY2Rl0M(VOYl+ZKJ7v4gIp%E%Ap?VKI&Q${L7x8UQ-$YkxL5sh`W z*zrNJ1~UbtuaKhOjj4n(*cyuGhm?^R&E!XiU1{gsoD<1hK9$Y9&#)*jmrTc*AlmV? z%~jI9)F9Ou%#va!N9ezEu{Cm8=LT#fhpd5Id^l+hC8!WsF4cN9XA!~E-gz$CRpxd-E`m@8s$&Bsmy8p`L`OR|$^LC~85EPDi1)G1%@=$^Ae{ay? zD6-0D-$}NYZ%aXtn|%I${!YrjPIDUdW1T~OPxx5(SYIgiUQ)OZz{frSiT5L3KI{WN z*5d)fcrAgCeFYNx2)Q`P-+%WL%4-&UtW!wbl@LGtARiz;?*Xu}4?yZpt?nBL*NylE zRu7TFeF{GIA;>9lME&5S{X7Yo9}_?9YmnFnna>^)C>D>q%el|`_ -* `documentation `_ -* `development version - `_ - - - diff --git a/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/INSTALLER b/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/METADATA b/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/METADATA deleted file mode 100644 index 2362e83..0000000 --- a/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/METADATA +++ /dev/null @@ -1,73 +0,0 @@ -Metadata-Version: 2.0 -Name: Flask -Version: 0.11.1 -Summary: A microframework based on Werkzeug, Jinja2 and good intentions -Home-page: http://github.com/pallets/flask/ -Author: Armin Ronacher -Author-email: armin.ronacher@active-4.com -License: BSD -Platform: any -Classifier: Development Status :: 4 - Beta -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: Topic :: Internet :: WWW/HTTP :: Dynamic Content -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Requires-Dist: Jinja2 (>=2.4) -Requires-Dist: Werkzeug (>=0.7) -Requires-Dist: click (>=2.0) -Requires-Dist: itsdangerous (>=0.21) - -Flask ------ - -Flask is a microframework for Python based on Werkzeug, Jinja 2 and good -intentions. And before you ask: It's BSD licensed! - -Flask is Fun -```````````` - -Save in a hello.py: - -.. code:: python - - from flask import Flask - app = Flask(__name__) - - @app.route("/") - def hello(): - return "Hello World!" - - if __name__ == "__main__": - app.run() - -And Easy to Setup -````````````````` - -And run it: - -.. code:: bash - - $ pip install Flask - $ python hello.py - * Running on http://localhost:5000/ - -Links -````` - -* `website `_ -* `documentation `_ -* `development version - `_ - - - diff --git a/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/RECORD b/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/RECORD deleted file mode 100644 index 1d0ce4b..0000000 --- a/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/RECORD +++ /dev/null @@ -1,51 +0,0 @@ -Flask-0.11.1.dist-info/DESCRIPTION.rst,sha256=d3yS7Rb_P-vHUNF_euyueS52NGfpmuqg-C4FVuyGL1w,726 -Flask-0.11.1.dist-info/METADATA,sha256=JYsqnubNfoj5zUbNv3Ip0Oim6ubPsjTRmdgDj0vsQOQ,1864 -Flask-0.11.1.dist-info/RECORD,, -Flask-0.11.1.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 -Flask-0.11.1.dist-info/entry_points.txt,sha256=jzk2Wy2h30uEcqqzd4CVnlzsMXB-vaD5GXjuPMXmTmI,60 -Flask-0.11.1.dist-info/metadata.json,sha256=mZ1xu9uLOritQIZ0KTAMKnzcA1PHcyFP6e7pgi2Uh64,1363 -Flask-0.11.1.dist-info/top_level.txt,sha256=dvi65F6AeGWVU0TBpYiC04yM60-FX1gJFkK31IKQr5c,6 -flask/__init__.py,sha256=nPn1VVakZyNaD7j1f3k8I4y81ch8U_aKTUhGWE0mcTY,1673 -flask/__main__.py,sha256=cldbNi5zpjE68XzIWI8uYHNWwBHHVJmwtlXWk6P4CO4,291 -flask/_compat.py,sha256=vtZmNzjdZ5slXhSrjOVIX52C_FIY03xipZjJdrWUv1o,2490 -flask/app.py,sha256=Fmtl5tQjFcz-x6jSFpRDX7AmLSu-qPP4jvpx2G7d2EE,83370 -flask/blueprints.py,sha256=6HVasMcPcaq7tk36kCrgX4bnhTkky4G5WIWCyyJL8HY,16872 -flask/cli.py,sha256=mUcrLCj_e_GjTxM5YOF4f_qCB3kPphTFwDHPuStrL4s,16941 -flask/config.py,sha256=94IXmQjOxy4L-EsW5R5cljc9uzzCdYNFVwZjg4uJ2Xg,9602 -flask/ctx.py,sha256=UPA0YwoIlHP0txOGanC9lQLSGv6eCqV5Fmw2cVJRmgQ,14739 -flask/debughelpers.py,sha256=z-uQavKIymOZl0WQDLXsnacA00ERIlCx3S3Tnb_OYsE,6024 -flask/exthook.py,sha256=BHC5mNVqvmMpktMxTckJKiw2wyWiKQkV8eiiK1ZXTSQ,5748 -flask/globals.py,sha256=I3m_4RssLhWW1R11zuEI8oFryHUHX3NQwjMkGXOZzg8,1645 -flask/helpers.py,sha256=VTVl7C1GRyp3y-SkX1fYGJhG-TlzfAWphglayGR6Zog,37384 -flask/json.py,sha256=Dht7yQuPBpp0YH3gETBJDo5hdeSSzHCVEEOxnSslgpg,9261 -flask/logging.py,sha256=NwB0-gTqg2htzI88_BxWcmiMWVKb0NgY2RgZTzEZQmA,2683 -flask/sessions.py,sha256=iZBE63y-1SA8PmpIIngYrjJZvc3ZY3wDZt0Q2DTFrdc,14333 -flask/signals.py,sha256=ufhmyiteedO7_nOOcsMheQwGpjPkEP93dh_I7lZlu_c,2209 -flask/templating.py,sha256=u7FbN6j56H_q6CrdJJyJ6gZtqaMa0vh1_GP12gEHRQQ,4912 -flask/testing.py,sha256=t8dNl0b8Qq8A9dUXgYp1HmLOq-FGa90gTr-gLh9WTgA,5101 -flask/views.py,sha256=1QCDSf2w9cmrrKPAmresZiBbvYsDbPnEr1-KmBkQFGY,5632 -flask/wrappers.py,sha256=avZTGHogskE_pJ1iHhULhXFPkaYoeha6vT20JskH_fc,7538 -flask/ext/__init__.py,sha256=UEezCApsG4ZJWqwUnX9YmWcNN4OVENgph_9L05n0eOM,842 -../../../bin/flask,sha256=TCnOAaDSCS_Yn-mcXLKUOrE9i1L2r4Ukgzy0gHRnUgU,227 -Flask-0.11.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -flask/config.pyc,, -flask/testing.pyc,, -flask/wrappers.pyc,, -flask/views.pyc,, -flask/logging.pyc,, -flask/json.pyc,, -flask/helpers.pyc,, -flask/__main__.pyc,, -flask/blueprints.pyc,, -flask/signals.pyc,, -flask/exthook.pyc,, -flask/__init__.pyc,, -flask/debughelpers.pyc,, -flask/templating.pyc,, -flask/ctx.pyc,, -flask/cli.pyc,, -flask/_compat.pyc,, -flask/app.pyc,, -flask/globals.pyc,, -flask/ext/__init__.pyc,, -flask/sessions.pyc,, diff --git a/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/WHEEL b/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/WHEEL deleted file mode 100644 index 8b6dd1b..0000000 --- a/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.29.0) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/entry_points.txt b/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/entry_points.txt deleted file mode 100644 index 14adf18..0000000 --- a/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/entry_points.txt +++ /dev/null @@ -1,4 +0,0 @@ - - [console_scripts] - flask=flask.cli:main - \ No newline at end of file diff --git a/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/metadata.json b/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/metadata.json deleted file mode 100644 index 7b5a3c9..0000000 --- a/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"classifiers": ["Development Status :: 4 - Beta", "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", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules"], "extensions": {"python.commands": {"wrap_console": {"flask": "flask.cli:main"}}, "python.details": {"contacts": [{"email": "armin.ronacher@active-4.com", "name": "Armin Ronacher", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/pallets/flask/"}}, "python.exports": {"console_scripts": {"flask": "flask.cli:main"}}}, "extras": [], "generator": "bdist_wheel (0.29.0)", "license": "BSD", "metadata_version": "2.0", "name": "Flask", "platform": "any", "run_requires": [{"requires": ["Jinja2 (>=2.4)", "Werkzeug (>=0.7)", "click (>=2.0)", "itsdangerous (>=0.21)"]}], "summary": "A microframework based on Werkzeug, Jinja2 and good intentions", "version": "0.11.1"} \ No newline at end of file diff --git a/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/top_level.txt b/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/top_level.txt deleted file mode 100644 index 7e10602..0000000 --- a/venv/lib/python2.7/site-packages/Flask-0.11.1.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -flask diff --git a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/DESCRIPTION.rst b/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/DESCRIPTION.rst deleted file mode 100644 index 4421f04..0000000 --- a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/DESCRIPTION.rst +++ /dev/null @@ -1,36 +0,0 @@ -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: http://en.wikipedia.org/wiki/Sandbox_(computer_security) -.. _Django: http://www.djangoproject.com/ -.. _Jinja2 webpage: http://jinja.pocoo.org/ -.. _documentation: http://jinja.pocoo.org/2/documentation/ - - diff --git a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/INSTALLER b/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/METADATA b/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/METADATA deleted file mode 100644 index 304d7c9..0000000 --- a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/METADATA +++ /dev/null @@ -1,63 +0,0 @@ -Metadata-Version: 2.0 -Name: Jinja2 -Version: 2.8 -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 -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: Topic :: Internet :: WWW/HTTP :: Dynamic Content -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Text Processing :: Markup :: HTML -Requires-Dist: MarkupSafe -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: http://en.wikipedia.org/wiki/Sandbox_(computer_security) -.. _Django: http://www.djangoproject.com/ -.. _Jinja2 webpage: http://jinja.pocoo.org/ -.. _documentation: http://jinja.pocoo.org/2/documentation/ - - diff --git a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/RECORD b/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/RECORD deleted file mode 100644 index 0e47ff6..0000000 --- a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/RECORD +++ /dev/null @@ -1,54 +0,0 @@ -jinja2/__init__.py,sha256=c59bnaAFo63I7lYUZlO2UKHj8LPG3JACKnCrwWgvjGY,2326 -jinja2/_compat.py,sha256=O4FnYOMi4HRBfoCKkX137tt3sR6HvpnQNcwqg8ARYog,3109 -jinja2/_stringdefs.py,sha256=SFObWX5vSMeGNc_aSO3_B2EEmScCstFWtjS4K0YFBXk,404291 -jinja2/bccache.py,sha256=EMN9fsvOpwK3DfxQ9F1lmWoxU2Qlo6AnNhPXTsMrw84,12793 -jinja2/compiler.py,sha256=nQmoS6HpGwgDIC8UXkSdjPYiAjbVqZ-Gf4odO-SAR6E,63846 -jinja2/constants.py,sha256=DCr-oKC2xQO-fkOQO3kXRJW7rEYgmcsMRNpPnM66YSU,1626 -jinja2/debug.py,sha256=GEGHM8vFsNFF-kGc0_fwyj1ftMtuyaH4r0nyG-XA9Z8,11553 -jinja2/defaults.py,sha256=eLMOE7JC52QwZBu5Gz4TPZqzoJy9IgV5EynL_pW7MUw,1057 -jinja2/environment.py,sha256=jzJmujSFtxb1HvITO4TdUCOOA-hSZx0gHrxeDZ2VE-M,48120 -jinja2/exceptions.py,sha256=Q9yZOUif-lhVj5BRw0ELjfBvEdBsB7xZobgOvC2qGy4,4428 -jinja2/ext.py,sha256=X-1zCiut1cuxIteKPkJr3jb6odlVE1jciO8RnrMniPE,25072 -jinja2/filters.py,sha256=R4x2flPfyzIjrtItzpGpK4LzBvx-NOlEXH9wD-ZBWtU,30115 -jinja2/lexer.py,sha256=QyiQwAQVEE2YREZJLcA04F3yqv0XOwBbSlWaFW4xJ20,28425 -jinja2/loaders.py,sha256=BgDCvmiB0gH_zPMf-6TMemqtJdrck3IyJ8g0kWUvFa0,17380 -jinja2/meta.py,sha256=cxAOtMuSWWSQX2H8zhYsAtjNwRcNB8Zvs06Y-JlWnbk,4198 -jinja2/nodes.py,sha256=YN6hfFa0WlfToG2r-Q-yhUkAUp0O9l8KulK53mOAVUo,28954 -jinja2/optimizer.py,sha256=bNNKbo5SC5FBUm9dvP-I3GkiXZYBYIER7_g9hK77ZVI,2302 -jinja2/parser.py,sha256=pjLfkZDg2IKJKt_ixNosV-RzwAja5GWYuVeBQumIRns,35442 -jinja2/runtime.py,sha256=Ct36Q9-gVmKer45syS4j3thQ15T_DnLDh6CqvTcnPwQ,22530 -jinja2/sandbox.py,sha256=qgH4CoBsF5NwGj0krqsCOw8sg2mXmfpZKnvmZEE-da4,13327 -jinja2/tests.py,sha256=znB0L_k6wdKp_lQJvxboXwUXDy1HhFe5SSA888tHt_w,4131 -jinja2/utils.py,sha256=pjbOhQJ5NYexu2MbjA66nBibudUkYcQRZbxvbYE0tFk,16560 -jinja2/visitor.py,sha256=3hEAYD26xS_JiJBf4RfcqYPpiuR6efOH8Hh6om59eU8,3316 -Jinja2-2.8.dist-info/DESCRIPTION.rst,sha256=CXIS1UnPSk5_lZBS6Lb8ko-3lqGfjsiUwNBLXCTj2lc,975 -Jinja2-2.8.dist-info/entry_points.txt,sha256=NdzVcOrqyNyKDxD09aERj__3bFx2paZhizFDsKmVhiA,72 -Jinja2-2.8.dist-info/METADATA,sha256=Vio5F8qaEVcGzaCV1rl8tIWEKsHUFSSSAfL0u9oMmGk,2061 -Jinja2-2.8.dist-info/metadata.json,sha256=4TsqsSBwGwy0C2xF_uRZHYsRn2W5Lv4NUMBjTnXPldM,1275 -Jinja2-2.8.dist-info/RECORD,, -Jinja2-2.8.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7 -Jinja2-2.8.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110 -Jinja2-2.8.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -jinja2/_compat.pyc,, -jinja2/defaults.pyc,, -jinja2/sandbox.pyc,, -jinja2/_stringdefs.pyc,, -jinja2/environment.pyc,, -jinja2/bccache.pyc,, -jinja2/runtime.pyc,, -jinja2/utils.pyc,, -jinja2/parser.pyc,, -jinja2/debug.pyc,, -jinja2/visitor.pyc,, -jinja2/ext.pyc,, -jinja2/lexer.pyc,, -jinja2/nodes.pyc,, -jinja2/meta.pyc,, -jinja2/compiler.pyc,, -jinja2/exceptions.pyc,, -jinja2/__init__.pyc,, -jinja2/optimizer.pyc,, -jinja2/constants.pyc,, -jinja2/tests.pyc,, -jinja2/loaders.pyc,, -jinja2/filters.pyc,, diff --git a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/WHEEL b/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/WHEEL deleted file mode 100644 index 9dff69d..0000000 --- a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.24.0) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/entry_points.txt b/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/entry_points.txt deleted file mode 100644 index 32e6b75..0000000 --- a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/entry_points.txt +++ /dev/null @@ -1,4 +0,0 @@ - - [babel.extractors] - jinja2 = jinja2.ext:babel_extract[i18n] - \ No newline at end of file diff --git a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/metadata.json b/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/metadata.json deleted file mode 100644 index f5b1ffa..0000000 --- a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"license": "BSD", "name": "Jinja2", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "summary": "A small but fast and easy to use stand-alone template engine written in pure python.", "run_requires": [{"requires": ["Babel (>=0.8)"], "extra": "i18n"}, {"requires": ["MarkupSafe"]}], "version": "2.8", "extensions": {"python.details": {"project_urls": {"Home": "http://jinja.pocoo.org/"}, "document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"role": "author", "email": "armin.ronacher@active-4.com", "name": "Armin Ronacher"}]}, "python.exports": {"babel.extractors": {"jinja2": "jinja2.ext:babel_extract [i18n]"}}}, "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", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Text Processing :: Markup :: HTML"], "extras": ["i18n"]} \ No newline at end of file diff --git a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/top_level.txt b/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/top_level.txt deleted file mode 100644 index 7f7afbf..0000000 --- a/venv/lib/python2.7/site-packages/Jinja2-2.8.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -jinja2 diff --git a/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/DESCRIPTION.rst b/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/DESCRIPTION.rst deleted file mode 100644 index 67256b9..0000000 --- a/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/DESCRIPTION.rst +++ /dev/null @@ -1,101 +0,0 @@ -MarkupSafe -========== - -Implements a unicode subclass that supports HTML strings: - ->>> from markupsafe import Markup, escape ->>> escape("") -Markup(u'<script>alert(document.cookie);</script>') ->>> tmpl = Markup("%s") ->>> tmpl % "Peter > Lustig" -Markup(u'Peter > Lustig') - -If you want to make an object unicode that is not yet unicode -but don't want to lose the taint information, you can use the -`soft_unicode` function. (On Python 3 you can also use `soft_str` which -is a different name for the same function). - ->>> from markupsafe import soft_unicode ->>> soft_unicode(42) -u'42' ->>> soft_unicode(Markup('foo')) -Markup(u'foo') - -HTML Representations --------------------- - -Objects can customize their HTML markup equivalent by overriding -the `__html__` function: - ->>> class Foo(object): -... def __html__(self): -... return 'Nice' -... ->>> escape(Foo()) -Markup(u'Nice') ->>> Markup(Foo()) -Markup(u'Nice') - -Silent Escapes --------------- - -Since MarkupSafe 0.10 there is now also a separate escape function -called `escape_silent` that returns an empty string for `None` for -consistency with other systems that return empty strings for `None` -when escaping (for instance Pylons' webhelpers). - -If you also want to use this for the escape method of the Markup -object, you can create your own subclass that does that:: - - from markupsafe import Markup, escape_silent as escape - - class SilentMarkup(Markup): - __slots__ = () - - @classmethod - def escape(cls, s): - return cls(escape(s)) - -New-Style String Formatting ---------------------------- - -Starting with MarkupSafe 0.21 new style string formats from Python 2.6 and -3.x are now fully supported. Previously the escape behavior of those -functions was spotty at best. The new implementations operates under the -following algorithm: - -1. if an object has an ``__html_format__`` method it is called as - replacement for ``__format__`` with the format specifier. It either - has to return a string or markup object. -2. if an object has an ``__html__`` method it is called. -3. otherwise the default format system of Python kicks in and the result - is HTML escaped. - -Here is how you can implement your own formatting:: - - class User(object): - - def __init__(self, id, username): - self.id = id - self.username = username - - def __html_format__(self, format_spec): - if format_spec == 'link': - return Markup('{1}').format( - self.id, - self.__html__(), - ) - elif format_spec: - raise ValueError('Invalid format spec') - return self.__html__() - - def __html__(self): - return Markup('{0}').format(self.username) - -And to format that user: - ->>> user = User(1, 'foo') ->>> Markup('

User: {0:link}').format(user) -Markup(u'

User: foo') - - diff --git a/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/INSTALLER b/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/METADATA b/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/METADATA deleted file mode 100644 index d06751b..0000000 --- a/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/METADATA +++ /dev/null @@ -1,121 +0,0 @@ -Metadata-Version: 2.0 -Name: MarkupSafe -Version: 0.23 -Summary: Implements a XML/HTML/XHTML Markup safe string for Python -Home-page: http://github.com/mitsuhiko/markupsafe -Author: Armin Ronacher -Author-email: armin.ronacher@active-4.com -License: BSD -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 :: 3 -Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Text Processing :: Markup :: HTML - -MarkupSafe -========== - -Implements a unicode subclass that supports HTML strings: - ->>> from markupsafe import Markup, escape ->>> escape("") -Markup(u'<script>alert(document.cookie);</script>') ->>> tmpl = Markup("%s") ->>> tmpl % "Peter > Lustig" -Markup(u'Peter > Lustig') - -If you want to make an object unicode that is not yet unicode -but don't want to lose the taint information, you can use the -`soft_unicode` function. (On Python 3 you can also use `soft_str` which -is a different name for the same function). - ->>> from markupsafe import soft_unicode ->>> soft_unicode(42) -u'42' ->>> soft_unicode(Markup('foo')) -Markup(u'foo') - -HTML Representations --------------------- - -Objects can customize their HTML markup equivalent by overriding -the `__html__` function: - ->>> class Foo(object): -... def __html__(self): -... return 'Nice' -... ->>> escape(Foo()) -Markup(u'Nice') ->>> Markup(Foo()) -Markup(u'Nice') - -Silent Escapes --------------- - -Since MarkupSafe 0.10 there is now also a separate escape function -called `escape_silent` that returns an empty string for `None` for -consistency with other systems that return empty strings for `None` -when escaping (for instance Pylons' webhelpers). - -If you also want to use this for the escape method of the Markup -object, you can create your own subclass that does that:: - - from markupsafe import Markup, escape_silent as escape - - class SilentMarkup(Markup): - __slots__ = () - - @classmethod - def escape(cls, s): - return cls(escape(s)) - -New-Style String Formatting ---------------------------- - -Starting with MarkupSafe 0.21 new style string formats from Python 2.6 and -3.x are now fully supported. Previously the escape behavior of those -functions was spotty at best. The new implementations operates under the -following algorithm: - -1. if an object has an ``__html_format__`` method it is called as - replacement for ``__format__`` with the format specifier. It either - has to return a string or markup object. -2. if an object has an ``__html__`` method it is called. -3. otherwise the default format system of Python kicks in and the result - is HTML escaped. - -Here is how you can implement your own formatting:: - - class User(object): - - def __init__(self, id, username): - self.id = id - self.username = username - - def __html_format__(self, format_spec): - if format_spec == 'link': - return Markup('{1}').format( - self.id, - self.__html__(), - ) - elif format_spec: - raise ValueError('Invalid format spec') - return self.__html__() - - def __html__(self): - return Markup('{0}').format(self.username) - -And to format that user: - ->>> user = User(1, 'foo') ->>> Markup('

User: {0:link}').format(user) -Markup(u'

User: foo') - - diff --git a/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/RECORD b/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/RECORD deleted file mode 100644 index 6306e76..0000000 --- a/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/RECORD +++ /dev/null @@ -1,19 +0,0 @@ -MarkupSafe-0.23.dist-info/DESCRIPTION.rst,sha256=VnEbwPneiOkqh-nzxb0DUiGlcVGHuaDQjsNBLi-yNYw,3091 -MarkupSafe-0.23.dist-info/METADATA,sha256=g-KikeSr9J7vagkJoCt0ViT2ORy9O4NYV7XtRu1Pni8,3879 -MarkupSafe-0.23.dist-info/RECORD,, -MarkupSafe-0.23.dist-info/WHEEL,sha256=LgENoDL_AkiRWbJ7HCJLnTXVvHl9ZtXZ7UZmjmcCeN0,105 -MarkupSafe-0.23.dist-info/metadata.json,sha256=y9yFyMJYU3UIRc5KfHIY6A3Z6nvJ2lXva5-7Ts2lsvY,901 -MarkupSafe-0.23.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 -markupsafe/__init__.py,sha256=zFQpANILi3mCCALiPd6ZJdlW6ibu_hTKzikMXKXVtaM,10338 -markupsafe/_compat.py,sha256=r1HE0CpcAZeb-AiTV9wITR91PeLHn0CzZ_XHkYoozpI,565 -markupsafe/_constants.py,sha256=U_xybFQsyXKCgHSfranJnFzo-z9nn9fuBeSk243sE5Q,4795 -markupsafe/_native.py,sha256=E2Un1ysOf-w45d18YCj8UelT5UP7Vt__IuFPYJ7YRIs,1187 -markupsafe/_speedups.c,sha256=gZwPEM_0zcbAzJjPuPYXk97R67QR1uUGtDvOPsvirCA,5939 -markupsafe/_speedups.so,sha256=fQbdBe1y1gzgoPajRdqAcU39EvIJQ_j_ZrgO3v2zIQk,13344 -markupsafe/tests.py,sha256=RLI4eYI0ICNZwkoN638VHXf_fDu4d_jnvbGr22j58Ng,6107 -MarkupSafe-0.23.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -markupsafe/_constants.pyc,, -markupsafe/__init__.pyc,, -markupsafe/_native.pyc,, -markupsafe/_compat.pyc,, -markupsafe/tests.pyc,, diff --git a/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/WHEEL b/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/WHEEL deleted file mode 100644 index d15279d..0000000 --- a/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.29.0) -Root-Is-Purelib: false -Tag: cp27-cp27mu-linux_x86_64 - diff --git a/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/metadata.json b/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/metadata.json deleted file mode 100644 index 21fcd69..0000000 --- a/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"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 :: 3", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Text Processing :: Markup :: HTML"], "extensions": {"python.details": {"contacts": [{"email": "armin.ronacher@active-4.com", "name": "Armin Ronacher", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/mitsuhiko/markupsafe"}}}, "generator": "bdist_wheel (0.29.0)", "license": "BSD", "metadata_version": "2.0", "name": "MarkupSafe", "summary": "Implements a XML/HTML/XHTML Markup safe string for Python", "version": "0.23"} \ No newline at end of file diff --git a/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/top_level.txt b/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/top_level.txt deleted file mode 100644 index 75bf729..0000000 --- a/venv/lib/python2.7/site-packages/MarkupSafe-0.23.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -markupsafe diff --git a/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/DESCRIPTION.rst b/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/DESCRIPTION.rst deleted file mode 100644 index 2a6e8bb..0000000 --- a/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/DESCRIPTION.rst +++ /dev/null @@ -1,54 +0,0 @@ -Werkzeug -======== - -Werkzeug started as simple collection of various utilities for WSGI -applications and has become one of the most advanced WSGI utility -modules. It includes a powerful debugger, full featured request and -response objects, HTTP utilities to handle entity tags, cache control -headers, HTTP dates, cookie handling, file uploads, a powerful URL -routing system and a bunch of community contributed addon modules. - -Werkzeug is unicode aware and doesn't enforce a specific template -engine, database adapter or anything else. It doesn't even enforce -a specific way of handling requests and leaves all that up to the -developer. It's most useful for end user applications which should work -on as many server environments as possible (such as blogs, wikis, -bulletin boards, etc.). - -Details and example applications are available on the -`Werkzeug website `_. - - -Features --------- - -- unicode awareness - -- request and response objects - -- various utility functions for dealing with HTTP headers such as - `Accept` and `Cache-Control` headers. - -- thread local objects with proper cleanup at request end - -- an interactive debugger - -- A simple WSGI server with support for threading and forking - with an automatic reloader. - -- a flexible URL routing system with REST support. - -- fully WSGI compatible - - -Development Version -------------------- - -The Werkzeug development version can be installed by cloning the git -repository from `github`_:: - - git clone git@github.com:mitsuhiko/werkzeug.git - -.. _github: http://github.com/mitsuhiko/werkzeug - - diff --git a/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/INSTALLER b/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/METADATA b/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/METADATA deleted file mode 100644 index d8a1764..0000000 --- a/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/METADATA +++ /dev/null @@ -1,79 +0,0 @@ -Metadata-Version: 2.0 -Name: Werkzeug -Version: 0.11.10 -Summary: The Swiss Army knife of Python web development -Home-page: http://werkzeug.pocoo.org/ -Author: Armin Ronacher -Author-email: armin.ronacher@active-4.com -License: BSD -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: Topic :: Internet :: WWW/HTTP :: Dynamic Content -Classifier: Topic :: Software Development :: Libraries :: Python Modules - -Werkzeug -======== - -Werkzeug started as simple collection of various utilities for WSGI -applications and has become one of the most advanced WSGI utility -modules. It includes a powerful debugger, full featured request and -response objects, HTTP utilities to handle entity tags, cache control -headers, HTTP dates, cookie handling, file uploads, a powerful URL -routing system and a bunch of community contributed addon modules. - -Werkzeug is unicode aware and doesn't enforce a specific template -engine, database adapter or anything else. It doesn't even enforce -a specific way of handling requests and leaves all that up to the -developer. It's most useful for end user applications which should work -on as many server environments as possible (such as blogs, wikis, -bulletin boards, etc.). - -Details and example applications are available on the -`Werkzeug website `_. - - -Features --------- - -- unicode awareness - -- request and response objects - -- various utility functions for dealing with HTTP headers such as - `Accept` and `Cache-Control` headers. - -- thread local objects with proper cleanup at request end - -- an interactive debugger - -- A simple WSGI server with support for threading and forking - with an automatic reloader. - -- a flexible URL routing system with REST support. - -- fully WSGI compatible - - -Development Version -------------------- - -The Werkzeug development version can be installed by cloning the git -repository from `github`_:: - - git clone git@github.com:mitsuhiko/werkzeug.git - -.. _github: http://github.com/mitsuhiko/werkzeug - - diff --git a/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/RECORD b/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/RECORD deleted file mode 100644 index b9b77f6..0000000 --- a/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/RECORD +++ /dev/null @@ -1,94 +0,0 @@ -Werkzeug-0.11.10.dist-info/DESCRIPTION.rst,sha256=5sTwZ_Sj5aeEN8mlcOdNJ_ng40HiGazGmILLyTMX8o0,1595 -Werkzeug-0.11.10.dist-info/METADATA,sha256=vweH07PHdEwMIeOU7fvKXKi_6zY5e6d_dfVCgqT--Aw,2600 -Werkzeug-0.11.10.dist-info/RECORD,, -Werkzeug-0.11.10.dist-info/WHEEL,sha256=GrqQvamwgBV4nLoJe0vhYRSWzWsx7xjlt74FT0SWYfE,110 -Werkzeug-0.11.10.dist-info/metadata.json,sha256=PC5ATsCVx4EUlD8uEGHEhx-aTnCr9GA8vfWXT3CLwQw,1096 -Werkzeug-0.11.10.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9 -werkzeug/__init__.py,sha256=BDx17HnxLmB4L8OSE0Bkj3DjDTO6xLu6_nNck8QiRaw,6920 -werkzeug/_compat.py,sha256=8c4U9o6A_TR9nKCcTbpZNxpqCXcXDVIbFawwKM2s92c,6311 -werkzeug/_internal.py,sha256=IEScSoFtQ8KqFH_2ubdfscNAdQ2RIysyVupI5BR9W2U,13709 -werkzeug/_reloader.py,sha256=YQykMSQW7AlojJQ7qOlgNaXw5_CNjf9yzxplwzVdL7Q,8336 -werkzeug/datastructures.py,sha256=5CLVMLROGMoB0dSdQ7aBabma6IC_vxIx77VnMoFFVyc,87447 -werkzeug/exceptions.py,sha256=c-3fKHItsPvC52X_NwBNLcmGXR30h0WP5ynPSwCqPiw,18733 -werkzeug/filesystem.py,sha256=hHWeWo_gqLMzTRfYt8-7n2wWcWUNTnDyudQDLOBEICE,2175 -werkzeug/formparser.py,sha256=90D5Urp8Ghrzw32kAs090G0nXPYlU73NeAzPlQFMVrY,21296 -werkzeug/http.py,sha256=sqNaMmLBvi16cPoVRiBviwnVOb1bAQ1lGTrvfCdaQrY,35264 -werkzeug/local.py,sha256=4Q5gwHQJhfhZFqTR8iQDs2VHohpR1OEsP4YTwn7rt7w,14275 -werkzeug/posixemulation.py,sha256=xEF2Bxc-vUCPkiu4IbfWVd3LW7DROYAT-ExW6THqyzw,3519 -werkzeug/routing.py,sha256=TqiZD5HkwdLBnKBUjC5PlytzXmpczQC5dz54VfQzMOw,66350 -werkzeug/script.py,sha256=DwaVDcXdaOTffdNvlBdLitxWXjKaRVT32VbhDtljFPY,11365 -werkzeug/security.py,sha256=tuVc22OqoHV5K-TrYJmynCJJa12aUt9BQ3wR_vEPQ34,8971 -werkzeug/serving.py,sha256=uRUqXuA-Dw2MRA-d232cK_034-taldoj66fEFrtin7k,27736 -werkzeug/test.py,sha256=nan0aDi3g5hyUzWCtaN3XL9HrbIsNNNgMNjwpfM6qMc,34152 -werkzeug/testapp.py,sha256=3HQRW1sHZKXuAjCvFMet4KXtQG3loYTFnvn6LWt-4zI,9396 -werkzeug/urls.py,sha256=fSbI4Gb29_p02Zk21VAZQRN1QdOVY9CNTgpb2rbajNQ,36710 -werkzeug/useragents.py,sha256=uqpgPcJ5BfcCVh9nPIIl2r3duIrIuENmrbRqbAMmPDk,5418 -werkzeug/utils.py,sha256=lkybtv_mq35zV1qhelvEcILTzrMUwZ9yon6E8XwapJE,22972 -werkzeug/wrappers.py,sha256=lKYevpKD1-quk9Cop7bsFxt1eWJxU3h33HCnOI_YzSU,77011 -werkzeug/wsgi.py,sha256=S8R3pBGPlBK67s-d6Wa93nhzG27WjfcHs_ZBGIAQCxM,39573 -werkzeug/contrib/__init__.py,sha256=f7PfttZhbrImqpr5Ezre8CXgwvcGUJK7zWNpO34WWrw,623 -werkzeug/contrib/atom.py,sha256=rvijBrphjMzVObfuCR6ddu6aLwI_SiNiudu64OSTh4Q,15588 -werkzeug/contrib/cache.py,sha256=4W2WCT9Hw6HEU8yME9GuU4Xf8e50r2K84ASMxhLb6tY,27983 -werkzeug/contrib/fixers.py,sha256=MtN_YmENxoTsGvXGGERmtbQ62LaeFc5I2d1YifXNENA,10183 -werkzeug/contrib/iterio.py,sha256=pTX36rYCKO_9IEoB5sIN5cFSYszI9zdx6YhquWovcPY,10814 -werkzeug/contrib/jsrouting.py,sha256=QTmgeDoKXvNK02KzXgx9lr3cAH6fAzpwF5bBdPNvJPs,8564 -werkzeug/contrib/limiter.py,sha256=iS8-ahPZ-JLRnmfIBzxpm7O_s3lPsiDMVWv7llAIDCI,1334 -werkzeug/contrib/lint.py,sha256=XDKYx0ELn9k18xRn4SiAsCgltCuN4yLjzxnCN8tG_eM,12490 -werkzeug/contrib/profiler.py,sha256=ISwCWvwVyGpDLRBRpLjo_qUWma6GXYBrTAco4PEQSHY,5151 -werkzeug/contrib/securecookie.py,sha256=X-Ao_0NRDveW6K1Fhe4U42hHWBW8esCpA3VcBDpzWIk,12206 -werkzeug/contrib/sessions.py,sha256=uAPcnyxaxEla-bUA13gKc3KK4mwSagdzbCZzyKl3PeE,12577 -werkzeug/contrib/testtools.py,sha256=G9xN-qeihJlhExrIZMCahvQOIDxdL9NiX874jiiHFMs,2453 -werkzeug/contrib/wrappers.py,sha256=Uv5FRO5OqKwOsNgkW2-FRcw0vUDe3uiaivjPNYWNfAk,10337 -werkzeug/debug/__init__.py,sha256=qQT5YnOv9Eov9Jt5eLtP6MOqwpmo-tORJ6HcQmmnvro,17271 -werkzeug/debug/console.py,sha256=B7uAu9Rk60siDnGlEt-A_q1ZR4zCtmxx5itg3X-BOxo,5599 -werkzeug/debug/repr.py,sha256=NaoB89aHb0vuvdSWels-GWdeGDZp76uE4uSNZPX1jAM,9354 -werkzeug/debug/tbtools.py,sha256=L5P5TkGEHc_Bc5duNosP6D4CNe7ieTo1oiPX8nKQdek,18402 -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=PEMBoNuD6fUaNou8Km_ZvVmFcIA3z3k3jSEMWLW-cA0,6187 -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=7x1s8olZO1XHalqD4M9MWn9vRqQkA635S9_6zRoe220,6231 -werkzeug/debug/shared/ubuntu.ttf,sha256=1eaHFyepmy4FyDvjLVzpITrGEBu_CZYY94jE0nED1c0,70220 -Werkzeug-0.11.10.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -werkzeug/_reloader.pyc,, -werkzeug/filesystem.pyc,, -werkzeug/contrib/testtools.pyc,, -werkzeug/formparser.pyc,, -werkzeug/_compat.pyc,, -werkzeug/posixemulation.pyc,, -werkzeug/wsgi.pyc,, -werkzeug/serving.pyc,, -werkzeug/contrib/__init__.pyc,, -werkzeug/contrib/iterio.pyc,, -werkzeug/test.pyc,, -werkzeug/__init__.pyc,, -werkzeug/contrib/limiter.pyc,, -werkzeug/debug/tbtools.pyc,, -werkzeug/contrib/sessions.pyc,, -werkzeug/contrib/securecookie.pyc,, -werkzeug/local.pyc,, -werkzeug/utils.pyc,, -werkzeug/_internal.pyc,, -werkzeug/security.pyc,, -werkzeug/contrib/cache.pyc,, -werkzeug/script.pyc,, -werkzeug/routing.pyc,, -werkzeug/wrappers.pyc,, -werkzeug/contrib/jsrouting.pyc,, -werkzeug/contrib/fixers.pyc,, -werkzeug/contrib/profiler.pyc,, -werkzeug/debug/console.pyc,, -werkzeug/debug/__init__.pyc,, -werkzeug/datastructures.pyc,, -werkzeug/http.pyc,, -werkzeug/urls.pyc,, -werkzeug/contrib/lint.pyc,, -werkzeug/contrib/wrappers.pyc,, -werkzeug/exceptions.pyc,, -werkzeug/contrib/atom.pyc,, -werkzeug/testapp.pyc,, -werkzeug/debug/repr.pyc,, -werkzeug/useragents.pyc,, diff --git a/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/WHEEL b/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/WHEEL deleted file mode 100644 index 0de529b..0000000 --- a/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -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/python2.7/site-packages/Werkzeug-0.11.10.dist-info/metadata.json b/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/metadata.json deleted file mode 100644 index ac71ca2..0000000 --- a/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"generator": "bdist_wheel (0.26.0)", "summary": "The Swiss Army knife of Python web development", "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", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules"], "extensions": {"python.details": {"project_urls": {"Home": "http://werkzeug.pocoo.org/"}, "contacts": [{"email": "armin.ronacher@active-4.com", "name": "Armin Ronacher", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}}}, "license": "BSD", "metadata_version": "2.0", "name": "Werkzeug", "platform": "any", "version": "0.11.10"} \ No newline at end of file diff --git a/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/top_level.txt b/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/top_level.txt deleted file mode 100644 index 6fe8da8..0000000 --- a/venv/lib/python2.7/site-packages/Werkzeug-0.11.10.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -werkzeug diff --git a/venv/lib/python2.7/site-packages/click-6.6.dist-info/DESCRIPTION.rst b/venv/lib/python2.7/site-packages/click-6.6.dist-info/DESCRIPTION.rst deleted file mode 100644 index e118723..0000000 --- a/venv/lib/python2.7/site-packages/click-6.6.dist-info/DESCRIPTION.rst +++ /dev/null @@ -1,3 +0,0 @@ -UNKNOWN - - diff --git a/venv/lib/python2.7/site-packages/click-6.6.dist-info/INSTALLER b/venv/lib/python2.7/site-packages/click-6.6.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/lib/python2.7/site-packages/click-6.6.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/lib/python2.7/site-packages/click-6.6.dist-info/METADATA b/venv/lib/python2.7/site-packages/click-6.6.dist-info/METADATA deleted file mode 100644 index ec533a2..0000000 --- a/venv/lib/python2.7/site-packages/click-6.6.dist-info/METADATA +++ /dev/null @@ -1,16 +0,0 @@ -Metadata-Version: 2.0 -Name: click -Version: 6.6 -Summary: A simple wrapper around optparse for powerful command line utilities. -Home-page: http://github.com/mitsuhiko/click -Author: Armin Ronacher -Author-email: armin.ronacher@active-4.com -License: UNKNOWN -Platform: UNKNOWN -Classifier: License :: OSI Approved :: BSD License -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 - -UNKNOWN - - diff --git a/venv/lib/python2.7/site-packages/click-6.6.dist-info/RECORD b/venv/lib/python2.7/site-packages/click-6.6.dist-info/RECORD deleted file mode 100644 index 615da13..0000000 --- a/venv/lib/python2.7/site-packages/click-6.6.dist-info/RECORD +++ /dev/null @@ -1,41 +0,0 @@ -click/__init__.py,sha256=-BDiJ4G3FXy1nn7Lt9UTIYdKb7SZTHnTm9yv2WQbHm0,2858 -click/_bashcomplete.py,sha256=J8L3TvE23YVicEf5LQdiN5pCS3aAquP8D8897Jp_PvE,2423 -click/_compat.py,sha256=xP-n7Tkuof1I6aEzcR6A88vusa-Lu6gpkPxnO-KY0pA,20706 -click/_termui_impl.py,sha256=BbBPaw2nDEmNYgjBl_JlxUOSAzFexO0jL3z0OY-Z6BM,16377 -click/_textwrap.py,sha256=gwS4m7bdQiJnzaDG8osFcRb-5vn4t4l2qSCy-5csCEc,1198 -click/_unicodefun.py,sha256=vK1_Y_d29nxYhE7_QF8y8-DLjG_fYDhiT-LlcmiHVeE,4265 -click/_winconsole.py,sha256=MzG46DEYPoRyx4SO7EIhFuFZHESgooAfJLIukbB6p5c,7790 -click/core.py,sha256=PFAK6hjAiM8fcXxdHNwaDKINSoHYCYfLV8n9TdodYS4,70085 -click/decorators.py,sha256=y7CX2needh8iRWafj-QS_hGQFsN24eyXAhx5Y2ATwas,10941 -click/exceptions.py,sha256=rOa0pP3PbSy0_AAPOW9irBEM8AJ3BySN-4z2VUwFVo4,6788 -click/formatting.py,sha256=eh-cypTUAhpI3HD-K4ZpR3vCiURIO62xXvKkR3tNUTM,8889 -click/globals.py,sha256=PAgnKvGxq4YuEIldw3lgYOGBLYwsyxnm1IByBX3BFXo,1515 -click/parser.py,sha256=i01xgYuIA6AwQWEXjshwHSwnTR3gUep4FxJIfyW4ta4,15510 -click/termui.py,sha256=Bp99MSWQtyoWe1_7HggDmA77n--3KLxu7NsZMFMaCUo,21008 -click/testing.py,sha256=LjWcVtxA5vMdWx5ev7KSjU4vcTlxZiSDrP7tOV1nCxk,11004 -click/types.py,sha256=ZGb2lmFs5Vwd9loTRIMbGcqhPVOql8mGoBhWBRT6V4E,18864 -click/utils.py,sha256=1jalPlkUU28JReTEQeeSFtbJd-SirYWBNfjtELBKzT4,14916 -click-6.6.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 -click-6.6.dist-info/METADATA,sha256=gYQxaGWBuXfQy0NGVARhka10cSm9b_6XF6WeD7k3_Rg,424 -click-6.6.dist-info/RECORD,, -click-6.6.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 -click-6.6.dist-info/metadata.json,sha256=6QQvh7-Y7ZLMEiYZVIYzVKGeKPmzdxcgSIioWfhp9DE,568 -click-6.6.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 -click-6.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -click/_unicodefun.pyc,, -click/_compat.pyc,, -click/_termui_impl.pyc,, -click/_textwrap.pyc,, -click/formatting.pyc,, -click/exceptions.pyc,, -click/decorators.pyc,, -click/parser.pyc,, -click/testing.pyc,, -click/utils.pyc,, -click/_bashcomplete.pyc,, -click/types.pyc,, -click/termui.pyc,, -click/core.pyc,, -click/_winconsole.pyc,, -click/__init__.pyc,, -click/globals.pyc,, diff --git a/venv/lib/python2.7/site-packages/click-6.6.dist-info/WHEEL b/venv/lib/python2.7/site-packages/click-6.6.dist-info/WHEEL deleted file mode 100644 index 8b6dd1b..0000000 --- a/venv/lib/python2.7/site-packages/click-6.6.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.29.0) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/venv/lib/python2.7/site-packages/click-6.6.dist-info/metadata.json b/venv/lib/python2.7/site-packages/click-6.6.dist-info/metadata.json deleted file mode 100644 index ddf94ca..0000000 --- a/venv/lib/python2.7/site-packages/click-6.6.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"classifiers": ["License :: OSI Approved :: BSD License", "Programming Language :: Python", "Programming Language :: Python :: 3"], "extensions": {"python.details": {"contacts": [{"email": "armin.ronacher@active-4.com", "name": "Armin Ronacher", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/mitsuhiko/click"}}}, "generator": "bdist_wheel (0.29.0)", "metadata_version": "2.0", "name": "click", "summary": "A simple wrapper around optparse for powerful command line utilities.", "version": "6.6"} \ No newline at end of file diff --git a/venv/lib/python2.7/site-packages/click-6.6.dist-info/top_level.txt b/venv/lib/python2.7/site-packages/click-6.6.dist-info/top_level.txt deleted file mode 100644 index dca9a90..0000000 --- a/venv/lib/python2.7/site-packages/click-6.6.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -click diff --git a/venv/lib/python2.7/site-packages/click/__init__.py b/venv/lib/python2.7/site-packages/click/__init__.py deleted file mode 100644 index 3a7e3ee..0000000 --- a/venv/lib/python2.7/site-packages/click/__init__.py +++ /dev/null @@ -1,98 +0,0 @@ -# -*- coding: utf-8 -*- -""" - click - ~~~~~ - - Click is a simple Python module that wraps the stdlib's 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. - - In case optparse ever gets removed from the stdlib, it will be shipped by - this module. - - :copyright: (c) 2014 by Armin Ronacher. - :license: BSD, see LICENSE 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, \ - STRING, INT, FLOAT, BOOL, UUID, UNPROCESSED - -# 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', 'STRING', - 'INT', 'FLOAT', 'BOOL', 'UUID', 'UNPROCESSED', - - # 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__ = '6.6' diff --git a/venv/lib/python2.7/site-packages/click/_bashcomplete.py b/venv/lib/python2.7/site-packages/click/_bashcomplete.py deleted file mode 100644 index d97b66a..0000000 --- a/venv/lib/python2.7/site-packages/click/_bashcomplete.py +++ /dev/null @@ -1,83 +0,0 @@ -import os -import re -from .utils import echo -from .parser import split_arg_string -from .core import MultiCommand, Option - - -COMPLETION_SCRIPT = ''' -%(complete_func)s() { - COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \\ - COMP_CWORD=$COMP_CWORD \\ - %(autocomplete_var)s=complete $1 ) ) - return 0 -} - -complete -F %(complete_func)s -o default %(script_names)s -''' - -_invalid_ident_char_re = re.compile(r'[^a-zA-Z0-9_]') - - -def get_completion_script(prog_name, complete_var): - cf_name = _invalid_ident_char_re.sub('', prog_name.replace('-', '_')) - return (COMPLETION_SCRIPT % { - 'complete_func': '_%s_completion' % cf_name, - 'script_names': prog_name, - 'autocomplete_var': complete_var, - }).strip() + ';' - - -def resolve_ctx(cli, prog_name, args): - ctx = cli.make_context(prog_name, args, resilient_parsing=True) - while ctx.args + ctx.protected_args and isinstance(ctx.command, MultiCommand): - a = ctx.args + ctx.protected_args - cmd = ctx.command.get_command(ctx, a[0]) - if cmd is None: - return None - ctx = cmd.make_context(a[0], a[1:], parent=ctx, resilient_parsing=True) - return ctx - - -def get_choices(cli, prog_name, args, incomplete): - ctx = resolve_ctx(cli, prog_name, args) - if ctx is None: - return - - choices = [] - if incomplete and not incomplete[:1].isalnum(): - for param in ctx.command.params: - if not isinstance(param, Option): - continue - choices.extend(param.opts) - choices.extend(param.secondary_opts) - elif isinstance(ctx.command, MultiCommand): - choices.extend(ctx.command.list_commands(ctx)) - - for item in choices: - if item.startswith(incomplete): - yield item - - -def do_complete(cli, prog_name): - 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) - - return True - - -def bashcomplete(cli, prog_name, complete_var, complete_instr): - if complete_instr == 'source': - echo(get_completion_script(prog_name, complete_var)) - return True - elif complete_instr == 'complete': - return do_complete(cli, prog_name) - return False diff --git a/venv/lib/python2.7/site-packages/click/_compat.py b/venv/lib/python2.7/site-packages/click/_compat.py deleted file mode 100644 index 6bbebd3..0000000 --- a/venv/lib/python2.7/site-packages/click/_compat.py +++ /dev/null @@ -1,642 +0,0 @@ -import re -import io -import os -import sys -import codecs -from weakref import WeakKeyDictionary - - -PY2 = sys.version_info[0] == 2 -WIN = sys.platform.startswith('win') -DEFAULT_COLUMNS = 80 - - -_ansi_re = re.compile('\033\[((?:\d|;)*)([a-zA-Z])') - - -def get_filesystem_encoding(): - return sys.getfilesystemencoding() or sys.getdefaultencoding() - - -def _make_text_stream(stream, encoding, errors): - if encoding is None: - encoding = get_best_encoding(stream) - if errors is None: - errors = 'replace' - return _NonClosingTextIOWrapper(stream, encoding, errors, - line_buffering=True) - - -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, **extra): - self._stream = stream = _FixupStream(stream) - 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. - """ - - def __init__(self, stream): - self._stream = stream - - 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): - 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): - 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) - 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. - if WIN: - import msvcrt - def set_binary_mode(f): - try: - fileno = f.fileno() - except Exception: - pass - else: - msvcrt.setmode(fileno, os.O_BINARY) - return f - else: - set_binary_mode = lambda x: x - - 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(): - return set_binary_mode(sys.stdout) - - def get_binary_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) - - 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 _make_text_stream(sys.stdout, encoding, errors) - - 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 _make_text_stream(sys.stderr, encoding, errors) - - 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,) - 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): - 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) - - def _force_correct_text_writer(text_writer, encoding, errors): - 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) - - 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) - - 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) - - 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) - - 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 'w' in mode: - 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, 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 - - 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 - - -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: - 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/python2.7/site-packages/click/_termui_impl.py b/venv/lib/python2.7/site-packages/click/_termui_impl.py deleted file mode 100644 index a2c1eb0..0000000 --- a/venv/lib/python2.7/site-packages/click/_termui_impl.py +++ /dev/null @@ -1,547 +0,0 @@ -""" - 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: (c) 2014 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -import os -import sys -import time -import math -from ._compat import _default_text_stdout, range_type, PY2, isatty, \ - open_stream, strip_ansi, term_len, get_best_encoding, WIN -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 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, long)) 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 - - 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 - - def render_finish(self): - if self.is_hidden: - 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 = self.eta + 1 - 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_progress_line(self): - show_percent = self.show_percent - - info_bits = [] - 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) - if show_percent is None: - show_percent = not self.show_pos - else: - if 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) - - 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': bar, - 'info': self.info_sep.join(info_bits) - }).rstrip() - - def render_progress(self): - from .termui import get_terminal_size - nl = False - - if self.is_hidden: - buf = [self.label] - nl = True - else: - 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: - self._last_line = line - echo(line, file=self.file, color=self.color, nl=nl) - 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 = self.avg[-6:] + [-(self.start - time.time()) / (self.pos)] - - 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 next(self): - if self.is_hidden: - return next(self.iter) - try: - rv = next(self.iter) - self.current_item = rv - except StopIteration: - self.finish() - self.render_progress() - raise StopIteration() - else: - self.update(1) - return rv - - if not PY2: - __next__ = next - del next - - -def pager(text, 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, text, color) - pager_cmd = (os.environ.get('PAGER', None) or '').strip() - if pager_cmd: - if WIN: - return _tempfilepager(text, pager_cmd, color) - return _pipepager(text, pager_cmd, color) - if os.environ.get('TERM') in ('dumb', 'emacs'): - return _nullpager(stdout, text, color) - if WIN or sys.platform.startswith('os2'): - return _tempfilepager(text, 'more <', color) - if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0: - return _pipepager(text, '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(text, 'more', color) - return _nullpager(stdout, text, color) - finally: - os.unlink(filename) - - -def _pipepager(text, 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 - - if not color: - text = strip_ansi(text) - - c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, - env=env) - encoding = get_best_encoding(c.stdin) - try: - c.stdin.write(text.encode(encoding, 'replace')) - c.stdin.close() - except (IOError, KeyboardInterrupt): - pass - - # 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(text, cmd, color): - """Page through text by invoking a program on a temporary file.""" - import tempfile - filename = tempfile.mktemp() - 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, text, color): - """Simply print unformatted text. This is the ultimate fallback.""" - 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) - - 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 == '\x03': - raise KeyboardInterrupt() - if ch == '\x04': - raise EOFError() - - -if WIN: - import msvcrt - - def getchar(echo): - rv = msvcrt.getch() - if echo: - msvcrt.putchar(rv) - _translate_ch_to_exc(rv) - if PY2: - enc = getattr(sys.stdin, 'encoding', None) - if enc is not None: - rv = rv.decode(enc, 'replace') - else: - rv = rv.decode('cp1252', 'replace') - return rv -else: - import tty - import termios - - def getchar(echo): - 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) - ch = os.read(fd, 32) - if echo and isatty(sys.stdout): - sys.stdout.write(ch) - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - sys.stdout.flush() - if f is not None: - f.close() - except termios.error: - pass - _translate_ch_to_exc(ch) - return ch.decode(get_best_encoding(sys.stdin), 'replace') diff --git a/venv/lib/python2.7/site-packages/click/_textwrap.py b/venv/lib/python2.7/site-packages/click/_textwrap.py deleted file mode 100644 index 7e77603..0000000 --- a/venv/lib/python2.7/site-packages/click/_textwrap.py +++ /dev/null @@ -1,38 +0,0 @@ -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/python2.7/site-packages/click/_unicodefun.py b/venv/lib/python2.7/site-packages/click/_unicodefun.py deleted file mode 100644 index 24b7031..0000000 --- a/venv/lib/python2.7/site-packages/click/_unicodefun.py +++ /dev/null @@ -1,119 +0,0 @@ -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__ - 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 ' - 'http://click.pocoo.org/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 - rv = subprocess.Popen(['locale', '-a'], stdout=subprocess.PIPE, - stderr=subprocess.PIPE).communicate()[0] - 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 where\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. Either run this under Python 2 or ' - 'consult http://click.pocoo.org/python3/ for ' - 'mitigation steps.' + extra) diff --git a/venv/lib/python2.7/site-packages/click/_winconsole.py b/venv/lib/python2.7/site-packages/click/_winconsole.py deleted file mode 100644 index 9aed942..0000000 --- a/venv/lib/python2.7/site-packages/click/_winconsole.py +++ /dev/null @@ -1,273 +0,0 @@ -# -*- 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 click._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, - ) - - -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( - _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( - _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') - 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/python2.7/site-packages/click/core.py b/venv/lib/python2.7/site-packages/click/core.py deleted file mode 100644 index 33a527a..0000000 --- a/venv/lib/python2.7/site-packages/click/core.py +++ /dev/null @@ -1,1738 +0,0 @@ -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 make_str, make_default_short_help, echo, get_os_args -from .exceptions import ClickException, UsageError, BadParameter, Abort, \ - MissingParameter -from .termui import prompt, confirm -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 -from ._unicodefun import _check_for_unicode_literals, _verify_python3_env - - -_missing = object() - - -SUBCOMMAND_METAVAR = 'COMMAND [ARGS]...' -SUBCOMMANDS_METAVAR = 'COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...' - - -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): - sys.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"). This restriction was supposed to ' - 'be lifted in 6.0 but the fix was flawed. This ' - 'will be fixed in Click 7.0' % ( - 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. 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. - 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: - self.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 utiltiies 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.""" - sys.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 of 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 - 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 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. - """ - - 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): - 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 [] - self.help = help - self.epilog = epilog - self.options_metavar = options_metavar - if short_help is None and help: - short_help = make_default_short_help(help) - self.short_help = short_help - self.add_help_option = add_help_option - - 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) - parser.allow_interspersed_args = ctx.allow_interspersed_args - parser.ignore_unknown_options = ctx.ignore_unknown_options - 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 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(): - formatter.write_text(self.help) - - 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. - """ - 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. - """ - rows = [] - 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 - - help = cmd.short_help or '' - 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: - 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): - """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): - 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 - - @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 = ctx.lookup_default(self.name) - if value is None: - value = self.value_from_envvar(ctx) - 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: - 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 [] - - -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. - :param prompt: if set to `True` or a non empty string then the user will - be prompted for input if not set. 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_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, **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 - - # 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 - - # 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])) - name = possible_names[-1][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): - 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.default is not None and self.show_default: - extra.append('default: %s' % ( - ', '.join('%s' % d for d in self.default) - if isinstance(self.default, (list, tuple)) - else self.default, )) - 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, - hide_input=self.hide_input, - 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.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() - elif len(decls) == 2: - name, arg = decls - else: - raise TypeError('Arguments take exactly one or two ' - 'parameter declarations, got %d' % len(decls)) - return name, [arg], [] - - def get_usage_pieces(self, ctx): - return [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/python2.7/site-packages/click/decorators.py b/venv/lib/python2.7/site-packages/click/decorators.py deleted file mode 100644 index 9893452..0000000 --- a/venv/lib/python2.7/site-packages/click/decorators.py +++ /dev/null @@ -1,304 +0,0 @@ -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[1:], **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(), - callback=f, params=params, **attrs) - - -def command(name=None, cls=None, **attrs): - """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. - :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): - if 'help' in attrs: - attrs['help'] = inspect.cleandoc(attrs['help']) - OptionClass = attrs.pop('cls', Option) - _param_memo(f, OptionClass(param_decls, **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: - module = sys._getframe(1).f_globals.get('__name__') - 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/python2.7/site-packages/click/exceptions.py b/venv/lib/python2.7/site-packages/click/exceptions.py deleted file mode 100644 index 74a4542..0000000 --- a/venv/lib/python2.7/site-packages/click/exceptions.py +++ /dev/null @@ -1,201 +0,0 @@ -from ._compat import PY2, filename_to_ui, get_text_stderr -from .utils import echo - - -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): - if PY2: - if message is not None: - message = message.encode('utf-8') - Exception.__init__(self, message) - self.message = message - - def format_message(self): - return self.message - - 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 - - def show(self, file=None): - if file is None: - file = get_text_stderr() - color = None - if self.ctx is not None: - color = self.ctx.color - echo(self.ctx.get_usage() + '\n', 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.opts or [self.param.human_readable_name] - else: - return 'Invalid value: %s' % self.message - if isinstance(param_hint, (tuple, list)): - param_hint = ' / '.join('"%s"' % x for x in 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.opts or [self.param.human_readable_name] - else: - param_hint = None - if isinstance(param_hint, (tuple, list)): - param_hint = ' / '.join('"%s"' % x for x in 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 - """ - - def __init__(self, message, ctx=None): - UsageError.__init__(self, message, ctx) - - -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.""" diff --git a/venv/lib/python2.7/site-packages/click/formatting.py b/venv/lib/python2.7/site-packages/click/formatting.py deleted file mode 100644 index a3d6a4d..0000000 --- a/venv/lib/python2.7/site-packages/click/formatting.py +++ /dev/null @@ -1,256 +0,0 @@ -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/python2.7/site-packages/click/globals.py b/venv/lib/python2.7/site-packages/click/globals.py deleted file mode 100644 index 14338e6..0000000 --- a/venv/lib/python2.7/site-packages/click/globals.py +++ /dev/null @@ -1,48 +0,0 @@ -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 it's 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/python2.7/site-packages/click/parser.py b/venv/lib/python2.7/site-packages/click/parser.py deleted file mode 100644 index 9775c9f..0000000 --- a/venv/lib/python2.7/site-packages/click/parser.py +++ /dev/null @@ -1,426 +0,0 @@ -# -*- 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('%s option requires an argument' % opt) - raise BadOptionUsage('%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) - - 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('%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) - 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/python2.7/site-packages/click/termui.py b/venv/lib/python2.7/site-packages/click/termui.py deleted file mode 100644 index d9fba52..0000000 --- a/venv/lib/python2.7/site-packages/click/termui.py +++ /dev/null @@ -1,539 +0,0 @@ -import os -import sys -import struct - -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 -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', 'red', 'green', 'yellow', 'blue', 'magenta', - 'cyan', 'white', 'reset') -_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): - prompt = text - 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): - """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:: 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. - """ - 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) - - while 1: - while 1: - value = prompt_func(prompt) - if value: - break - # If a default is set and used, then the confirmation - # prompt is always skipped because that's the only thing - # that really makes sense. - elif default is not None: - 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 - - if get_winterm_size is not None: - return get_winterm_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, 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: 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 not isinstance(text, string_types): - text = text_type(text) - from ._termui_impl import pager - return pager(text + '\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) - * ``reset`` (reset the color code only) - - .. versionadded:: 2.0 - - :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.index(fg) + 30)) - except ValueError: - raise TypeError('Unknown color %r' % fg) - if bg: - try: - bits.append('\033[%dm' % (_ansi_colors.index(bg) + 40)) - except ValueError: - 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(text, 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 - """ - return echo(style(text, **styles), 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('http://click.pocoo.org/') - 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. - - .. 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 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/python2.7/site-packages/click/testing.py b/venv/lib/python2.7/site-packages/click/testing.py deleted file mode 100644 index d43581f..0000000 --- a/venv/lib/python2.7/site-packages/click/testing.py +++ /dev/null @@ -1,322 +0,0 @@ -import os -import sys -import shutil -import tempfile -import contextlib - -from ._compat import iteritems, 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. -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, output_bytes, exit_code, exception, - exc_info=None): - #: The runner that created the result - self.runner = runner - #: The output as bytes. - self.output_bytes = output_bytes - #: The exit code as integer. - self.exit_code = exit_code - #: The exception that happend if one did. - self.exception = exception - #: The traceback - self.exc_info = exc_info - - @property - def output(self): - """The output as unicode string.""" - return self.output_bytes.decode(self.runner.charset, 'replace') \ - .replace('\r\n', '\n') - - def __repr__(self): - return '' % ( - 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. - """ - - def __init__(self, charset=None, env=None, echo_stdin=False): - if charset is None: - charset = 'utf-8' - self.charset = charset - self.env = env or {} - self.echo_stdin = echo_stdin - - 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: - sys.stdout = sys.stderr = bytes_output = StringIO() - if self.echo_stdin: - input = EchoingStdin(input, bytes_output) - else: - bytes_output = io.BytesIO() - if self.echo_stdin: - input = EchoingStdin(input, bytes_output) - input = io.TextIOWrapper(input, encoding=self.charset) - sys.stdout = sys.stderr = io.TextIOWrapper( - bytes_output, encoding=self.charset) - - 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(value) - if value is None: - try: - del os.environ[key] - except Exception: - pass - else: - os.environ[key] = value - yield bytes_output - 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, **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 - :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 out: - exception = None - exit_code = 0 - - try: - cli.main(args=args or (), - prog_name=self.get_default_prog_name(cli), **extra) - except SystemExit as e: - if e.code != 0: - exception = e - - exc_info = sys.exc_info() - - exit_code = e.code - 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() - output = out.getvalue() - - return Result(runner=self, - output_bytes=output, - 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/python2.7/site-packages/click/types.py b/venv/lib/python2.7/site-packages/click/types.py deleted file mode 100644 index 3639002..0000000 --- a/venv/lib/python2.7/site-packages/click/types.py +++ /dev/null @@ -1,550 +0,0 @@ -import os -import stat - -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. - - See :ref:`choice-opts` for an example. - """ - name = 'choice' - - def __init__(self, choices): - self.choices = choices - - def get_metavar(self, param): - return '[%s]' % '|'.join(self.choices) - - def get_missing_message(self, param): - return 'Choose from %s.' % ', '.join(self.choices) - - def convert(self, value, param, ctx): - # Exact match - if value in self.choices: - return value - - # Match through normalization - if ctx is not None and \ - ctx.token_normalize_func is not None: - value = ctx.token_normalize_func(value) - for choice in self.choices: - if ctx.token_normalize_func(choice) == value: - return choice - - self.fail('invalid choice: %s. (choose from %s)' % - (value, ', '.join(self.choices)), param, ctx) - - def __repr__(self): - return 'Choice(%r)' % list(self.choices) - - -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 BoolParamType(ParamType): - name = 'boolean' - - def convert(self, value, param, ctx): - if isinstance(value, bool): - return bool(value) - value = value.lower() - if value in ('true', '1', 'yes', 'y'): - return True - elif value in ('false', '0', 'no', 'n'): - return False - self.fail('%s is not a valid boolean' % value, param, ctx) - - def __repr__(self): - return 'BOOL' - - -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 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. - - 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. - :param allow_dash: If this is set to `True`, a single dash to indicate - standard streams is permitted. - :param 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' - if 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/python2.7/site-packages/click/utils.py b/venv/lib/python2.7/site-packages/click/utils.py deleted file mode 100644 index eee626d..0000000 --- a/venv/lib/python2.7/site-packages/click/utils.py +++ /dev/null @@ -1,415 +0,0 @@ -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): - 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 as 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: http://pypi.python.org/pypi/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)) diff --git a/venv/lib/python2.7/site-packages/dateutil/__init__.py b/venv/lib/python2.7/site-packages/dateutil/__init__.py deleted file mode 100644 index b78ef9d..0000000 --- a/venv/lib/python2.7/site-packages/dateutil/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# -*- coding: utf-8 -*- -__version__ = "2.5.3" diff --git a/venv/lib/python2.7/site-packages/dateutil/easter.py b/venv/lib/python2.7/site-packages/dateutil/easter.py deleted file mode 100644 index 8d30c4e..0000000 --- a/venv/lib/python2.7/site-packages/dateutil/easter.py +++ /dev/null @@ -1,89 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module offers a generic easter computing method for any given year, using -Western, Orthodox or Julian algorithms. -""" - -import datetime - -__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"] - -EASTER_JULIAN = 1 -EASTER_ORTHODOX = 2 -EASTER_WESTERN = 3 - - -def easter(year, method=EASTER_WESTERN): - """ - This method was ported from the work done by GM Arts, - on top of the algorithm by Claus Tondering, which was - based in part on the algorithm of Ouding (1940), as - quoted in "Explanatory Supplement to the Astronomical - Almanac", P. Kenneth Seidelmann, editor. - - This algorithm implements three different easter - calculation methods: - - 1 - Original calculation in Julian calendar, valid in - dates after 326 AD - 2 - Original method, with date converted to Gregorian - calendar, valid in years 1583 to 4099 - 3 - Revised method, in Gregorian calendar, valid in - years 1583 to 4099 as well - - These methods are represented by the constants: - - EASTER_JULIAN = 1 - EASTER_ORTHODOX = 2 - EASTER_WESTERN = 3 - - The default method is method 3. - - More about the algorithm may be found at: - - http://users.chariot.net.au/~gmarts/eastalg.htm - - and - - http://www.tondering.dk/claus/calendar.html - - """ - - if not (1 <= method <= 3): - raise ValueError("invalid method") - - # g - Golden year - 1 - # c - Century - # h - (23 - Epact) mod 30 - # i - Number of days from March 21 to Paschal Full Moon - # j - Weekday for PFM (0=Sunday, etc) - # p - Number of days from March 21 to Sunday on or before PFM - # (-6 to 28 methods 1 & 3, to 56 for method 2) - # e - Extra days to add for method 2 (converting Julian - # date to Gregorian date) - - y = year - g = y % 19 - e = 0 - if method < 3: - # Old method - i = (19*g + 15) % 30 - j = (y + y//4 + i) % 7 - if method == 2: - # Extra dates to convert Julian to Gregorian date - e = 10 - if y > 1600: - e = e + y//100 - 16 - (y//100 - 16)//4 - else: - # New method - c = y//100 - h = (c - c//4 - (8*c + 13)//25 + 19*g + 15) % 30 - i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11)) - j = (y + y//4 + i + 2 - c + c//4) % 7 - - # p can be from -6 to 56 corresponding to dates 22 March to 23 May - # (later dates apply to method 2, although 23 May never actually occurs) - p = i - j + e - d = 1 + (p + 27 + (p + 6)//40) % 31 - m = 3 + (p + 26)//30 - return datetime.date(int(y), int(m), int(d)) diff --git a/venv/lib/python2.7/site-packages/dateutil/parser.py b/venv/lib/python2.7/site-packages/dateutil/parser.py deleted file mode 100644 index 6738791..0000000 --- a/venv/lib/python2.7/site-packages/dateutil/parser.py +++ /dev/null @@ -1,1356 +0,0 @@ -# -*- coding:iso-8859-1 -*- -""" -This module offers a generic date/time string parser which is able to parse -most known formats to represent a date and/or time. - -This module attempts to be forgiving with regards to unlikely input formats, -returning a datetime object even for dates which are ambiguous. If an element -of a date/time stamp is omitted, the following rules are applied: -- If AM or PM is left unspecified, a 24-hour clock is assumed, however, an hour - on a 12-hour clock (``0 <= hour <= 12``) *must* be specified if AM or PM is - specified. -- If a time zone is omitted, a timezone-naive datetime is returned. - -If any other elements are missing, they are taken from the -:class:`datetime.datetime` object passed to the parameter ``default``. If this -results in a day number exceeding the valid number of days per month, the -value falls back to the end of the month. - -Additional resources about date/time string formats can be found below: - -- `A summary of the international standard date and time notation - `_ -- `W3C Date and Time Formats `_ -- `Time Formats (Planetary Rings Node) `_ -- `CPAN ParseDate module - `_ -- `Java SimpleDateFormat Class - `_ -""" -from __future__ import unicode_literals - -import datetime -import string -import time -import collections -import re -from io import StringIO -from calendar import monthrange, isleap - -from six import text_type, binary_type, integer_types - -from . import relativedelta -from . import tz - -__all__ = ["parse", "parserinfo"] - - -class _timelex(object): - # Fractional seconds are sometimes split by a comma - _split_decimal = re.compile("([\.,])") - - def __init__(self, instream): - if isinstance(instream, binary_type): - instream = instream.decode() - - if isinstance(instream, text_type): - instream = StringIO(instream) - - self.instream = instream - self.charstack = [] - self.tokenstack = [] - self.eof = False - - def get_token(self): - """ - This function breaks the time string into lexical units (tokens), which - can be parsed by the parser. Lexical units are demarcated by changes in - the character set, so any continuous string of letters is considered - one unit, any continuous string of numbers is considered one unit. - - The main complication arises from the fact that dots ('.') can be used - both as separators (e.g. "Sep.20.2009") or decimal points (e.g. - "4:30:21.447"). As such, it is necessary to read the full context of - any dot-separated strings before breaking it into tokens; as such, this - function maintains a "token stack", for when the ambiguous context - demands that multiple tokens be parsed at once. - """ - if self.tokenstack: - return self.tokenstack.pop(0) - - seenletters = False - token = None - state = None - - while not self.eof: - # We only realize that we've reached the end of a token when we - # find a character that's not part of the current token - since - # that character may be part of the next token, it's stored in the - # charstack. - if self.charstack: - nextchar = self.charstack.pop(0) - else: - nextchar = self.instream.read(1) - while nextchar == '\x00': - nextchar = self.instream.read(1) - - if not nextchar: - self.eof = True - break - elif not state: - # First character of the token - determines if we're starting - # to parse a word, a number or something else. - token = nextchar - if self.isword(nextchar): - state = 'a' - elif self.isnum(nextchar): - state = '0' - elif self.isspace(nextchar): - token = ' ' - break # emit token - else: - break # emit token - elif state == 'a': - # If we've already started reading a word, we keep reading - # letters until we find something that's not part of a word. - seenletters = True - if self.isword(nextchar): - token += nextchar - elif nextchar == '.': - token += nextchar - state = 'a.' - else: - self.charstack.append(nextchar) - break # emit token - elif state == '0': - # If we've already started reading a number, we keep reading - # numbers until we find something that doesn't fit. - if self.isnum(nextchar): - token += nextchar - elif nextchar == '.' or (nextchar == ',' and len(token) >= 2): - token += nextchar - state = '0.' - else: - self.charstack.append(nextchar) - break # emit token - elif state == 'a.': - # If we've seen some letters and a dot separator, continue - # parsing, and the tokens will be broken up later. - seenletters = True - if nextchar == '.' or self.isword(nextchar): - token += nextchar - elif self.isnum(nextchar) and token[-1] == '.': - token += nextchar - state = '0.' - else: - self.charstack.append(nextchar) - break # emit token - elif state == '0.': - # If we've seen at least one dot separator, keep going, we'll - # break up the tokens later. - if nextchar == '.' or self.isnum(nextchar): - token += nextchar - elif self.isword(nextchar) and token[-1] == '.': - token += nextchar - state = 'a.' - else: - self.charstack.append(nextchar) - break # emit token - - if (state in ('a.', '0.') and (seenletters or token.count('.') > 1 or - token[-1] in '.,')): - l = self._split_decimal.split(token) - token = l[0] - for tok in l[1:]: - if tok: - self.tokenstack.append(tok) - - if state == '0.' and token.count('.') == 0: - token = token.replace(',', '.') - - return token - - def __iter__(self): - return self - - def __next__(self): - token = self.get_token() - if token is None: - raise StopIteration - - return token - - def next(self): - return self.__next__() # Python 2.x support - - @classmethod - def split(cls, s): - return list(cls(s)) - - @classmethod - def isword(cls, nextchar): - """ Whether or not the next character is part of a word """ - return nextchar.isalpha() - - @classmethod - def isnum(cls, nextchar): - """ Whether the next character is part of a number """ - return nextchar.isdigit() - - @classmethod - def isspace(cls, nextchar): - """ Whether the next character is whitespace """ - return nextchar.isspace() - - -class _resultbase(object): - - def __init__(self): - for attr in self.__slots__: - setattr(self, attr, None) - - def _repr(self, classname): - l = [] - for attr in self.__slots__: - value = getattr(self, attr) - if value is not None: - l.append("%s=%s" % (attr, repr(value))) - return "%s(%s)" % (classname, ", ".join(l)) - - def __len__(self): - return (sum(getattr(self, attr) is not None - for attr in self.__slots__)) - - def __repr__(self): - return self._repr(self.__class__.__name__) - - -class parserinfo(object): - """ - Class which handles what inputs are accepted. Subclass this to customize - the language and acceptable values for each parameter. - - :param dayfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the day (``True``) or month (``False``). If - ``yearfirst`` is set to ``True``, this distinguishes between YDM - and YMD. Default is ``False``. - - :param yearfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the year. If ``True``, the first number is taken - to be the year, otherwise the last number is taken to be the year. - Default is ``False``. - """ - - # m from a.m/p.m, t from ISO T separator - JUMP = [" ", ".", ",", ";", "-", "/", "'", - "at", "on", "and", "ad", "m", "t", "of", - "st", "nd", "rd", "th"] - - WEEKDAYS = [("Mon", "Monday"), - ("Tue", "Tuesday"), - ("Wed", "Wednesday"), - ("Thu", "Thursday"), - ("Fri", "Friday"), - ("Sat", "Saturday"), - ("Sun", "Sunday")] - MONTHS = [("Jan", "January"), - ("Feb", "February"), - ("Mar", "March"), - ("Apr", "April"), - ("May", "May"), - ("Jun", "June"), - ("Jul", "July"), - ("Aug", "August"), - ("Sep", "Sept", "September"), - ("Oct", "October"), - ("Nov", "November"), - ("Dec", "December")] - HMS = [("h", "hour", "hours"), - ("m", "minute", "minutes"), - ("s", "second", "seconds")] - AMPM = [("am", "a"), - ("pm", "p")] - UTCZONE = ["UTC", "GMT", "Z"] - PERTAIN = ["of"] - TZOFFSET = {} - - def __init__(self, dayfirst=False, yearfirst=False): - self._jump = self._convert(self.JUMP) - self._weekdays = self._convert(self.WEEKDAYS) - self._months = self._convert(self.MONTHS) - self._hms = self._convert(self.HMS) - self._ampm = self._convert(self.AMPM) - self._utczone = self._convert(self.UTCZONE) - self._pertain = self._convert(self.PERTAIN) - - self.dayfirst = dayfirst - self.yearfirst = yearfirst - - self._year = time.localtime().tm_year - self._century = self._year // 100 * 100 - - def _convert(self, lst): - dct = {} - for i, v in enumerate(lst): - if isinstance(v, tuple): - for v in v: - dct[v.lower()] = i - else: - dct[v.lower()] = i - return dct - - def jump(self, name): - return name.lower() in self._jump - - def weekday(self, name): - if len(name) >= 3: - try: - return self._weekdays[name.lower()] - except KeyError: - pass - return None - - def month(self, name): - if len(name) >= 3: - try: - return self._months[name.lower()] + 1 - except KeyError: - pass - return None - - def hms(self, name): - try: - return self._hms[name.lower()] - except KeyError: - return None - - def ampm(self, name): - try: - return self._ampm[name.lower()] - except KeyError: - return None - - def pertain(self, name): - return name.lower() in self._pertain - - def utczone(self, name): - return name.lower() in self._utczone - - def tzoffset(self, name): - if name in self._utczone: - return 0 - - return self.TZOFFSET.get(name) - - def convertyear(self, year, century_specified=False): - if year < 100 and not century_specified: - year += self._century - if abs(year - self._year) >= 50: - if year < self._year: - year += 100 - else: - year -= 100 - return year - - def validate(self, res): - # move to info - if res.year is not None: - res.year = self.convertyear(res.year, res.century_specified) - - if res.tzoffset == 0 and not res.tzname or res.tzname == 'Z': - res.tzname = "UTC" - res.tzoffset = 0 - elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname): - res.tzoffset = 0 - return True - - -class _ymd(list): - def __init__(self, tzstr, *args, **kwargs): - super(self.__class__, self).__init__(*args, **kwargs) - self.century_specified = False - self.tzstr = tzstr - - @staticmethod - def token_could_be_year(token, year): - try: - return int(token) == year - except ValueError: - return False - - @staticmethod - def find_potential_year_tokens(year, tokens): - return [token for token in tokens if _ymd.token_could_be_year(token, year)] - - def find_probable_year_index(self, tokens): - """ - attempt to deduce if a pre 100 year was lost - due to padded zeros being taken off - """ - for index, token in enumerate(self): - potential_year_tokens = _ymd.find_potential_year_tokens(token, tokens) - if len(potential_year_tokens) == 1 and len(potential_year_tokens[0]) > 2: - return index - - def append(self, val): - if hasattr(val, '__len__'): - if val.isdigit() and len(val) > 2: - self.century_specified = True - elif val > 100: - self.century_specified = True - - super(self.__class__, self).append(int(val)) - - def resolve_ymd(self, mstridx, yearfirst, dayfirst): - len_ymd = len(self) - year, month, day = (None, None, None) - - if len_ymd > 3: - raise ValueError("More than three YMD values") - elif len_ymd == 1 or (mstridx != -1 and len_ymd == 2): - # One member, or two members with a month string - if mstridx != -1: - month = self[mstridx] - del self[mstridx] - - if len_ymd > 1 or mstridx == -1: - if self[0] > 31: - year = self[0] - else: - day = self[0] - - elif len_ymd == 2: - # Two members with numbers - if self[0] > 31: - # 99-01 - year, month = self - elif self[1] > 31: - # 01-99 - month, year = self - elif dayfirst and self[1] <= 12: - # 13-01 - day, month = self - else: - # 01-13 - month, day = self - - elif len_ymd == 3: - # Three members - if mstridx == 0: - month, day, year = self - elif mstridx == 1: - if self[0] > 31 or (yearfirst and self[2] <= 31): - # 99-Jan-01 - year, month, day = self - else: - # 01-Jan-01 - # Give precendence to day-first, since - # two-digit years is usually hand-written. - day, month, year = self - - elif mstridx == 2: - # WTF!? - if self[1] > 31: - # 01-99-Jan - day, year, month = self - else: - # 99-01-Jan - year, day, month = self - - else: - if self[0] > 31 or \ - self.find_probable_year_index(_timelex.split(self.tzstr)) == 0 or \ - (yearfirst and self[1] <= 12 and self[2] <= 31): - # 99-01-01 - if dayfirst and self[2] <= 12: - year, day, month = self - else: - year, month, day = self - elif self[0] > 12 or (dayfirst and self[1] <= 12): - # 13-01-01 - day, month, year = self - else: - # 01-13-01 - month, day, year = self - - return year, month, day - - -class parser(object): - def __init__(self, info=None): - self.info = info or parserinfo() - - def parse(self, timestr, default=None, ignoretz=False, tzinfos=None, **kwargs): - """ - Parse the date/time string into a :class:`datetime.datetime` object. - - :param timestr: - Any date/time string using the supported formats. - - :param default: - The default datetime object, if this is a datetime object and not - ``None``, elements specified in ``timestr`` replace elements in the - default object. - - :param ignoretz: - If set ``True``, time zones in parsed strings are ignored and a - naive :class:`datetime.datetime` object is returned. - - :param tzinfos: - Additional time zone names / aliases which may be present in the - string. This argument maps time zone names (and optionally offsets - from those time zones) to time zones. This parameter can be a - dictionary with timezone aliases mapping time zone names to time - zones or a function taking two parameters (``tzname`` and - ``tzoffset``) and returning a time zone. - - The timezones to which the names are mapped can be an integer - offset from UTC in minutes or a :class:`tzinfo` object. - - .. doctest:: - :options: +NORMALIZE_WHITESPACE - - >>> from dateutil.parser import parse - >>> from dateutil.tz import gettz - >>> tzinfos = {"BRST": -10800, "CST": gettz("America/Chicago")} - >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) - datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -10800)) - >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) - datetime.datetime(2012, 1, 19, 17, 21, - tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) - - This parameter is ignored if ``ignoretz`` is set. - - :param **kwargs: - Keyword arguments as passed to ``_parse()``. - - :return: - Returns a :class:`datetime.datetime` object or, if the - ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the - first element being a :class:`datetime.datetime` object, the second - a tuple containing the fuzzy tokens. - - :raises ValueError: - Raised for invalid or unknown string format, if the provided - :class:`tzinfo` is not in a valid format, or if an invalid date - would be created. - - :raises OverflowError: - Raised if the parsed date exceeds the largest valid C integer on - your system. - """ - - if default is None: - effective_dt = datetime.datetime.now() - default = datetime.datetime.now().replace(hour=0, minute=0, - second=0, microsecond=0) - else: - effective_dt = default - - res, skipped_tokens = self._parse(timestr, **kwargs) - - if res is None: - raise ValueError("Unknown string format") - - if len(res) == 0: - raise ValueError("String does not contain a date.") - - repl = {} - for attr in ("year", "month", "day", "hour", - "minute", "second", "microsecond"): - value = getattr(res, attr) - if value is not None: - repl[attr] = value - - if 'day' not in repl: - # If the default day exceeds the last day of the month, fall back to - # the end of the month. - cyear = default.year if res.year is None else res.year - cmonth = default.month if res.month is None else res.month - cday = default.day if res.day is None else res.day - - if cday > monthrange(cyear, cmonth)[1]: - repl['day'] = monthrange(cyear, cmonth)[1] - - ret = default.replace(**repl) - - if res.weekday is not None and not res.day: - ret = ret+relativedelta.relativedelta(weekday=res.weekday) - - if not ignoretz: - if (isinstance(tzinfos, collections.Callable) or - tzinfos and res.tzname in tzinfos): - - if isinstance(tzinfos, collections.Callable): - tzdata = tzinfos(res.tzname, res.tzoffset) - else: - tzdata = tzinfos.get(res.tzname) - - if isinstance(tzdata, datetime.tzinfo): - tzinfo = tzdata - elif isinstance(tzdata, text_type): - tzinfo = tz.tzstr(tzdata) - elif isinstance(tzdata, integer_types): - tzinfo = tz.tzoffset(res.tzname, tzdata) - else: - raise ValueError("Offset must be tzinfo subclass, " - "tz string, or int offset.") - ret = ret.replace(tzinfo=tzinfo) - elif res.tzname and res.tzname in time.tzname: - ret = ret.replace(tzinfo=tz.tzlocal()) - elif res.tzoffset == 0: - ret = ret.replace(tzinfo=tz.tzutc()) - elif res.tzoffset: - ret = ret.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) - - if kwargs.get('fuzzy_with_tokens', False): - return ret, skipped_tokens - else: - return ret - - class _result(_resultbase): - __slots__ = ["year", "month", "day", "weekday", - "hour", "minute", "second", "microsecond", - "tzname", "tzoffset", "ampm"] - - def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False, - fuzzy_with_tokens=False): - """ - Private method which performs the heavy lifting of parsing, called from - ``parse()``, which passes on its ``kwargs`` to this function. - - :param timestr: - The string to parse. - - :param dayfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the day (``True``) or month (``False``). If - ``yearfirst`` is set to ``True``, this distinguishes between YDM - and YMD. If set to ``None``, this value is retrieved from the - current :class:`parserinfo` object (which itself defaults to - ``False``). - - :param yearfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the year. If ``True``, the first number is taken - to be the year, otherwise the last number is taken to be the year. - If this is set to ``None``, the value is retrieved from the current - :class:`parserinfo` object (which itself defaults to ``False``). - - :param fuzzy: - Whether to allow fuzzy parsing, allowing for string like "Today is - January 1, 2047 at 8:21:00AM". - - :param fuzzy_with_tokens: - If ``True``, ``fuzzy`` is automatically set to True, and the parser - will return a tuple where the first element is the parsed - :class:`datetime.datetime` datetimestamp and the second element is - a tuple containing the portions of the string which were ignored: - - .. doctest:: - - >>> from dateutil.parser import parse - >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) - (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) - - """ - if fuzzy_with_tokens: - fuzzy = True - - info = self.info - - if dayfirst is None: - dayfirst = info.dayfirst - - if yearfirst is None: - yearfirst = info.yearfirst - - res = self._result() - l = _timelex.split(timestr) # Splits the timestr into tokens - - # keep up with the last token skipped so we can recombine - # consecutively skipped tokens (-2 for when i begins at 0). - last_skipped_token_i = -2 - skipped_tokens = list() - - try: - # year/month/day list - ymd = _ymd(timestr) - - # Index of the month string in ymd - mstridx = -1 - - len_l = len(l) - i = 0 - while i < len_l: - - # Check if it's a number - try: - value_repr = l[i] - value = float(value_repr) - except ValueError: - value = None - - if value is not None: - # Token is a number - len_li = len(l[i]) - i += 1 - - if (len(ymd) == 3 and len_li in (2, 4) - and res.hour is None and (i >= len_l or (l[i] != ':' and - info.hms(l[i]) is None))): - # 19990101T23[59] - s = l[i-1] - res.hour = int(s[:2]) - - if len_li == 4: - res.minute = int(s[2:]) - - elif len_li == 6 or (len_li > 6 and l[i-1].find('.') == 6): - # YYMMDD or HHMMSS[.ss] - s = l[i-1] - - if not ymd and l[i-1].find('.') == -1: - #ymd.append(info.convertyear(int(s[:2]))) - - ymd.append(s[:2]) - ymd.append(s[2:4]) - ymd.append(s[4:]) - else: - # 19990101T235959[.59] - res.hour = int(s[:2]) - res.minute = int(s[2:4]) - res.second, res.microsecond = _parsems(s[4:]) - - elif len_li in (8, 12, 14): - # YYYYMMDD - s = l[i-1] - ymd.append(s[:4]) - ymd.append(s[4:6]) - ymd.append(s[6:8]) - - if len_li > 8: - res.hour = int(s[8:10]) - res.minute = int(s[10:12]) - - if len_li > 12: - res.second = int(s[12:]) - - elif ((i < len_l and info.hms(l[i]) is not None) or - (i+1 < len_l and l[i] == ' ' and - info.hms(l[i+1]) is not None)): - - # HH[ ]h or MM[ ]m or SS[.ss][ ]s - if l[i] == ' ': - i += 1 - - idx = info.hms(l[i]) - - while True: - if idx == 0: - res.hour = int(value) - - if value % 1: - res.minute = int(60*(value % 1)) - - elif idx == 1: - res.minute = int(value) - - if value % 1: - res.second = int(60*(value % 1)) - - elif idx == 2: - res.second, res.microsecond = \ - _parsems(value_repr) - - i += 1 - - if i >= len_l or idx == 2: - break - - # 12h00 - try: - value_repr = l[i] - value = float(value_repr) - except ValueError: - break - else: - i += 1 - idx += 1 - - if i < len_l: - newidx = info.hms(l[i]) - - if newidx is not None: - idx = newidx - - elif (i == len_l and l[i-2] == ' ' and - info.hms(l[i-3]) is not None): - # X h MM or X m SS - idx = info.hms(l[i-3]) + 1 - - if idx == 1: - res.minute = int(value) - - if value % 1: - res.second = int(60*(value % 1)) - elif idx == 2: - res.second, res.microsecond = \ - _parsems(value_repr) - i += 1 - - elif i+1 < len_l and l[i] == ':': - # HH:MM[:SS[.ss]] - res.hour = int(value) - i += 1 - value = float(l[i]) - res.minute = int(value) - - if value % 1: - res.second = int(60*(value % 1)) - - i += 1 - - if i < len_l and l[i] == ':': - res.second, res.microsecond = _parsems(l[i+1]) - i += 2 - - elif i < len_l and l[i] in ('-', '/', '.'): - sep = l[i] - ymd.append(value_repr) - i += 1 - - if i < len_l and not info.jump(l[i]): - try: - # 01-01[-01] - ymd.append(l[i]) - except ValueError: - # 01-Jan[-01] - value = info.month(l[i]) - - if value is not None: - ymd.append(value) - assert mstridx == -1 - mstridx = len(ymd)-1 - else: - return None, None - - i += 1 - - if i < len_l and l[i] == sep: - # We have three members - i += 1 - value = info.month(l[i]) - - if value is not None: - ymd.append(value) - mstridx = len(ymd)-1 - assert mstridx == -1 - else: - ymd.append(l[i]) - - i += 1 - elif i >= len_l or info.jump(l[i]): - if i+1 < len_l and info.ampm(l[i+1]) is not None: - # 12 am - res.hour = int(value) - - if res.hour < 12 and info.ampm(l[i+1]) == 1: - res.hour += 12 - elif res.hour == 12 and info.ampm(l[i+1]) == 0: - res.hour = 0 - - i += 1 - else: - # Year, month or day - ymd.append(value) - i += 1 - elif info.ampm(l[i]) is not None: - - # 12am - res.hour = int(value) - - if res.hour < 12 and info.ampm(l[i]) == 1: - res.hour += 12 - elif res.hour == 12 and info.ampm(l[i]) == 0: - res.hour = 0 - i += 1 - - elif not fuzzy: - return None, None - else: - i += 1 - continue - - # Check weekday - value = info.weekday(l[i]) - if value is not None: - res.weekday = value - i += 1 - continue - - # Check month name - value = info.month(l[i]) - if value is not None: - ymd.append(value) - assert mstridx == -1 - mstridx = len(ymd)-1 - - i += 1 - if i < len_l: - if l[i] in ('-', '/'): - # Jan-01[-99] - sep = l[i] - i += 1 - ymd.append(l[i]) - i += 1 - - if i < len_l and l[i] == sep: - # Jan-01-99 - i += 1 - ymd.append(l[i]) - i += 1 - - elif (i+3 < len_l and l[i] == l[i+2] == ' ' - and info.pertain(l[i+1])): - # Jan of 01 - # In this case, 01 is clearly year - try: - value = int(l[i+3]) - except ValueError: - # Wrong guess - pass - else: - # Convert it here to become unambiguous - ymd.append(str(info.convertyear(value))) - i += 4 - continue - - # Check am/pm - value = info.ampm(l[i]) - if value is not None: - # For fuzzy parsing, 'a' or 'am' (both valid English words) - # may erroneously trigger the AM/PM flag. Deal with that - # here. - val_is_ampm = True - - # If there's already an AM/PM flag, this one isn't one. - if fuzzy and res.ampm is not None: - val_is_ampm = False - - # If AM/PM is found and hour is not, raise a ValueError - if res.hour is None: - if fuzzy: - val_is_ampm = False - else: - raise ValueError('No hour specified with ' + - 'AM or PM flag.') - elif not 0 <= res.hour <= 12: - # If AM/PM is found, it's a 12 hour clock, so raise - # an error for invalid range - if fuzzy: - val_is_ampm = False - else: - raise ValueError('Invalid hour specified for ' + - '12-hour clock.') - - if val_is_ampm: - if value == 1 and res.hour < 12: - res.hour += 12 - elif value == 0 and res.hour == 12: - res.hour = 0 - - res.ampm = value - - i += 1 - continue - - # Check for a timezone name - if (res.hour is not None and len(l[i]) <= 5 and - res.tzname is None and res.tzoffset is None and - not [x for x in l[i] if x not in - string.ascii_uppercase]): - res.tzname = l[i] - res.tzoffset = info.tzoffset(res.tzname) - i += 1 - - # Check for something like GMT+3, or BRST+3. Notice - # that it doesn't mean "I am 3 hours after GMT", but - # "my time +3 is GMT". If found, we reverse the - # logic so that timezone parsing code will get it - # right. - if i < len_l and l[i] in ('+', '-'): - l[i] = ('+', '-')[l[i] == '+'] - res.tzoffset = None - if info.utczone(res.tzname): - # With something like GMT+3, the timezone - # is *not* GMT. - res.tzname = None - - continue - - # Check for a numbered timezone - if res.hour is not None and l[i] in ('+', '-'): - signal = (-1, 1)[l[i] == '+'] - i += 1 - len_li = len(l[i]) - - if len_li == 4: - # -0300 - res.tzoffset = int(l[i][:2])*3600+int(l[i][2:])*60 - elif i+1 < len_l and l[i+1] == ':': - # -03:00 - res.tzoffset = int(l[i])*3600+int(l[i+2])*60 - i += 2 - elif len_li <= 2: - # -[0]3 - res.tzoffset = int(l[i][:2])*3600 - else: - return None, None - i += 1 - - res.tzoffset *= signal - - # Look for a timezone name between parenthesis - if (i+3 < len_l and - info.jump(l[i]) and l[i+1] == '(' and l[i+3] == ')' and - 3 <= len(l[i+2]) <= 5 and - not [x for x in l[i+2] - if x not in string.ascii_uppercase]): - # -0300 (BRST) - res.tzname = l[i+2] - i += 4 - continue - - # Check jumps - if not (info.jump(l[i]) or fuzzy): - return None, None - - if last_skipped_token_i == i - 1: - # recombine the tokens - skipped_tokens[-1] += l[i] - else: - # just append - skipped_tokens.append(l[i]) - last_skipped_token_i = i - i += 1 - - # Process year/month/day - year, month, day = ymd.resolve_ymd(mstridx, yearfirst, dayfirst) - if year is not None: - res.year = year - res.century_specified = ymd.century_specified - - if month is not None: - res.month = month - - if day is not None: - res.day = day - - except (IndexError, ValueError, AssertionError): - return None, None - - if not info.validate(res): - return None, None - - if fuzzy_with_tokens: - return res, tuple(skipped_tokens) - else: - return res, None - -DEFAULTPARSER = parser() - - -def parse(timestr, parserinfo=None, **kwargs): - """ - - Parse a string in one of the supported formats, using the - ``parserinfo`` parameters. - - :param timestr: - A string containing a date/time stamp. - - :param parserinfo: - A :class:`parserinfo` object containing parameters for the parser. - If ``None``, the default arguments to the :class:`parserinfo` - constructor are used. - - The ``**kwargs`` parameter takes the following keyword arguments: - - :param default: - The default datetime object, if this is a datetime object and not - ``None``, elements specified in ``timestr`` replace elements in the - default object. - - :param ignoretz: - If set ``True``, time zones in parsed strings are ignored and a naive - :class:`datetime` object is returned. - - :param tzinfos: - Additional time zone names / aliases which may be present in the - string. This argument maps time zone names (and optionally offsets - from those time zones) to time zones. This parameter can be a - dictionary with timezone aliases mapping time zone names to time - zones or a function taking two parameters (``tzname`` and - ``tzoffset``) and returning a time zone. - - The timezones to which the names are mapped can be an integer - offset from UTC in minutes or a :class:`tzinfo` object. - - .. doctest:: - :options: +NORMALIZE_WHITESPACE - - >>> from dateutil.parser import parse - >>> from dateutil.tz import gettz - >>> tzinfos = {"BRST": -10800, "CST": gettz("America/Chicago")} - >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) - datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -10800)) - >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) - datetime.datetime(2012, 1, 19, 17, 21, - tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) - - This parameter is ignored if ``ignoretz`` is set. - - :param dayfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the day (``True``) or month (``False``). If - ``yearfirst`` is set to ``True``, this distinguishes between YDM and - YMD. If set to ``None``, this value is retrieved from the current - :class:`parserinfo` object (which itself defaults to ``False``). - - :param yearfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the year. If ``True``, the first number is taken to - be the year, otherwise the last number is taken to be the year. If - this is set to ``None``, the value is retrieved from the current - :class:`parserinfo` object (which itself defaults to ``False``). - - :param fuzzy: - Whether to allow fuzzy parsing, allowing for string like "Today is - January 1, 2047 at 8:21:00AM". - - :param fuzzy_with_tokens: - If ``True``, ``fuzzy`` is automatically set to True, and the parser - will return a tuple where the first element is the parsed - :class:`datetime.datetime` datetimestamp and the second element is - a tuple containing the portions of the string which were ignored: - - .. doctest:: - - >>> from dateutil.parser import parse - >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) - (datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) - - :return: - Returns a :class:`datetime.datetime` object or, if the - ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the - first element being a :class:`datetime.datetime` object, the second - a tuple containing the fuzzy tokens. - - :raises ValueError: - Raised for invalid or unknown string format, if the provided - :class:`tzinfo` is not in a valid format, or if an invalid date - would be created. - - :raises OverflowError: - Raised if the parsed date exceeds the largest valid C integer on - your system. - """ - if parserinfo: - return parser(parserinfo).parse(timestr, **kwargs) - else: - return DEFAULTPARSER.parse(timestr, **kwargs) - - -class _tzparser(object): - - class _result(_resultbase): - - __slots__ = ["stdabbr", "stdoffset", "dstabbr", "dstoffset", - "start", "end"] - - class _attr(_resultbase): - __slots__ = ["month", "week", "weekday", - "yday", "jyday", "day", "time"] - - def __repr__(self): - return self._repr("") - - def __init__(self): - _resultbase.__init__(self) - self.start = self._attr() - self.end = self._attr() - - def parse(self, tzstr): - res = self._result() - l = _timelex.split(tzstr) - try: - - len_l = len(l) - - i = 0 - while i < len_l: - # BRST+3[BRDT[+2]] - j = i - while j < len_l and not [x for x in l[j] - if x in "0123456789:,-+"]: - j += 1 - if j != i: - if not res.stdabbr: - offattr = "stdoffset" - res.stdabbr = "".join(l[i:j]) - else: - offattr = "dstoffset" - res.dstabbr = "".join(l[i:j]) - i = j - if (i < len_l and (l[i] in ('+', '-') or l[i][0] in - "0123456789")): - if l[i] in ('+', '-'): - # Yes, that's right. See the TZ variable - # documentation. - signal = (1, -1)[l[i] == '+'] - i += 1 - else: - signal = -1 - len_li = len(l[i]) - if len_li == 4: - # -0300 - setattr(res, offattr, (int(l[i][:2])*3600 + - int(l[i][2:])*60)*signal) - elif i+1 < len_l and l[i+1] == ':': - # -03:00 - setattr(res, offattr, - (int(l[i])*3600+int(l[i+2])*60)*signal) - i += 2 - elif len_li <= 2: - # -[0]3 - setattr(res, offattr, - int(l[i][:2])*3600*signal) - else: - return None - i += 1 - if res.dstabbr: - break - else: - break - - if i < len_l: - for j in range(i, len_l): - if l[j] == ';': - l[j] = ',' - - assert l[i] == ',' - - i += 1 - - if i >= len_l: - pass - elif (8 <= l.count(',') <= 9 and - not [y for x in l[i:] if x != ',' - for y in x if y not in "0123456789"]): - # GMT0BST,3,0,30,3600,10,0,26,7200[,3600] - for x in (res.start, res.end): - x.month = int(l[i]) - i += 2 - if l[i] == '-': - value = int(l[i+1])*-1 - i += 1 - else: - value = int(l[i]) - i += 2 - if value: - x.week = value - x.weekday = (int(l[i])-1) % 7 - else: - x.day = int(l[i]) - i += 2 - x.time = int(l[i]) - i += 2 - if i < len_l: - if l[i] in ('-', '+'): - signal = (-1, 1)[l[i] == "+"] - i += 1 - else: - signal = 1 - res.dstoffset = (res.stdoffset+int(l[i]))*signal - elif (l.count(',') == 2 and l[i:].count('/') <= 2 and - not [y for x in l[i:] if x not in (',', '/', 'J', 'M', - '.', '-', ':') - for y in x if y not in "0123456789"]): - for x in (res.start, res.end): - if l[i] == 'J': - # non-leap year day (1 based) - i += 1 - x.jyday = int(l[i]) - elif l[i] == 'M': - # month[-.]week[-.]weekday - i += 1 - x.month = int(l[i]) - i += 1 - assert l[i] in ('-', '.') - i += 1 - x.week = int(l[i]) - if x.week == 5: - x.week = -1 - i += 1 - assert l[i] in ('-', '.') - i += 1 - x.weekday = (int(l[i])-1) % 7 - else: - # year day (zero based) - x.yday = int(l[i])+1 - - i += 1 - - if i < len_l and l[i] == '/': - i += 1 - # start time - len_li = len(l[i]) - if len_li == 4: - # -0300 - x.time = (int(l[i][:2])*3600+int(l[i][2:])*60) - elif i+1 < len_l and l[i+1] == ':': - # -03:00 - x.time = int(l[i])*3600+int(l[i+2])*60 - i += 2 - if i+1 < len_l and l[i+1] == ':': - i += 2 - x.time += int(l[i]) - elif len_li <= 2: - # -[0]3 - x.time = (int(l[i][:2])*3600) - else: - return None - i += 1 - - assert i == len_l or l[i] == ',' - - i += 1 - - assert i >= len_l - - except (IndexError, ValueError, AssertionError): - return None - - return res - - -DEFAULTTZPARSER = _tzparser() - - -def _parsetz(tzstr): - return DEFAULTTZPARSER.parse(tzstr) - - -def _parsems(value): - """Parse a I[.F] seconds value into (seconds, microseconds).""" - if "." not in value: - return int(value), 0 - else: - i, f = value.split(".") - return int(i), int(f.ljust(6, "0")[:6]) - - -# vim:ts=4:sw=4:et diff --git a/venv/lib/python2.7/site-packages/dateutil/relativedelta.py b/venv/lib/python2.7/site-packages/dateutil/relativedelta.py deleted file mode 100644 index 0217d57..0000000 --- a/venv/lib/python2.7/site-packages/dateutil/relativedelta.py +++ /dev/null @@ -1,530 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -import calendar - -import operator -from math import copysign - -from six import integer_types -from warnings import warn - -__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] - - -class weekday(object): - __slots__ = ["weekday", "n"] - - def __init__(self, weekday, n=None): - self.weekday = weekday - self.n = n - - def __call__(self, n): - if n == self.n: - return self - else: - return self.__class__(self.weekday, n) - - def __eq__(self, other): - try: - if self.weekday != other.weekday or self.n != other.n: - return False - except AttributeError: - return False - return True - - def __repr__(self): - s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday] - if not self.n: - return s - else: - return "%s(%+d)" % (s, self.n) - -MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([weekday(x) for x in range(7)]) - - -class relativedelta(object): - """ - The relativedelta type is based on the specification of the excellent - work done by M.-A. Lemburg in his - `mx.DateTime `_ extension. - However, notice that this type does *NOT* implement the same algorithm as - his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. - - There are two different ways to build a relativedelta instance. The - first one is passing it two date/datetime classes:: - - relativedelta(datetime1, datetime2) - - The second one is passing it any number of the following keyword arguments:: - - relativedelta(arg1=x,arg2=y,arg3=z...) - - year, month, day, hour, minute, second, microsecond: - Absolute information (argument is singular); adding or subtracting a - relativedelta with absolute information does not perform an aritmetic - operation, but rather REPLACES the corresponding value in the - original datetime with the value(s) in relativedelta. - - years, months, weeks, days, hours, minutes, seconds, microseconds: - Relative information, may be negative (argument is plural); adding - or subtracting a relativedelta with relative information performs - the corresponding aritmetic operation on the original datetime value - with the information in the relativedelta. - - weekday: - One of the weekday instances (MO, TU, etc). These instances may - receive a parameter N, specifying the Nth weekday, which could - be positive or negative (like MO(+1) or MO(-2). Not specifying - it is the same as specifying +1. You can also use an integer, - where 0=MO. - - leapdays: - Will add given days to the date found, if year is a leap - year, and the date found is post 28 of february. - - yearday, nlyearday: - Set the yearday or the non-leap year day (jump leap days). - These are converted to day/month/leapdays information. - - Here is the behavior of operations with relativedelta: - - 1. Calculate the absolute year, using the 'year' argument, or the - original datetime year, if the argument is not present. - - 2. Add the relative 'years' argument to the absolute year. - - 3. Do steps 1 and 2 for month/months. - - 4. Calculate the absolute day, using the 'day' argument, or the - original datetime day, if the argument is not present. Then, - subtract from the day until it fits in the year and month - found after their operations. - - 5. Add the relative 'days' argument to the absolute day. Notice - that the 'weeks' argument is multiplied by 7 and added to - 'days'. - - 6. Do steps 1 and 2 for hour/hours, minute/minutes, second/seconds, - microsecond/microseconds. - - 7. If the 'weekday' argument is present, calculate the weekday, - with the given (wday, nth) tuple. wday is the index of the - weekday (0-6, 0=Mon), and nth is the number of weeks to add - forward or backward, depending on its signal. Notice that if - the calculated date is already Monday, for example, using - (0, 1) or (0, -1) won't change the day. - """ - - def __init__(self, dt1=None, dt2=None, - years=0, months=0, days=0, leapdays=0, weeks=0, - hours=0, minutes=0, seconds=0, microseconds=0, - year=None, month=None, day=None, weekday=None, - yearday=None, nlyearday=None, - hour=None, minute=None, second=None, microsecond=None): - - # Check for non-integer values in integer-only quantities - if any(x is not None and x != int(x) for x in (years, months)): - raise ValueError("Non-integer years and months are " - "ambiguous and not currently supported.") - - if dt1 and dt2: - # datetime is a subclass of date. So both must be date - if not (isinstance(dt1, datetime.date) and - isinstance(dt2, datetime.date)): - raise TypeError("relativedelta only diffs datetime/date") - - # We allow two dates, or two datetimes, so we coerce them to be - # of the same type - if (isinstance(dt1, datetime.datetime) != - isinstance(dt2, datetime.datetime)): - if not isinstance(dt1, datetime.datetime): - dt1 = datetime.datetime.fromordinal(dt1.toordinal()) - elif not isinstance(dt2, datetime.datetime): - dt2 = datetime.datetime.fromordinal(dt2.toordinal()) - - self.years = 0 - self.months = 0 - self.days = 0 - self.leapdays = 0 - self.hours = 0 - self.minutes = 0 - self.seconds = 0 - self.microseconds = 0 - self.year = None - self.month = None - self.day = None - self.weekday = None - self.hour = None - self.minute = None - self.second = None - self.microsecond = None - self._has_time = 0 - - # Get year / month delta between the two - months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month) - self._set_months(months) - - # Remove the year/month delta so the timedelta is just well-defined - # time units (seconds, days and microseconds) - dtm = self.__radd__(dt2) - - # If we've overshot our target, make an adjustment - if dt1 < dt2: - compare = operator.gt - increment = 1 - else: - compare = operator.lt - increment = -1 - - while compare(dt1, dtm): - months += increment - self._set_months(months) - dtm = self.__radd__(dt2) - - # Get the timedelta between the "months-adjusted" date and dt1 - delta = dt1 - dtm - self.seconds = delta.seconds + delta.days * 86400 - self.microseconds = delta.microseconds - else: - # Relative information - self.years = years - self.months = months - self.days = days + weeks * 7 - self.leapdays = leapdays - self.hours = hours - self.minutes = minutes - self.seconds = seconds - self.microseconds = microseconds - - # Absolute information - self.year = year - self.month = month - self.day = day - self.hour = hour - self.minute = minute - self.second = second - self.microsecond = microsecond - - if any(x is not None and int(x) != x - for x in (year, month, day, hour, - minute, second, microsecond)): - # For now we'll deprecate floats - later it'll be an error. - warn("Non-integer value passed as absolute information. " + - "This is not a well-defined condition and will raise " + - "errors in future versions.", DeprecationWarning) - - - if isinstance(weekday, integer_types): - self.weekday = weekdays[weekday] - else: - self.weekday = weekday - - yday = 0 - if nlyearday: - yday = nlyearday - elif yearday: - yday = yearday - if yearday > 59: - self.leapdays = -1 - if yday: - ydayidx = [31, 59, 90, 120, 151, 181, 212, - 243, 273, 304, 334, 366] - for idx, ydays in enumerate(ydayidx): - if yday <= ydays: - self.month = idx+1 - if idx == 0: - self.day = yday - else: - self.day = yday-ydayidx[idx-1] - break - else: - raise ValueError("invalid year day (%d)" % yday) - - self._fix() - - def _fix(self): - if abs(self.microseconds) > 999999: - s = _sign(self.microseconds) - div, mod = divmod(self.microseconds * s, 1000000) - self.microseconds = mod * s - self.seconds += div * s - if abs(self.seconds) > 59: - s = _sign(self.seconds) - div, mod = divmod(self.seconds * s, 60) - self.seconds = mod * s - self.minutes += div * s - if abs(self.minutes) > 59: - s = _sign(self.minutes) - div, mod = divmod(self.minutes * s, 60) - self.minutes = mod * s - self.hours += div * s - if abs(self.hours) > 23: - s = _sign(self.hours) - div, mod = divmod(self.hours * s, 24) - self.hours = mod * s - self.days += div * s - if abs(self.months) > 11: - s = _sign(self.months) - div, mod = divmod(self.months * s, 12) - self.months = mod * s - self.years += div * s - if (self.hours or self.minutes or self.seconds or self.microseconds - or self.hour is not None or self.minute is not None or - self.second is not None or self.microsecond is not None): - self._has_time = 1 - else: - self._has_time = 0 - - @property - def weeks(self): - return self.days // 7 - @weeks.setter - def weeks(self, value): - self.days = self.days - (self.weeks * 7) + value * 7 - - def _set_months(self, months): - self.months = months - if abs(self.months) > 11: - s = _sign(self.months) - div, mod = divmod(self.months * s, 12) - self.months = mod * s - self.years = div * s - else: - self.years = 0 - - def normalized(self): - """ - Return a version of this object represented entirely using integer - values for the relative attributes. - - >>> relativedelta(days=1.5, hours=2).normalized() - relativedelta(days=1, hours=14) - - :return: - Returns a :class:`dateutil.relativedelta.relativedelta` object. - """ - # Cascade remainders down (rounding each to roughly nearest microsecond) - days = int(self.days) - - hours_f = round(self.hours + 24 * (self.days - days), 11) - hours = int(hours_f) - - minutes_f = round(self.minutes + 60 * (hours_f - hours), 10) - minutes = int(minutes_f) - - seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8) - seconds = int(seconds_f) - - microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds)) - - # Constructor carries overflow back up with call to _fix() - return self.__class__(years=self.years, months=self.months, - days=days, hours=hours, minutes=minutes, - seconds=seconds, microseconds=microseconds, - leapdays=self.leapdays, year=self.year, - month=self.month, day=self.day, - weekday=self.weekday, hour=self.hour, - minute=self.minute, second=self.second, - microsecond=self.microsecond) - - def __add__(self, other): - if isinstance(other, relativedelta): - return self.__class__(years=other.years + self.years, - months=other.months + self.months, - days=other.days + self.days, - hours=other.hours + self.hours, - minutes=other.minutes + self.minutes, - seconds=other.seconds + self.seconds, - microseconds=(other.microseconds + - self.microseconds), - leapdays=other.leapdays or self.leapdays, - year=other.year or self.year, - month=other.month or self.month, - day=other.day or self.day, - weekday=other.weekday or self.weekday, - hour=other.hour or self.hour, - minute=other.minute or self.minute, - second=other.second or self.second, - microsecond=(other.microsecond or - self.microsecond)) - if not isinstance(other, datetime.date): - raise TypeError("unsupported type for add operation") - elif self._has_time and not isinstance(other, datetime.datetime): - other = datetime.datetime.fromordinal(other.toordinal()) - year = (self.year or other.year)+self.years - month = self.month or other.month - if self.months: - assert 1 <= abs(self.months) <= 12 - month += self.months - if month > 12: - year += 1 - month -= 12 - elif month < 1: - year -= 1 - month += 12 - day = min(calendar.monthrange(year, month)[1], - self.day or other.day) - repl = {"year": year, "month": month, "day": day} - for attr in ["hour", "minute", "second", "microsecond"]: - value = getattr(self, attr) - if value is not None: - repl[attr] = value - days = self.days - if self.leapdays and month > 2 and calendar.isleap(year): - days += self.leapdays - ret = (other.replace(**repl) - + datetime.timedelta(days=days, - hours=self.hours, - minutes=self.minutes, - seconds=self.seconds, - microseconds=self.microseconds)) - if self.weekday: - weekday, nth = self.weekday.weekday, self.weekday.n or 1 - jumpdays = (abs(nth) - 1) * 7 - if nth > 0: - jumpdays += (7 - ret.weekday() + weekday) % 7 - else: - jumpdays += (ret.weekday() - weekday) % 7 - jumpdays *= -1 - ret += datetime.timedelta(days=jumpdays) - return ret - - def __radd__(self, other): - return self.__add__(other) - - def __rsub__(self, other): - return self.__neg__().__radd__(other) - - def __sub__(self, other): - if not isinstance(other, relativedelta): - raise TypeError("unsupported type for sub operation") - return self.__class__(years=self.years - other.years, - months=self.months - other.months, - days=self.days - other.days, - hours=self.hours - other.hours, - minutes=self.minutes - other.minutes, - seconds=self.seconds - other.seconds, - microseconds=self.microseconds - other.microseconds, - leapdays=self.leapdays or other.leapdays, - year=self.year or other.year, - month=self.month or other.month, - day=self.day or other.day, - weekday=self.weekday or other.weekday, - hour=self.hour or other.hour, - minute=self.minute or other.minute, - second=self.second or other.second, - microsecond=self.microsecond or other.microsecond) - - def __neg__(self): - return self.__class__(years=-self.years, - months=-self.months, - days=-self.days, - hours=-self.hours, - minutes=-self.minutes, - seconds=-self.seconds, - microseconds=-self.microseconds, - leapdays=self.leapdays, - year=self.year, - month=self.month, - day=self.day, - weekday=self.weekday, - hour=self.hour, - minute=self.minute, - second=self.second, - microsecond=self.microsecond) - - def __bool__(self): - return not (not self.years and - not self.months and - not self.days and - not self.hours and - not self.minutes and - not self.seconds and - not self.microseconds and - not self.leapdays and - self.year is None and - self.month is None and - self.day is None and - self.weekday is None and - self.hour is None and - self.minute is None and - self.second is None and - self.microsecond is None) - # Compatibility with Python 2.x - __nonzero__ = __bool__ - - def __mul__(self, other): - f = float(other) - return self.__class__(years=int(self.years * f), - months=int(self.months * f), - days=int(self.days * f), - hours=int(self.hours * f), - minutes=int(self.minutes * f), - seconds=int(self.seconds * f), - microseconds=int(self.microseconds * f), - leapdays=self.leapdays, - year=self.year, - month=self.month, - day=self.day, - weekday=self.weekday, - hour=self.hour, - minute=self.minute, - second=self.second, - microsecond=self.microsecond) - - __rmul__ = __mul__ - - def __eq__(self, other): - if not isinstance(other, relativedelta): - return False - if self.weekday or other.weekday: - if not self.weekday or not other.weekday: - return False - if self.weekday.weekday != other.weekday.weekday: - return False - n1, n2 = self.weekday.n, other.weekday.n - if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)): - return False - return (self.years == other.years and - self.months == other.months and - self.days == other.days and - self.hours == other.hours and - self.minutes == other.minutes and - self.seconds == other.seconds and - self.microseconds == other.microseconds and - self.leapdays == other.leapdays and - self.year == other.year and - self.month == other.month and - self.day == other.day and - self.hour == other.hour and - self.minute == other.minute and - self.second == other.second and - self.microsecond == other.microsecond) - - def __ne__(self, other): - return not self.__eq__(other) - - def __div__(self, other): - return self.__mul__(1/float(other)) - - __truediv__ = __div__ - - def __repr__(self): - l = [] - for attr in ["years", "months", "days", "leapdays", - "hours", "minutes", "seconds", "microseconds"]: - value = getattr(self, attr) - if value: - l.append("{attr}={value:+g}".format(attr=attr, value=value)) - for attr in ["year", "month", "day", "weekday", - "hour", "minute", "second", "microsecond"]: - value = getattr(self, attr) - if value is not None: - l.append("{attr}={value}".format(attr=attr, value=repr(value))) - return "{classname}({attrs})".format(classname=self.__class__.__name__, - attrs=", ".join(l)) - -def _sign(x): - return int(copysign(1, x)) - -# vim:ts=4:sw=4:et diff --git a/venv/lib/python2.7/site-packages/dateutil/rrule.py b/venv/lib/python2.7/site-packages/dateutil/rrule.py deleted file mode 100644 index 22d6dfc..0000000 --- a/venv/lib/python2.7/site-packages/dateutil/rrule.py +++ /dev/null @@ -1,1612 +0,0 @@ -# -*- coding: utf-8 -*- -""" -The rrule module offers a small, complete, and very fast, implementation of -the recurrence rules documented in the -`iCalendar RFC `_, -including support for caching of results. -""" -import itertools -import datetime -import calendar -import sys - -try: - from math import gcd -except ImportError: - from fractions import gcd - -from six import advance_iterator, integer_types -from six.moves import _thread -import heapq - -# For warning about deprecation of until and count -from warnings import warn - -__all__ = ["rrule", "rruleset", "rrulestr", - "YEARLY", "MONTHLY", "WEEKLY", "DAILY", - "HOURLY", "MINUTELY", "SECONDLY", - "MO", "TU", "WE", "TH", "FR", "SA", "SU"] - -# Every mask is 7 days longer to handle cross-year weekly periods. -M366MASK = tuple([1]*31+[2]*29+[3]*31+[4]*30+[5]*31+[6]*30 + - [7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7) -M365MASK = list(M366MASK) -M29, M30, M31 = list(range(1, 30)), list(range(1, 31)), list(range(1, 32)) -MDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) -MDAY365MASK = list(MDAY366MASK) -M29, M30, M31 = list(range(-29, 0)), list(range(-30, 0)), list(range(-31, 0)) -NMDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) -NMDAY365MASK = list(NMDAY366MASK) -M366RANGE = (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366) -M365RANGE = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365) -WDAYMASK = [0, 1, 2, 3, 4, 5, 6]*55 -del M29, M30, M31, M365MASK[59], MDAY365MASK[59], NMDAY365MASK[31] -MDAY365MASK = tuple(MDAY365MASK) -M365MASK = tuple(M365MASK) - -FREQNAMES = ['YEARLY','MONTHLY','WEEKLY','DAILY','HOURLY','MINUTELY','SECONDLY'] - -(YEARLY, - MONTHLY, - WEEKLY, - DAILY, - HOURLY, - MINUTELY, - SECONDLY) = list(range(7)) - -# Imported on demand. -easter = None -parser = None - - -class weekday(object): - __slots__ = ["weekday", "n"] - - def __init__(self, weekday, n=None): - if n == 0: - raise ValueError("Can't create weekday with n == 0") - - self.weekday = weekday - self.n = n - - def __call__(self, n): - if n == self.n: - return self - else: - return self.__class__(self.weekday, n) - - def __eq__(self, other): - try: - if self.weekday != other.weekday or self.n != other.n: - return False - except AttributeError: - return False - return True - - def __repr__(self): - s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday] - if not self.n: - return s - else: - return "%s(%+d)" % (s, self.n) - -MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([weekday(x) for x in range(7)]) - - -def _invalidates_cache(f): - """ - Decorator for rruleset methods which may invalidate the - cached length. - """ - def inner_func(self, *args, **kwargs): - rv = f(self, *args, **kwargs) - self._invalidate_cache() - return rv - - return inner_func - - -class rrulebase(object): - def __init__(self, cache=False): - if cache: - self._cache = [] - self._cache_lock = _thread.allocate_lock() - self._invalidate_cache() - else: - self._cache = None - self._cache_complete = False - self._len = None - - def __iter__(self): - if self._cache_complete: - return iter(self._cache) - elif self._cache is None: - return self._iter() - else: - return self._iter_cached() - - def _invalidate_cache(self): - if self._cache is not None: - self._cache = [] - self._cache_complete = False - self._cache_gen = self._iter() - - if self._cache_lock.locked(): - self._cache_lock.release() - - self._len = None - - def _iter_cached(self): - i = 0 - gen = self._cache_gen - cache = self._cache - acquire = self._cache_lock.acquire - release = self._cache_lock.release - while gen: - if i == len(cache): - acquire() - if self._cache_complete: - break - try: - for j in range(10): - cache.append(advance_iterator(gen)) - except StopIteration: - self._cache_gen = gen = None - self._cache_complete = True - break - release() - yield cache[i] - i += 1 - while i < self._len: - yield cache[i] - i += 1 - - def __getitem__(self, item): - if self._cache_complete: - return self._cache[item] - elif isinstance(item, slice): - if item.step and item.step < 0: - return list(iter(self))[item] - else: - return list(itertools.islice(self, - item.start or 0, - item.stop or sys.maxsize, - item.step or 1)) - elif item >= 0: - gen = iter(self) - try: - for i in range(item+1): - res = advance_iterator(gen) - except StopIteration: - raise IndexError - return res - else: - return list(iter(self))[item] - - def __contains__(self, item): - if self._cache_complete: - return item in self._cache - else: - for i in self: - if i == item: - return True - elif i > item: - return False - return False - - # __len__() introduces a large performance penality. - def count(self): - """ Returns the number of recurrences in this set. It will have go - trough the whole recurrence, if this hasn't been done before. """ - if self._len is None: - for x in self: - pass - return self._len - - def before(self, dt, inc=False): - """ Returns the last recurrence before the given datetime instance. The - inc keyword defines what happens if dt is an occurrence. With - inc=True, if dt itself is an occurrence, it will be returned. """ - if self._cache_complete: - gen = self._cache - else: - gen = self - last = None - if inc: - for i in gen: - if i > dt: - break - last = i - else: - for i in gen: - if i >= dt: - break - last = i - return last - - def after(self, dt, inc=False): - """ Returns the first recurrence after the given datetime instance. The - inc keyword defines what happens if dt is an occurrence. With - inc=True, if dt itself is an occurrence, it will be returned. """ - if self._cache_complete: - gen = self._cache - else: - gen = self - if inc: - for i in gen: - if i >= dt: - return i - else: - for i in gen: - if i > dt: - return i - return None - - def xafter(self, dt, count=None, inc=False): - """ - Generator which yields up to `count` recurrences after the given - datetime instance, equivalent to `after`. - - :param dt: - The datetime at which to start generating recurrences. - - :param count: - The maximum number of recurrences to generate. If `None` (default), - dates are generated until the recurrence rule is exhausted. - - :param inc: - If `dt` is an instance of the rule and `inc` is `True`, it is - included in the output. - - :yields: Yields a sequence of `datetime` objects. - """ - - if self._cache_complete: - gen = self._cache - else: - gen = self - - # Select the comparison function - if inc: - comp = lambda dc, dtc: dc >= dtc - else: - comp = lambda dc, dtc: dc > dtc - - # Generate dates - n = 0 - for d in gen: - if comp(d, dt): - yield d - - if count is not None: - n += 1 - if n >= count: - break - - def between(self, after, before, inc=False, count=1): - """ Returns all the occurrences of the rrule between after and before. - The inc keyword defines what happens if after and/or before are - themselves occurrences. With inc=True, they will be included in the - list, if they are found in the recurrence set. """ - if self._cache_complete: - gen = self._cache - else: - gen = self - started = False - l = [] - if inc: - for i in gen: - if i > before: - break - elif not started: - if i >= after: - started = True - l.append(i) - else: - l.append(i) - else: - for i in gen: - if i >= before: - break - elif not started: - if i > after: - started = True - l.append(i) - else: - l.append(i) - return l - - -class rrule(rrulebase): - """ - That's the base of the rrule operation. It accepts all the keywords - defined in the RFC as its constructor parameters (except byday, - which was renamed to byweekday) and more. The constructor prototype is:: - - rrule(freq) - - Where freq must be one of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, - or SECONDLY. - - .. note:: - Per RFC section 3.3.10, recurrence instances falling on invalid dates - and times are ignored rather than coerced: - - Recurrence rules may generate recurrence instances with an invalid - date (e.g., February 30) or nonexistent local time (e.g., 1:30 AM - on a day where the local time is moved forward by an hour at 1:00 - AM). Such recurrence instances MUST be ignored and MUST NOT be - counted as part of the recurrence set. - - This can lead to possibly surprising behavior when, for example, the - start date occurs at the end of the month: - - >>> from dateutil.rrule import rrule, MONTHLY - >>> from datetime import datetime - >>> start_date = datetime(2014, 12, 31) - >>> list(rrule(freq=MONTHLY, count=4, dtstart=start_date)) - ... # doctest: +NORMALIZE_WHITESPACE - [datetime.datetime(2014, 12, 31, 0, 0), - datetime.datetime(2015, 1, 31, 0, 0), - datetime.datetime(2015, 3, 31, 0, 0), - datetime.datetime(2015, 5, 31, 0, 0)] - - Additionally, it supports the following keyword arguments: - - :param cache: - If given, it must be a boolean value specifying to enable or disable - caching of results. If you will use the same rrule instance multiple - times, enabling caching will improve the performance considerably. - :param dtstart: - The recurrence start. Besides being the base for the recurrence, - missing parameters in the final recurrence instances will also be - extracted from this date. If not given, datetime.now() will be used - instead. - :param interval: - The interval between each freq iteration. For example, when using - YEARLY, an interval of 2 means once every two years, but with HOURLY, - it means once every two hours. The default interval is 1. - :param wkst: - The week start day. Must be one of the MO, TU, WE constants, or an - integer, specifying the first day of the week. This will affect - recurrences based on weekly periods. The default week start is got - from calendar.firstweekday(), and may be modified by - calendar.setfirstweekday(). - :param count: - How many occurrences will be generated. - - .. note:: - As of version 2.5.0, the use of the ``until`` keyword together - with the ``count`` keyword is deprecated per RFC-2445 Sec. 4.3.10. - :param until: - If given, this must be a datetime instance, that will specify the - limit of the recurrence. The last recurrence in the rule is the greatest - datetime that is less than or equal to the value specified in the - ``until`` parameter. - - .. note:: - As of version 2.5.0, the use of the ``until`` keyword together - with the ``count`` keyword is deprecated per RFC-2445 Sec. 4.3.10. - :param bysetpos: - If given, it must be either an integer, or a sequence of integers, - positive or negative. Each given integer will specify an occurrence - number, corresponding to the nth occurrence of the rule inside the - frequency period. For example, a bysetpos of -1 if combined with a - MONTHLY frequency, and a byweekday of (MO, TU, WE, TH, FR), will - result in the last work day of every month. - :param bymonth: - If given, it must be either an integer, or a sequence of integers, - meaning the months to apply the recurrence to. - :param bymonthday: - If given, it must be either an integer, or a sequence of integers, - meaning the month days to apply the recurrence to. - :param byyearday: - If given, it must be either an integer, or a sequence of integers, - meaning the year days to apply the recurrence to. - :param byweekno: - If given, it must be either an integer, or a sequence of integers, - meaning the week numbers to apply the recurrence to. Week numbers - have the meaning described in ISO8601, that is, the first week of - the year is that containing at least four days of the new year. - :param byweekday: - If given, it must be either an integer (0 == MO), a sequence of - integers, one of the weekday constants (MO, TU, etc), or a sequence - of these constants. When given, these variables will define the - weekdays where the recurrence will be applied. It's also possible to - use an argument n for the weekday instances, which will mean the nth - occurrence of this weekday in the period. For example, with MONTHLY, - or with YEARLY and BYMONTH, using FR(+1) in byweekday will specify the - first friday of the month where the recurrence happens. Notice that in - the RFC documentation, this is specified as BYDAY, but was renamed to - avoid the ambiguity of that keyword. - :param byhour: - If given, it must be either an integer, or a sequence of integers, - meaning the hours to apply the recurrence to. - :param byminute: - If given, it must be either an integer, or a sequence of integers, - meaning the minutes to apply the recurrence to. - :param bysecond: - If given, it must be either an integer, or a sequence of integers, - meaning the seconds to apply the recurrence to. - :param byeaster: - If given, it must be either an integer, or a sequence of integers, - positive or negative. Each integer will define an offset from the - Easter Sunday. Passing the offset 0 to byeaster will yield the Easter - Sunday itself. This is an extension to the RFC specification. - """ - def __init__(self, freq, dtstart=None, - interval=1, wkst=None, count=None, until=None, bysetpos=None, - bymonth=None, bymonthday=None, byyearday=None, byeaster=None, - byweekno=None, byweekday=None, - byhour=None, byminute=None, bysecond=None, - cache=False): - super(rrule, self).__init__(cache) - global easter - if not dtstart: - dtstart = datetime.datetime.now().replace(microsecond=0) - elif not isinstance(dtstart, datetime.datetime): - dtstart = datetime.datetime.fromordinal(dtstart.toordinal()) - else: - dtstart = dtstart.replace(microsecond=0) - self._dtstart = dtstart - self._tzinfo = dtstart.tzinfo - self._freq = freq - self._interval = interval - self._count = count - - # Cache the original byxxx rules, if they are provided, as the _byxxx - # attributes do not necessarily map to the inputs, and this can be - # a problem in generating the strings. Only store things if they've - # been supplied (the string retrieval will just use .get()) - self._original_rule = {} - - if until and not isinstance(until, datetime.datetime): - until = datetime.datetime.fromordinal(until.toordinal()) - self._until = until - - if count and until: - warn("Using both 'count' and 'until' is inconsistent with RFC 2445" - " and has been deprecated in dateutil. Future versions will " - "raise an error.", DeprecationWarning) - - if wkst is None: - self._wkst = calendar.firstweekday() - elif isinstance(wkst, integer_types): - self._wkst = wkst - else: - self._wkst = wkst.weekday - - if bysetpos is None: - self._bysetpos = None - elif isinstance(bysetpos, integer_types): - if bysetpos == 0 or not (-366 <= bysetpos <= 366): - raise ValueError("bysetpos must be between 1 and 366, " - "or between -366 and -1") - self._bysetpos = (bysetpos,) - else: - self._bysetpos = tuple(bysetpos) - for pos in self._bysetpos: - if pos == 0 or not (-366 <= pos <= 366): - raise ValueError("bysetpos must be between 1 and 366, " - "or between -366 and -1") - - if self._bysetpos: - self._original_rule['bysetpos'] = self._bysetpos - - if (byweekno is None and byyearday is None and bymonthday is None and - byweekday is None and byeaster is None): - if freq == YEARLY: - if bymonth is None: - bymonth = dtstart.month - self._original_rule['bymonth'] = None - bymonthday = dtstart.day - self._original_rule['bymonthday'] = None - elif freq == MONTHLY: - bymonthday = dtstart.day - self._original_rule['bymonthday'] = None - elif freq == WEEKLY: - byweekday = dtstart.weekday() - self._original_rule['byweekday'] = None - - # bymonth - if bymonth is None: - self._bymonth = None - else: - if isinstance(bymonth, integer_types): - bymonth = (bymonth,) - - self._bymonth = tuple(sorted(set(bymonth))) - - if 'bymonth' not in self._original_rule: - self._original_rule['bymonth'] = self._bymonth - - # byyearday - if byyearday is None: - self._byyearday = None - else: - if isinstance(byyearday, integer_types): - byyearday = (byyearday,) - - self._byyearday = tuple(sorted(set(byyearday))) - self._original_rule['byyearday'] = self._byyearday - - # byeaster - if byeaster is not None: - if not easter: - from dateutil import easter - if isinstance(byeaster, integer_types): - self._byeaster = (byeaster,) - else: - self._byeaster = tuple(sorted(byeaster)) - - self._original_rule['byeaster'] = self._byeaster - else: - self._byeaster = None - - # bymonthday - if bymonthday is None: - self._bymonthday = () - self._bynmonthday = () - else: - if isinstance(bymonthday, integer_types): - bymonthday = (bymonthday,) - - bymonthday = set(bymonthday) # Ensure it's unique - - self._bymonthday = tuple(sorted([x for x in bymonthday if x > 0])) - self._bynmonthday = tuple(sorted([x for x in bymonthday if x < 0])) - - # Storing positive numbers first, then negative numbers - if 'bymonthday' not in self._original_rule: - self._original_rule['bymonthday'] = tuple( - itertools.chain(self._bymonthday, self._bynmonthday)) - - # byweekno - if byweekno is None: - self._byweekno = None - else: - if isinstance(byweekno, integer_types): - byweekno = (byweekno,) - - self._byweekno = tuple(sorted(set(byweekno))) - - self._original_rule['byweekno'] = self._byweekno - - # byweekday / bynweekday - if byweekday is None: - self._byweekday = None - self._bynweekday = None - else: - # If it's one of the valid non-sequence types, convert to a - # single-element sequence before the iterator that builds the - # byweekday set. - if isinstance(byweekday, integer_types) or hasattr(byweekday, "n"): - byweekday = (byweekday,) - - self._byweekday = set() - self._bynweekday = set() - for wday in byweekday: - if isinstance(wday, integer_types): - self._byweekday.add(wday) - elif not wday.n or freq > MONTHLY: - self._byweekday.add(wday.weekday) - else: - self._bynweekday.add((wday.weekday, wday.n)) - - if not self._byweekday: - self._byweekday = None - elif not self._bynweekday: - self._bynweekday = None - - if self._byweekday is not None: - self._byweekday = tuple(sorted(self._byweekday)) - orig_byweekday = [weekday(x) for x in self._byweekday] - else: - orig_byweekday = tuple() - - if self._bynweekday is not None: - self._bynweekday = tuple(sorted(self._bynweekday)) - orig_bynweekday = [weekday(*x) for x in self._bynweekday] - else: - orig_bynweekday = tuple() - - if 'byweekday' not in self._original_rule: - self._original_rule['byweekday'] = tuple(itertools.chain( - orig_byweekday, orig_bynweekday)) - - # byhour - if byhour is None: - if freq < HOURLY: - self._byhour = set((dtstart.hour,)) - else: - self._byhour = None - else: - if isinstance(byhour, integer_types): - byhour = (byhour,) - - if freq == HOURLY: - self._byhour = self.__construct_byset(start=dtstart.hour, - byxxx=byhour, - base=24) - else: - self._byhour = set(byhour) - - self._byhour = tuple(sorted(self._byhour)) - self._original_rule['byhour'] = self._byhour - - # byminute - if byminute is None: - if freq < MINUTELY: - self._byminute = set((dtstart.minute,)) - else: - self._byminute = None - else: - if isinstance(byminute, integer_types): - byminute = (byminute,) - - if freq == MINUTELY: - self._byminute = self.__construct_byset(start=dtstart.minute, - byxxx=byminute, - base=60) - else: - self._byminute = set(byminute) - - self._byminute = tuple(sorted(self._byminute)) - self._original_rule['byminute'] = self._byminute - - # bysecond - if bysecond is None: - if freq < SECONDLY: - self._bysecond = ((dtstart.second,)) - else: - self._bysecond = None - else: - if isinstance(bysecond, integer_types): - bysecond = (bysecond,) - - self._bysecond = set(bysecond) - - if freq == SECONDLY: - self._bysecond = self.__construct_byset(start=dtstart.second, - byxxx=bysecond, - base=60) - else: - self._bysecond = set(bysecond) - - self._bysecond = tuple(sorted(self._bysecond)) - self._original_rule['bysecond'] = self._bysecond - - if self._freq >= HOURLY: - self._timeset = None - else: - self._timeset = [] - for hour in self._byhour: - for minute in self._byminute: - for second in self._bysecond: - self._timeset.append( - datetime.time(hour, minute, second, - tzinfo=self._tzinfo)) - self._timeset.sort() - self._timeset = tuple(self._timeset) - - def __str__(self): - """ - Output a string that would generate this RRULE if passed to rrulestr. - This is mostly compatible with RFC2445, except for the - dateutil-specific extension BYEASTER. - """ - - output = [] - h, m, s = [None] * 3 - if self._dtstart: - output.append(self._dtstart.strftime('DTSTART:%Y%m%dT%H%M%S')) - h, m, s = self._dtstart.timetuple()[3:6] - - parts = ['FREQ=' + FREQNAMES[self._freq]] - if self._interval != 1: - parts.append('INTERVAL=' + str(self._interval)) - - if self._wkst: - parts.append('WKST=' + str(self._wkst)) - - if self._count: - parts.append('COUNT=' + str(self._count)) - - if self._until: - parts.append(self._until.strftime('UNTIL=%Y%m%dT%H%M%S')) - - if self._original_rule.get('byweekday') is not None: - # The str() method on weekday objects doesn't generate - # RFC2445-compliant strings, so we should modify that. - original_rule = dict(self._original_rule) - wday_strings = [] - for wday in original_rule['byweekday']: - if wday.n: - wday_strings.append('{n:+d}{wday}'.format( - n=wday.n, - wday=repr(wday)[0:2])) - else: - wday_strings.append(repr(wday)) - - original_rule['byweekday'] = wday_strings - else: - original_rule = self._original_rule - - partfmt = '{name}={vals}' - for name, key in [('BYSETPOS', 'bysetpos'), - ('BYMONTH', 'bymonth'), - ('BYMONTHDAY', 'bymonthday'), - ('BYYEARDAY', 'byyearday'), - ('BYWEEKNO', 'byweekno'), - ('BYDAY', 'byweekday'), - ('BYHOUR', 'byhour'), - ('BYMINUTE', 'byminute'), - ('BYSECOND', 'bysecond'), - ('BYEASTER', 'byeaster')]: - value = original_rule.get(key) - if value: - parts.append(partfmt.format(name=name, vals=(','.join(str(v) - for v in value)))) - - output.append(';'.join(parts)) - return '\n'.join(output) - - def _iter(self): - year, month, day, hour, minute, second, weekday, yearday, _ = \ - self._dtstart.timetuple() - - # Some local variables to speed things up a bit - freq = self._freq - interval = self._interval - wkst = self._wkst - until = self._until - bymonth = self._bymonth - byweekno = self._byweekno - byyearday = self._byyearday - byweekday = self._byweekday - byeaster = self._byeaster - bymonthday = self._bymonthday - bynmonthday = self._bynmonthday - bysetpos = self._bysetpos - byhour = self._byhour - byminute = self._byminute - bysecond = self._bysecond - - ii = _iterinfo(self) - ii.rebuild(year, month) - - getdayset = {YEARLY: ii.ydayset, - MONTHLY: ii.mdayset, - WEEKLY: ii.wdayset, - DAILY: ii.ddayset, - HOURLY: ii.ddayset, - MINUTELY: ii.ddayset, - SECONDLY: ii.ddayset}[freq] - - if freq < HOURLY: - timeset = self._timeset - else: - gettimeset = {HOURLY: ii.htimeset, - MINUTELY: ii.mtimeset, - SECONDLY: ii.stimeset}[freq] - if ((freq >= HOURLY and - self._byhour and hour not in self._byhour) or - (freq >= MINUTELY and - self._byminute and minute not in self._byminute) or - (freq >= SECONDLY and - self._bysecond and second not in self._bysecond)): - timeset = () - else: - timeset = gettimeset(hour, minute, second) - - total = 0 - count = self._count - while True: - # Get dayset with the right frequency - dayset, start, end = getdayset(year, month, day) - - # Do the "hard" work ;-) - filtered = False - for i in dayset[start:end]: - if ((bymonth and ii.mmask[i] not in bymonth) or - (byweekno and not ii.wnomask[i]) or - (byweekday and ii.wdaymask[i] not in byweekday) or - (ii.nwdaymask and not ii.nwdaymask[i]) or - (byeaster and not ii.eastermask[i]) or - ((bymonthday or bynmonthday) and - ii.mdaymask[i] not in bymonthday and - ii.nmdaymask[i] not in bynmonthday) or - (byyearday and - ((i < ii.yearlen and i+1 not in byyearday and - -ii.yearlen+i not in byyearday) or - (i >= ii.yearlen and i+1-ii.yearlen not in byyearday and - -ii.nextyearlen+i-ii.yearlen not in byyearday)))): - dayset[i] = None - filtered = True - - # Output results - if bysetpos and timeset: - poslist = [] - for pos in bysetpos: - if pos < 0: - daypos, timepos = divmod(pos, len(timeset)) - else: - daypos, timepos = divmod(pos-1, len(timeset)) - try: - i = [x for x in dayset[start:end] - if x is not None][daypos] - time = timeset[timepos] - except IndexError: - pass - else: - date = datetime.date.fromordinal(ii.yearordinal+i) - res = datetime.datetime.combine(date, time) - if res not in poslist: - poslist.append(res) - poslist.sort() - for res in poslist: - if until and res > until: - self._len = total - return - elif res >= self._dtstart: - total += 1 - yield res - if count: - count -= 1 - if not count: - self._len = total - return - else: - for i in dayset[start:end]: - if i is not None: - date = datetime.date.fromordinal(ii.yearordinal + i) - for time in timeset: - res = datetime.datetime.combine(date, time) - if until and res > until: - self._len = total - return - elif res >= self._dtstart: - total += 1 - yield res - if count: - count -= 1 - if not count: - self._len = total - return - - # Handle frequency and interval - fixday = False - if freq == YEARLY: - year += interval - if year > datetime.MAXYEAR: - self._len = total - return - ii.rebuild(year, month) - elif freq == MONTHLY: - month += interval - if month > 12: - div, mod = divmod(month, 12) - month = mod - year += div - if month == 0: - month = 12 - year -= 1 - if year > datetime.MAXYEAR: - self._len = total - return - ii.rebuild(year, month) - elif freq == WEEKLY: - if wkst > weekday: - day += -(weekday+1+(6-wkst))+self._interval*7 - else: - day += -(weekday-wkst)+self._interval*7 - weekday = wkst - fixday = True - elif freq == DAILY: - day += interval - fixday = True - elif freq == HOURLY: - if filtered: - # Jump to one iteration before next day - hour += ((23-hour)//interval)*interval - - if byhour: - ndays, hour = self.__mod_distance(value=hour, - byxxx=self._byhour, - base=24) - else: - ndays, hour = divmod(hour+interval, 24) - - if ndays: - day += ndays - fixday = True - - timeset = gettimeset(hour, minute, second) - elif freq == MINUTELY: - if filtered: - # Jump to one iteration before next day - minute += ((1439-(hour*60+minute))//interval)*interval - - valid = False - rep_rate = (24*60) - for j in range(rep_rate // gcd(interval, rep_rate)): - if byminute: - nhours, minute = \ - self.__mod_distance(value=minute, - byxxx=self._byminute, - base=60) - else: - nhours, minute = divmod(minute+interval, 60) - - div, hour = divmod(hour+nhours, 24) - if div: - day += div - fixday = True - filtered = False - - if not byhour or hour in byhour: - valid = True - break - - if not valid: - raise ValueError('Invalid combination of interval and ' + - 'byhour resulting in empty rule.') - - timeset = gettimeset(hour, minute, second) - elif freq == SECONDLY: - if filtered: - # Jump to one iteration before next day - second += (((86399 - (hour * 3600 + minute * 60 + second)) - // interval) * interval) - - rep_rate = (24 * 3600) - valid = False - for j in range(0, rep_rate // gcd(interval, rep_rate)): - if bysecond: - nminutes, second = \ - self.__mod_distance(value=second, - byxxx=self._bysecond, - base=60) - else: - nminutes, second = divmod(second+interval, 60) - - div, minute = divmod(minute+nminutes, 60) - if div: - hour += div - div, hour = divmod(hour, 24) - if div: - day += div - fixday = True - - if ((not byhour or hour in byhour) and - (not byminute or minute in byminute) and - (not bysecond or second in bysecond)): - valid = True - break - - if not valid: - raise ValueError('Invalid combination of interval, ' + - 'byhour and byminute resulting in empty' + - ' rule.') - - timeset = gettimeset(hour, minute, second) - - if fixday and day > 28: - daysinmonth = calendar.monthrange(year, month)[1] - if day > daysinmonth: - while day > daysinmonth: - day -= daysinmonth - month += 1 - if month == 13: - month = 1 - year += 1 - if year > datetime.MAXYEAR: - self._len = total - return - daysinmonth = calendar.monthrange(year, month)[1] - ii.rebuild(year, month) - - def __construct_byset(self, start, byxxx, base): - """ - If a `BYXXX` sequence is passed to the constructor at the same level as - `FREQ` (e.g. `FREQ=HOURLY,BYHOUR={2,4,7},INTERVAL=3`), there are some - specifications which cannot be reached given some starting conditions. - - This occurs whenever the interval is not coprime with the base of a - given unit and the difference between the starting position and the - ending position is not coprime with the greatest common denominator - between the interval and the base. For example, with a FREQ of hourly - starting at 17:00 and an interval of 4, the only valid values for - BYHOUR would be {21, 1, 5, 9, 13, 17}, because 4 and 24 are not - coprime. - - :param start: - Specifies the starting position. - :param byxxx: - An iterable containing the list of allowed values. - :param base: - The largest allowable value for the specified frequency (e.g. - 24 hours, 60 minutes). - - This does not preserve the type of the iterable, returning a set, since - the values should be unique and the order is irrelevant, this will - speed up later lookups. - - In the event of an empty set, raises a :exception:`ValueError`, as this - results in an empty rrule. - """ - - cset = set() - - # Support a single byxxx value. - if isinstance(byxxx, integer_types): - byxxx = (byxxx, ) - - for num in byxxx: - i_gcd = gcd(self._interval, base) - # Use divmod rather than % because we need to wrap negative nums. - if i_gcd == 1 or divmod(num - start, i_gcd)[1] == 0: - cset.add(num) - - if len(cset) == 0: - raise ValueError("Invalid rrule byxxx generates an empty set.") - - return cset - - def __mod_distance(self, value, byxxx, base): - """ - Calculates the next value in a sequence where the `FREQ` parameter is - specified along with a `BYXXX` parameter at the same "level" - (e.g. `HOURLY` specified with `BYHOUR`). - - :param value: - The old value of the component. - :param byxxx: - The `BYXXX` set, which should have been generated by - `rrule._construct_byset`, or something else which checks that a - valid rule is present. - :param base: - The largest allowable value for the specified frequency (e.g. - 24 hours, 60 minutes). - - If a valid value is not found after `base` iterations (the maximum - number before the sequence would start to repeat), this raises a - :exception:`ValueError`, as no valid values were found. - - This returns a tuple of `divmod(n*interval, base)`, where `n` is the - smallest number of `interval` repetitions until the next specified - value in `byxxx` is found. - """ - accumulator = 0 - for ii in range(1, base + 1): - # Using divmod() over % to account for negative intervals - div, value = divmod(value + self._interval, base) - accumulator += div - if value in byxxx: - return (accumulator, value) - - -class _iterinfo(object): - __slots__ = ["rrule", "lastyear", "lastmonth", - "yearlen", "nextyearlen", "yearordinal", "yearweekday", - "mmask", "mrange", "mdaymask", "nmdaymask", - "wdaymask", "wnomask", "nwdaymask", "eastermask"] - - def __init__(self, rrule): - for attr in self.__slots__: - setattr(self, attr, None) - self.rrule = rrule - - def rebuild(self, year, month): - # Every mask is 7 days longer to handle cross-year weekly periods. - rr = self.rrule - if year != self.lastyear: - self.yearlen = 365 + calendar.isleap(year) - self.nextyearlen = 365 + calendar.isleap(year + 1) - firstyday = datetime.date(year, 1, 1) - self.yearordinal = firstyday.toordinal() - self.yearweekday = firstyday.weekday() - - wday = datetime.date(year, 1, 1).weekday() - if self.yearlen == 365: - self.mmask = M365MASK - self.mdaymask = MDAY365MASK - self.nmdaymask = NMDAY365MASK - self.wdaymask = WDAYMASK[wday:] - self.mrange = M365RANGE - else: - self.mmask = M366MASK - self.mdaymask = MDAY366MASK - self.nmdaymask = NMDAY366MASK - self.wdaymask = WDAYMASK[wday:] - self.mrange = M366RANGE - - if not rr._byweekno: - self.wnomask = None - else: - self.wnomask = [0]*(self.yearlen+7) - # no1wkst = firstwkst = self.wdaymask.index(rr._wkst) - no1wkst = firstwkst = (7-self.yearweekday+rr._wkst) % 7 - if no1wkst >= 4: - no1wkst = 0 - # Number of days in the year, plus the days we got - # from last year. - wyearlen = self.yearlen+(self.yearweekday-rr._wkst) % 7 - else: - # Number of days in the year, minus the days we - # left in last year. - wyearlen = self.yearlen-no1wkst - div, mod = divmod(wyearlen, 7) - numweeks = div+mod//4 - for n in rr._byweekno: - if n < 0: - n += numweeks+1 - if not (0 < n <= numweeks): - continue - if n > 1: - i = no1wkst+(n-1)*7 - if no1wkst != firstwkst: - i -= 7-firstwkst - else: - i = no1wkst - for j in range(7): - self.wnomask[i] = 1 - i += 1 - if self.wdaymask[i] == rr._wkst: - break - if 1 in rr._byweekno: - # Check week number 1 of next year as well - # TODO: Check -numweeks for next year. - i = no1wkst+numweeks*7 - if no1wkst != firstwkst: - i -= 7-firstwkst - if i < self.yearlen: - # If week starts in next year, we - # don't care about it. - for j in range(7): - self.wnomask[i] = 1 - i += 1 - if self.wdaymask[i] == rr._wkst: - break - if no1wkst: - # Check last week number of last year as - # well. If no1wkst is 0, either the year - # started on week start, or week number 1 - # got days from last year, so there are no - # days from last year's last week number in - # this year. - if -1 not in rr._byweekno: - lyearweekday = datetime.date(year-1, 1, 1).weekday() - lno1wkst = (7-lyearweekday+rr._wkst) % 7 - lyearlen = 365+calendar.isleap(year-1) - if lno1wkst >= 4: - lno1wkst = 0 - lnumweeks = 52+(lyearlen + - (lyearweekday-rr._wkst) % 7) % 7//4 - else: - lnumweeks = 52+(self.yearlen-no1wkst) % 7//4 - else: - lnumweeks = -1 - if lnumweeks in rr._byweekno: - for i in range(no1wkst): - self.wnomask[i] = 1 - - if (rr._bynweekday and (month != self.lastmonth or - year != self.lastyear)): - ranges = [] - if rr._freq == YEARLY: - if rr._bymonth: - for month in rr._bymonth: - ranges.append(self.mrange[month-1:month+1]) - else: - ranges = [(0, self.yearlen)] - elif rr._freq == MONTHLY: - ranges = [self.mrange[month-1:month+1]] - if ranges: - # Weekly frequency won't get here, so we may not - # care about cross-year weekly periods. - self.nwdaymask = [0]*self.yearlen - for first, last in ranges: - last -= 1 - for wday, n in rr._bynweekday: - if n < 0: - i = last+(n+1)*7 - i -= (self.wdaymask[i]-wday) % 7 - else: - i = first+(n-1)*7 - i += (7-self.wdaymask[i]+wday) % 7 - if first <= i <= last: - self.nwdaymask[i] = 1 - - if rr._byeaster: - self.eastermask = [0]*(self.yearlen+7) - eyday = easter.easter(year).toordinal()-self.yearordinal - for offset in rr._byeaster: - self.eastermask[eyday+offset] = 1 - - self.lastyear = year - self.lastmonth = month - - def ydayset(self, year, month, day): - return list(range(self.yearlen)), 0, self.yearlen - - def mdayset(self, year, month, day): - dset = [None]*self.yearlen - start, end = self.mrange[month-1:month+1] - for i in range(start, end): - dset[i] = i - return dset, start, end - - def wdayset(self, year, month, day): - # We need to handle cross-year weeks here. - dset = [None]*(self.yearlen+7) - i = datetime.date(year, month, day).toordinal()-self.yearordinal - start = i - for j in range(7): - dset[i] = i - i += 1 - # if (not (0 <= i < self.yearlen) or - # self.wdaymask[i] == self.rrule._wkst): - # This will cross the year boundary, if necessary. - if self.wdaymask[i] == self.rrule._wkst: - break - return dset, start, i - - def ddayset(self, year, month, day): - dset = [None] * self.yearlen - i = datetime.date(year, month, day).toordinal() - self.yearordinal - dset[i] = i - return dset, i, i + 1 - - def htimeset(self, hour, minute, second): - tset = [] - rr = self.rrule - for minute in rr._byminute: - for second in rr._bysecond: - tset.append(datetime.time(hour, minute, second, - tzinfo=rr._tzinfo)) - tset.sort() - return tset - - def mtimeset(self, hour, minute, second): - tset = [] - rr = self.rrule - for second in rr._bysecond: - tset.append(datetime.time(hour, minute, second, tzinfo=rr._tzinfo)) - tset.sort() - return tset - - def stimeset(self, hour, minute, second): - return (datetime.time(hour, minute, second, - tzinfo=self.rrule._tzinfo),) - - -class rruleset(rrulebase): - """ The rruleset type allows more complex recurrence setups, mixing - multiple rules, dates, exclusion rules, and exclusion dates. The type - constructor takes the following keyword arguments: - - :param cache: If True, caching of results will be enabled, improving - performance of multiple queries considerably. """ - - class _genitem(object): - def __init__(self, genlist, gen): - try: - self.dt = advance_iterator(gen) - genlist.append(self) - except StopIteration: - pass - self.genlist = genlist - self.gen = gen - - def __next__(self): - try: - self.dt = advance_iterator(self.gen) - except StopIteration: - if self.genlist[0] is self: - heapq.heappop(self.genlist) - else: - self.genlist.remove(self) - heapq.heapify(self.genlist) - - next = __next__ - - def __lt__(self, other): - return self.dt < other.dt - - def __gt__(self, other): - return self.dt > other.dt - - def __eq__(self, other): - return self.dt == other.dt - - def __ne__(self, other): - return self.dt != other.dt - - def __init__(self, cache=False): - super(rruleset, self).__init__(cache) - self._rrule = [] - self._rdate = [] - self._exrule = [] - self._exdate = [] - - @_invalidates_cache - def rrule(self, rrule): - """ Include the given :py:class:`rrule` instance in the recurrence set - generation. """ - self._rrule.append(rrule) - - @_invalidates_cache - def rdate(self, rdate): - """ Include the given :py:class:`datetime` instance in the recurrence - set generation. """ - self._rdate.append(rdate) - - @_invalidates_cache - def exrule(self, exrule): - """ Include the given rrule instance in the recurrence set exclusion - list. Dates which are part of the given recurrence rules will not - be generated, even if some inclusive rrule or rdate matches them. - """ - self._exrule.append(exrule) - - @_invalidates_cache - def exdate(self, exdate): - """ Include the given datetime instance in the recurrence set - exclusion list. Dates included that way will not be generated, - even if some inclusive rrule or rdate matches them. """ - self._exdate.append(exdate) - - def _iter(self): - rlist = [] - self._rdate.sort() - self._genitem(rlist, iter(self._rdate)) - for gen in [iter(x) for x in self._rrule]: - self._genitem(rlist, gen) - exlist = [] - self._exdate.sort() - self._genitem(exlist, iter(self._exdate)) - for gen in [iter(x) for x in self._exrule]: - self._genitem(exlist, gen) - lastdt = None - total = 0 - heapq.heapify(rlist) - heapq.heapify(exlist) - while rlist: - ritem = rlist[0] - if not lastdt or lastdt != ritem.dt: - while exlist and exlist[0] < ritem: - exitem = exlist[0] - advance_iterator(exitem) - if exlist and exlist[0] is exitem: - heapq.heapreplace(exlist, exitem) - if not exlist or ritem != exlist[0]: - total += 1 - yield ritem.dt - lastdt = ritem.dt - advance_iterator(ritem) - if rlist and rlist[0] is ritem: - heapq.heapreplace(rlist, ritem) - self._len = total - - -class _rrulestr(object): - - _freq_map = {"YEARLY": YEARLY, - "MONTHLY": MONTHLY, - "WEEKLY": WEEKLY, - "DAILY": DAILY, - "HOURLY": HOURLY, - "MINUTELY": MINUTELY, - "SECONDLY": SECONDLY} - - _weekday_map = {"MO": 0, "TU": 1, "WE": 2, "TH": 3, - "FR": 4, "SA": 5, "SU": 6} - - def _handle_int(self, rrkwargs, name, value, **kwargs): - rrkwargs[name.lower()] = int(value) - - def _handle_int_list(self, rrkwargs, name, value, **kwargs): - rrkwargs[name.lower()] = [int(x) for x in value.split(',')] - - _handle_INTERVAL = _handle_int - _handle_COUNT = _handle_int - _handle_BYSETPOS = _handle_int_list - _handle_BYMONTH = _handle_int_list - _handle_BYMONTHDAY = _handle_int_list - _handle_BYYEARDAY = _handle_int_list - _handle_BYEASTER = _handle_int_list - _handle_BYWEEKNO = _handle_int_list - _handle_BYHOUR = _handle_int_list - _handle_BYMINUTE = _handle_int_list - _handle_BYSECOND = _handle_int_list - - def _handle_FREQ(self, rrkwargs, name, value, **kwargs): - rrkwargs["freq"] = self._freq_map[value] - - def _handle_UNTIL(self, rrkwargs, name, value, **kwargs): - global parser - if not parser: - from dateutil import parser - try: - rrkwargs["until"] = parser.parse(value, - ignoretz=kwargs.get("ignoretz"), - tzinfos=kwargs.get("tzinfos")) - except ValueError: - raise ValueError("invalid until date") - - def _handle_WKST(self, rrkwargs, name, value, **kwargs): - rrkwargs["wkst"] = self._weekday_map[value] - - def _handle_BYWEEKDAY(self, rrkwargs, name, value, **kwargs): - """ - Two ways to specify this: +1MO or MO(+1) - """ - l = [] - for wday in value.split(','): - if '(' in wday: - # If it's of the form TH(+1), etc. - splt = wday.split('(') - w = splt[0] - n = int(splt[1][:-1]) - elif len(wday): - # If it's of the form +1MO - for i in range(len(wday)): - if wday[i] not in '+-0123456789': - break - n = wday[:i] or None - w = wday[i:] - if n: - n = int(n) - else: - raise ValueError("Invalid (empty) BYDAY specification.") - - l.append(weekdays[self._weekday_map[w]](n)) - rrkwargs["byweekday"] = l - - _handle_BYDAY = _handle_BYWEEKDAY - - def _parse_rfc_rrule(self, line, - dtstart=None, - cache=False, - ignoretz=False, - tzinfos=None): - if line.find(':') != -1: - name, value = line.split(':') - if name != "RRULE": - raise ValueError("unknown parameter name") - else: - value = line - rrkwargs = {} - for pair in value.split(';'): - name, value = pair.split('=') - name = name.upper() - value = value.upper() - try: - getattr(self, "_handle_"+name)(rrkwargs, name, value, - ignoretz=ignoretz, - tzinfos=tzinfos) - except AttributeError: - raise ValueError("unknown parameter '%s'" % name) - except (KeyError, ValueError): - raise ValueError("invalid '%s': %s" % (name, value)) - return rrule(dtstart=dtstart, cache=cache, **rrkwargs) - - def _parse_rfc(self, s, - dtstart=None, - cache=False, - unfold=False, - forceset=False, - compatible=False, - ignoretz=False, - tzinfos=None): - global parser - if compatible: - forceset = True - unfold = True - s = s.upper() - if not s.strip(): - raise ValueError("empty string") - if unfold: - lines = s.splitlines() - i = 0 - while i < len(lines): - line = lines[i].rstrip() - if not line: - del lines[i] - elif i > 0 and line[0] == " ": - lines[i-1] += line[1:] - del lines[i] - else: - i += 1 - else: - lines = s.split() - if (not forceset and len(lines) == 1 and (s.find(':') == -1 or - s.startswith('RRULE:'))): - return self._parse_rfc_rrule(lines[0], cache=cache, - dtstart=dtstart, ignoretz=ignoretz, - tzinfos=tzinfos) - else: - rrulevals = [] - rdatevals = [] - exrulevals = [] - exdatevals = [] - for line in lines: - if not line: - continue - if line.find(':') == -1: - name = "RRULE" - value = line - else: - name, value = line.split(':', 1) - parms = name.split(';') - if not parms: - raise ValueError("empty property name") - name = parms[0] - parms = parms[1:] - if name == "RRULE": - for parm in parms: - raise ValueError("unsupported RRULE parm: "+parm) - rrulevals.append(value) - elif name == "RDATE": - for parm in parms: - if parm != "VALUE=DATE-TIME": - raise ValueError("unsupported RDATE parm: "+parm) - rdatevals.append(value) - elif name == "EXRULE": - for parm in parms: - raise ValueError("unsupported EXRULE parm: "+parm) - exrulevals.append(value) - elif name == "EXDATE": - for parm in parms: - if parm != "VALUE=DATE-TIME": - raise ValueError("unsupported RDATE parm: "+parm) - exdatevals.append(value) - elif name == "DTSTART": - for parm in parms: - raise ValueError("unsupported DTSTART parm: "+parm) - if not parser: - from dateutil import parser - dtstart = parser.parse(value, ignoretz=ignoretz, - tzinfos=tzinfos) - else: - raise ValueError("unsupported property: "+name) - if (forceset or len(rrulevals) > 1 or rdatevals - or exrulevals or exdatevals): - if not parser and (rdatevals or exdatevals): - from dateutil import parser - rset = rruleset(cache=cache) - for value in rrulevals: - rset.rrule(self._parse_rfc_rrule(value, dtstart=dtstart, - ignoretz=ignoretz, - tzinfos=tzinfos)) - for value in rdatevals: - for datestr in value.split(','): - rset.rdate(parser.parse(datestr, - ignoretz=ignoretz, - tzinfos=tzinfos)) - for value in exrulevals: - rset.exrule(self._parse_rfc_rrule(value, dtstart=dtstart, - ignoretz=ignoretz, - tzinfos=tzinfos)) - for value in exdatevals: - for datestr in value.split(','): - rset.exdate(parser.parse(datestr, - ignoretz=ignoretz, - tzinfos=tzinfos)) - if compatible and dtstart: - rset.rdate(dtstart) - return rset - else: - return self._parse_rfc_rrule(rrulevals[0], - dtstart=dtstart, - cache=cache, - ignoretz=ignoretz, - tzinfos=tzinfos) - - def __call__(self, s, **kwargs): - return self._parse_rfc(s, **kwargs) - -rrulestr = _rrulestr() - -# vim:ts=4:sw=4:et diff --git a/venv/lib/python2.7/site-packages/dateutil/tz/__init__.py b/venv/lib/python2.7/site-packages/dateutil/tz/__init__.py deleted file mode 100644 index 1cba7b9..0000000 --- a/venv/lib/python2.7/site-packages/dateutil/tz/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .tz import * - -__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", - "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz"] diff --git a/venv/lib/python2.7/site-packages/dateutil/tz/_common.py b/venv/lib/python2.7/site-packages/dateutil/tz/_common.py deleted file mode 100644 index bbce0fb..0000000 --- a/venv/lib/python2.7/site-packages/dateutil/tz/_common.py +++ /dev/null @@ -1,18 +0,0 @@ -from six import PY3 - -__all__ = ['tzname_in_python2'] - -def tzname_in_python2(namefunc): - """Change unicode output into bytestrings in Python 2 - - tzname() API changed in Python 3. It used to return bytes, but was changed - to unicode strings - """ - def adjust_encoding(*args, **kwargs): - name = namefunc(*args, **kwargs) - if name is not None and not PY3: - name = name.encode() - - return name - - return adjust_encoding \ No newline at end of file diff --git a/venv/lib/python2.7/site-packages/dateutil/tz/tz.py b/venv/lib/python2.7/site-packages/dateutil/tz/tz.py deleted file mode 100644 index ecf1ee1..0000000 --- a/venv/lib/python2.7/site-packages/dateutil/tz/tz.py +++ /dev/null @@ -1,979 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module offers timezone implementations subclassing the abstract -:py:`datetime.tzinfo` type. There are classes to handle tzfile format files -(usually are in :file:`/etc/localtime`, :file:`/usr/share/zoneinfo`, etc), TZ -environment string (in all known formats), given ranges (with help from -relative deltas), local machine timezone, fixed offset timezone, and UTC -timezone. -""" -import datetime -import struct -import time -import sys -import os - -from six import string_types, PY3 -from ._common import tzname_in_python2 - -try: - from .win import tzwin, tzwinlocal -except ImportError: - tzwin = tzwinlocal = None - -relativedelta = None -parser = None -rrule = None - -ZERO = datetime.timedelta(0) -EPOCHORDINAL = datetime.datetime.utcfromtimestamp(0).toordinal() - -class tzutc(datetime.tzinfo): - - def utcoffset(self, dt): - return ZERO - - def dst(self, dt): - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - return "UTC" - - def __eq__(self, other): - return (isinstance(other, tzutc) or - (isinstance(other, tzoffset) and other._offset == ZERO)) - - def __ne__(self, other): - return not self.__eq__(other) - - def __repr__(self): - return "%s()" % self.__class__.__name__ - - __reduce__ = object.__reduce__ - - -class tzoffset(datetime.tzinfo): - - def __init__(self, name, offset): - self._name = name - self._offset = datetime.timedelta(seconds=offset) - - def utcoffset(self, dt): - return self._offset - - def dst(self, dt): - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - return self._name - - def __eq__(self, other): - return (isinstance(other, tzoffset) and - self._offset == other._offset) - - def __ne__(self, other): - return not self.__eq__(other) - - def __repr__(self): - return "%s(%s, %s)" % (self.__class__.__name__, - repr(self._name), - self._offset.days*86400+self._offset.seconds) - - __reduce__ = object.__reduce__ - - -class tzlocal(datetime.tzinfo): - def __init__(self): - self._std_offset = datetime.timedelta(seconds=-time.timezone) - if time.daylight: - self._dst_offset = datetime.timedelta(seconds=-time.altzone) - else: - self._dst_offset = self._std_offset - - def utcoffset(self, dt): - if dt is None: - return dt - - if self._isdst(dt): - return self._dst_offset - else: - return self._std_offset - - def dst(self, dt): - if self._isdst(dt): - return self._dst_offset-self._std_offset - else: - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - return time.tzname[self._isdst(dt)] - - def _isdst(self, dt): - # We can't use mktime here. It is unstable when deciding if - # the hour near to a change is DST or not. - # - # timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, - # dt.minute, dt.second, dt.weekday(), 0, -1)) - # return time.localtime(timestamp).tm_isdst - # - # The code above yields the following result: - # - # >>> import tz, datetime - # >>> t = tz.tzlocal() - # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() - # 'BRDT' - # >>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname() - # 'BRST' - # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() - # 'BRST' - # >>> datetime.datetime(2003,2,15,22,tzinfo=t).tzname() - # 'BRDT' - # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() - # 'BRDT' - # - # Here is a more stable implementation: - # - timestamp = ((dt.toordinal() - EPOCHORDINAL) * 86400 - + dt.hour * 3600 - + dt.minute * 60 - + dt.second) - return time.localtime(timestamp+time.timezone).tm_isdst - - def __eq__(self, other): - return (isinstance(other, tzlocal) and - (self._std_offset == other._std_offset and - self._dst_offset == other._dst_offset)) - - def __ne__(self, other): - return not self.__eq__(other) - - def __repr__(self): - return "%s()" % self.__class__.__name__ - - __reduce__ = object.__reduce__ - - -class _ttinfo(object): - __slots__ = ["offset", "delta", "isdst", "abbr", "isstd", "isgmt"] - - def __init__(self): - for attr in self.__slots__: - setattr(self, attr, None) - - def __repr__(self): - l = [] - for attr in self.__slots__: - value = getattr(self, attr) - if value is not None: - l.append("%s=%s" % (attr, repr(value))) - return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) - - def __eq__(self, other): - if not isinstance(other, _ttinfo): - return False - return (self.offset == other.offset and - self.delta == other.delta and - self.isdst == other.isdst and - self.abbr == other.abbr and - self.isstd == other.isstd and - self.isgmt == other.isgmt) - - def __ne__(self, other): - return not self.__eq__(other) - - def __getstate__(self): - state = {} - for name in self.__slots__: - state[name] = getattr(self, name, None) - return state - - def __setstate__(self, state): - for name in self.__slots__: - if name in state: - setattr(self, name, state[name]) - - -class tzfile(datetime.tzinfo): - - # http://www.twinsun.com/tz/tz-link.htm - # ftp://ftp.iana.org/tz/tz*.tar.gz - - def __init__(self, fileobj, filename=None): - file_opened_here = False - if isinstance(fileobj, string_types): - self._filename = fileobj - fileobj = open(fileobj, 'rb') - file_opened_here = True - elif filename is not None: - self._filename = filename - elif hasattr(fileobj, "name"): - self._filename = fileobj.name - else: - self._filename = repr(fileobj) - - # From tzfile(5): - # - # The time zone information files used by tzset(3) - # begin with the magic characters "TZif" to identify - # them as time zone information files, followed by - # sixteen bytes reserved for future use, followed by - # six four-byte values of type long, written in a - # ``standard'' byte order (the high-order byte - # of the value is written first). - try: - if fileobj.read(4).decode() != "TZif": - raise ValueError("magic not found") - - fileobj.read(16) - - ( - # The number of UTC/local indicators stored in the file. - ttisgmtcnt, - - # The number of standard/wall indicators stored in the file. - ttisstdcnt, - - # The number of leap seconds for which data is - # stored in the file. - leapcnt, - - # The number of "transition times" for which data - # is stored in the file. - timecnt, - - # The number of "local time types" for which data - # is stored in the file (must not be zero). - typecnt, - - # The number of characters of "time zone - # abbreviation strings" stored in the file. - charcnt, - - ) = struct.unpack(">6l", fileobj.read(24)) - - # The above header is followed by tzh_timecnt four-byte - # values of type long, sorted in ascending order. - # These values are written in ``standard'' byte order. - # Each is used as a transition time (as returned by - # time(2)) at which the rules for computing local time - # change. - - if timecnt: - self._trans_list = struct.unpack(">%dl" % timecnt, - fileobj.read(timecnt*4)) - else: - self._trans_list = [] - - # Next come tzh_timecnt one-byte values of type unsigned - # char; each one tells which of the different types of - # ``local time'' types described in the file is associated - # with the same-indexed transition time. These values - # serve as indices into an array of ttinfo structures that - # appears next in the file. - - if timecnt: - self._trans_idx = struct.unpack(">%dB" % timecnt, - fileobj.read(timecnt)) - else: - self._trans_idx = [] - - # Each ttinfo structure is written as a four-byte value - # for tt_gmtoff of type long, in a standard byte - # order, followed by a one-byte value for tt_isdst - # and a one-byte value for tt_abbrind. In each - # structure, tt_gmtoff gives the number of - # seconds to be added to UTC, tt_isdst tells whether - # tm_isdst should be set by localtime(3), and - # tt_abbrind serves as an index into the array of - # time zone abbreviation characters that follow the - # ttinfo structure(s) in the file. - - ttinfo = [] - - for i in range(typecnt): - ttinfo.append(struct.unpack(">lbb", fileobj.read(6))) - - abbr = fileobj.read(charcnt).decode() - - # Then there are tzh_leapcnt pairs of four-byte - # values, written in standard byte order; the - # first value of each pair gives the time (as - # returned by time(2)) at which a leap second - # occurs; the second gives the total number of - # leap seconds to be applied after the given time. - # The pairs of values are sorted in ascending order - # by time. - - # Not used, for now (but read anyway for correct file position) - if leapcnt: - leap = struct.unpack(">%dl" % (leapcnt*2), - fileobj.read(leapcnt*8)) - - # Then there are tzh_ttisstdcnt standard/wall - # indicators, each stored as a one-byte value; - # they tell whether the transition times associated - # with local time types were specified as standard - # time or wall clock time, and are used when - # a time zone file is used in handling POSIX-style - # time zone environment variables. - - if ttisstdcnt: - isstd = struct.unpack(">%db" % ttisstdcnt, - fileobj.read(ttisstdcnt)) - - # Finally, there are tzh_ttisgmtcnt UTC/local - # indicators, each stored as a one-byte value; - # they tell whether the transition times associated - # with local time types were specified as UTC or - # local time, and are used when a time zone file - # is used in handling POSIX-style time zone envi- - # ronment variables. - - if ttisgmtcnt: - isgmt = struct.unpack(">%db" % ttisgmtcnt, - fileobj.read(ttisgmtcnt)) - - # ** Everything has been read ** - finally: - if file_opened_here: - fileobj.close() - - # Build ttinfo list - self._ttinfo_list = [] - for i in range(typecnt): - gmtoff, isdst, abbrind = ttinfo[i] - # Round to full-minutes if that's not the case. Python's - # datetime doesn't accept sub-minute timezones. Check - # http://python.org/sf/1447945 for some information. - gmtoff = (gmtoff+30)//60*60 - tti = _ttinfo() - tti.offset = gmtoff - tti.delta = datetime.timedelta(seconds=gmtoff) - tti.isdst = isdst - tti.abbr = abbr[abbrind:abbr.find('\x00', abbrind)] - tti.isstd = (ttisstdcnt > i and isstd[i] != 0) - tti.isgmt = (ttisgmtcnt > i and isgmt[i] != 0) - self._ttinfo_list.append(tti) - - # Replace ttinfo indexes for ttinfo objects. - trans_idx = [] - for idx in self._trans_idx: - trans_idx.append(self._ttinfo_list[idx]) - self._trans_idx = tuple(trans_idx) - - # Set standard, dst, and before ttinfos. before will be - # used when a given time is before any transitions, - # and will be set to the first non-dst ttinfo, or to - # the first dst, if all of them are dst. - self._ttinfo_std = None - self._ttinfo_dst = None - self._ttinfo_before = None - if self._ttinfo_list: - if not self._trans_list: - self._ttinfo_std = self._ttinfo_first = self._ttinfo_list[0] - else: - for i in range(timecnt-1, -1, -1): - tti = self._trans_idx[i] - if not self._ttinfo_std and not tti.isdst: - self._ttinfo_std = tti - elif not self._ttinfo_dst and tti.isdst: - self._ttinfo_dst = tti - if self._ttinfo_std and self._ttinfo_dst: - break - else: - if self._ttinfo_dst and not self._ttinfo_std: - self._ttinfo_std = self._ttinfo_dst - - for tti in self._ttinfo_list: - if not tti.isdst: - self._ttinfo_before = tti - break - else: - self._ttinfo_before = self._ttinfo_list[0] - - # Now fix transition times to become relative to wall time. - # - # I'm not sure about this. In my tests, the tz source file - # is setup to wall time, and in the binary file isstd and - # isgmt are off, so it should be in wall time. OTOH, it's - # always in gmt time. Let me know if you have comments - # about this. - laststdoffset = 0 - self._trans_list = list(self._trans_list) - for i in range(len(self._trans_list)): - tti = self._trans_idx[i] - if not tti.isdst: - # This is std time. - self._trans_list[i] += tti.offset - laststdoffset = tti.offset - else: - # This is dst time. Convert to std. - self._trans_list[i] += laststdoffset - self._trans_list = tuple(self._trans_list) - - def _find_ttinfo(self, dt, laststd=0): - timestamp = ((dt.toordinal() - EPOCHORDINAL) * 86400 - + dt.hour * 3600 - + dt.minute * 60 - + dt.second) - idx = 0 - for trans in self._trans_list: - if timestamp < trans: - break - idx += 1 - else: - return self._ttinfo_std - if idx == 0: - return self._ttinfo_before - if laststd: - while idx > 0: - tti = self._trans_idx[idx-1] - if not tti.isdst: - return tti - idx -= 1 - else: - return self._ttinfo_std - else: - return self._trans_idx[idx-1] - - def utcoffset(self, dt): - if dt is None: - return None - - if not self._ttinfo_std: - return ZERO - return self._find_ttinfo(dt).delta - - def dst(self, dt): - if not self._ttinfo_dst: - return ZERO - tti = self._find_ttinfo(dt) - if not tti.isdst: - return ZERO - - # The documentation says that utcoffset()-dst() must - # be constant for every dt. - return tti.delta-self._find_ttinfo(dt, laststd=1).delta - - # An alternative for that would be: - # - # return self._ttinfo_dst.offset-self._ttinfo_std.offset - # - # However, this class stores historical changes in the - # dst offset, so I belive that this wouldn't be the right - # way to implement this. - - @tzname_in_python2 - def tzname(self, dt): - if not self._ttinfo_std: - return None - return self._find_ttinfo(dt).abbr - - def __eq__(self, other): - if not isinstance(other, tzfile): - return False - return (self._trans_list == other._trans_list and - self._trans_idx == other._trans_idx and - self._ttinfo_list == other._ttinfo_list) - - def __ne__(self, other): - return not self.__eq__(other) - - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, repr(self._filename)) - - def __reduce__(self): - if not os.path.isfile(self._filename): - raise ValueError("Unpickable %s class" % self.__class__.__name__) - return (self.__class__, (self._filename,)) - - -class tzrange(datetime.tzinfo): - def __init__(self, stdabbr, stdoffset=None, - dstabbr=None, dstoffset=None, - start=None, end=None): - global relativedelta - if not relativedelta: - from dateutil import relativedelta - self._std_abbr = stdabbr - self._dst_abbr = dstabbr - if stdoffset is not None: - self._std_offset = datetime.timedelta(seconds=stdoffset) - else: - self._std_offset = ZERO - if dstoffset is not None: - self._dst_offset = datetime.timedelta(seconds=dstoffset) - elif dstabbr and stdoffset is not None: - self._dst_offset = self._std_offset+datetime.timedelta(hours=+1) - else: - self._dst_offset = ZERO - if dstabbr and start is None: - self._start_delta = relativedelta.relativedelta( - hours=+2, month=4, day=1, weekday=relativedelta.SU(+1)) - else: - self._start_delta = start - if dstabbr and end is None: - self._end_delta = relativedelta.relativedelta( - hours=+1, month=10, day=31, weekday=relativedelta.SU(-1)) - else: - self._end_delta = end - - def utcoffset(self, dt): - if dt is None: - return None - - if self._isdst(dt): - return self._dst_offset - else: - return self._std_offset - - def dst(self, dt): - if self._isdst(dt): - return self._dst_offset-self._std_offset - else: - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - if self._isdst(dt): - return self._dst_abbr - else: - return self._std_abbr - - def _isdst(self, dt): - if not self._start_delta: - return False - year = datetime.datetime(dt.year, 1, 1) - start = year+self._start_delta - end = year+self._end_delta - dt = dt.replace(tzinfo=None) - if start < end: - return dt >= start and dt < end - else: - return dt >= start or dt < end - - def __eq__(self, other): - if not isinstance(other, tzrange): - return False - return (self._std_abbr == other._std_abbr and - self._dst_abbr == other._dst_abbr and - self._std_offset == other._std_offset and - self._dst_offset == other._dst_offset and - self._start_delta == other._start_delta and - self._end_delta == other._end_delta) - - def __ne__(self, other): - return not self.__eq__(other) - - def __repr__(self): - return "%s(...)" % self.__class__.__name__ - - __reduce__ = object.__reduce__ - - -class tzstr(tzrange): - - def __init__(self, s): - global parser - if not parser: - from dateutil import parser - self._s = s - - res = parser._parsetz(s) - if res is None: - raise ValueError("unknown string format") - - # Here we break the compatibility with the TZ variable handling. - # GMT-3 actually *means* the timezone -3. - if res.stdabbr in ("GMT", "UTC"): - res.stdoffset *= -1 - - # We must initialize it first, since _delta() needs - # _std_offset and _dst_offset set. Use False in start/end - # to avoid building it two times. - tzrange.__init__(self, res.stdabbr, res.stdoffset, - res.dstabbr, res.dstoffset, - start=False, end=False) - - if not res.dstabbr: - self._start_delta = None - self._end_delta = None - else: - self._start_delta = self._delta(res.start) - if self._start_delta: - self._end_delta = self._delta(res.end, isend=1) - - def _delta(self, x, isend=0): - kwargs = {} - if x.month is not None: - kwargs["month"] = x.month - if x.weekday is not None: - kwargs["weekday"] = relativedelta.weekday(x.weekday, x.week) - if x.week > 0: - kwargs["day"] = 1 - else: - kwargs["day"] = 31 - elif x.day: - kwargs["day"] = x.day - elif x.yday is not None: - kwargs["yearday"] = x.yday - elif x.jyday is not None: - kwargs["nlyearday"] = x.jyday - if not kwargs: - # Default is to start on first sunday of april, and end - # on last sunday of october. - if not isend: - kwargs["month"] = 4 - kwargs["day"] = 1 - kwargs["weekday"] = relativedelta.SU(+1) - else: - kwargs["month"] = 10 - kwargs["day"] = 31 - kwargs["weekday"] = relativedelta.SU(-1) - if x.time is not None: - kwargs["seconds"] = x.time - else: - # Default is 2AM. - kwargs["seconds"] = 7200 - if isend: - # Convert to standard time, to follow the documented way - # of working with the extra hour. See the documentation - # of the tzinfo class. - delta = self._dst_offset-self._std_offset - kwargs["seconds"] -= delta.seconds+delta.days*86400 - return relativedelta.relativedelta(**kwargs) - - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, repr(self._s)) - - -class _tzicalvtzcomp(object): - def __init__(self, tzoffsetfrom, tzoffsetto, isdst, - tzname=None, rrule=None): - self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom) - self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto) - self.tzoffsetdiff = self.tzoffsetto-self.tzoffsetfrom - self.isdst = isdst - self.tzname = tzname - self.rrule = rrule - - -class _tzicalvtz(datetime.tzinfo): - def __init__(self, tzid, comps=[]): - self._tzid = tzid - self._comps = comps - self._cachedate = [] - self._cachecomp = [] - - def _find_comp(self, dt): - if len(self._comps) == 1: - return self._comps[0] - dt = dt.replace(tzinfo=None) - try: - return self._cachecomp[self._cachedate.index(dt)] - except ValueError: - pass - lastcomp = None - lastcompdt = None - for comp in self._comps: - if not comp.isdst: - # Handle the extra hour in DST -> STD - compdt = comp.rrule.before(dt-comp.tzoffsetdiff, inc=True) - else: - compdt = comp.rrule.before(dt, inc=True) - if compdt and (not lastcompdt or lastcompdt < compdt): - lastcompdt = compdt - lastcomp = comp - if not lastcomp: - # RFC says nothing about what to do when a given - # time is before the first onset date. We'll look for the - # first standard component, or the first component, if - # none is found. - for comp in self._comps: - if not comp.isdst: - lastcomp = comp - break - else: - lastcomp = comp[0] - self._cachedate.insert(0, dt) - self._cachecomp.insert(0, lastcomp) - if len(self._cachedate) > 10: - self._cachedate.pop() - self._cachecomp.pop() - return lastcomp - - def utcoffset(self, dt): - if dt is None: - return None - - return self._find_comp(dt).tzoffsetto - - def dst(self, dt): - comp = self._find_comp(dt) - if comp.isdst: - return comp.tzoffsetdiff - else: - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - return self._find_comp(dt).tzname - - def __repr__(self): - return "" % repr(self._tzid) - - __reduce__ = object.__reduce__ - - -class tzical(object): - def __init__(self, fileobj): - global rrule - if not rrule: - from dateutil import rrule - - if isinstance(fileobj, string_types): - self._s = fileobj - # ical should be encoded in UTF-8 with CRLF - fileobj = open(fileobj, 'r') - elif hasattr(fileobj, "name"): - self._s = fileobj.name - else: - self._s = repr(fileobj) - - self._vtz = {} - - self._parse_rfc(fileobj.read()) - - def keys(self): - return list(self._vtz.keys()) - - def get(self, tzid=None): - if tzid is None: - keys = list(self._vtz.keys()) - if len(keys) == 0: - raise ValueError("no timezones defined") - elif len(keys) > 1: - raise ValueError("more than one timezone available") - tzid = keys[0] - return self._vtz.get(tzid) - - def _parse_offset(self, s): - s = s.strip() - if not s: - raise ValueError("empty offset") - if s[0] in ('+', '-'): - signal = (-1, +1)[s[0] == '+'] - s = s[1:] - else: - signal = +1 - if len(s) == 4: - return (int(s[:2])*3600+int(s[2:])*60)*signal - elif len(s) == 6: - return (int(s[:2])*3600+int(s[2:4])*60+int(s[4:]))*signal - else: - raise ValueError("invalid offset: "+s) - - def _parse_rfc(self, s): - lines = s.splitlines() - if not lines: - raise ValueError("empty string") - - # Unfold - i = 0 - while i < len(lines): - line = lines[i].rstrip() - if not line: - del lines[i] - elif i > 0 and line[0] == " ": - lines[i-1] += line[1:] - del lines[i] - else: - i += 1 - - tzid = None - comps = [] - invtz = False - comptype = None - for line in lines: - if not line: - continue - name, value = line.split(':', 1) - parms = name.split(';') - if not parms: - raise ValueError("empty property name") - name = parms[0].upper() - parms = parms[1:] - if invtz: - if name == "BEGIN": - if value in ("STANDARD", "DAYLIGHT"): - # Process component - pass - else: - raise ValueError("unknown component: "+value) - comptype = value - founddtstart = False - tzoffsetfrom = None - tzoffsetto = None - rrulelines = [] - tzname = None - elif name == "END": - if value == "VTIMEZONE": - if comptype: - raise ValueError("component not closed: "+comptype) - if not tzid: - raise ValueError("mandatory TZID not found") - if not comps: - raise ValueError( - "at least one component is needed") - # Process vtimezone - self._vtz[tzid] = _tzicalvtz(tzid, comps) - invtz = False - elif value == comptype: - if not founddtstart: - raise ValueError("mandatory DTSTART not found") - if tzoffsetfrom is None: - raise ValueError( - "mandatory TZOFFSETFROM not found") - if tzoffsetto is None: - raise ValueError( - "mandatory TZOFFSETFROM not found") - # Process component - rr = None - if rrulelines: - rr = rrule.rrulestr("\n".join(rrulelines), - compatible=True, - ignoretz=True, - cache=True) - comp = _tzicalvtzcomp(tzoffsetfrom, tzoffsetto, - (comptype == "DAYLIGHT"), - tzname, rr) - comps.append(comp) - comptype = None - else: - raise ValueError("invalid component end: "+value) - elif comptype: - if name == "DTSTART": - rrulelines.append(line) - founddtstart = True - elif name in ("RRULE", "RDATE", "EXRULE", "EXDATE"): - rrulelines.append(line) - elif name == "TZOFFSETFROM": - if parms: - raise ValueError( - "unsupported %s parm: %s " % (name, parms[0])) - tzoffsetfrom = self._parse_offset(value) - elif name == "TZOFFSETTO": - if parms: - raise ValueError( - "unsupported TZOFFSETTO parm: "+parms[0]) - tzoffsetto = self._parse_offset(value) - elif name == "TZNAME": - if parms: - raise ValueError( - "unsupported TZNAME parm: "+parms[0]) - tzname = value - elif name == "COMMENT": - pass - else: - raise ValueError("unsupported property: "+name) - else: - if name == "TZID": - if parms: - raise ValueError( - "unsupported TZID parm: "+parms[0]) - tzid = value - elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"): - pass - else: - raise ValueError("unsupported property: "+name) - elif name == "BEGIN" and value == "VTIMEZONE": - tzid = None - comps = [] - invtz = True - - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, repr(self._s)) - -if sys.platform != "win32": - TZFILES = ["/etc/localtime", "localtime"] - TZPATHS = ["/usr/share/zoneinfo", "/usr/lib/zoneinfo", "/etc/zoneinfo"] -else: - TZFILES = [] - TZPATHS = [] - - -def gettz(name=None): - tz = None - if not name: - try: - name = os.environ["TZ"] - except KeyError: - pass - if name is None or name == ":": - for filepath in TZFILES: - if not os.path.isabs(filepath): - filename = filepath - for path in TZPATHS: - filepath = os.path.join(path, filename) - if os.path.isfile(filepath): - break - else: - continue - if os.path.isfile(filepath): - try: - tz = tzfile(filepath) - break - except (IOError, OSError, ValueError): - pass - else: - tz = tzlocal() - else: - if name.startswith(":"): - name = name[:-1] - if os.path.isabs(name): - if os.path.isfile(name): - tz = tzfile(name) - else: - tz = None - else: - for path in TZPATHS: - filepath = os.path.join(path, name) - if not os.path.isfile(filepath): - filepath = filepath.replace(' ', '_') - if not os.path.isfile(filepath): - continue - try: - tz = tzfile(filepath) - break - except (IOError, OSError, ValueError): - pass - else: - tz = None - if tzwin is not None: - try: - tz = tzwin(name) - except WindowsError: - tz = None - if not tz: - from dateutil.zoneinfo import gettz - tz = gettz(name) - if not tz: - for c in name: - # name must have at least one offset to be a tzstr - if c in "0123456789": - try: - tz = tzstr(name) - except ValueError: - pass - break - else: - if name in ("GMT", "UTC"): - tz = tzutc() - elif name in time.tzname: - tz = tzlocal() - return tz - -# vim:ts=4:sw=4:et diff --git a/venv/lib/python2.7/site-packages/dateutil/tz/win.py b/venv/lib/python2.7/site-packages/dateutil/tz/win.py deleted file mode 100644 index 7642e5c..0000000 --- a/venv/lib/python2.7/site-packages/dateutil/tz/win.py +++ /dev/null @@ -1,330 +0,0 @@ -# This code was originally contributed by Jeffrey Harris. -import datetime -import struct - -from six.moves import winreg -from six import text_type - -try: - import ctypes - from ctypes import wintypes -except ValueError: - # ValueError is raised on non-Windows systems for some horrible reason. - raise ImportError("Running tzwin on non-Windows system") - -from ._common import tzname_in_python2 - -__all__ = ["tzwin", "tzwinlocal", "tzres"] - -ONEWEEK = datetime.timedelta(7) - -TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" -TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones" -TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" - - -def _settzkeyname(): - handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) - try: - winreg.OpenKey(handle, TZKEYNAMENT).Close() - TZKEYNAME = TZKEYNAMENT - except WindowsError: - TZKEYNAME = TZKEYNAME9X - handle.Close() - return TZKEYNAME - -TZKEYNAME = _settzkeyname() - - -class tzres(object): - """ - Class for accessing `tzres.dll`, which contains timezone name related - resources. - - ..versionadded:: 2.5.0 - """ - p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char - - def __init__(self, tzres_loc='tzres.dll'): - # Load the user32 DLL so we can load strings from tzres - user32 = ctypes.WinDLL('user32') - - # Specify the LoadStringW function - user32.LoadStringW.argtypes = (wintypes.HINSTANCE, - wintypes.UINT, - wintypes.LPWSTR, - ctypes.c_int) - - self.LoadStringW = user32.LoadStringW - self._tzres = ctypes.WinDLL(tzres_loc) - self.tzres_loc = tzres_loc - - def load_name(self, offset): - """ - Load a timezone name from a DLL offset (integer). - - >>> from dateutil.tzwin import tzres - >>> tzr = tzres() - >>> print(tzr.load_name(112)) - 'Eastern Standard Time' - - :param offset: - A positive integer value referring to a string from the tzres dll. - - ..note: - Offsets found in the registry are generally of the form - `@tzres.dll,-114`. The offset in this case if 114, not -114. - - """ - resource = self.p_wchar() - lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR) - nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0) - return resource[:nchar] - - def name_from_string(self, tzname_str): - """ - Parse strings as returned from the Windows registry into the time zone - name as defined in the registry. - - >>> from dateutil.tzwin import tzres - >>> tzr = tzres() - >>> print(tzr.name_from_string('@tzres.dll,-251')) - 'Dateline Daylight Time' - >>> print(tzr.name_from_string('Eastern Standard Time')) - 'Eastern Standard Time' - - :param tzname_str: - A timezone name string as returned from a Windows registry key. - - :return: - Returns the localized timezone string from tzres.dll if the string - is of the form `@tzres.dll,-offset`, else returns the input string. - """ - if not tzname_str.startswith('@'): - return tzname_str - - name_splt = tzname_str.split(',-') - try: - offset = int(name_splt[1]) - except: - raise ValueError("Malformed timezone string.") - - return self.load_name(offset) - - -class tzwinbase(datetime.tzinfo): - """tzinfo class based on win32's timezones available in the registry.""" - def __eq__(self, other): - # Compare on all relevant dimensions, including name. - return (isinstance(other, tzwinbase) and - (self._stdoffset == other._stdoffset and - self._dstoffset == other._dstoffset and - self._stddayofweek == other._stddayofweek and - self._dstdayofweek == other._dstdayofweek and - self._stdweeknumber == other._stdweeknumber and - self._dstweeknumber == other._dstweeknumber and - self._stdhour == other._stdhour and - self._dsthour == other._dsthour and - self._stdminute == other._stdminute and - self._dstminute == other._dstminute and - self._stdname == other._stdname and - self._dstname == other._dstname)) - - def __ne__(self, other): - return not self.__eq__(other) - - def utcoffset(self, dt): - isdst = self._isdst(dt) - - if isdst is None: - return None - elif isdst: - return datetime.timedelta(minutes=self._dstoffset) - else: - return datetime.timedelta(minutes=self._stdoffset) - - def dst(self, dt): - isdst = self._isdst(dt) - - if isdst is None: - return None - elif isdst: - minutes = self._dstoffset - self._stdoffset - return datetime.timedelta(minutes=minutes) - else: - return datetime.timedelta(0) - - @tzname_in_python2 - def tzname(self, dt): - if self._isdst(dt): - return self._dstname - else: - return self._stdname - - @staticmethod - def list(): - """Return a list of all time zones known to the system.""" - handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) - tzkey = winreg.OpenKey(handle, TZKEYNAME) - result = [winreg.EnumKey(tzkey, i) - for i in range(winreg.QueryInfoKey(tzkey)[0])] - tzkey.Close() - handle.Close() - return result - - def display(self): - return self._display - - def _isdst(self, dt): - if not self._dstmonth: - # dstmonth == 0 signals the zone has no daylight saving time - return False - elif dt is None: - return None - - dston = picknthweekday(dt.year, self._dstmonth, self._dstdayofweek, - self._dsthour, self._dstminute, - self._dstweeknumber) - dstoff = picknthweekday(dt.year, self._stdmonth, self._stddayofweek, - self._stdhour, self._stdminute, - self._stdweeknumber) - if dston < dstoff: - return dston <= dt.replace(tzinfo=None) < dstoff - else: - return not dstoff <= dt.replace(tzinfo=None) < dston - - -class tzwin(tzwinbase): - - def __init__(self, name): - self._name = name - - # multiple contexts only possible in 2.7 and 3.1, we still support 2.6 - with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: - tzkeyname = text_type("{kn}\{name}").format(kn=TZKEYNAME, name=name) - with winreg.OpenKey(handle, tzkeyname) as tzkey: - keydict = valuestodict(tzkey) - - self._stdname = keydict["Std"] - self._dstname = keydict["Dlt"] - - self._display = keydict["Display"] - - # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm - tup = struct.unpack("=3l16h", keydict["TZI"]) - self._stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 - self._dstoffset = self._stdoffset-tup[2] # + DaylightBias * -1 - - # for the meaning see the win32 TIME_ZONE_INFORMATION structure docs - # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx - (self._stdmonth, - self._stddayofweek, # Sunday = 0 - self._stdweeknumber, # Last = 5 - self._stdhour, - self._stdminute) = tup[4:9] - - (self._dstmonth, - self._dstdayofweek, # Sunday = 0 - self._dstweeknumber, # Last = 5 - self._dsthour, - self._dstminute) = tup[12:17] - - def __repr__(self): - return "tzwin(%s)" % repr(self._name) - - def __reduce__(self): - return (self.__class__, (self._name,)) - - -class tzwinlocal(tzwinbase): - - def __init__(self): - with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: - - with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: - keydict = valuestodict(tzlocalkey) - - self._stdname = keydict["StandardName"] - self._dstname = keydict["DaylightName"] - - try: - tzkeyname = text_type('{kn}\{sn}').format(kn=TZKEYNAME, - sn=self._stdname) - with winreg.OpenKey(handle, tzkeyname) as tzkey: - _keydict = valuestodict(tzkey) - self._display = _keydict["Display"] - except OSError: - self._display = None - - self._stdoffset = -keydict["Bias"]-keydict["StandardBias"] - self._dstoffset = self._stdoffset-keydict["DaylightBias"] - - # For reasons unclear, in this particular key, the day of week has been - # moved to the END of the SYSTEMTIME structure. - tup = struct.unpack("=8h", keydict["StandardStart"]) - - (self._stdmonth, - self._stdweeknumber, # Last = 5 - self._stdhour, - self._stdminute) = tup[1:5] - - self._stddayofweek = tup[7] - - tup = struct.unpack("=8h", keydict["DaylightStart"]) - - (self._dstmonth, - self._dstweeknumber, # Last = 5 - self._dsthour, - self._dstminute) = tup[1:5] - - self._dstdayofweek = tup[7] - - def __repr__(self): - return "tzwinlocal()" - - def __str__(self): - # str will return the standard name, not the daylight name. - return "tzwinlocal(%s)" % repr(self._stdname) - - def __reduce__(self): - return (self.__class__, ()) - - -def picknthweekday(year, month, dayofweek, hour, minute, whichweek): - """ dayofweek == 0 means Sunday, whichweek 5 means last instance """ - first = datetime.datetime(year, month, 1, hour, minute) - - # This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6), - # Because 7 % 7 = 0 - weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1) - wd = weekdayone + ((whichweek - 1) * ONEWEEK) - if (wd.month != month): - wd -= ONEWEEK - - return wd - - -def valuestodict(key): - """Convert a registry key's values to a dictionary.""" - dout = {} - size = winreg.QueryInfoKey(key)[1] - tz_res = None - - for i in range(size): - key_name, value, dtype = winreg.EnumValue(key, i) - if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN: - # If it's a DWORD (32-bit integer), it's stored as unsigned - convert - # that to a proper signed integer - if value & (1 << 31): - value = value - (1 << 32) - elif dtype == winreg.REG_SZ: - # If it's a reference to the tzres DLL, load the actual string - if value.startswith('@tzres'): - tz_res = tz_res or tzres() - value = tz_res.name_from_string(value) - - value = value.rstrip('\x00') # Remove trailing nulls - - dout[key_name] = value - - return dout diff --git a/venv/lib/python2.7/site-packages/dateutil/tzwin.py b/venv/lib/python2.7/site-packages/dateutil/tzwin.py deleted file mode 100644 index 55cd910..0000000 --- a/venv/lib/python2.7/site-packages/dateutil/tzwin.py +++ /dev/null @@ -1,2 +0,0 @@ -# tzwin has moved to dateutil.tz.win -from .tz.win import * \ No newline at end of file diff --git a/venv/lib/python2.7/site-packages/dateutil/zoneinfo/__init__.py b/venv/lib/python2.7/site-packages/dateutil/zoneinfo/__init__.py deleted file mode 100644 index b1a39b0..0000000 --- a/venv/lib/python2.7/site-packages/dateutil/zoneinfo/__init__.py +++ /dev/null @@ -1,101 +0,0 @@ -# -*- coding: utf-8 -*- -import logging -import os -import warnings -import tempfile -import shutil -import json - -from tarfile import TarFile -from pkgutil import get_data -from io import BytesIO -from contextlib import closing - -from dateutil.tz import tzfile - -__all__ = ["gettz", "gettz_db_metadata", "rebuild"] - -ZONEFILENAME = "dateutil-zoneinfo.tar.gz" -METADATA_FN = 'METADATA' - -# python2.6 compatability. Note that TarFile.__exit__ != TarFile.close, but -# it's close enough for python2.6 -tar_open = TarFile.open -if not hasattr(TarFile, '__exit__'): - def tar_open(*args, **kwargs): - return closing(TarFile.open(*args, **kwargs)) - - -class tzfile(tzfile): - def __reduce__(self): - return (gettz, (self._filename,)) - - -def getzoneinfofile_stream(): - try: - return BytesIO(get_data(__name__, ZONEFILENAME)) - except IOError as e: # TODO switch to FileNotFoundError? - warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror)) - return None - - -class ZoneInfoFile(object): - def __init__(self, zonefile_stream=None): - if zonefile_stream is not None: - with tar_open(fileobj=zonefile_stream, mode='r') as tf: - # dict comprehension does not work on python2.6 - # TODO: get back to the nicer syntax when we ditch python2.6 - # self.zones = {zf.name: tzfile(tf.extractfile(zf), - # filename = zf.name) - # for zf in tf.getmembers() if zf.isfile()} - self.zones = dict((zf.name, tzfile(tf.extractfile(zf), - filename=zf.name)) - for zf in tf.getmembers() - if zf.isfile() and zf.name != METADATA_FN) - # deal with links: They'll point to their parent object. Less - # waste of memory - # links = {zl.name: self.zones[zl.linkname] - # for zl in tf.getmembers() if zl.islnk() or zl.issym()} - links = dict((zl.name, self.zones[zl.linkname]) - for zl in tf.getmembers() if - zl.islnk() or zl.issym()) - self.zones.update(links) - try: - metadata_json = tf.extractfile(tf.getmember(METADATA_FN)) - metadata_str = metadata_json.read().decode('UTF-8') - self.metadata = json.loads(metadata_str) - except KeyError: - # no metadata in tar file - self.metadata = None - else: - self.zones = dict() - self.metadata = None - - -# The current API has gettz as a module function, although in fact it taps into -# a stateful class. So as a workaround for now, without changing the API, we -# will create a new "global" class instance the first time a user requests a -# timezone. Ugly, but adheres to the api. -# -# TODO: deprecate this. -_CLASS_ZONE_INSTANCE = list() - - -def gettz(name): - if len(_CLASS_ZONE_INSTANCE) == 0: - _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) - return _CLASS_ZONE_INSTANCE[0].zones.get(name) - - -def gettz_db_metadata(): - """ Get the zonefile metadata - - See `zonefile_metadata`_ - - :returns: A dictionary with the database metadata - """ - if len(_CLASS_ZONE_INSTANCE) == 0: - _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) - return _CLASS_ZONE_INSTANCE[0].metadata - - diff --git a/venv/lib/python2.7/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz b/venv/lib/python2.7/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz deleted file mode 100644 index 4b89c0f0d38f244ca0d0b4e70e19c85d9c429911..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 156039 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

RM7Svz}@cpL6>nG|>ak)=rDDrJI}tPA#zv>$$l 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%0A%wBq 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.0.0,<3.0.0 - - # Elasticsearch 1.x - elasticsearch>=1.0.0,<2.0.0 - - # Elasticsearch 0.90.x - elasticsearch<1.0.0 - -The development is happening on ``master`` and ``1.x`` branches, respectively. - -Installation ------------- - -Install the ``elasticsearch`` package with `pip -`_:: - - pip install elasticsearch - - -Example use ------------ - -Simple use-case:: - - >>> from datetime import datetime - >>> from elasticsearch import Elasticsearch - - # by default we connect to localhost:9200 - >>> es = Elasticsearch() - - # create an index in elasticsearch, ignore status code 400 (index already exists) - >>> es.indices.create(index='my-index', ignore=400) - {u'acknowledged': True} - - # datetimes will be serialized - >>> es.index(index="my-index", doc_type="test-type", id=42, body={"any": "data", "timestamp": datetime.now()}) - {u'_id': u'42', u'_index': u'my-index', u'_type': u'test-type', u'_version': 1, u'ok': True} - - # but not deserialized - >>> es.get(index="my-index", doc_type="test-type", id=42)['_source'] - {u'any': u'data', u'timestamp': u'2013-05-12T19:45:31.804229'} - -`Full documentation`_. - -.. _Full documentation: http://elasticsearch-py.rtfd.org/ - - -Features --------- - -The client's features include: - - * translating basic Python data types to and from json (datetimes are not - decoded for performance reasons) - * configurable automatic discovery of cluster nodes - * persistent connections - * load balancing (with pluggable selection strategy) across all available nodes - * failed connection penalization (time based - failed connections won't be - retried until a timeout is reached) - * support for ssl and http authentication - * thread safety - * pluggable architecture - - -License -------- - -Copyright 2015 Elasticsearch - -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. - -Build status ------------- - -.. image:: https://secure.travis-ci.org/elastic/elasticsearch-py.png - :target: http://travis-ci.org/#!/elastic/elasticsearch-py - diff --git a/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/INSTALLER b/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/METADATA b/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/METADATA deleted file mode 100644 index 235c0c1..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/METADATA +++ /dev/null @@ -1,155 +0,0 @@ -Metadata-Version: 2.0 -Name: elasticsearch -Version: 2.3.0 -Summary: Python client for Elasticsearch -Home-page: https://github.com/elastic/elasticsearch-py -Author: Honza Král -Author-email: honza.kral@gmail.com -License: Apache License, Version 2.0 -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: License :: OSI Approved :: Apache Software License -Classifier: Intended Audience :: Developers -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.2 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Requires-Dist: urllib3 (>=1.8,<2.0) - -Python Elasticsearch Client -=========================== - -Official low-level client for Elasticsearch. Its goal is to provide common -ground for all Elasticsearch-related code in Python; because of this it tries -to be opinion-free and very extendable. - -For a more high level client library with more limited scope, have a look at -`elasticsearch-dsl`_ - a more pythonic library sitting on top of -``elasticsearch-py``. - -It provides a more convenient and idiomatic way to write and manipulate -`queries`_. It stays close to the Elasticsearch JSON DSL, mirroring its -terminology and structure while exposing the whole range of the DSL from Python -either directly using defined classes or a queryset-like expressions. - -It also provides an optional `persistence layer`_ for working with documents as -Python objects in an ORM-like fashion: defining mappings, retrieving and saving -documents, wrapping the document data in user-defined classes. - -.. _elasticsearch-dsl: http://elasticsearch-dsl.rtfd.org/ -.. _queries: http://elasticsearch-dsl.readthedocs.org/en/latest/search_dsl.html -.. _persistence layer: http://elasticsearch-dsl.readthedocs.org/en/latest/persistence.html#doctype - -Compatibility -------------- - -The library is compatible with all Elasticsearch versions since ``0.90.x`` but you -**have to use a matching major version**: - -For **Elasticsearch 2.0** and later, use the major version 2 (``2.x.y``) of the -library. - -For **Elasticsearch 1.0** and later, use the major version 1 (``1.x.y``) of the -library. - -For **Elasticsearch 0.90.x**, use a version from ``0.4.x`` releases of the -library. - -The recommended way to set your requirements in your `setup.py` or -`requirements.txt` is:: - - # Elasticsearch 2.x - elasticsearch>=2.0.0,<3.0.0 - - # Elasticsearch 1.x - elasticsearch>=1.0.0,<2.0.0 - - # Elasticsearch 0.90.x - elasticsearch<1.0.0 - -The development is happening on ``master`` and ``1.x`` branches, respectively. - -Installation ------------- - -Install the ``elasticsearch`` package with `pip -`_:: - - pip install elasticsearch - - -Example use ------------ - -Simple use-case:: - - >>> from datetime import datetime - >>> from elasticsearch import Elasticsearch - - # by default we connect to localhost:9200 - >>> es = Elasticsearch() - - # create an index in elasticsearch, ignore status code 400 (index already exists) - >>> es.indices.create(index='my-index', ignore=400) - {u'acknowledged': True} - - # datetimes will be serialized - >>> es.index(index="my-index", doc_type="test-type", id=42, body={"any": "data", "timestamp": datetime.now()}) - {u'_id': u'42', u'_index': u'my-index', u'_type': u'test-type', u'_version': 1, u'ok': True} - - # but not deserialized - >>> es.get(index="my-index", doc_type="test-type", id=42)['_source'] - {u'any': u'data', u'timestamp': u'2013-05-12T19:45:31.804229'} - -`Full documentation`_. - -.. _Full documentation: http://elasticsearch-py.rtfd.org/ - - -Features --------- - -The client's features include: - - * translating basic Python data types to and from json (datetimes are not - decoded for performance reasons) - * configurable automatic discovery of cluster nodes - * persistent connections - * load balancing (with pluggable selection strategy) across all available nodes - * failed connection penalization (time based - failed connections won't be - retried until a timeout is reached) - * support for ssl and http authentication - * thread safety - * pluggable architecture - - -License -------- - -Copyright 2015 Elasticsearch - -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. - -Build status ------------- - -.. image:: https://secure.travis-ci.org/elastic/elasticsearch-py.png - :target: http://travis-ci.org/#!/elastic/elasticsearch-py - diff --git a/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/RECORD b/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/RECORD deleted file mode 100644 index 8f4c11a..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/RECORD +++ /dev/null @@ -1,48 +0,0 @@ -elasticsearch/compat.py,sha256=MLDabdJN3w5KkeVykvh0rpUO1SFFds0mBnDWZJ30SdI,312 -elasticsearch/transport.py,sha256=El6Li6eHmkFZxeLMfWiZb-nf-zBnsXBwQR8KRamiAiU,15277 -elasticsearch/exceptions.py,sha256=RGOIdnNlpKYB9-Hy-gyMIALNq3CxOdzPu4bySZN97q8,3127 -elasticsearch/connection_pool.py,sha256=re2lYiUtyO3CSzkJLZc8L2ooVZ76bH7rKm1oQ_5SG9Q,9607 -elasticsearch/serializer.py,sha256=qqyY9_JaaPuTw1UAbL99CSKllbvXOHF0VfCXcupM4sk,2287 -elasticsearch/__init__.py,sha256=xPgCWpzwwo4-PB0zjt3u9Yx9MnO1VcULq8e1W3589-U,837 -elasticsearch/client/cluster.py,sha256=ez_hB0Ywkn8s1QSh-Sdolr9EEaKXPiVQ1T8CU1v0udQ,7619 -elasticsearch/client/cat.py,sha256=2egI0ak1z9u8ruR_rp9BqQxLOpDltt4dgL1o99QnN_Q,15962 -elasticsearch/client/snapshot.py,sha256=w-cteBMUMYV0DOU1oJXFqRnhfvGjb0dJreSbj2Uy6Vs,7609 -elasticsearch/client/indices.py,sha256=juLLjjOtNo0BL6DzMcyXS2Ch_PrwL24M89Ut7XewezM,52688 -elasticsearch/client/utils.py,sha256=O9ELpuzQIShFON8UBhTo87T4vzQTQwHga8oVcskiVOk,2645 -elasticsearch/client/__init__.py,sha256=9Gllv5q-PmhLVNqAxa1ESWVf5vGkQmJWTWV_lyx8Bc8,64638 -elasticsearch/client/nodes.py,sha256=P8YUL-BppKfn4tdw3gL5hEbne5nw5hzu_oivmQVXd0I,4805 -elasticsearch/connection/pooling.py,sha256=19x-kzyqndWJnnMGZ1ucoZvpMIliH3q3oJLe34OEROA,798 -elasticsearch/connection/http_urllib3.py,sha256=8uPSBkXe-0nR9lGxRjpBBZO6ccmVmDWf1tos2s0VqE8,5285 -elasticsearch/connection/base.py,sha256=s-5JTcSCR5aHGkywwsreG9XUFcK_EavjG9mBFJ6jTR0,4357 -elasticsearch/connection/http_requests.py,sha256=m2mWMyJw-I-CAwjNEt336Md5kMhbLrdrxVX1OD2_9kg,4039 -elasticsearch/connection/__init__.py,sha256=V50xojadYNqjTkV1KdY4GQnxfG-hrxr-FkmQknvYjJo,127 -elasticsearch/helpers/test.py,sha256=iyaFosPjWYupZOcpk6kYdIzpw6-qNGkfWuK-G-iFtYQ,1839 -elasticsearch/helpers/__init__.py,sha256=oL6ffwCljzXhKBh1rdVtiouw6yVTCo7pA0xEaOuFANk,14046 -elasticsearch-2.3.0.dist-info/pbr.json,sha256=7LDsmS2o3dnopaClq6T03zl5eegwepeyDWzJEMMl-Jg,47 -elasticsearch-2.3.0.dist-info/top_level.txt,sha256=Jp2bLWq49skvCN4YCZsg1Hfn_NDLgleC-x-Bn01_HgM,14 -elasticsearch-2.3.0.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110 -elasticsearch-2.3.0.dist-info/METADATA,sha256=mYScigdcXtHELTg0kiPdRuVQgMv-puR5mNPqIwPxLUw,5261 -elasticsearch-2.3.0.dist-info/DESCRIPTION.rst,sha256=oVlNdYwfM0G2v0gLd_xVaUcGpbX5n4cmTM96JZb9DWo,4221 -elasticsearch-2.3.0.dist-info/metadata.json,sha256=qPjP4V88tKhTOWEiRc9mVnbjangh3NcqHao1awv08RE,1272 -elasticsearch-2.3.0.dist-info/RECORD,, -elasticsearch-2.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -elasticsearch/transport.pyc,, -elasticsearch/connection/pooling.pyc,, -elasticsearch/compat.pyc,, -elasticsearch/client/snapshot.pyc,, -elasticsearch/client/indices.pyc,, -elasticsearch/connection/__init__.pyc,, -elasticsearch/client/utils.pyc,, -elasticsearch/__init__.pyc,, -elasticsearch/client/nodes.pyc,, -elasticsearch/client/__init__.pyc,, -elasticsearch/client/cat.pyc,, -elasticsearch/connection/http_urllib3.pyc,, -elasticsearch/connection/http_requests.pyc,, -elasticsearch/serializer.pyc,, -elasticsearch/connection_pool.pyc,, -elasticsearch/helpers/__init__.pyc,, -elasticsearch/client/cluster.pyc,, -elasticsearch/exceptions.pyc,, -elasticsearch/connection/base.pyc,, -elasticsearch/helpers/test.pyc,, diff --git a/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/WHEEL b/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/WHEEL deleted file mode 100644 index 9dff69d..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.24.0) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/metadata.json b/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/metadata.json deleted file mode 100644 index 4a9da4d..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"summary": "Python client for Elasticsearch", "extras": [], "metadata_version": "2.0", "test_requires": [{"requires": ["requests (>=1.0.0,<3.0.0)", "nose", "coverage", "mock", "pyaml", "nosexcover"]}], "version": "2.3.0", "name": "elasticsearch", "extensions": {"python.details": {"document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"role": "author", "name": "Honza Kr\u00e1l", "email": "honza.kral@gmail.com"}], "project_urls": {"Home": "https://github.com/elastic/elasticsearch-py"}}}, "classifiers": ["Development Status :: 5 - Production/Stable", "License :: OSI Approved :: Apache Software License", "Intended Audience :: Developers", "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.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy"], "license": "Apache License, Version 2.0", "generator": "bdist_wheel (0.24.0)", "run_requires": [{"requires": ["urllib3 (>=1.8,<2.0)"]}]} \ No newline at end of file diff --git a/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/pbr.json b/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/pbr.json deleted file mode 100644 index 17f6d5d..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/pbr.json +++ /dev/null @@ -1 +0,0 @@ -{"git_version": "14f5b35", "is_release": false} \ No newline at end of file diff --git a/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/top_level.txt b/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/top_level.txt deleted file mode 100644 index 174c3f8..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch-2.3.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -elasticsearch diff --git a/venv/lib/python2.7/site-packages/elasticsearch/__init__.py b/venv/lib/python2.7/site-packages/elasticsearch/__init__.py deleted file mode 100644 index 405bc21..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -from __future__ import absolute_import - -VERSION = (2, 3, 0) -__version__ = VERSION -__versionstr__ = '.'.join(map(str, VERSION)) - -import sys - -if (2, 7) <= sys.version_info < (3, 2): - # On Python 2.7 and Python3 < 3.2, install no-op handler to silence - # `No handlers could be found for logger "elasticsearch"` message per - # - import logging - logger = logging.getLogger('elasticsearch') - logger.addHandler(logging.NullHandler()) - -from .client import Elasticsearch -from .transport import Transport -from .connection_pool import ConnectionPool, ConnectionSelector, \ - RoundRobinSelector -from .serializer import JSONSerializer -from .connection import Connection, RequestsHttpConnection, \ - Urllib3HttpConnection -from .exceptions import * - diff --git a/venv/lib/python2.7/site-packages/elasticsearch/client/__init__.py b/venv/lib/python2.7/site-packages/elasticsearch/client/__init__.py deleted file mode 100644 index b7275c9..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/client/__init__.py +++ /dev/null @@ -1,1279 +0,0 @@ -from __future__ import unicode_literals -import weakref -import logging - -from ..transport import Transport -from ..exceptions import NotFoundError, TransportError -from ..compat import string_types, urlparse -from .indices import IndicesClient -from .cluster import ClusterClient -from .cat import CatClient -from .nodes import NodesClient -from .snapshot import SnapshotClient -from .utils import query_params, _make_path, SKIP_IN_PATH - -logger = logging.getLogger('elasticsearch') - -def _normalize_hosts(hosts): - """ - Helper function to transform hosts argument to - :class:`~elasticsearch.Elasticsearch` to a list of dicts. - """ - # if hosts are empty, just defer to defaults down the line - if hosts is None: - return [{}] - - # passed in just one string - if isinstance(hosts, string_types): - hosts = [hosts] - - out = [] - # normalize hosts to dicts - for host in hosts: - if isinstance(host, string_types): - if '://' not in host: - host = "//%s" % host - - parsed_url = urlparse(host) - h = {"host": parsed_url.hostname} - - if parsed_url.port: - h["port"] = parsed_url.port - - if parsed_url.scheme == "https": - h['port'] = parsed_url.port or 443 - h['use_ssl'] = True - h['scheme'] = 'http' - elif parsed_url.scheme: - h['scheme'] = parsed_url.scheme - - if parsed_url.username or parsed_url.password: - h['http_auth'] = '%s:%s' % (parsed_url.username, parsed_url.password) - - if parsed_url.path and parsed_url.path != '/': - h['url_prefix'] = parsed_url.path - - out.append(h) - else: - out.append(host) - return out - - -class Elasticsearch(object): - """ - Elasticsearch low-level client. Provides a straightforward mapping from - Python to ES REST endpoints. - - The instance has attributes ``cat``, ``cluster``, ``indices``, ``nodes`` - and ``snapshot`` that provide access to instances of - :class:`~elasticsearch.client.CatClient`, - :class:`~elasticsearch.client.ClusterClient`, - :class:`~elasticsearch.client.IndicesClient`, - :class:`~elasticsearch.client.NodesClient` and - :class:`~elasticsearch.client.SnapshotClient` respectively. This is the - preferred (and only supported) way to get access to those classes and their - methods. - - You can specify your own connection class which should be used by providing - the ``connection_class`` parameter:: - - # create connection to localhost using the ThriftConnection - es = Elasticsearch(connection_class=ThriftConnection) - - If you want to turn on :ref:`sniffing` you have several options (described - in :class:`~elasticsearch.Transport`):: - - # create connection that will automatically inspect the cluster to get - # the list of active nodes. Start with nodes running on 'esnode1' and - # 'esnode2' - es = Elasticsearch( - ['esnode1', 'esnode2'], - # sniff before doing anything - sniff_on_start=True, - # refresh nodes after a node fails to respond - sniff_on_connection_fail=True, - # and also every 60 seconds - sniffer_timeout=60 - ) - - Different hosts can have different parameters, use a dictionary per node to - specify those:: - - # connect to localhost directly and another node using SSL on port 443 - # and an url_prefix. Note that ``port`` needs to be an int. - es = Elasticsearch([ - {'host': 'localhost'}, - {'host': 'othernode', 'port': 443, 'url_prefix': 'es', 'use_ssl': True}, - ]) - - If using SSL, there are several parameters that control how we deal with - certificates (see :class:`~elasticsearch.Urllib3HttpConnection` for - detailed description of the options):: - - es = Elasticsearch( - ['localhost:443', 'other_host:443'], - # turn on SSL - use_ssl=True, - # make sure we verify SSL certificates (off by default) - verify_certs=True, - # provide a path to CA certs on disk - ca_certs='/path/to/CA_certs' - ) - - SSL client authentication is supported - (see :class:`~elasticsearch.Urllib3HttpConnection` for - detailed description of the options):: - - es = Elasticsearch( - ['localhost:443', 'other_host:443'], - # turn on SSL - use_ssl=True, - # make sure we verify SSL certificates (off by default) - verify_certs=True, - # provide a path to CA certs on disk - ca_certs='/path/to/CA_certs', - # PEM formatted SSL client certificate - client_cert='/path/to/clientcert.pem', - # PEM formatted SSL client key - client_key='/path/to/clientkey.pem' - ) - - Alternatively you can use RFC-1738 formatted URLs, as long as they are not - in conflict with other options:: - - es = Elasticsearch( - [ - 'http://user:secret@localhost:9200/', - 'https://user:secret@other_host:443/production' - ], - verify_certs=True - ) - - """ - def __init__(self, hosts=None, transport_class=Transport, **kwargs): - """ - :arg hosts: list of nodes we should connect to. Node should be a - dictionary ({"host": "localhost", "port": 9200}), the entire dictionary - will be passed to the :class:`~elasticsearch.Connection` class as - kwargs, or a string in the format of ``host[:port]`` which will be - translated to a dictionary automatically. If no value is given the - :class:`~elasticsearch.Urllib3HttpConnection` class defaults will be used. - - :arg transport_class: :class:`~elasticsearch.Transport` subclass to use. - - :arg kwargs: any additional arguments will be passed on to the - :class:`~elasticsearch.Transport` class and, subsequently, to the - :class:`~elasticsearch.Connection` instances. - """ - self.transport = transport_class(_normalize_hosts(hosts), **kwargs) - - # namespaced clients for compatibility with API names - # use weakref to make GC's work a little easier - self.indices = IndicesClient(weakref.proxy(self)) - self.cluster = ClusterClient(weakref.proxy(self)) - self.cat = CatClient(weakref.proxy(self)) - self.nodes = NodesClient(weakref.proxy(self)) - self.snapshot = SnapshotClient(weakref.proxy(self)) - - def __repr__(self): - try: - # get a lost of all connections - cons = self.transport.hosts - # truncate to 10 if there are too many - if len(cons) > 5: - cons = cons[:5] + ['...'] - return '' % cons - except: - # probably operating on custom transport and connection_pool, ignore - return super(Elasticsearch, self).__repr__() - - def _bulk_body(self, body): - # if not passed in a string, serialize items and join by newline - if not isinstance(body, string_types): - body = '\n'.join(map(self.transport.serializer.dumps, body)) - - # bulk body must end with a newline - if not body.endswith('\n'): - body += '\n' - - return body - - @query_params() - def ping(self, params=None): - """ - Returns True if the cluster is up, False otherwise. - ``_ - """ - try: - self.transport.perform_request('HEAD', '/', params=params) - except NotFoundError: - return False - return True - - @query_params() - def info(self, params=None): - """ - Get the basic info from the current cluster. - ``_ - """ - _, data = self.transport.perform_request('GET', '/', params=params) - return data - - @query_params('consistency', 'parent', 'refresh', 'routing', - 'timeout', 'timestamp', 'ttl', 'version', 'version_type') - def create(self, index, doc_type, body, id=None, params=None): - """ - Adds a typed JSON document in a specific index, making it searchable. - Behind the scenes this method calls index(..., op_type='create') - ``_ - - :arg index: The name of the index - :arg doc_type: The type of the document - :arg body: The document - :arg id: Document ID - :arg consistency: Explicit write consistency setting for the operation, - valid choices are: 'one', 'quorum', 'all' - :arg op_type: Explicit operation type, default 'index', valid choices - are: 'index', 'create' - :arg parent: ID of the parent document - :arg refresh: Refresh the index after performing the operation - :arg routing: Specific routing value - :arg timeout: Explicit operation timeout - :arg timestamp: Explicit timestamp for the document - :arg ttl: Expiration time for the document - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - return self.index(index, doc_type, body, id=id, params=params, op_type='create') - - @query_params('consistency', 'op_type', 'parent', 'refresh', 'routing', - 'timeout', 'timestamp', 'ttl', 'version', 'version_type') - def index(self, index, doc_type, body, id=None, params=None): - """ - Adds or updates a typed JSON document in a specific index, making it searchable. - ``_ - - :arg index: The name of the index - :arg doc_type: The type of the document - :arg body: The document - :arg id: Document ID - :arg consistency: Explicit write consistency setting for the operation, - valid choices are: 'one', 'quorum', 'all' - :arg op_type: Explicit operation type, default 'index', valid choices - are: 'index', 'create' - :arg parent: ID of the parent document - :arg refresh: Refresh the index after performing the operation - :arg routing: Specific routing value - :arg timeout: Explicit operation timeout - :arg timestamp: Explicit timestamp for the document - :arg ttl: Expiration time for the document - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - for param in (index, doc_type, body): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('POST' if id in SKIP_IN_PATH else 'PUT', - _make_path(index, doc_type, id), params=params, body=body) - return data - - @query_params('parent', 'preference', 'realtime', 'refresh', 'routing') - def exists(self, index, doc_type, id, params=None): - """ - Returns a boolean indicating whether or not given document exists in Elasticsearch. - ``_ - - :arg index: The name of the index - :arg doc_type: The type of the document (use `_all` to fetch the first - document matching the ID across all types) - :arg id: The document ID - :arg parent: The ID of the parent document - :arg preference: Specify the node or shard the operation should be - performed on (default: random) - :arg realtime: Specify whether to perform the operation in realtime or - search mode - :arg refresh: Refresh the shard containing the document before - performing the operation - :arg routing: Specific routing value - """ - for param in (index, doc_type, id): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - try: - self.transport.perform_request('HEAD', _make_path(index, doc_type, - id), params=params) - except NotFoundError: - return False - return True - - @query_params('_source', '_source_exclude', '_source_include', 'fields', - 'parent', 'preference', 'realtime', 'refresh', 'routing', 'version', - 'version_type') - def get(self, index, id, doc_type='_all', params=None): - """ - Get a typed JSON document from the index based on its id. - ``_ - - :arg index: The name of the index - :arg doc_type: The type of the document (use `_all` to fetch the first - document matching the ID across all types) - :arg id: The document ID - :arg _source: True or false to return the _source field or not, or a - list of fields to return - :arg _source_exclude: A list of fields to exclude from the returned - _source field - :arg _source_include: A list of fields to extract and return from the - _source field - :arg fields: A comma-separated list of fields to return in the response - :arg parent: The ID of the parent document - :arg preference: Specify the node or shard the operation should be - performed on (default: random) - :arg realtime: Specify whether to perform the operation in realtime or - search mode - :arg refresh: Refresh the shard containing the document before - performing the operation - :arg routing: Specific routing value - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - for param in (index, doc_type, id): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, id), params=params) - return data - - @query_params('_source', '_source_exclude', '_source_include', 'parent', - 'preference', 'realtime', 'refresh', 'routing', 'version', - 'version_type') - def get_source(self, index, doc_type, id, params=None): - """ - Get the source of a document by it's index, type and id. - ``_ - - :arg index: The name of the index - :arg doc_type: The type of the document; use `_all` to fetch the first - document matching the ID across all types - :arg id: The document ID - :arg _source: True or false to return the _source field or not, or a - list of fields to return - :arg _source_exclude: A list of fields to exclude from the returned - _source field - :arg _source_include: A list of fields to extract and return from the - _source field - :arg parent: The ID of the parent document - :arg preference: Specify the node or shard the operation should be - performed on (default: random) - :arg realtime: Specify whether to perform the operation in realtime or - search mode - :arg refresh: Refresh the shard containing the document before - performing the operation - :arg routing: Specific routing value - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - for param in (index, doc_type, id): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, id, '_source'), params=params) - return data - - @query_params('_source', '_source_exclude', '_source_include', 'fields', - 'preference', 'realtime', 'refresh') - def mget(self, body, index=None, doc_type=None, params=None): - """ - Get multiple documents based on an index, type (optional) and ids. - ``_ - - :arg body: Document identifiers; can be either `docs` (containing full - document information) or `ids` (when index and type is provided in - the URL. - :arg index: The name of the index - :arg doc_type: The type of the document - :arg _source: True or false to return the _source field or not, or a - list of fields to return - :arg _source_exclude: A list of fields to exclude from the returned - _source field - :arg _source_include: A list of fields to extract and return from the - _source field - :arg fields: A comma-separated list of fields to return in the response - :arg preference: Specify the node or shard the operation should be - performed on (default: random) - :arg realtime: Specify whether to perform the operation in realtime or - search mode - :arg refresh: Refresh the shard containing the document before - performing the operation - """ - if body in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'body'.") - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, '_mget'), params=params, body=body) - return data - - @query_params('consistency', 'detect_noop', 'fields', 'lang', 'parent', - 'refresh', 'retry_on_conflict', 'routing', 'script', 'script_id', - 'scripted_upsert', 'timeout', 'timestamp', 'ttl', 'version', - 'version_type') - def update(self, index, doc_type, id, body=None, params=None): - """ - Update a document based on a script or partial data provided. - ``_ - - :arg index: The name of the index - :arg doc_type: The type of the document - :arg id: Document ID - :arg body: The request definition using either `script` or partial `doc` - :arg consistency: Explicit write consistency setting for the operation, - valid choices are: 'one', 'quorum', 'all' - :arg detect_noop: Specifying as true will cause Elasticsearch to check - if there are changes and, if there aren't, turn the update request - into a noop. - :arg fields: A comma-separated list of fields to return in the response - :arg lang: The script language (default: groovy) - :arg parent: ID of the parent document. Is is only used for routing and - when for the upsert request - :arg refresh: Refresh the index after performing the operation - :arg retry_on_conflict: Specify how many times should the operation be - retried when a conflict occurs (default: 0) - :arg routing: Specific routing value - :arg script: The URL-encoded script definition (instead of using request - body) - :arg script_id: The id of a stored script - :arg scripted_upsert: True if the script referenced in script or - script_id should be called to perform inserts - defaults to false - :arg timeout: Explicit operation timeout - :arg timestamp: Explicit timestamp for the document - :arg ttl: Expiration time for the document - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'force' - """ - for param in (index, doc_type, id): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('POST', _make_path(index, - doc_type, id, '_update'), params=params, body=body) - return data - - @query_params('_source', '_source_exclude', '_source_include', - 'allow_no_indices', 'analyze_wildcard', 'analyzer', 'default_operator', - 'df', 'expand_wildcards', 'explain', 'fielddata_fields', 'fields', - 'from_', 'ignore_unavailable', 'lenient', 'lowercase_expanded_terms', - 'preference', 'q', 'request_cache', 'routing', 'scroll', 'search_type', - 'size', 'sort', 'stats', 'suggest_field', 'suggest_mode', - 'suggest_size', 'suggest_text', 'terminate_after', 'timeout', - 'track_scores', 'version') - def search(self, index=None, doc_type=None, body=None, params=None): - """ - Execute a search query and get back search hits that match the query. - ``_ - - :arg index: A comma-separated list of index names to search; use `_all` - or empty string to perform the operation on all indices - :arg doc_type: A comma-separated list of document types to search; leave - empty to perform the operation on all types - :arg body: The search definition using the Query DSL - :arg _source: True or false to return the _source field or not, or a - list of fields to return - :arg _source_exclude: A list of fields to exclude from the returned - _source field - :arg _source_include: A list of fields to extract and return from the - _source field - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg analyze_wildcard: Specify whether wildcard and prefix queries - should be analyzed (default: false) - :arg analyzer: The analyzer to use for the query string - :arg default_operator: The default operator for query string query (AND - or OR), default 'OR', valid choices are: 'AND', 'OR' - :arg df: The field to use as default where no field prefix is given in - the query string - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg explain: Specify whether to return detailed information about score - computation as part of a hit - :arg fielddata_fields: A comma-separated list of fields to return as the - field data representation of a field for each hit - :arg fields: A comma-separated list of fields to return as part of a hit - :arg from\_: Starting offset (default: 0) - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg lenient: Specify whether format-based query failures (such as - providing text to a numeric field) should be ignored - :arg lowercase_expanded_terms: Specify whether query terms should be - lowercased - :arg preference: Specify the node or shard the operation should be - performed on (default: random) - :arg q: Query in the Lucene query string syntax - :arg request_cache: Specify if request cache should be used for this - request or not, defaults to index level setting - :arg routing: A comma-separated list of specific routing values - :arg scroll: Specify how long a consistent view of the index should be - maintained for scrolled search - :arg search_type: Search operation type, valid choices are: - 'query_then_fetch', 'dfs_query_then_fetch', 'count', 'scan' - :arg size: Number of hits to return (default: 10) - :arg sort: A comma-separated list of : pairs - :arg stats: Specific 'tag' of the request for logging and statistical - purposes - :arg suggest_field: Specify which field to use for suggestions - :arg suggest_mode: Specify suggest mode, default 'missing', valid - choices are: 'missing', 'popular', 'always' - :arg suggest_size: How many suggestions to return in response - :arg suggest_text: The source text for which the suggestions should be - returned - :arg terminate_after: The maximum number of documents to collect for - each shard, upon reaching which the query execution will terminate - early. - :arg timeout: Explicit operation timeout - :arg track_scores: Whether to calculate and return scores even if they - are not used for sorting - :arg version: Specify whether to return document version as part of a - hit - """ - # from is a reserved word so it cannot be used, use from_ instead - if 'from_' in params: - params['from'] = params.pop('from_') - - if doc_type and not index: - index = '_all' - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, '_search'), params=params, body=body) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'local', 'preference', 'routing') - def search_shards(self, index=None, doc_type=None, params=None): - """ - The search shards api returns the indices and shards that a search - request would be executed against. This can give useful feedback for working - out issues or planning optimizations with routing and shard preferences. - ``_ - - :arg index: A comma-separated list of index names to search; use `_all` - or empty string to perform the operation on all indices - :arg doc_type: A comma-separated list of document types to search; leave - empty to perform the operation on all types - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg preference: Specify the node or shard the operation should be - performed on (default: random) - :arg routing: Specific routing value - """ - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, '_search_shards'), params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'preference', 'routing', 'scroll', 'search_type') - def search_template(self, index=None, doc_type=None, body=None, params=None): - """ - A query that accepts a query template and a map of key/value pairs to - fill in template parameters. - ``_ - - :arg index: A comma-separated list of index names to search; use `_all` - or empty string to perform the operation on all indices - :arg doc_type: A comma-separated list of document types to search; leave - empty to perform the operation on all types - :arg body: The search definition template and its params - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg preference: Specify the node or shard the operation should be - performed on (default: random) - :arg routing: A comma-separated list of specific routing values - :arg scroll: Specify how long a consistent view of the index should be - maintained for scrolled search - :arg search_type: Search operation type, valid choices are: - 'query_then_fetch', 'query_and_fetch', 'dfs_query_then_fetch', - 'dfs_query_and_fetch', 'count', 'scan' - """ - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, '_search', 'template'), params=params, body=body) - return data - - @query_params('_source', '_source_exclude', '_source_include', - 'analyze_wildcard', 'analyzer', 'default_operator', 'df', 'fields', - 'lenient', 'lowercase_expanded_terms', 'parent', 'preference', 'q', - 'routing') - def explain(self, index, doc_type, id, body=None, params=None): - """ - The explain api computes a score explanation for a query and a specific - document. This can give useful feedback whether a document matches or - didn't match a specific query. - ``_ - - :arg index: The name of the index - :arg doc_type: The type of the document - :arg id: The document ID - :arg body: The query definition using the Query DSL - :arg _source: True or false to return the _source field or not, or a - list of fields to return - :arg _source_exclude: A list of fields to exclude from the returned - _source field - :arg _source_include: A list of fields to extract and return from the - _source field - :arg analyze_wildcard: Specify whether wildcards and prefix queries in - the query string query should be analyzed (default: false) - :arg analyzer: The analyzer for the query string query - :arg default_operator: The default operator for query string query (AND - or OR), default 'OR', valid choices are: 'AND', 'OR' - :arg df: The default field for query string query (default: _all) - :arg fields: A comma-separated list of fields to return in the response - :arg lenient: Specify whether format-based query failures (such as - providing text to a numeric field) should be ignored - :arg lowercase_expanded_terms: Specify whether query terms should be - lowercased - :arg parent: The ID of the parent document - :arg preference: Specify the node or shard the operation should be - performed on (default: random) - :arg q: Query in the Lucene query string syntax - :arg routing: Specific routing value - """ - for param in (index, doc_type, id): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, id, '_explain'), params=params, body=body) - return data - - @query_params('scroll') - def scroll(self, scroll_id=None, body=None, params=None): - """ - Scroll a search request created by specifying the scroll parameter. - ``_ - - :arg scroll_id: The scroll ID - :arg body: The scroll ID if not passed by URL or query parameter. - :arg scroll: Specify how long a consistent view of the index should be - maintained for scrolled search - """ - if scroll_id in SKIP_IN_PATH and body in SKIP_IN_PATH: - raise ValueError("You need to supply scroll_id or body.") - elif scroll_id and not body: - body = scroll_id - elif scroll_id: - params['scroll_id'] = scroll_id - - _, data = self.transport.perform_request('GET', '/_search/scroll', - params=params, body=body) - return data - - @query_params() - def clear_scroll(self, scroll_id=None, body=None, params=None): - """ - Clear the scroll request created by specifying the scroll parameter to - search. - ``_ - - :arg scroll_id: A comma-separated list of scroll IDs to clear - :arg body: A comma-separated list of scroll IDs to clear if none was - specified via the scroll_id parameter - """ - _, data = self.transport.perform_request('DELETE', _make_path('_search', - 'scroll', scroll_id), params=params, body=body) - return data - - @query_params('consistency', 'parent', 'refresh', 'routing', 'timeout', - 'version', 'version_type') - def delete(self, index, doc_type, id, params=None): - """ - Delete a typed JSON document from a specific index based on its id. - ``_ - - :arg index: The name of the index - :arg doc_type: The type of the document - :arg id: The document ID - :arg consistency: Specific write consistency setting for the operation, - valid choices are: 'one', 'quorum', 'all' - :arg parent: ID of parent document - :arg refresh: Refresh the index after performing the operation - :arg routing: Specific routing value - :arg timeout: Explicit operation timeout - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - for param in (index, doc_type, id): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('DELETE', _make_path(index, - doc_type, id), params=params) - return data - - @query_params('allow_no_indices', 'analyze_wildcard', 'analyzer', - 'default_operator', 'df', 'expand_wildcards', 'ignore_unavailable', - 'lenient', 'lowercase_expanded_terms', 'min_score', 'preference', 'q', - 'routing') - def count(self, index=None, doc_type=None, body=None, params=None): - """ - Execute a query and get the number of matches for that query. - ``_ - - :arg index: A comma-separated list of indices to restrict the results - :arg doc_type: A comma-separated list of types to restrict the results - :arg body: A query to restrict the results specified with the Query DSL - (optional) - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg analyze_wildcard: Specify whether wildcard and prefix queries - should be analyzed (default: false) - :arg analyzer: The analyzer to use for the query string - :arg default_operator: The default operator for query string query (AND - or OR), default 'OR', valid choices are: 'AND', 'OR' - :arg df: The field to use as default where no field prefix is given in - the query string - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg lenient: Specify whether format-based query failures (such as - providing text to a numeric field) should be ignored - :arg lowercase_expanded_terms: Specify whether query terms should be - lowercased - :arg min_score: Include only documents with a specific `_score` value in - the result - :arg preference: Specify the node or shard the operation should be - performed on (default: random) - :arg q: Query in the Lucene query string syntax - :arg routing: Specific routing value - """ - if doc_type and not index: - index = '_all' - - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, '_count'), params=params, body=body) - return data - - @query_params('consistency', 'fields', 'refresh', 'routing', 'timeout') - def bulk(self, body, index=None, doc_type=None, params=None): - """ - Perform many index/delete operations in a single API call. - - See the :func:`~elasticsearch.helpers.bulk` helper function for a more - friendly API. - ``_ - - :arg body: The operation definition and data (action-data pairs), - separated by newlines - :arg index: Default index for items which don't provide one - :arg doc_type: Default document type for items which don't provide one - :arg consistency: Explicit write consistency setting for the operation, - valid choices are: 'one', 'quorum', 'all' - :arg fields: Default comma-separated list of fields to return in the - response for updates - :arg refresh: Refresh the index after performing the operation - :arg routing: Specific routing value - :arg timeout: Explicit operation timeout - """ - if body in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'body'.") - _, data = self.transport.perform_request('POST', _make_path(index, - doc_type, '_bulk'), params=params, body=self._bulk_body(body)) - return data - - @query_params('search_type') - def msearch(self, body, index=None, doc_type=None, params=None): - """ - Execute several search requests within the same API. - ``_ - - :arg body: The request definitions (metadata-search request definition - pairs), separated by newlines - :arg index: A comma-separated list of index names to use as default - :arg doc_type: A comma-separated list of document types to use as - default - :arg search_type: Search operation type, valid choices are: - 'query_then_fetch', 'query_and_fetch', 'dfs_query_then_fetch', - 'dfs_query_and_fetch', 'count', 'scan' - """ - if body in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'body'.") - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, '_msearch'), params=params, body=self._bulk_body(body)) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'preference', 'routing') - def suggest(self, body, index=None, params=None): - """ - The suggest feature suggests similar looking terms based on a provided - text by using a suggester. - ``_ - - :arg body: The request definition - :arg index: A comma-separated list of index names to restrict the - operation; use `_all` or empty string to perform the operation on - all indices - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg preference: Specify the node or shard the operation should be - performed on (default: random) - :arg routing: Specific routing value - """ - if body in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'body'.") - _, data = self.transport.perform_request('POST', _make_path(index, - '_suggest'), params=params, body=body) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'percolate_format', 'percolate_index', 'percolate_preference', - 'percolate_routing', 'percolate_type', 'preference', 'routing', - 'version', 'version_type') - def percolate(self, index, doc_type, id=None, body=None, params=None): - """ - The percolator allows to register queries against an index, and then - send percolate requests which include a doc, and getting back the - queries that match on that doc out of the set of registered queries. - ``_ - - :arg index: The index of the document being percolated. - :arg doc_type: The type of the document being percolated. - :arg id: Substitute the document in the request body with a document - that is known by the specified id. On top of the id, the index and - type parameter will be used to retrieve the document from within the - cluster. - :arg body: The percolator request definition using the percolate DSL - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg percolate_format: Return an array of matching query IDs instead of - objects, valid choices are: 'ids' - :arg percolate_index: The index to percolate the document into. Defaults - to index. - :arg percolate_preference: Which shard to prefer when executing the - percolate request. - :arg percolate_routing: The routing value to use when percolating the - existing document. - :arg percolate_type: The type to percolate document into. Defaults to - type. - :arg preference: Specify the node or shard the operation should be - performed on (default: random) - :arg routing: A comma-separated list of specific routing values - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - for param in (index, doc_type): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, id, '_percolate'), params=params, body=body) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable') - def mpercolate(self, body, index=None, doc_type=None, params=None): - """ - The percolator allows to register queries against an index, and then - send percolate requests which include a doc, and getting back the - queries that match on that doc out of the set of registered queries. - ``_ - - :arg body: The percolate request definitions (header & body pair), - separated by newlines - :arg index: The index of the document being count percolated to use as - default - :arg doc_type: The type of the document being percolated to use as - default. - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - """ - if body in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'body'.") - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, '_mpercolate'), params=params, body=self._bulk_body(body)) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'percolate_index', 'percolate_type', 'preference', 'routing', 'version', - 'version_type') - def count_percolate(self, index, doc_type, id=None, body=None, params=None): - """ - The percolator allows to register queries against an index, and then - send percolate requests which include a doc, and getting back the - queries that match on that doc out of the set of registered queries. - ``_ - - :arg index: The index of the document being count percolated. - :arg doc_type: The type of the document being count percolated. - :arg id: Substitute the document in the request body with a document - that is known by the specified id. On top of the id, the index and - type parameter will be used to retrieve the document from within the - cluster. - :arg body: The count percolator request definition using the percolate - DSL - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg percolate_index: The index to count percolate the document into. - Defaults to index. - :arg percolate_type: The type to count percolate document into. Defaults - to type. - :arg preference: Specify the node or shard the operation should be - performed on (default: random) - :arg routing: A comma-separated list of specific routing values - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - for param in (index, doc_type): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, id, '_percolate', 'count'), params=params, body=body) - return data - - @query_params('dfs', 'field_statistics', 'fields', 'offsets', 'parent', - 'payloads', 'positions', 'preference', 'realtime', 'routing', - 'term_statistics', 'version', 'version_type') - def termvectors(self, index, doc_type, id=None, body=None, params=None): - """ - Returns information and statistics on terms in the fields of a - particular document. The document could be stored in the index or - artificially provided by the user (Added in 1.4). Note that for - documents stored in the index, this is a near realtime API as the term - vectors are not available until the next refresh. - ``_ - - :arg index: The index in which the document resides. - :arg doc_type: The type of the document. - :arg id: The id of the document, when not specified a doc param should - be supplied. - :arg body: Define parameters and or supply a document to get termvectors - for. See documentation. - :arg dfs: Specifies if distributed frequencies should be returned - instead shard frequencies., default False - :arg field_statistics: Specifies if document count, sum of document - frequencies and sum of total term frequencies should be returned., - default True - :arg fields: A comma-separated list of fields to return. - :arg offsets: Specifies if term offsets should be returned., default - True - :arg parent: Parent id of documents. - :arg payloads: Specifies if term payloads should be returned., default - True - :arg positions: Specifies if term positions should be returned., default - True - :arg preference: Specify the node or shard the operation should be - performed on (default: random). - :arg realtime: Specifies if request is real-time as opposed to near- - real-time (default: true). - :arg routing: Specific routing value. - :arg term_statistics: Specifies if total term frequency and document - frequency should be returned., default False - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - for param in (index, doc_type): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, id, '_termvectors'), params=params, body=body) - return data - - @query_params('field_statistics', 'fields', 'ids', 'offsets', 'parent', - 'payloads', 'positions', 'preference', 'realtime', 'routing', - 'term_statistics', 'version', 'version_type') - def mtermvectors(self, index=None, doc_type=None, body=None, params=None): - """ - Multi termvectors API allows to get multiple termvectors based on an - index, type and id. - ``_ - - :arg index: The index in which the document resides. - :arg doc_type: The type of the document. - :arg body: Define ids, documents, parameters or a list of parameters per - document here. You must at least provide a list of document ids. See - documentation. - :arg field_statistics: Specifies if document count, sum of document - frequencies and sum of total term frequencies should be returned. - Applies to all returned documents unless otherwise specified in body - "params" or "docs"., default True - :arg fields: A comma-separated list of fields to return. Applies to all - returned documents unless otherwise specified in body "params" or - "docs". - :arg ids: A comma-separated list of documents ids. You must define ids - as parameter or set "ids" or "docs" in the request body - :arg offsets: Specifies if term offsets should be returned. Applies to - all returned documents unless otherwise specified in body "params" - or "docs"., default True - :arg parent: Parent id of documents. Applies to all returned documents - unless otherwise specified in body "params" or "docs". - :arg payloads: Specifies if term payloads should be returned. Applies to - all returned documents unless otherwise specified in body "params" - or "docs"., default True - :arg positions: Specifies if term positions should be returned. Applies - to all returned documents unless otherwise specified in body - "params" or "docs"., default True - :arg preference: Specify the node or shard the operation should be - performed on (default: random) .Applies to all returned documents - unless otherwise specified in body "params" or "docs". - :arg realtime: Specifies if requests are real-time as opposed to near- - real-time (default: true). - :arg routing: Specific routing value. Applies to all returned documents - unless otherwise specified in body "params" or "docs". - :arg term_statistics: Specifies if total term frequency and document - frequency should be returned. Applies to all returned documents - unless otherwise specified in body "params" or "docs"., default - False - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, '_mtermvectors'), params=params, body=body) - return data - - @query_params('op_type', 'version', 'version_type') - def put_script(self, lang, id, body, params=None): - """ - Create a script in given language with specified ID. - ``_ - - :arg lang: Script language - :arg id: Script ID - :arg body: The document - :arg op_type: Explicit operation type, default 'index', valid choices - are: 'index', 'create' - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - for param in (lang, id, body): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('PUT', _make_path('_scripts', - lang, id), params=params, body=body) - return data - - @query_params('version', 'version_type') - def get_script(self, lang, id, params=None): - """ - Retrieve a script from the API. - ``_ - - :arg lang: Script language - :arg id: Script ID - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - for param in (lang, id): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('GET', _make_path('_scripts', - lang, id), params=params) - return data - - @query_params('version', 'version_type') - def delete_script(self, lang, id, params=None): - """ - Remove a stored script from elasticsearch. - ``_ - - :arg lang: Script language - :arg id: Script ID - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - for param in (lang, id): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('DELETE', - _make_path('_scripts', lang, id), params=params) - return data - - @query_params('op_type', 'version', 'version_type') - def put_template(self, id, body, params=None): - """ - Create a search template. - ``_ - - :arg id: Template ID - :arg body: The document - :arg op_type: Explicit operation type, default 'index', valid choices - are: 'index', 'create' - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - for param in (id, body): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('PUT', _make_path('_search', - 'template', id), params=params, body=body) - return data - - @query_params('version', 'version_type') - def get_template(self, id, params=None): - """ - Retrieve a search template. - ``_ - - :arg id: Template ID - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - if id in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'id'.") - _, data = self.transport.perform_request('GET', _make_path('_search', - 'template', id), params=params) - return data - - @query_params('version', 'version_type') - def delete_template(self, id, params=None): - """ - Delete a search template. - ``_ - - :arg id: Template ID - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type, valid choices are: 'internal', - 'external', 'external_gte', 'force' - """ - if id in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'id'.") - _, data = self.transport.perform_request('DELETE', _make_path('_search', - 'template', id), params=params) - return data - - @query_params('allow_no_indices', 'analyze_wildcard', 'analyzer', - 'default_operator', 'df', 'expand_wildcards', 'ignore_unavailable', - 'lenient', 'lowercase_expanded_terms', 'min_score', 'preference', 'q', - 'routing') - def search_exists(self, index=None, doc_type=None, body=None, params=None): - """ - The exists API allows to easily determine if any matching documents - exist for a provided query. - ``_ - - :arg index: A comma-separated list of indices to restrict the results - :arg doc_type: A comma-separated list of types to restrict the results - :arg body: A query to restrict the results specified with the Query DSL - (optional) - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg analyze_wildcard: Specify whether wildcard and prefix queries - should be analyzed (default: false) - :arg analyzer: The analyzer to use for the query string - :arg default_operator: The default operator for query string query (AND - or OR), default 'OR', valid choices are: 'AND', 'OR' - :arg df: The field to use as default where no field prefix is given in - the query string - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg lenient: Specify whether format-based query failures (such as - providing text to a numeric field) should be ignored - :arg lowercase_expanded_terms: Specify whether query terms should be - lowercased - :arg min_score: Include only documents with a specific `_score` value in - the result - :arg preference: Specify the node or shard the operation should be - performed on (default: random) - :arg q: Query in the Lucene query string syntax - :arg routing: Specific routing value - """ - try: - self.transport.perform_request('POST', _make_path(index, - doc_type, '_search', 'exists'), params=params, body=body) - except NotFoundError: - return False - return True - - @query_params('allow_no_indices', 'expand_wildcards', 'fields', - 'ignore_unavailable', 'level') - def field_stats(self, index=None, body=None, params=None): - """ - The field stats api allows one to find statistical properties of a - field without executing a search, but looking up measurements that are - natively available in the Lucene index. - ``_ - - :arg index: A comma-separated list of index names; use `_all` or empty - string to perform the operation on all indices - :arg body: Field json objects containing the name and optionally a range - to filter out indices result, that have results outside the defined - bounds - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg fields: A comma-separated list of fields for to get field - statistics for (min value, max value, and more) - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg level: Defines if field stats should be returned on a per index - level or on a cluster wide level, default 'cluster', valid choices - are: 'indices', 'cluster' - """ - _, data = self.transport.perform_request('GET', _make_path(index, - '_field_stats'), params=params, body=body) - return data - - @query_params() - def render_search_template(self, id=None, body=None, params=None): - """ - ``_ - - :arg id: The id of the stored search template - :arg body: The search definition template and its params - """ - _, data = self.transport.perform_request('GET', _make_path('_render', - 'template', id), params=params, body=body) - return data - diff --git a/venv/lib/python2.7/site-packages/elasticsearch/client/cat.py b/venv/lib/python2.7/site-packages/elasticsearch/client/cat.py deleted file mode 100644 index 092ce30..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/client/cat.py +++ /dev/null @@ -1,346 +0,0 @@ -from .utils import NamespacedClient, query_params, _make_path - -class CatClient(NamespacedClient): - @query_params('h', 'help', 'local', 'master_timeout', 'v') - def aliases(self, name=None, params=None): - """ - - ``_ - - :arg name: A comma-separated list of alias names to return - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', _make_path('_cat', - 'aliases', name), params=params) - return data - - @query_params('bytes', 'h', 'help', 'local', 'master_timeout', 'v') - def allocation(self, node_id=None, params=None): - """ - Allocation provides a snapshot of how shards have located around the - cluster and the state of disk usage. - ``_ - - :arg node_id: A comma-separated list of node IDs or names to limit the - returned information - :arg bytes: The unit in which to display byte values, valid choices are: - 'b', 'k', 'm', 'g' - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', _make_path('_cat', - 'allocation', node_id), params=params) - return data - - @query_params('h', 'help', 'local', 'master_timeout', 'v') - def count(self, index=None, params=None): - """ - Count provides quick access to the document count of the entire cluster, - or individual indices. - ``_ - - :arg index: A comma-separated list of index names to limit the returned - information - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', _make_path('_cat', - 'count', index), params=params) - return data - - @query_params('h', 'help', 'local', 'master_timeout', 'ts', 'v') - def health(self, params=None): - """ - health is a terse, one-line representation of the same information from - :meth:`~elasticsearch.client.cluster.ClusterClient.health` API - ``_ - - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg ts: Set to false to disable timestamping, default True - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', '/_cat/health', - params=params) - return data - - @query_params('help') - def help(self, params=None): - """ - A simple help for the cat api. - ``_ - - :arg help: Return help information, default False - """ - _, data = self.transport.perform_request('GET', '/_cat', params=params) - return data - - @query_params('bytes', 'h', 'help', 'local', 'master_timeout', 'pri', 'v') - def indices(self, index=None, params=None): - """ - The indices command provides a cross-section of each index. - ``_ - - :arg index: A comma-separated list of index names to limit the returned - information - :arg bytes: The unit in which to display byte values, valid choices are: - 'b', 'k', 'm', 'g' - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg pri: Set to true to return stats only for primary shards, default - False - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', _make_path('_cat', - 'indices', index), params=params) - return data - - @query_params('h', 'help', 'local', 'master_timeout', 'v') - def master(self, params=None): - """ - Displays the master's node ID, bound IP address, and node name. - ``_ - - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', '/_cat/master', - params=params) - return data - - @query_params('h', 'help', 'local', 'master_timeout', 'v') - def nodes(self, params=None): - """ - The nodes command shows the cluster topology. - ``_ - - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', '/_cat/nodes', - params=params) - return data - - @query_params('bytes', 'h', 'help', 'master_timeout', 'v') - def recovery(self, index=None, params=None): - """ - recovery is a view of shard replication. - ``_ - - :arg index: A comma-separated list of index names to limit the returned - information - :arg bytes: The unit in which to display byte values, valid choices are: - 'b', 'k', 'm', 'g' - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', _make_path('_cat', - 'recovery', index), params=params) - return data - - @query_params('bytes', 'h', 'help', 'local', 'master_timeout', 'v') - def shards(self, index=None, params=None): - """ - The shards command is the detailed view of what nodes contain which shards. - ``_ - - :arg index: A comma-separated list of index names to limit the returned - information - :arg bytes: The unit in which to display byte values, valid choices are: - 'b', 'k', 'm', 'g' - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', _make_path('_cat', - 'shards', index), params=params) - return data - - @query_params('bytes', 'h', 'help', 'v') - def segments(self, index=None, params=None): - """ - The segments command is the detailed view of Lucene segments per index. - ``_ - - :arg index: A comma-separated list of index names to limit the returned - information - :arg bytes: The unit in which to display byte values, valid choices are: - 'b', 'k', 'm', 'g' - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', _make_path('_cat', - 'segments', index), params=params) - return data - - @query_params('h', 'help', 'local', 'master_timeout', 'v') - def pending_tasks(self, params=None): - """ - pending_tasks provides the same information as the - :meth:`~elasticsearch.client.cluster.ClusterClient.pending_tasks` API - in a convenient tabular format. - ``_ - - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', '/_cat/pending_tasks', - params=params) - return data - - @query_params('full_id', 'h', 'help', 'local', 'master_timeout', 'v') - def thread_pool(self, params=None): - """ - Get information about thread pools. - ``_ - - :arg full_id: Enables displaying the complete node ids, default False - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', '/_cat/thread_pool', - params=params) - return data - - @query_params('bytes', 'h', 'help', 'local', 'master_timeout', 'v') - def fielddata(self, fields=None, params=None): - """ - Shows information about currently loaded fielddata on a per-node basis. - ``_ - - :arg fields: A comma-separated list of fields to return the fielddata - size - :arg bytes: The unit in which to display byte values, valid choices are: - 'b', 'k', 'm', 'g' - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', _make_path('_cat', - 'fielddata', fields), params=params) - return data - - @query_params('h', 'help', 'local', 'master_timeout', 'v') - def plugins(self, params=None): - """ - - ``_ - - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', '/_cat/plugins', - params=params) - return data - - @query_params('h', 'help', 'local', 'master_timeout', 'v') - def nodeattrs(self, params=None): - """ - ``_ - - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', '/_cat/nodeattrs', - params=params) - return data - - @query_params('h', 'help', 'local', 'master_timeout', 'v') - def repositories(self, params=None): - """ - ``_ - - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg local: Return local information, do not retrieve the state from - master node, default False - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', '/_cat/repositories', - params=params) - return data - - @query_params('h', 'help', 'master_timeout', 'v') - def snapshots(self, repository=None, params=None): - """ - ``_ - - :arg repository: Name of repository from which to fetch the snapshot - information - :arg h: Comma-separated list of column names to display - :arg help: Return help information, default False - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg v: Verbose mode. Display column headers, default False - """ - _, data = self.transport.perform_request('GET', _make_path('_cat', - 'snapshots', repository), params=params) - return data diff --git a/venv/lib/python2.7/site-packages/elasticsearch/client/cluster.py b/venv/lib/python2.7/site-packages/elasticsearch/client/cluster.py deleted file mode 100644 index 2f9e063..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/client/cluster.py +++ /dev/null @@ -1,154 +0,0 @@ -from .utils import NamespacedClient, query_params, _make_path - -class ClusterClient(NamespacedClient): - @query_params('level', 'local', 'master_timeout', 'timeout', - 'wait_for_active_shards', 'wait_for_nodes', - 'wait_for_relocating_shards', 'wait_for_status') - def health(self, index=None, params=None): - """ - Get a very simple status on the health of the cluster. - ``_ - - :arg index: Limit the information returned to a specific index - :arg level: Specify the level of detail for returned information, - default 'cluster', valid choices are: 'cluster', 'indices', 'shards' - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg timeout: Explicit operation timeout - :arg wait_for_active_shards: Wait until the specified number of shards - is active - :arg wait_for_nodes: Wait until the specified number of nodes is - available - :arg wait_for_relocating_shards: Wait until the specified number of - relocating shards is finished - :arg wait_for_status: Wait until cluster is in a specific state, default - None, valid choices are: 'green', 'yellow', 'red' - """ - _, data = self.transport.perform_request('GET', _make_path('_cluster', - 'health', index), params=params) - return data - - @query_params('local', 'master_timeout') - def pending_tasks(self, params=None): - """ - The pending cluster tasks API returns a list of any cluster-level - changes (e.g. create index, update mapping, allocate or fail shard) - which have not yet been executed. - ``_ - - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Specify timeout for connection to master - """ - _, data = self.transport.perform_request('GET', - '/_cluster/pending_tasks', params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'flat_settings', - 'ignore_unavailable', 'local', 'master_timeout') - def state(self, metric=None, index=None, params=None): - """ - Get a comprehensive state information of the whole cluster. - ``_ - - :arg metric: Limit the information returned to the specified metrics - :arg index: A comma-separated list of index names; use `_all` or empty - string to perform the operation on all indices - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg flat_settings: Return settings in flat format (default: false) - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Specify timeout for connection to master - """ - if index and not metric: - metric = '_all' - _, data = self.transport.perform_request('GET', _make_path('_cluster', - 'state', metric, index), params=params) - return data - - @query_params('flat_settings', 'human', 'timeout') - def stats(self, node_id=None, params=None): - """ - The Cluster Stats API allows to retrieve statistics from a cluster wide - perspective. The API returns basic index metrics and information about - the current nodes that form the cluster. - ``_ - - :arg node_id: A comma-separated list of node IDs or names to limit the - returned information; use `_local` to return information from the - node you're connecting to, leave empty to get information from all - nodes - :arg flat_settings: Return settings in flat format (default: false) - :arg human: Whether to return time and byte values in human-readable - format., default False - :arg timeout: Explicit operation timeout - """ - url = '/_cluster/stats' - if node_id: - url = _make_path('_cluster/stats/nodes', node_id) - _, data = self.transport.perform_request('GET', url, params=params) - return data - - @query_params('dry_run', 'explain', 'master_timeout', 'metric', 'timeout') - def reroute(self, body=None, params=None): - """ - Explicitly execute a cluster reroute allocation command including specific commands. - ``_ - - :arg body: The definition of `commands` to perform (`move`, `cancel`, - `allocate`) - :arg dry_run: Simulate the operation only and return the resulting state - :arg explain: Return an explanation of why the commands can or cannot be - executed - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg metric: Limit the information returned to the specified metrics. - Defaults to all but metadata, valid choices are: '_all', 'blocks', - 'metadata', 'nodes', 'routing_table', 'master_node', 'version' - :arg timeout: Explicit operation timeout - """ - _, data = self.transport.perform_request('POST', '/_cluster/reroute', - params=params, body=body) - return data - - @query_params('flat_settings', 'master_timeout', 'timeout') - def get_settings(self, params=None): - """ - Get cluster settings. - ``_ - - :arg flat_settings: Return settings in flat format (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg timeout: Explicit operation timeout - """ - _, data = self.transport.perform_request('GET', '/_cluster/settings', - params=params) - return data - - @query_params('flat_settings', 'master_timeout', 'timeout') - def put_settings(self, body=None, params=None): - """ - Update cluster wide specific settings. - ``_ - - :arg body: The settings to be updated. Can be either `transient` or - `persistent` (survives cluster restart). - :arg flat_settings: Return settings in flat format (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg timeout: Explicit operation timeout - """ - _, data = self.transport.perform_request('PUT', '/_cluster/settings', - params=params, body=body) - return data - diff --git a/venv/lib/python2.7/site-packages/elasticsearch/client/indices.py b/venv/lib/python2.7/site-packages/elasticsearch/client/indices.py deleted file mode 100644 index 55dad3c..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/client/indices.py +++ /dev/null @@ -1,998 +0,0 @@ -from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH -from ..exceptions import NotFoundError - -class IndicesClient(NamespacedClient): - @query_params('analyzer', 'char_filters', 'field', 'filters', 'format', - 'prefer_local', 'text', 'tokenizer') - def analyze(self, index=None, body=None, params=None): - """ - Perform the analysis process on a text and return the tokens breakdown of the text. - ``_ - - :arg index: The name of the index to scope the operation - :arg body: The text on which the analysis should be performed - :arg analyzer: The name of the analyzer to use - :arg char_filters: A comma-separated list of character filters to use - for the analysis - :arg field: Use the analyzer configured for this field (instead of - passing the analyzer name) - :arg filters: A comma-separated list of filters to use for the analysis - :arg format: Format of the output, default 'detailed', valid choices - are: 'detailed', 'text' - :arg prefer_local: With `true`, specify that a local shard should be - used if available, with `false`, use a random shard (default: true) - :arg text: The text on which the analysis should be performed (when - request body is not used) - :arg tokenizer: The name of the tokenizer to use for the analysis - """ - _, data = self.transport.perform_request('GET', _make_path(index, - '_analyze'), params=params, body=body) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'force', - 'ignore_unavailable', 'operation_threading') - def refresh(self, index=None, params=None): - """ - Explicitly refresh one or more index, making all operations performed - since the last refresh available for search. - ``_ - - :arg index: A comma-separated list of index names; use `_all` or empty - string to perform the operation on all indices - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg force: Force a refresh even if not required, default False - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg operation_threading: TODO: ? - """ - _, data = self.transport.perform_request('POST', _make_path(index, - '_refresh'), params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'force', - 'ignore_unavailable', 'wait_if_ongoing') - def flush(self, index=None, params=None): - """ - Explicitly flush one or more indices. - ``_ - - :arg index: A comma-separated list of index names; use `_all` or empty - string for all indices - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg force: Whether a flush should be forced even if it is not - necessarily needed ie. if no changes will be committed to the index. - This is useful if transaction log IDs should be incremented even if - no uncommitted changes are present. (This setting can be considered - as internal) - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg wait_if_ongoing: If set to true the flush operation will block - until the flush can be executed if another flush operation is - already executing. The default is false and will cause an exception - to be thrown on the shard level if another flush operation is - already running. - """ - _, data = self.transport.perform_request('POST', _make_path(index, - '_flush'), params=params) - return data - - @query_params('master_timeout', 'timeout', 'update_all_types') - def create(self, index, body=None, params=None): - """ - Create an index in Elasticsearch. - ``_ - - :arg index: The name of the index - :arg body: The configuration for the index (`settings` and `mappings`) - :arg master_timeout: Specify timeout for connection to master - :arg timeout: Explicit operation timeout - :arg update_all_types: Whether to update the mapping for all fields with - the same name across all types or not - """ - if index in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'index'.") - _, data = self.transport.perform_request('PUT', _make_path(index), - params=params, body=body) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'flat_settings', - 'human', 'ignore_unavailable', 'local') - def get(self, index, feature=None, params=None): - """ - The get index API allows to retrieve information about one or more indexes. - ``_ - - :arg index: A comma-separated list of index names - :arg feature: A comma-separated list of features - :arg allow_no_indices: Ignore if a wildcard expression resolves to no - concrete indices (default: false) - :arg expand_wildcards: Whether wildcard expressions should get expanded - to open or closed indices (default: open), default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg flat_settings: Return settings in flat format (default: false) - :arg human: Whether to return version and creation date values in human- - readable format., default False - :arg ignore_unavailable: Ignore unavailable indexes (default: false) - :arg local: Return local information, do not retrieve the state from - master node (default: false) - """ - if index in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'index'.") - _, data = self.transport.perform_request('GET', _make_path(index, - feature), params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'master_timeout', 'timeout') - def open(self, index, params=None): - """ - Open a closed index to make it available for search. - ``_ - - :arg index: The name of the index - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'closed', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg master_timeout: Specify timeout for connection to master - :arg timeout: Explicit operation timeout - """ - if index in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'index'.") - _, data = self.transport.perform_request('POST', _make_path(index, - '_open'), params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'master_timeout', 'timeout') - def close(self, index, params=None): - """ - Close an index to remove it's overhead from the cluster. Closed index - is blocked for read/write operations. - ``_ - - :arg index: The name of the index - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg master_timeout: Specify timeout for connection to master - :arg timeout: Explicit operation timeout - """ - if index in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'index'.") - _, data = self.transport.perform_request('POST', _make_path(index, - '_close'), params=params) - return data - - @query_params('master_timeout', 'timeout') - def delete(self, index, params=None): - """ - Delete an index in Elasticsearch - ``_ - - :arg index: A comma-separated list of indices to delete; use `_all` or - `*` string to delete all indices - :arg master_timeout: Specify timeout for connection to master - :arg timeout: Explicit operation timeout - """ - if index in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'index'.") - _, data = self.transport.perform_request('DELETE', _make_path(index), - params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'local') - def exists(self, index, params=None): - """ - Return a boolean indicating whether given index exists. - ``_ - - :arg index: A comma-separated list of indices to check - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg local: Return local information, do not retrieve the state from - master node (default: false) - """ - if index in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'index'.") - try: - self.transport.perform_request('HEAD', _make_path(index), - params=params) - except NotFoundError: - return False - return True - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'local') - def exists_type(self, index, doc_type, params=None): - """ - Check if a type/types exists in an index/indices. - ``_ - - :arg index: A comma-separated list of index names; use `_all` to check - the types across all indices - :arg doc_type: A comma-separated list of document types to check - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg local: Return local information, do not retrieve the state from - master node (default: false) - """ - for param in (index, doc_type): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - try: - self.transport.perform_request('HEAD', _make_path(index, doc_type), - params=params) - except NotFoundError: - return False - return True - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'master_timeout', 'timeout', 'update_all_types') - def put_mapping(self, doc_type, body, index=None, params=None): - """ - Register specific mapping definition for a specific type. - ``_ - - :arg doc_type: The name of the document type - :arg body: The mapping definition - :arg index: A comma-separated list of index names the mapping should be - added to (supports wildcards); use `_all` or omit to add the mapping - on all indices. - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg master_timeout: Specify timeout for connection to master - :arg timeout: Explicit operation timeout - :arg update_all_types: Whether to update the mapping for all fields with - the same name across all types or not - """ - for param in (doc_type, body): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('PUT', _make_path(index, - '_mapping', doc_type), params=params, body=body) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'local') - def get_mapping(self, index=None, doc_type=None, params=None): - """ - Retrieve mapping definition of index or index/type. - ``_ - - :arg index: A comma-separated list of index names - :arg doc_type: A comma-separated list of document types - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg local: Return local information, do not retrieve the state from - master node (default: false) - """ - _, data = self.transport.perform_request('GET', _make_path(index, - '_mapping', doc_type), params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'include_defaults', 'local') - def get_field_mapping(self, fields, index=None, doc_type=None, params=None): - """ - Retrieve mapping definition of a specific field. - ``_ - - :arg fields: A comma-separated list of fields - :arg index: A comma-separated list of index names - :arg doc_type: A comma-separated list of document types - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg include_defaults: Whether the default mapping values should be - returned as well - :arg local: Return local information, do not retrieve the state from - master node (default: false) - """ - if fields in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'fields'.") - _, data = self.transport.perform_request('GET', _make_path(index, - '_mapping', doc_type, 'field', fields), params=params) - return data - - @query_params('master_timeout', 'timeout') - def put_alias(self, index, name, body=None, params=None): - """ - Create an alias for a specific index/indices. - ``_ - - :arg index: A comma-separated list of index names the alias should point - to (supports wildcards); use `_all` to perform the operation on all - indices. - :arg name: The name of the alias to be created or updated - :arg body: The settings for the alias, such as `routing` or `filter` - :arg master_timeout: Specify timeout for connection to master - :arg timeout: Explicit timeout for the operation - """ - for param in (index, name): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('PUT', _make_path(index, - '_alias', name), params=params, body=body) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'local') - def exists_alias(self, index=None, name=None, params=None): - """ - Return a boolean indicating whether given alias exists. - ``_ - - :arg index: A comma-separated list of index names to filter aliases - :arg name: A comma-separated list of alias names to return - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default ['open', 'closed'], - valid choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg local: Return local information, do not retrieve the state from - master node (default: false) - """ - try: - self.transport.perform_request('HEAD', _make_path(index, '_alias', - name), params=params) - except NotFoundError: - return False - return True - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'local') - def get_alias(self, index=None, name=None, params=None): - """ - Retrieve a specified alias. - ``_ - - :arg index: A comma-separated list of index names to filter aliases - :arg name: A comma-separated list of alias names to return - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg local: Return local information, do not retrieve the state from - master node (default: false) - """ - _, data = self.transport.perform_request('GET', _make_path(index, - '_alias', name), params=params) - return data - - @query_params('local', 'timeout') - def get_aliases(self, index=None, name=None, params=None): - """ - Retrieve specified aliases - ``_ - - :arg index: A comma-separated list of index names to filter aliases - :arg name: A comma-separated list of alias names to filter - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg timeout: Explicit operation timeout - """ - _, data = self.transport.perform_request('GET', _make_path(index, - '_aliases', name), params=params) - return data - - @query_params('master_timeout', 'timeout') - def update_aliases(self, body, params=None): - """ - Update specified aliases. - ``_ - - :arg body: The definition of `actions` to perform - :arg master_timeout: Specify timeout for connection to master - :arg timeout: Request timeout - """ - if body in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'body'.") - _, data = self.transport.perform_request('POST', '/_aliases', - params=params, body=body) - return data - - @query_params('master_timeout', 'timeout') - def delete_alias(self, index, name, params=None): - """ - Delete specific alias. - ``_ - - :arg index: A comma-separated list of index names (supports wildcards); - use `_all` for all indices - :arg name: A comma-separated list of aliases to delete (supports - wildcards); use `_all` to delete all aliases for the specified - indices. - :arg master_timeout: Specify timeout for connection to master - :arg timeout: Explicit timeout for the operation - """ - for param in (index, name): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('DELETE', _make_path(index, - '_alias', name), params=params) - return data - - @query_params('create', 'flat_settings', 'master_timeout', 'order', - 'timeout') - def put_template(self, name, body, params=None): - """ - Create an index template that will automatically be applied to new - indices created. - ``_ - - :arg name: The name of the template - :arg body: The template definition - :arg create: Whether the index template should only be added if new or - can also replace an existing one, default False - :arg flat_settings: Return settings in flat format (default: false) - :arg master_timeout: Specify timeout for connection to master - :arg order: The order for this template when merging multiple matching - ones (higher numbers are merged later, overriding the lower numbers) - :arg timeout: Explicit operation timeout - """ - for param in (name, body): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('PUT', _make_path('_template', - name), params=params, body=body) - return data - - @query_params('local', 'master_timeout') - def exists_template(self, name, params=None): - """ - Return a boolean indicating whether given template exists. - ``_ - - :arg name: The name of the template - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - """ - if name in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'name'.") - try: - self.transport.perform_request('HEAD', _make_path('_template', - name), params=params) - except NotFoundError: - return False - return True - - @query_params('flat_settings', 'local', 'master_timeout') - def get_template(self, name=None, params=None): - """ - Retrieve an index template by its name. - ``_ - - :arg name: The name of the template - :arg flat_settings: Return settings in flat format (default: false) - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - """ - _, data = self.transport.perform_request('GET', _make_path('_template', - name), params=params) - return data - - @query_params('master_timeout', 'timeout') - def delete_template(self, name, params=None): - """ - Delete an index template by its name. - ``_ - - :arg name: The name of the template - :arg master_timeout: Specify timeout for connection to master - :arg timeout: Explicit operation timeout - """ - if name in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'name'.") - _, data = self.transport.perform_request('DELETE', - _make_path('_template', name), params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'flat_settings', - 'human', 'ignore_unavailable', 'local') - def get_settings(self, index=None, name=None, params=None): - """ - Retrieve settings for one or more (or all) indices. - ``_ - - :arg index: A comma-separated list of index names; use `_all` or empty - string to perform the operation on all indices - :arg name: The name of the settings that should be included - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default ['open', 'closed'], - valid choices are: 'open', 'closed', 'none', 'all' - :arg flat_settings: Return settings in flat format (default: false) - :arg human: Whether to return version and creation date values in human- - readable format., default False - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg local: Return local information, do not retrieve the state from - master node (default: false) - """ - _, data = self.transport.perform_request('GET', _make_path(index, - '_settings', name), params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'flat_settings', - 'ignore_unavailable', 'master_timeout') - def put_settings(self, body, index=None, params=None): - """ - Change specific index level settings in real time. - ``_ - - :arg body: The index settings to be updated - :arg index: A comma-separated list of index names; use `_all` or empty - string to perform the operation on all indices - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg flat_settings: Return settings in flat format (default: false) - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg master_timeout: Specify timeout for connection to master - """ - if body in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'body'.") - _, data = self.transport.perform_request('PUT', _make_path(index, - '_settings'), params=params, body=body) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'master_timeout', 'request_cache') - def put_warmer(self, name, body, index=None, doc_type=None, params=None): - """ - Create an index warmer to run registered search requests to warm up the - index before it is available for search. - ``_ - - :arg name: The name of the warmer - :arg body: The search request definition for the warmer (query, filters, - facets, sorting, etc) - :arg index: A comma-separated list of index names to register the warmer - for; use `_all` or omit to perform the operation on all indices - :arg doc_type: A comma-separated list of document types to register the - warmer for; leave empty to perform the operation on all types - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices in the search request - to warm. (This includes `_all` string or when no indices have been - specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both, in the search request to - warm., default 'open', valid choices are: 'open', 'closed', 'none', - 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) in the search request - to warm - :arg master_timeout: Specify timeout for connection to master - :arg request_cache: Specify whether the request to be warmed should use - the request cache, defaults to index level setting - """ - for param in (name, body): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('PUT', _make_path(index, - doc_type, '_warmer', name), params=params, body=body) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'local') - def get_warmer(self, index=None, doc_type=None, name=None, params=None): - """ - Retreieve an index warmer. - ``_ - - :arg index: A comma-separated list of index names to restrict the - operation; use `_all` to perform the operation on all indices - :arg doc_type: A comma-separated list of document types to restrict the - operation; leave empty to perform the operation on all types - :arg name: The name of the warmer (supports wildcards); leave empty to - get all warmers - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg local: Return local information, do not retrieve the state from - master node (default: false) - """ - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, '_warmer', name), params=params) - return data - - @query_params('master_timeout') - def delete_warmer(self, index, name, params=None): - """ - Delete an index warmer. - ``_ - - :arg index: A comma-separated list of index names to delete warmers from - (supports wildcards); use `_all` to perform the operation on all - indices. - :arg name: A comma-separated list of warmer names to delete (supports - wildcards); use `_all` to delete all warmers in the specified - indices. You must specify a name either in the uri or in the - parameters. - :arg master_timeout: Specify timeout for connection to master - """ - for param in (index, name): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('DELETE', _make_path(index, - '_warmer', name), params=params) - return data - - @query_params('completion_fields', 'fielddata_fields', 'fields', 'groups', - 'human', 'level', 'types') - def stats(self, index=None, metric=None, params=None): - """ - Retrieve statistics on different operations happening on an index. - ``_ - - :arg index: A comma-separated list of index names; use `_all` or empty - string to perform the operation on all indices - :arg metric: Limit the information returned the specific metrics. - :arg completion_fields: A comma-separated list of fields for `fielddata` - and `suggest` index metric (supports wildcards) - :arg fielddata_fields: A comma-separated list of fields for `fielddata` - index metric (supports wildcards) - :arg fields: A comma-separated list of fields for `fielddata` and - `completion` index metric (supports wildcards) - :arg groups: A comma-separated list of search groups for `search` index - metric - :arg human: Whether to return time and byte values in human-readable - format., default False - :arg level: Return stats aggregated at cluster, index or shard level, - default 'indices', valid choices are: 'cluster', 'indices', 'shards' - :arg types: A comma-separated list of document types for the `indexing` - index metric - """ - _, data = self.transport.perform_request('GET', _make_path(index, - '_stats', metric), params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'human', - 'ignore_unavailable', 'operation_threading') - def segments(self, index=None, params=None): - """ - Provide low level segments information that a Lucene index (shard level) is built with. - ``_ - - :arg index: A comma-separated list of index names; use `_all` or empty - string to perform the operation on all indices - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg human: Whether to return time and byte values in human-readable - format., default False - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg operation_threading: TODO: ? - """ - _, data = self.transport.perform_request('GET', _make_path(index, - '_segments'), params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'flush', - 'ignore_unavailable', 'max_num_segments', 'only_expunge_deletes', - 'operation_threading', 'wait_for_merge') - def optimize(self, index=None, params=None): - """ - Explicitly optimize one or more indices through an API. - ``_ - - :arg index: A comma-separated list of index names; use `_all` or empty - string to perform the operation on all indices - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg flush: Specify whether the index should be flushed after performing - the operation (default: true) - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg max_num_segments: The number of segments the index should be merged - into (default: dynamic) - :arg only_expunge_deletes: Specify whether the operation should only - expunge deleted documents - :arg operation_threading: TODO: ? - :arg wait_for_merge: Specify whether the request should block until the - merge process is finished (default: true) - """ - _, data = self.transport.perform_request('POST', _make_path(index, - '_optimize'), params=params) - return data - - @query_params('allow_no_indices', 'analyze_wildcard', 'analyzer', - 'default_operator', 'df', 'expand_wildcards', 'explain', - 'ignore_unavailable', 'lenient', 'lowercase_expanded_terms', - 'operation_threading', 'q', 'rewrite') - def validate_query(self, index=None, doc_type=None, body=None, params=None): - """ - Validate a potentially expensive query without executing it. - ``_ - - :arg index: A comma-separated list of index names to restrict the - operation; use `_all` or empty string to perform the operation on - all indices - :arg doc_type: A comma-separated list of document types to restrict the - operation; leave empty to perform the operation on all types - :arg body: The query definition specified with the Query DSL - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg analyze_wildcard: Specify whether wildcard and prefix queries - should be analyzed (default: false) - :arg analyzer: The analyzer to use for the query string - :arg default_operator: The default operator for query string query (AND - or OR), default 'OR', valid choices are: 'AND', 'OR' - :arg df: The field to use as default where no field prefix is given in - the query string - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg explain: Return detailed information about the error - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg lenient: Specify whether format-based query failures (such as - providing text to a numeric field) should be ignored - :arg lowercase_expanded_terms: Specify whether query terms should be - lowercased - :arg operation_threading: TODO: ? - :arg q: Query in the Lucene query string syntax - :arg rewrite: Provide a more detailed explanation showing the actual - Lucene query that will be executed. - """ - _, data = self.transport.perform_request('GET', _make_path(index, - doc_type, '_validate', 'query'), params=params, body=body) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'field_data', - 'fielddata', 'fields', 'ignore_unavailable', 'query', 'recycler', - 'request') - def clear_cache(self, index=None, params=None): - """ - Clear either all caches or specific cached associated with one ore more indices. - ``_ - - :arg index: A comma-separated list of index name to limit the operation - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg field_data: Clear field data - :arg fielddata: Clear field data - :arg fields: A comma-separated list of fields to clear when using the - `field_data` parameter (default: all) - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg query: Clear query caches - :arg recycler: Clear the recycler cache - :arg request: Clear request cache - """ - _, data = self.transport.perform_request('POST', _make_path(index, - '_cache', 'clear'), params=params) - return data - - @query_params('active_only', 'detailed', 'human') - def recovery(self, index=None, params=None): - """ - The indices recovery API provides insight into on-going shard - recoveries. Recovery status may be reported for specific indices, or - cluster-wide. - ``_ - - :arg index: A comma-separated list of index names; use `_all` or empty - string to perform the operation on all indices - :arg active_only: Display only those recoveries that are currently on- - going, default False - :arg detailed: Whether to display detailed information about shard - recovery, default False - :arg human: Whether to return time and byte values in human-readable - format., default False - """ - _, data = self.transport.perform_request('GET', _make_path(index, - '_recovery'), params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'only_ancient_segments', 'wait_for_completion') - def upgrade(self, index=None, params=None): - """ - Upgrade one or more indices to the latest format through an API. - ``_ - - :arg index: A comma-separated list of index names; use `_all` or empty - string to perform the operation on all indices - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg only_ancient_segments: If true, only ancient (an older Lucene major - release) segments will be upgraded - :arg wait_for_completion: Specify whether the request should block until - the all segments are upgraded (default: false) - """ - _, data = self.transport.perform_request('POST', _make_path(index, - '_upgrade'), params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'human', - 'ignore_unavailable') - def get_upgrade(self, index=None, params=None): - """ - Monitor how much of one or more index is upgraded. - ``_ - - :arg index: A comma-separated list of index names; use `_all` or empty - string to perform the operation on all indices - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg human: Whether to return time and byte values in human-readable - format., default False - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - """ - _, data = self.transport.perform_request('GET', _make_path(index, - '_upgrade'), params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable') - def flush_synced(self, index=None, params=None): - """ - Perform a normal flush, then add a generated unique marker (sync_id) to all shards. - ``_ - - :arg index: A comma-separated list of index names; use `_all` or empty - string for all indices - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - """ - _, data = self.transport.perform_request('POST', _make_path(index, - '_flush', 'synced'), params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable', - 'operation_threading', 'status') - def shard_stores(self, index=None, params=None): - """ - ``_ - - :arg index: A comma-separated list of index names; use `_all` or empty - string to perform the operation on all indices - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg operation_threading: TODO: ? - :arg status: A comma-separated list of statuses used to filter on shards - to get store information for, valid choices are: 'green', 'yellow', - 'red', 'all' - """ - _, data = self.transport.perform_request('GET', _make_path(index, - '_shard_stores'), params=params) - return data - - @query_params('allow_no_indices', 'expand_wildcards', 'flush', - 'ignore_unavailable', 'max_num_segments', 'only_expunge_deletes', - 'operation_threading', 'wait_for_merge') - def forcemerge(self, index=None, params=None): - """ - ``_ - - :arg index: A comma-separated list of index names; use `_all` or empty - string to perform the operation on all indices - :arg allow_no_indices: Whether to ignore if a wildcard indices - expression resolves into no concrete indices. (This includes `_all` - string or when no indices have been specified) - :arg expand_wildcards: Whether to expand wildcard expression to concrete - indices that are open, closed or both., default 'open', valid - choices are: 'open', 'closed', 'none', 'all' - :arg flush: Specify whether the index should be flushed after performing - the operation (default: true) - :arg ignore_unavailable: Whether specified concrete indices should be - ignored when unavailable (missing or closed) - :arg max_num_segments: The number of segments the index should be merged - into (default: dynamic) - :arg only_expunge_deletes: Specify whether the operation should only - expunge deleted documents - :arg operation_threading: TODO: ? - :arg wait_for_merge: Specify whether the request should block until the - merge process is finished (default: true) - """ - _, data = self.transport.perform_request('POST', _make_path(index, - '_forcemerge'), params=params) - return data diff --git a/venv/lib/python2.7/site-packages/elasticsearch/client/nodes.py b/venv/lib/python2.7/site-packages/elasticsearch/client/nodes.py deleted file mode 100644 index b81fcf6..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/client/nodes.py +++ /dev/null @@ -1,91 +0,0 @@ -from .utils import NamespacedClient, query_params, _make_path - -class NodesClient(NamespacedClient): - @query_params('flat_settings', 'human', 'timeout') - def info(self, node_id=None, metric=None, params=None): - """ - The cluster nodes info API allows to retrieve one or more (or all) of - the cluster nodes information. - ``_ - - :arg node_id: A comma-separated list of node IDs or names to limit the - returned information; use `_local` to return information from the - node you're connecting to, leave empty to get information from all - nodes - :arg metric: A comma-separated list of metrics you wish returned. Leave - empty to return all. - :arg flat_settings: Return settings in flat format (default: false) - :arg human: Whether to return time and byte values in human-readable - format., default False - :arg timeout: Explicit operation timeout - """ - _, data = self.transport.perform_request('GET', _make_path('_nodes', - node_id, metric), params=params) - return data - - @query_params('completion_fields', 'fielddata_fields', 'fields', 'groups', - 'human', 'level', 'timeout', 'types') - def stats(self, node_id=None, metric=None, index_metric=None, params=None): - """ - The cluster nodes stats API allows to retrieve one or more (or all) of - the cluster nodes statistics. - ``_ - - :arg node_id: A comma-separated list of node IDs or names to limit the - returned information; use `_local` to return information from the - node you're connecting to, leave empty to get information from all - nodes - :arg metric: Limit the information returned to the specified metrics - :arg index_metric: Limit the information returned for `indices` metric - to the specific index metrics. Isn't used if `indices` (or `all`) - metric isn't specified. - :arg completion_fields: A comma-separated list of fields for `fielddata` - and `suggest` index metric (supports wildcards) - :arg fielddata_fields: A comma-separated list of fields for `fielddata` - index metric (supports wildcards) - :arg fields: A comma-separated list of fields for `fielddata` and - `completion` index metric (supports wildcards) - :arg groups: A comma-separated list of search groups for `search` index - metric - :arg human: Whether to return time and byte values in human-readable - format., default False - :arg level: Return indices stats aggregated at node, index or shard - level, default 'node', valid choices are: 'node', 'indices', - 'shards' - :arg timeout: Explicit operation timeout - :arg types: A comma-separated list of document types for the `indexing` - index metric - """ - _, data = self.transport.perform_request('GET', _make_path('_nodes', - node_id, 'stats', metric, index_metric), params=params) - return data - - @query_params('doc_type', 'ignore_idle_threads', 'interval', 'snapshots', - 'threads', 'timeout') - def hot_threads(self, node_id=None, params=None): - """ - An API allowing to get the current hot threads on each node in the cluster. - ``_ - - :arg node_id: A comma-separated list of node IDs or names to limit the - returned information; use `_local` to return information from the - node you're connecting to, leave empty to get information from all - nodes - :arg doc_type: The type to sample (default: cpu), valid choices are: - 'cpu', 'wait', 'block' - :arg ignore_idle_threads: Don't show threads that are in known-idle - places, such as waiting on a socket select or pulling from an empty - task queue (default: true) - :arg interval: The interval for the second sampling of threads - :arg snapshots: Number of samples of thread stacktrace (default: 10) - :arg threads: Specify the number of threads to provide information for - (default: 3) - :arg timeout: Explicit operation timeout - """ - # avoid python reserved words - if params and 'type_' in params: - params['type'] = params.pop('type_') - _, data = self.transport.perform_request('GET', _make_path('_cluster', - 'nodes', node_id, 'hotthreads'), params=params) - return data - diff --git a/venv/lib/python2.7/site-packages/elasticsearch/client/snapshot.py b/venv/lib/python2.7/site-packages/elasticsearch/client/snapshot.py deleted file mode 100644 index 321580e..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/client/snapshot.py +++ /dev/null @@ -1,169 +0,0 @@ -from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH - -class SnapshotClient(NamespacedClient): - @query_params('master_timeout', 'wait_for_completion') - def create(self, repository, snapshot, body=None, params=None): - """ - Create a snapshot in repository - ``_ - - :arg repository: A repository name - :arg snapshot: A snapshot name - :arg body: The snapshot definition - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg wait_for_completion: Should this request wait until the operation - has completed before returning, default False - """ - for param in (repository, snapshot): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('PUT', _make_path('_snapshot', - repository, snapshot), params=params, body=body) - return data - - @query_params('master_timeout') - def delete(self, repository, snapshot, params=None): - """ - Deletes a snapshot from a repository. - ``_ - - :arg repository: A repository name - :arg snapshot: A snapshot name - :arg master_timeout: Explicit operation timeout for connection to master - node - """ - for param in (repository, snapshot): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('DELETE', - _make_path('_snapshot', repository, snapshot), params=params) - return data - - @query_params('master_timeout') - def get(self, repository, snapshot, params=None): - """ - Retrieve information about a snapshot. - ``_ - - :arg repository: A repository name - :arg snapshot: A comma-separated list of snapshot names - :arg master_timeout: Explicit operation timeout for connection to master - node - """ - for param in (repository, snapshot): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('GET', _make_path('_snapshot', - repository, snapshot), params=params) - return data - - @query_params('master_timeout', 'timeout') - def delete_repository(self, repository, params=None): - """ - Removes a shared file system repository. - ``_ - - :arg repository: A comma-separated list of repository names - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg timeout: Explicit operation timeout - """ - if repository in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'repository'.") - _, data = self.transport.perform_request('DELETE', - _make_path('_snapshot', repository), params=params) - return data - - @query_params('local', 'master_timeout') - def get_repository(self, repository=None, params=None): - """ - Return information about registered repositories. - ``_ - - :arg repository: A comma-separated list of repository names - :arg local: Return local information, do not retrieve the state from - master node (default: false) - :arg master_timeout: Explicit operation timeout for connection to master - node - """ - _, data = self.transport.perform_request('GET', _make_path('_snapshot', - repository), params=params) - return data - - @query_params('master_timeout', 'timeout', 'verify') - def create_repository(self, repository, body, params=None): - """ - Registers a shared file system repository. - ``_ - - :arg repository: A repository name - :arg body: The repository definition - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg timeout: Explicit operation timeout - :arg verify: Whether to verify the repository after creation - """ - for param in (repository, body): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('PUT', _make_path('_snapshot', - repository), params=params, body=body) - return data - - @query_params('master_timeout', 'wait_for_completion') - def restore(self, repository, snapshot, body=None, params=None): - """ - Restore a snapshot. - ``_ - - :arg repository: A repository name - :arg snapshot: A snapshot name - :arg body: Details of what to restore - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg wait_for_completion: Should this request wait until the operation - has completed before returning, default False - """ - for param in (repository, snapshot): - if param in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument.") - _, data = self.transport.perform_request('POST', _make_path('_snapshot', - repository, snapshot, '_restore'), params=params, body=body) - return data - - @query_params('master_timeout') - def status(self, repository=None, snapshot=None, params=None): - """ - Return information about all currently running snapshots. By specifying - a repository name, it's possible to limit the results to a particular - repository. - ``_ - - :arg repository: A repository name - :arg snapshot: A comma-separated list of snapshot names - :arg master_timeout: Explicit operation timeout for connection to master - node - """ - _, data = self.transport.perform_request('GET', _make_path('_snapshot', - repository, snapshot, '_status'), params=params) - return data - - @query_params('master_timeout', 'timeout') - def verify_repository(self, repository, params=None): - """ - Returns a list of nodes where repository was successfully verified or - an error message if verification process failed. - ``_ - - :arg repository: A repository name - :arg master_timeout: Explicit operation timeout for connection to master - node - :arg timeout: Explicit operation timeout - """ - if repository in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'repository'.") - _, data = self.transport.perform_request('POST', _make_path('_snapshot', - repository, '_verify'), params=params) - return data - diff --git a/venv/lib/python2.7/site-packages/elasticsearch/client/utils.py b/venv/lib/python2.7/site-packages/elasticsearch/client/utils.py deleted file mode 100644 index 7a015a6..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/client/utils.py +++ /dev/null @@ -1,87 +0,0 @@ -from __future__ import unicode_literals - -import weakref -from datetime import date, datetime -from functools import wraps -from ..compat import string_types, quote_plus - -# parts of URL to be omitted -SKIP_IN_PATH = (None, '', b'', [], ()) - -def _escape(value): - """ - Escape a single value of a URL string or a query parameter. If it is a list - or tuple, turn it into a comma-separated string first. - """ - - # make sequences into comma-separated stings - if isinstance(value, (list, tuple)): - value = ','.join(value) - - # dates and datetimes into isoformat - elif isinstance(value, (date, datetime)): - value = value.isoformat() - - # make bools into true/false strings - elif isinstance(value, bool): - value = str(value).lower() - - # encode strings to utf-8 - if isinstance(value, string_types): - try: - return value.encode('utf-8') - except UnicodeDecodeError: - # Python 2 and str, no need to re-encode - pass - - return str(value) - -def _make_path(*parts): - """ - Create a URL string from parts, omit all `None` values and empty strings. - Convert lists nad tuples to comma separated values. - """ - #TODO: maybe only allow some parts to be lists/tuples ? - return '/' + '/'.join( - # preserve ',' and '*' in url for nicer URLs in logs - quote_plus(_escape(p), b',*') for p in parts if p not in SKIP_IN_PATH) - -# parameters that apply to all methods -GLOBAL_PARAMS = ('pretty', 'format', 'filter_path') - -def query_params(*es_query_params): - """ - Decorator that pops all accepted parameters from method's kwargs and puts - them in the params argument. - """ - def _wrapper(func): - @wraps(func) - def _wrapped(*args, **kwargs): - params = kwargs.pop('params', {}) - for p in es_query_params + GLOBAL_PARAMS: - if p in kwargs: - params[p] = _escape(kwargs.pop(p)) - - # don't treat ignore and request_timeout as other params to avoid escaping - for p in ('ignore', 'request_timeout'): - if p in kwargs: - params[p] = kwargs.pop(p) - return func(*args, params=params, **kwargs) - return _wrapped - return _wrapper - - -class NamespacedClient(object): - def __init__(self, client): - self.client = client - - @property - def transport(self): - return self.client.transport - -class AddonClient(NamespacedClient): - @classmethod - def infect_client(cls, client): - addon = cls(weakref.proxy(client)) - setattr(client, cls.namespace, addon) - return client diff --git a/venv/lib/python2.7/site-packages/elasticsearch/compat.py b/venv/lib/python2.7/site-packages/elasticsearch/compat.py deleted file mode 100644 index deee3c5..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/compat.py +++ /dev/null @@ -1,13 +0,0 @@ -import sys - -PY2 = sys.version_info[0] == 2 - -if PY2: - string_types = basestring, - from urllib import quote_plus, urlencode - from urlparse import urlparse - from itertools import imap as map -else: - string_types = str, bytes - from urllib.parse import quote_plus, urlencode, urlparse - map = map diff --git a/venv/lib/python2.7/site-packages/elasticsearch/connection/__init__.py b/venv/lib/python2.7/site-packages/elasticsearch/connection/__init__.py deleted file mode 100644 index c2f484a..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/connection/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .base import Connection -from .http_requests import RequestsHttpConnection -from .http_urllib3 import Urllib3HttpConnection diff --git a/venv/lib/python2.7/site-packages/elasticsearch/connection/base.py b/venv/lib/python2.7/site-packages/elasticsearch/connection/base.py deleted file mode 100644 index 914967c..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/connection/base.py +++ /dev/null @@ -1,110 +0,0 @@ -import logging -try: - import simplejson as json -except ImportError: - import json - -from ..exceptions import TransportError, HTTP_EXCEPTIONS - -logger = logging.getLogger('elasticsearch') - -# create the elasticsearch.trace logger, but only set propagate to False if the -# logger hasn't already been configured -_tracer_already_configured = 'elasticsearch.trace' in logging.Logger.manager.loggerDict -tracer = logging.getLogger('elasticsearch.trace') -if not _tracer_already_configured: - tracer.propagate = False - - -class Connection(object): - """ - Class responsible for maintaining a connection to an Elasticsearch node. It - holds persistent connection pool to it and it's main interface - (`perform_request`) is thread-safe. - - Also responsible for logging. - """ - transport_schema = 'http' - - def __init__(self, host='localhost', port=9200, url_prefix='', timeout=10, **kwargs): - """ - :arg host: hostname of the node (default: localhost) - :arg port: port to use (integer, default: 9200) - :arg url_prefix: optional url prefix for elasticsearch - :arg timeout: default timeout in seconds (float, default: 10) - """ - self.host = '%s://%s:%s' % (self.transport_schema, host, port) - if url_prefix: - url_prefix = '/' + url_prefix.strip('/') - self.url_prefix = url_prefix - self.timeout = timeout - - def __repr__(self): - return '<%s: %s>' % (self.__class__.__name__, self.host) - - def log_request_success(self, method, full_url, path, body, status_code, response, duration): - """ Log a successful API call. """ - # TODO: optionally pass in params instead of full_url and do urlencode only when needed - def _pretty_json(data): - # pretty JSON in tracer curl logs - try: - return json.dumps(json.loads(data), sort_keys=True, indent=2, separators=(',', ': ')).replace("'", r'\u0027') - except (ValueError, TypeError): - # non-json data or a bulk request - return data - - # body has already been serialized to utf-8, deserialize it for logging - # TODO: find a better way to avoid (de)encoding the body back and forth - if body: - body = body.decode('utf-8') - - logger.info( - '%s %s [status:%s request:%.3fs]', method, full_url, - status_code, duration - ) - logger.debug('> %s', body) - logger.debug('< %s', response) - - if tracer.isEnabledFor(logging.INFO): - # include pretty in trace curls - path = path.replace('?', '?pretty&', 1) if '?' in path else path + '?pretty' - if self.url_prefix: - path = path.replace(self.url_prefix, '', 1) - tracer.info("curl -X%s 'http://localhost:9200%s' -d '%s'", method, path, _pretty_json(body) if body else '') - - if tracer.isEnabledFor(logging.DEBUG): - tracer.debug('#[%s] (%.3fs)\n#%s', status_code, duration, _pretty_json(response).replace('\n', '\n#') if response else '') - - def log_request_fail(self, method, full_url, body, duration, status_code=None, response=None, exception=None): - """ Log an unsuccessful API call. """ - logger.warning( - '%s %s [status:%s request:%.3fs]', method, full_url, - status_code or 'N/A', duration, exc_info=exception is not None - ) - - # body has already been serialized to utf-8, deserialize it for logging - # TODO: find a better way to avoid (de)encoding the body back and forth - if body: - body = body.decode('utf-8') - - logger.debug('> %s', body) - - if response is not None: - logger.debug('< %s', response) - - def _raise_error(self, status_code, raw_data): - """ Locate appropriate exception and raise it. """ - error_message = raw_data - additional_info = None - try: - additional_info = json.loads(raw_data) - error_message = additional_info.get('error', error_message) - if isinstance(error_message, dict) and 'type' in error_message: - error_message = error_message['type'] - except: - # we don't care what went wrong - pass - - raise HTTP_EXCEPTIONS.get(status_code, TransportError)(status_code, error_message, additional_info) - - diff --git a/venv/lib/python2.7/site-packages/elasticsearch/connection/http_requests.py b/venv/lib/python2.7/site-packages/elasticsearch/connection/http_requests.py deleted file mode 100644 index b97a94c..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/connection/http_requests.py +++ /dev/null @@ -1,88 +0,0 @@ -import time -import warnings -try: - import requests - REQUESTS_AVAILABLE = True -except ImportError: - REQUESTS_AVAILABLE = False - -from .base import Connection -from ..exceptions import ConnectionError, ImproperlyConfigured, ConnectionTimeout, SSLError -from ..compat import urlencode, string_types - -class RequestsHttpConnection(Connection): - """ - Connection using the `requests` library. - - :arg http_auth: optional http auth information as either ':' separated - string or a tuple. Any value will be passed into requests as `auth`. - :arg use_ssl: use ssl for the connection if `True` - :arg verify_certs: whether to verify SSL certificates - :arg ca_certs: optional path to CA bundle. By default standard requests' - bundle will be used. - :arg client_cert: path to the file containing the private key and the - certificate, or cert only if using client_key - :arg client_key: path to the file containing the private key if using - separate cert and key files (client_cert will contain only the cert) - """ - def __init__(self, host='localhost', port=9200, http_auth=None, - use_ssl=False, verify_certs=False, ca_certs=None, client_cert=None, - client_key=None, **kwargs): - if not REQUESTS_AVAILABLE: - raise ImproperlyConfigured("Please install requests to use RequestsHttpConnection.") - - super(RequestsHttpConnection, self).__init__(host= host, port=port, **kwargs) - self.session = requests.session() - if http_auth is not None: - if isinstance(http_auth, (tuple, list)): - http_auth = tuple(http_auth) - elif isinstance(http_auth, string_types): - http_auth = tuple(http_auth.split(':', 1)) - self.session.auth = http_auth - self.base_url = 'http%s://%s:%d%s' % ( - 's' if use_ssl else '', - host, port, self.url_prefix - ) - self.session.verify = verify_certs - if not client_key: - self.session.cert = client_cert - elif client_cert: - # cert is a tuple of (certfile, keyfile) - self.session.cert = (client_cert, client_key) - if ca_certs: - if not verify_certs: - raise ImproperlyConfigured("You cannot pass CA certificates when verify SSL is off.") - self.session.verify = ca_certs - - if use_ssl and not verify_certs: - warnings.warn( - 'Connecting to %s using SSL with verify_certs=False is insecure.' % self.base_url) - - def perform_request(self, method, url, params=None, body=None, timeout=None, ignore=()): - url = self.base_url + url - if params: - url = '%s?%s' % (url, urlencode(params or {})) - - start = time.time() - try: - response = self.session.request(method, url, data=body, timeout=timeout or self.timeout) - duration = time.time() - start - raw_data = response.text - except requests.exceptions.SSLError as e: - self.log_request_fail(method, url, body, time.time() - start, exception=e) - raise SSLError('N/A', str(e), e) - except requests.Timeout as e: - self.log_request_fail(method, url, body, time.time() - start, exception=e) - raise ConnectionTimeout('TIMEOUT', str(e), e) - except requests.ConnectionError as e: - self.log_request_fail(method, url, body, time.time() - start, exception=e) - raise ConnectionError('N/A', str(e), e) - - # raise errors based on http status codes, let the client handle those if needed - if not (200 <= response.status_code < 300) and response.status_code not in ignore: - self.log_request_fail(method, url, body, duration, response.status_code, raw_data) - self._raise_error(response.status_code, raw_data) - - self.log_request_success(method, url, response.request.path_url, body, response.status_code, raw_data, duration) - - return response.status_code, response.headers, raw_data diff --git a/venv/lib/python2.7/site-packages/elasticsearch/connection/http_urllib3.py b/venv/lib/python2.7/site-packages/elasticsearch/connection/http_urllib3.py deleted file mode 100644 index d0f213f..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/connection/http_urllib3.py +++ /dev/null @@ -1,115 +0,0 @@ -import time -import urllib3 -from urllib3.exceptions import ReadTimeoutError, SSLError as UrllibSSLError -import warnings - -from .base import Connection -from ..exceptions import ConnectionError, ImproperlyConfigured, ConnectionTimeout, SSLError -from ..compat import urlencode - -class Urllib3HttpConnection(Connection): - """ - Default connection class using the `urllib3` library and the http protocol. - - :arg host: hostname of the node (default: localhost) - :arg port: port to use (integer, default: 9200) - :arg url_prefix: optional url prefix for elasticsearch - :arg timeout: default timeout in seconds (float, default: 10) - :arg http_auth: optional http auth information as either ':' separated - string or a tuple - :arg use_ssl: use ssl for the connection if `True` - :arg verify_certs: whether to verify SSL certificates - :arg ca_certs: optional path to CA bundle. See - http://urllib3.readthedocs.org/en/latest/security.html#using-certifi-with-urllib3 - for instructions how to get default set - :arg client_cert: path to the file containing the private key and the - certificate, or cert only if using client_key - :arg client_key: path to the file containing the private key if using - separate cert and key files (client_cert will contain only the cert) - :arg ssl_version: version of the SSL protocol to use. Choices are: - SSLv23 (default) SSLv2 SSLv3 TLSv1 (see ``PROTOCOL_*`` constants in the - ``ssl`` module for exact options for your environment). - :arg ssl_assert_hostname: use hostname verification if not `False` - :arg ssl_assert_fingerprint: verify the supplied certificate fingerprint if not `None` - :arg maxsize: the maximum number of connections which will be kept open to - this host. - """ - def __init__(self, host='localhost', port=9200, http_auth=None, - use_ssl=False, verify_certs=False, ca_certs=None, client_cert=None, - client_key=None, ssl_version=None, ssl_assert_hostname=None, - ssl_assert_fingerprint=None, maxsize=10, **kwargs): - - super(Urllib3HttpConnection, self).__init__(host=host, port=port, **kwargs) - self.headers = urllib3.make_headers(keep_alive=True) - if http_auth is not None: - if isinstance(http_auth, (tuple, list)): - http_auth = ':'.join(http_auth) - self.headers.update(urllib3.make_headers(basic_auth=http_auth)) - - pool_class = urllib3.HTTPConnectionPool - kw = {} - if use_ssl: - pool_class = urllib3.HTTPSConnectionPool - kw.update({ - 'ssl_version': ssl_version, - 'assert_hostname': ssl_assert_hostname, - 'assert_fingerprint': ssl_assert_fingerprint, - }) - - if verify_certs: - kw.update({ - 'cert_reqs': 'CERT_REQUIRED', - 'ca_certs': ca_certs, - 'cert_file': client_cert, - 'key_file': client_key, - }) - elif ca_certs: - raise ImproperlyConfigured("You cannot pass CA certificates when verify SSL is off.") - else: - warnings.warn( - 'Connecting to %s using SSL with verify_certs=False is insecure.' % host) - - self.pool = pool_class(host, port=port, timeout=self.timeout, maxsize=maxsize, **kw) - - def perform_request(self, method, url, params=None, body=None, timeout=None, ignore=()): - url = self.url_prefix + url - if params: - url = '%s?%s' % (url, urlencode(params)) - full_url = self.host + url - - start = time.time() - try: - kw = {} - if timeout: - kw['timeout'] = timeout - - # in python2 we need to make sure the url and method are not - # unicode. Otherwise the body will be decoded into unicode too and - # that will fail (#133, #201). - if not isinstance(url, str): - url = url.encode('utf-8') - if not isinstance(method, str): - method = method.encode('utf-8') - - response = self.pool.urlopen(method, url, body, retries=False, headers=self.headers, **kw) - duration = time.time() - start - raw_data = response.data.decode('utf-8') - except UrllibSSLError as e: - self.log_request_fail(method, full_url, body, time.time() - start, exception=e) - raise SSLError('N/A', str(e), e) - except ReadTimeoutError as e: - self.log_request_fail(method, full_url, body, time.time() - start, exception=e) - raise ConnectionTimeout('TIMEOUT', str(e), e) - except Exception as e: - self.log_request_fail(method, full_url, body, time.time() - start, exception=e) - raise ConnectionError('N/A', str(e), e) - - if not (200 <= response.status < 300) and response.status not in ignore: - self.log_request_fail(method, url, body, duration, response.status, raw_data) - self._raise_error(response.status, raw_data) - - self.log_request_success(method, full_url, url, body, response.status, - raw_data, duration) - - return response.status, response.getheaders(), raw_data - diff --git a/venv/lib/python2.7/site-packages/elasticsearch/connection/pooling.py b/venv/lib/python2.7/site-packages/elasticsearch/connection/pooling.py deleted file mode 100644 index a772120..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/connection/pooling.py +++ /dev/null @@ -1,26 +0,0 @@ -try: - import queue -except ImportError: - import Queue as queue -from .base import Connection - - -class PoolingConnection(Connection): - """ - Base connection class for connections that use libraries without thread - safety and no capacity for connection pooling. To use this just implement a - ``_make_connection`` method that constructs a new connection and returns - it. - """ - def __init__(self, *args, **kwargs): - self._free_connections = queue.Queue() - super(PoolingConnection, self).__init__(*args, **kwargs) - - def _get_connection(self): - try: - return self._free_connections.get_nowait() - except queue.Empty: - return self._make_connection() - - def _release_connection(self, con): - self._free_connections.put(con) diff --git a/venv/lib/python2.7/site-packages/elasticsearch/connection_pool.py b/venv/lib/python2.7/site-packages/elasticsearch/connection_pool.py deleted file mode 100644 index 346a884..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/connection_pool.py +++ /dev/null @@ -1,248 +0,0 @@ -import time -import random -import logging - -try: - from Queue import PriorityQueue, Empty -except ImportError: - from queue import PriorityQueue, Empty - -from .exceptions import ImproperlyConfigured - -logger = logging.getLogger('elasticsearch') - -class ConnectionSelector(object): - """ - Simple class used to select a connection from a list of currently live - connection instances. In init time it is passed a dictionary containing all - the connections' options which it can then use during the selection - process. When the `select` method is called it is given a list of - *currently* live connections to choose from. - - The options dictionary is the one that has been passed to - :class:`~elasticsearch.Transport` as `hosts` param and the same that is - used to construct the Connection object itself. When the Connection was - created from information retrieved from the cluster via the sniffing - process it will be the dictionary returned by the `host_info_callback`. - - Example of where this would be useful is a zone-aware selector that would - only select connections from it's own zones and only fall back to other - connections where there would be none in it's zones. - """ - def __init__(self, opts): - """ - :arg opts: dictionary of connection instances and their options - """ - self.connection_opts = opts - - def select(self, connections): - """ - Select a connection from the given list. - - :arg connections: list of live connections to choose from - """ - pass - - -class RandomSelector(ConnectionSelector): - """ - Select a connection at random - """ - def select(self, connections): - return random.choice(connections) - - -class RoundRobinSelector(ConnectionSelector): - """ - Selector using round-robin. - """ - def __init__(self, opts): - super(RoundRobinSelector, self).__init__(opts) - self.rr = -1 - - def select(self, connections): - self.rr += 1 - self.rr %= len(connections) - return connections[self.rr] - -class ConnectionPool(object): - """ - Container holding the :class:`~elasticsearch.Connection` instances, - managing the selection process (via a - :class:`~elasticsearch.ConnectionSelector`) and dead connections. - - It's only interactions are with the :class:`~elasticsearch.Transport` class - that drives all the actions within `ConnectionPool`. - - Initially connections are stored on the class as a list and, along with the - connection options, get passed to the `ConnectionSelector` instance for - future reference. - - Upon each request the `Transport` will ask for a `Connection` via the - `get_connection` method. If the connection fails (it's `perform_request` - raises a `ConnectionError`) it will be marked as dead (via `mark_dead`) and - put on a timeout (if it fails N times in a row the timeout is exponentially - longer - the formula is `default_timeout * 2 ** (fail_count - 1)`). When - the timeout is over the connection will be resurrected and returned to the - live pool. A connection that has been peviously marked as dead and - succeedes will be marked as live (it's fail count will be deleted). - """ - def __init__(self, connections, dead_timeout=60, timeout_cutoff=5, - selector_class=RoundRobinSelector, randomize_hosts=True, **kwargs): - """ - :arg connections: list of tuples containing the - :class:`~elasticsearch.Connection` instance and it's options - :arg dead_timeout: number of seconds a connection should be retired for - after a failure, increases on consecutive failures - :arg timeout_cutoff: number of consecutive failures after which the - timeout doesn't increase - :arg selector_class: :class:`~elasticsearch.ConnectionSelector` - subclass to use if more than one connection is live - :arg randomize_hosts: shuffle the list of connections upon arrival to - avoid dog piling effect across processes - """ - if not connections: - raise ImproperlyConfigured("No defined connections, you need to " - "specify at least one host.") - self.connection_opts = connections - self.connections = [c for (c, opts) in connections] - # remember original connection list for resurrect(force=True) - self.orig_connections = tuple(self.connections) - # PriorityQueue for thread safety and ease of timeout management - self.dead = PriorityQueue(len(self.connections)) - self.dead_count = {} - - if randomize_hosts: - # randomize the connection list to avoid all clients hitting same node - # after startup/restart - random.shuffle(self.connections) - - # default timeout after which to try resurrecting a connection - self.dead_timeout = dead_timeout - self.timeout_cutoff = timeout_cutoff - - self.selector = selector_class(dict(connections)) - - def mark_dead(self, connection, now=None): - """ - Mark the connection as dead (failed). Remove it from the live pool and - put it on a timeout. - - :arg connection: the failed instance - """ - # allow inject for testing purposes - now = now if now else time.time() - try: - self.connections.remove(connection) - except ValueError: - # connection not alive or another thread marked it already, ignore - return - else: - dead_count = self.dead_count.get(connection, 0) + 1 - self.dead_count[connection] = dead_count - timeout = self.dead_timeout * 2 ** min(dead_count - 1, self.timeout_cutoff) - self.dead.put((now + timeout, connection)) - logger.warning( - 'Connection %r has failed for %i times in a row, putting on %i second timeout.', - connection, dead_count, timeout - ) - - def mark_live(self, connection): - """ - Mark connection as healthy after a resurrection. Resets the fail - counter for the connection. - - :arg connection: the connection to redeem - """ - try: - del self.dead_count[connection] - except KeyError: - # race condition, safe to ignore - pass - - def resurrect(self, force=False): - """ - Attempt to resurrect a connection from the dead pool. It will try to - locate one (not all) eligible (it's timeout is over) connection to - return to the live pool. Any resurrected connection is also returned. - - :arg force: resurrect a connection even if there is none eligible (used - when we have no live connections). If force is specified resurrect - always returns a connection. - - """ - # no dead connections - if self.dead.empty(): - # we are forced to return a connection, take one from the original - # list. This is to avoid a race condition where get_connection can - # see no live connections but when it calls resurrect self.dead is - # also empty. We assume that other threat has resurrected all - # available connections so we can safely return one at random. - if force: - return random.choice(self.orig_connections) - return - - try: - # retrieve a connection to check - timeout, connection = self.dead.get(block=False) - except Empty: - # other thread has been faster and the queue is now empty. If we - # are forced, return a connection at random again. - if force: - return random.choice(self.orig_connections) - return - - if not force and timeout > time.time(): - # return it back if not eligible and not forced - self.dead.put((timeout, connection)) - return - - # either we were forced or the connection is elligible to be retried - self.connections.append(connection) - logger.info('Resurrecting connection %r (force=%s).', connection, force) - return connection - - def get_connection(self): - """ - Return a connection from the pool using the `ConnectionSelector` - instance. - - It tries to resurrect eligible connections, forces a resurrection when - no connections are availible and passes the list of live connections to - the selector instance to choose from. - - Returns a connection instance and it's current fail count. - """ - self.resurrect() - connections = self.connections[:] - - # no live nodes, resurrect one by force and return it - if not connections: - return self.resurrect(True) - - # only call selector if we have a selection - if len(connections) > 1: - return self.selector.select(self.connections) - - # only one connection, no need for a selector - return connections[0] - - -class DummyConnectionPool(ConnectionPool): - def __init__(self, connections, **kwargs): - if len(connections) != 1: - raise ImproperlyConfigured("DummyConnectionPool needs exactly one " - "connection defined.") - # we need connection opts for sniffing logic - self.connection_opts = connections - self.connection = connections[0][0] - self.connections = (self.connection, ) - - def get_connection(self): - return self.connection - - def _noop(self, *args, **kwargs): - pass - mark_dead = mark_live = resurrect = _noop - - diff --git a/venv/lib/python2.7/site-packages/elasticsearch/exceptions.py b/venv/lib/python2.7/site-packages/elasticsearch/exceptions.py deleted file mode 100644 index 76978a8..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/exceptions.py +++ /dev/null @@ -1,109 +0,0 @@ -__all__ = [ - 'ImproperlyConfigured', 'ElasticsearchException', 'SerializationError', - 'TransportError', 'NotFoundError', 'ConflictError', 'RequestError', 'ConnectionError', - 'SSLError', 'ConnectionTimeout' -] - -class ImproperlyConfigured(Exception): - """ - Exception raised when the config passed to the client is inconsistent or invalid. - """ - - -class ElasticsearchException(Exception): - """ - Base class for all exceptions raised by this package's operations (doesn't - apply to :class:`~elasticsearch.ImproperlyConfigured`). - """ - - -class SerializationError(ElasticsearchException): - """ - Data passed in failed to serialize properly in the ``Serializer`` being - used. - """ - - -class TransportError(ElasticsearchException): - """ - Exception raised when ES returns a non-OK (>=400) HTTP status code. Or when - an actual connection error happens; in that case the ``status_code`` will - be set to ``'N/A'``. - """ - @property - def status_code(self): - """ - The HTTP status code of the response that precipitated the error or - ``'N/A'`` if not applicable. - """ - return self.args[0] - - @property - def error(self): - """ A string error message. """ - return self.args[1] - - @property - def info(self): - """ Dict of returned error info from ES, where available. """ - return self.args[2] - - def __str__(self): - cause = '' - try: - if self.info: - cause = ', %r' % self.info['error']['root_cause'][0]['reason'] - except LookupError: - pass - return 'TransportError(%s, %r%s)' % (self.status_code, self.error, cause) - - -class ConnectionError(TransportError): - """ - Error raised when there was an exception while talking to ES. Original - exception from the underlying :class:`~elasticsearch.Connection` - implementation is available as ``.info.`` - """ - def __str__(self): - return 'ConnectionError(%s) caused by: %s(%s)' % ( - self.error, self.info.__class__.__name__, self.info) - - -class SSLError(ConnectionError): - """ Error raised when encountering SSL errors. """ - - -class ConnectionTimeout(ConnectionError): - """ A network timeout. Doesn't cause a node retry by default. """ - def __str__(self): - return 'ConnectionTimeout caused by - %s(%s)' % ( - self.info.__class__.__name__, self.info) - - -class NotFoundError(TransportError): - """ Exception representing a 404 status code. """ - - -class ConflictError(TransportError): - """ Exception representing a 409 status code. """ - - -class RequestError(TransportError): - """ Exception representing a 400 status code. """ - - -class AuthenticationException(TransportError): - """ Exception representing a 401 status code. """ - - -class AuthorizationException(TransportError): - """ Exception representing a 403 status code. """ - -# more generic mappings from status_code to python exceptions -HTTP_EXCEPTIONS = { - 400: RequestError, - 401: AuthenticationException, - 403: AuthorizationException, - 404: NotFoundError, - 409: ConflictError, -} diff --git a/venv/lib/python2.7/site-packages/elasticsearch/helpers/__init__.py b/venv/lib/python2.7/site-packages/elasticsearch/helpers/__init__.py deleted file mode 100644 index 2f98ba7..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/helpers/__init__.py +++ /dev/null @@ -1,355 +0,0 @@ -from __future__ import unicode_literals - -import logging -from operator import methodcaller - -from ..exceptions import ElasticsearchException, TransportError -from ..compat import map, string_types - -logger = logging.getLogger('elasticsearch.helpers') - -class BulkIndexError(ElasticsearchException): - @property - def errors(self): - """ List of errors from execution of the last chunk. """ - return self.args[1] - - -class ScanError(ElasticsearchException): - pass - -def expand_action(data): - """ - From one document or action definition passed in by the user extract the - action/data lines needed for elasticsearch's - :meth:`~elasticsearch.Elasticsearch.bulk` api. - """ - # when given a string, assume user wants to index raw json - if isinstance(data, string_types): - return '{"index":{}}', data - - # make sure we don't alter the action - data = data.copy() - op_type = data.pop('_op_type', 'index') - action = {op_type: {}} - for key in ('_index', '_parent', '_percolate', '_routing', '_timestamp', - '_ttl', '_type', '_version', '_version_type', '_id', '_retry_on_conflict'): - if key in data: - action[op_type][key] = data.pop(key) - - # no data payload for delete - if op_type == 'delete': - return action, None - - return action, data.get('_source', data) - -def _chunk_actions(actions, chunk_size, max_chunk_bytes, serializer): - """ - Split actions into chunks by number or size, serialize them into strings in - the process. - """ - bulk_actions = [] - size, action_count = 0, 0 - for action, data in actions: - action = serializer.dumps(action) - cur_size = len(action) + 1 - - if data is not None: - data = serializer.dumps(data) - cur_size += len(data) + 1 - - # full chunk, send it and start a new one - if bulk_actions and (size + cur_size > max_chunk_bytes or action_count == chunk_size): - yield bulk_actions - bulk_actions = [] - size, action_count = 0, 0 - - bulk_actions.append(action) - if data is not None: - bulk_actions.append(data) - size += cur_size - action_count += 1 - - if bulk_actions: - yield bulk_actions - -def _process_bulk_chunk(client, bulk_actions, raise_on_exception=True, raise_on_error=True, **kwargs): - """ - Send a bulk request to elasticsearch and process the output. - """ - # if raise on error is set, we need to collect errors per chunk before raising them - errors = [] - - try: - # send the actual request - resp = client.bulk('\n'.join(bulk_actions) + '\n', **kwargs) - except TransportError as e: - # default behavior - just propagate exception - if raise_on_exception: - raise e - - # if we are not propagating, mark all actions in current chunk as failed - err_message = str(e) - exc_errors = [] - - # deserialize the data back, thisis expensive but only run on - # errors if raise_on_exception is false, so shouldn't be a real - # issue - bulk_data = map(client.transport.serializer.loads, bulk_actions) - while True: - try: - # collect all the information about failed actions - action = next(bulk_data) - op_type, action = action.popitem() - info = {"error": err_message, "status": e.status_code, "exception": e} - if op_type != 'delete': - info['data'] = next(bulk_data) - info.update(action) - exc_errors.append({op_type: info}) - except StopIteration: - break - - # emulate standard behavior for failed actions - if raise_on_error: - raise BulkIndexError('%i document(s) failed to index.' % len(exc_errors), exc_errors) - else: - for err in exc_errors: - yield False, err - return - - # go through request-reponse pairs and detect failures - for op_type, item in map(methodcaller('popitem'), resp['items']): - ok = 200 <= item.get('status', 500) < 300 - if not ok and raise_on_error: - errors.append({op_type: item}) - - if ok or not errors: - # if we are not just recording all errors to be able to raise - # them all at once, yield items individually - yield ok, {op_type: item} - - if errors: - raise BulkIndexError('%i document(s) failed to index.' % len(errors), errors) - -def streaming_bulk(client, actions, chunk_size=500, max_chunk_bytes=100 * 1024 * 1024, - raise_on_error=True, expand_action_callback=expand_action, - raise_on_exception=True, **kwargs): - """ - Streaming bulk consumes actions from the iterable passed in and yields - results per action. For non-streaming usecases use - :func:`~elasticsearch.helpers.bulk` which is a wrapper around streaming - bulk that returns summary information about the bulk operation once the - entire input is consumed and sent. - - - :arg client: instance of :class:`~elasticsearch.Elasticsearch` to use - :arg actions: iterable containing the actions to be executed - :arg chunk_size: number of docs in one chunk sent to es (default: 500) - :arg max_chunk_bytes: the maximum size of the request in bytes (default: 100MB) - :arg raise_on_error: raise ``BulkIndexError`` containing errors (as `.errors`) - from the execution of the last chunk when some occur. By default we raise. - :arg raise_on_exception: if ``False`` then don't propagate exceptions from - call to ``bulk`` and just report the items that failed as failed. - :arg expand_action_callback: callback executed on each action passed in, - should return a tuple containing the action line and the data line - (`None` if data line should be omitted). - """ - actions = map(expand_action_callback, actions) - - for bulk_actions in _chunk_actions(actions, chunk_size, max_chunk_bytes, client.transport.serializer): - for result in _process_bulk_chunk(client, bulk_actions, raise_on_exception, raise_on_error, **kwargs): - yield result - -def bulk(client, actions, stats_only=False, **kwargs): - """ - Helper for the :meth:`~elasticsearch.Elasticsearch.bulk` api that provides - a more human friendly interface - it consumes an iterator of actions and - sends them to elasticsearch in chunks. It returns a tuple with summary - information - number of successfully executed actions and either list of - errors or number of errors if `stats_only` is set to `True`. - - See :func:`~elasticsearch.helpers.streaming_bulk` for more accepted - parameters - - :arg client: instance of :class:`~elasticsearch.Elasticsearch` to use - :arg actions: iterator containing the actions - :arg stats_only: if `True` only report number of successful/failed - operations instead of just number of successful and a list of error responses - - Any additional keyword arguments will be passed to - :func:`~elasticsearch.helpers.streaming_bulk` which is used to execute - the operation. - """ - success, failed = 0, 0 - - # list of errors to be collected is not stats_only - errors = [] - - for ok, item in streaming_bulk(client, actions, **kwargs): - # go through request-reponse pairs and detect failures - if not ok: - if not stats_only: - errors.append(item) - failed += 1 - else: - success += 1 - - return success, failed if stats_only else errors - -def parallel_bulk(client, actions, thread_count=4, chunk_size=500, - max_chunk_bytes=100 * 1024 * 1024, - expand_action_callback=expand_action, **kwargs): - """ - Parallel version of the bulk helper run in multiple threads at once. - - :arg client: instance of :class:`~elasticsearch.Elasticsearch` to use - :arg actions: iterator containing the actions - :arg thread_count: size of the threadpool to use for the bulk requests - :arg chunk_size: number of docs in one chunk sent to es (default: 500) - :arg max_chunk_bytes: the maximum size of the request in bytes (default: 100MB) - :arg raise_on_error: raise ``BulkIndexError`` containing errors (as `.errors`) - from the execution of the last chunk when some occur. By default we raise. - :arg raise_on_exception: if ``False`` then don't propagate exceptions from - call to ``bulk`` and just report the items that failed as failed. - :arg expand_action_callback: callback executed on each action passed in, - should return a tuple containing the action line and the data line - (`None` if data line should be omitted). - """ - # Avoid importing multiprocessing unless parallel_bulk is used - # to avoid exceptions on restricted environments like App Engine - from multiprocessing.dummy import Pool - actions = map(expand_action_callback, actions) - - pool = Pool(thread_count) - - try: - for result in pool.imap( - lambda chunk: list(_process_bulk_chunk(client, chunk, **kwargs)), - _chunk_actions(actions, chunk_size, max_chunk_bytes, client.transport.serializer) - ): - for item in result: - yield item - - finally: - pool.close() - pool.join() - -def scan(client, query=None, scroll='5m', raise_on_error=True, preserve_order=False, **kwargs): - """ - Simple abstraction on top of the - :meth:`~elasticsearch.Elasticsearch.scroll` api - a simple iterator that - yields all hits as returned by underlining scroll requests. - - By default scan does not return results in any pre-determined order. To - have a standard order in the returned documents (either by score or - explicit sort definition) when scrolling, use ``preserve_order=True``. This - may be an expensive operation and will negate the performance benefits of - using ``scan``. - - :arg client: instance of :class:`~elasticsearch.Elasticsearch` to use - :arg query: body for the :meth:`~elasticsearch.Elasticsearch.search` api - :arg scroll: Specify how long a consistent view of the index should be - maintained for scrolled search - :arg raise_on_error: raises an exception (``ScanError``) if an error is - encountered (some shards fail to execute). By default we raise. - :arg preserve_order: don't set the ``search_type`` to ``scan`` - this will - cause the scroll to paginate with preserving the order. Note that this - can be an extremely expensive operation and can easily lead to - unpredictable results, use with caution. - - Any additional keyword arguments will be passed to the initial - :meth:`~elasticsearch.Elasticsearch.search` call:: - - scan(es, - query={"query": {"match": {"title": "python"}}}, - index="orders-*", - doc_type="books" - ) - - """ - if not preserve_order: - kwargs['search_type'] = 'scan' - # initial search - resp = client.search(body=query, scroll=scroll, **kwargs) - - scroll_id = resp.get('_scroll_id') - if scroll_id is None: - return - - first_run = True - while True: - # if we didn't set search_type to scan initial search contains data - if preserve_order and first_run: - first_run = False - else: - resp = client.scroll(scroll_id, scroll=scroll) - - for hit in resp['hits']['hits']: - yield hit - - # check if we have any errrors - if resp["_shards"]["failed"]: - logger.warning( - 'Scroll request has failed on %d shards out of %d.', - resp['_shards']['failed'], resp['_shards']['total'] - ) - if raise_on_error: - raise ScanError( - 'Scroll request has failed on %d shards out of %d.' % - (resp['_shards']['failed'], resp['_shards']['total']) - ) - - scroll_id = resp.get('_scroll_id') - # end of scroll - if scroll_id is None or not resp['hits']['hits']: - break - -def reindex(client, source_index, target_index, query=None, target_client=None, - chunk_size=500, scroll='5m', scan_kwargs={}, bulk_kwargs={}): - - """ - Reindex all documents from one index that satisfy a given query - to another, potentially (if `target_client` is specified) on a different cluster. - If you don't specify the query you will reindex all the documents. - - .. note:: - - This helper doesn't transfer mappings, just the data. - - :arg client: instance of :class:`~elasticsearch.Elasticsearch` to use (for - read if `target_client` is specified as well) - :arg source_index: index (or list of indices) to read documents from - :arg target_index: name of the index in the target cluster to populate - :arg query: body for the :meth:`~elasticsearch.Elasticsearch.search` api - :arg target_client: optional, is specified will be used for writing (thus - enabling reindex between clusters) - :arg chunk_size: number of docs in one chunk sent to es (default: 500) - :arg scroll: Specify how long a consistent view of the index should be - maintained for scrolled search - :arg scan_kwargs: additional kwargs to be passed to - :func:`~elasticsearch.helpers.scan` - :arg bulk_kwargs: additional kwargs to be passed to - :func:`~elasticsearch.helpers.bulk` - """ - target_client = client if target_client is None else target_client - - docs = scan(client, - query=query, - index=source_index, - scroll=scroll, - fields=('_source', '_parent', '_routing', '_timestamp'), - **scan_kwargs - ) - def _change_doc_index(hits, index): - for h in hits: - h['_index'] = index - if 'fields' in h: - h.update(h.pop('fields')) - yield h - - kwargs = { - 'stats_only': True, - } - kwargs.update(bulk_kwargs) - return bulk(target_client, _change_doc_index(docs, target_index), - chunk_size=chunk_size, **kwargs) diff --git a/venv/lib/python2.7/site-packages/elasticsearch/helpers/test.py b/venv/lib/python2.7/site-packages/elasticsearch/helpers/test.py deleted file mode 100644 index 2abf473..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/helpers/test.py +++ /dev/null @@ -1,60 +0,0 @@ -import time -import os -try: - # python 2.6 - from unittest2 import TestCase, SkipTest -except ImportError: - from unittest import TestCase, SkipTest - -from elasticsearch import Elasticsearch -from elasticsearch.exceptions import ConnectionError - -def get_test_client(nowait=False, **kwargs): - # construct kwargs from the environment - kw = {'timeout': 30} - if 'TEST_ES_CONNECTION' in os.environ: - from elasticsearch import connection - kw['connection_class'] = getattr(connection, os.environ['TEST_ES_CONNECTION']) - - kw.update(kwargs) - client = Elasticsearch([os.environ.get('TEST_ES_SERVER', {})], **kw) - - # wait for yellow status - for _ in range(1 if nowait else 100): - try: - client.cluster.health(wait_for_status='yellow') - return client - except ConnectionError: - time.sleep(.1) - else: - # timeout - raise SkipTest("Elasticsearch failed to start.") - -def _get_version(version_string): - if '.' not in version_string: - return () - version = version_string.strip().split('.') - return tuple(int(v) if v.isdigit() else 999 for v in version) - -class ElasticsearchTestCase(TestCase): - @staticmethod - def _get_client(): - return get_test_client() - - @classmethod - def setUpClass(cls): - super(ElasticsearchTestCase, cls).setUpClass() - cls.client = cls._get_client() - - def tearDown(self): - super(ElasticsearchTestCase, self).tearDown() - self.client.indices.delete(index='*') - self.client.indices.delete_template(name='*', ignore=404) - - @property - def es_version(self): - if not hasattr(self, '_es_version'): - version_string = self.client.info()['version']['number'] - self._es_version = _get_version(version_string) - return self._es_version - diff --git a/venv/lib/python2.7/site-packages/elasticsearch/serializer.py b/venv/lib/python2.7/site-packages/elasticsearch/serializer.py deleted file mode 100644 index 808db71..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/serializer.py +++ /dev/null @@ -1,77 +0,0 @@ -try: - import simplejson as json -except ImportError: - import json -import uuid -from datetime import date, datetime -from decimal import Decimal - -from .exceptions import SerializationError, ImproperlyConfigured -from .compat import string_types - -class TextSerializer(object): - mimetype = 'text/plain' - - def loads(self, s): - return s - - def dumps(self, data): - if isinstance(data, string_types): - return data - - raise SerializationError('Cannot serialize %r into text.' % data) - -class JSONSerializer(object): - mimetype = 'application/json' - - def default(self, data): - if isinstance(data, (date, datetime)): - return data.isoformat() - elif isinstance(data, Decimal): - return float(data) - elif isinstance(data, uuid.UUID): - return str(data) - raise TypeError("Unable to serialize %r (type: %s)" % (data, type(data))) - - def loads(self, s): - try: - return json.loads(s) - except (ValueError, TypeError) as e: - raise SerializationError(s, e) - - def dumps(self, data): - # don't serialize strings - if isinstance(data, string_types): - return data - - try: - return json.dumps(data, default=self.default, ensure_ascii=False) - except (ValueError, TypeError) as e: - raise SerializationError(data, e) - -DEFAULT_SERIALIZERS = { - JSONSerializer.mimetype: JSONSerializer(), - TextSerializer.mimetype: TextSerializer(), -} - -class Deserializer(object): - def __init__(self, serializers, default_mimetype='application/json'): - try: - self.default = serializers[default_mimetype] - except KeyError: - raise ImproperlyConfigured('Cannot find default serializer (%s)' % default_mimetype) - self.serializers = serializers - - def loads(self, s, mimetype=None): - if not mimetype: - deserializer = self.default - else: - # split out charset - mimetype = mimetype.split(';', 1)[0] - try: - deserializer = self.serializers[mimetype] - except KeyError: - raise SerializationError('Unknown mimetype, unable to deserialize: %s' % mimetype) - - return deserializer.loads(s) - diff --git a/venv/lib/python2.7/site-packages/elasticsearch/transport.py b/venv/lib/python2.7/site-packages/elasticsearch/transport.py deleted file mode 100644 index 505768d..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch/transport.py +++ /dev/null @@ -1,355 +0,0 @@ -import time -from itertools import chain - -from .connection import Urllib3HttpConnection -from .connection_pool import ConnectionPool, DummyConnectionPool -from .serializer import JSONSerializer, Deserializer, DEFAULT_SERIALIZERS -from .exceptions import ConnectionError, TransportError, SerializationError, \ - ConnectionTimeout, ImproperlyConfigured - - -def get_host_info(node_info, host): - """ - Simple callback that takes the node info from `/_cluster/nodes` and a - parsed connection information and return the connection information. If - `None` is returned this node will be skipped. - - Useful for filtering nodes (by proximity for example) or if additional - information needs to be provided for the :class:`~elasticsearch.Connection` - class. By default master only nodes are filtered out since they shouldn't - typically be used for API operations. - - :arg node_info: node information from `/_cluster/nodes` - :arg host: connection information (host, port) extracted from the node info - """ - attrs = node_info.get('attributes', {}) - - # ignore master only nodes - if (attrs.get('data', 'true') == 'false' and - attrs.get('client', 'false') == 'false' and - attrs.get('master', 'true') == 'true'): - return None - return host - -class Transport(object): - """ - Encapsulation of transport-related to logic. Handles instantiation of the - individual connections as well as creating a connection pool to hold them. - - Main interface is the `perform_request` method. - """ - def __init__(self, hosts, connection_class=Urllib3HttpConnection, - connection_pool_class=ConnectionPool, host_info_callback=get_host_info, - sniff_on_start=False, sniffer_timeout=None, sniff_timeout=.1, - sniff_on_connection_fail=False, serializer=JSONSerializer(), serializers=None, - default_mimetype='application/json', max_retries=3, retry_on_status=(503, 504, ), - retry_on_timeout=False, send_get_body_as='GET', **kwargs): - """ - :arg hosts: list of dictionaries, each containing keyword arguments to - create a `connection_class` instance - :arg connection_class: subclass of :class:`~elasticsearch.Connection` to use - :arg connection_pool_class: subclass of :class:`~elasticsearch.ConnectionPool` to use - :arg host_info_callback: callback responsible for taking the node information from - `/_cluser/nodes`, along with already extracted information, and - producing a list of arguments (same as `hosts` parameter) - :arg sniff_on_start: flag indicating whether to obtain a list of nodes - from the cluser at startup time - :arg sniffer_timeout: number of seconds between automatic sniffs - :arg sniff_on_connection_fail: flag controlling if connection failure triggers a sniff - :arg sniff_timeout: timeout used for the sniff request - it should be a - fast api call and we are talking potentially to more nodes so we want - to fail quickly. Not used during initial sniffing (if - ``sniff_on_start`` is on) when the connection still isn't - initialized. - :arg serializer: serializer instance - :arg serializers: optional dict of serializer instances that will be - used for deserializing data coming from the server. (key is the mimetype) - :arg default_mimetype: when no mimetype is specified by the server - response assume this mimetype, defaults to `'application/json'` - :arg max_retries: maximum number of retries before an exception is propagated - :arg retry_on_status: set of HTTP status codes on which we should retry - on a different node. defaults to ``(503, 504, )`` - :arg retry_on_timeout: should timeout trigger a retry on different - node? (default `False`) - :arg send_get_body_as: for GET requests with body this option allows - you to specify an alternate way of execution for environments that - don't support passing bodies with GET requests. If you set this to - 'POST' a POST method will be used instead, if to 'source' then the body - will be serialized and passed as a query parameter `source`. - - Any extra keyword arguments will be passed to the `connection_class` - when creating and instance unless overriden by that connection's - options provided as part of the hosts parameter. - """ - - # serialization config - _serializers = DEFAULT_SERIALIZERS.copy() - # if a serializer has been specified, use it for deserialization as well - _serializers[serializer.mimetype] = serializer - # if custom serializers map has been supplied, override the defaults with it - if serializers: - _serializers.update(serializers) - # create a deserializer with our config - self.deserializer = Deserializer(_serializers, default_mimetype) - - self.max_retries = max_retries - self.retry_on_timeout = retry_on_timeout - self.retry_on_status = retry_on_status - self.send_get_body_as = send_get_body_as - - # data serializer - self.serializer = serializer - - # store all strategies... - self.connection_pool_class = connection_pool_class - self.connection_class = connection_class - - # ...save kwargs to be passed to the connections - self.kwargs = kwargs - self.hosts = hosts - - # ...and instantiate them - self.set_connections(hosts) - # retain the original connection instances for sniffing - self.seed_connections = self.connection_pool.connections[:] - - # sniffing data - self.sniffer_timeout = sniffer_timeout - self.sniff_on_connection_fail = sniff_on_connection_fail - self.last_sniff = time.time() - self.sniff_timeout = sniff_timeout - - # callback to construct host dict from data in /_cluster/nodes - self.host_info_callback = host_info_callback - - if sniff_on_start: - self.sniff_hosts(True) - - def add_connection(self, host): - """ - Create a new :class:`~elasticsearch.Connection` instance and add it to the pool. - - :arg host: kwargs that will be used to create the instance - """ - self.hosts.append(host) - self.set_connections(self.hosts) - - def set_connections(self, hosts): - """ - Instantiate all the connections and crate new connection pool to hold - them. Tries to identify unchanged hosts and re-use existing - :class:`~elasticsearch.Connection` instances. - - :arg hosts: same as `__init__` - """ - # construct the connections - def _create_connection(host): - # if this is not the initial setup look at the existing connection - # options and identify connections that haven't changed and can be - # kept around. - if hasattr(self, 'connection_pool'): - for (connection, old_host) in self.connection_pool.connection_opts: - if old_host == host: - return connection - - # previously unseen params, create new connection - kwargs = self.kwargs.copy() - kwargs.update(host) - - if 'scheme' in host and host['scheme'] != self.connection_class.transport_schema: - raise ImproperlyConfigured( - 'Scheme specified in connection (%s) is not the same as the connection class (%s) specifies (%s).' % ( - host['scheme'], self.connection_class.__name__, self.connection_class.transport_schema - )) - return self.connection_class(**kwargs) - connections = map(_create_connection, hosts) - - connections = list(zip(connections, hosts)) - if len(connections) == 1: - self.connection_pool = DummyConnectionPool(connections) - else: - # pass the hosts dicts to the connection pool to optionally extract parameters from - self.connection_pool = self.connection_pool_class(connections, **self.kwargs) - - def get_connection(self): - """ - Retreive a :class:`~elasticsearch.Connection` instance from the - :class:`~elasticsearch.ConnectionPool` instance. - """ - if self.sniffer_timeout: - if time.time() >= self.last_sniff + self.sniffer_timeout: - self.sniff_hosts() - return self.connection_pool.get_connection() - - def _get_sniff_data(self, initial=False): - """ - Perform the request to get sniffins information. Returns a list of - dictionaries (one per node) containing all the information from the - cluster. - - It also sets the last_sniff attribute in case of a successful attempt. - - In rare cases it might be possible to override this method in your - custom Transport class to serve data from alternative source like - configuration management. - """ - previous_sniff = self.last_sniff - - try: - # reset last_sniff timestamp - self.last_sniff = time.time() - # go through all current connections as well as the - # seed_connections for good measure - for c in chain(self.connection_pool.connections, self.seed_connections): - try: - # use small timeout for the sniffing request, should be a fast api call - _, headers, node_info = c.perform_request('GET', '/_nodes/_all/clear', - timeout=self.sniff_timeout if not initial else None) - node_info = self.deserializer.loads(node_info, headers.get('content-type')) - break - except (ConnectionError, SerializationError): - pass - else: - raise TransportError("N/A", "Unable to sniff hosts.") - except: - # keep the previous value on error - self.last_sniff = previous_sniff - raise - - return list(node_info['nodes'].values()) - - - def sniff_hosts(self, initial=False): - """ - Obtain a list of nodes from the cluster and create a new connection - pool using the information retrieved. - - To extract the node connection parameters use the ``nodes_to_host_callback``. - - :arg initial: flag indicating if this is during startup - (``sniff_on_start``), ignore the ``sniff_timeout`` if ``True`` - """ - node_info = self._get_sniff_data(initial) - - hosts = [] - address_key = self.connection_class.transport_schema + '_address' - for n in node_info: - host = {} - address = n.get(address_key, '') - if '/' in address: - host['host'], address = address.split('/', 1) - - # malformed address - if ':' not in address: - continue - - ip, port = address.rsplit(':', 1) - - # use the ip if not overridden by publish_host - host.setdefault('host', ip) - host['port'] = int(port) - - host = self.host_info_callback(n, host) - if host is not None: - hosts.append(host) - - # we weren't able to get any nodes, maybe using an incompatible - # transport_schema or host_info_callback blocked all - raise error. - if not hosts: - raise TransportError("N/A", "Unable to sniff hosts - no viable hosts found.") - - self.set_connections(hosts) - - def mark_dead(self, connection): - """ - Mark a connection as dead (failed) in the connection pool. If sniffing - on failure is enabled this will initiate the sniffing process. - - :arg connection: instance of :class:`~elasticsearch.Connection` that failed - """ - # mark as dead even when sniffing to avoid hitting this host during the sniff process - self.connection_pool.mark_dead(connection) - if self.sniff_on_connection_fail: - self.sniff_hosts() - - def perform_request(self, method, url, params=None, body=None): - """ - Perform the actual request. Retrieve a connection from the connection - pool, pass all the information to it's perform_request method and - return the data. - - If an exception was raised, mark the connection as failed and retry (up - to `max_retries` times). - - If the operation was succesful and the connection used was previously - marked as dead, mark it as live, resetting it's failure count. - - :arg method: HTTP method to use - :arg url: absolute url (without host) to target - :arg params: dictionary of query parameters, will be handed over to the - underlying :class:`~elasticsearch.Connection` class for serialization - :arg body: body of the request, will be serializes using serializer and - passed to the connection - """ - if body is not None: - body = self.serializer.dumps(body) - - # some clients or environments don't support sending GET with body - if method in ('HEAD', 'GET') and self.send_get_body_as != 'GET': - # send it as post instead - if self.send_get_body_as == 'POST': - method = 'POST' - - # or as source parameter - elif self.send_get_body_as == 'source': - if params is None: - params = {} - params['source'] = body - body = None - - if body is not None: - try: - body = body.encode('utf-8') - except (UnicodeDecodeError, AttributeError): - # bytes/str - no need to re-encode - pass - - ignore = () - timeout = None - if params: - timeout = params.pop('request_timeout', None) - ignore = params.pop('ignore', ()) - if isinstance(ignore, int): - ignore = (ignore, ) - - for attempt in range(self.max_retries + 1): - connection = self.get_connection() - - try: - status, headers, data = connection.perform_request(method, url, params, body, ignore=ignore, timeout=timeout) - - except TransportError as e: - retry = False - if isinstance(e, ConnectionTimeout): - retry = self.retry_on_timeout - elif isinstance(e, ConnectionError): - retry = True - elif e.status_code in self.retry_on_status: - retry = True - - if retry: - # only mark as dead if we are retrying - self.mark_dead(connection) - # raise exception on last retry - if attempt == self.max_retries: - raise - else: - raise - - else: - # connection didn't fail, confirm it's live status - self.connection_pool.mark_live(connection) - if data: - data = self.deserializer.loads(data, headers.get('content-type')) - return status, data - diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/DESCRIPTION.rst b/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/DESCRIPTION.rst deleted file mode 100644 index 63dafc7..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/DESCRIPTION.rst +++ /dev/null @@ -1,238 +0,0 @@ -Elasticsearch DSL -================= - -Elasticsearch DSL is a high-level library whose aim is to help with writing and -running queries against Elasticsearch. It is built on top of the official -low-level client (``elasticsearch-py``). - -It provides a more convenient and idiomatic way to write and manipulate -queries. It stays close to the Elasticsearch JSON DSL, mirroring its -terminology and structure. It exposes the whole range of the DSL from Python -either directly using defined classes or a queryset-like expressions. - -It also provides an optional wrapper for working with documents as Python -objects: defining mappings, retrieving and saving documents, wrapping the -document data in user-defined classes. - -To use the other Elasticsearch APIs (eg. cluster health) just use the -underlying client. - -Compatibility -------------- - -The library is compatible with all Elasticsearch versions since ``1.x`` but you -**have to use a matching major version**: - -For **Elasticsearch 2.0** and later, use the major version 2 (``2.x.y``) of the -library. - -For **Elasticsearch 1.0** and later, use the major version 0 (``0.x.y``) of the -library. - - -The recommended way to set your requirements in your `setup.py` or -`requirements.txt` is:: - - # Elasticsearch 2.x - elasticsearch-dsl>=2.0.0,<3.0.0 - - # Elasticsearch 1.x - elasticsearch-dsl<2.0.0 - - -The development is happening on ``master`` and ``1.x`` branches, respectively. - -Search Example --------------- - -Let's have a typical search request written directly as a ``dict``: - -.. code:: python - - from elasticsearch import Elasticsearch - client = Elasticsearch() - - response = client.search( - index="my-index", - body={ - "query": { - "filtered": { - "query": { - "bool": { - "must": [{"match": {"title": "python"}}], - "must_not": [{"match": {"description": "beta"}}] - } - }, - "filter": {"term": {"category": "search"}} - } - }, - "aggs" : { - "per_tag": { - "terms": {"field": "tags"}, - "aggs": { - "max_lines": {"max": {"field": "lines"}} - } - } - } - } - ) - - for hit in response['hits']['hits']: - print(hit['_score'], hit['_source']['title']) - - for tag in response['aggregations']['per_tag']['buckets']: - print(tag['key'], tag['max_lines']['value']) - - - -The problem with this approach is that it is very verbose, prone to syntax -mistakes like incorrect nesting, hard to modify (eg. adding another filter) and -definitely not fun to write. - -Let's rewrite the example using the Python DSL: - -.. code:: python - - from elasticsearch import Elasticsearch - from elasticsearch_dsl import Search, Q - - client = Elasticsearch() - - s = Search(using=client, index="my-index") \ - .filter("term", category="search") \ - .query("match", title="python") \ - .query(~Q("match", description="beta")) - - s.aggs.bucket('per_tag', 'terms', field='tags') \ - .metric('max_lines', 'max', field='lines') - - response = s.execute() - - for hit in response: - print(hit.meta.score, hit.title) - - for tag in response.aggregations.per_tag.buckets: - print(tag.key, tag.max_lines.value) - -As you see, the library took care of: - - * creating appropriate ``Query`` objects by name (eq. "match") - - * composing queries into a compound ``bool`` query - - * creating a ``filtered`` query since ``.filter()`` was used - - * providing a convenient access to response data - - * no curly or square brackets everywhere - - -Persistence Example -------------------- - -Let's have a simple Python class representing an article in a blogging system: - -.. code:: python - - from datetime import datetime - from elasticsearch_dsl import DocType, String, Date, Integer - from elasticsearch_dsl.connections import connections - - # Define a default Elasticsearch client - connections.create_connection(hosts=['localhost']) - - class Article(DocType): - title = String(analyzer='snowball', fields={'raw': String(index='not_analyzed')}) - body = String(analyzer='snowball') - tags = String(index='not_analyzed') - published_from = Date() - lines = Integer() - - class Meta: - index = 'blog' - - def save(self, ** kwargs): - self.lines = len(self.body.split()) - return super(Article, self).save(** kwargs) - - def is_published(self): - return datetime.now() > self.published_from - - # create the mappings in elasticsearch - Article.init() - - # create and save and article - article = Article(meta={'id': 42}, title='Hello world!', tags=['test']) - article.body = ''' looong text ''' - article.published_from = datetime.now() - article.save() - - article = Article.get(id=42) - print(article.is_published()) - - # Display cluster health - print(connections.get_connection().cluster.health()) - - -In this example you can see: - - * providing a default connection - - * defining fields with mapping configuration - - * setting index name - - * defining custom methods - - * overriding the built-in ``.save()`` method to hook into the persistence - life cycle - - * retrieving and saving the object into Elasticsearch - - * accessing the underlying client for other APIs - -You can see more in the persistence chapter of the documentation. - -Migration from ``elasticsearch-py`` ------------------------------------ - -You don't have to port your entire application to get the benefits of the -Python DSL, you can start gradually by creating a ``Search`` object from your -existing ``dict``, modifying it using the API and serializing it back to a -``dict``: - -.. code:: python - - body = {...} # insert complicated query here - - # Convert to Search object - s = Search.from_dict(body) - - # Add some filters, aggregations, queries, ... - s.filter("term", tags="python") - - # Convert back to dict to plug back into existing code - body = s.to_dict() - -Documentation -------------- - -Documentation is available at https://elasticsearch-dsl.readthedocs.org. - -License -------- - -Copyright 2013 Elasticsearch - -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. - diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/INSTALLER b/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/METADATA b/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/METADATA deleted file mode 100644 index 1c22771..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/METADATA +++ /dev/null @@ -1,265 +0,0 @@ -Metadata-Version: 2.0 -Name: elasticsearch-dsl -Version: 2.0.0 -Summary: Python client for Elasticsearch -Home-page: https://github.com/elasticsearch/elasticsearch-dsl-py -Author: Honza Král -Author-email: honza.kral@gmail.com -License: Apache License, Version 2.0 -Platform: UNKNOWN -Classifier: Development Status :: 4 - Beta -Classifier: License :: OSI Approved :: Apache Software License -Classifier: Intended Audience :: Developers -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.2 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Requires-Dist: six -Requires-Dist: python-dateutil -Requires-Dist: elasticsearch (>=2.0.0,<3.0.0) - -Elasticsearch DSL -================= - -Elasticsearch DSL is a high-level library whose aim is to help with writing and -running queries against Elasticsearch. It is built on top of the official -low-level client (``elasticsearch-py``). - -It provides a more convenient and idiomatic way to write and manipulate -queries. It stays close to the Elasticsearch JSON DSL, mirroring its -terminology and structure. It exposes the whole range of the DSL from Python -either directly using defined classes or a queryset-like expressions. - -It also provides an optional wrapper for working with documents as Python -objects: defining mappings, retrieving and saving documents, wrapping the -document data in user-defined classes. - -To use the other Elasticsearch APIs (eg. cluster health) just use the -underlying client. - -Compatibility -------------- - -The library is compatible with all Elasticsearch versions since ``1.x`` but you -**have to use a matching major version**: - -For **Elasticsearch 2.0** and later, use the major version 2 (``2.x.y``) of the -library. - -For **Elasticsearch 1.0** and later, use the major version 0 (``0.x.y``) of the -library. - - -The recommended way to set your requirements in your `setup.py` or -`requirements.txt` is:: - - # Elasticsearch 2.x - elasticsearch-dsl>=2.0.0,<3.0.0 - - # Elasticsearch 1.x - elasticsearch-dsl<2.0.0 - - -The development is happening on ``master`` and ``1.x`` branches, respectively. - -Search Example --------------- - -Let's have a typical search request written directly as a ``dict``: - -.. code:: python - - from elasticsearch import Elasticsearch - client = Elasticsearch() - - response = client.search( - index="my-index", - body={ - "query": { - "filtered": { - "query": { - "bool": { - "must": [{"match": {"title": "python"}}], - "must_not": [{"match": {"description": "beta"}}] - } - }, - "filter": {"term": {"category": "search"}} - } - }, - "aggs" : { - "per_tag": { - "terms": {"field": "tags"}, - "aggs": { - "max_lines": {"max": {"field": "lines"}} - } - } - } - } - ) - - for hit in response['hits']['hits']: - print(hit['_score'], hit['_source']['title']) - - for tag in response['aggregations']['per_tag']['buckets']: - print(tag['key'], tag['max_lines']['value']) - - - -The problem with this approach is that it is very verbose, prone to syntax -mistakes like incorrect nesting, hard to modify (eg. adding another filter) and -definitely not fun to write. - -Let's rewrite the example using the Python DSL: - -.. code:: python - - from elasticsearch import Elasticsearch - from elasticsearch_dsl import Search, Q - - client = Elasticsearch() - - s = Search(using=client, index="my-index") \ - .filter("term", category="search") \ - .query("match", title="python") \ - .query(~Q("match", description="beta")) - - s.aggs.bucket('per_tag', 'terms', field='tags') \ - .metric('max_lines', 'max', field='lines') - - response = s.execute() - - for hit in response: - print(hit.meta.score, hit.title) - - for tag in response.aggregations.per_tag.buckets: - print(tag.key, tag.max_lines.value) - -As you see, the library took care of: - - * creating appropriate ``Query`` objects by name (eq. "match") - - * composing queries into a compound ``bool`` query - - * creating a ``filtered`` query since ``.filter()`` was used - - * providing a convenient access to response data - - * no curly or square brackets everywhere - - -Persistence Example -------------------- - -Let's have a simple Python class representing an article in a blogging system: - -.. code:: python - - from datetime import datetime - from elasticsearch_dsl import DocType, String, Date, Integer - from elasticsearch_dsl.connections import connections - - # Define a default Elasticsearch client - connections.create_connection(hosts=['localhost']) - - class Article(DocType): - title = String(analyzer='snowball', fields={'raw': String(index='not_analyzed')}) - body = String(analyzer='snowball') - tags = String(index='not_analyzed') - published_from = Date() - lines = Integer() - - class Meta: - index = 'blog' - - def save(self, ** kwargs): - self.lines = len(self.body.split()) - return super(Article, self).save(** kwargs) - - def is_published(self): - return datetime.now() > self.published_from - - # create the mappings in elasticsearch - Article.init() - - # create and save and article - article = Article(meta={'id': 42}, title='Hello world!', tags=['test']) - article.body = ''' looong text ''' - article.published_from = datetime.now() - article.save() - - article = Article.get(id=42) - print(article.is_published()) - - # Display cluster health - print(connections.get_connection().cluster.health()) - - -In this example you can see: - - * providing a default connection - - * defining fields with mapping configuration - - * setting index name - - * defining custom methods - - * overriding the built-in ``.save()`` method to hook into the persistence - life cycle - - * retrieving and saving the object into Elasticsearch - - * accessing the underlying client for other APIs - -You can see more in the persistence chapter of the documentation. - -Migration from ``elasticsearch-py`` ------------------------------------ - -You don't have to port your entire application to get the benefits of the -Python DSL, you can start gradually by creating a ``Search`` object from your -existing ``dict``, modifying it using the API and serializing it back to a -``dict``: - -.. code:: python - - body = {...} # insert complicated query here - - # Convert to Search object - s = Search.from_dict(body) - - # Add some filters, aggregations, queries, ... - s.filter("term", tags="python") - - # Convert back to dict to plug back into existing code - body = s.to_dict() - -Documentation -------------- - -Documentation is available at https://elasticsearch-dsl.readthedocs.org. - -License -------- - -Copyright 2013 Elasticsearch - -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. - diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/RECORD b/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/RECORD deleted file mode 100644 index a438bc1..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/RECORD +++ /dev/null @@ -1,42 +0,0 @@ -elasticsearch_dsl/filter.py,sha256=MOtItO7APSI3ZxzkXonWPmkVBa85Zrp328UHhVx7o9Y,5627 -elasticsearch_dsl/connections.py,sha256=fKluPp0QCk3ENi2nZoxGXiB7qFWRrwfMn7MpZ2uA1ws,3119 -elasticsearch_dsl/faceted_search.py,sha256=xoRp4p9pg8PA8ksl70azjYYUm5lNWBHPFLjwv6nA82I,8046 -elasticsearch_dsl/field.py,sha256=sBvnZssItuiJLBdGhRGMVrv70fa8rtqj-ycxWMpVCfo,7513 -elasticsearch_dsl/document.py,sha256=tdcFNFuxxUsU7xQ3eGV2tcPWmxh36sk44Hw9N7haocg,8332 -elasticsearch_dsl/analysis.py,sha256=jzIlDS_79CStTf5QuIERAjhaNSiRtCISnJxhtAraPQo,3262 -elasticsearch_dsl/exceptions.py,sha256=IxJtSE19mUzM77FtrO2n5l2mPbL3TsupdwhirIHk8EE,254 -elasticsearch_dsl/index.py,sha256=HTjNRBxV5t_l7TkopZ_OnGltFQihh3sLS9kXxMX1dgQ,2915 -elasticsearch_dsl/function.py,sha256=RbwPF2bssRA4cwzxPROtmvaTGNB9652l8qNsM1XCMm4,2535 -elasticsearch_dsl/result.py,sha256=UkRy9z-8R5mogJjwv3usvTsmNK6jUmCtbd7TtNuo9gY,2599 -elasticsearch_dsl/mapping.py,sha256=tjJUGd9Gqx958VaMF_mYVtXPlSD5abD3rQfMCfSQEs0,5258 -elasticsearch_dsl/query.py,sha256=yvzyWLX9XF_pV2t9LofmAUObYUxUUsPb_YbpyCskEfQ,8019 -elasticsearch_dsl/serializer.py,sha256=h7-fSiJYc3J_zPv3S6llODtYRJfehXz5ZVL8lwMt0bc,387 -elasticsearch_dsl/search.py,sha256=ma0MsnqoCDyM21ZLxyBPNlFi0epHw7sVkz_PyeOlb88,20962 -elasticsearch_dsl/aggs.py,sha256=3y4RHAUPEtXvbdGBaKL_vn_SKvgEgvscQ13rDSjii9o,5978 -elasticsearch_dsl/utils.py,sha256=xOpcQMbJeabjDbgnRrKK2JFWO2oYM9zHeSZuLJ15nPU,12863 -elasticsearch_dsl/__init__.py,sha256=j5vPCDPeww6bLkFlOCYralCddcp3l4HhnKl-B0t7CMA,409 -elasticsearch_dsl-2.0.0.dist-info/pbr.json,sha256=9_6GDWaW_xvDbV0aqIGzXAGBdmRcvrO7BhxnqWtV1dg,47 -elasticsearch_dsl-2.0.0.dist-info/top_level.txt,sha256=xSZYjeDU8RrTQufEKhY7RX9KcSGZywt42PcWADXg1KY,18 -elasticsearch_dsl-2.0.0.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110 -elasticsearch_dsl-2.0.0.dist-info/METADATA,sha256=TWoWL4ORPWJ4xX26qXymRbbygKmDUYX-KjliCNnBOTc,7842 -elasticsearch_dsl-2.0.0.dist-info/DESCRIPTION.rst,sha256=b80GVru_ZTqybq24SFqhcHlWwtj3e_cJ8QsCTtAmyTc,6741 -elasticsearch_dsl-2.0.0.dist-info/metadata.json,sha256=HNkyIWVfAWlSGy2WZUrIDKQQrhOdhgRp20l9Wzjleg4,1269 -elasticsearch_dsl-2.0.0.dist-info/RECORD,, -elasticsearch_dsl-2.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -elasticsearch_dsl/utils.pyc,, -elasticsearch_dsl/filter.pyc,, -elasticsearch_dsl/serializer.pyc,, -elasticsearch_dsl/connections.pyc,, -elasticsearch_dsl/result.pyc,, -elasticsearch_dsl/index.pyc,, -elasticsearch_dsl/exceptions.pyc,, -elasticsearch_dsl/__init__.pyc,, -elasticsearch_dsl/field.pyc,, -elasticsearch_dsl/faceted_search.pyc,, -elasticsearch_dsl/analysis.pyc,, -elasticsearch_dsl/aggs.pyc,, -elasticsearch_dsl/search.pyc,, -elasticsearch_dsl/document.pyc,, -elasticsearch_dsl/function.pyc,, -elasticsearch_dsl/query.pyc,, -elasticsearch_dsl/mapping.pyc,, diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/WHEEL b/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/WHEEL deleted file mode 100644 index 9dff69d..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.24.0) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/metadata.json b/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/metadata.json deleted file mode 100644 index ff2a789..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"extras": [], "classifiers": ["Development Status :: 4 - Beta", "License :: OSI Approved :: Apache Software License", "Intended Audience :: Developers", "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.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy"], "test_requires": [{"requires": ["mock", "pytest", "pytest-cov", "pytz"]}], "license": "Apache License, Version 2.0", "run_requires": [{"requires": ["six", "python-dateutil", "elasticsearch (>=2.0.0,<3.0.0)"]}], "summary": "Python client for Elasticsearch", "generator": "bdist_wheel (0.24.0)", "name": "elasticsearch-dsl", "version": "2.0.0", "extensions": {"python.details": {"project_urls": {"Home": "https://github.com/elasticsearch/elasticsearch-dsl-py"}, "document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"name": "Honza Kr\u00e1l", "role": "author", "email": "honza.kral@gmail.com"}]}}, "metadata_version": "2.0"} \ No newline at end of file diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/pbr.json b/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/pbr.json deleted file mode 100644 index 6c0df49..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/pbr.json +++ /dev/null @@ -1 +0,0 @@ -{"is_release": false, "git_version": "d5c3476"} \ No newline at end of file diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/top_level.txt b/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/top_level.txt deleted file mode 100644 index 0a6baaa..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl-2.0.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -elasticsearch_dsl diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/__init__.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/__init__.py deleted file mode 100644 index 1cda645..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -from .query import Q -from .aggs import A -from .function import SF -from .search import Search, MultiSearch -from .field import * -from .document import DocType, MetaField -from .mapping import Mapping -from .index import Index -from .analysis import analyzer, token_filter, char_filter, tokenizer -from .faceted_search import * - -VERSION = (2, 0, 0) -__version__ = VERSION -__versionstr__ = '.'.join(map(str, VERSION)) diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/aggs.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/aggs.py deleted file mode 100644 index 8264499..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/aggs.py +++ /dev/null @@ -1,176 +0,0 @@ -from .utils import DslBase, _make_dsl_class - -__all__ = [ - 'A', 'Agg', 'Filter', 'Bucket', 'Children', 'DateHistogram', 'Filters', - 'GeoDistance', 'GeohashGrid', 'Global', 'Histogram', 'Iprange', 'Missing', - 'Nested', 'Range', 'ReverseNested', 'SignificantTerms', 'Terms', 'Avg', - 'Cardinality', 'ExtendedStats', 'GeoBounds', 'Max', 'Min', 'Percentiles', - 'PercenileRanks', 'ScriptedMetric', 'Stats', 'Sum', 'TopHits', 'ValueCount', - 'AvgBucket', 'BucketScript', 'BucketSelector', 'CumulativeSum', - 'Derivative', 'ExtendedStatsBucket', 'MaxBucket', 'MinBucket', 'MovingAvg', - 'PercentilesBucket', 'SerialDiff', 'StatsBucket', 'SumBucket' -] - - -def A(name_or_agg, filter=None, **params): - if filter is not None: - if name_or_agg != 'filter': - raise ValueError("Aggregation %r doesn't accept positional argument 'filter'." % name_or_agg) - params['filter'] = filter - - # {"terms": {"field": "tags"}, "aggs": {...}} - if isinstance(name_or_agg, dict): - if params: - raise ValueError('A() cannot accept parameters when passing in a dict.') - # copy to avoid modifying in-place - agg = name_or_agg.copy() - # pop out nested aggs - aggs = agg.pop('aggs', None) - # should be {"terms": {"fied": "tags"}} - if len(agg) != 1: - raise ValueError('A() can only accept dict with an aggregation ({"terms": {...}}). ' - 'Instead it got (%r)' % name_or_agg) - agg_type, params = agg.popitem() - if aggs: - params = params.copy() - params['aggs'] = aggs - return Agg.get_dsl_class(agg_type)(**params) - - # Terms(...) just return the nested agg - elif isinstance(name_or_agg, Agg): - if params: - raise ValueError('A() cannot accept parameters when passing in an Agg object.') - return name_or_agg - - # "terms", field="tags" - return Agg.get_dsl_class(name_or_agg)(**params) - -class Agg(DslBase): - _type_name = 'agg' - _type_shortcut = staticmethod(A) - name = None - -class AggBase(object): - _param_defs = { - 'aggs': {'type': 'agg', 'hash': True}, - } - def __getitem__(self, agg_name): - agg = self._params.setdefault('aggs', {})[agg_name] # propagate KeyError - - # make sure we're not mutating a shared state - whenever accessing a - # bucket, return a shallow copy of it to be safe - if isinstance(agg, Bucket): - agg = A(agg.name, **agg._params) - # be sure to store the copy so any modifications to it will affect us - self._params['aggs'][agg_name] = agg - - return agg - - def __setitem__(self, agg_name, agg): - self.aggs[agg_name] = A(agg) - - def _agg(self, bucket, name, agg_type, *args, **params): - agg = self[name] = A(agg_type, *args, **params) - - # For chaining - when creating new buckets return them... - if bucket: - return agg - # otherwise return self._base so we can keep chaining - else: - return self._base - - def metric(self, name, agg_type, *args, **params): - return self._agg(False, name, agg_type, *args, **params) - - def bucket(self, name, agg_type, *args, **params): - return self._agg(True, name, agg_type, *args, **params) - - def pipeline(self, name, agg_type, *args, **params): - return self._agg(False, name, agg_type, *args, **params) - - -class Bucket(AggBase, Agg): - def __init__(self, **params): - super(Bucket, self).__init__(**params) - # remember self for chaining - self._base = self - - def to_dict(self): - d = super(AggBase, self).to_dict() - if 'aggs' in d[self.name]: - d['aggs'] = d[self.name].pop('aggs') - return d - -class Filter(Bucket): - name = 'filter' - _param_defs = { - 'filter': {'type': 'query'}, - 'aggs': {'type': 'agg', 'hash': True}, - } - - def __init__(self, filter=None, **params): - if filter is not None: - params['filter'] = filter - super(Filter, self).__init__(**params) - - def to_dict(self): - d = super(Filter, self).to_dict() - d[self.name].update(d[self.name].pop('filter', {})) - return d - -class Pipeline(Agg): - pass - -AGGS = ( - (Bucket, 'children', None), - (Bucket, 'date_histogram', None), - (Bucket, 'date_range', None), - (Bucket, 'filters', {'filters': {'type': 'query', 'hash': True}}), - (Bucket, 'geo_distance', None), - (Bucket, 'geohash_grid', None), - (Bucket, 'global', None), - (Bucket, 'histogram', None), - (Bucket, 'iprange', None), - (Bucket, 'missing', None), - (Bucket, 'nested', None), - (Bucket, 'range', None), - (Bucket, 'reverse_nested', None), - (Bucket, 'significant_terms', None), - (Bucket, 'terms', None), - - (Agg, 'avg', None), - (Agg, 'cardinality', None), - (Agg, 'extended_stats', None), - (Agg, 'geo_bounds', None), - (Agg, 'max', None), - (Agg, 'min', None), - (Agg, 'percentiles', None), - (Agg, 'percentile_ranks', None), - (Agg, 'scripted_metric', None), - (Agg, 'stats', None), - (Agg, 'sum', None), - (Agg, 'top_hits', None), - (Agg, 'value_count', None), - - (Pipeline, 'avg_bucket', None), - (Pipeline, 'bucket_script', None), - (Pipeline, 'bucket_selector', None), - (Pipeline, 'cumulative_sum', None), - (Pipeline, 'derivative', None), - (Pipeline, 'extended_stats_bucket', None), - (Pipeline, 'max_bucket', None), - (Pipeline, 'min_bucket', None), - (Pipeline, 'moving_avg', None), - (Pipeline, 'percentiles_bucket', None), - (Pipeline, 'serial_diff', None), - (Pipeline, 'stats_bucket', None), - (Pipeline, 'sum_bucket', None), -) - -# generate the aggregation classes dynamicaly -for base, fname, params_def in AGGS: - # don't override the params def from AggBase - if params_def: - params_def.update(AggBase._param_defs) - fclass = _make_dsl_class(base, fname, params_def) - globals()[fclass.__name__] = fclass diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/analysis.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/analysis.py deleted file mode 100644 index b735365..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/analysis.py +++ /dev/null @@ -1,121 +0,0 @@ -from .utils import DslBase - -__all__ = [ - 'tokenizer', 'analyzer', 'char_filter', 'token_filter' -] - -class AnalysisBase(object): - @classmethod - def _type_shortcut(cls, name_or_instance, type=None, **kwargs): - if isinstance(name_or_instance, cls): - if type or kwargs: - raise ValueError('%s() cannot accept parameters.' % cls.__name__) - return name_or_instance - - if not (type or kwargs): - return cls.get_dsl_class('builtin')(name_or_instance) - - return cls.get_dsl_class('custom')(name_or_instance, type or 'custom', **kwargs) - -class CustomAnalysis(object): - name = 'custom' - def __init__(self, name, builtin_type='custom', **kwargs): - self._builtin_type = builtin_type - self._name = name - super(CustomAnalysis, self).__init__(**kwargs) - - def to_dict(self): - # only name to present in lists - return self._name - - def get_definition(self): - d = super(CustomAnalysis, self).to_dict() - d = d.pop(self.name) - d['type'] = self._builtin_type - return d - -class BuiltinAnalysis(object): - name = 'builtin' - def __init__(self, name): - self._name = name - super(BuiltinAnalysis, self).__init__() - - def to_dict(self): - # only name to present in lists - return self._name - -class Analyzer(AnalysisBase, DslBase): - _type_name = 'analyzer' - name = None - -class BuiltinAnalyzer(BuiltinAnalysis, Analyzer): - def get_analysis_definition(self): - return {} - -class CustomAnalyzer(CustomAnalysis, Analyzer): - _param_defs = { - 'filter': {'type': 'token_filter', 'multi': True}, - 'char_filter': {'type': 'char_filter', 'multi': True}, - 'tokenizer': {'type': 'tokenizer'}, - } - - def get_analysis_definition(self): - out = {'analyzer': {self._name: self.get_definition()}} - - t = getattr(self, 'tokenizer', None) - if hasattr(t, 'get_definition'): - out['tokenizer'] = {t._name: t.get_definition()} - - filters = dict((f._name, f.get_definition()) - for f in self.filter if hasattr(f, 'get_definition')) - if filters: - out['filter'] = filters - - - char_filters = dict((f._name, f.get_definition()) - for f in self.char_filter if hasattr(f, 'get_definition')) - if char_filters: - out['char_filter'] = char_filters - - return out - - -class Tokenizer(AnalysisBase, DslBase): - _type_name = 'tokenizer' - name = None - -class BuiltinTokenizer(BuiltinAnalysis, Tokenizer): - pass - -class CustomTokenizer(CustomAnalysis, Tokenizer): - pass - - -class TokenFilter(AnalysisBase, DslBase): - _type_name = 'token_filter' - name = None - -class BuiltinTokenFilter(BuiltinAnalysis, TokenFilter): - pass - -class CustomTokenFilter(CustomAnalysis, TokenFilter): - pass - - -class CharFilter(AnalysisBase, DslBase): - _type_name = 'char_filter' - name = None - -class BuiltinCharFilter(BuiltinAnalysis, CharFilter): - pass - -class CustomCharFilter(CustomAnalysis, CharFilter): - pass - - - -# shortcuts for direct use -analyzer = Analyzer._type_shortcut -tokenizer = Tokenizer._type_shortcut -token_filter = TokenFilter._type_shortcut -char_filter = CharFilter._type_shortcut diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/connections.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/connections.py deleted file mode 100644 index bdc62e0..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/connections.py +++ /dev/null @@ -1,96 +0,0 @@ -from six import string_types - -from elasticsearch import Elasticsearch - -from .serializer import serializer - -class Connections(object): - """ - Class responsible for holding connections to different clusters. Used as a - singleton in this module. - """ - def __init__(self): - self._kwargs = {} - self._conns = {} - - def configure(self, **kwargs): - """ - Configure multiple connections at once, useful for passing in config - dictionaries obtained from other sources, like Django's settings or a - configuration management tool. - - Example:: - - connections.configure( - default={'hosts': 'localhost'}, - dev={'hosts': ['esdev1.example.com:9200'], sniff_on_start=True} - ) - - Connections will only be constructed lazily when requested through - ``get_connection``. - """ - for k in list(self._conns): - # try and preserve existing client to keep the persistent connections alive - if k in self._kwargs and kwargs.get(k, None) == self._kwargs[k]: - continue - del self._conns[k] - self._kwargs = kwargs - - def add_connection(self, alias, conn): - """ - Add a connection object, it will be passed through as-is. - """ - self._conns[alias] = conn - - def remove_connection(self, alias): - """ - Remove connection from the registry. Raises ``KeyError`` if connection - wasn't found. - """ - errors = 0 - for d in (self._conns, self._kwargs): - try: - del d[alias] - except KeyError: - errors += 1 - - if errors == 2: - raise KeyError('There is no connection with alias %r.' % alias) - - def create_connection(self, alias='default', **kwargs): - """ - Construct an instance of ``elasticsearch.Elasticsearch`` and register - it under given alias. - """ - kwargs.setdefault('serializer', serializer) - conn = self._conns[alias] = Elasticsearch(**kwargs) - return conn - - def get_connection(self, alias='default'): - """ - Retrieve a connection, construct it if necessary (only configuration - was passed to us). If a non-string alias has been passed through we - assume it's already a client instance and will just return it as-is. - - Raises ``KeyError`` if no client (or its definition) is registered - under the alias. - """ - # do not check isinstance(Elasticsearch) so that people can wrap their - # clients - if not isinstance(alias, string_types): - return alias - - # connection already established - try: - return self._conns[alias] - except KeyError: - pass - - # if not, try to create it - try: - return self.create_connection(alias, **self._kwargs[alias]) - except KeyError: - # no connection and no kwargs to set one up - raise KeyError('There is no connection with alias %r.' % alias) - -connections = Connections() diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/document.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/document.py deleted file mode 100644 index c4f55da..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/document.py +++ /dev/null @@ -1,264 +0,0 @@ -import re - -from six import iteritems, add_metaclass - -from .field import Field -from .mapping import Mapping -from .utils import ObjectBase, AttrDict, merge -from .result import ResultMeta -from .search import Search -from .connections import connections -from .exceptions import ValidationException - -DELETE_META_FIELDS = frozenset(( - 'id', 'parent', 'routing', 'version', 'version_type' -)) - -DOC_META_FIELDS = frozenset(( - 'timestamp', 'ttl' -)).union(DELETE_META_FIELDS) - -META_FIELDS = frozenset(( - # Elasticsearch metadata fields, except 'type' - 'index', 'using', 'score', -)).union(DOC_META_FIELDS) - -class MetaField(object): - def __init__(self, *args, **kwargs): - self.args, self.kwargs = args, kwargs - -class DocTypeMeta(type): - def __new__(cls, name, bases, attrs): - # DocTypeMeta filters attrs in place - attrs['_doc_type'] = DocTypeOptions(name, bases, attrs) - return super(DocTypeMeta, cls).__new__(cls, name, bases, attrs) - -class DocTypeOptions(object): - def __init__(self, name, bases, attrs): - meta = attrs.pop('Meta', None) - - # default index, if not overriden by doc.meta - self.index = getattr(meta, 'index', None) - - # default cluster alias, can be overriden in doc.meta - self._using = getattr(meta, 'using', None) - - # get doc_type name, if not defined take the name of the class and - # tranform it to lower_case - doc_type = getattr(meta, 'doc_type', - re.sub(r'(.)([A-Z])', r'\1_\2', name).lower()) - - # create the mapping instance - self.mapping = getattr(meta, 'mapping', Mapping(doc_type)) - - # register all declared fields into the mapping - for name, value in list(iteritems(attrs)): - if isinstance(value, Field): - self.mapping.field(name, value) - del attrs[name] - - # add all the mappings for meta fields - for name in dir(meta): - if isinstance(getattr(meta, name, None), MetaField): - params = getattr(meta, name) - self.mapping.meta(name, *params.args, **params.kwargs) - - # document inheritance - include the fields from parents' mappings and - # index/using values - for b in bases: - if hasattr(b, '_doc_type') and hasattr(b._doc_type, 'mapping'): - self.mapping.update(b._doc_type.mapping, update_only=True) - self._using = self._using or b._doc_type._using - self.index = self.index or b._doc_type.index - - @property - def using(self): - return self._using or 'default' - - @property - def name(self): - return self.mapping.properties.name - - @property - def parent(self): - if '_parent' in self.mapping._meta: - return self.mapping._meta['_parent']['type'] - return - - def init(self, index=None, using=None): - self.mapping.save(index or self.index, using=using or self.using) - - def refresh(self, index=None, using=None): - self.mapping.update_from_es(index or self.index, using=using or self.using) - - -@add_metaclass(DocTypeMeta) -class DocType(ObjectBase): - def __init__(self, meta=None, **kwargs): - meta = meta or {} - for k in list(kwargs): - if k.startswith('_'): - meta[k] = kwargs.pop(k) - - if self._doc_type.index: - meta.setdefault('_index', self._doc_type.index) - super(AttrDict, self).__setattr__('meta', ResultMeta(meta)) - - super(DocType, self).__init__(**kwargs) - - def __getstate__(self): - return (self.to_dict(), self.meta._d_) - - def __setstate__(self, state): - data, meta = state - super(AttrDict, self).__setattr__('_d_', data) - super(AttrDict, self).__setattr__('meta', ResultMeta(meta)) - - def __getattr__(self, name): - if name.startswith('_') and name[1:] in META_FIELDS: - return getattr(self.meta, name[1:]) - return super(DocType, self).__getattr__(name) - - def __setattr__(self, name, value): - if name.startswith('_') and name[1:] in META_FIELDS: - return setattr(self.meta, name[1:], value) - return super(DocType, self).__setattr__(name, value) - - @classmethod - def init(cls, index=None, using=None): - cls._doc_type.init(index, using) - - @classmethod - def search(cls, using=None, index=None): - return Search( - using=using or cls._doc_type.using, - index=index or cls._doc_type.index, - doc_type={cls._doc_type.name: cls.from_es}, - ) - - @classmethod - def get(cls, id, using=None, index=None, **kwargs): - es = connections.get_connection(using or cls._doc_type.using) - doc = es.get( - index=index or cls._doc_type.index, - doc_type=cls._doc_type.name, - id=id, - **kwargs - ) - if not doc['found']: - return None - return cls.from_es(doc) - - @classmethod - def from_es(cls, hit): - # don't modify in place - meta = hit.copy() - doc = meta.pop('_source', {}) - - if 'fields' in meta: - for k, v in iteritems(meta.pop('fields')): - if k == '_source': - doc.update(v) - if k.startswith('_'): - meta[k] = v - else: - doc[k] = v - - return cls(meta=meta, **doc) - - def _get_connection(self, using=None): - return connections.get_connection(using or self._doc_type.using) - connection = property(_get_connection) - - def _get_index(self, index=None): - if index is None: - index = getattr(self.meta, 'index', self._doc_type.index) - if index is None: - raise ValidationException('No index') - return index - - def delete(self, using=None, index=None, **kwargs): - es = self._get_connection(using) - # extract parent, routing etc from meta - doc_meta = dict( - (k, self.meta[k]) - for k in DELETE_META_FIELDS - if k in self.meta - ) - doc_meta.update(kwargs) - es.delete( - index=self._get_index(index), - doc_type=self._doc_type.name, - **doc_meta - ) - - def to_dict(self, include_meta=False): - d = super(DocType, self).to_dict() - if not include_meta: - return d - - meta = dict( - ('_' + k, self.meta[k]) - for k in DOC_META_FIELDS - if k in self.meta - ) - - # in case of to_dict include the index unlike save/update/delete - if 'index' in self.meta: - meta['_index'] = self.meta.index - elif self._doc_type.index: - meta['_index'] = self._doc_type.index - - meta['_type'] = self._doc_type.name - meta['_source'] = d - return meta - - def update(self, using=None, index=None, **fields): - es = self._get_connection(using) - - # update the data locally - merge(self._d_, fields) - - # extract parent, routing etc from meta - doc_meta = dict( - (k, self.meta[k]) - for k in DOC_META_FIELDS - if k in self.meta - ) - meta = es.update( - index=self._get_index(index), - doc_type=self._doc_type.name, - body={'doc': fields}, - **doc_meta - ) - # update meta information from ES - for k in META_FIELDS: - if '_' + k in meta: - setattr(self.meta, k, meta['_' + k]) - - def save(self, using=None, index=None, validate=True, **kwargs): - if validate: - self.full_clean() - - es = self._get_connection(using) - # extract parent, routing etc from meta - doc_meta = dict( - (k, self.meta[k]) - for k in DOC_META_FIELDS - if k in self.meta - ) - doc_meta.update(kwargs) - meta = es.index( - index=self._get_index(index), - doc_type=self._doc_type.name, - body=self.to_dict(), - **doc_meta - ) - # update meta information from ES - for k in META_FIELDS: - if '_' + k in meta: - setattr(self.meta, k, meta['_' + k]) - - # return True/False if the document has been created/updated - return meta['created'] - diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/exceptions.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/exceptions.py deleted file mode 100644 index 0ed5863..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/exceptions.py +++ /dev/null @@ -1,14 +0,0 @@ -class ElasticsearchDslException(Exception): - pass - - -class UnknownDslObject(ElasticsearchDslException): - pass - - -class ValidationException(ValueError, ElasticsearchDslException): - pass - - -class IllegalOperation(ElasticsearchDslException): - pass diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/faceted_search.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/faceted_search.py deleted file mode 100644 index 8497122..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/faceted_search.py +++ /dev/null @@ -1,284 +0,0 @@ -from datetime import timedelta, datetime -from six import iteritems, itervalues -from functools import partial - -from .search import Search -from .aggs import A -from .utils import AttrDict -from .result import Response -from .query import Q - -__all__ = ['FacetedSearch', 'HistogramFacet', 'TermsFacet', 'DateHistogramFacet', 'RangeFacet'] - -class Facet(object): - """ - A facet on faceted search. Wraps and aggregation and provides functionality - to create a filter for selected values and return a list of facet values - from the result of the aggregation. - """ - agg_type = None - - def __init__(self, **kwargs): - self.filter_values = () - self._params = kwargs - - def get_aggregation(self): - """ - Return the aggregation object. - """ - return A(self.agg_type, **self._params) - - def add_filter(self, filter_values): - """ - Construct a filter. - """ - if not filter_values: - return - - f = self.get_value_filter(filter_values[0]) - for v in filter_values[1:]: - f |= self.get_value_filter(v) - return f - - def get_value_filter(self, filter_value): - """ - Construct a filter for an individual value - """ - pass - - def is_filtered(self, key, filter_values): - """ - Is a filter active on the given key. - """ - return key in filter_values - - def get_value(self, bucket): - """ - return a value representing a bucket. Its key as default. - """ - return bucket['key'] - - def get_values(self, data, filter_values): - """ - Turn the raw bucket data into a list of tuples containing the key, - number of documents and a flag indicating whether this value has been - selected or not. - """ - out = [] - for bucket in data: - key = self.get_value(bucket) - out.append(( - key, - bucket['doc_count'], - self.is_filtered(key, filter_values) - )) - return out - - -class TermsFacet(Facet): - agg_type = 'terms' - - def add_filter(self, filter_values): - """ Create a terms filter instead of bool containing term filters. """ - if filter_values: - return Q('terms', **{self._params['field']: filter_values}) - - -class RangeFacet(Facet): - agg_type = 'range' - - def _range_to_dict(self, range): - key, range = range - out = {'key': key} - if range[0] is not None: - out['from'] = range[0] - if range[1] is not None: - out['to'] = range[1] - return out - - def __init__(self, ranges, **kwargs): - super(RangeFacet, self).__init__(**kwargs) - self._params['ranges'] = list(map(self._range_to_dict, ranges)) - self._params['keyed'] = False - self._ranges = dict(ranges) - - def get_value_filter(self, filter_value): - f, t = self._ranges[filter_value] - limits = {} - if f is not None: - limits['from'] = f - if t is not None: - limits['to'] = t - - return Q('range', **{ - self._params['field']: limits - }) - -class HistogramFacet(Facet): - agg_type = 'histogram' - - def get_value_filter(self, filter_value): - return Q('range', **{ - self._params['field']: { - 'gte': filter_value, - 'lt': filter_value + self._params['interval'] - } - }) - - -class DateHistogramFacet(Facet): - agg_type = 'date_histogram' - - DATE_INTERVALS = { - 'month': lambda d: (d+timedelta(days=32)).replace(day=1), - 'week': lambda d: d+timedelta(days=7), - 'day': lambda d: d+timedelta(days=1), - 'hour': lambda d: d+timedelta(hours=1), - } - - def __init__(self, **kwargs): - kwargs.setdefault("min_doc_count", 0) - super(DateHistogramFacet, self).__init__(**kwargs) - - def get_value(self, bucket): - return datetime.utcfromtimestamp(int(bucket['key']) / 1000) - - def get_value_filter(self, filter_value): - return Q('range', **{ - self._params['field']: { - 'gte': filter_value, - 'lt': self.DATE_INTERVALS[self._params['interval']](filter_value) - } - }) - - -class FacetedResponse(Response): - def __init__(self, search, *args, **kwargs): - super(FacetedResponse, self).__init__(*args, **kwargs) - super(AttrDict, self).__setattr__('_search', search) - - @property - def query_string(self): - return self._search._query - - @property - def facets(self): - if not hasattr(self, '_facets'): - super(AttrDict, self).__setattr__('_facets', AttrDict({})) - for name, facet in iteritems(self._search.facets): - self._facets[name] = facet.get_values( - self.aggregations['_filter_' + name][name]['buckets'], - self._search.filter_values.get(name, ()) - ) - return self._facets - - -class FacetedSearch(object): - index = '_all' - doc_types = ['_all'] - fields = ('*', ) - facets = {} - - def __init__(self, query=None, filters={}): - self._query = query - self._filters = {} - self.filter_values = {} - for name, value in iteritems(filters): - self.add_filter(name, value) - - self._s = self.build_search() - - def count(self): - return self._s.count() - - def __getitem__(self, k): - self._s = self._s[k] - return self - - def __iter__(self): - return iter(self._s) - - def add_filter(self, name, filter_values): - """ - Add a filter for a facet. - """ - # normalize the value into a list - if not isinstance(filter_values, (tuple, list)): - if filter_values in (None, ''): - return - filter_values = [filter_values, ] - - # remember the filter values for use in FacetedResponse - self.filter_values[name] = filter_values - - # get the filter from the facet - f = self.facets[name].add_filter(filter_values) - if f is None: - return - - self._filters[name] = f - - def search(self): - """ - Construct the Search object. - """ - s = Search(doc_type=self.doc_types, index=self.index) - return s.response_class(partial(FacetedResponse, self)) - - def query(self, search, query): - """ - Add query part to ``search``. - - Override this if you wish to customize the query used. - """ - if query: - return search.query('multi_match', fields=self.fields, query=query) - return search - - def aggregate(self, search): - """ - Add aggregations representing the facets selected, including potential - filters. - """ - for f, facet in iteritems(self.facets): - agg = facet.get_aggregation() - agg_filter = Q('match_all') - for field, filter in iteritems(self._filters): - if f == field: - continue - agg_filter &= filter - search.aggs.bucket( - '_filter_' + f, - 'filter', - filter=agg_filter - ).bucket(f, agg) - - def filter(self, search): - """ - Add a ``post_filter`` to the search request narrowing the results based - on the facet filters. - """ - post_filter = Q('match_all') - for f in itervalues(self._filters): - post_filter &= f - return search.post_filter(post_filter) - - def highlight(self, search): - """ - Add highlighting for all the fields - """ - return search.highlight(*self.fields) - - def build_search(self): - """ - Construct the ``Search`` object. - """ - s = self.search() - s = self.query(s, self._query) - s = self.filter(s) - s = self.highlight(s) - self.aggregate(s) - return s - - def execute(self): - return self._s.execute() diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/field.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/field.py deleted file mode 100644 index 30bb85a..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/field.py +++ /dev/null @@ -1,256 +0,0 @@ -from datetime import date -from dateutil import parser -from six import itervalues - -from .utils import DslBase, _make_dsl_class, ObjectBase, AttrDict, AttrList -from .exceptions import ValidationException - -__all__ = [ - 'construct_field', 'Field', 'Object', 'Nested', 'Date', 'String', 'Float', 'Double', - 'Byte', 'Short', 'Integer', 'Long', 'Boolean', 'Ip', 'Attachment', - 'GeoPoint', 'GeoShape', 'InnerObjectWrapper' -] - -def construct_field(name_or_field, **params): - # {"type": "string", "index": "not_analyzed"} - if isinstance(name_or_field, dict): - if params: - raise ValueError('construct_field() cannot accept parameters when passing in a dict.') - params = name_or_field.copy() - if 'type' not in params: - # inner object can be implicitly defined - if 'properties' in params: - name = 'object' - else: - raise ValueError('construct_field() needs to have a "type" key.') - else: - name = params.pop('type') - return Field.get_dsl_class(name)(**params) - - # String() - if isinstance(name_or_field, Field): - if params: - raise ValueError('construct_field() cannot accept parameters when passing in a construct_field object.') - return name_or_field - - # "string", index="not_analyzed" - return Field.get_dsl_class(name_or_field)(**params) - -class Field(DslBase): - _type_name = 'field' - _type_shortcut = staticmethod(construct_field) - # all fields can be multifields - _param_defs = {'fields': {'type': 'field', 'hash': True}} - name = None - _coerce = False - - def __init__(self, *args, **kwargs): - self._multi = kwargs.pop('multi', False) - self._required = kwargs.pop('required', False) - super(Field, self).__init__(*args, **kwargs) - - def _serialize(self, data): - return data - - def _deserialize(self, data): - return data - - def _empty(self): - return None - - def empty(self): - if self._multi: - return AttrList([]) - return self._empty() - - def serialize(self, data): - if isinstance(data, (list, AttrList)): - data[:] = map(self._serialize, data) - return data - return self._serialize(data) - - def deserialize(self, data): - if isinstance(data, (list, AttrList)): - data[:] = map(self._deserialize, data) - return data - return self._deserialize(data) - - def clean(self, data): - if data is not None: - data = self.deserialize(data) - # FIXME: numeric 0 - if not data and self._required: - raise ValidationException("Value required for this field.") - return data - - def to_dict(self): - d = super(Field, self).to_dict() - name, value = d.popitem() - value['type'] = name - return value - -class CustomField(Field): - name = 'custom' - _coerce = True - - def to_dict(self): - if isinstance(self.builtin_type, Field): - return self.builtin_type.to_dict() - - d = super(CustomField, self).to_dict() - d['type'] = self.builtin_type - return d - -class InnerObjectWrapper(ObjectBase): - def __init__(self, mapping, **kwargs): - # mimic DocType behavior with _doc_type.mapping - super(AttrDict, self).__setattr__('_doc_type', type('Meta', (), {'mapping': mapping})) - super(InnerObjectWrapper, self).__init__(**kwargs) - - -class InnerObject(object): - " Common functionality for nested and object fields. " - _param_defs = {'properties': {'type': 'field', 'hash': True}} - _coerce = True - - def __init__(self, *args, **kwargs): - self._doc_class = kwargs.pop('doc_class', InnerObjectWrapper) - super(InnerObject, self).__init__(*args, **kwargs) - - def field(self, name, *args, **kwargs): - self.properties[name] = construct_field(*args, **kwargs) - return self - # XXX: backwards compatible, will be removed - property = field - - def _empty(self): - return self._doc_class(self.properties) - - def empty(self): - if self._multi: - return AttrList([], lambda d: self._doc_class(self.properties, **d)) - return self._empty() - - def __getitem__(self, name): - return self.properties[name] - - def __contains__(self, name): - return name in self.properties - - def _collect_fields(self): - " Iterate over all Field objects within, including multi fields. " - for f in itervalues(self.properties.to_dict()): - yield f - # multi fields - if hasattr(f, 'fields'): - for inner_f in itervalues(f.fields.to_dict()): - yield inner_f - # nested and inner objects - if hasattr(f, '_collect_fields'): - for inner_f in f._collect_fields(): - yield inner_f - - def update(self, other_object): - if not hasattr(other_object, 'properties'): - # not an inner/nested object, no merge possible - return - - our, other = self.properties, other_object.properties - for name in other: - if name in our: - if hasattr(our[name], 'update'): - our[name].update(other[name]) - continue - our[name] = other[name] - - def _deserialize(self, data): - if data is None: - return None - # don't wrap already wrapped data - if isinstance(data, self._doc_class): - return data - - if isinstance(data, (list, AttrList)): - data[:] = list(map(self._deserialize, data)) - return data - - if isinstance(data, AttrDict): - data = data._d_ - - return self._doc_class(self.properties, **data) - - def _serialize(self, data): - return data.to_dict() - - def clean(self, data): - data = super(InnerObject, self).clean(data) - if data is None: - return None - if isinstance(data, (list, AttrList)): - for d in data: - d.full_clean() - else: - data.full_clean() - return data - - -class Object(InnerObject, Field): - name = 'object' - -class Nested(InnerObject, Field): - name = 'nested' - - def __init__(self, *args, **kwargs): - # change the default for Nested fields - kwargs.setdefault('multi', True) - super(Nested, self).__init__(*args, **kwargs) - -class Date(Field): - name = 'date' - _coerce = True - - def _deserialize(self, data): - if not data: - return None - if isinstance(data, date): - return data - - try: - # TODO: add format awareness - return parser.parse(data) - except Exception as e: - raise ValidationException('Could not parse date from the value (%r)' % data, e) - -class String(Field): - _param_defs = { - 'fields': {'type': 'field', 'hash': True}, - 'analyzer': {'type': 'analyzer'}, - 'index_analyzer': {'type': 'analyzer'}, - 'search_analyzer': {'type': 'analyzer'}, - } - name = 'string' - - def _empty(self): - return '' - -FIELDS = ( - 'float', - 'double', - 'byte', - 'short', - 'integer', - 'long', - 'boolean', - 'ip', - 'attachment', - 'geo_point', - 'geo_shape', - 'completion', -) - -# generate the query classes dynamicaly -for f in FIELDS: - fclass = _make_dsl_class(Field, f) - globals()[fclass.__name__] = fclass - __all__.append(fclass.__name__) - diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/filter.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/filter.py deleted file mode 100644 index d462bcd..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/filter.py +++ /dev/null @@ -1,180 +0,0 @@ -from .utils import DslBase, BoolMixin, _make_dsl_class - -__all__ = [ - 'F', 'And', 'AndOrFilter', 'Bool', 'EMPTY_FILTER', 'Exists', 'Filter', - 'Fquery', 'GeoBoundingBox', 'GeoDistance', 'GeoDistanceRange', - 'GeoPolygon', 'GeoShape', 'GeohashCell', 'HasChild', 'HasParent', 'Ids', - 'Indices', 'Limit', 'MatchAll', 'Missing', 'Nested', 'Not', 'Or', 'Prefix', - 'Query', 'Range', 'Regexp', 'Script', 'Term', 'Terms', 'Type' -] - - -def F(name_or_filter='match_all', filters=None, **params): - # 'and/or', [F(), F()] - if filters is not None: - # someone passed in positional argument to F outside of and/or or query - if name_or_filter in ('and', 'or'): - params['filters'] = filters - elif name_or_filter == 'query': - params['query'] = filters - else: - raise ValueError("Filter %r doesn't accept positional argument." % name_or_filter) - - # {"term": {...}} - if isinstance(name_or_filter, dict): - if params: - raise ValueError('F() cannot accept parameters when passing in a dict.') - if len(name_or_filter) != 1: - raise ValueError('F() can only accept dict with a single filter ({"bool": {...}}). ' - 'Instead it got (%r)' % name_or_filter) - name, params = name_or_filter.copy().popitem() - if isinstance(params, dict): - return Filter.get_dsl_class(name)(**params) - else: - # and filter can have list - return Filter.get_dsl_class(name)(params) - - # Term(...) - if isinstance(name_or_filter, Filter): - if params: - raise ValueError('F() cannot accept parameters when passing in a Filter object.') - return name_or_filter - - # s.filter = ~F(s.filter) - if hasattr(name_or_filter, '_proxied'): - return name_or_filter._proxied - - # 'term', tag='python', ... - return Filter.get_dsl_class(name_or_filter)(**params) - -class Filter(DslBase): - _type_name = 'filter' - _type_shortcut = staticmethod(F) - name = None - -class MatchAll(Filter): - name = 'match_all' - def __add__(self, other): - return other._clone() - __and__ = __rand__ = __radd__ = __add__ - - def __or__(self, other): - return self - __ror__ = __or__ -EMPTY_FILTER = MatchAll() - -class Bool(BoolMixin, Filter): - name = 'bool' - _param_defs = { - 'must': {'type': 'filter', 'multi': True}, - 'should': {'type': 'filter', 'multi': True}, - 'must_not': {'type': 'filter', 'multi': True}, - } - - def __and__(self, other): - f = self._clone() - if isinstance(other, self.__class__): - f.must += other.must - f.must_not += other.must_not - f.should = [] - if self.should and other.should: - selfshould, othershould = self.should[:], other.should[:] - # required subfilter, move to must - for s in (selfshould, othershould): - if len(s) == 1: - f.must.append(s.pop()) - - # we have leftover lists, nothing to do but add to must as bool(should) - if selfshould and othershould: - f.must.extend(( - Bool(should=selfshould), - Bool(should=othershould), - )) - # at most one should list is left, keep as should - else: - f.should = selfshould + othershould - - # at most one should list is left, keep as should - else: - f.should = self.should + other.should - else: - f.must.append(other) - return f - __rand__ = __and__ - -# register this as Bool for Filter -Filter._bool = Bool - -class Not(Filter): - name = 'not' - _param_defs = {'filter': {'type': 'filter'}} - - def __init__(self, filter=None, **kwargs): - if filter is None: - filter, kwargs = kwargs, {} - super(Not, self).__init__(filter=filter, **kwargs) - -class AndOrFilter(object): - _param_defs = {'filters': {'type': 'filter', 'multi': True}} - - def __init__(self, filters=None, **kwargs): - if filters is not None: - kwargs['filters'] = filters - super(AndOrFilter, self).__init__(**kwargs) - - # compound filters -class And(AndOrFilter, Filter): - name = 'and' - -class Or(AndOrFilter, Filter): - name = 'or' - -class Query(Filter): - name = 'query' - _param_defs = {'query': {'type': 'query'}} - - def __init__(self, query=None, **kwargs): - if query is not None: - kwargs['query'] = query - super(Query, self).__init__(**kwargs) - - def to_dict(self): - d = super(Query, self).to_dict() - d[self.name].update(d[self.name].pop('query', {})) - return d - - -FILTERS = ( - # relationships - ('nested', {'filter': {'type': 'filter'}}), - ('has_child', {'filter': {'type': 'filter'}}), - ('has_parent', {'filter': {'type': 'filter'}}), - - ('fquery', {'query': {'type': 'query'}}), - - # core filters - ('exists', None), - ('geo_bounding_box', None), - ('geo_distance', None), - ('geo_distance_range', None), - ('geo_polygon', None), - ('geo_shape', None), - ('geohash_cell', None), - ('ids', None), - ('indices', None), - ('limit', None), - ('missing', None), - ('prefix', None), - ('range', None), - ('regexp', None), - ('script', None), - ('term', None), - ('terms', None), - ('type', None), -) - -# generate the filter classes dynamicaly -for fname, params_def in FILTERS: - fclass = _make_dsl_class(Filter, fname, params_def) - globals()[fclass.__name__] = fclass - diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/function.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/function.py deleted file mode 100644 index 688b226..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/function.py +++ /dev/null @@ -1,86 +0,0 @@ -from .utils import DslBase - -def SF(name_or_sf, **params): - # {"script_score": {"script": "_score"}, "filter": {}} - if isinstance(name_or_sf, dict): - if params: - raise ValueError('SF() cannot accept parameters when passing in a dict.') - kwargs = {} - sf = name_or_sf.copy() - for k in ScoreFunction._param_defs: - if k in name_or_sf: - kwargs[k] = sf.pop(k) - - # not sf, so just filter+weight, which used to be boost factor - if not sf: - name = 'boost_factor' - # {'FUNCTION': {...}} - elif len(sf) == 1: - name, params = sf.popitem() - else: - raise ValueError('SF() got an unexpected fields in the dictionary: %r' % sf) - - # boost factor special case, see elasticsearch #6343 - if not isinstance(params, dict): - params = {'value': params} - - # mix known params (from _param_defs) and from inside the function - kwargs.update(params) - return ScoreFunction.get_dsl_class(name)(**kwargs) - - # ScriptScore(script="_score", filter=Q()) - if isinstance(name_or_sf, ScoreFunction): - if params: - raise ValueError('SF() cannot accept parameters when passing in a ScoreFunction object.') - return name_or_sf - - # "script_score", script="_score", filter=Q() - return ScoreFunction.get_dsl_class(name_or_sf)(**params) - -class ScoreFunction(DslBase): - _type_name = 'score_function' - _type_shortcut = staticmethod(SF) - _param_defs = { - 'query': {'type': 'query'}, - 'filter': {'type': 'query'}, - 'weight': {} - } - name = None - - def to_dict(self): - d = super(ScoreFunction, self).to_dict() - # filter and query dicts should be at the same level as us - for k in self._param_defs: - if k in d[self.name]: - d[k] = d[self.name].pop(k) - return d - -class ScriptScore(ScoreFunction): - name = 'script_score' - -class BoostFactor(ScoreFunction): - name = 'boost_factor' - - def to_dict(self): - d = super(BoostFactor, self).to_dict() - if 'value' in d[self.name]: - d[self.name] = d[self.name].pop('value') - else: - del d[self.name] - return d - -class RandomScore(ScoreFunction): - name = 'random_score' - -class FieldValueFactor(ScoreFunction): - name = 'field_value_factor' - -class Linear(ScoreFunction): - name = 'linear' - -class Gauss(ScoreFunction): - name = 'gauss' - -class Exp(ScoreFunction): - name = 'exp' - diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/index.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/index.py deleted file mode 100644 index f4e7522..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/index.py +++ /dev/null @@ -1,91 +0,0 @@ -from .connections import connections -from .search import Search - -class Index(object): - def __init__(self, name, using='default'): - self._name = name - self._doc_types = {} - self._mappings = {} - self._using = using - self._settings = {} - self._aliases = {} - - def clone(self, name, using=None): - i = Index(name, using=using or self._using) - for attr in ('_doc_types', '_mappings', '_settings', '_aliases'): - setattr(i, attr, getattr(self, attr).copy()) - return i - - def _get_connection(self): - return connections.get_connection(self._using) - connection = property(_get_connection) - - def doc_type(self, doc_type): - name = doc_type._doc_type.name - self._doc_types[name] = doc_type - self._mappings[name] = doc_type._doc_type.mapping - - if not doc_type._doc_type.index: - doc_type._doc_type.index = self._name - return doc_type # to use as decorator??? - - def settings(self, **kwargs): - self._settings.update(kwargs) - return self - - def aliases(self, **kwargs): - self._aliases.update(kwargs) - return self - - def search(self): - return Search( - using=self._using, - index=self._name, - doc_type=[self._doc_types.get(k, k) for k in self._mappings] - ) - - def _get_mappings(self): - analysis, mappings = {}, {} - for mapping in self._mappings.values(): - mappings.update(mapping.to_dict()) - a = mapping._collect_analysis() - # merge the defintion - # TODO: conflict detection/resolution - for key in a: - analysis.setdefault(key, {}).update(a[key]) - - return mappings, analysis - - def to_dict(self): - out = {} - if self._settings: - out['settings'] = self._settings - if self._aliases: - out['aliases'] = self._aliases - mappings, analysis = self._get_mappings() - if mappings: - out['mappings'] = mappings - if analysis: - out.setdefault('settings', {})['analysis'] = analysis - return out - - def exists(self, **kwargs): - return self.connection.indices.exists(index=self._name, **kwargs) - - def refresh(self, **kwargs): - return self.connection.indices.refresh(index=self._name, **kwargs) - - def flush(self, **kwargs): - return self.connection.indices.flush(index=self._name, **kwargs) - - def open(self, **kwargs): - return self.connection.indices.open(index=self._name, **kwargs) - - def close(self, **kwargs): - return self.connection.indices.close(index=self._name, **kwargs) - - def create(self, **kwargs): - self.connection.indices.create(index=self._name, body=self.to_dict(), **kwargs) - - def delete(self, **kwargs): - self.connection.indices.delete(index=self._name, **kwargs) diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/mapping.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/mapping.py deleted file mode 100644 index e25cd81..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/mapping.py +++ /dev/null @@ -1,151 +0,0 @@ -from six import iteritems -from itertools import chain - -from .utils import DslBase -from .field import InnerObject, String -from .connections import connections -from .exceptions import IllegalOperation - -META_FIELDS = frozenset(( - 'dynamic', 'transform', 'dynamic_date_formats', 'date_detection', - 'numeric_detection', 'dynamic_templates' -)) - -class Properties(InnerObject, DslBase): - def __init__(self, name): - self._name = name - super(Properties, self).__init__() - - def __repr__(self): - return 'Properties(%r)' % self._name - - @property - def name(self): - return self._name - - -class Mapping(object): - def __init__(self, name): - self.properties = Properties(name) - self._meta = {} - - def __repr__(self): - return 'Mapping(%r)' % self.doc_type - - @classmethod - def from_es(cls, index, doc_type, using='default'): - m = cls(doc_type) - m.update_from_es(index, using) - return m - - def _collect_analysis(self): - analysis = {} - fields = [] - if '_all' in self._meta: - fields.append(String(**self._meta['_all'])) - - for f in chain(fields, self.properties._collect_fields()): - for analyzer_name in ('analyzer', 'index_analyzer', 'search_analyzer'): - if not hasattr(f, analyzer_name): - continue - analyzer = getattr(f, analyzer_name) - d = analyzer.get_analysis_definition() - # empty custom analyzer, probably already defined out of our control - if not d: - continue - - # merge the defintion - # TODO: conflict detection/resolution - for key in d: - analysis.setdefault(key, {}).update(d[key]) - - return analysis - - def save(self, index, using='default'): - # TODO: replace with creating an Index instance to avoid duplication - es = connections.get_connection(using) - if not es.indices.exists(index=index): - es.indices.create(index=index, body={'mappings': self.to_dict(), 'settings': {'analysis': self._collect_analysis()}}) - else: - analysis = self._collect_analysis() - if analysis: - if es.cluster.state(index=index, metric='metadata')['metadata']['indices'][index]['state'] != 'close': - # TODO: check if the analysis config is already there - raise IllegalOperation( - 'You cannot update analysis configuration on an open index, you need to close index %s first.' % index) - es.indices.put_settings(index=index, body={'analysis': analysis}) - es.indices.put_mapping(index=index, doc_type=self.doc_type, body=self.to_dict()) - - def update_from_es(self, index, using='default'): - es = connections.get_connection(using) - raw = es.indices.get_mapping(index=index, doc_type=self.doc_type) - _, raw = raw.popitem() - raw = raw['mappings'][self.doc_type] - - for name, definition in iteritems(raw['properties']): - self.field(name, definition) - - # metadata like _all etc - for name, value in iteritems(raw): - if name != 'properties': - if isinstance(value, dict): - self.meta(name, **value) - else: - self.meta(name, value) - - def update(self, mapping, update_only=False): - for name in mapping: - if update_only and name in self: - # nested and inner objects, merge recursively - if hasattr(self[name], 'update'): - self[name].update(mapping[name]) - continue - self.field(name, mapping[name]) - - if update_only: - for name in mapping._meta: - if name not in self._meta: - self._meta[name] = mapping._meta[name] - else: - self._meta.update(mapping._meta) - - def __contains__(self, name): - return name in self.properties.properties - - def __getitem__(self, name): - return self.properties.properties[name] - - def __iter__(self): - return iter(self.properties.properties) - - @property - def doc_type(self): - return self.properties.name - - def field(self, *args, **kwargs): - self.properties.field(*args, **kwargs) - return self - - def meta(self, name, params=None, **kwargs): - if not name.startswith('_') and name not in META_FIELDS: - name = '_' + name - - if params and kwargs: - raise ValueError('Meta configs cannot have both value and a dictionary.') - - self._meta[name] = kwargs if params is None else params - return self - - def to_dict(self): - d = self.properties.to_dict() - meta = self._meta - - # hard coded serialization of analyzers in _all - if '_all' in meta: - meta = meta.copy() - _all = meta['_all'] = meta['_all'].copy() - for f in ('analyzer', 'search_analyzer', 'index_analyzer'): - if hasattr(_all.get(f, None), 'to_dict'): - _all[f] = _all[f].to_dict() - d[self.doc_type].update(meta) - return d diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/query.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/query.py deleted file mode 100644 index d50aeb2..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/query.py +++ /dev/null @@ -1,233 +0,0 @@ -from .utils import DslBase, _make_dsl_class -from .function import SF, ScoreFunction - -__all__ = [ - 'Q', 'Bool', 'Boosting', 'Common', 'ConstantScore', 'DisMax', 'Filtered', - 'FunctionScore', 'Fuzzy', 'FuzzyLikeThis', 'FuzzyLikeThisField', - 'GeoShape', 'HasChild', 'HasParent', 'Ids', 'Indices', 'Match', 'MatchAll', - 'MatchPhrase', 'MatchPhrasePrefix', 'MoreLikeThis', 'MoreLikeThisField', - 'MultiMatch', 'Nested', 'Prefix', 'Query', 'QueryString', 'Range', - 'Regexp', 'SF', 'ScoreFunction', 'SimpleQueryString', 'SpanFirst', - 'SpanMulti', 'SpanNear', 'SpanNot', 'SpanOr', 'SpanTerm', 'Template', - 'Term', 'Terms', 'TopChildren', 'Wildcard' -] - - -def Q(name_or_query='match_all', **params): - # {"match": {"title": "python"}} - if isinstance(name_or_query, dict): - if params: - raise ValueError('Q() cannot accept parameters when passing in a dict.') - if len(name_or_query) != 1: - raise ValueError('Q() can only accept dict with a single query ({"match": {...}}). ' - 'Instead it got (%r)' % name_or_query) - name, params = name_or_query.copy().popitem() - return Query.get_dsl_class(name)(**params) - - # MatchAll() - if isinstance(name_or_query, Query): - if params: - raise ValueError('Q() cannot accept parameters when passing in a Query object.') - return name_or_query - - # s.query = Q('filtered', query=s.query) - if hasattr(name_or_query, '_proxied'): - return name_or_query._proxied - - # "match", title="python" - return Query.get_dsl_class(name_or_query)(**params) - -class Query(DslBase): - _type_name = 'query' - _type_shortcut = staticmethod(Q) - name = None - - def __add__(self, other): - # make sure we give queries that know how to combine themselves - # preference - if hasattr(other, '__radd__'): - return other.__radd__(self) - return Bool(must=[self, other]) - - def __invert__(self): - return Bool(must_not=[self]) - - def __or__(self, other): - # make sure we give queries that know how to combine themselves - # preference - if hasattr(other, '__ror__'): - return other.__ror__(self) - return Bool(should=[self, other]) - - def __and__(self, other): - # make sure we give queries that know how to combine themselves - # preference - if hasattr(other, '__rand__'): - return other.__rand__(self) - return Bool(must=[self, other]) - - -class MatchAll(Query): - name = 'match_all' - def __add__(self, other): - return other._clone() - __and__ = __rand__ = __radd__ = __add__ - - def __or__(self, other): - return self - __ror__ = __or__ -EMPTY_QUERY = MatchAll() - -class Bool(Query): - name = 'bool' - _param_defs = { - 'must': {'type': 'query', 'multi': True}, - 'should': {'type': 'query', 'multi': True}, - 'must_not': {'type': 'query', 'multi': True}, - 'filter': {'type': 'query', 'multi': True}, - } - - def __add__(self, other): - q = self._clone() - if isinstance(other, Bool): - q.must += other.must - q.should += other.should - q.must_not += other.must_not - q.filter += other.filter - else: - q.must.append(other) - return q - __radd__ = __add__ - - def __or__(self, other): - for q in (self, other): - if isinstance(q, Bool) and len(q.should) == 1 and not any((q.must, q.must_not, q.filter)): - other = self if q is other else other - q = q._clone() - q.should.append(other) - return q - - return Bool(should=[self, other]) - __ror__ = __or__ - - def __invert__(self): - # special case for single negated query - if not (self.must or self.should or self.filter) and len(self.must_not) == 1: - return self.must_not[0]._clone() - - # bol without should, just flip must and must_not - elif not self.should: - q = self._clone() - q.must, q.must_not = q.must_not, q.must - if q.filter: - q.filter = [Bool(must_not=q.filter)] - return q - - # TODO: should -> must_not.append(Bool(should=self.should)) ?? - # queries with should just invert normally - return super(Bool, self).__invert__() - - def __and__(self, other): - q = self._clone() - if isinstance(other, Bool): - q.must += other.must - q.must_not += other.must_not - q.filter += other.filter - q.should = [] - for qx in (self, other): - # TODO: percetages will fail here - min_should_match = getattr(qx, 'minimum_should_match', 0 if qx.must else 1) - # all subqueries are required - if len(qx.should) <= min_should_match: - q.must.extend(qx.should) - # not all of them are required, use it and remember min_should_match - elif not q.should: - q.minimum_should_match = min_should_match - q.should = qx.should - # not all are required, add a should list to the must with proper min_should_match - else: - q.must.append(Bool(should=qx.should, minimum_should_match=min_should_match)) - else: - q.must.append(other) - return q - __rand__ = __and__ - -class FunctionScore(Query): - name = 'function_score' - _param_defs = { - 'query': {'type': 'query'}, - 'filter': {'type': 'query'}, - 'functions': {'type': 'score_function', 'multi': True}, - } - - def __init__(self, **kwargs): - if 'functions' in kwargs: - pass - else: - fns = kwargs['functions'] = [] - for name in ScoreFunction._classes: - if name in kwargs: - fns.append({name: kwargs.pop(name)}) - super(FunctionScore, self).__init__(**kwargs) - -QUERIES = ( - # compound queries - ('boosting', {'positive': {'type': 'query'}, 'negative': {'type': 'query'}}), - ('constant_score', {'query': {'type': 'query'}, 'filter': {'type': 'query'}}), - ('dis_max', {'queries': {'type': 'query', 'multi': True}}), - ('filtered', {'query': {'type': 'query'}, 'filter': {'type': 'query'}}), - ('indices', {'query': {'type': 'query'}, 'no_match_query': {'type': 'query'}}), - - # relationship queries - ('nested', {'query': {'type': 'query'}}), - ('has_child', {'query': {'type': 'query'}}), - ('has_parent', {'query': {'type': 'query'}}), - ('top_children', {'query': {'type': 'query'}}), - - # compount span queries - ('span_first', {'match': {'type': 'query'}}), - ('span_multi', {'match': {'type': 'query'}}), - ('span_near', {'clauses': {'type': 'query', 'multi': True}}), - ('span_not', {'exclude': {'type': 'query'}, 'include': {'type': 'query'}}), - ('span_or', {'clauses': {'type': 'query', 'multi': True}}), - - # core queries - ('common', None), - ('fuzzy', None), - ('fuzzy_like_this', None), - ('fuzzy_like_this_field', None), - ('geo_bounding_box', None), - ('geo_distance', None), - ('geo_distance_range', None), - ('geo_polygon', None), - ('geo_shape', None), - ('geohash_cell', None), - ('ids', None), - ('limit', None), - ('match', None), - ('match_phrase', None), - ('match_phrase_prefix', None), - ('exists', None), - ('missing', None), - ('more_like_this', None), - ('more_like_this_field', None), - ('multi_match', None), - ('prefix', None), - ('query_string', None), - ('range', None), - ('regexp', None), - ('simple_query_string', None), - ('span_term', None), - ('template', None), - ('term', None), - ('terms', None), - ('wildcard', None), - ('script', None), - ('type', None), -) - -# generate the query classes dynamicaly -for qname, params_def in QUERIES: - qclass = _make_dsl_class(Query, qname, params_def) - globals()[qclass.__name__] = qclass - diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/result.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/result.py deleted file mode 100644 index 6de2ea8..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/result.py +++ /dev/null @@ -1,82 +0,0 @@ -from six import iteritems, u - -from .utils import AttrDict, AttrList - -class SuggestResponse(AttrDict): - def success(self): - return not self._shards.failed - -class Response(AttrDict): - def __init__(self, response, callbacks=None): - super(AttrDict, self).__setattr__('_callbacks', callbacks or {}) - super(Response, self).__init__(response) - - def __iter__(self): - return iter(self.hits) - - def __getitem__(self, key): - # for slicing etc - return self.hits[key] - - def __nonzero__(self): - return bool(self.hits) - __bool__ = __nonzero__ - - def __repr__(self): - return '' % self.hits - - def __len__(self): - return len(self.hits) - - def success(self): - return self._shards.total == self._shards.successful and not self.timed_out - - def _get_result(self, hit): - dt = hit['_type'] - return self._callbacks.get(dt, Result)(hit) - - @property - def hits(self): - if not hasattr(self, '_hits'): - h = self._d_['hits'] - - try: - hits = AttrList(map(self._get_result, h['hits'])) - except AttributeError as e: - # avoid raising AttributeError since it will be hidden by the property - raise TypeError("Could not parse hits.", e) - - # avoid assigning _hits into self._d_ - super(AttrDict, self).__setattr__('_hits', hits) - for k in h: - setattr(self._hits, k, h[k]) - return self._hits - - -class ResultMeta(AttrDict): - def __init__(self, document, exclude=('_source', '_fields')): - d = dict((k[1:] if k.startswith('_') else k, v) for (k, v) in iteritems(document) if k not in exclude) - if 'type' in d: - # make sure we are consistent everywhere in python - d['doc_type'] = d.pop('type') - super(ResultMeta, self).__init__(d) - -class Result(AttrDict): - def __init__(self, document): - data = {} - if '_source' in document: - data = document['_source'] - if 'fields' in document: - data.update(document['fields']) - - super(Result, self).__init__(data) - # assign meta as attribute and not as key in self._d_ - super(AttrDict, self).__setattr__('meta', ResultMeta(document)) - - def __dir__(self): - # be sure to expose meta in dir(self) - return super(Result, self).__dir__() + ['meta'] - - def __repr__(self): - return u('') % ( - self.meta.index, self.meta.doc_type, self.meta.id, super(Result, self).__repr__()) diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/search.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/search.py deleted file mode 100644 index d6132d3..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/search.py +++ /dev/null @@ -1,676 +0,0 @@ -from six import iteritems, string_types - -from elasticsearch.helpers import scan - -from .query import Q, EMPTY_QUERY, Bool -from .aggs import A, AggBase -from .utils import DslBase -from .result import Response, Result, SuggestResponse -from .connections import connections - -class QueryProxy(object): - """ - Simple proxy around DSL objects (queries) that can be called - (to add query/post_filter) and also allows attribute access which is proxied to - the wrapped query. - """ - def __init__(self, search, attr_name): - self._search = search - self._proxied = EMPTY_QUERY - self._attr_name = attr_name - - def __nonzero__(self): - return self._proxied != EMPTY_QUERY - __bool__ = __nonzero__ - - def __call__(self, *args, **kwargs): - s = self._search._clone() - getattr(s, self._attr_name)._proxied += Q(*args, **kwargs) - - # always return search to be chainable - return s - - def __getattr__(self, attr_name): - return getattr(self._proxied, attr_name) - - def __setattr__(self, attr_name, value): - if not attr_name.startswith('_'): - self._proxied = Q(self._proxied.to_dict()) - setattr(self._proxied, attr_name, value) - super(QueryProxy, self).__setattr__(attr_name, value) - - -class ProxyDescriptor(object): - """ - Simple descriptor to enable setting of queries and filters as: - - s = Search() - s.query = Q(...) - - """ - def __init__(self, name): - self._attr_name = '_%s_proxy' % name - - def __get__(self, instance, owner): - return getattr(instance, self._attr_name) - - def __set__(self, instance, value): - proxy = getattr(instance, self._attr_name) - proxy._proxied = Q(value) - - -class AggsProxy(AggBase, DslBase): - name = 'aggs' - def __init__(self, search): - self._base = self._search = search - self._params = {'aggs': {}} - - def to_dict(self): - return super(AggsProxy, self).to_dict().get('aggs', {}) - -class Request(object): - def __init__(self, using='default', index=None, doc_type=None, extra=None): - self._using = using - - self._index = None - if isinstance(index, (tuple, list)): - self._index = list(index) - elif index: - self._index = [index] - - self._doc_type = [] - self._doc_type_map = {} - if isinstance(doc_type, (tuple, list)): - for dt in doc_type: - self._add_doc_type(dt) - elif isinstance(doc_type, dict): - self._doc_type.extend(doc_type.keys()) - self._doc_type_map.update(doc_type) - elif doc_type: - self._add_doc_type(doc_type) - - self._params = {} - self._extra = extra or {} - - def params(self, **kwargs): - """ - Specify query params to be used when executing the search. All the - keyword arguments will override the current values. See - http://elasticsearch-py.readthedocs.org/en/master/api.html#elasticsearch.Elasticsearch.search - for all availible parameters. - - Example:: - - s = Search() - s = s.params(routing='user-1', preference='local') - """ - s = self._clone() - s._params.update(kwargs) - return s - - def index(self, *index): - """ - Set the index for the search. If called empty it will rmove all information. - - Example: - - s = Search() - s = s.index('twitter-2015.01.01', 'twitter-2015.01.02') - """ - # .index() resets - s = self._clone() - if not index: - s._index = None - else: - s._index = (self._index or []) + list(index) - return s - - def _add_doc_type(self, doc_type): - if hasattr(doc_type, '_doc_type'): - self._doc_type_map[doc_type._doc_type.name] = doc_type.from_es - doc_type = doc_type._doc_type.name - self._doc_type.append(doc_type) - - def doc_type(self, *doc_type, **kwargs): - """ - Set the type to search through. You can supply a single value or - multiple. Values can be strings or subclasses of ``DocType``. - - You can also pass in any keyword arguments, mapping a doc_type to a - callback that should be used instead of the Result class. - - If no doc_type is supplied any information stored on the instance will - be erased. - - Example: - - s = Search().doc_type('product', 'store', User, custom=my_callback) - """ - # .doc_type() resets - s = self._clone() - if not doc_type and not kwargs: - s._doc_type = [] - s._doc_type_map = {} - else: - for dt in doc_type: - s._add_doc_type(dt) - s._doc_type.extend(kwargs.keys()) - s._doc_type_map.update(kwargs) - return s - - def using(self, client): - """ - Associate the search request with an elasticsearch client. A fresh copy - will be returned with current instance remaining unchanged. - - :arg client: an instance of ``elasticsearch.Elasticsearch`` to use or - an alias to look up in ``elasticsearch_dsl.connections`` - - """ - s = self._clone() - s._using = client - return s - - def extra(self, **kwargs): - """ - Add extra keys to the request body. Mostly here for backwards - compatibility. - """ - s = self._clone() - if 'from_' in kwargs: - kwargs['from'] = kwargs.pop('from_') - s._extra.update(kwargs) - return s - - def _clone(self): - s = self.__class__(using=self._using, index=self._index, - doc_type=self._doc_type) - s._doc_type_map = self._doc_type_map.copy() - s._extra = self._extra.copy() - s._params = self._params.copy() - return s - - -class Search(Request): - query = ProxyDescriptor('query') - post_filter = ProxyDescriptor('post_filter') - - def __init__(self, **kwargs): - """ - Search request to elasticsearch. - - :arg using: `Elasticsearch` instance to use - :arg index: limit the search to index - :arg doc_type: only query this type. - - All the paramters supplied (or omitted) at creation type can be later - overriden by methods (`using`, `index` and `doc_type` respectively). - """ - super(Search, self).__init__(**kwargs) - - self.aggs = AggsProxy(self) - self._sort = [] - self._fields = None - self._partial_fields = {} - self._highlight = {} - self._highlight_opts = {} - self._suggest = {} - self._script_fields = {} - self._response_class = Response - - self._query_proxy = QueryProxy(self, 'query') - self._post_filter_proxy = QueryProxy(self, 'post_filter') - - def filter(self, *args, **kwargs): - return self.query(Bool(filter=[Q(*args, **kwargs)])) - - def __iter__(self): - """ - Iterate over the hits. - """ - return iter(self.execute()) - - def __getitem__(self, n): - """ - Support slicing the `Search` instance for pagination. - - Slicing equates to the from/size parameters. E.g.:: - - s = Search().query(...)[0:25] - - is equivalent to:: - - s = Search().query(...).extra(from_=0, size=25) - - """ - s = self._clone() - - if isinstance(n, slice): - # If negative slicing, abort. - if n.start and n.start < 0 or n.stop and n.stop < 0: - raise ValueError("Search does not support negative slicing.") - # Elasticsearch won't get all results so we default to size: 10 if - # stop not given. - s._extra['from'] = n.start or 0 - s._extra['size'] = n.stop - (n.start or 0) if n.stop is not None else 10 - return s - else: # This is an index lookup, equivalent to slicing by [n:n+1]. - # If negative index, abort. - if n < 0: - raise ValueError("Search does not support negative indexing.") - s._extra['from'] = n - s._extra['size'] = 1 - return s - - @classmethod - def from_dict(cls, d): - """ - Construct a `Search` instance from a raw dict containing the search - body. Useful when migrating from raw dictionaries. - - Example:: - - s = Search.from_dict({ - "query": { - "bool": { - "must": [...] - } - }, - "aggs": {...} - }) - s = s.filter('term', published=True) - """ - s = cls() - s.update_from_dict(d) - return s - - def _clone(self): - """ - Return a clone of the current search request. Performs a shallow copy - of all the underlying objects. Used internally by most state modifying - APIs. - """ - s = super(Search, self)._clone() - - s._response_class = self._response_class - s._sort = self._sort[:] - s._fields = self._fields[:] if self._fields is not None else None - s._partial_fields = self._partial_fields.copy() - s._highlight = self._highlight.copy() - s._highlight_opts = self._highlight_opts.copy() - s._suggest = self._suggest.copy() - s._script_fields = self._script_fields.copy() - for x in ('query', 'post_filter'): - getattr(s, x)._proxied = getattr(self, x)._proxied - - # copy top-level bucket definitions - if self.aggs._params.get('aggs'): - s.aggs._params = {'aggs': self.aggs._params['aggs'].copy()} - return s - - def response_class(self, cls): - """ - Override the default wrapper used for the response. - """ - s = self._clone() - s._response_class = cls - return s - - def update_from_dict(self, d): - """ - Apply options from a serialized body to the current instance. Modifies - the object in-place. Used mostly by ``from_dict``. - """ - d = d.copy() - if 'query' in d: - self.query._proxied = Q(d.pop('query')) - if 'post_filter' in d: - self.post_filter._proxied = Q(d.pop('post_filter')) - - aggs = d.pop('aggs', d.pop('aggregations', {})) - if aggs: - self.aggs._params = { - 'aggs': dict( - (name, A(value)) for (name, value) in iteritems(aggs)) - } - if 'sort' in d: - self._sort = d.pop('sort') - if 'fields' in d: - self._fields = d.pop('fields') - if 'partial_fields' in d: - self._partial_fields = d.pop('partial_fields') - if 'highlight' in d: - high = d.pop('highlight').copy() - self._highlight = high.pop('fields') - self._highlight_opts = high - if 'suggest' in d: - self._suggest = d.pop('suggest') - if 'text' in self._suggest: - text = self._suggest.pop('text') - for s in self._suggest.values(): - s.setdefault('text', text) - if 'script_fields' in d: - self._script_fields = d.pop('script_fields') - self._extra = d - - def script_fields(self, **kwargs): - """ - Define script fields to be calculated on hits. See - https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-script-fields.html - for more details. - - Example:: - - s = Search() - s = s.script_fields(times_two="doc['field'].value * 2") - s = s.script_fields( - times_three={ - 'script': "doc['field'].value * n", - 'params': {'n': 3} - } - ) - - """ - s = self._clone() - for name in kwargs: - if isinstance(kwargs[name], string_types): - kwargs[name] = {'script': kwargs[name]} - s._script_fields.update(kwargs) - return s - - def fields(self, fields=None): - """ - Selectively load specific stored fields for each document. - - :arg fields: list of fields to return for each document - - If ``fields`` is None, the entire document will be returned for - each hit. If fields is the empty list, no fields will be - returned for each hit, just the metadata. - """ - s = self._clone() - s._fields = fields - return s - - def partial_fields(self, **partial): - """ - Control which part of the fields to extract from the `_source` document - - :kwargs partial: dict specifying which fields to extract from the source - - An example usage would be: - - s = Search().partial_fields(authors_data={ - 'include': ['authors.*'], - 'exclude': ['authors.name'] - }) - - which will include all fields from the `authors` nested property except for - each authors `name` - - If ``partial`` is not provided, the whole `_source` will be fetched. Calling this multiple - times will override the previous values with the new ones. - """ - s = self._clone() - s._partial_fields = partial - return s - - def sort(self, *keys): - """ - Add sorting information to the search request. If called without - arguments it will remove all sort requirements. Otherwise it will - replace them. Acceptable arguments are:: - - 'some.field' - '-some.other.field' - {'different.field': {'any': 'dict'}} - - so for example:: - - s = Search().sort( - 'category', - '-title', - {"price" : {"order" : "asc", "mode" : "avg"}} - ) - - will sort by ``category``, ``title`` (in descending order) and - ``price`` in ascending order using the ``avg`` mode. - - The API returns a copy of the Search object and can thus be chained. - """ - s = self._clone() - s._sort = [] - for k in keys: - if isinstance(k, string_types) and k.startswith('-'): - k = {k[1:]: {"order": "desc"}} - s._sort.append(k) - return s - - def highlight_options(self, **kwargs): - """ - Update the global highlighting options used for this request. For - example:: - - s = Search() - s = s.highlight_options(order='score') - """ - s = self._clone() - s._highlight_opts.update(kwargs) - return s - - def highlight(self, *fields, **kwargs): - """ - Request highliting of some fields. All keyword arguments passed in will be - used as parameters. Example:: - - Search().highlight('title', 'body', fragment_size=50) - - will produce the equivalent of:: - - { - "highlight": { - "fields": { - "body": {"fragment_size": 50}, - "title": {"fragment_size": 50} - } - } - } - - """ - s = self._clone() - for f in fields: - s._highlight[f] = kwargs - return s - - def suggest(self, name, text, **kwargs): - """ - Add a suggestions request to the search. - - :arg name: name of the suggestion - :arg text: text to suggest on - - All keyword arguments will be added to the suggestions body. For example:: - - s = Search() - s = s.suggest('suggestion-1', 'Elasticserach', term={'field': 'body'}) - """ - s = self._clone() - s._suggest[name] = {'text': text} - s._suggest[name].update(kwargs) - return s - - def to_dict(self, count=False, **kwargs): - """ - Serialize the search into the dictionary that will be sent over as the - request's body. - - :arg count: a flag to specify we are interested in a body for count - - no aggregations, no pagination bounds etc. - - All additional keyword arguments will be included into the dictionary. - """ - d = {"query": self.query.to_dict()} - - if self.post_filter: - d['post_filter'] = self.post_filter.to_dict() - - # count request doesn't care for sorting and other things - if not count: - if self.aggs.aggs: - d.update(self.aggs.to_dict()) - - if self._sort: - d['sort'] = self._sort - - d.update(self._extra) - - if self._fields is not None: - d['fields'] = self._fields - - if self._partial_fields: - d['partial_fields'] = self._partial_fields - - if self._highlight: - d['highlight'] = {'fields': self._highlight} - d['highlight'].update(self._highlight_opts) - - if self._suggest: - d['suggest'] = self._suggest - - if self._script_fields: - d['script_fields'] = self._script_fields - - d.update(kwargs) - return d - - def count(self): - """ - Return the number of hits matching the query and filters. Note that - only the actual number is returned. - """ - if hasattr(self, '_response'): - return self._response.hits.total - - es = connections.get_connection(self._using) - - d = self.to_dict(count=True) - # TODO: failed shards detection - return es.count( - index=self._index, - doc_type=self._doc_type, - body=d - )['count'] - - def execute(self, ignore_cache=False): - """ - Execute the search and return an instance of ``Response`` wrapping all - the data. - - :arg response_class: optional subclass of ``Response`` to use instead. - """ - if ignore_cache or not hasattr(self, '_response'): - es = connections.get_connection(self._using) - - self._response = self._response_class( - es.search( - index=self._index, - doc_type=self._doc_type, - body=self.to_dict(), - **self._params - ), - callbacks=self._doc_type_map - ) - return self._response - - def execute_suggest(self): - """ - Execute just the suggesters. Ignores all parts of the request that are - not relevant, including ``query`` and ``doc_type``. - """ - es = connections.get_connection(self._using) - return SuggestResponse( - es.suggest( - index=self._index, - body=self._suggest, - **self._params - ) - ) - - def scan(self): - """ - Turn the search into a scan search and return a generator that will - iterate over all the documents matching the query. - - Use ``params`` method to specify any additional arguments you with to - pass to the underlying ``scan`` helper from ``elasticsearch-py`` - - http://elasticsearch-py.readthedocs.org/en/master/helpers.html#elasticsearch.helpers.scan - - """ - es = connections.get_connection(self._using) - - for hit in scan( - es, - query=self.to_dict(), - index=self._index, - doc_type=self._doc_type, - **self._params - ): - yield self._doc_type_map.get(hit['_type'], Result)(hit) - - -class MultiSearch(Request): - def __init__(self, **kwargs): - super(MultiSearch, self).__init__(**kwargs) - self._searches = [] - - def __getitem__(self, key): - return self._searches[key] - - def __iter__(self): - return iter(self._searches) - - def _clone(self): - ms = super(MultiSearch, self)._clone() - ms._searches = self._searches[:] - return ms - - def add(self, search): - ms = self._clone() - ms._searches.append(search) - return ms - - def to_dict(self): - out = [] - for s in self._searches: - meta = {} - if s._index: - meta['index'] = s._index - if s._doc_type: - meta['type'] = s._doc_type - meta.update(s._params) - - out.append(meta) - out.append(s.to_dict()) - - return out - - def execute(self, ignore_cache=False): - if ignore_cache or not hasattr(self, '_response'): - es = connections.get_connection(self._using) - - responses = es.msearch( - index=self._index, - doc_type=self._doc_type, - body=self.to_dict(), - **self._params - ) - - out = [] - for s, r in zip(self._searches, responses['responses']): - r = Response(r, callbacks=s._doc_type_map) - r.search = s - out.append(r) - - self._response = out - - return self._response diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/serializer.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/serializer.py deleted file mode 100644 index ed0956b..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/serializer.py +++ /dev/null @@ -1,13 +0,0 @@ -from elasticsearch.serializer import JSONSerializer - -from .utils import AttrList - -class AttrJSONSerializer(JSONSerializer): - def default(self, data): - if isinstance(data, AttrList): - return data._l_ - if hasattr(data, 'to_dict'): - return data.to_dict() - return super(AttrJSONSerializer, self).default(data) - -serializer = AttrJSONSerializer() diff --git a/venv/lib/python2.7/site-packages/elasticsearch_dsl/utils.py b/venv/lib/python2.7/site-packages/elasticsearch_dsl/utils.py deleted file mode 100644 index 6fdec33..0000000 --- a/venv/lib/python2.7/site-packages/elasticsearch_dsl/utils.py +++ /dev/null @@ -1,396 +0,0 @@ -from __future__ import unicode_literals - -from six import iteritems, add_metaclass -from six.moves import map - -from .exceptions import UnknownDslObject, ValidationException - -SKIP_VALUES = ('', None) - -def _wrap(val, obj_wrapper=None): - if isinstance(val, dict): - return AttrDict(val) if obj_wrapper is None else obj_wrapper(val) - if isinstance(val, list): - return AttrList(val) - return val - -def _make_dsl_class(base, name, params_def=None, suffix=''): - """ - Generate a DSL class based on the name of the DSL object and it's parameters - """ - attrs = {'name': name} - if params_def: - attrs['_param_defs'] = params_def - cls_name = str(''.join(s.title() for s in name.split('_')) + suffix) - return type(cls_name, (base, ), attrs) - -class AttrList(object): - def __init__(self, l, obj_wrapper=None): - # make iteables into lists - if not isinstance(l, list): - l = list(l) - self._l_ = l - self._obj_wrapper = obj_wrapper - - def __repr__(self): - return repr(self._l_) - - def __eq__(self, other): - if isinstance(other, AttrList): - return other._l_ == self._l_ - # make sure we still equal to a dict with the same data - return other == self._l_ - - def __getitem__(self, k): - l = self._l_[k] - if isinstance(k, slice): - return AttrList(l) - return _wrap(l, self._obj_wrapper) - - def __setitem__(self, k, value): - self._l_[k] = value - - def __iter__(self): - return map(lambda i: _wrap(i, self._obj_wrapper), self._l_) - - def __len__(self): - return len(self._l_) - - def __nonzero__(self): - return bool(self._l_) - __bool__ = __nonzero__ - - def __getattr__(self, name): - return getattr(self._l_, name) - - -class AttrDict(object): - """ - Helper class to provide attribute like access (read and write) to - dictionaries. Used to provide a convenient way to access both results and - nested dsl dicts. - """ - def __init__(self, d): - # assign the inner dict manually to prevent __setattr__ from firing - super(AttrDict, self).__setattr__('_d_', d) - - def __contains__(self, key): - return key in self._d_ - - def __nonzero__(self): - return bool(self._d_) - __bool__ = __nonzero__ - - def __dir__(self): - # introspection for auto-complete in IPython etc - return list(self._d_.keys()) - - def __eq__(self, other): - if isinstance(other, AttrDict): - return other._d_ == self._d_ - # make sure we still equal to a dict with the same data - return other == self._d_ - - def __repr__(self): - r = repr(self._d_) - if len(r) > 60: - r = r[:60] + '...}' - return r - - def __getattr__(self, attr_name): - try: - return _wrap(self._d_[attr_name]) - except KeyError: - raise AttributeError( - '%r object has no attribute %r' % (self.__class__.__name__, attr_name)) - - def __delattr__(self, attr_name): - try: - del self._d_[attr_name] - except KeyError: - raise AttributeError( - '%r object has no attribute %r' % (self.__class__.__name__, attr_name)) - - def __getitem__(self, key): - return _wrap(self._d_[key]) - - def __setitem__(self, key, value): - self._d_[key] = value - - def __delitem__(self, key): - del self._d_[key] - - def __setattr__(self, name, value): - if name in self._d_ or not hasattr(self.__class__, name): - self._d_[name] = value - else: - # there is an attribute on the class (could be property, ..) - don't add it as field - super(AttrDict, self).__setattr__(name, value) - - def __iter__(self): - return iter(self._d_) - - def to_dict(self): - return self._d_ - - -class DslMeta(type): - """ - Base Metaclass for DslBase subclasses that builds a registry of all classes - for given DslBase subclass (== all the query types for the Query subclass - of DslBase). - - It then uses the information from that registry (as well as `name` and - `shortcut` attributes from the base class) to construct any subclass based - on it's name. - - For typical use see `QueryMeta` and `Query` in `elasticsearch_dsl.query`. - """ - _types = {} - def __init__(cls, name, bases, attrs): - super(DslMeta, cls).__init__(name, bases, attrs) - # skip for DslBase - if not hasattr(cls, '_type_shortcut'): - return - if cls.name is None: - # abstract base class, register it's shortcut - cls._types[cls._type_name] = cls._type_shortcut - # and create a registry for subclasses - if not hasattr(cls, '_classes'): - cls._classes = {} - elif cls.name not in cls._classes: - # normal class, register it - cls._classes[cls.name] = cls - - @classmethod - def get_dsl_type(cls, name): - try: - return cls._types[name] - except KeyError: - raise UnknownDslObject('DSL type %s does not exist.' % name) - - -@add_metaclass(DslMeta) -class DslBase(object): - """ - Base class for all DSL objects - queries, filters, aggregations etc. Wraps - a dictionary representing the object's json. - - Provides several feature: - - attribute access to the wrapped dictionary (.field instead of ['field']) - - _clone method returning a deep copy of self - - to_dict method to serialize into dict (to be sent via elasticsearch-py) - - basic logical operators (&, | and ~) using a Bool(Filter|Query) TODO: - move into a class specific for Query/Filter - - respects the definiton of the class and (de)serializes it's - attributes based on the `_param_defs` definition (for example turning - all values in the `must` attribute into Query objects) - """ - _param_defs = {} - - @classmethod - def get_dsl_class(cls, name): - try: - return cls._classes[name] - except KeyError: - raise UnknownDslObject('DSL class `%s` does not exist in %s.' % (name, cls._type_name)) - - def __init__(self, **params): - self._params = {} - for pname, pvalue in iteritems(params): - if '__' in pname: - pname = pname.replace('__', '.') - self._setattr(pname, pvalue) - - def _repr_params(self): - """ Produce a repr of all our parameters to be used in __repr__. """ - return ', '.join( - '%s=%r' % (n.replace('.', '__'), v) - for (n, v) in sorted(iteritems(self._params)) - # make sure we don't include empty typed params - if 'type' not in self._param_defs.get(n, {}) or v - ) - - def __repr__(self): - return '%s(%s)' % ( - self.__class__.__name__, - self._repr_params() - ) - - def __eq__(self, other): - return isinstance(other, self.__class__) and other.to_dict() == self.to_dict() - - def __ne__(self, other): - return not self == other - - def __setattr__(self, name, value): - if name.startswith('_'): - return super(DslBase, self).__setattr__(name, value) - return self._setattr(name, value) - - def _setattr(self, name, value): - # if this attribute has special type assigned to it... - if name in self._param_defs: - pinfo = self._param_defs[name] - - if 'type' in pinfo: - # get the shortcut used to construct this type (query.Q, aggs.A, etc) - shortcut = self.__class__.get_dsl_type(pinfo['type']) - if pinfo.get('multi'): - if not isinstance(value, (tuple, list)): - value = (value, ) - value = list(map(shortcut, value)) - - # dict(name -> DslBase), make sure we pickup all the objs - elif pinfo.get('hash'): - value = dict((k, shortcut(v)) for (k, v) in iteritems(value)) - - # single value object, just convert - else: - value = shortcut(value) - self._params[name] = value - - def __getattr__(self, name): - if name.startswith('_'): - raise AttributeError( - '%r object has no attribute %r' % (self.__class__.__name__, name)) - - value = None - try: - value = self._params[name] - except KeyError: - # compound types should never throw AttributeError and return empty - # container instead - if name in self._param_defs: - pinfo = self._param_defs[name] - if pinfo.get('multi'): - value = self._params.setdefault(name, []) - elif pinfo.get('hash'): - value = self._params.setdefault(name, {}) - if value is None: - raise AttributeError( - '%r object has no attribute %r' % (self.__class__.__name__, name)) - - # wrap nested dicts in AttrDict for convenient access - if isinstance(value, dict): - return AttrDict(value) - return value - - def to_dict(self): - """ - Serialize the DSL object to plain dict - """ - d = {} - for pname, value in iteritems(self._params): - pinfo = self._param_defs.get(pname) - - # typed param - if pinfo and 'type' in pinfo: - # don't serialize empty lists and dicts for typed fields - if value in ({}, []): - continue - - # multi-values are serialized as list of dicts - if pinfo.get('multi'): - value = list(map(lambda x: x.to_dict(), value)) - - # squash all the hash values into one dict - elif pinfo.get('hash'): - value = dict((k, v.to_dict()) for k, v in iteritems(value)) - - # serialize single values - else: - value = value.to_dict() - - # serialize anything with to_dict method - elif hasattr(value, 'to_dict'): - value = value.to_dict() - - d[pname] = value - return {self.name: d} - - def _clone(self): - return self._type_shortcut(self.to_dict()) - - -class ObjectBase(AttrDict): - def __init__(self, **kwargs): - m = self._doc_type.mapping - for k in m: - if k in kwargs and m[k]._coerce: - kwargs[k] = m[k].deserialize(kwargs[k]) - super(ObjectBase, self).__init__(kwargs) - - def __getattr__(self, name): - try: - return super(ObjectBase, self).__getattr__(name) - except AttributeError: - if name in self._doc_type.mapping: - f = self._doc_type.mapping[name] - if hasattr(f, 'empty'): - value = f.empty() - if value not in SKIP_VALUES: - setattr(self, name, value) - value = getattr(self, name) - return value - raise - - def __setattr__(self, name, value): - if name in self._doc_type.mapping: - value = self._doc_type.mapping[name].deserialize(value) - super(ObjectBase, self).__setattr__(name, value) - - def to_dict(self): - out = {} - for k, v in iteritems(self._d_): - try: - f = self._doc_type.mapping[k] - if f._coerce: - v = f.serialize(v) - except KeyError: - pass - - # don't serialize empty values - # careful not to include numeric zeros - if v in ([], {}, None): - continue - - out[k] = v - return out - - def clean_fields(self): - errors = {} - for name in self._doc_type.mapping: - field = self._doc_type.mapping[name] - data = self._d_.get(name, None) - try: - # save the cleaned value - data = field.clean(data) - except ValidationException as e: - errors.setdefault(name, []).append(e) - - if name in self._d_ or data not in ([], {}, None): - self._d_[name] = data - - if errors: - raise ValidationException(errors) - - def clean(self): - pass - - def full_clean(self): - self.clean_fields() - self.clean() - -def merge(data, new_data): - if not (isinstance(data, (AttrDict, dict)) - and isinstance(new_data, (AttrDict, dict))): - raise ValueError('You can only merge two dicts! Got %r and %r instead.' % (data, new_data)) - - for key, value in iteritems(new_data): - if key in data and isinstance(data[key], (AttrDict, dict)): - merge(data[key], value) - else: - data[key] = value - - diff --git a/venv/lib/python2.7/site-packages/flask/__init__.py b/venv/lib/python2.7/site-packages/flask/__init__.py deleted file mode 100644 index 70d5e58..0000000 --- a/venv/lib/python2.7/site-packages/flask/__init__.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask - ~~~~~ - - A microframework based on Werkzeug. It's extensively documented - and follows best practice patterns. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - -__version__ = '0.11.1' - -# 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/python2.7/site-packages/flask/__main__.py b/venv/lib/python2.7/site-packages/flask/__main__.py deleted file mode 100644 index cbefccd..0000000 --- a/venv/lib/python2.7/site-packages/flask/__main__.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.__main__ - ~~~~~~~~~~~~~~ - - Alias for flask.run for the command line. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - - -if __name__ == '__main__': - from .cli import main - main(as_module=True) diff --git a/venv/lib/python2.7/site-packages/flask/_compat.py b/venv/lib/python2.7/site-packages/flask/_compat.py deleted file mode 100644 index bfe607d..0000000 --- a/venv/lib/python2.7/site-packages/flask/_compat.py +++ /dev/null @@ -1,88 +0,0 @@ -# -*- 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: (c) 2015 by Armin Ronacher. - :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 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 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. This is currently -# true for pypy 2.2.1 for instance. The second level of exception blocks -# is necessary because pypy seems to forget to check if an exception -# happened until the next bytecode instruction? -BROKEN_PYPY_CTXMGR_EXIT = False -if hasattr(sys, 'pypy_version_info'): - class _Mgr(object): - def __enter__(self): - return self - def __exit__(self, *args): - 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/python2.7/site-packages/flask/app.py b/venv/lib/python2.7/site-packages/flask/app.py deleted file mode 100644 index b1ea046..0000000 --- a/venv/lib/python2.7/site-packages/flask/app.py +++ /dev/null @@ -1,2006 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.app - ~~~~~~~~~ - - This module implements the central WSGI application object. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -import os -import sys -from threading import Lock -from datetime import timedelta -from itertools import chain -from functools import update_wrapper -from collections import deque - -from werkzeug.datastructures import ImmutableDict -from werkzeug.routing import Map, Rule, RequestRedirect, BuildError -from werkzeug.exceptions import HTTPException, InternalServerError, \ - MethodNotAllowed, BadRequest, default_exceptions - -from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \ - locked_cached_property, _endpoint_from_view_func, find_package, \ - get_debug_flag -from . import json, cli -from .wrappers import Request, Response -from .config import ConfigAttribute, Config -from .ctx import RequestContext, AppContext, _AppCtxGlobals -from .globals import _request_ctx_stack, request, session, g -from .sessions import SecureCookieSessionInterface -from .templating import DispatchingJinjaLoader, Environment, \ - _default_template_ctx_processor -from .signals import request_started, request_finished, got_request_exception, \ - request_tearing_down, appcontext_tearing_down -from ._compat import reraise, string_types, text_type, integer_types - -# a lock used for logger initialization -_logger_lock = Lock() - -# 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. - - :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 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 - - # Backwards compatibility support - def _get_request_globals_class(self): - return self.app_ctx_globals_class - def _set_request_globals_class(self, value): - from warnings import warn - warn(DeprecationWarning('request_globals_class attribute is now ' - 'called app_ctx_globals_class')) - self.app_ctx_globals_class = value - request_globals_class = property(_get_request_globals_class, - _set_request_globals_class) - del _get_request_globals_class, _set_request_globals_class - - #: 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 debug flag. Set this to ``True`` to enable debugging of the - #: application. In debug mode the debugger will kick in when an unhandled - #: exception occurs and the integrated server will automatically reload - #: the application if changes in the code are detected. - #: - #: This attribute can also be configured from the config with the ``DEBUG`` - #: configuration key. Defaults to ``False``. - debug = ConfigAttribute('DEBUG') - - #: 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 unittest 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 - #: ``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 name of the logger to use. By default the logger name is the - #: package name passed to the constructor. - #: - #: .. versionadded:: 0.4 - logger_name = ConfigAttribute('LOGGER_NAME') - - #: 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({ - 'DEBUG': get_debug_flag(default=False), - 'TESTING': False, - 'PROPAGATE_EXCEPTIONS': None, - 'PRESERVE_CONTEXT_ON_EXCEPTION': None, - 'SECRET_KEY': None, - 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), - 'USE_X_SENDFILE': False, - 'LOGGER_NAME': None, - 'LOGGER_HANDLER_POLICY': 'always', - 'SERVER_NAME': None, - 'APPLICATION_ROOT': None, - 'SESSION_COOKIE_NAME': 'session', - 'SESSION_COOKIE_DOMAIN': None, - 'SESSION_COOKIE_PATH': None, - 'SESSION_COOKIE_HTTPONLY': True, - 'SESSION_COOKIE_SECURE': False, - 'SESSION_REFRESH_EACH_REQUEST': True, - 'MAX_CONTENT_LENGTH': None, - 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), - 'TRAP_BAD_REQUEST_ERRORS': False, - 'TRAP_HTTP_EXCEPTIONS': False, - 'EXPLAIN_TEMPLATE_LOADING': False, - 'PREFERRED_URL_SCHEME': 'http', - 'JSON_AS_ASCII': True, - 'JSON_SORT_KEYS': True, - 'JSONIFY_PRETTYPRINT_REGULAR': True, - 'JSONIFY_MIMETYPE': 'application/json', - 'TEMPLATES_AUTO_RELOAD': None, - }) - - #: 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 session interface to use. By default an instance of - #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here. - #: - #: .. versionadded:: 0.8 - session_interface = SecureCookieSessionInterface() - - def __init__(self, import_name, static_path=None, static_url_path=None, - static_folder='static', 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_path is not None: - from warnings import warn - warn(DeprecationWarning('static_path is now called ' - 'static_url_path'), stacklevel=2) - static_url_path = static_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) - - # Prepare the deferred setup of the logger. - self._logger = None - self.logger_name = self.import_name - - #: 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 = {} - - # support for the now deprecated `error_handlers` attribute. The - # :attr:`error_handler_spec` shall be used now. - self._error_handlers = {} - - #: 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 a error handler, use the :meth:`errorhandler` - #: decorator. - self.error_handler_spec = {None: self._error_handlers} - - #: 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 should be called at the - #: beginning of the 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 open database connections or - #: getting hold of the currently logged in user. To register a - #: function here, use the :meth:`before_request` decorator. - self.before_request_funcs = {} - - #: A lists of functions that should be called at the beginning of the - #: first request to this instance. To register a function here, 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 can be used as URL - #: value processor functions. Whenever a URL is built these functions - #: are called to modify the dictionary of values in place. 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 - #: - #: .. 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(BaseConverter.to_url(value) - #: for value in values) - #: - #: app = Flask(__name__) - #: app.url_map.converters['list'] = ListConverter - self.url_map = Map() - - # tracks internally if the application already handled at least one - # request. - self._got_first_request = False - self._before_request_lock = Lock() - - # register the static folder for the application. Do that even - # if the folder does not exist. First of all it might be created - # while the server is running (usually happens during development) - # but also because google appengine stores static files somewhere - # else when mapped with the .yml file. - if self.has_static_folder: - self.add_url_rule(self.static_url_path + '/', - endpoint='static', - 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) - - def _get_error_handlers(self): - from warnings import warn - warn(DeprecationWarning('error_handlers is deprecated, use the ' - 'new error_handler_spec attribute instead.'), stacklevel=1) - return self._error_handlers - def _set_error_handlers(self, value): - self._error_handlers = value - self.error_handler_spec[None] = value - error_handlers = property(_get_error_handlers, _set_error_handlers) - del _get_error_handlers, _set_error_handlers - - @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 - - @property - def logger(self): - """A :class:`logging.Logger` object for this application. The - default configuration is to log to stderr if the application is - in debug mode. This logger can be used to (surprise) log messages. - Here some examples:: - - app.logger.debug('A value for debugging') - app.logger.warning('A warning occurred (%d apples)', 42) - app.logger.error('An error occurred') - - .. versionadded:: 0.3 - """ - if self._logger and self._logger.name == self.logger_name: - return self._logger - with _logger_lock: - if self._logger and self._logger.name == self.logger_name: - return self._logger - from flask.logging import create_logger - self._logger = rv = create_logger(self) - return rv - - @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 - return self.config_class(root_path, self.default_config) - - 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 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: - if self.config['TEMPLATES_AUTO_RELOAD'] is not None: - options['auto_reload'] = self.config['TEMPLATES_AUTO_RELOAD'] - else: - options['auto_reload'] = self.debug - 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 init_jinja_globals(self): - """Deprecated. Used to initialize the Jinja2 globals. - - .. versionadded:: 0.5 - .. versionchanged:: 0.7 - This method is deprecated with 0.7. Override - :meth:`create_jinja_environment` instead. - """ - - 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 - - def run(self, host=None, port=None, debug=None, **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. - - .. versionchanged:: 0.10 - The default port is now picked from the ``SERVER_NAME`` variable. - - :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'``. - :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 options: the options to be forwarded to the underlying - Werkzeug server. See - :func:`werkzeug.serving.run_simple` for more - information. - """ - from werkzeug.serving import run_simple - if host is None: - host = '127.0.0.1' - if port is None: - server_name = self.config['SERVER_NAME'] - if server_name and ':' in server_name: - port = int(server_name.rsplit(':', 1)[1]) - else: - port = 5000 - if debug is not None: - self.debug = bool(debug) - options.setdefault('use_reloader', self.debug) - options.setdefault('use_debugger', self.debug) - options.setdefault('passthrough_errors', True) - try: - run_simple(host, port, self, **options) - finally: - # reset the first request information if the development server - # resetted 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, authentication=None, *args, **kwargs): - FlaskClient.__init__(*args, **kwargs) - self._authentication = authentication - - 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 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`. - - :param request: an instance of :attr:`request_class`. - """ - 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`. - - :param session: the session to be saved (a - :class:`~werkzeug.contrib.securecookie.SecureCookie` - object) - :param response: an instance of :attr:`response_class` - """ - 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`. - - .. versionadded:: 0.7 - """ - return self.session_interface.make_null_session(self) - - @setupmethod - def register_blueprint(self, blueprint, **options): - """Register a blueprint on the application. For information about - blueprints head over to :ref:`blueprints`. - - The blueprint name is passed in as the first argument. - Options are passed as additional keyword arguments and forwarded to - `blueprints` in an "options" dictionary. - - :param subdomain: set a subdomain for the blueprint - :param url_prefix: set the prefix for all URLs defined on the blueprint. - ``(url_prefix='/')`` - :param url_defaults: a dictionary with URL defaults that is added to - each and every URL defined with this blueprint - :param static_folder: add a static folder to urls in this blueprint - :param static_url_path: add a static url path to urls in this blueprint - :param template_folder: set an alternate template folder - :param root_path: set an alternate root path for this blueprint - - .. versionadded:: 0.7 - """ - first_registration = False - if blueprint.name in self.blueprints: - assert self.blueprints[blueprint.name] is blueprint, \ - 'A blueprint\'s name collision occurred between %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, **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 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. - 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): - """A decorator that is used to register a function give a given - 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 - - You can also register a function as error handler without using - the :meth:`errorhandler` decorator. The following example is - equivalent to the one above:: - - def page_not_found(error): - return 'This page does not exist', 404 - app.error_handler_spec[None][404] = page_not_found - - Setting error handlers via assignments to :attr:`error_handler_spec` - however is discouraged as it requires fiddling with nested dictionaries - and the special case for arbitrary exception types. - - The first ``None`` refers to the active blueprint. If the error - handler should be application wide ``None`` shall be used. - - .. 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: the code as integer for the handler - """ - def decorator(f): - self._register_error_handler(None, code_or_exception, f) - return f - return decorator - - 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)) - - exc_class, code = self._get_exc_class_and_code(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. - - The function will be called without any arguments. - If the function returns a non-None value, it's 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 a 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 exception it will - be passed an error object. - - 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): - """Registers a function as URL value preprocessor for all view - functions of the application. It's called before the view functions - are called and can modify the url values provided. - """ - 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): - """Finds a registered error handler for the request’s blueprint. - Otherwise falls back to the app, returns None if not a suitable - handler is found. - """ - exc_class, code = self._get_exc_class_and_code(type(e)) - - def find_handler(handler_map): - if not handler_map: - return - queue = deque(exc_class.__mro__) - # Protect from geniuses who might create circular references in - # __mro__ - done = set() - - while queue: - cls = queue.popleft() - if cls in done: - continue - done.add(cls) - handler = handler_map.get(cls) - if handler is not None: - # cache for next time exc_class is raised - handler_map[exc_class] = handler - return handler - - queue.extend(cls.__mro__) - - # try blueprint handlers - handler = find_handler(self.error_handler_spec - .get(request.blueprint, {}) - .get(code)) - if handler is not None: - return handler - - # fall back to app handlers - return find_handler(self.error_handler_spec[None].get(code)) - - 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. - - .. versionadded:: 0.8 - """ - if self.config['TRAP_HTTP_EXCEPTIONS']: - return True - if self.config['TRAP_BAD_REQUEST_ERRORS']: - 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. - - .. 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. - - 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 handler(e) - - 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) - response = self.make_response(rv) - response = self.process_response(response) - request_finished.send(self, response=response) - 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): - """Converts the return value from a view function to a real - response object that is an instance of :attr:`response_class`. - - The following types are allowed for `rv`: - - .. tabularcolumns:: |p{3.5cm}|p{9.5cm}| - - ======================= =========================================== - :attr:`response_class` the object is returned unchanged - :class:`str` a response object is created with the - string as body - :class:`unicode` a response object is created with the - string encoded to utf-8 as body - a WSGI function the function is called as WSGI application - and buffered as response object - :class:`tuple` A tuple in the form ``(response, status, - headers)`` or ``(response, headers)`` - where `response` is any of the - types defined here, `status` is a string - or an integer and `headers` is a list or - a dictionary with header values. - ======================= =========================================== - - :param rv: the return value from the view function - - .. versionchanged:: 0.9 - Previously a tuple was interpreted as the arguments for the - response object. - """ - status_or_headers = headers = None - if isinstance(rv, tuple): - rv, status_or_headers, headers = rv + (None,) * (3 - len(rv)) - - if rv is None: - raise ValueError('View function did not return a response') - - if isinstance(status_or_headers, (dict, list)): - headers, status_or_headers = status_or_headers, None - - if not isinstance(rv, self.response_class): - # When we create a response object directly, we let the constructor - # set the headers and status. We do this because there can be - # some extra logic involved when creating these objects with - # specific values (like default content type selection). - if isinstance(rv, (text_type, bytes, bytearray)): - rv = self.response_class(rv, headers=headers, - status=status_or_headers) - headers = status_or_headers = None - else: - rv = self.response_class.force_type(rv, request.environ) - - if status_or_headers is not None: - if isinstance(status_or_headers, string_types): - rv.status = status_or_headers - else: - rv.status_code = status_or_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. - """ - if request is not None: - return self.url_map.bind_to_environ(request.environ, - server_name=self.config['SERVER_NAME']) - # 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'] or '/', - 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 actual request dispatching and will - call each :meth:`before_request` decorated function, passing no - arguments. - If any of these functions returns a value, it's handled as - if it was the return value from the view and further - request handling is stopped. - - This also triggers the :meth:`url_value_processor` functions before - the actual :meth:`before_request` functions are called. - """ - 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.save_session(ctx.session, response) - return response - - def do_teardown_request(self, exc=_sentinel): - """Called after the actual request dispatching and will - call every as :meth:`teardown_request` decorated function. This is - not actually called by the :class:`Flask` object itself but is always - triggered when the request context is popped. That way we have a - tighter control over certain resources under testing environments. - - .. versionchanged:: 0.9 - Added the `exc` argument. Previously this was always using the - current exception information. - """ - 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 when an application context is popped. This works pretty - much the same as :meth:`do_teardown_request` but for the application - context. - - .. 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): - """Binds the application only. For as long as the application is bound - to the current context the :data:`flask.current_app` points to that - application. An application context is automatically created when a - request context is pushed if necessary. - - Example usage:: - - with app.app_context(): - ... - - .. versionadded:: 0.9 - """ - return AppContext(self) - - def request_context(self, environ): - """Creates a :class:`~flask.ctx.RequestContext` from the given - environment and binds it to the current context. This must be used in - combination with the ``with`` statement because the request is only bound - to the current context for the duration of the ``with`` block. - - Example usage:: - - with app.request_context(environ): - do_something_with(request) - - The object returned can also be used without the ``with`` statement - which is useful for working in the shell. The example above is - doing exactly the same as this code:: - - ctx = app.request_context(environ) - ctx.push() - try: - do_something_with(request) - finally: - ctx.pop() - - .. versionchanged:: 0.3 - Added support for non-with statement usage and ``with`` statement - is now passed the ctx object. - - :param environ: a WSGI environment - """ - return RequestContext(self, environ) - - def test_request_context(self, *args, **kwargs): - """Creates a WSGI environment from the given values (see - :class:`werkzeug.test.EnvironBuilder` for more information, this - function accepts the same arguments). - """ - 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 - `__call__` so that middlewares can be applied without losing a - reference to the class. So 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 - The behavior of the before and after request callbacks was changed - under error conditions and a new callback was added that will - always execute at the end of the request, independent on if an - error occurred or not. 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) - ctx.push() - error = None - try: - try: - response = self.full_dispatch_request() - except Exception as e: - error = e - response = self.make_response(self.handle_exception(e)) - return response(environ, start_response) - finally: - if self.should_ignore_error(error): - error = None - ctx.auto_pop(error) - - def __call__(self, environ, start_response): - """Shortcut for :attr:`wsgi_app`.""" - return self.wsgi_app(environ, start_response) - - def __repr__(self): - return '<%s %r>' % ( - self.__class__.__name__, - self.name, - ) diff --git a/venv/lib/python2.7/site-packages/flask/blueprints.py b/venv/lib/python2.7/site-packages/flask/blueprints.py deleted file mode 100644 index 586a1b0..0000000 --- a/venv/lib/python2.7/site-packages/flask/blueprints.py +++ /dev/null @@ -1,413 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.blueprints - ~~~~~~~~~~~~~~~~ - - Blueprints are the recommended way to implement larger or more - pluggable applications in Flask 0.7 and later. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -from functools import update_wrapper - -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: - rule = self.url_prefix + rule - 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 - - 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 a blueprint - on the application. This can be overridden to customize the register - behavior. Keyword arguments from - :func:`~flask.Flask.register_blueprint` are directly forwarded to this - method in the `options` dictionary. - """ - 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 + '/', - 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" - 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/python2.7/site-packages/flask/cli.py b/venv/lib/python2.7/site-packages/flask/cli.py deleted file mode 100644 index d3e74d9..0000000 --- a/venv/lib/python2.7/site-packages/flask/cli.py +++ /dev/null @@ -1,482 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.cli - ~~~~~~~~~ - - A simple command line application to run flask apps. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - -import os -import sys -from threading import Lock, Thread -from functools import update_wrapper - -import click - -from ._compat import iteritems, reraise -from .helpers import get_debug_flag - - -class NoAppException(click.UsageError): - """Raised if an application cannot be found or loaded.""" - - -def find_best_app(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 app is not None and 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] - raise NoAppException('Failed to find application in module "%s". Are ' - 'you sure it contains a Flask application? Maybe ' - 'you wrapped it in a WSGI middleware or you are ' - 'using a factory function.' % module.__name__) - - -def prepare_exec_for_file(filename): - """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. - """ - module = [] - - # Chop off file extensions or package markers - if os.path.split(filename)[1] == '__init__.py': - filename = os.path.dirname(filename) - elif filename.endswith('.py'): - filename = filename[:-3] - else: - raise NoAppException('The file provided (%s) does exist but is not a ' - 'valid Python file. This means that it cannot ' - 'be used as application. Please change the ' - 'extension to .py' % filename) - filename = os.path.realpath(filename) - - dirpath = filename - while 1: - dirpath, extra = os.path.split(dirpath) - module.append(extra) - if not os.path.isfile(os.path.join(dirpath, '__init__.py')): - break - - sys.path.insert(0, dirpath) - return '.'.join(module[::-1]) - - -def locate_app(app_id): - """Attempts to locate the application.""" - __traceback_hide__ = True - if ':' in app_id: - module, app_obj = app_id.split(':', 1) - else: - module = app_id - app_obj = None - - __import__(module) - mod = sys.modules[module] - if app_obj is None: - app = find_best_app(mod) - else: - app = getattr(mod, app_obj, None) - if app is None: - raise RuntimeError('Failed to find application in module "%s"' - % module) - - return app - - -def find_default_import_path(): - app = os.environ.get('FLASK_APP') - if app is None: - return - if os.path.isfile(app): - return prepare_exec_for_file(app) - return app - - -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 is recorded and shows 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): - if create_app is None: - if app_import_path is None: - app_import_path = find_default_import_path() - self.app_import_path = app_import_path - else: - app_import_path = None - - #: Optionally the import path for the Flask application. - self.app_import_path = app_import_path - #: 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 - if self.create_app is not None: - rv = self.create_app(self) - else: - if not self.app_import_path: - raise NoAppException( - 'Could not locate Flask application. You did not provide ' - 'the FLASK_APP environment variable.\n\nFor more ' - 'information see ' - 'http://flask.pocoo.org/docs/latest/quickstart/') - rv = locate_app(self.app_import_path) - debug = get_debug_flag() - if debug is not None: - rv.debug = debug - self._loaded_app = rv - return rv - - -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 create_app: an optional callback that is passed the script info - and returns the loaded app. - """ - - def __init__(self, add_default_commands=True, create_app=None, **extra): - AppGroup.__init__(self, **extra) - self.create_app = create_app - - if add_default_commands: - self.add_command(run_command) - self.add_command(shell_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. - pass - return sorted(rv) - - def main(self, *args, **kwargs): - 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 AppGroup.main(self, *args, **kwargs) - - -@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('--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=False, - help='Enable or disable multithreading.') -@pass_script_info -def run_command(info, host, port, reload, debugger, eager_loading, - with_threads): - """Runs a local development server for the Flask application. - - This local server is recommended for development purposes only but it - can also be used for simple intranet deployments. By default it will - not support any sort of concurrency at all to simplify debugging. This - can be changed with the --with-threads option which will enable basic - multithreading. - - The reloader and debugger are by default enabled if the debug flag of - Flask is enabled and disabled otherwise. - """ - from werkzeug.serving import run_simple - - debug = get_debug_flag() - if reload is None: - reload = bool(debug) - if debugger is None: - debugger = bool(debug) - if eager_loading is None: - eager_loading = not reload - - app = DispatchingApp(info.load_app, use_eager_loading=eager_loading) - - # Extra startup messages. This depends a but on Werkzeug internals to - # not double execute when the reloader kicks in. - if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': - # If we have an import path we can print it out now which can help - # people understand what's being served. If we do not have an - # import path because the app was loaded through a callback then - # we won't print anything. - if info.app_import_path is not None: - print(' * Serving Flask app "%s"' % info.app_import_path) - if debug is not None: - print(' * Forcing debug mode %s' % (debug and 'on' or 'off')) - - run_simple(host, port, app, use_reloader=reload, - use_debugger=debugger, threaded=with_threads) - - -@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 configuring 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.debug and ' [debug]' or '', - 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) - - -cli = FlaskGroup(help="""\ -This shell command acts as general utility script for Flask applications. - -It loads the application configured (either through the FLASK_APP environment -variable) and then provides commands either provided by the application or -Flask itself. - -The most useful commands are the "run" and "shell" command. - -Example usage: - -\b - %(prefix)s%(cmd)s FLASK_APP=hello - %(prefix)s%(cmd)s FLASK_DEBUG=1 - %(prefix)sflask run -""" % { - 'cmd': os.name == 'posix' and 'export' or 'set', - 'prefix': os.name == 'posix' and '$ ' or '', -}) - - -def main(as_module=False): - this_module = __package__ + '.cli' - args = sys.argv[1:] - - if as_module: - if sys.version_info >= (2, 7): - name = 'python -m ' + this_module.rsplit('.', 1)[0] - else: - name = 'python -m ' + this_module - - # This module is always executed as "python -m flask.run" and as such - # we need to ensure that we restore the actual command line so that - # the reloader can properly operate. - sys.argv = ['-m', this_module] + sys.argv[1:] - else: - name = None - - cli.main(args=args, prog_name=name) - - -if __name__ == '__main__': - main(as_module=True) diff --git a/venv/lib/python2.7/site-packages/flask/config.py b/venv/lib/python2.7/site-packages/flask/config.py deleted file mode 100644 index 426a23a..0000000 --- a/venv/lib/python2.7/site-packages/flask/config.py +++ /dev/null @@ -1,258 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.config - ~~~~~~~~~~~~ - - Implements the configuration related objects. - - :copyright: (c) 2015 by Armin Ronacher. - :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) 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): - 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. - - Just the uppercase variables in that object are stored in the config. - Example usage:: - - 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. - - :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/python2.7/site-packages/flask/ctx.py b/venv/lib/python2.7/site-packages/flask/ctx.py deleted file mode 100644 index 480d9c5..0000000 --- a/venv/lib/python2.7/site-packages/flask/ctx.py +++ /dev/null @@ -1,410 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.ctx - ~~~~~~~~~ - - Implements the objects required to keep the context. - - :copyright: (c) 2015 by Armin Ronacher. - :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.""" - - def get(self, name, default=None): - return self.__dict__.get(name, default) - - def pop(self, name, default=_sentinel): - if default is _sentinel: - return self.__dict__.pop(name) - else: - return self.__dict__.pop(name, default) - - def setdefault(self, name, default=None): - 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 '' % 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 (e.g. code that access database information - # stored on `g` instead of the appcontext). - self.session = self.app.open_session(self.request) - if self.session is None: - self.session = self.app.make_null_session() - - 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/python2.7/site-packages/flask/debughelpers.py b/venv/lib/python2.7/site-packages/flask/debughelpers.py deleted file mode 100644 index 90710dd..0000000 --- a/venv/lib/python2.7/site-packages/flask/debughelpers.py +++ /dev/null @@ -1,155 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.debughelpers - ~~~~~~~~~~~~~~~~~~ - - Various helpers to make the development experience better. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -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 '') - 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)) diff --git a/venv/lib/python2.7/site-packages/flask/ext/__init__.py b/venv/lib/python2.7/site-packages/flask/ext/__init__.py deleted file mode 100644 index 051f44a..0000000 --- a/venv/lib/python2.7/site-packages/flask/ext/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.ext - ~~~~~~~~~ - - Redirect imports for extensions. This module basically makes it possible - for us to transition from flaskext.foo to flask_foo without having to - force all extensions to upgrade at the same time. - - When a user does ``from flask.ext.foo import bar`` it will attempt to - import ``from flask_foo import bar`` first and when that fails it will - try to import ``from flaskext.foo import bar``. - - We're switching from namespace packages because it was just too painful for - everybody involved. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - - -def setup(): - from ..exthook import ExtensionImporter - importer = ExtensionImporter(['flask_%s', 'flaskext.%s'], __name__) - importer.install() - - -setup() -del setup diff --git a/venv/lib/python2.7/site-packages/flask/exthook.py b/venv/lib/python2.7/site-packages/flask/exthook.py deleted file mode 100644 index 6522e06..0000000 --- a/venv/lib/python2.7/site-packages/flask/exthook.py +++ /dev/null @@ -1,143 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.exthook - ~~~~~~~~~~~~~ - - Redirect imports for extensions. This module basically makes it possible - for us to transition from flaskext.foo to flask_foo without having to - force all extensions to upgrade at the same time. - - When a user does ``from flask.ext.foo import bar`` it will attempt to - import ``from flask_foo import bar`` first and when that fails it will - try to import ``from flaskext.foo import bar``. - - We're switching from namespace packages because it was just too painful for - everybody involved. - - This is used by `flask.ext`. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -import sys -import os -import warnings -from ._compat import reraise - - -class ExtDeprecationWarning(DeprecationWarning): - pass - -warnings.simplefilter('always', ExtDeprecationWarning) - - -class ExtensionImporter(object): - """This importer redirects imports from this submodule to other locations. - This makes it possible to transition from the old flaskext.name to the - newer flask_name without people having a hard time. - """ - - def __init__(self, module_choices, wrapper_module): - self.module_choices = module_choices - self.wrapper_module = wrapper_module - self.prefix = wrapper_module + '.' - self.prefix_cutoff = wrapper_module.count('.') + 1 - - def __eq__(self, other): - return self.__class__.__module__ == other.__class__.__module__ and \ - self.__class__.__name__ == other.__class__.__name__ and \ - self.wrapper_module == other.wrapper_module and \ - self.module_choices == other.module_choices - - def __ne__(self, other): - return not self.__eq__(other) - - def install(self): - sys.meta_path[:] = [x for x in sys.meta_path if self != x] + [self] - - def find_module(self, fullname, path=None): - if fullname.startswith(self.prefix) and \ - fullname != 'flask.ext.ExtDeprecationWarning': - return self - - def load_module(self, fullname): - if fullname in sys.modules: - return sys.modules[fullname] - - modname = fullname.split('.', self.prefix_cutoff)[self.prefix_cutoff] - - warnings.warn( - "Importing flask.ext.{x} is deprecated, use flask_{x} instead." - .format(x=modname), ExtDeprecationWarning - ) - - for path in self.module_choices: - realname = path % modname - try: - __import__(realname) - except ImportError: - exc_type, exc_value, tb = sys.exc_info() - # since we only establish the entry in sys.modules at the - # very this seems to be redundant, but if recursive imports - # happen we will call into the move import a second time. - # On the second invocation we still don't have an entry for - # fullname in sys.modules, but we will end up with the same - # fake module name and that import will succeed since this - # one already has a temporary entry in the modules dict. - # Since this one "succeeded" temporarily that second - # invocation now will have created a fullname entry in - # sys.modules which we have to kill. - sys.modules.pop(fullname, None) - - # If it's an important traceback we reraise it, otherwise - # we swallow it and try the next choice. The skipped frame - # is the one from __import__ above which we don't care about - if self.is_important_traceback(realname, tb): - reraise(exc_type, exc_value, tb.tb_next) - continue - module = sys.modules[fullname] = sys.modules[realname] - if '.' not in modname: - setattr(sys.modules[self.wrapper_module], modname, module) - - if realname.startswith('flaskext.'): - warnings.warn( - "Detected extension named flaskext.{x}, please rename it " - "to flask_{x}. The old form is deprecated." - .format(x=modname), ExtDeprecationWarning - ) - - return module - raise ImportError('No module named %s' % fullname) - - def is_important_traceback(self, important_module, tb): - """Walks a traceback's frames and checks if any of the frames - originated in the given important module. If that is the case then we - were able to import the module itself but apparently something went - wrong when the module was imported. (Eg: import of an import failed). - """ - while tb is not None: - if self.is_important_frame(important_module, tb): - return True - tb = tb.tb_next - return False - - def is_important_frame(self, important_module, tb): - """Checks a single frame if it's important.""" - g = tb.tb_frame.f_globals - if '__name__' not in g: - return False - - module_name = g['__name__'] - - # Python 2.7 Behavior. Modules are cleaned up late so the - # name shows up properly here. Success! - if module_name == important_module: - return True - - # Some python versions will clean up modules so early that the - # module name at that point is no longer set. Try guessing from - # the filename then. - filename = os.path.abspath(tb.tb_frame.f_code.co_filename) - test_string = os.path.sep + important_module.replace('.', os.path.sep) - return test_string + '.py' in filename or \ - test_string + os.path.sep + '__init__.py' in filename diff --git a/venv/lib/python2.7/site-packages/flask/globals.py b/venv/lib/python2.7/site-packages/flask/globals.py deleted file mode 100644 index 0b70a3e..0000000 --- a/venv/lib/python2.7/site-packages/flask/globals.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.globals - ~~~~~~~~~~~~~ - - Defines all the global objects that are proxies to the current - active context. - - :copyright: (c) 2015 by Armin Ronacher. - :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 a 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/python2.7/site-packages/flask/helpers.py b/venv/lib/python2.7/site-packages/flask/helpers.py deleted file mode 100644 index c744bb8..0000000 --- a/venv/lib/python2.7/site-packages/flask/helpers.py +++ /dev/null @@ -1,930 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.helpers - ~~~~~~~~~~~~~ - - Implements various helpers. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - -import os -import sys -import pkgutil -import posixpath -import mimetypes -from time import time -from zlib import adler32 -from threading import RLock -from werkzeug.routing import BuildError -from functools import update_wrapper - -try: - from werkzeug.urls import url_quote -except ImportError: - from urlparse import quote as url_quote - -from werkzeug.datastructures import Headers -from werkzeug.exceptions import BadRequest, NotFound - -# this was moved in 0.7 -try: - from werkzeug.wsgi import wrap_file -except ImportError: - from werkzeug.utils 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 - - -# 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_debug_flag(default=None): - val = os.environ.get('FLASK_DEBUG') - if not val: - return default - return val not 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 `. - - 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 ` 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 not reqctx.request._is_old_module: - if endpoint[:1] == '.': - if blueprint_name is not None: - endpoint = blueprint_name + endpoint - else: - endpoint = endpoint[1:] - else: - # TODO: get rid of this deprecated functionality in 1.0 - if '.' not in endpoint: - if blueprint_name is not None: - endpoint = blueprint_name + '.' + endpoint - elif endpoint.startswith('.'): - 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 - 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 - # 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): - """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. - - 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. - - :param filename_or_fp: the filename of the file to send in `latin-1`. - 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, otherwise - auto detection happens. - :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`. - """ - mtime = None - if isinstance(filename_or_fp, string_types): - filename = filename_or_fp - file = None - else: - from warnings import warn - file = filename_or_fp - filename = getattr(file, 'name', None) - - # XXX: this behavior is now deprecated because it was unreliable. - # removed in Flask 1.0 - if not attachment_filename and not mimetype \ - and isinstance(filename, string_types): - warn(DeprecationWarning('The filename support for file objects ' - 'passed to send_file is now deprecated. Pass an ' - 'attach_filename if you want mimetypes to be guessed.'), - stacklevel=2) - if add_etags: - warn(DeprecationWarning('In future flask releases etags will no ' - 'longer be generated for file objects passed to the send_file ' - 'function because this behavior was unreliable. Pass ' - 'filenames instead if possible, otherwise attach an etag ' - 'yourself based on another value'), stacklevel=2) - - if filename is not None: - if not os.path.isabs(filename): - filename = os.path.join(current_app.root_path, filename) - if mimetype is None and (filename or attachment_filename): - mimetype = mimetypes.guess_type(filename or attachment_filename)[0] - if mimetype is None: - mimetype = 'application/octet-stream' - - headers = Headers() - if as_attachment: - if attachment_filename is None: - if filename is None: - raise TypeError('filename unavailable, required for ' - 'sending as attachment') - attachment_filename = os.path.basename(filename) - headers.add('Content-Disposition', 'attachment', - filename=attachment_filename) - - if current_app.use_x_sendfile and filename: - if file is not None: - file.close() - headers['X-Sendfile'] = filename - headers['Content-Length'] = os.path.getsize(filename) - data = None - else: - if file is None: - file = open(filename, 'rb') - mtime = os.path.getmtime(filename) - headers['Content-Length'] = os.path.getsize(filename) - data = wrap_file(request.environ, file) - - rv = current_app.response_class(data, mimetype=mimetype, headers=headers, - direct_passthrough=True) - - # if we know the file modification date, we can store it as - # the time of the last modification. - if mtime is not None: - rv.last_modified = int(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: - 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: - rv = rv.make_conditional(request) - # 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, filename): - """Safely join `directory` and `filename`. - - Example usage:: - - @app.route('/wiki/') - 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 base directory. - :param filename: the untrusted filename relative to that directory. - :raises: :class:`~werkzeug.exceptions.NotFound` if the resulting path - would fall out of `directory`. - """ - filename = posixpath.normpath(filename) - for sep in _os_alt_seps: - if sep in filename: - raise NotFound() - if os.path.isabs(filename) or \ - filename == '..' or \ - filename.startswith('../'): - raise NotFound() - return os.path.join(directory, filename) - - -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/') - 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): - - def __init__(self, import_name, template_folder=None, root_path=None): - #: The name of the package or module. Do not change this once - #: it was set by the constructor. - self.import_name = import_name - - #: location of the templates. ``None`` if templates should not be - #: exposed. - self.template_folder = template_folder - - if root_path is None: - root_path = get_root_path(self.import_name) - - #: Where is the app root located? - 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) - 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 diff --git a/venv/lib/python2.7/site-packages/flask/json.py b/venv/lib/python2.7/site-packages/flask/json.py deleted file mode 100644 index b9ce4a0..0000000 --- a/venv/lib/python2.7/site-packages/flask/json.py +++ /dev/null @@ -1,272 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.jsonimpl - ~~~~~~~~~~~~~~ - - Implementation helpers for the JSON support in Flask. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -import io -import uuid -from datetime import date -from .globals import current_app, request -from ._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. -try: - from itsdangerous import simplejson as _json -except ImportError: - 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, 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: - kwargs.setdefault('cls', 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: - kwargs.setdefault('cls', current_app.json_decoder) - else: - kwargs.setdefault('cls', JSONDecoder) - - -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): - s = s.decode(kwargs.pop('encoding', None) or 'utf-8') - 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 ``' - safe = Markup('username') - assert unsafe + safe == text_type(escape(unsafe)) + text_type(safe) - - def test_string_interpolation(self): - # string interpolations are safe to use too - assert Markup('%s') % '' == \ - '<bad user>' - assert Markup('%(username)s') % { - 'username': '' - } == '<bad user>' - - assert Markup('%i') % 3.14 == '3' - assert Markup('%.2f') % 3.14 == '3.14' - - def test_type_behavior(self): - # an escaped object is markup too - assert type(Markup('foo') + 'bar') is Markup - - # and it implements __html__ by returning itself - x = Markup("foo") - assert x.__html__() is x - - def test_html_interop(self): - # it also knows how to treat __html__ objects - class Foo(object): - def __html__(self): - return 'awesome' - def __unicode__(self): - return 'awesome' - __str__ = __unicode__ - assert Markup(Foo()) == 'awesome' - assert Markup('%s') % Foo() == \ - 'awesome' - - def test_tuple_interpol(self): - self.assertEqual(Markup('%s:%s') % ( - '', - '', - ), Markup(u'<foo>:<bar>')) - - def test_dict_interpol(self): - self.assertEqual(Markup('%(foo)s') % { - 'foo': '', - }, Markup(u'<foo>')) - self.assertEqual(Markup('%(foo)s:%(bar)s') % { - 'foo': '', - 'bar': '', - }, Markup(u'<foo>:<bar>')) - - def test_escaping(self): - # escaping and unescaping - assert escape('"<>&\'') == '"<>&'' - assert Markup("Foo & Bar").striptags() == "Foo & Bar" - assert Markup("<test>").unescape() == "" - - def test_formatting(self): - for actual, expected in ( - (Markup('%i') % 3.14, '3'), - (Markup('%.2f') % 3.14159, '3.14'), - (Markup('%s %s %s') % ('<', 123, '>'), '< 123 >'), - (Markup('{awesome}').format(awesome=''), - '<awesome>'), - (Markup('{0[1][bar]}').format([0, {'bar': ''}]), - '<bar/>'), - (Markup('{0[1][bar]}').format([0, {'bar': Markup('')}]), - '')): - assert actual == expected, "%r should be %r!" % (actual, expected) - - # This is new in 2.7 - if sys.version_info >= (2, 7): - def test_formatting_empty(self): - formatted = Markup('{}').format(0) - assert formatted == Markup('0') - - def test_custom_formatting(self): - class HasHTMLOnly(object): - def __html__(self): - return Markup('') - - class HasHTMLAndFormat(object): - def __html__(self): - return Markup('') - def __html_format__(self, spec): - return Markup('') - - assert Markup('{0}').format(HasHTMLOnly()) == Markup('') - assert Markup('{0}').format(HasHTMLAndFormat()) == Markup('') - - def test_complex_custom_formatting(self): - class User(object): - def __init__(self, id, username): - self.id = id - self.username = username - def __html_format__(self, format_spec): - if format_spec == 'link': - return Markup('{1}').format( - self.id, - self.__html__(), - ) - elif format_spec: - raise ValueError('Invalid format spec') - return self.__html__() - def __html__(self): - return Markup('{0}').format(self.username) - - user = User(1, 'foo') - assert Markup('

User: {0:link}').format(user) == \ - Markup('

User: foo') - - def test_all_set(self): - import markupsafe as markup - for item in markup.__all__: - getattr(markup, item) - - def test_escape_silent(self): - assert escape_silent(None) == Markup() - assert escape(None) == Markup(None) - assert escape_silent('') == Markup(u'<foo>') - - def test_splitting(self): - self.assertEqual(Markup('a b').split(), [ - Markup('a'), - Markup('b') - ]) - self.assertEqual(Markup('a b').rsplit(), [ - Markup('a'), - Markup('b') - ]) - self.assertEqual(Markup('a\nb').splitlines(), [ - Markup('a'), - Markup('b') - ]) - - def test_mul(self): - self.assertEqual(Markup('a') * 3, Markup('aaa')) - - -class MarkupLeakTestCase(unittest.TestCase): - - def test_markup_leaks(self): - counts = set() - for count in range(20): - for item in range(1000): - escape("foo") - escape("") - escape(u"foo") - escape(u"") - counts.add(len(gc.get_objects())) - assert len(counts) == 1, 'ouch, c extension seems to leak objects' - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(MarkupTestCase)) - - # this test only tests the c extension - if not hasattr(escape, 'func_code'): - suite.addTest(unittest.makeSuite(MarkupLeakTestCase)) - - return suite - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') - -# vim:sts=4:sw=4:et: diff --git a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/DESCRIPTION.rst b/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/DESCRIPTION.rst deleted file mode 100644 index 39586d2..0000000 --- a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/DESCRIPTION.rst +++ /dev/null @@ -1,36 +0,0 @@ -pip -=== - -The `PyPA recommended -`_ -tool for installing Python packages. - -* `Installation `_ -* `Documentation `_ -* `Changelog `_ -* `Github Page `_ -* `Issue Tracking `_ -* `User mailing list `_ -* `Dev mailing list `_ -* 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/develop.svg - :target: http://travis-ci.org/pypa/pip - -.. 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/ - - diff --git a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/INSTALLER b/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/METADATA b/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/METADATA deleted file mode 100644 index 83d3c03..0000000 --- a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/METADATA +++ /dev/null @@ -1,65 +0,0 @@ -Metadata-Version: 2.0 -Name: pip -Version: 8.1.2 -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 -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 -Provides-Extra: testing -Requires-Dist: mock; extra == 'testing' -Requires-Dist: pretend; extra == 'testing' -Requires-Dist: pytest; extra == 'testing' -Requires-Dist: scripttest (>=1.3); extra == 'testing' -Requires-Dist: virtualenv (>=1.10); extra == 'testing' - -pip -=== - -The `PyPA recommended -`_ -tool for installing Python packages. - -* `Installation `_ -* `Documentation `_ -* `Changelog `_ -* `Github Page `_ -* `Issue Tracking `_ -* `User mailing list `_ -* `Dev mailing list `_ -* 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/develop.svg - :target: http://travis-ci.org/pypa/pip - -.. 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/ - - diff --git a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/RECORD b/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/RECORD deleted file mode 100644 index c59b682..0000000 --- a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/RECORD +++ /dev/null @@ -1,483 +0,0 @@ -pip/__init__.py,sha256=OQB9eSOv7HjK2W5VesX26tKN8uvMhvPaXoM4HuoiKn0,10604 -pip/__main__.py,sha256=V6Kh-IEDEFpt1cahRE6MajUF_14qJR_Qsvn4MjWZXzE,584 -pip/basecommand.py,sha256=4WPEhwtMgVGe4ahlr4Y2JyXJ8chjBmYlU8RCa0sQ5bo,11648 -pip/baseparser.py,sha256=Nlc7Un9gat27xtB24SnKL_3pZZOoh62gNNRdS6tDRZY,10465 -pip/cmdoptions.py,sha256=pf24iszA39rhcJ5DjFA4oD_z5vTI0NG98qUahHs3qPM,15878 -pip/download.py,sha256=oJ3sZ8I6ct9X3eoXQ9xm_Ne0e6N85G_rWaERmMCVF2k,31722 -pip/exceptions.py,sha256=GdDhHOROBj-kW2rgerLJYXsxN8ENy1BX5RUb_Vs9TXM,7980 -pip/index.py,sha256=DsxoKRxoL4oEdaqQOctou0HN1rciulGp0EBcHtnyBR4,37235 -pip/locations.py,sha256=MqUzS8YI2wDa7oFzTQw4zM4s0Hci05yubxfU_kTXXlU,5632 -pip/pep425tags.py,sha256=elib4cgVtzTw8CUJ8IxoX1mkaeLxS-2XHHqkvS5ryZM,11853 -pip/status_codes.py,sha256=F6uDG6Gj7RNKQJUDnd87QKqI16Us-t-B0wPF_4QMpWc,156 -pip/wheel.py,sha256=wO99S_aDXkjyActzGLOw0cV73YFLXjz740oD96wXyXo,32216 -pip/_vendor/__init__.py,sha256=yzwJqv89TDqeqqWRgBlZZmiAc5sTNOrdIMgF0MT8TWI,4647 -pip/_vendor/ipaddress.py,sha256=FngG_V6As8JnDJVR1g1i0D6wVa5ycSKJYOh7IrI4fq0,79904 -pip/_vendor/pyparsing.py,sha256=cBtJbRYQNhvtw0IqoLvDUzW-ZdgfRFVm3QD2O3-jlz0,158096 -pip/_vendor/re-vendor.py,sha256=PcdZ40d0ohMsdJmA4t0AeAWbPXi1tFsvAwA5KE5FGeY,773 -pip/_vendor/retrying.py,sha256=k3fflf5_Mm0XcIJYhB7Tj34bqCCPhUDkYbx1NvW2FPE,9972 -pip/_vendor/six.py,sha256=A6hdJZVjI3t_geebZ9BzUvwRrIXo0lfwzQlM2LcKyas,30098 -pip/_vendor/cachecontrol/__init__.py,sha256=uz7Oxs9hQmUxtxod1WyVoUj6Jab7NfiLECPHFuc-22U,302 -pip/_vendor/cachecontrol/_cmd.py,sha256=MPxZfZd2LKDzVrs55X3wA1rsI2YuP8evLZSwQj0dIk0,1320 -pip/_vendor/cachecontrol/adapter.py,sha256=eizWWJwOnG5TToxL-XiEywPEf2k20--e-5C6u6wAEts,4196 -pip/_vendor/cachecontrol/cache.py,sha256=xtl-V-pr9KSt9VvFDRCB9yrHPEvqvbk-5M1vAInZb5k,790 -pip/_vendor/cachecontrol/compat.py,sha256=uyovOpd1ehI3J1XeBqJvcsIp6fvkjBpoQmu_0J2st8c,416 -pip/_vendor/cachecontrol/controller.py,sha256=Aky7U9UZ1VqutfvilljlWi1uk40yis6lmaz8c_wQLw8,12996 -pip/_vendor/cachecontrol/filewrapper.py,sha256=jkC0GOorbWIpy9CzP3PwxSHx5J2CXBAxNbGH68HmP1M,2168 -pip/_vendor/cachecontrol/heuristics.py,sha256=WtJrVsyWjpP9WoUiDVdTZZRNBCz5ZVptaQpYnqofDQU,4141 -pip/_vendor/cachecontrol/serialize.py,sha256=vZQ-Rsn4618ATipqqZJ-Hife2qVzpIepNjxTCUM-eAw,6305 -pip/_vendor/cachecontrol/wrapper.py,sha256=Kqyu_3TW_54XDudha4-HF21vyEOAJ4ZnRXFysTiLmXA,498 -pip/_vendor/cachecontrol/caches/__init__.py,sha256=uWnUtyMvHY_LULaL_4_IR1F_xPgK5zHfJyRnBq4DnPE,369 -pip/_vendor/cachecontrol/caches/file_cache.py,sha256=FsDug3bwUAQ3okjjfGzxlDaBf2fwVSn1iBKMTL6SyGU,3532 -pip/_vendor/cachecontrol/caches/redis_cache.py,sha256=XywqxkS9MkCaflTOY_wjrE02neKdywB9YwlOBbP7Ywc,973 -pip/_vendor/colorama/__init__.py,sha256=9xByrTvk9upkL5NGV5It2Eje4-kzNLwa_1lGPWpXoNU,240 -pip/_vendor/colorama/ansi.py,sha256=Fi0un-QLqRm-v7o_nKiOqyC8PapBJK7DLV_q9LKtTO0,2524 -pip/_vendor/colorama/ansitowin32.py,sha256=gJZB35Lbdjatykd2zrUUnokMzkvcFgscyn_tNxxMFHA,9668 -pip/_vendor/colorama/initialise.py,sha256=cHqVJtb82OG7HUCxvQ2joG7N_CoxbIKbI_fgryZkj20,1917 -pip/_vendor/colorama/win32.py,sha256=_SCEoTK_GA2tU1nhbayKKac-v9Jn98lCPIFOeFMGCHQ,5365 -pip/_vendor/colorama/winterm.py,sha256=V7U7ojwG1q4n6PKripjEvW_htYQi5ueXSM3LUUoqqDY,6290 -pip/_vendor/distlib/__init__.py,sha256=7B6hxGONsbhxuy9NCkPGnXHuuV4EVLwRGl3qwkXrEn8,581 -pip/_vendor/distlib/compat.py,sha256=13653e8hB0lLj87tSxctbi4I9zC3VtCjJxjwQ3yxwAI,40660 -pip/_vendor/distlib/database.py,sha256=rZHwz4NcZZ7HtXH4SNca6ITyYBSvalvPXZmIj1BjNP8,49671 -pip/_vendor/distlib/index.py,sha256=qU38JCRI5F_1Z-QmydiocDE58d4KvHzKjS_T0dBsPlg,20976 -pip/_vendor/distlib/locators.py,sha256=LhEPbiagqlvOA6PWGuN-AGVDeakS3ykStoe_OYBkAUE,50493 -pip/_vendor/distlib/manifest.py,sha256=JF5EstaCOPnsW2tUdXCdjIFn-Zkf48Dqw0TcxKjuni0,13598 -pip/_vendor/distlib/markers.py,sha256=iRrVWwpyVwjkKJSX8NEQ92_MRMwpROcfNGKCD-Ch1QM,6282 -pip/_vendor/distlib/metadata.py,sha256=UJdzZIbC6Bkp1H9BtiOLXzph6HVujHKcUW1yDgO9oJ0,38702 -pip/_vendor/distlib/resources.py,sha256=7hQ2OgeRRwMrU81hztflAy3b0no-JOtUGCAIC8GImtk,10620 -pip/_vendor/distlib/scripts.py,sha256=ZVGGXYJwW06rIT5gOhO0fvc5CO2Q89LVVNYNF2XVcIY,15223 -pip/_vendor/distlib/t32.exe,sha256=rOJD6eDYk88TJ5lJtyt58El-nYNip4UvnYIDJ2y6QNs,89088 -pip/_vendor/distlib/t64.exe,sha256=qDBQu9uPHCVdBFM6ANg-Xp9nc5Wz_iFnSmsCTvdEQec,97792 -pip/_vendor/distlib/util.py,sha256=DXYa1CiOqPEJE77L6bx6mwNiQE1ayU8LCr2ux8mLBMo,52032 -pip/_vendor/distlib/version.py,sha256=_XjbARzhJfjH7MyFyBr3X5NWzSt7pY73la5KCWbD4Sc,23711 -pip/_vendor/distlib/w32.exe,sha256=LrnXXqK-Yb1tzS0lxymvQPiMlkQZWAB0eHM5jnL0mAk,85504 -pip/_vendor/distlib/w64.exe,sha256=GbKq4oBmzHZXdcpaLupKLenmQD7_DXsYX8PDPWo_U3M,94208 -pip/_vendor/distlib/wheel.py,sha256=UP53cKxOM5r7bHSS-n5prF6hwJEVsMW9ZNJutOuC26c,39115 -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=AUi8718iRoJ9K26mRi-rywtt8Gx7ykvrvbUbZszjfYE,25650 -pip/_vendor/distlib/_backport/sysconfig.cfg,sha256=swZKxq9RY5e9r3PXCrlvQPMsvOdiWZBTHLEbqS8LJLU,2617 -pip/_vendor/distlib/_backport/sysconfig.py,sha256=7WdYP0wbw8izH1eAEGNA-HXUyJrhzIAGK_LniUs4UNI,26958 -pip/_vendor/distlib/_backport/tarfile.py,sha256=bjyTNONZb-YEXrHFLExOSuagtSOoPaONP2UUoxwkAqE,92627 -pip/_vendor/html5lib/__init__.py,sha256=emLYTm9_7OPnOInA9dPc4IIVF7GWA5tpl2H_bLqVoWk,779 -pip/_vendor/html5lib/constants.py,sha256=B5LN2DMP-6lEp9wpON4ecX3Kx01n_cbMjuGd6AteixE,86873 -pip/_vendor/html5lib/html5parser.py,sha256=o9FOrhdLGYOtUhsueAsuXwduydagASvwxJ0lUpGYrYg,117347 -pip/_vendor/html5lib/ihatexml.py,sha256=MT12cVXAKaW-ALUkUeN175HpUP73xK8wAIpPzQ8cgfI,16581 -pip/_vendor/html5lib/inputstream.py,sha256=ss3wjtlObOVoVGWFsBztYdpnUqRaezyJ0sTXfdb4Ly4,31665 -pip/_vendor/html5lib/sanitizer.py,sha256=sbyGySzFzCD_v0JYYSr6sLYVLpO6bpVmRiDMKbFRcCw,17804 -pip/_vendor/html5lib/tokenizer.py,sha256=6Uf8sDUkvNn661bcBSBYUCTfXzSs9EyCTiPcj5PAjYI,76929 -pip/_vendor/html5lib/utils.py,sha256=PSVv1ig9oAZa-DU16DT4cbbggnG7K3qQIkPm6uJKxFg,3267 -pip/_vendor/html5lib/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip/_vendor/html5lib/filters/_base.py,sha256=z-IU9ZAYjpsVsqmVt7kuWC63jR11hDMr6CVrvuao8W0,286 -pip/_vendor/html5lib/filters/alphabeticalattributes.py,sha256=fpRLbz6TCe5yXEkGmyMlJ80FekWsTR-sHk3Ano0U9LQ,624 -pip/_vendor/html5lib/filters/inject_meta_charset.py,sha256=xllv1I7unxhcyZTf3LTsv30wh2mAkT7wmTZx7zIhpuY,2746 -pip/_vendor/html5lib/filters/lint.py,sha256=8eJo0SXDcY40OhsNd0Cft36kUXCZ5t-30mNFSUf4LnE,4208 -pip/_vendor/html5lib/filters/optionaltags.py,sha256=4ozLwBgMRaxe7iqxefLQpDhp3irK7YHo9LgSGsvZYMw,10500 -pip/_vendor/html5lib/filters/sanitizer.py,sha256=MvGUs_v2taWPgGhjxswRSUiHfxrqMUhsNPz-eSeUYUQ,352 -pip/_vendor/html5lib/filters/whitespace.py,sha256=LbOUcC0zQ9z703KNZrArOr0kVBO7OMXjKjucDW32LU4,1142 -pip/_vendor/html5lib/serializer/__init__.py,sha256=xFXFP-inaTNlbnau5c5DGrH_O8yPm-C6HWbJxpiSqFE,490 -pip/_vendor/html5lib/serializer/htmlserializer.py,sha256=G-aVHmlR7uMR011jO0ev7sZvkVHpLr3OrLSYMZ7liVs,12855 -pip/_vendor/html5lib/treeadapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip/_vendor/html5lib/treeadapters/sax.py,sha256=3of4vvaUYIAic7pngebwJV24hpOS7Zg9ggJa_WQegy4,1661 -pip/_vendor/html5lib/treebuilders/__init__.py,sha256=Xz4X6B5DA1R-5GyRa44j0sJwfl6dUNyb0NBu9-7sK3U,3405 -pip/_vendor/html5lib/treebuilders/_base.py,sha256=Xf0FZVcVwIQS6tEseJdj5wKbYucbNCnbAsnsG4lONis,13711 -pip/_vendor/html5lib/treebuilders/dom.py,sha256=jvmtvnERtpxXpHvBgiq1FpzAUYAAzoolOTx_DoXwGEI,8469 -pip/_vendor/html5lib/treebuilders/etree.py,sha256=etbO6yQlyV46rWlj9mSyVqQOWrgoHgyJ01Tut4lWZkk,12621 -pip/_vendor/html5lib/treebuilders/etree_lxml.py,sha256=z3Bnfm2MstEEb_lbaAeicl5l-ab6MSQa5Q1ZZreK7Pc,14031 -pip/_vendor/html5lib/treewalkers/__init__.py,sha256=m2-4a5P4dMNlQb26MNIhgj69p6ms1i-JD2HPDr7iTfw,5766 -pip/_vendor/html5lib/treewalkers/_base.py,sha256=9nXtXtgubdWKFlKxhVzWarE0Hiv3T4VC7_Wt9ulVzB0,7014 -pip/_vendor/html5lib/treewalkers/dom.py,sha256=Lb63Nuz8HtgvkuuvSmU5LOyUkEtstH5saPPAg5xN4r8,1421 -pip/_vendor/html5lib/treewalkers/etree.py,sha256=966h5cOYPwQIcHjxJmVP5cDOnWKiyqpyt9QKQo9W-uo,4597 -pip/_vendor/html5lib/treewalkers/genshistream.py,sha256=IbBFrlgi-59-K7P1zm0d7ZFIknBN4c5E57PHJDkx39s,2278 -pip/_vendor/html5lib/treewalkers/lxmletree.py,sha256=am6t_JHh_Fpm10CaW-zDaGGsDwTPK3Pas7TRBezFs4w,5992 -pip/_vendor/html5lib/treewalkers/pulldom.py,sha256=9W6i8yWtUzayV6EwX-okVacttHaqpQZwdBCc2S3XeQ4,2302 -pip/_vendor/html5lib/trie/__init__.py,sha256=mec5zyJ5wIKRM8819gIcIsYQwncg91rEmPwGH1dG3Ho,212 -pip/_vendor/html5lib/trie/_base.py,sha256=WGY8SGptFmx4O0aKLJ54zrIQOoyuvhS0ngA36vAcIcc,927 -pip/_vendor/html5lib/trie/datrie.py,sha256=EQpqSfkZRuTbE-DuhW7xMdVDxdZNZ0CfmnYfHA_3zxM,1178 -pip/_vendor/html5lib/trie/py.py,sha256=wXmQLrZRf4MyWNyg0m3h81m9InhLR7GJ002mIIZh-8o,1775 -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/packaging/__about__.py,sha256=E9KR5UJ_8U9K-R1mScu6FmkXtbnlFEEFjEXJp-7LxNU,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=RImECJ4c_wTlaTYYwZYLHEiebDMaAJmK1oPARhw1T5o,1416 -pip/_vendor/packaging/markers.py,sha256=GAjjUG2SkQzc3fkG19g_oK4s5QhR9ySIq25I3ZWJWn8,7921 -pip/_vendor/packaging/requirements.py,sha256=SD7dVJGjdPUqtoHb47qwK6wWJTQd-ZXWjxpJg83UcBA,4327 -pip/_vendor/packaging/specifiers.py,sha256=SAMRerzO3fK2IkFZCaZkuwZaL_EGqHNOz4pni4vhnN0,28025 -pip/_vendor/packaging/utils.py,sha256=3m6WvPm6NNxE8rkTGmn0r75B_GZSGg7ikafxHsBN1WA,421 -pip/_vendor/packaging/version.py,sha256=OwGnxYfr2ghNzYx59qWIBkrK3SnB6n-Zfd1XaLpnnM0,11556 -pip/_vendor/pkg_resources/__init__.py,sha256=UaN-6iF8-E8TVvI4tJ8TWeeE34lzATAfYVoc3REb_yg,101295 -pip/_vendor/progress/__init__.py,sha256=Wn1074LUDZovd4zfoVYojnPBgOc6ctHbQX7rp_p8lRA,3023 -pip/_vendor/progress/bar.py,sha256=YNPJeRrwYVKFO2nyaEwsQjYByamMWTgJMvQO1NpD-AY,2685 -pip/_vendor/progress/counter.py,sha256=kEqA8jWEdwrc6P_9VaRx7bjOHwk9gxl-Q9oVbQ08v5c,1502 -pip/_vendor/progress/helpers.py,sha256=FehfwZTv-5cCfsbcMlvlUkm3xZ0cRhsev6XVpmeTF4c,2854 -pip/_vendor/progress/spinner.py,sha256=iCVtUQbaJUFHTjn1ZLPQLPYeao4lC9aXAa_HxIeUK6k,1314 -pip/_vendor/requests/__init__.py,sha256=ZaQ9ZpESi8Zhi-SHxMJwAqs_7En4GlvjEa-uJOQaQxc,2215 -pip/_vendor/requests/adapters.py,sha256=eKKxnQoL8yFVb3Hh607POK0dBm2gu50fgQTtSX-lJy8,18680 -pip/_vendor/requests/api.py,sha256=MCiVT9TG56jThCzrfy1pLQVOCXxaTOzNHj8HNKDu1P4,5795 -pip/_vendor/requests/auth.py,sha256=YBFhR0KH8og250hDRTYgC9KBqMVxh9yMjpZh0-kXefI,8075 -pip/_vendor/requests/cacert.pem,sha256=5xzWFRrSP0ZsXiW6emg8UQ_w497lT4qWCv32OO8R1ME,344712 -pip/_vendor/requests/certs.py,sha256=RX5H1cSiB52Hbjh_qv3eMW8hqHEF_r4Qiv_4AwfziuU,613 -pip/_vendor/requests/compat.py,sha256=hq7CKHoykNs8yzKPAJiOkHQJPoNp9A89MufTdhlCniY,1469 -pip/_vendor/requests/cookies.py,sha256=ShZ9lzb0qhTV03nhafCMQXrnugBGr32M30FEo5u6rkQ,17564 -pip/_vendor/requests/exceptions.py,sha256=lyzK5I-zkNCN9zfYGJgkDMvtt3akjw0QUq4q8pYI4wA,2776 -pip/_vendor/requests/hooks.py,sha256=jSdmZjB5oRJ6xmKM3VtqkYkq8oFTwx6gqG9AaUWpAlw,767 -pip/_vendor/requests/models.py,sha256=17T7cPYBk4JggKQxaeGMWWDB5KqLG8u6QiBXaH7IFYY,29530 -pip/_vendor/requests/sessions.py,sha256=K6tPEgZ4giLZ5sQg3QymC26lcsmFvwtZ044g2hrY3WE,24897 -pip/_vendor/requests/status_codes.py,sha256=QCHM7hbByBJCcPxnN63tCBYkJLjE04umd5m5DSZk-FE,3315 -pip/_vendor/requests/structures.py,sha256=TJ-VyA0GJwzMtLw2A9AqiHWsjvnzjf0s1AHGYc7n-pM,3017 -pip/_vendor/requests/utils.py,sha256=KQwDsJdE5DMGmSdRmBVHsdIVDak4rmK41gKOYR9WhfU,22228 -pip/_vendor/requests/packages/__init__.py,sha256=CVheqNRcXIkAi5037RhxeqbAqd0QhrK1o9R9kS2xvuI,1384 -pip/_vendor/requests/packages/chardet/__init__.py,sha256=XuTKCYOR7JwsoHxqZTYH86LVyMDbDI3s1s0W_qoGEBM,1295 -pip/_vendor/requests/packages/chardet/big5freq.py,sha256=D8oTdz-GM7Jg8TsaWJDm65vM_OLHC3xub6qUJ3rOgsQ,82594 -pip/_vendor/requests/packages/chardet/big5prober.py,sha256=XX96C--6WKYW36mL-z7pJSAtc169Z8ZImByCP4pEN9A,1684 -pip/_vendor/requests/packages/chardet/chardetect.py,sha256=f4299UZG6uWd3i3r_N0OdrFj2sA9JFI54PAmDLAFmWA,2504 -pip/_vendor/requests/packages/chardet/chardistribution.py,sha256=cUARQFr1oTLXeJCDQrDRkUP778AvSMzhSCnG8VLCV58,9226 -pip/_vendor/requests/packages/chardet/charsetgroupprober.py,sha256=0lKk7VE516fgMw119tNefFqLOxKfIE9WfdkpIT69OKU,3791 -pip/_vendor/requests/packages/chardet/charsetprober.py,sha256=Z48o2KiOj23FNqYH8FqzhH5m1qdm3rI8DcTm2Yqtklg,1902 -pip/_vendor/requests/packages/chardet/codingstatemachine.py,sha256=E85rYhHVMw9xDEJVgiQhp0OnLGr6i2r8_7QOWMKTH08,2318 -pip/_vendor/requests/packages/chardet/compat.py,sha256=5mm6yrHwef1JEG5OxkPJlSq5lkjLVpEGh3iPgFBkpkM,1157 -pip/_vendor/requests/packages/chardet/constants.py,sha256=-UnY8U7EP7z9fTyd09yq35BEkSFEAUAiv9ohd1DW1s4,1335 -pip/_vendor/requests/packages/chardet/cp949prober.py,sha256=FMvdLyB7fejPXRsTbca7LK1P3RUvvssmjUNyaEfz8zY,1782 -pip/_vendor/requests/packages/chardet/escprober.py,sha256=q5TcQKeVq31WxrW7Sv8yjpZkjEoaHO8S92EJZ9hodys,3187 -pip/_vendor/requests/packages/chardet/escsm.py,sha256=7iljEKN8lXTh8JFXPUSwlibMno6R6ksq4evLxbkzfro,7839 -pip/_vendor/requests/packages/chardet/eucjpprober.py,sha256=5IpfSEjAb7h3hcGMd6dkU80O900C2N6xku28rdYFKuc,3678 -pip/_vendor/requests/packages/chardet/euckrfreq.py,sha256=T5saK5mImySG5ygQPtsp6o2uKulouCwYm2ElOyFkJqU,45978 -pip/_vendor/requests/packages/chardet/euckrprober.py,sha256=Wo7dnZ5Erw_nB4H-m5alMiOxOuJUmGHlwCSaGqExDZA,1675 -pip/_vendor/requests/packages/chardet/euctwfreq.py,sha256=G_I0BW9i1w0ONeeUwIYqV7_U09buIHdqh-wNHVaql7I,34872 -pip/_vendor/requests/packages/chardet/euctwprober.py,sha256=upS2P6GuT5ujOxXYw-RJLcT7A4PTuo27KGUKU4UZpIQ,1676 -pip/_vendor/requests/packages/chardet/gb2312freq.py,sha256=M2gFdo_qQ_BslStEchrPW5CrPEZEacC0uyDLw4ok-kY,36011 -pip/_vendor/requests/packages/chardet/gb2312prober.py,sha256=VWnjoRa83Y6V6oczMaxyUr0uy48iCnC2nzk9zfEIRHc,1681 -pip/_vendor/requests/packages/chardet/hebrewprober.py,sha256=8pdoUfsVXf_L4BnJde_BewS6H2yInV5688eu0nFhLHY,13359 -pip/_vendor/requests/packages/chardet/jisfreq.py,sha256=ZcL4R5ekHHbP2KCYGakVMBsiKqZZZAABzhwi-uRkOps,47315 -pip/_vendor/requests/packages/chardet/jpcntx.py,sha256=yftmp0QaF6RJO5SJs8I7LU5AF4rwP23ebeCQL4BM1OY,19348 -pip/_vendor/requests/packages/chardet/langbulgarianmodel.py,sha256=ZyPsA796MSVhYdfWhMCgKWckupAKAnKqWcE3Cl3ej6o,12784 -pip/_vendor/requests/packages/chardet/langcyrillicmodel.py,sha256=fkcd5OvogUp-GrNDWAZPgkYsSRCD2omotAEvqjlmLKE,17725 -pip/_vendor/requests/packages/chardet/langgreekmodel.py,sha256=QHMy31CH_ot67UCtmurCEKqKx2WwoaKrw2YCYYBK2Lw,12628 -pip/_vendor/requests/packages/chardet/langhebrewmodel.py,sha256=4ASl5vzKJPng4H278VHKtRYC03TpQpenlHTcsmZH1rE,11318 -pip/_vendor/requests/packages/chardet/langhungarianmodel.py,sha256=SXwuUzh49_cBeMXhshRHdrhlkz0T8_pZWV_pdqBKNFk,12536 -pip/_vendor/requests/packages/chardet/langthaimodel.py,sha256=-k7djh3dGKngAGnt3WfuoJN7acDcWcmHAPojhaUd7q4,11275 -pip/_vendor/requests/packages/chardet/latin1prober.py,sha256=238JHOxH8aRudJY2NmeSv5s7i0Qe3GuklIU3HlYybvg,5232 -pip/_vendor/requests/packages/chardet/mbcharsetprober.py,sha256=9rOCjDVsmSMp6e7q2syqak22j7lrbUZhJhMee2gbVL0,3268 -pip/_vendor/requests/packages/chardet/mbcsgroupprober.py,sha256=SHRzNPLpDXfMJLA8phCHVU0WgqbgDCNxDQMolGX_7yk,1967 -pip/_vendor/requests/packages/chardet/mbcssm.py,sha256=IKwJXyxu34n6NojmxVxC60MLFtJKm-hIfxaFEnb3uBA,19590 -pip/_vendor/requests/packages/chardet/sbcharsetprober.py,sha256=Xq0lODqJnDgxglBiQI4BqTFiPbn63-0a5XNA5-hVu7U,4793 -pip/_vendor/requests/packages/chardet/sbcsgroupprober.py,sha256=8hLyH8RAG-aohBo7o_KciWVgRo42ZE_zEtuNG1JMRYI,3291 -pip/_vendor/requests/packages/chardet/sjisprober.py,sha256=UYOmiMDzttYIkSDoOB08UEagivJpUXz4tuWiWzTiOr8,3764 -pip/_vendor/requests/packages/chardet/universaldetector.py,sha256=h-E2x6XSCzlNjycYWG0Fe4Cf1SGdaIzUNu2HCphpMZA,6840 -pip/_vendor/requests/packages/chardet/utf8prober.py,sha256=7tdNZGrJY7jZUBD483GGMkiP0Tx8Fp-cGvWHoAsilHg,2652 -pip/_vendor/requests/packages/urllib3/__init__.py,sha256=dgnv71-LhuNA7j-ov1A7TL7h4Ajq5JT1fONyGO8ivCg,2854 -pip/_vendor/requests/packages/urllib3/_collections.py,sha256=RP-cHyTx4AgYwvoETK8q1IVRbWFJnE0VV692ZHSbU68,10553 -pip/_vendor/requests/packages/urllib3/connection.py,sha256=QCmkelYgtbc06DfJtgs22na78kRTLCTbLb-OSWLbt-A,11617 -pip/_vendor/requests/packages/urllib3/connectionpool.py,sha256=ws48c_kVg09RUxuqpusWg_oOXIRTf25y3SLVN6yuCcQ,32713 -pip/_vendor/requests/packages/urllib3/exceptions.py,sha256=zGjhZCR1wefEnCN5b7WouQ3UhXesJ2bRKYIeWusaFJs,5599 -pip/_vendor/requests/packages/urllib3/fields.py,sha256=WUMvCLvnw7XemBq6AmCgNPJwyIJL_vWaMHaA2FLlscM,5931 -pip/_vendor/requests/packages/urllib3/filepost.py,sha256=NvLlFsdt8ih_Q4S2ekQF3CJG0nOXs32YI-G04_AdT2g,2320 -pip/_vendor/requests/packages/urllib3/poolmanager.py,sha256=W09uewCGoKSzezei0DwaTXT7kuvsF2elO2wUXWfiAco,9614 -pip/_vendor/requests/packages/urllib3/request.py,sha256=jET7OvA3FSjxABBRGhCyMdPvM9XuJA6df9gRhkJiJiY,5988 -pip/_vendor/requests/packages/urllib3/response.py,sha256=Xx9-heNZOrcx5suX-I1PYT_uEbhVtqOHqkui309g9XY,18542 -pip/_vendor/requests/packages/urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip/_vendor/requests/packages/urllib3/contrib/appengine.py,sha256=VP10uoVbNpH0kYVbOFd7dN5dtDcVfEytMoriKsDBBuI,7938 -pip/_vendor/requests/packages/urllib3/contrib/ntlmpool.py,sha256=r-vMDMXAGbix9a7-IhbKVTATmAst-5g4hKYOLf8Kd5M,4531 -pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py,sha256=JsdAh0gL4XvQzhOEBRoFtJN91qLf1LFIDEFZs95445I,11778 -pip/_vendor/requests/packages/urllib3/contrib/socks.py,sha256=mAHrChjtbElhGBoHOGq5CqRjtmdFiS_YjnTr2HlUNYU,5669 -pip/_vendor/requests/packages/urllib3/packages/__init__.py,sha256=nlChrGzkjCkmhCX9HrF_qHPUgosfsPQkVIJxiiLhk9g,109 -pip/_vendor/requests/packages/urllib3/packages/ordered_dict.py,sha256=VQaPONfhVMsb8B63Xg7ZOydJqIE_jzeMhVN3Pec6ogw,8935 -pip/_vendor/requests/packages/urllib3/packages/six.py,sha256=U-rO-WBrFS8PxHeamSl6okKCjqPF18NhiZb0qPZ67XM,11628 -pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py,sha256=cOWMIn1orgJoA35p6pSzO_-Dc6iOX9Dhl6D2sL9b_2o,460 -pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py,sha256=fK28k37hL7-D79v9iM2fHgNK9Q1Pw0M7qVRL4rkfFjQ,3778 -pip/_vendor/requests/packages/urllib3/util/__init__.py,sha256=n2QE9_0Bb6u8tf7LUc4qKe8V-Hz9G8lEOc9j_30Q8d0,892 -pip/_vendor/requests/packages/urllib3/util/connection.py,sha256=6PvDBlK_6QDLHzEDT-uEMhqKcDoSuRO43Vtb4IXfkzQ,3380 -pip/_vendor/requests/packages/urllib3/util/request.py,sha256=ZMDewRK-mjlK72szGIIjzYnLIn-zPP0WgJUMjKeZ6Tg,2128 -pip/_vendor/requests/packages/urllib3/util/response.py,sha256=1UFd5TIp9MyBp4xgnZoyQZscZVPPr0tWRaXNR5w_vds,2165 -pip/_vendor/requests/packages/urllib3/util/retry.py,sha256=WjO8iuwSR-TFJUPW1OhuuhvNReZHJ8v_QEPThdUzOLE,10350 -pip/_vendor/requests/packages/urllib3/util/ssl_.py,sha256=eVPo9L7d8x8g1wnBi2swWM49AXu5I2pKXKAIHs2ar_k,11624 -pip/_vendor/requests/packages/urllib3/util/timeout.py,sha256=ioAIYptFyBG7eU_r8_ZmO45hpj1dJE6WCvrGR9dNFjs,9596 -pip/_vendor/requests/packages/urllib3/util/url.py,sha256=EcX4ZfmgKWcqM4sY9FlC-yN4y_snuURPV0TpUPHNjnc,5879 -pip/commands/__init__.py,sha256=naZ1iIWRutNznOVpLj8qyn1GPE0B5rhCWCrSUOZSt4M,2145 -pip/commands/completion.py,sha256=2BEUY3jowgemiIGgUP3rpk6A9My4Eu8rTPosFxlESOE,1967 -pip/commands/download.py,sha256=dMRtH0JMBhNGlJWr1qC29vOeiBzG2K0OjOAfzdxSVgA,4804 -pip/commands/freeze.py,sha256=KmQoLf-HruqBDzc-F2-ganGVn2lboNQqppfyrMsx3SU,2774 -pip/commands/hash.py,sha256=MCt4jEFyfoce0lVeNEz1x49uaTY-VDkKiBvvxrVcHkw,1597 -pip/commands/help.py,sha256=84HWkEdnGP_AEBHnn8gJP2Te0XTXRKFoXqXopbOZTNo,982 -pip/commands/install.py,sha256=8MOsH3IlL3ovZhTQtZwHhJb19pnkr8eKNE_9klVJ3PU,14971 -pip/commands/list.py,sha256=u76U5TLODQ2g53sSUA4q6WhYus7usbuWuITQJsCnP3E,7412 -pip/commands/search.py,sha256=qOfCTw9wEN6_lVBSCLjOHiPrTGBMx4n8_ZCIWgIKL0M,4816 -pip/commands/show.py,sha256=dytBbI9XV-ChpV51tsuBygZJJO-QaO2Gtz5kbLkBCZE,5815 -pip/commands/uninstall.py,sha256=tz8cXz4WdpUdnt3RvpdQwH6_SNMB50egBIZWa1dwfcc,2884 -pip/commands/wheel.py,sha256=iT92Uo8qpVILl_Yk8L7AtkFVYGmY0ep5oDeyQSpwkLs,7528 -pip/compat/__init__.py,sha256=7WN0B0XMYIldfminnT679VoEJLxNQPi9MFwCIt1_llU,4669 -pip/compat/dictconfig.py,sha256=dRrelPDWrceDSzFT51RTEVY2GuM7UDyc5Igh_tn4Fvk,23096 -pip/compat/ordereddict.py,sha256=6RQCd4PyTE4tvLUoAnsygvrreOSTV4BRDbc_4gCSkTs,4110 -pip/models/__init__.py,sha256=0Rs7_RA4DxeOkWT5Cq4CQzDrSEhvYcN3TH2cazr72PE,71 -pip/models/index.py,sha256=pUfbO__v3mD9j-2n_ClwPS8pVyx4l2wIwyvWt8GMCRA,487 -pip/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip/operations/freeze.py,sha256=H6xpxe1XgoNm5f3UXK47kNy0OQfM5jzo4UUwQu7G-Lo,4048 -pip/req/__init__.py,sha256=vFwZY8_Vc1WU1zFAespg1My_r_AT3n7cN0W9eX0EFqk,276 -pip/req/req_file.py,sha256=3eaVnPMUAjikLdC5i8hZUAf8aAOby2UxmAVFf94FOXY,11928 -pip/req/req_install.py,sha256=ArdEHUTrB3Xvyy_lWop2V0c_8ArTgpZdpR8shXrJ8eU,45583 -pip/req/req_set.py,sha256=5gLPqd4vDbpl7mz9wOYPgpqIoB-EsjI0hcFuSzC8dw8,32764 -pip/req/req_uninstall.py,sha256=fdH2VgCjEC8NRYDS7fRu3ZJaBBUEy-N5muwxDX5MBNM,6897 -pip/utils/__init__.py,sha256=WdAVVUl5j0OQ3u3eR3Qtu3Um3OmeVflR7W1BmMmvmDU,27531 -pip/utils/appdirs.py,sha256=KTpZANfjYw5K2tZ0_jNNdP_kMxQAns79qZWelwaJo0c,7896 -pip/utils/build.py,sha256=4smLRrfSCmXmjEnVnMFh2tBEpNcSLRe6J0ejZJ-wWJE,1312 -pip/utils/deprecation.py,sha256=DR3cKqzovYu9Pif7c9bT2KmwekfW95N3BsI45_5u38I,2239 -pip/utils/encoding.py,sha256=NQxGiFS5GbeAveLZTnx92t5r0PYqvt0iRnP2u9SGG1w,971 -pip/utils/filesystem.py,sha256=ZEVBuYM3fqr2_lgOESh4Y7fPFszGD474zVm_M3Mb5Tk,899 -pip/utils/hashes.py,sha256=oMk7cd3PbJgzpSQyXq1MytMud5f6H5Oa2YY5hYuCq6I,2866 -pip/utils/logging.py,sha256=7yWu4gZw-Qclj7X80QVdpGWkdTWGKT4LiUVKcE04pro,3327 -pip/utils/outdated.py,sha256=fNwOCL5r2EftPGhgCYGMKu032HC8cV-JAr9lp0HmToM,5455 -pip/utils/setuptools_build.py,sha256=8IGop-SZ6lxUl5HMOjLRaDlORPugIH_b_b2Y67x4jQc,240 -pip/utils/ui.py,sha256=pbDkSAeumZ6jdZcOJ2yAbx8iBgeP2zfpqNnLJK1gskQ,11597 -pip/vcs/__init__.py,sha256=lnea41zMq9HqB1Qo7hxy2IjUzk5WtBvnoloCCMR6Vk4,12349 -pip/vcs/bazaar.py,sha256=tYTwc4b4off8mr0O2o8SiGejqBDJxcbDBMSMd9-ISYc,3803 -pip/vcs/git.py,sha256=u16VCiNW_a9AaYqLri2b8-f4lOZlOYwsGpHHV3uv_dQ,10218 -pip/vcs/mercurial.py,sha256=xG6rDiwHCRytJEs23SIHBXl_SwQo2jkkdD_6rVVP5h4,3472 -pip/vcs/subversion.py,sha256=mGT7sAzuVc1u-9MPoXJNyShnRzhdJpDdGNuhhzUPv6w,8687 -pip-8.1.2.dist-info/DESCRIPTION.rst,sha256=jSvW1qOjwzndvm_p_DexGCVJfwgg3rWPMJWzf6Rmsfc,1167 -pip-8.1.2.dist-info/METADATA,sha256=P3mID1oPoIKxUDsH1D5yk4h6P4N1T5t0sosQul1OMx4,2362 -pip-8.1.2.dist-info/RECORD,, -pip-8.1.2.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 -pip-8.1.2.dist-info/entry_points.txt,sha256=GWc-Wb9WUKZ1EuVWNz-G0l3BeIpbNJLx0OJbZ61AAV0,68 -pip-8.1.2.dist-info/metadata.json,sha256=pGIpFivM6SXoww8RYGdf33RD9yK7EFA2eAgvEP7SlLg,1513 -pip-8.1.2.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -../../../bin/pip,sha256=2LpQhjQy1UtjEfjAoFcnLkwS7dWpYH2wN2tpGsU6FBc,221 -../../../bin/pip2,sha256=2LpQhjQy1UtjEfjAoFcnLkwS7dWpYH2wN2tpGsU6FBc,221 -../../../bin/pip2.7,sha256=2LpQhjQy1UtjEfjAoFcnLkwS7dWpYH2wN2tpGsU6FBc,221 -pip-8.1.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -pip/_vendor/requests/compat.pyc,, -pip/_vendor/cachecontrol/cache.pyc,, -pip/_vendor/requests/certs.pyc,, -pip/_vendor/requests/adapters.pyc,, -pip/_vendor/progress/spinner.pyc,, -pip/__init__.pyc,, -pip/_vendor/html5lib/treewalkers/genshistream.pyc,, -pip/_vendor/requests/packages/chardet/latin1prober.pyc,, -pip/_vendor/distlib/_backport/__init__.pyc,, -pip/vcs/bazaar.pyc,, -pip/_vendor/distlib/metadata.pyc,, -pip/compat/__init__.pyc,, -pip/_vendor/html5lib/treebuilders/__init__.pyc,, -pip/_vendor/html5lib/filters/alphabeticalattributes.pyc,, -pip/_vendor/requests/packages/urllib3/request.pyc,, -pip/_vendor/requests/packages/chardet/escprober.pyc,, -pip/_vendor/distlib/util.pyc,, -pip/_vendor/requests/packages/chardet/langbulgarianmodel.pyc,, -pip/commands/completion.pyc,, -pip/_vendor/html5lib/html5parser.pyc,, -pip/_vendor/html5lib/filters/lint.pyc,, -pip/pep425tags.pyc,, -pip/_vendor/packaging/__init__.pyc,, -pip/_vendor/lockfile/symlinklockfile.pyc,, -pip/_vendor/requests/packages/chardet/__init__.pyc,, -pip/_vendor/packaging/utils.pyc,, -pip/_vendor/html5lib/filters/whitespace.pyc,, -pip/_vendor/requests/packages/chardet/euckrprober.pyc,, -pip/_vendor/html5lib/treebuilders/etree_lxml.pyc,, -pip/_vendor/distlib/database.pyc,, -pip/_vendor/html5lib/treeadapters/sax.pyc,, -pip/commands/wheel.pyc,, -pip/_vendor/requests/auth.pyc,, -pip/_vendor/html5lib/treewalkers/pulldom.pyc,, -pip/_vendor/packaging/__about__.pyc,, -pip/_vendor/progress/__init__.pyc,, -pip/_vendor/cachecontrol/_cmd.pyc,, -pip/_vendor/html5lib/ihatexml.pyc,, -pip/_vendor/html5lib/treewalkers/_base.pyc,, -pip/commands/list.pyc,, -pip/_vendor/distlib/scripts.pyc,, -pip/_vendor/html5lib/filters/sanitizer.pyc,, -pip/vcs/git.pyc,, -pip/cmdoptions.pyc,, -pip/_vendor/lockfile/linklockfile.pyc,, -pip/commands/hash.pyc,, -pip/_vendor/requests/packages/urllib3/util/ssl_.pyc,, -pip/_vendor/requests/packages/urllib3/poolmanager.pyc,, -pip/req/req_set.pyc,, -pip/_vendor/html5lib/inputstream.pyc,, -pip/req/__init__.pyc,, -pip/_vendor/html5lib/sanitizer.pyc,, -pip/_vendor/colorama/win32.pyc,, -pip/_vendor/distlib/resources.pyc,, -pip/_vendor/requests/packages/chardet/hebrewprober.pyc,, -pip/_vendor/requests/cookies.pyc,, -pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.pyc,, -pip/_vendor/lockfile/__init__.pyc,, -pip/_vendor/requests/packages/urllib3/packages/six.pyc,, -pip/_vendor/progress/helpers.pyc,, -pip/_vendor/ipaddress.pyc,, -pip/_vendor/html5lib/trie/__init__.pyc,, -pip/operations/__init__.pyc,, -pip/_vendor/pyparsing.pyc,, -pip/_vendor/html5lib/__init__.pyc,, -pip/_vendor/requests/packages/chardet/charsetgroupprober.pyc,, -pip/_vendor/cachecontrol/serialize.pyc,, -pip/_vendor/requests/packages/urllib3/packages/__init__.pyc,, -pip/_vendor/distlib/_backport/misc.pyc,, -pip/_vendor/requests/packages/chardet/gb2312freq.pyc,, -pip/_vendor/requests/packages/chardet/codingstatemachine.pyc,, -pip/_vendor/cachecontrol/compat.pyc,, -pip/_vendor/requests/packages/urllib3/util/url.pyc,, -pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/__init__.pyc,, -pip/_vendor/cachecontrol/controller.pyc,, -pip/utils/deprecation.pyc,, -pip/_vendor/requests/packages/chardet/langgreekmodel.pyc,, -pip/_vendor/requests/packages/__init__.pyc,, -pip/commands/show.pyc,, -pip/_vendor/requests/packages/chardet/jpcntx.pyc,, -pip/_vendor/colorama/initialise.pyc,, -pip/_vendor/requests/packages/urllib3/contrib/appengine.pyc,, -pip/_vendor/html5lib/treebuilders/_base.pyc,, -pip/commands/help.pyc,, -pip/_vendor/requests/utils.pyc,, -pip/_vendor/colorama/__init__.pyc,, -pip/_vendor/packaging/_compat.pyc,, -pip/_vendor/distlib/version.pyc,, -pip/utils/ui.pyc,, -pip/commands/uninstall.pyc,, -pip/_vendor/distlib/index.pyc,, -pip/_vendor/cachecontrol/heuristics.pyc,, -pip/commands/freeze.pyc,, -pip/_vendor/requests/sessions.pyc,, -pip/_vendor/requests/packages/chardet/big5prober.pyc,, -pip/_vendor/requests/packages/chardet/langthaimodel.pyc,, -pip/_vendor/distlib/_backport/tarfile.pyc,, -pip/_vendor/requests/packages/urllib3/util/response.pyc,, -pip/_vendor/requests/packages/chardet/mbcsgroupprober.pyc,, -pip/_vendor/html5lib/filters/__init__.pyc,, -pip/baseparser.pyc,, -pip/status_codes.pyc,, -pip/_vendor/cachecontrol/caches/redis_cache.pyc,, -pip/_vendor/distlib/__init__.pyc,, -pip/_vendor/pkg_resources/__init__.pyc,, -pip/_vendor/html5lib/treewalkers/__init__.pyc,, -pip/operations/freeze.pyc,, -pip/commands/search.pyc,, -pip/_vendor/requests/packages/chardet/langhungarianmodel.pyc,, -pip/_vendor/requests/models.pyc,, -pip/_vendor/html5lib/utils.pyc,, -pip/compat/dictconfig.pyc,, -pip/_vendor/html5lib/trie/datrie.pyc,, -pip/_vendor/requests/structures.pyc,, -pip/_vendor/packaging/version.pyc,, -pip/_vendor/requests/packages/chardet/euckrfreq.pyc,, -pip/_vendor/cachecontrol/adapter.pyc,, -pip/_vendor/requests/packages/urllib3/fields.pyc,, -pip/_vendor/requests/packages/urllib3/contrib/ntlmpool.pyc,, -pip/vcs/__init__.pyc,, -pip/req/req_file.pyc,, -pip/models/__init__.pyc,, -pip/_vendor/html5lib/trie/py.pyc,, -pip/_vendor/packaging/specifiers.pyc,, -pip/download.pyc,, -pip/commands/__init__.pyc,, -pip/_vendor/requests/packages/urllib3/__init__.pyc,, -pip/_vendor/distlib/compat.pyc,, -pip/models/index.pyc,, -pip/_vendor/lockfile/mkdirlockfile.pyc,, -pip/_vendor/distlib/wheel.pyc,, -pip/_vendor/requests/packages/chardet/euctwprober.pyc,, -pip/utils/build.pyc,, -pip/_vendor/requests/packages/chardet/escsm.pyc,, -pip/_vendor/requests/status_codes.pyc,, -pip/_vendor/requests/exceptions.pyc,, -pip/_vendor/distlib/markers.pyc,, -pip/index.pyc,, -pip/utils/logging.pyc,, -pip/_vendor/packaging/requirements.pyc,, -pip/_vendor/requests/packages/chardet/cp949prober.pyc,, -pip/utils/outdated.pyc,, -pip/_vendor/requests/api.pyc,, -pip/_vendor/requests/packages/urllib3/filepost.pyc,, -pip/_vendor/requests/packages/chardet/big5freq.pyc,, -pip/_vendor/html5lib/treebuilders/etree.pyc,, -pip/_vendor/cachecontrol/caches/file_cache.pyc,, -pip/_vendor/html5lib/treebuilders/dom.pyc,, -pip/_vendor/requests/packages/chardet/mbcssm.pyc,, -pip/_vendor/distlib/_backport/shutil.pyc,, -pip/_vendor/requests/packages/chardet/sbcsgroupprober.pyc,, -pip/utils/encoding.pyc,, -pip/__main__.pyc,, -pip/_vendor/html5lib/tokenizer.pyc,, -pip/_vendor/lockfile/sqlitelockfile.pyc,, -pip/_vendor/requests/packages/chardet/utf8prober.pyc,, -pip/_vendor/requests/packages/chardet/langhebrewmodel.pyc,, -pip/_vendor/requests/packages/chardet/compat.pyc,, -pip/_vendor/progress/counter.pyc,, -pip/utils/filesystem.pyc,, -pip/_vendor/six.pyc,, -pip/_vendor/requests/packages/chardet/langcyrillicmodel.pyc,, -pip/_vendor/requests/packages/chardet/constants.pyc,, -pip/_vendor/requests/packages/urllib3/packages/ordered_dict.pyc,, -pip/_vendor/requests/packages/chardet/eucjpprober.pyc,, -pip/_vendor/retrying.pyc,, -pip/utils/hashes.pyc,, -pip/_vendor/html5lib/treewalkers/etree.pyc,, -pip/_vendor/cachecontrol/wrapper.pyc,, -pip/_vendor/requests/hooks.pyc,, -pip/_vendor/packaging/_structures.pyc,, -pip/_vendor/requests/packages/urllib3/connectionpool.pyc,, -pip/_vendor/requests/packages/chardet/mbcharsetprober.pyc,, -pip/locations.pyc,, -pip/_vendor/requests/packages/chardet/jisfreq.pyc,, -pip/utils/setuptools_build.pyc,, -pip/vcs/subversion.pyc,, -pip/_vendor/requests/packages/urllib3/util/connection.pyc,, -pip/exceptions.pyc,, -pip/basecommand.pyc,, -pip/_vendor/distlib/locators.pyc,, -pip/_vendor/html5lib/filters/_base.pyc,, -pip/_vendor/re-vendor.pyc,, -pip/_vendor/requests/packages/urllib3/contrib/__init__.pyc,, -pip/_vendor/requests/packages/chardet/euctwfreq.pyc,, -pip/commands/download.pyc,, -pip/_vendor/requests/packages/chardet/chardistribution.pyc,, -pip/_vendor/cachecontrol/caches/__init__.pyc,, -pip/_vendor/requests/packages/urllib3/contrib/socks.pyc,, -pip/_vendor/distlib/_backport/sysconfig.pyc,, -pip/_vendor/requests/__init__.pyc,, -pip/req/req_uninstall.pyc,, -pip/_vendor/requests/packages/chardet/sbcharsetprober.pyc,, -pip/_vendor/colorama/ansitowin32.pyc,, -pip/_vendor/cachecontrol/filewrapper.pyc,, -pip/_vendor/requests/packages/chardet/sjisprober.pyc,, -pip/_vendor/requests/packages/urllib3/util/timeout.pyc,, -pip/_vendor/html5lib/treewalkers/dom.pyc,, -pip/utils/__init__.pyc,, -pip/_vendor/requests/packages/urllib3/_collections.pyc,, -pip/_vendor/requests/packages/urllib3/util/request.pyc,, -pip/_vendor/requests/packages/urllib3/util/__init__.pyc,, -pip/_vendor/distlib/manifest.pyc,, -pip/_vendor/html5lib/serializer/htmlserializer.pyc,, -pip/_vendor/html5lib/serializer/__init__.pyc,, -pip/_vendor/html5lib/trie/_base.pyc,, -pip/_vendor/requests/packages/urllib3/response.pyc,, -pip/req/req_install.pyc,, -pip/_vendor/html5lib/treeadapters/__init__.pyc,, -pip/_vendor/html5lib/filters/inject_meta_charset.pyc,, -pip/_vendor/requests/packages/chardet/charsetprober.pyc,, -pip/compat/ordereddict.pyc,, -pip/_vendor/requests/packages/urllib3/util/retry.pyc,, -pip/vcs/mercurial.pyc,, -pip/_vendor/cachecontrol/__init__.pyc,, -pip/_vendor/requests/packages/urllib3/exceptions.pyc,, -pip/_vendor/__init__.pyc,, -pip/_vendor/requests/packages/chardet/universaldetector.pyc,, -pip/_vendor/html5lib/constants.pyc,, -pip/_vendor/html5lib/treewalkers/lxmletree.pyc,, -pip/_vendor/colorama/ansi.pyc,, -pip/_vendor/packaging/markers.pyc,, -pip/commands/install.pyc,, -pip/_vendor/requests/packages/chardet/gb2312prober.pyc,, -pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.pyc,, -pip/_vendor/progress/bar.pyc,, -pip/wheel.pyc,, -pip/_vendor/lockfile/pidlockfile.pyc,, -pip/_vendor/requests/packages/chardet/chardetect.pyc,, -pip/_vendor/html5lib/filters/optionaltags.pyc,, -pip/_vendor/requests/packages/urllib3/connection.pyc,, -pip/utils/appdirs.pyc,, -pip/_vendor/colorama/winterm.pyc,, diff --git a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/WHEEL b/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/WHEEL deleted file mode 100644 index 8b6dd1b..0000000 --- a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.29.0) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/entry_points.txt b/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/entry_points.txt deleted file mode 100644 index c02a8d5..0000000 --- a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/entry_points.txt +++ /dev/null @@ -1,5 +0,0 @@ -[console_scripts] -pip = pip:main -pip3 = pip:main -pip3.5 = pip:main - diff --git a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/metadata.json b/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/metadata.json deleted file mode 100644 index 17ac7e0..0000000 --- a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Topic :: Software Development :: Build Tools", "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 :: Implementation :: PyPy"], "extensions": {"python.commands": {"wrap_console": {"pip": "pip:main", "pip3": "pip:main", "pip3.5": "pip:main"}}, "python.details": {"contacts": [{"email": "python-virtualenv@groups.google.com", "name": "The pip developers", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://pip.pypa.io/"}}, "python.exports": {"console_scripts": {"pip": "pip:main", "pip3": "pip:main", "pip3.5": "pip:main"}}}, "extras": ["testing"], "generator": "bdist_wheel (0.29.0)", "keywords": ["easy_install", "distutils", "setuptools", "egg", "virtualenv"], "license": "MIT", "metadata_version": "2.0", "name": "pip", "run_requires": [{"extra": "testing", "requires": ["mock", "pretend", "pytest", "scripttest (>=1.3)", "virtualenv (>=1.10)"]}], "summary": "The PyPA recommended tool for installing Python packages.", "test_requires": [{"requires": ["mock", "pretend", "pytest", "scripttest (>=1.3)", "virtualenv (>=1.10)"]}], "version": "8.1.2"} \ No newline at end of file diff --git a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/top_level.txt b/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/top_level.txt deleted file mode 100644 index a1b589e..0000000 --- a/venv/lib/python2.7/site-packages/pip-8.1.2.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/lib/python2.7/site-packages/pip/__init__.py b/venv/lib/python2.7/site-packages/pip/__init__.py deleted file mode 100644 index 588c881..0000000 --- a/venv/lib/python2.7/site-packages/pip/__init__.py +++ /dev/null @@ -1,319 +0,0 @@ -#!/usr/bin/env python -from __future__ import absolute_import - -import locale -import logging -import os -import optparse -import warnings - -import sys -import re - -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__ = "8.1.2" - - -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 or zsh). - """ - # 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 [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/python2.7/site-packages/pip/__main__.py b/venv/lib/python2.7/site-packages/pip/__main__.py deleted file mode 100644 index 5556539..0000000 --- a/venv/lib/python2.7/site-packages/pip/__main__.py +++ /dev/null @@ -1,19 +0,0 @@ -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/python2.7/site-packages/pip/_vendor/__init__.py b/venv/lib/python2.7/site-packages/pip/_vendor/__init__.py deleted file mode 100644 index d0e7b34..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/__init__.py +++ /dev/null @@ -1,106 +0,0 @@ -""" -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("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/python2.7/site-packages/pip/_vendor/cachecontrol/__init__.py b/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/__init__.py deleted file mode 100644 index 724e220..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -"""CacheControl import Interface. - -Make it easy to import from cachecontrol without long namespaces. -""" -__author__ = 'Eric Larson' -__email__ = 'eric@ionrock.org' -__version__ = '0.11.6' - -from .wrapper import CacheControl -from .adapter import CacheControlAdapter -from .controller import CacheController diff --git a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/_cmd.py b/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/_cmd.py deleted file mode 100644 index afdcc88..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/_cmd.py +++ /dev/null @@ -1,60 +0,0 @@ -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/python2.7/site-packages/pip/_vendor/cachecontrol/adapter.py b/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/adapter.py deleted file mode 100644 index 74589e0..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/adapter.py +++ /dev/null @@ -1,117 +0,0 @@ -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': - - # 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: - # Check for any heuristics that might update headers - # before trying to cache. - if self.heuristic: - response = self.heuristic.apply(response) - - # 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, - ) - ) - - 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/python2.7/site-packages/pip/_vendor/cachecontrol/cache.py b/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/cache.py deleted file mode 100644 index 7389a73..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/cache.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -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/python2.7/site-packages/pip/_vendor/cachecontrol/caches/__init__.py b/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/__init__.py deleted file mode 100644 index f9e66a1..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -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/python2.7/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py b/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py deleted file mode 100644 index b77728f..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py +++ /dev/null @@ -1,116 +0,0 @@ -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/python2.7/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py b/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py deleted file mode 100644 index 9f5d55f..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py +++ /dev/null @@ -1,41 +0,0 @@ -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/python2.7/site-packages/pip/_vendor/cachecontrol/compat.py b/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/compat.py deleted file mode 100644 index 018e6ac..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/compat.py +++ /dev/null @@ -1,20 +0,0 @@ -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/python2.7/site-packages/pip/_vendor/cachecontrol/controller.py b/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/controller.py deleted file mode 100644 index 6e591f8..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/controller.py +++ /dev/null @@ -1,353 +0,0 @@ -""" -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 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/python2.7/site-packages/pip/_vendor/cachecontrol/filewrapper.py b/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/filewrapper.py deleted file mode 100644 index 4b91bce..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/filewrapper.py +++ /dev/null @@ -1,63 +0,0 @@ -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 read(self, amt=None): - data = self.__fp.read(amt) - self.__buf.write(data) - - if self.__is_fp_closed(): - 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 - - return data diff --git a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/heuristics.py b/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/heuristics.py deleted file mode 100644 index 94715a4..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/heuristics.py +++ /dev/null @@ -1,138 +0,0 @@ -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/python2.7/site-packages/pip/_vendor/cachecontrol/serialize.py b/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/serialize.py deleted file mode 100644 index ffbfbf6..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/serialize.py +++ /dev/null @@ -1,190 +0,0 @@ -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") - - 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/python2.7/site-packages/pip/_vendor/cachecontrol/wrapper.py b/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/wrapper.py deleted file mode 100644 index ea421aa..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/cachecontrol/wrapper.py +++ /dev/null @@ -1,21 +0,0 @@ -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/python2.7/site-packages/pip/_vendor/colorama/__init__.py b/venv/lib/python2.7/site-packages/pip/_vendor/colorama/__init__.py deleted file mode 100644 index 670e6b3..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/colorama/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# 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/python2.7/site-packages/pip/_vendor/colorama/ansi.py b/venv/lib/python2.7/site-packages/pip/_vendor/colorama/ansi.py deleted file mode 100644 index 7877658..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/colorama/ansi.py +++ /dev/null @@ -1,102 +0,0 @@ -# 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/python2.7/site-packages/pip/_vendor/colorama/ansitowin32.py b/venv/lib/python2.7/site-packages/pip/_vendor/colorama/ansitowin32.py deleted file mode 100644 index b7ff6f2..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/colorama/ansitowin32.py +++ /dev/null @@ -1,236 +0,0 @@ -# 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/python2.7/site-packages/pip/_vendor/colorama/initialise.py b/venv/lib/python2.7/site-packages/pip/_vendor/colorama/initialise.py deleted file mode 100644 index 834962a..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/colorama/initialise.py +++ /dev/null @@ -1,82 +0,0 @@ -# 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/python2.7/site-packages/pip/_vendor/colorama/win32.py b/venv/lib/python2.7/site-packages/pip/_vendor/colorama/win32.py deleted file mode 100644 index 3d1d2f2..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/colorama/win32.py +++ /dev/null @@ -1,154 +0,0 @@ -# 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/python2.7/site-packages/pip/_vendor/colorama/winterm.py b/venv/lib/python2.7/site-packages/pip/_vendor/colorama/winterm.py deleted file mode 100644 index 60309d3..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/colorama/winterm.py +++ /dev/null @@ -1,162 +0,0 @@ -# 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/python2.7/site-packages/pip/_vendor/distlib/__init__.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/__init__.py deleted file mode 100644 index 7026860..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- 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.3' - -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/python2.7/site-packages/pip/_vendor/distlib/_backport/__init__.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/__init__.py deleted file mode 100644 index f7dbf4c..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""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/python2.7/site-packages/pip/_vendor/distlib/_backport/misc.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/misc.py deleted file mode 100644 index cfb318d..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/misc.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- 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/python2.7/site-packages/pip/_vendor/distlib/_backport/shutil.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/shutil.py deleted file mode 100644 index 9e2e234..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/shutil.py +++ /dev/null @@ -1,761 +0,0 @@ -# -*- 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 registery operation with the archiving - and unpacking registeries 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 registery.""" - 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/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg deleted file mode 100644 index 1746bd0..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg +++ /dev/null @@ -1,84 +0,0 @@ -[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/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.py deleted file mode 100644 index 1d31326..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.py +++ /dev/null @@ -1,788 +0,0 @@ -# -*- 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 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('-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'ProductUserVisibleVersion\s*' - r'(.*?)', 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/python2.7/site-packages/pip/_vendor/distlib/_backport/tarfile.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/tarfile.py deleted file mode 100644 index 0580fb7..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/tarfile.py +++ /dev/null @@ -1,2607 +0,0 @@ -#------------------------------------------------------------------- -# tarfile.py -#------------------------------------------------------------------- -# Copyright (C) 2002 Lars Gustaebel -# 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 unreadble 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(" 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("= 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/python2.7/site-packages/pip/_vendor/distlib/compat.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/compat.py deleted file mode 100644 index 069ec77..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/compat.py +++ /dev/null @@ -1,1102 +0,0 @@ -# -*- 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 - -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, - HTTPSHandler, HTTPHandler, HTTPRedirectHandler, - build_opener) - 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, - HTTPSHandler, HTTPHandler, HTTPRedirectHandler, - build_opener) - 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 survery 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 argment, 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[a-z]+)://(?P.*)$') - - 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/python2.7/site-packages/pip/_vendor/distlib/database.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/database.py deleted file mode 100644 index 7bc1914..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/database.py +++ /dev/null @@ -1,1312 +0,0 @@ -# -*- 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 '' % (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 '' % ( - 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 '' % ( - 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 'Plaeholder for summary' - return Distribution(md) diff --git a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/index.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/index.py deleted file mode 100644 index 73037c9..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/index.py +++ /dev/null @@ -1,513 +0,0 @@ -# -*- 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: - for s in ('gpg2', 'gpg'): - 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 acutal 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', - 'protcol_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/python2.7/site-packages/pip/_vendor/distlib/locators.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/locators.py deleted file mode 100644 index 1e14638..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/locators.py +++ /dev/null @@ -1,1264 +0,0 @@ -# -*- 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, - 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) -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 - - 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: - 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): - name1, name2 = name1.lower(), name2.lower() - if name1 == name2: - result = True - else: - # distribute replaces '-' by '_' in project names, so it - # can tell where the version starts in a filename. - result = name1.replace('_', '-') == name2.replace('_', '-') - return result - - 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: - 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: - 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[^"]*)"|'(?P[^']*)'|(?P[^>\s\n]*))\s+)? -href\s*=\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\s\n]*)) -(\s+rel\s*=\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\s\n]*)))? -""", re.I | re.S | re.X) - _base = re.compile(r"""]+)""", 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) - 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(']*>([^<]+)<') - - 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[\w-]+)\s*' - r'\(\s*(==\s*)?(?P[^)]+)\)$') - -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/python2.7/site-packages/pip/_vendor/distlib/manifest.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/manifest.py deleted file mode 100644 index 21cff45..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/manifest.py +++ /dev/null @@ -1,367 +0,0 @@ -# -*- 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 - -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) - - -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 ...' % 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

...' % 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 ' % 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 pattern: - pattern_re = self._glob_to_re(pattern) - else: - pattern_re = '' - - base = re.escape(os.path.join(self.base, '')) - if prefix is not None: - # ditch end of pattern character - empty_pattern = self._glob_to_re('') - prefix_re = self._glob_to_re(prefix)[:-len(empty_pattern)] - sep = os.sep - if os.sep == '\\': - sep = r'\\' - pattern_re = '^' + base + sep.join((prefix_re, - '.*' + pattern_re)) - else: # no prefix -- respect anchor flag - if anchor: - pattern_re = '^' + base + pattern_re - - 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'((? 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/python2.7/site-packages/pip/_vendor/distlib/metadata.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/metadata.py deleted file mode 100644 index 71525dd..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/metadata.py +++ /dev/null @@ -1,1066 +0,0 @@ -# -*- 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( - '%r: %r is not valid (field %r)', - 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('%r: %r is not a valid version (field %r)', - project_name, value, name) - elif name in _VERSION_FIELDS and value is not None: - if not scheme.is_valid_version(value): - logger.warning('%r: %r is not a valid version (field %r)', - 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 %r: %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 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('%r is an invalid value for ' - 'the %r 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/python2.7/site-packages/pip/_vendor/distlib/resources.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/resources.py deleted file mode 100644 index 9dd8ca0..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/resources.py +++ /dev/null @@ -1,350 +0,0 @@ -# -*- 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: - import _frozen_importlib - _finder_registry[_frozen_importlib.SourceFileLoader] = ResourceFinder - _finder_registry[_frozen_importlib.FileFinder] = ResourceFinder -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/python2.7/site-packages/pip/_vendor/distlib/scripts.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/scripts.py deleted file mode 100644 index c9996d5..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/scripts.py +++ /dev/null @@ -1,384 +0,0 @@ -# -*- 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 = ''' - - - - - - - - - - - - -'''.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/python2.7/site-packages/pip/_vendor/distlib/t32.exe b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/t32.exe deleted file mode 100644 index e0168c22c57f32e59dc7f64f805c586e959c3ec2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 89088 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

cCam5!j}jO7Ps19?sV&U#*qxz<7n$K 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 P24SC;PdV`a$$|d@Alj9? diff --git a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/util.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/util.py deleted file mode 100644 index 7e209ec..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/util.py +++ /dev/null @@ -1,1597 +0,0 @@ -# -# 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 -import ssl -import subprocess -import sys -import tarfile -import tempfile -import textwrap - -try: - import threading -except ImportError: - 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, HTTPSHandler as BaseHTTPSHandler, - BaseConfigurator, valid_ident, Container, configparser, - URLError, match_hostname, CertificateError, ZipFile, - fsdecode) - -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.*))' - -# -# Either the bare constraints or the bare constraints in parentheses -# -CONSTRAINTS = (r'\(\s*(?P' + BARE_CONSTRAINTS + '|' + DIRECT_REF + - r')\s*\)|(?P' + BARE_CONSTRAINTS + '\s*)') - -EXTRA_LIST = EXTRA_IDENT + '(' + COMMA + EXTRA_IDENT + ')*' -EXTRAS = r'\[\s*(?P' + EXTRA_LIST + r')?\s*\]' -REQUIREMENT = ('(?P' + IDENT + r')\s*(' + EXTRAS + r'\s*)?(\s*' + - CONSTRAINTS + ')?$') -REQUIREMENT_RE = re.compile(REQUIREMENT) - -# -# Used to scan through the constraints -# -RELOP_IDENT = '(?P' + RELOP + r')\s*(?P' + 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 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): - return '' % (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(\w|[-.+])+) - \s*=\s*(?P(\w+)([:\.]\w+)*) - \s*(\[\s*(?P\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 ' - '%r' % 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 ' - '%r' % specification) - prefix, suffix = path.split(':') - flags = d['flags'] - if flags is None: - if '[' in specification or ']' in specification: - raise DistlibException('Invalid specification ' - '%r' % 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 - 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[\w .-]+)\s*' - r'\(\s*(?P[^\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): - 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: - raise ValueError('%r not a successor of anything' % succ) - try: - preds.remove(pred) - succs.remove(succ) - except KeyError: - 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: - 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 - - - -# -# 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: - 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: - 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)) - - - 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 - -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 diff --git a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/version.py b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/version.py deleted file mode 100644 index d3dcfa0..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/version.py +++ /dev/null @@ -1,742 +0,0 @@ -# -*- 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: Strring 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 # mininum 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 pobably 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/python2.7/site-packages/pip/_vendor/distlib/w32.exe b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/w32.exe deleted file mode 100644 index f27573a1eac23fc0ddbc2e056e3c099d32da1681..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 85504 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

OXP&)nf^INfn@Yjmk!&Z!Yw)omj5_8RgdSy- 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 diff --git a/venv/lib/python2.7/site-packages/pip/_vendor/distlib/w64.exe b/venv/lib/python2.7/site-packages/pip/_vendor/distlib/w64.exe deleted file mode 100644 index fd37d9ee468d5fcab4ccd51d4e85c48607efd310..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 94208 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%0A+m!ns>B!NUlDz;pPF&Aoq22e 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[^-]+) --(?P\d+[^-]*) -(-(?P\d+[^-]*))? --(?P\w+\d+(\.\w+\d+)*) --(?P\w+) --(?P\w+(\.\w+)*) -\.whl$ -''', re.IGNORECASE | re.VERBOSE) - -NAME_VERSION_RE = re.compile(r''' -(?P[^-]+) --(?P\d+[^-]*) -(-(?P\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/python2.7/site-packages/pip/_vendor/html5lib/__init__.py b/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/__init__.py deleted file mode 100644 index 9484fdc..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -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.0b8" diff --git a/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/constants.py b/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/constants.py deleted file mode 100644 index d938e0a..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/constants.py +++ /dev/null @@ -1,3102 +0,0 @@ -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.", - "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") -]) - -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", - 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", -} - -encodings = { - '437': 'cp437', - '850': 'cp850', - '852': 'cp852', - '855': 'cp855', - '857': 'cp857', - '860': 'cp860', - '861': 'cp861', - '862': 'cp862', - '863': 'cp863', - '865': 'cp865', - '866': 'cp866', - '869': 'cp869', - 'ansix341968': 'ascii', - 'ansix341986': 'ascii', - 'arabic': 'iso8859-6', - 'ascii': 'ascii', - 'asmo708': 'iso8859-6', - 'big5': 'big5', - 'big5hkscs': 'big5hkscs', - 'chinese': 'gbk', - 'cp037': 'cp037', - 'cp1026': 'cp1026', - 'cp154': 'ptcp154', - 'cp367': 'ascii', - 'cp424': 'cp424', - 'cp437': 'cp437', - 'cp500': 'cp500', - 'cp775': 'cp775', - 'cp819': 'windows-1252', - 'cp850': 'cp850', - 'cp852': 'cp852', - 'cp855': 'cp855', - 'cp857': 'cp857', - 'cp860': 'cp860', - 'cp861': 'cp861', - 'cp862': 'cp862', - 'cp863': 'cp863', - 'cp864': 'cp864', - 'cp865': 'cp865', - 'cp866': 'cp866', - 'cp869': 'cp869', - 'cp936': 'gbk', - 'cpgr': 'cp869', - 'cpis': 'cp861', - 'csascii': 'ascii', - 'csbig5': 'big5', - 'cseuckr': 'cp949', - 'cseucpkdfmtjapanese': 'euc_jp', - 'csgb2312': 'gbk', - 'cshproman8': 'hp-roman8', - 'csibm037': 'cp037', - 'csibm1026': 'cp1026', - 'csibm424': 'cp424', - 'csibm500': 'cp500', - 'csibm855': 'cp855', - 'csibm857': 'cp857', - 'csibm860': 'cp860', - 'csibm861': 'cp861', - 'csibm863': 'cp863', - 'csibm864': 'cp864', - 'csibm865': 'cp865', - 'csibm866': 'cp866', - 'csibm869': 'cp869', - 'csiso2022jp': 'iso2022_jp', - 'csiso2022jp2': 'iso2022_jp_2', - 'csiso2022kr': 'iso2022_kr', - 'csiso58gb231280': 'gbk', - 'csisolatin1': 'windows-1252', - 'csisolatin2': 'iso8859-2', - 'csisolatin3': 'iso8859-3', - 'csisolatin4': 'iso8859-4', - 'csisolatin5': 'windows-1254', - 'csisolatin6': 'iso8859-10', - 'csisolatinarabic': 'iso8859-6', - 'csisolatincyrillic': 'iso8859-5', - 'csisolatingreek': 'iso8859-7', - 'csisolatinhebrew': 'iso8859-8', - 'cskoi8r': 'koi8-r', - 'csksc56011987': 'cp949', - 'cspc775baltic': 'cp775', - 'cspc850multilingual': 'cp850', - 'cspc862latinhebrew': 'cp862', - 'cspc8codepage437': 'cp437', - 'cspcp852': 'cp852', - 'csptcp154': 'ptcp154', - 'csshiftjis': 'shift_jis', - 'csunicode11utf7': 'utf-7', - 'cyrillic': 'iso8859-5', - 'cyrillicasian': 'ptcp154', - 'ebcdiccpbe': 'cp500', - 'ebcdiccpca': 'cp037', - 'ebcdiccpch': 'cp500', - 'ebcdiccphe': 'cp424', - 'ebcdiccpnl': 'cp037', - 'ebcdiccpus': 'cp037', - 'ebcdiccpwt': 'cp037', - 'ecma114': 'iso8859-6', - 'ecma118': 'iso8859-7', - 'elot928': 'iso8859-7', - 'eucjp': 'euc_jp', - 'euckr': 'cp949', - 'extendedunixcodepackedformatforjapanese': 'euc_jp', - 'gb18030': 'gb18030', - 'gb2312': 'gbk', - 'gb231280': 'gbk', - 'gbk': 'gbk', - 'greek': 'iso8859-7', - 'greek8': 'iso8859-7', - 'hebrew': 'iso8859-8', - 'hproman8': 'hp-roman8', - 'hzgb2312': 'hz', - 'ibm037': 'cp037', - 'ibm1026': 'cp1026', - 'ibm367': 'ascii', - 'ibm424': 'cp424', - 'ibm437': 'cp437', - 'ibm500': 'cp500', - 'ibm775': 'cp775', - 'ibm819': 'windows-1252', - 'ibm850': 'cp850', - 'ibm852': 'cp852', - 'ibm855': 'cp855', - 'ibm857': 'cp857', - 'ibm860': 'cp860', - 'ibm861': 'cp861', - 'ibm862': 'cp862', - 'ibm863': 'cp863', - 'ibm864': 'cp864', - 'ibm865': 'cp865', - 'ibm866': 'cp866', - 'ibm869': 'cp869', - 'iso2022jp': 'iso2022_jp', - 'iso2022jp2': 'iso2022_jp_2', - 'iso2022kr': 'iso2022_kr', - 'iso646irv1991': 'ascii', - 'iso646us': 'ascii', - 'iso88591': 'windows-1252', - 'iso885910': 'iso8859-10', - 'iso8859101992': 'iso8859-10', - 'iso885911987': 'windows-1252', - 'iso885913': 'iso8859-13', - 'iso885914': 'iso8859-14', - 'iso8859141998': 'iso8859-14', - 'iso885915': 'iso8859-15', - 'iso885916': 'iso8859-16', - 'iso8859162001': 'iso8859-16', - 'iso88592': 'iso8859-2', - 'iso885921987': 'iso8859-2', - 'iso88593': 'iso8859-3', - 'iso885931988': 'iso8859-3', - 'iso88594': 'iso8859-4', - 'iso885941988': 'iso8859-4', - 'iso88595': 'iso8859-5', - 'iso885951988': 'iso8859-5', - 'iso88596': 'iso8859-6', - 'iso885961987': 'iso8859-6', - 'iso88597': 'iso8859-7', - 'iso885971987': 'iso8859-7', - 'iso88598': 'iso8859-8', - 'iso885981988': 'iso8859-8', - 'iso88599': 'windows-1254', - 'iso885991989': 'windows-1254', - 'isoceltic': 'iso8859-14', - 'isoir100': 'windows-1252', - 'isoir101': 'iso8859-2', - 'isoir109': 'iso8859-3', - 'isoir110': 'iso8859-4', - 'isoir126': 'iso8859-7', - 'isoir127': 'iso8859-6', - 'isoir138': 'iso8859-8', - 'isoir144': 'iso8859-5', - 'isoir148': 'windows-1254', - 'isoir149': 'cp949', - 'isoir157': 'iso8859-10', - 'isoir199': 'iso8859-14', - 'isoir226': 'iso8859-16', - 'isoir58': 'gbk', - 'isoir6': 'ascii', - 'koi8r': 'koi8-r', - 'koi8u': 'koi8-u', - 'korean': 'cp949', - 'ksc5601': 'cp949', - 'ksc56011987': 'cp949', - 'ksc56011989': 'cp949', - 'l1': 'windows-1252', - 'l10': 'iso8859-16', - 'l2': 'iso8859-2', - 'l3': 'iso8859-3', - 'l4': 'iso8859-4', - 'l5': 'windows-1254', - 'l6': 'iso8859-10', - 'l8': 'iso8859-14', - 'latin1': 'windows-1252', - 'latin10': 'iso8859-16', - 'latin2': 'iso8859-2', - 'latin3': 'iso8859-3', - 'latin4': 'iso8859-4', - 'latin5': 'windows-1254', - 'latin6': 'iso8859-10', - 'latin8': 'iso8859-14', - 'latin9': 'iso8859-15', - 'ms936': 'gbk', - 'mskanji': 'shift_jis', - 'pt154': 'ptcp154', - 'ptcp154': 'ptcp154', - 'r8': 'hp-roman8', - 'roman8': 'hp-roman8', - 'shiftjis': 'shift_jis', - 'tis620': 'cp874', - 'unicode11utf7': 'utf-7', - 'us': 'ascii', - 'usascii': 'ascii', - 'utf16': 'utf-16', - 'utf16be': 'utf-16-be', - 'utf16le': 'utf-16-le', - 'utf8': 'utf-8', - 'windows1250': 'cp1250', - 'windows1251': 'cp1251', - 'windows1252': 'cp1252', - 'windows1253': 'cp1253', - 'windows1254': 'cp1254', - 'windows1255': 'cp1255', - 'windows1256': 'cp1256', - 'windows1257': 'cp1257', - 'windows1258': 'cp1258', - 'windows936': 'gbk', - 'x-x-big5': 'big5'} - -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/python2.7/site-packages/pip/_vendor/html5lib/filters/__init__.py b/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/_base.py b/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/_base.py deleted file mode 100644 index c7dbaed..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/_base.py +++ /dev/null @@ -1,12 +0,0 @@ -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/python2.7/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py b/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py deleted file mode 100644 index fed6996..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py +++ /dev/null @@ -1,20 +0,0 @@ -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/python2.7/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py b/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py deleted file mode 100644 index ca33b70..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py +++ /dev/null @@ -1,65 +0,0 @@ -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/python2.7/site-packages/pip/_vendor/html5lib/filters/lint.py b/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/lint.py deleted file mode 100644 index 8884696..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/lint.py +++ /dev/null @@ -1,90 +0,0 @@ -from __future__ import absolute_import, division, unicode_literals - -from . import _base -from ..constants import cdataElements, rcdataElements, voidElements - -from ..constants import spaceCharacters -spaceCharacters = "".join(spaceCharacters) - - -class LintError(Exception): - pass - - -class Filter(_base.Filter): - def __iter__(self): - open_elements = [] - contentModelFlag = "PCDATA" - for token in _base.Filter.__iter__(self): - type = token["type"] - if type in ("StartTag", "EmptyTag"): - name = token["name"] - if contentModelFlag != "PCDATA": - raise LintError("StartTag not in PCDATA content model flag: %(tag)s" % {"tag": name}) - if not isinstance(name, str): - raise LintError("Tag name is not a string: %(tag)r" % {"tag": name}) - if not name: - raise LintError("Empty tag name") - if type == "StartTag" and name in voidElements: - raise LintError("Void element reported as StartTag token: %(tag)s" % {"tag": name}) - elif type == "EmptyTag" and name not in voidElements: - raise LintError("Non-void element reported as EmptyTag token: %(tag)s" % {"tag": token["name"]}) - if type == "StartTag": - open_elements.append(name) - for name, value in token["data"]: - if not isinstance(name, str): - raise LintError("Attribute name is not a string: %(name)r" % {"name": name}) - if not name: - raise LintError("Empty attribute name") - if not isinstance(value, str): - raise LintError("Attribute value is not a string: %(value)r" % {"value": value}) - if name in cdataElements: - contentModelFlag = "CDATA" - elif name in rcdataElements: - contentModelFlag = "RCDATA" - elif name == "plaintext": - contentModelFlag = "PLAINTEXT" - - elif type == "EndTag": - name = token["name"] - if not isinstance(name, str): - raise LintError("Tag name is not a string: %(tag)r" % {"tag": name}) - if not name: - raise LintError("Empty tag name") - if name in voidElements: - raise LintError("Void element reported as EndTag token: %(tag)s" % {"tag": name}) - start_name = open_elements.pop() - if start_name != name: - raise LintError("EndTag (%(end)s) does not match StartTag (%(start)s)" % {"end": name, "start": start_name}) - contentModelFlag = "PCDATA" - - elif type == "Comment": - if contentModelFlag != "PCDATA": - raise LintError("Comment not in PCDATA content model flag") - - elif type in ("Characters", "SpaceCharacters"): - data = token["data"] - if not isinstance(data, str): - raise LintError("Attribute name is not a string: %(name)r" % {"name": data}) - if not data: - raise LintError("%(type)s token with empty data" % {"type": type}) - if type == "SpaceCharacters": - data = data.strip(spaceCharacters) - if data: - raise LintError("Non-space character(s) found in SpaceCharacters token: %(token)r" % {"token": data}) - - elif type == "Doctype": - name = token["name"] - if contentModelFlag != "PCDATA": - raise LintError("Doctype not in PCDATA content model flag: %(name)s" % {"name": name}) - if not isinstance(name, str): - raise LintError("Tag name is not a string: %(tag)r" % {"tag": name}) - # XXX: what to do with token["data"] ? - - elif type in ("ParseError", "SerializeError"): - pass - - else: - raise LintError("Unknown token type: %(type)s" % {"type": type}) - - yield token diff --git a/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/optionaltags.py b/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/optionaltags.py deleted file mode 100644 index fefe0b3..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/optionaltags.py +++ /dev/null @@ -1,205 +0,0 @@ -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 - 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 preceeded 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 preceeded 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 optgroup - # 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/python2.7/site-packages/pip/_vendor/html5lib/filters/sanitizer.py b/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/sanitizer.py deleted file mode 100644 index b206b54..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/sanitizer.py +++ /dev/null @@ -1,12 +0,0 @@ -from __future__ import absolute_import, division, unicode_literals - -from . import _base -from ..sanitizer import HTMLSanitizerMixin - - -class Filter(_base.Filter, HTMLSanitizerMixin): - def __iter__(self): - for token in _base.Filter.__iter__(self): - token = self.sanitize_token(token) - if token: - yield token diff --git a/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/whitespace.py b/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/whitespace.py deleted file mode 100644 index dfc60ee..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/whitespace.py +++ /dev/null @@ -1,38 +0,0 @@ -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/python2.7/site-packages/pip/_vendor/html5lib/html5parser.py b/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/html5parser.py deleted file mode 100644 index 40f3d09..0000000 --- a/venv/lib/python2.7/site-packages/pip/_vendor/html5lib/html5parser.py +++ /dev/null @@ -1,2724 +0,0 @@ -from __future__ import absolute_import, division, unicode_literals -from pip._vendor.six import with_metaclass - -import types - -from . import inputstream -from . import tokenizer - -from . import treebuilders -from .treebuilders._base import Marker - -from . import utils -from . import constants -from .constants import spaceCharacters, asciiUpper2Lower -from .constants import specialElements -from .constants import headingElements -from .constants import cdataElements, rcdataElements -from .constants import tokenTypes, ReparseException, namespaces -from .constants import htmlIntegrationPointElements, mathmlTextIntegrationPointElements -from .constants import adjustForeignAttributes as adjustForeignAttributesMap -from .constants import E - - -def parse(doc, treebuilder="etree", encoding=None, - namespaceHTMLElements=True): - """Parse a string or file-like object into a tree""" - tb = treebuilders.getTreeBuilder(treebuilder) - p = HTMLParser(tb, namespaceHTMLElements=namespaceHTMLElements) - return p.parse(doc, encoding=encoding) - - -def parseFragment(doc, container="div", treebuilder="etree", encoding=None, - namespaceHTMLElements=True): - tb = treebuilders.getTreeBuilder(treebuilder) - p = HTMLParser(tb, namespaceHTMLElements=namespaceHTMLElements) - return p.parseFragment(doc, container=container, encoding=encoding) - - -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, tokenizer=tokenizer.HTMLTokenizer, - 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) - - tokenizer - a class that provides a stream of tokens to the treebuilder. - This may be replaced for e.g. a sanitizer which converts some tags to - text - """ - - # Raise an exception on the first error encountered - self.strict = strict - - if tree is None: - tree = treebuilders.getTreeBuilder("etree") - self.tree = tree(namespaceHTMLElements) - self.tokenizer_class = tokenizer - 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", - encoding=None, parseMeta=True, useChardet=True, **kwargs): - - self.innerHTMLMode = innerHTML - self.container = container - self.tokenizer = self.tokenizer_class(stream, encoding=encoding, - parseMeta=parseMeta, - useChardet=useChardet, - parser=self, **kwargs) - self.reset() - - while True: - try: - self.mainLoop() - break - except ReparseException: - self.reset() - - 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 - 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] - - 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(): - new_token = token - while new_token is not None: - 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 - 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 token["selfClosing"] - and not token["selfClosingAcknowledged"]): - self.parseError("non-void-element-with-trailing-solidus", - {"name": 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, encoding=None, parseMeta=True, useChardet=True): - """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) - """ - self._parse(stream, innerHTML=False, encoding=encoding, - parseMeta=parseMeta, useChardet=useChardet) - return self.tree.getDocument() - - def parseFragment(self, stream, container="div", encoding=None, - parseMeta=False, useChardet=True): - """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) - """ - self._parse(stream, True, container=container, encoding=encoding) - return self.tree.getFragment() - - def parseError(self, errorcode="XXX-undefined-error", datavars={}): - # XXX The idea is to make errorcode mandatory. - 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"]: - token["data"] = dict(token["data"][::-1]) - - return token - - def adjustMathMLAttributes(self, token): - replacements = {"definitionurl": "definitionURL"} - for k, v in replacements.items(): - if k in token["data"]: - token["data"][v] = token["data"][k] - del token["data"][k] - - def adjustSVGAttributes(self, token): - replacements = { - "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" - } - for originalName in list(token["data"].keys()): - if originalName in replacements: - svgName = replacements[originalName] - token["data"][svgName] = token["data"][originalName] - del token["data"][originalName] - - def adjustForeignAttributes(self, token): - replacements = adjustForeignAttributesMap - - for originalName in token["data"].keys(): - if originalName in replacements: - foreignName = replacements[originalName] - token["data"][foreignName] = token["data"][originalName] - del token["data"][originalName] - - def reparseTokenNormal(self, token): - 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"] - - -def getPhases(debug): - def log(function): - """Logger that records which phase processes each token""" - type_names = dict((value, key) for key, value in - constants.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 constants.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 - - 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), - (("noscript", "noframes", "style"), self.startTagNoScriptNoFramesStyle), - ("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 startTagNoScriptNoFramesStyle(self, token): - # Need to decide whether to implement the scripting-disabled case - self.parser.parseRCDataRawtext(token, "RAWTEXT") - - 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")) - - # XXX If we implement a parser for which scripting is disabled we need to - # implement this phase. - # - # class InHeadNoScriptPhase(Phase): - 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) - - # Keep a ref to this for special handling of whitespace in

-            self.processSpaceCharactersNonPre = self.processSpaceCharacters
-
-            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",
-                  "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),
-                (("noembed", "noframes", "noscript"), 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):
-            if node1.name != node2.name or node1.namespace != node2.namespace:
-                return False
-            elif len(node1.attributes) != len(node2.attributes):
-                return False
-            else:
-                attributes1 = sorted(node1.attributes.items())
-                attributes2 = sorted(node2.attributes.items())
-                for attr1, attr2 in zip(attributes1, attributes2):
-                    if attr1 != attr2:
-                        return False
-            return True
-
-        # 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 
, , and ",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("