From e61cdbd61e7c02665df7144f662e6ca3a63d629f Mon Sep 17 00:00:00 2001 From: sgoudham Date: Wed, 3 Jun 2020 23:02:14 +0100 Subject: [PATCH] Trying to use environment variables --- .env | 1 + .gitignore | 2 +- EnsoBot.py | 10 +- cogs/FunCommands.py | 16 +- requirements.txt | 4 +- .../__pycache__/decouple.cpython-36.pyc | Bin 0 -> 7600 bytes venv/Lib/site-packages/dateutil/__init__.py | 8 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 366 bytes .../__pycache__/_common.cpython-36.pyc | Bin 0 -> 1425 bytes .../__pycache__/_version.cpython-36.pyc | Bin 0 -> 182 bytes .../__pycache__/easter.cpython-36.pyc | Bin 0 -> 2185 bytes .../__pycache__/relativedelta.cpython-36.pyc | Bin 0 -> 15498 bytes .../dateutil/__pycache__/rrule.cpython-36.pyc | Bin 0 -> 43877 bytes .../dateutil/__pycache__/tzwin.cpython-36.pyc | Bin 0 -> 185 bytes .../dateutil/__pycache__/utils.cpython-36.pyc | Bin 0 -> 2237 bytes venv/Lib/site-packages/dateutil/_common.py | 43 + venv/Lib/site-packages/dateutil/_version.py | 4 + venv/Lib/site-packages/dateutil/easter.py | 89 + .../site-packages/dateutil/parser/__init__.py | 61 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 2071 bytes .../parser/__pycache__/_parser.cpython-36.pyc | Bin 0 -> 40521 bytes .../__pycache__/isoparser.cpython-36.pyc | Bin 0 -> 11196 bytes .../site-packages/dateutil/parser/_parser.py | 1609 ++++++++++++++ .../dateutil/parser/isoparser.py | 411 ++++ .../site-packages/dateutil/relativedelta.py | 599 ++++++ venv/Lib/site-packages/dateutil/rrule.py | 1735 ++++++++++++++++ .../Lib/site-packages/dateutil/tz/__init__.py | 12 + .../tz/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 679 bytes .../tz/__pycache__/_common.cpython-36.pyc | Bin 0 -> 12274 bytes .../tz/__pycache__/_factories.cpython-36.pyc | Bin 0 -> 2876 bytes .../dateutil/tz/__pycache__/tz.cpython-36.pyc | Bin 0 -> 45442 bytes .../tz/__pycache__/win.cpython-36.pyc | Bin 0 -> 11094 bytes venv/Lib/site-packages/dateutil/tz/_common.py | 419 ++++ .../site-packages/dateutil/tz/_factories.py | 80 + venv/Lib/site-packages/dateutil/tz/tz.py | 1849 +++++++++++++++++ venv/Lib/site-packages/dateutil/tz/win.py | 370 ++++ venv/Lib/site-packages/dateutil/tzwin.py | 2 + venv/Lib/site-packages/dateutil/utils.py | 71 + .../dateutil/zoneinfo/__init__.py | 167 ++ .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 5611 bytes .../__pycache__/rebuild.cpython-36.pyc | Bin 0 -> 1886 bytes .../zoneinfo/dateutil-zoneinfo.tar.gz | Bin 0 -> 153315 bytes .../dateutil/zoneinfo/rebuild.py | 53 + venv/Lib/site-packages/decouple.py | 234 +++ venv/Lib/site-packages/dotenv/__init__.py | 46 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 1175 bytes .../dotenv/__pycache__/cli.cpython-36.pyc | Bin 0 -> 3970 bytes .../dotenv/__pycache__/compat.cpython-36.pyc | Bin 0 -> 1105 bytes .../dotenv/__pycache__/ipython.cpython-36.pyc | Bin 0 -> 1495 bytes .../dotenv/__pycache__/main.cpython-36.pyc | Bin 0 -> 8508 bytes .../dotenv/__pycache__/parser.cpython-36.pyc | Bin 0 -> 5747 bytes .../dotenv/__pycache__/version.cpython-36.pyc | Bin 0 -> 184 bytes venv/Lib/site-packages/dotenv/cli.py | 145 ++ venv/Lib/site-packages/dotenv/compat.py | 49 + venv/Lib/site-packages/dotenv/ipython.py | 41 + venv/Lib/site-packages/dotenv/main.py | 323 +++ venv/Lib/site-packages/dotenv/parser.py | 237 +++ venv/Lib/site-packages/dotenv/py.typed | 1 + venv/Lib/site-packages/dotenv/version.py | 1 + .../python_dateutil-2.8.1.dist-info/INSTALLER | 1 + .../python_dateutil-2.8.1.dist-info/LICENSE | 54 + .../python_dateutil-2.8.1.dist-info/METADATA | 200 ++ .../python_dateutil-2.8.1.dist-info/RECORD | 44 + .../python_dateutil-2.8.1.dist-info/WHEEL | 6 + .../top_level.txt | 1 + .../python_dateutil-2.8.1.dist-info/zip-safe | 1 + .../PKG-INFO | 418 ++++ .../SOURCES.txt | 11 + .../dependency_links.txt | 1 + .../installed-files.txt | 7 + .../not-zip-safe | 1 + .../top_level.txt | 1 + .../python_dotenv-0.13.0.dist-info/INSTALLER | 1 + .../python_dotenv-0.13.0.dist-info/LICENSE | 87 + .../python_dotenv-0.13.0.dist-info/METADATA | 516 +++++ .../python_dotenv-0.13.0.dist-info/RECORD | 23 + .../python_dotenv-0.13.0.dist-info/WHEEL | 6 + .../entry_points.txt | 4 + .../top_level.txt | 1 + .../urllib3-1.25.9.dist-info/INSTALLER | 1 + .../urllib3-1.25.9.dist-info/LICENSE.txt | 21 + .../urllib3-1.25.9.dist-info/METADATA | 1262 +++++++++++ .../urllib3-1.25.9.dist-info/RECORD | 78 + .../urllib3-1.25.9.dist-info/WHEEL | 6 + .../urllib3-1.25.9.dist-info/top_level.txt | 1 + venv/Lib/site-packages/urllib3/__init__.py | 86 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 2172 bytes .../__pycache__/_collections.cpython-36.pyc | Bin 0 -> 10665 bytes .../__pycache__/connection.cpython-36.pyc | Bin 0 -> 10215 bytes .../__pycache__/connectionpool.cpython-36.pyc | Bin 0 -> 24019 bytes .../__pycache__/exceptions.cpython-36.pyc | Bin 0 -> 11099 bytes .../urllib3/__pycache__/fields.cpython-36.pyc | Bin 0 -> 8077 bytes .../__pycache__/filepost.cpython-36.pyc | Bin 0 -> 2733 bytes .../__pycache__/poolmanager.cpython-36.pyc | Bin 0 -> 13752 bytes .../__pycache__/request.cpython-36.pyc | Bin 0 -> 5565 bytes .../__pycache__/response.cpython-36.pyc | Bin 0 -> 20635 bytes .../Lib/site-packages/urllib3/_collections.py | 336 +++ venv/Lib/site-packages/urllib3/connection.py | 423 ++++ .../site-packages/urllib3/connectionpool.py | 1033 +++++++++ .../site-packages/urllib3/contrib/__init__.py | 0 .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 172 bytes .../_appengine_environ.cpython-36.pyc | Bin 0 -> 1381 bytes .../__pycache__/appengine.cpython-36.pyc | Bin 0 -> 8129 bytes .../__pycache__/ntlmpool.cpython-36.pyc | Bin 0 -> 3218 bytes .../__pycache__/pyopenssl.cpython-36.pyc | Bin 0 -> 14951 bytes .../securetransport.cpython-36.pyc | Bin 0 -> 19698 bytes .../contrib/__pycache__/socks.cpython-36.pyc | Bin 0 -> 5488 bytes .../urllib3/contrib/_appengine_environ.py | 36 + .../contrib/_securetransport/__init__.py | 0 .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 189 bytes .../__pycache__/bindings.cpython-36.pyc | Bin 0 -> 10256 bytes .../__pycache__/low_level.cpython-36.pyc | Bin 0 -> 7433 bytes .../contrib/_securetransport/bindings.py | 493 +++++ .../contrib/_securetransport/low_level.py | 328 +++ .../urllib3/contrib/appengine.py | 314 +++ .../site-packages/urllib3/contrib/ntlmpool.py | 121 ++ .../urllib3/contrib/pyopenssl.py | 501 +++++ .../urllib3/contrib/securetransport.py | 864 ++++++++ .../site-packages/urllib3/contrib/socks.py | 210 ++ venv/Lib/site-packages/urllib3/exceptions.py | 272 +++ venv/Lib/site-packages/urllib3/fields.py | 273 +++ venv/Lib/site-packages/urllib3/filepost.py | 98 + .../urllib3/packages/__init__.py | 5 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 298 bytes .../packages/__pycache__/six.cpython-36.pyc | Bin 0 -> 26525 bytes .../urllib3/packages/backports/__init__.py | 0 .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 183 bytes .../__pycache__/makefile.cpython-36.pyc | Bin 0 -> 1273 bytes .../urllib3/packages/backports/makefile.py | 52 + .../Lib/site-packages/urllib3/packages/six.py | 1021 +++++++++ .../packages/ssl_match_hostname/__init__.py | 19 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 559 bytes .../_implementation.cpython-36.pyc | Bin 0 -> 3266 bytes .../ssl_match_hostname/_implementation.py | 160 ++ venv/Lib/site-packages/urllib3/poolmanager.py | 492 +++++ venv/Lib/site-packages/urllib3/request.py | 171 ++ venv/Lib/site-packages/urllib3/response.py | 821 ++++++++ .../site-packages/urllib3/util/__init__.py | 46 + .../util/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 1137 bytes .../__pycache__/connection.cpython-36.pyc | Bin 0 -> 3141 bytes .../util/__pycache__/queue.cpython-36.pyc | Bin 0 -> 1013 bytes .../util/__pycache__/request.cpython-36.pyc | Bin 0 -> 3307 bytes .../util/__pycache__/response.cpython-36.pyc | Bin 0 -> 1940 bytes .../util/__pycache__/retry.cpython-36.pyc | Bin 0 -> 12923 bytes .../util/__pycache__/ssl_.cpython-36.pyc | Bin 0 -> 10028 bytes .../util/__pycache__/timeout.cpython-36.pyc | Bin 0 -> 8817 bytes .../util/__pycache__/url.cpython-36.pyc | Bin 0 -> 10582 bytes .../util/__pycache__/wait.cpython-36.pyc | Bin 0 -> 3123 bytes .../site-packages/urllib3/util/connection.py | 138 ++ venv/Lib/site-packages/urllib3/util/queue.py | 21 + .../Lib/site-packages/urllib3/util/request.py | 135 ++ .../site-packages/urllib3/util/response.py | 86 + venv/Lib/site-packages/urllib3/util/retry.py | 453 ++++ venv/Lib/site-packages/urllib3/util/ssl_.py | 414 ++++ .../Lib/site-packages/urllib3/util/timeout.py | 261 +++ venv/Lib/site-packages/urllib3/util/url.py | 430 ++++ venv/Lib/site-packages/urllib3/util/wait.py | 153 ++ venv/Scripts/dotenv.exe | Bin 0 -> 106359 bytes 158 files changed, 21697 insertions(+), 14 deletions(-) create mode 100644 .env create mode 100644 venv/Lib/site-packages/__pycache__/decouple.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/__init__.py create mode 100644 venv/Lib/site-packages/dateutil/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/__pycache__/_common.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/__pycache__/_version.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/__pycache__/easter.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/__pycache__/relativedelta.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/__pycache__/rrule.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/__pycache__/tzwin.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/__pycache__/utils.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/_common.py create mode 100644 venv/Lib/site-packages/dateutil/_version.py create mode 100644 venv/Lib/site-packages/dateutil/easter.py create mode 100644 venv/Lib/site-packages/dateutil/parser/__init__.py create mode 100644 venv/Lib/site-packages/dateutil/parser/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/parser/__pycache__/_parser.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/parser/__pycache__/isoparser.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/parser/_parser.py create mode 100644 venv/Lib/site-packages/dateutil/parser/isoparser.py create mode 100644 venv/Lib/site-packages/dateutil/relativedelta.py create mode 100644 venv/Lib/site-packages/dateutil/rrule.py create mode 100644 venv/Lib/site-packages/dateutil/tz/__init__.py create mode 100644 venv/Lib/site-packages/dateutil/tz/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/tz/__pycache__/_common.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/tz/__pycache__/_factories.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/tz/__pycache__/tz.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/tz/__pycache__/win.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/tz/_common.py create mode 100644 venv/Lib/site-packages/dateutil/tz/_factories.py create mode 100644 venv/Lib/site-packages/dateutil/tz/tz.py create mode 100644 venv/Lib/site-packages/dateutil/tz/win.py create mode 100644 venv/Lib/site-packages/dateutil/tzwin.py create mode 100644 venv/Lib/site-packages/dateutil/utils.py create mode 100644 venv/Lib/site-packages/dateutil/zoneinfo/__init__.py create mode 100644 venv/Lib/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz create mode 100644 venv/Lib/site-packages/dateutil/zoneinfo/rebuild.py create mode 100644 venv/Lib/site-packages/decouple.py create mode 100644 venv/Lib/site-packages/dotenv/__init__.py create mode 100644 venv/Lib/site-packages/dotenv/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dotenv/__pycache__/cli.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dotenv/__pycache__/compat.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dotenv/__pycache__/ipython.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dotenv/__pycache__/main.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dotenv/__pycache__/parser.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dotenv/__pycache__/version.cpython-36.pyc create mode 100644 venv/Lib/site-packages/dotenv/cli.py create mode 100644 venv/Lib/site-packages/dotenv/compat.py create mode 100644 venv/Lib/site-packages/dotenv/ipython.py create mode 100644 venv/Lib/site-packages/dotenv/main.py create mode 100644 venv/Lib/site-packages/dotenv/parser.py create mode 100644 venv/Lib/site-packages/dotenv/py.typed create mode 100644 venv/Lib/site-packages/dotenv/version.py create mode 100644 venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/zip-safe create mode 100644 venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/PKG-INFO create mode 100644 venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/SOURCES.txt create mode 100644 venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/dependency_links.txt create mode 100644 venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/installed-files.txt create mode 100644 venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/not-zip-safe create mode 100644 venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/top_level.txt create mode 100644 venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/urllib3-1.25.9.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/urllib3-1.25.9.dist-info/LICENSE.txt create mode 100644 venv/Lib/site-packages/urllib3-1.25.9.dist-info/METADATA create mode 100644 venv/Lib/site-packages/urllib3-1.25.9.dist-info/RECORD create mode 100644 venv/Lib/site-packages/urllib3-1.25.9.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/urllib3-1.25.9.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/urllib3/__init__.py create mode 100644 venv/Lib/site-packages/urllib3/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/__pycache__/_collections.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/__pycache__/connection.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/__pycache__/connectionpool.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/__pycache__/exceptions.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/__pycache__/fields.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/__pycache__/filepost.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/__pycache__/poolmanager.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/__pycache__/request.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/__pycache__/response.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/_collections.py create mode 100644 venv/Lib/site-packages/urllib3/connection.py create mode 100644 venv/Lib/site-packages/urllib3/connectionpool.py rename DiscordToken/blank.py => venv/Lib/site-packages/urllib3/contrib/__init__.py (100%) create mode 100644 venv/Lib/site-packages/urllib3/contrib/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/contrib/__pycache__/_appengine_environ.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/contrib/__pycache__/appengine.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/contrib/__pycache__/ntlmpool.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/contrib/__pycache__/pyopenssl.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/contrib/__pycache__/securetransport.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/contrib/__pycache__/socks.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/contrib/_appengine_environ.py create mode 100644 venv/Lib/site-packages/urllib3/contrib/_securetransport/__init__.py create mode 100644 venv/Lib/site-packages/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/contrib/_securetransport/bindings.py create mode 100644 venv/Lib/site-packages/urllib3/contrib/_securetransport/low_level.py create mode 100644 venv/Lib/site-packages/urllib3/contrib/appengine.py create mode 100644 venv/Lib/site-packages/urllib3/contrib/ntlmpool.py create mode 100644 venv/Lib/site-packages/urllib3/contrib/pyopenssl.py create mode 100644 venv/Lib/site-packages/urllib3/contrib/securetransport.py create mode 100644 venv/Lib/site-packages/urllib3/contrib/socks.py create mode 100644 venv/Lib/site-packages/urllib3/exceptions.py create mode 100644 venv/Lib/site-packages/urllib3/fields.py create mode 100644 venv/Lib/site-packages/urllib3/filepost.py create mode 100644 venv/Lib/site-packages/urllib3/packages/__init__.py create mode 100644 venv/Lib/site-packages/urllib3/packages/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/packages/__pycache__/six.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/packages/backports/__init__.py create mode 100644 venv/Lib/site-packages/urllib3/packages/backports/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/packages/backports/__pycache__/makefile.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/packages/backports/makefile.py create mode 100644 venv/Lib/site-packages/urllib3/packages/six.py create mode 100644 venv/Lib/site-packages/urllib3/packages/ssl_match_hostname/__init__.py create mode 100644 venv/Lib/site-packages/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/packages/ssl_match_hostname/_implementation.py create mode 100644 venv/Lib/site-packages/urllib3/poolmanager.py create mode 100644 venv/Lib/site-packages/urllib3/request.py create mode 100644 venv/Lib/site-packages/urllib3/response.py create mode 100644 venv/Lib/site-packages/urllib3/util/__init__.py create mode 100644 venv/Lib/site-packages/urllib3/util/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/util/__pycache__/connection.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/util/__pycache__/queue.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/util/__pycache__/request.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/util/__pycache__/response.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/util/__pycache__/retry.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/util/__pycache__/ssl_.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/util/__pycache__/timeout.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/util/__pycache__/url.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/util/__pycache__/wait.cpython-36.pyc create mode 100644 venv/Lib/site-packages/urllib3/util/connection.py create mode 100644 venv/Lib/site-packages/urllib3/util/queue.py create mode 100644 venv/Lib/site-packages/urllib3/util/request.py create mode 100644 venv/Lib/site-packages/urllib3/util/response.py create mode 100644 venv/Lib/site-packages/urllib3/util/retry.py create mode 100644 venv/Lib/site-packages/urllib3/util/ssl_.py create mode 100644 venv/Lib/site-packages/urllib3/util/timeout.py create mode 100644 venv/Lib/site-packages/urllib3/util/url.py create mode 100644 venv/Lib/site-packages/urllib3/util/wait.py create mode 100644 venv/Scripts/dotenv.exe diff --git a/.env b/.env new file mode 100644 index 00000000..1f5b3172 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +DISCORD_TOKEN=NzE2NzAxNjk5MTQ1NzI4MDk0.XtgR5w.QI2bzKWTC4wFuQnDQuJanx1gZns diff --git a/.gitignore b/.gitignore index 9c6b501e..2f34e221 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ #Hiding Token -*token.txt \ No newline at end of file +*.env \ No newline at end of file diff --git a/EnsoBot.py b/EnsoBot.py index 25230cf1..beaef200 100644 --- a/EnsoBot.py +++ b/EnsoBot.py @@ -1,13 +1,15 @@ import asyncio import discord from discord.ext import commands +from decouple import config + +API_TOKEN = config('DISCORD_TOKEN') -# Bot Prefix +# Bot Prefix client = commands.Bot(command_prefix='~') + #token = open("DiscordToken/token.txt", "rt").readline() -with open('DiscordToken/token.txt') as file: - token = file.readline() # Instantiates a list for all the cogs extensions = ['cogs.WaifuImages', 'cogs.FunCommands'] @@ -85,7 +87,7 @@ async def on_command_error(ctx, error): await message.delete() -client.run(token) +client.run(API_TOKEN) ''' diff --git a/cogs/FunCommands.py b/cogs/FunCommands.py index 1e0d5724..d4d0195c 100644 --- a/cogs/FunCommands.py +++ b/cogs/FunCommands.py @@ -36,7 +36,7 @@ class Fun(commands.Cog): f"#{target.mention}IsOverParty", f"I hope {target.mention} drops dead with a curable disease that doctors simply didn’t feel like curing :)", f"{target.mention} You know there's no vaccine for stupidity right?", - f"{target.mention} bruh moment", + f"", f"", f"", f"", @@ -49,8 +49,8 @@ class Fun(commands.Cog): async def compliment(self, ctx, target: discord.Member): responses = [ f"{target.mention} is the most adorable uwu :heart_eyes: :heart_eyes:", - f"{target.mention} You have my ENTIRE HEART UvU", - f"{target.mention} Hun you're CUTE UwU :pleading_face: :flushed: :pleading_face: :flushed: :pleading_face:", + f"{target.mention} You have my ENTIRE HEART uvu", + f"{target.mention} Hun you're CUTE uwu :pleading_face: :flushed: :pleading_face: :flushed: :pleading_face:", f"I love {target.mention} so so much :heartbeat: :heartbeat: :heartbeat: ", f"My heart is full of love for you {target.mention}", f"{target.mention} I admire your greatness so much that I consider making a fan club to become your #1 fan (´꒳`)", @@ -59,11 +59,11 @@ class Fun(commands.Cog): f"{target.mention} Your smile is so beautiful it blinds me :heart_eyes: :heart_eyes:", f"Being on a journey all my life, I will never meet a person as amazing as you are {target.mention}", f"Such a pleasure to be on the same sever with you {target.mention}", - f"", - f"", - f"", - f"", - f"", + f"With {target.mention}, even the worst day will be filled with joy", + f"There's no better antidepressant than {target.mention}", + f"{target.mention} You're great, keep going Σd(˘ꇴ˘๑)", + f"I'd simp for {target.mention} anyday :flushed: :heart_eyes: :flushed: ", + f"{target.mention} Even the ugliest clothes won't ruin your look (。•̀ᴗ -)☆", f"", f"", f"", diff --git a/requirements.txt b/requirements.txt index da2f0126..0408bd5e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,6 @@ git+https://github.com/Rapptz/discord.py dnspython==1.16.0 PyNaCl==1.3.0 certifi -async-timeout==3.0.1 \ No newline at end of file +async-timeout==3.0.1 +python-dotenv~=0.13.0 +python-decouple \ No newline at end of file diff --git a/venv/Lib/site-packages/__pycache__/decouple.cpython-36.pyc b/venv/Lib/site-packages/__pycache__/decouple.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..669241c41c5ddf001a2ba152c2d0e0ba34dbaaa5 GIT binary patch literal 7600 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dateutil/__pycache__/_common.cpython-36.pyc b/venv/Lib/site-packages/dateutil/__pycache__/_common.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77700c076448a01a5da16dca62b8198be77d8569 GIT binary patch literal 1425 zcmZuxQBM;=5Z>Lpw%4OAsHmWcsfnnKfd}=87-NH=4_c!@FwMoyakJ3UYwy^-hCu2Q z{1jjON9JDr7d|<2S6WKgW@qMhXLshC`L?gBRkykP=f^LLvA^ufu+e^p(=4F?)@C0V za1a*^gyQE`TY$CA8ur;UcO&Zeqd`50K-GsvLA^Vu_lIH9jYC!MM=%VPS#txtW->5X ze)$#}inTcg5P$&=McaaTu)x0HZ5s+uykPADl)ynw_LBY_gb;z8bO2+eUvlV8J1ZKJ}2>B?`WsTlvh zZmfH|Mrq@j!)ORzGmhW;iSK=i0zdS2R6q9OZoE1i#D36IuzJ!_DqMw5Y+feao(F!S zhDkT{WRMkXEj~>hDZ7JiB4rr^7>dGpk)JJ$fvjC_2b38N(u62OQ%8f)9I>9zwGm=L zo4&<}pJPcb(ODU=v@B&1`o>6^VMEY#Q?{1@%tZm$C@;hmN?FA(Q$)eI%WZKrv)o}O zvpMw3GFwOEv`(3gIA&*=2U%wPi1#f02>3SJV}nJTC8Fo**PPLhR=*>=up?G$Yo}pN zJCN3zNus;^!$fUr9qDoEX(38FN?%XDD5W}<@($);DkvBie2F`}My0JyOG{1x^_>Rk zNnwd5$)uPE;v7>WnjkegzUfR8Yw)~=)VbO6k)M1SxHoaAu8qiUyRaQOoQnQHPtw5uybrBNjgp z_b`KuNNy_?pTDGHXEzmlo2WmgV(VKfwwpM2&q^!i`N|7uG)j6NFV$2-eT&3x64VX7 zNa8MvnFp8iFy-btC}g2lv6k`)9x)CIVqx(b4|5l4k4wBA_DwUJ!n^?JRbVt zcn6<>38RFb0TEl%y_%S-iR`*Y;y#IKQ!|SmM%H;cWysCpj&Nhjeyb)Kzz`*brh~a<{$Z`PUVh$jY!Vtxf!Whh;$yCK^q-UXL=%>kei@hwh zs5mn}ZzV$!6HpD9_!Z=A6%$$vl!z%#&o51haV#isNi0c>@ySn4%!vs}%`J#2$Slw; z%_~Su&Q49yEzd|z&CyNCC@3~6$;^&PNi0b%Ey>J@iH91lS5SG2!zMRBr8Fnijs@f% HAZ7pnK?yR2 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dateutil/__pycache__/easter.cpython-36.pyc b/venv/Lib/site-packages/dateutil/__pycache__/easter.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96aea4117025a4e710f64930ae58cd2bc4066656 GIT binary patch literal 2185 zcmZ`*OLN;c5C%xi^=2=cVsz!q|!qjw^dz zH)4JuVo$Q*@KEZAan_MaYPZcKkBro;9r$5vT-9N|G~Gb3!$32x2CU;ANyP?|>ne*Q zV5q#2D50usM-hizPq>mYX9l$XW!Rf zq!-82omYF`?L67}IlV9@yxB&!7b6PNBMZR>XyXW<8Td-@ZNV3Lz$9ow9b%In4K4|* zN0nm{N+M>6uutf3G$BJPv4nGt3~f;uIthfZNA#b4AH_hK64EHTq_^!o%yv&bhdERcMRBa~erLr49Q(}tx4-kd5fCMB>n2Qr@^ zS8f|-6YJ#!UU6v=ca#5KhF>~Q7755*Now z0=-k>8gNaCd01b_{J(w;b}_673#f}gEg^L{ZRlIe*ok#Q0jD#poX`^+(&+}Dft;)` zc=D{ulcmoi76#f#$QvE_%ycE|2fD{Zpd>pOuy3EUx;9ZYlMECyK?tds@tvj&@@;uM zj@VwH1SF^GR9V04wz~}UXOagz0u!!cA=ieX9x!yK05tBz0+aa8>ivzoOI0?O`!x>G zHxRtFeH?mRaTDkP+l|A}lfG029LjXnBclTq_->nfnZ>&219$sYmA$Mn_JdRkcFcCA zDSbgp2iu2J@=Oguqri_PB9jLwWT@o4LAGwhUF~ zc3j0hcAgD(GOjqFN+qy5kGSXJ9y6VILV_C!rGAL@y}tG(t3Sz>*3K)^C2(grj7A>3Nel%ScXw#kiJC%TNPN;Wb*y;{oFsUL_TPtr(5H|vv2A@o@&ojMq{03Qh_zmTWMoauE1%nVmxHJU}Zj&KvZ(D3LE!siH!0836<`H6e6+}1kKX&EcPxV}ss9Hq9a%+gHiV0NX&+{=^>QrbzW zOsSjFw<&F>w42f}r6Q&NUqmm1-hW?iZ8l#=aC0`JP7sS`Jq(|4!<)|n2upKM`e8G4 z!{u0oyxo&xx!;x2TNd3gS~G61iPqtE^_r6a4m93pxZMbMKyEM4@{B{v6h3Q?UIE0} zExSys^e(;a%+Q;3jxLqb(*C{(+WY$&!-+HQc`(B}Qe$v+0RgpVo}Ysn(9-gQ{ExsR RWQ*`Jb*!>A1J>ZR{{xWRPCftt literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dateutil/__pycache__/relativedelta.cpython-36.pyc b/venv/Lib/site-packages/dateutil/__pycache__/relativedelta.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..687f5bcf7d86538d984e606528b7492f66fba15b GIT binary patch literal 15498 zcmcgzO^h7Jb?)xz`Q6$7CAnNuQj7ZAkyb--C0PnBGNS%0C(_E6DA5_v8TC&0?#}G& z%&5AD++FuLhL(Ne(#~fzi1H$RS}4$sso*K=OUB zdU|J;OG-AR?4YY&Rad=w_3G7ouU_>&J2_dpa{8CQ|E6mg|7wi<6#&1CC-^fYp5d8o zqo#ASW=h$rS;(z+X2-7CCf{Y+*_~W1C$Qbl?-XhUz*#TXF7A|SCDZUHyu4SqW%y;U zcwoL}9GC~DZCuaSCTml*>Do+fwl-IruPt~bZ^A2klirj!?ag?zn}ynmuNdB(SGr|* zCI93>rgqAk_ZDs$wMEZ-�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 Hef0kU5g433 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dateutil/__pycache__/tzwin.cpython-36.pyc b/venv/Lib/site-packages/dateutil/__pycache__/tzwin.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2165148082942876977a0e7a9476af666835968d GIT binary patch literal 185 zcmXr!<>hjeyb)*3z`*brh~a<<$Z`PUVjduo!jQt4!w|xd!W7J)$^4QLD5S}Fi&4u@ zlQD{|q)M+mGjAnB5i?L2O#Je9wu%WYPAw{qDNfHXO^IP?hc>(SxIfBgL0vgiHj-FrgJ zU*a_f7=+jMgfD{39|i5er+Ju#qp%&0YV8`aNf}RocM0ziUbBV4dguPwyY=7rm<411JiPU6O+Y4scfoCd^g{j&+areO zCuu8m<#%F-dRktH-n~wKOufl!zTlmR4G0$ppV<+_I4pxjGx#$QesYuK9@B z8bjY@mTEQwK1qp>jvLi(r5BjHPKdaa|L@CShJ&O_tMQ)JLR~8adN7(hPK+eh-BG z-O~}KfB$%tbY2-8bjO?(mFOIn<#*ij&JRV(v(Bj;m7P+RyH#HD^g@c=tD%(Ht{9eP z->QoanI>R$XfVz4dQ!Iq%DxWUhFp4Uk+0YAoe~D

K-PVc{7jWA7Z$zgW>5WAB7k z{uoX1&Od?F;ow)OyL3;aHLmcz-ZbG}kGa#NQ(;BUa;`2Vj(rAWh_dVHxwu9_uizjD zRpy{CU-MCkzClVkyDlomwPd9(E*0?3_`(c4QsIH6A-|L^5Q%PqN1ihU6Dn1fp_k8G_JwqDZYYHc6(j6Rr*p4%k2!BNr7FyJ>I8 zd7&+!jMg{OI$Mt`lV=1x+u&9o&~}G8R~B->tIT$%6@Rcx44j;Oy9i->>-&?_tsS|Li5gUq2vV7wYCoIOYb{FHI9Fj2+=8V}4oMn;cN9nPFc zs|Qr|9Sbs*8_S8|iLi`OXE|nzO7wcdqD9mIEQT9^x&e!r0j_8T*DU`=TMNp~8NZ^& zcr2WGw!JAE9ml@ompRQW&l1Oa1%2**bF4ERRw0{gJjBCO$PC>S-qXmh1$qOs6z%UG z;uD%5;WeLOu--Vp9TJSavHy|xCL9N=-Z-p5>B28Q9S0ZSkHGW6DzSevbY%h8&HEqx z2=uu-;|k2aAdN-4bC%09T$+85giv(5(q-z*pxk}jGq^=T78-ORf2nvjEd%&C`_ + + and + + `The Calendar FAQ: Easter `_ + + """ + + 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/site-packages/dateutil/parser/__init__.py b/venv/Lib/site-packages/dateutil/parser/__init__.py new file mode 100644 index 00000000..d174b0e4 --- /dev/null +++ b/venv/Lib/site-packages/dateutil/parser/__init__.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +from ._parser import parse, parser, parserinfo, ParserError +from ._parser import DEFAULTPARSER, DEFAULTTZPARSER +from ._parser import UnknownTimezoneWarning + +from ._parser import __doc__ + +from .isoparser import isoparser, isoparse + +__all__ = ['parse', 'parser', 'parserinfo', + 'isoparse', 'isoparser', + 'ParserError', + 'UnknownTimezoneWarning'] + + +### +# Deprecate portions of the private interface so that downstream code that +# is improperly relying on it is given *some* notice. + + +def __deprecated_private_func(f): + from functools import wraps + import warnings + + msg = ('{name} is a private function and may break without warning, ' + 'it will be moved and or renamed in future versions.') + msg = msg.format(name=f.__name__) + + @wraps(f) + def deprecated_func(*args, **kwargs): + warnings.warn(msg, DeprecationWarning) + return f(*args, **kwargs) + + return deprecated_func + +def __deprecate_private_class(c): + import warnings + + msg = ('{name} is a private class and may break without warning, ' + 'it will be moved and or renamed in future versions.') + msg = msg.format(name=c.__name__) + + class private_class(c): + __doc__ = c.__doc__ + + def __init__(self, *args, **kwargs): + warnings.warn(msg, DeprecationWarning) + super(private_class, self).__init__(*args, **kwargs) + + private_class.__name__ = c.__name__ + + return private_class + + +from ._parser import _timelex, _resultbase +from ._parser import _tzparser, _parsetz + +_timelex = __deprecate_private_class(_timelex) +_tzparser = __deprecate_private_class(_tzparser) +_resultbase = __deprecate_private_class(_resultbase) +_parsetz = __deprecated_private_func(_parsetz) diff --git a/venv/Lib/site-packages/dateutil/parser/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/dateutil/parser/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d41e52b14281ca9688f6cdbebd9af16aab23caed GIT binary patch literal 2071 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 Y;O^shm|y(w8Fun2@3VO%rbmbVKeaXhRR910 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dateutil/parser/__pycache__/isoparser.cpython-36.pyc b/venv/Lib/site-packages/dateutil/parser/__pycache__/isoparser.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb204225e4a5fd8ece6b5393bcf6a2ddc0f62534 GIT binary patch literal 11196 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 +- `W3C Date and Time Formats `_ +- `Time Formats (Planetary Rings Node) `_ +- `CPAN ParseDate module + `_ +- `Java SimpleDateFormat Class + `_ +""" +from __future__ import unicode_literals + +import datetime +import re +import string +import time +import warnings + +from calendar import monthrange +from io import StringIO + +import six +from six import integer_types, text_type + +from decimal import Decimal + +from warnings import warn + +from .. import relativedelta +from .. import tz + +__all__ = ["parse", "parserinfo", "ParserError"] + + +# TODO: pandas.core.tools.datetimes imports this explicitly. Might be worth +# making public and/or figuring out if there is something we can +# take off their plate. +class _timelex(object): + # Fractional seconds are sometimes split by a comma + _split_decimal = re.compile("([.,])") + + def __init__(self, instream): + if six.PY2: + # In Python 2, we can't duck type properly because unicode has + # a 'decode' function, and we'd be double-decoding + if isinstance(instream, (bytes, bytearray)): + instream = instream.decode() + else: + if getattr(instream, 'decode', None) is not None: + instream = instream.decode() + + if isinstance(instream, text_type): + instream = StringIO(instream) + elif getattr(instream, 'read', None) is None: + raise TypeError('Parser must be a string or character stream, not ' + '{itype}'.format(itype=instream.__class__.__name__)) + + 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"), # TODO: "Tues" + ("Wed", "Wednesday"), + ("Thu", "Thursday"), # TODO: "Thurs" + ("Fri", "Friday"), + ("Sat", "Saturday"), + ("Sun", "Sunday")] + MONTHS = [("Jan", "January"), + ("Feb", "February"), # TODO: "Febr" + ("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", "z"] + PERTAIN = ["of"] + TZOFFSET = {} + # TODO: ERA = ["AD", "BC", "CE", "BCE", "Stardate", + # "Anno Domini", "Year of Our Lord"] + + 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): + try: + return self._weekdays[name.lower()] + except KeyError: + pass + return None + + def month(self, name): + 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): + """ + Converts two-digit years to year within [-50, 49] + range of self._year (current local time) + """ + + # Function contract is that the year is always positive + assert year >= 0 + + if year < 100 and not century_specified: + # assume current century to start + year += self._century + + if year >= self._year + 50: # if too far in future + year -= 100 + elif year < self._year - 50: # if too far in past + 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' 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, *args, **kwargs): + super(self.__class__, self).__init__(*args, **kwargs) + self.century_specified = False + self.dstridx = None + self.mstridx = None + self.ystridx = None + + @property + def has_year(self): + return self.ystridx is not None + + @property + def has_month(self): + return self.mstridx is not None + + @property + def has_day(self): + return self.dstridx is not None + + def could_be_day(self, value): + if self.has_day: + return False + elif not self.has_month: + return 1 <= value <= 31 + elif not self.has_year: + # Be permissive, assume leap year + month = self[self.mstridx] + return 1 <= value <= monthrange(2000, month)[1] + else: + month = self[self.mstridx] + year = self[self.ystridx] + return 1 <= value <= monthrange(year, month)[1] + + def append(self, val, label=None): + if hasattr(val, '__len__'): + if val.isdigit() and len(val) > 2: + self.century_specified = True + if label not in [None, 'Y']: # pragma: no cover + raise ValueError(label) + label = 'Y' + elif val > 100: + self.century_specified = True + if label not in [None, 'Y']: # pragma: no cover + raise ValueError(label) + label = 'Y' + + super(self.__class__, self).append(int(val)) + + if label == 'M': + if self.has_month: + raise ValueError('Month is already set') + self.mstridx = len(self) - 1 + elif label == 'D': + if self.has_day: + raise ValueError('Day is already set') + self.dstridx = len(self) - 1 + elif label == 'Y': + if self.has_year: + raise ValueError('Year is already set') + self.ystridx = len(self) - 1 + + def _resolve_from_stridxs(self, strids): + """ + Try to resolve the identities of year/month/day elements using + ystridx, mstridx, and dstridx, if enough of these are specified. + """ + if len(self) == 3 and len(strids) == 2: + # we can back out the remaining stridx value + missing = [x for x in range(3) if x not in strids.values()] + key = [x for x in ['y', 'm', 'd'] if x not in strids] + assert len(missing) == len(key) == 1 + key = key[0] + val = missing[0] + strids[key] = val + + assert len(self) == len(strids) # otherwise this should not be called + out = {key: self[strids[key]] for key in strids} + return (out.get('y'), out.get('m'), out.get('d')) + + def resolve_ymd(self, yearfirst, dayfirst): + len_ymd = len(self) + year, month, day = (None, None, None) + + strids = (('y', self.ystridx), + ('m', self.mstridx), + ('d', self.dstridx)) + + strids = {key: val for key, val in strids if val is not None} + if (len(self) == len(strids) > 0 or + (len(self) == 3 and len(strids) == 2)): + return self._resolve_from_stridxs(strids) + + mstridx = self.mstridx + + if len_ymd > 3: + raise ValueError("More than three YMD values") + elif len_ymd == 1 or (mstridx is not None and len_ymd == 2): + # One member, or two members with a month string + if mstridx is not None: + month = self[mstridx] + # since mstridx is 0 or 1, self[mstridx-1] always + # looks up the other element + other = self[mstridx - 1] + else: + other = self[0] + + if len_ymd > 1 or mstridx is None: + if other > 31: + year = other + else: + day = other + + 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: + if self[1] > 31: + # Apr-2003-25 + month, year, day = self + else: + 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 precedence 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.ystridx == 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 seconds or a :class:`tzinfo` object. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> from dateutil.parser import parse + >>> from dateutil.tz import gettz + >>> tzinfos = {"BRST": -7200, "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', -7200)) + >>> 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 ParserError: + 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 TypeError: + Raised for non-string or character stream input. + + :raises OverflowError: + Raised if the parsed date exceeds the largest valid C integer on + your system. + """ + + if default is None: + default = datetime.datetime.now().replace(hour=0, minute=0, + second=0, microsecond=0) + + res, skipped_tokens = self._parse(timestr, **kwargs) + + if res is None: + raise ParserError("Unknown string format: %s", timestr) + + if len(res) == 0: + raise ParserError("String does not contain a date: %s", timestr) + + try: + ret = self._build_naive(res, default) + except ValueError as e: + six.raise_from(ParserError(e.args[0] + ": %s", timestr), e) + + if not ignoretz: + ret = self._build_tzaware(ret, res, tzinfos) + + 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","any_unused_tokens"] + + 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 + + skipped_idxs = [] + + # year/month/day list + ymd = _ymd() + + len_l = len(l) + i = 0 + try: + while i < len_l: + + # Check if it's a number + value_repr = l[i] + try: + value = float(value_repr) + except ValueError: + value = None + + if value is not None: + # Numeric token + i = self._parse_numeric_token(l, i, info, ymd, res, fuzzy) + + # Check weekday + elif info.weekday(l[i]) is not None: + value = info.weekday(l[i]) + res.weekday = value + + # Check month name + elif info.month(l[i]) is not None: + value = info.month(l[i]) + ymd.append(value, 'M') + + if i + 1 < len_l: + if l[i + 1] in ('-', '/'): + # Jan-01[-99] + sep = l[i + 1] + ymd.append(l[i + 2]) + + if i + 3 < len_l and l[i + 3] == sep: + # Jan-01-99 + ymd.append(l[i + 4]) + i += 2 + + i += 2 + + elif (i + 4 < len_l and l[i + 1] == l[i + 3] == ' ' and + info.pertain(l[i + 2])): + # Jan of 01 + # In this case, 01 is clearly year + if l[i + 4].isdigit(): + # Convert it here to become unambiguous + value = int(l[i + 4]) + year = str(info.convertyear(value)) + ymd.append(year, 'Y') + else: + # Wrong guess + pass + # TODO: not hit in tests + i += 4 + + # Check am/pm + elif info.ampm(l[i]) is not None: + value = info.ampm(l[i]) + val_is_ampm = self._ampm_valid(res.hour, res.ampm, fuzzy) + + if val_is_ampm: + res.hour = self._adjust_ampm(res.hour, value) + res.ampm = value + + elif fuzzy: + skipped_idxs.append(i) + + # Check for a timezone name + elif self._could_be_tzname(res.hour, res.tzname, res.tzoffset, l[i]): + res.tzname = l[i] + res.tzoffset = info.tzoffset(res.tzname) + + # 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 + 1 < len_l and l[i + 1] in ('+', '-'): + l[i + 1] = ('+', '-')[l[i + 1] == '+'] + res.tzoffset = None + if info.utczone(res.tzname): + # With something like GMT+3, the timezone + # is *not* GMT. + res.tzname = None + + # Check for a numbered timezone + elif res.hour is not None and l[i] in ('+', '-'): + signal = (-1, 1)[l[i] == '+'] + len_li = len(l[i + 1]) + + # TODO: check that l[i + 1] is integer? + if len_li == 4: + # -0300 + hour_offset = int(l[i + 1][:2]) + min_offset = int(l[i + 1][2:]) + elif i + 2 < len_l and l[i + 2] == ':': + # -03:00 + hour_offset = int(l[i + 1]) + min_offset = int(l[i + 3]) # TODO: Check that l[i+3] is minute-like? + i += 2 + elif len_li <= 2: + # -[0]3 + hour_offset = int(l[i + 1][:2]) + min_offset = 0 + else: + raise ValueError(timestr) + + res.tzoffset = signal * (hour_offset * 3600 + min_offset * 60) + + # Look for a timezone name between parenthesis + if (i + 5 < len_l and + info.jump(l[i + 2]) and l[i + 3] == '(' and + l[i + 5] == ')' and + 3 <= len(l[i + 4]) and + self._could_be_tzname(res.hour, res.tzname, + None, l[i + 4])): + # -0300 (BRST) + res.tzname = l[i + 4] + i += 4 + + i += 1 + + # Check jumps + elif not (info.jump(l[i]) or fuzzy): + raise ValueError(timestr) + + else: + skipped_idxs.append(i) + i += 1 + + # Process year/month/day + year, month, day = ymd.resolve_ymd(yearfirst, dayfirst) + + res.century_specified = ymd.century_specified + res.year = year + res.month = month + res.day = day + + except (IndexError, ValueError): + return None, None + + if not info.validate(res): + return None, None + + if fuzzy_with_tokens: + skipped_tokens = self._recombine_skipped(l, skipped_idxs) + return res, tuple(skipped_tokens) + else: + return res, None + + def _parse_numeric_token(self, tokens, idx, info, ymd, res, fuzzy): + # Token is a number + value_repr = tokens[idx] + try: + value = self._to_decimal(value_repr) + except Exception as e: + six.raise_from(ValueError('Unknown numeric token'), e) + + len_li = len(value_repr) + + len_l = len(tokens) + + if (len(ymd) == 3 and len_li in (2, 4) and + res.hour is None and + (idx + 1 >= len_l or + (tokens[idx + 1] != ':' and + info.hms(tokens[idx + 1]) is None))): + # 19990101T23[59] + s = tokens[idx] + res.hour = int(s[:2]) + + if len_li == 4: + res.minute = int(s[2:]) + + elif len_li == 6 or (len_li > 6 and tokens[idx].find('.') == 6): + # YYMMDD or HHMMSS[.ss] + s = tokens[idx] + + if not ymd and '.' not in tokens[idx]: + ymd.append(s[:2]) + ymd.append(s[2:4]) + ymd.append(s[4:]) + else: + # 19990101T235959[.59] + + # TODO: Check if res attributes already set. + res.hour = int(s[:2]) + res.minute = int(s[2:4]) + res.second, res.microsecond = self._parsems(s[4:]) + + elif len_li in (8, 12, 14): + # YYYYMMDD + s = tokens[idx] + ymd.append(s[:4], 'Y') + 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 self._find_hms_idx(idx, tokens, info, allow_jump=True) is not None: + # HH[ ]h or MM[ ]m or SS[.ss][ ]s + hms_idx = self._find_hms_idx(idx, tokens, info, allow_jump=True) + (idx, hms) = self._parse_hms(idx, tokens, info, hms_idx) + if hms is not None: + # TODO: checking that hour/minute/second are not + # already set? + self._assign_hms(res, value_repr, hms) + + elif idx + 2 < len_l and tokens[idx + 1] == ':': + # HH:MM[:SS[.ss]] + res.hour = int(value) + value = self._to_decimal(tokens[idx + 2]) # TODO: try/except for this? + (res.minute, res.second) = self._parse_min_sec(value) + + if idx + 4 < len_l and tokens[idx + 3] == ':': + res.second, res.microsecond = self._parsems(tokens[idx + 4]) + + idx += 2 + + idx += 2 + + elif idx + 1 < len_l and tokens[idx + 1] in ('-', '/', '.'): + sep = tokens[idx + 1] + ymd.append(value_repr) + + if idx + 2 < len_l and not info.jump(tokens[idx + 2]): + if tokens[idx + 2].isdigit(): + # 01-01[-01] + ymd.append(tokens[idx + 2]) + else: + # 01-Jan[-01] + value = info.month(tokens[idx + 2]) + + if value is not None: + ymd.append(value, 'M') + else: + raise ValueError() + + if idx + 3 < len_l and tokens[idx + 3] == sep: + # We have three members + value = info.month(tokens[idx + 4]) + + if value is not None: + ymd.append(value, 'M') + else: + ymd.append(tokens[idx + 4]) + idx += 2 + + idx += 1 + idx += 1 + + elif idx + 1 >= len_l or info.jump(tokens[idx + 1]): + if idx + 2 < len_l and info.ampm(tokens[idx + 2]) is not None: + # 12 am + hour = int(value) + res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 2])) + idx += 1 + else: + # Year, month or day + ymd.append(value) + idx += 1 + + elif info.ampm(tokens[idx + 1]) is not None and (0 <= value < 24): + # 12am + hour = int(value) + res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 1])) + idx += 1 + + elif ymd.could_be_day(value): + ymd.append(value) + + elif not fuzzy: + raise ValueError() + + return idx + + def _find_hms_idx(self, idx, tokens, info, allow_jump): + len_l = len(tokens) + + if idx+1 < len_l and info.hms(tokens[idx+1]) is not None: + # There is an "h", "m", or "s" label following this token. We take + # assign the upcoming label to the current token. + # e.g. the "12" in 12h" + hms_idx = idx + 1 + + elif (allow_jump and idx+2 < len_l and tokens[idx+1] == ' ' and + info.hms(tokens[idx+2]) is not None): + # There is a space and then an "h", "m", or "s" label. + # e.g. the "12" in "12 h" + hms_idx = idx + 2 + + elif idx > 0 and info.hms(tokens[idx-1]) is not None: + # There is a "h", "m", or "s" preceding this token. Since neither + # of the previous cases was hit, there is no label following this + # token, so we use the previous label. + # e.g. the "04" in "12h04" + hms_idx = idx-1 + + elif (1 < idx == len_l-1 and tokens[idx-1] == ' ' and + info.hms(tokens[idx-2]) is not None): + # If we are looking at the final token, we allow for a + # backward-looking check to skip over a space. + # TODO: Are we sure this is the right condition here? + hms_idx = idx - 2 + + else: + hms_idx = None + + return hms_idx + + def _assign_hms(self, res, value_repr, hms): + # See GH issue #427, fixing float rounding + value = self._to_decimal(value_repr) + + if hms == 0: + # Hour + res.hour = int(value) + if value % 1: + res.minute = int(60*(value % 1)) + + elif hms == 1: + (res.minute, res.second) = self._parse_min_sec(value) + + elif hms == 2: + (res.second, res.microsecond) = self._parsems(value_repr) + + def _could_be_tzname(self, hour, tzname, tzoffset, token): + return (hour is not None and + tzname is None and + tzoffset is None and + len(token) <= 5 and + (all(x in string.ascii_uppercase for x in token) + or token in self.info.UTCZONE)) + + def _ampm_valid(self, hour, ampm, fuzzy): + """ + 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 ampm is not None: + val_is_ampm = False + + # If AM/PM is found and hour is not, raise a ValueError + if hour is None: + if fuzzy: + val_is_ampm = False + else: + raise ValueError('No hour specified with AM or PM flag.') + elif not 0 <= 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.') + + return val_is_ampm + + def _adjust_ampm(self, hour, ampm): + if hour < 12 and ampm == 1: + hour += 12 + elif hour == 12 and ampm == 0: + hour = 0 + return hour + + def _parse_min_sec(self, value): + # TODO: Every usage of this function sets res.second to the return + # value. Are there any cases where second will be returned as None and + # we *don't* want to set res.second = None? + minute = int(value) + second = None + + sec_remainder = value % 1 + if sec_remainder: + second = int(60 * sec_remainder) + return (minute, second) + + def _parse_hms(self, idx, tokens, info, hms_idx): + # TODO: Is this going to admit a lot of false-positives for when we + # just happen to have digits and "h", "m" or "s" characters in non-date + # text? I guess hex hashes won't have that problem, but there's plenty + # of random junk out there. + if hms_idx is None: + hms = None + new_idx = idx + elif hms_idx > idx: + hms = info.hms(tokens[hms_idx]) + new_idx = hms_idx + else: + # Looking backwards, increment one. + hms = info.hms(tokens[hms_idx]) + 1 + new_idx = idx + + return (new_idx, hms) + + # ------------------------------------------------------------------ + # Handling for individual tokens. These are kept as methods instead + # of functions for the sake of customizability via subclassing. + + def _parsems(self, 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]) + + def _to_decimal(self, val): + try: + decimal_value = Decimal(val) + # See GH 662, edge case, infinite value should not be converted + # via `_to_decimal` + if not decimal_value.is_finite(): + raise ValueError("Converted decimal value is infinite or NaN") + except Exception as e: + msg = "Could not convert %s to decimal" % val + six.raise_from(ValueError(msg), e) + else: + return decimal_value + + # ------------------------------------------------------------------ + # Post-Parsing construction of datetime output. These are kept as + # methods instead of functions for the sake of customizability via + # subclassing. + + def _build_tzinfo(self, tzinfos, tzname, tzoffset): + if callable(tzinfos): + tzdata = tzinfos(tzname, tzoffset) + else: + tzdata = tzinfos.get(tzname) + # handle case where tzinfo is paased an options that returns None + # eg tzinfos = {'BRST' : None} + if isinstance(tzdata, datetime.tzinfo) or tzdata is None: + tzinfo = tzdata + elif isinstance(tzdata, text_type): + tzinfo = tz.tzstr(tzdata) + elif isinstance(tzdata, integer_types): + tzinfo = tz.tzoffset(tzname, tzdata) + else: + raise TypeError("Offset must be tzinfo subclass, tz string, " + "or int offset.") + return tzinfo + + def _build_tzaware(self, naive, res, tzinfos): + if (callable(tzinfos) or (tzinfos and res.tzname in tzinfos)): + tzinfo = self._build_tzinfo(tzinfos, res.tzname, res.tzoffset) + aware = naive.replace(tzinfo=tzinfo) + aware = self._assign_tzname(aware, res.tzname) + + elif res.tzname and res.tzname in time.tzname: + aware = naive.replace(tzinfo=tz.tzlocal()) + + # Handle ambiguous local datetime + aware = self._assign_tzname(aware, res.tzname) + + # This is mostly relevant for winter GMT zones parsed in the UK + if (aware.tzname() != res.tzname and + res.tzname in self.info.UTCZONE): + aware = aware.replace(tzinfo=tz.UTC) + + elif res.tzoffset == 0: + aware = naive.replace(tzinfo=tz.UTC) + + elif res.tzoffset: + aware = naive.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) + + elif not res.tzname and not res.tzoffset: + # i.e. no timezone information was found. + aware = naive + + elif res.tzname: + # tz-like string was parsed but we don't know what to do + # with it + warnings.warn("tzname {tzname} identified but not understood. " + "Pass `tzinfos` argument in order to correctly " + "return a timezone-aware datetime. In a future " + "version, this will raise an " + "exception.".format(tzname=res.tzname), + category=UnknownTimezoneWarning) + aware = naive + + return aware + + def _build_naive(self, res, default): + 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] + + naive = default.replace(**repl) + + if res.weekday is not None and not res.day: + naive = naive + relativedelta.relativedelta(weekday=res.weekday) + + return naive + + def _assign_tzname(self, dt, tzname): + if dt.tzname() != tzname: + new_dt = tz.enfold(dt, fold=1) + if new_dt.tzname() == tzname: + return new_dt + + return dt + + def _recombine_skipped(self, tokens, skipped_idxs): + """ + >>> tokens = ["foo", " ", "bar", " ", "19June2000", "baz"] + >>> skipped_idxs = [0, 1, 2, 5] + >>> _recombine_skipped(tokens, skipped_idxs) + ["foo bar", "baz"] + """ + skipped_tokens = [] + for i, idx in enumerate(sorted(skipped_idxs)): + if i > 0 and idx - 1 == skipped_idxs[i - 1]: + skipped_tokens[-1] = skipped_tokens[-1] + tokens[idx] + else: + skipped_tokens.append(tokens[idx]) + + return skipped_tokens + + +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 seconds or a :class:`tzinfo` object. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> from dateutil.parser import parse + >>> from dateutil.tz import gettz + >>> tzinfos = {"BRST": -7200, "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', -7200)) + >>> 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(2047, 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 = [x for x in re.split(r'([,:.]|[a-zA-Z]+|[0-9]+)',tzstr) if x] + used_idxs = list() + 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]) + + for ii in range(j): + used_idxs.append(ii) + 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] == '+'] + used_idxs.append(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) + used_idxs.append(i) + i += 2 + elif len_li <= 2: + # -[0]3 + setattr(res, offattr, + int(l[i][:2]) * 3600 * signal) + else: + return None + used_idxs.append(i) + 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]) + used_idxs.append(i) + i += 2 + if l[i] == '-': + value = int(l[i + 1]) * -1 + used_idxs.append(i) + i += 1 + else: + value = int(l[i]) + used_idxs.append(i) + i += 2 + if value: + x.week = value + x.weekday = (int(l[i]) - 1) % 7 + else: + x.day = int(l[i]) + used_idxs.append(i) + i += 2 + x.time = int(l[i]) + used_idxs.append(i) + i += 2 + if i < len_l: + if l[i] in ('-', '+'): + signal = (-1, 1)[l[i] == "+"] + used_idxs.append(i) + i += 1 + else: + signal = 1 + used_idxs.append(i) + res.dstoffset = (res.stdoffset + int(l[i]) * signal) + + # This was a made-up format that is not in normal use + warn(('Parsed time zone "%s"' % tzstr) + + 'is in a non-standard dateutil-specific format, which ' + + 'is now deprecated; support for parsing this format ' + + 'will be removed in future versions. It is recommended ' + + 'that you switch to a standard format like the GNU ' + + 'TZ variable format.', tz.DeprecatedTzFormatWarning) + 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) + used_idxs.append(i) + i += 1 + x.jyday = int(l[i]) + elif l[i] == 'M': + # month[-.]week[-.]weekday + used_idxs.append(i) + i += 1 + x.month = int(l[i]) + used_idxs.append(i) + i += 1 + assert l[i] in ('-', '.') + used_idxs.append(i) + i += 1 + x.week = int(l[i]) + if x.week == 5: + x.week = -1 + used_idxs.append(i) + i += 1 + assert l[i] in ('-', '.') + used_idxs.append(i) + i += 1 + x.weekday = (int(l[i]) - 1) % 7 + else: + # year day (zero based) + x.yday = int(l[i]) + 1 + + used_idxs.append(i) + i += 1 + + if i < len_l and l[i] == '/': + used_idxs.append(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 + used_idxs.append(i) + i += 2 + if i + 1 < len_l and l[i + 1] == ':': + used_idxs.append(i) + i += 2 + x.time += int(l[i]) + elif len_li <= 2: + # -[0]3 + x.time = (int(l[i][:2]) * 3600) + else: + return None + used_idxs.append(i) + i += 1 + + assert i == len_l or l[i] == ',' + + i += 1 + + assert i >= len_l + + except (IndexError, ValueError, AssertionError): + return None + + unused_idxs = set(range(len_l)).difference(used_idxs) + res.any_unused_tokens = not {l[n] for n in unused_idxs}.issubset({",",":"}) + return res + + +DEFAULTTZPARSER = _tzparser() + + +def _parsetz(tzstr): + return DEFAULTTZPARSER.parse(tzstr) + + +class ParserError(ValueError): + """Error class for representing failure to parse a datetime string.""" + def __str__(self): + try: + return self.args[0] % self.args[1:] + except (TypeError, IndexError): + return super(ParserError, self).__str__() + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, str(self)) + + +class UnknownTimezoneWarning(RuntimeWarning): + """Raised when the parser finds a timezone it cannot parse into a tzinfo""" +# vim:ts=4:sw=4:et diff --git a/venv/Lib/site-packages/dateutil/parser/isoparser.py b/venv/Lib/site-packages/dateutil/parser/isoparser.py new file mode 100644 index 00000000..48f86a33 --- /dev/null +++ b/venv/Lib/site-packages/dateutil/parser/isoparser.py @@ -0,0 +1,411 @@ +# -*- coding: utf-8 -*- +""" +This module offers a parser for ISO-8601 strings + +It is intended to support all valid date, time and datetime formats per the +ISO-8601 specification. + +..versionadded:: 2.7.0 +""" +from datetime import datetime, timedelta, time, date +import calendar +from dateutil import tz + +from functools import wraps + +import re +import six + +__all__ = ["isoparse", "isoparser"] + + +def _takes_ascii(f): + @wraps(f) + def func(self, str_in, *args, **kwargs): + # If it's a stream, read the whole thing + str_in = getattr(str_in, 'read', lambda: str_in)() + + # If it's unicode, turn it into bytes, since ISO-8601 only covers ASCII + if isinstance(str_in, six.text_type): + # ASCII is the same in UTF-8 + try: + str_in = str_in.encode('ascii') + except UnicodeEncodeError as e: + msg = 'ISO-8601 strings should contain only ASCII characters' + six.raise_from(ValueError(msg), e) + + return f(self, str_in, *args, **kwargs) + + return func + + +class isoparser(object): + def __init__(self, sep=None): + """ + :param sep: + A single character that separates date and time portions. If + ``None``, the parser will accept any single character. + For strict ISO-8601 adherence, pass ``'T'``. + """ + if sep is not None: + if (len(sep) != 1 or ord(sep) >= 128 or sep in '0123456789'): + raise ValueError('Separator must be a single, non-numeric ' + + 'ASCII character') + + sep = sep.encode('ascii') + + self._sep = sep + + @_takes_ascii + def isoparse(self, dt_str): + """ + Parse an ISO-8601 datetime string into a :class:`datetime.datetime`. + + An ISO-8601 datetime string consists of a date portion, followed + optionally by a time portion - the date and time portions are separated + by a single character separator, which is ``T`` in the official + standard. Incomplete date formats (such as ``YYYY-MM``) may *not* be + combined with a time portion. + + Supported date formats are: + + Common: + + - ``YYYY`` + - ``YYYY-MM`` or ``YYYYMM`` + - ``YYYY-MM-DD`` or ``YYYYMMDD`` + + Uncommon: + + - ``YYYY-Www`` or ``YYYYWww`` - ISO week (day defaults to 0) + - ``YYYY-Www-D`` or ``YYYYWwwD`` - ISO week and day + + The ISO week and day numbering follows the same logic as + :func:`datetime.date.isocalendar`. + + Supported time formats are: + + - ``hh`` + - ``hh:mm`` or ``hhmm`` + - ``hh:mm:ss`` or ``hhmmss`` + - ``hh:mm:ss.ssssss`` (Up to 6 sub-second digits) + + Midnight is a special case for `hh`, as the standard supports both + 00:00 and 24:00 as a representation. The decimal separator can be + either a dot or a comma. + + + .. caution:: + + Support for fractional components other than seconds is part of the + ISO-8601 standard, but is not currently implemented in this parser. + + Supported time zone offset formats are: + + - `Z` (UTC) + - `±HH:MM` + - `±HHMM` + - `±HH` + + Offsets will be represented as :class:`dateutil.tz.tzoffset` objects, + with the exception of UTC, which will be represented as + :class:`dateutil.tz.tzutc`. Time zone offsets equivalent to UTC (such + as `+00:00`) will also be represented as :class:`dateutil.tz.tzutc`. + + :param dt_str: + A string or stream containing only an ISO-8601 datetime string + + :return: + Returns a :class:`datetime.datetime` representing the string. + Unspecified components default to their lowest value. + + .. warning:: + + As of version 2.7.0, the strictness of the parser should not be + considered a stable part of the contract. Any valid ISO-8601 string + that parses correctly with the default settings will continue to + parse correctly in future versions, but invalid strings that + currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not + guaranteed to continue failing in future versions if they encode + a valid date. + + .. versionadded:: 2.7.0 + """ + components, pos = self._parse_isodate(dt_str) + + if len(dt_str) > pos: + if self._sep is None or dt_str[pos:pos + 1] == self._sep: + components += self._parse_isotime(dt_str[pos + 1:]) + else: + raise ValueError('String contains unknown ISO components') + + if len(components) > 3 and components[3] == 24: + components[3] = 0 + return datetime(*components) + timedelta(days=1) + + return datetime(*components) + + @_takes_ascii + def parse_isodate(self, datestr): + """ + Parse the date portion of an ISO string. + + :param datestr: + The string portion of an ISO string, without a separator + + :return: + Returns a :class:`datetime.date` object + """ + components, pos = self._parse_isodate(datestr) + if pos < len(datestr): + raise ValueError('String contains unknown ISO ' + + 'components: {}'.format(datestr)) + return date(*components) + + @_takes_ascii + def parse_isotime(self, timestr): + """ + Parse the time portion of an ISO string. + + :param timestr: + The time portion of an ISO string, without a separator + + :return: + Returns a :class:`datetime.time` object + """ + components = self._parse_isotime(timestr) + if components[0] == 24: + components[0] = 0 + return time(*components) + + @_takes_ascii + def parse_tzstr(self, tzstr, zero_as_utc=True): + """ + Parse a valid ISO time zone string. + + See :func:`isoparser.isoparse` for details on supported formats. + + :param tzstr: + A string representing an ISO time zone offset + + :param zero_as_utc: + Whether to return :class:`dateutil.tz.tzutc` for zero-offset zones + + :return: + Returns :class:`dateutil.tz.tzoffset` for offsets and + :class:`dateutil.tz.tzutc` for ``Z`` and (if ``zero_as_utc`` is + specified) offsets equivalent to UTC. + """ + return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc) + + # Constants + _DATE_SEP = b'-' + _TIME_SEP = b':' + _FRACTION_REGEX = re.compile(b'[\\.,]([0-9]+)') + + def _parse_isodate(self, dt_str): + try: + return self._parse_isodate_common(dt_str) + except ValueError: + return self._parse_isodate_uncommon(dt_str) + + def _parse_isodate_common(self, dt_str): + len_str = len(dt_str) + components = [1, 1, 1] + + if len_str < 4: + raise ValueError('ISO string too short') + + # Year + components[0] = int(dt_str[0:4]) + pos = 4 + if pos >= len_str: + return components, pos + + has_sep = dt_str[pos:pos + 1] == self._DATE_SEP + if has_sep: + pos += 1 + + # Month + if len_str - pos < 2: + raise ValueError('Invalid common month') + + components[1] = int(dt_str[pos:pos + 2]) + pos += 2 + + if pos >= len_str: + if has_sep: + return components, pos + else: + raise ValueError('Invalid ISO format') + + if has_sep: + if dt_str[pos:pos + 1] != self._DATE_SEP: + raise ValueError('Invalid separator in ISO string') + pos += 1 + + # Day + if len_str - pos < 2: + raise ValueError('Invalid common day') + components[2] = int(dt_str[pos:pos + 2]) + return components, pos + 2 + + def _parse_isodate_uncommon(self, dt_str): + if len(dt_str) < 4: + raise ValueError('ISO string too short') + + # All ISO formats start with the year + year = int(dt_str[0:4]) + + has_sep = dt_str[4:5] == self._DATE_SEP + + pos = 4 + has_sep # Skip '-' if it's there + if dt_str[pos:pos + 1] == b'W': + # YYYY-?Www-?D? + pos += 1 + weekno = int(dt_str[pos:pos + 2]) + pos += 2 + + dayno = 1 + if len(dt_str) > pos: + if (dt_str[pos:pos + 1] == self._DATE_SEP) != has_sep: + raise ValueError('Inconsistent use of dash separator') + + pos += has_sep + + dayno = int(dt_str[pos:pos + 1]) + pos += 1 + + base_date = self._calculate_weekdate(year, weekno, dayno) + else: + # YYYYDDD or YYYY-DDD + if len(dt_str) - pos < 3: + raise ValueError('Invalid ordinal day') + + ordinal_day = int(dt_str[pos:pos + 3]) + pos += 3 + + if ordinal_day < 1 or ordinal_day > (365 + calendar.isleap(year)): + raise ValueError('Invalid ordinal day' + + ' {} for year {}'.format(ordinal_day, year)) + + base_date = date(year, 1, 1) + timedelta(days=ordinal_day - 1) + + components = [base_date.year, base_date.month, base_date.day] + return components, pos + + def _calculate_weekdate(self, year, week, day): + """ + Calculate the day of corresponding to the ISO year-week-day calendar. + + This function is effectively the inverse of + :func:`datetime.date.isocalendar`. + + :param year: + The year in the ISO calendar + + :param week: + The week in the ISO calendar - range is [1, 53] + + :param day: + The day in the ISO calendar - range is [1 (MON), 7 (SUN)] + + :return: + Returns a :class:`datetime.date` + """ + if not 0 < week < 54: + raise ValueError('Invalid week: {}'.format(week)) + + if not 0 < day < 8: # Range is 1-7 + raise ValueError('Invalid weekday: {}'.format(day)) + + # Get week 1 for the specific year: + jan_4 = date(year, 1, 4) # Week 1 always has January 4th in it + week_1 = jan_4 - timedelta(days=jan_4.isocalendar()[2] - 1) + + # Now add the specific number of weeks and days to get what we want + week_offset = (week - 1) * 7 + (day - 1) + return week_1 + timedelta(days=week_offset) + + def _parse_isotime(self, timestr): + len_str = len(timestr) + components = [0, 0, 0, 0, None] + pos = 0 + comp = -1 + + if len(timestr) < 2: + raise ValueError('ISO time too short') + + has_sep = len_str >= 3 and timestr[2:3] == self._TIME_SEP + + while pos < len_str and comp < 5: + comp += 1 + + if timestr[pos:pos + 1] in b'-+Zz': + # Detect time zone boundary + components[-1] = self._parse_tzstr(timestr[pos:]) + pos = len_str + break + + if comp < 3: + # Hour, minute, second + components[comp] = int(timestr[pos:pos + 2]) + pos += 2 + if (has_sep and pos < len_str and + timestr[pos:pos + 1] == self._TIME_SEP): + pos += 1 + + if comp == 3: + # Fraction of a second + frac = self._FRACTION_REGEX.match(timestr[pos:]) + if not frac: + continue + + us_str = frac.group(1)[:6] # Truncate to microseconds + components[comp] = int(us_str) * 10**(6 - len(us_str)) + pos += len(frac.group()) + + if pos < len_str: + raise ValueError('Unused components in ISO string') + + if components[0] == 24: + # Standard supports 00:00 and 24:00 as representations of midnight + if any(component != 0 for component in components[1:4]): + raise ValueError('Hour may only be 24 at 24:00:00.000') + + return components + + def _parse_tzstr(self, tzstr, zero_as_utc=True): + if tzstr == b'Z' or tzstr == b'z': + return tz.UTC + + if len(tzstr) not in {3, 5, 6}: + raise ValueError('Time zone offset must be 1, 3, 5 or 6 characters') + + if tzstr[0:1] == b'-': + mult = -1 + elif tzstr[0:1] == b'+': + mult = 1 + else: + raise ValueError('Time zone offset requires sign') + + hours = int(tzstr[1:3]) + if len(tzstr) == 3: + minutes = 0 + else: + minutes = int(tzstr[(4 if tzstr[3:4] == self._TIME_SEP else 3):]) + + if zero_as_utc and hours == 0 and minutes == 0: + return tz.UTC + else: + if minutes > 59: + raise ValueError('Invalid minutes in time zone offset') + + if hours > 23: + raise ValueError('Invalid hours in time zone offset') + + return tz.tzoffset(None, mult * (hours * 60 + minutes) * 60) + + +DEFAULT_ISOPARSER = isoparser() +isoparse = DEFAULT_ISOPARSER.isoparse diff --git a/venv/Lib/site-packages/dateutil/relativedelta.py b/venv/Lib/site-packages/dateutil/relativedelta.py new file mode 100644 index 00000000..a9e85f7e --- /dev/null +++ b/venv/Lib/site-packages/dateutil/relativedelta.py @@ -0,0 +1,599 @@ +# -*- coding: utf-8 -*- +import datetime +import calendar + +import operator +from math import copysign + +from six import integer_types +from warnings import warn + +from ._common import weekday + +MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) + +__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] + + +class relativedelta(object): + """ + The relativedelta type is designed to be applied to an existing datetime and + can replace specific components of that datetime, or represents an interval + of time. + + It 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 arithmetic + 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 arithmetic operation on the original datetime value + with the information in the relativedelta. + + weekday: + One of the weekday instances (MO, TU, etc) available in the + relativedelta module. 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. This argument is always + relative e.g. if the calculated date is already Monday, using MO(1) + or MO(-1) won't change the day. To effectively make it absolute, use + it in combination with the day argument (e.g. day=1, MO(1) for first + Monday of the month). + + 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. + + There are relative and absolute forms of the keyword + arguments. The plural is relative, and the singular is + absolute. For each argument in the order below, the absolute form + is applied first (by setting each attribute to that value) and + then the relative form (by adding the value to the attribute). + + The order of attributes considered when this relativedelta is + added to a datetime is: + + 1. Year + 2. Month + 3. Day + 4. Hours + 5. Minutes + 6. Seconds + 7. Microseconds + + Finally, weekday is applied, using the rule described above. + + For example + + >>> from datetime import datetime + >>> from dateutil.relativedelta import relativedelta, MO + >>> dt = datetime(2018, 4, 9, 13, 37, 0) + >>> delta = relativedelta(hours=25, day=1, weekday=MO(1)) + >>> dt + delta + datetime.datetime(2018, 4, 2, 14, 37) + + First, the day is set to 1 (the first of the month), then 25 hours + are added, to get to the 2nd day and 14th hour, finally the + weekday is applied, but since the 2nd is already a Monday there is + no effect. + + """ + + 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): + + 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: + # 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.") + + # Relative information + self.years = int(years) + self.months = int(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 int(self.days / 7.0) + + @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 if other.year is not None + else self.year), + month=(other.month if other.month is not None + else self.month), + day=(other.day if other.day is not None + else self.day), + weekday=(other.weekday if other.weekday is not None + else self.weekday), + hour=(other.hour if other.hour is not None + else self.hour), + minute=(other.minute if other.minute is not None + else self.minute), + second=(other.second if other.second is not None + else self.second), + microsecond=(other.microsecond if other.microsecond + is not None else + self.microsecond)) + if isinstance(other, datetime.timedelta): + return self.__class__(years=self.years, + months=self.months, + days=self.days + other.days, + hours=self.hours, + minutes=self.minutes, + seconds=self.seconds + other.seconds, + microseconds=self.microseconds + other.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) + if not isinstance(other, datetime.date): + return NotImplemented + 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): + return NotImplemented # In case the other object defines __rsub__ + 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 if self.year is not None + else other.year), + month=(self.month if self.month is not None else + other.month), + day=(self.day if self.day is not None else + other.day), + weekday=(self.weekday if self.weekday is not None else + other.weekday), + hour=(self.hour if self.hour is not None else + other.hour), + minute=(self.minute if self.minute is not None else + other.minute), + second=(self.second if self.second is not None else + other.second), + microsecond=(self.microsecond if self.microsecond + is not None else + other.microsecond)) + + def __abs__(self): + return self.__class__(years=abs(self.years), + months=abs(self.months), + days=abs(self.days), + hours=abs(self.hours), + minutes=abs(self.minutes), + seconds=abs(self.seconds), + microseconds=abs(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 __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): + try: + f = float(other) + except TypeError: + return NotImplemented + + 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 NotImplemented + 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 __hash__(self): + return hash(( + self.weekday, + self.years, + self.months, + self.days, + self.hours, + self.minutes, + self.seconds, + self.microseconds, + self.leapdays, + self.year, + self.month, + self.day, + self.hour, + self.minute, + self.second, + self.microsecond, + )) + + def __ne__(self, other): + return not self.__eq__(other) + + def __div__(self, other): + try: + reciprocal = 1 / float(other) + except TypeError: + return NotImplemented + + return self.__mul__(reciprocal) + + __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/site-packages/dateutil/rrule.py b/venv/Lib/site-packages/dateutil/rrule.py new file mode 100644 index 00000000..6bf0ea9c --- /dev/null +++ b/venv/Lib/site-packages/dateutil/rrule.py @@ -0,0 +1,1735 @@ +# -*- 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 re +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, range +import heapq + +from ._common import weekday as weekdaybase + +# 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(weekdaybase): + """ + This version of weekday does not allow n = 0. + """ + def __init__(self, wkday, n=None): + if n == 0: + raise ValueError("Can't create weekday with n==0") + + super(weekday, self).__init__(wkday, 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 penalty. + 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): + if count is not None: + n += 1 + if n > count: + break + + yield d + + 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 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: + If given, this determines how many occurrences will be generated. + + .. note:: + As of version 2.5.0, the use of the keyword ``until`` in conjunction + with ``count`` is deprecated, to make sure ``dateutil`` is fully + compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` + **must not** occur in the same call to ``rrule``. + :param until: + If given, this must be a datetime instance specifying the upper-bound + 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 keyword ``until`` in conjunction + with ``count`` is deprecated, to make sure ``dateutil`` is fully + compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` + **must not** occur in the same call to ``rrule``. + :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 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. + :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 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. + """ + 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: + if until and until.tzinfo: + dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0) + else: + 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 self._dtstart and self._until: + if (self._dtstart.tzinfo is not None) != (self._until.tzinfo is not None): + # According to RFC5545 Section 3.3.10: + # https://tools.ietf.org/html/rfc5545#section-3.3.10 + # + # > If the "DTSTART" property is specified as a date with UTC + # > time or a date with local time and time zone reference, + # > then the UNTIL rule part MUST be specified as a date with + # > UTC time. + raise ValueError( + 'RRULE UNTIL values must be specified in UTC when DTSTART ' + 'is timezone-aware' + ) + + if count is not None and until: + warn("Using both 'count' and 'until' is inconsistent with RFC 5545" + " 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 = () + + if self._bynweekday is not None: + self._bynweekday = tuple(sorted(self._bynweekday)) + orig_bynweekday = [weekday(*x) for x in self._bynweekday] + else: + orig_bynweekday = () + + 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 = {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 = {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 RFC5545, 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=' + repr(weekday(self._wkst))[0:2]) + + if self._count is not None: + 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 + # RFC5545-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('RRULE:' + ';'.join(parts)) + return '\n'.join(output) + + def replace(self, **kwargs): + """Return new rrule with same attributes except for those attributes given new + values by whichever keyword arguments are specified.""" + new_kwargs = {"interval": self._interval, + "count": self._count, + "dtstart": self._dtstart, + "freq": self._freq, + "until": self._until, + "wkst": self._wkst, + "cache": False if self._cache is None else True } + new_kwargs.update(self._original_rule) + new_kwargs.update(kwargs) + return rrule(**new_kwargs) + + 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: + if count is not None: + count -= 1 + if count < 0: + self._len = total + return + total += 1 + yield res + 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: + if count is not None: + count -= 1 + if count < 0: + self._len = total + return + + total += 1 + yield res + + # 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): + """ Parses a string representation of a recurrence rule or set of + recurrence rules. + + :param s: + Required, a string defining one or more recurrence rules. + + :param dtstart: + If given, used as the default recurrence start if not specified in the + rule string. + + :param cache: + If set ``True`` caching of results will be enabled, improving + performance of multiple queries considerably. + + :param unfold: + If set ``True`` indicates that a rule string is split over more + than one line and should be joined before processing. + + :param forceset: + If set ``True`` forces a :class:`dateutil.rrule.rruleset` to + be returned. + + :param compatible: + If set ``True`` forces ``unfold`` and ``forceset`` to be ``True``. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a naive + :class:`datetime.datetime` object is returned. + + :param tzids: + If given, a callable or mapping used to retrieve a + :class:`datetime.tzinfo` from a string representation. + Defaults to :func:`dateutil.tz.gettz`. + + :param tzinfos: + Additional time zone names / aliases which may be present in a string + representation. See :func:`dateutil.parser.parse` for more + information. + + :return: + Returns a :class:`dateutil.rrule.rruleset` or + :class:`dateutil.rrule.rrule` + """ + + _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_date_value(self, date_value, parms, rule_tzids, + ignoretz, tzids, tzinfos): + global parser + if not parser: + from dateutil import parser + + datevals = [] + value_found = False + TZID = None + + for parm in parms: + if parm.startswith("TZID="): + try: + tzkey = rule_tzids[parm.split('TZID=')[-1]] + except KeyError: + continue + if tzids is None: + from . import tz + tzlookup = tz.gettz + elif callable(tzids): + tzlookup = tzids + else: + tzlookup = getattr(tzids, 'get', None) + if tzlookup is None: + msg = ('tzids must be a callable, mapping, or None, ' + 'not %s' % tzids) + raise ValueError(msg) + + TZID = tzlookup(tzkey) + continue + + # RFC 5445 3.8.2.4: The VALUE parameter is optional, but may be found + # only once. + if parm not in {"VALUE=DATE-TIME", "VALUE=DATE"}: + raise ValueError("unsupported parm: " + parm) + else: + if value_found: + msg = ("Duplicate value parameter found in: " + parm) + raise ValueError(msg) + value_found = True + + for datestr in date_value.split(','): + date = parser.parse(datestr, ignoretz=ignoretz, tzinfos=tzinfos) + if TZID is not None: + if date.tzinfo is None: + date = date.replace(tzinfo=TZID) + else: + raise ValueError('DTSTART/EXDATE specifies multiple timezone') + datevals.append(date) + + return datevals + + def _parse_rfc(self, s, + dtstart=None, + cache=False, + unfold=False, + forceset=False, + compatible=False, + ignoretz=False, + tzids=None, + tzinfos=None): + global parser + if compatible: + forceset = True + unfold = True + + TZID_NAMES = dict(map( + lambda x: (x.upper(), x), + re.findall('TZID=(?P[^:]+):', s) + )) + 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": + exdatevals.extend( + self._parse_date_value(value, parms, + TZID_NAMES, ignoretz, + tzids, tzinfos) + ) + elif name == "DTSTART": + dtvals = self._parse_date_value(value, parms, TZID_NAMES, + ignoretz, tzids, tzinfos) + if len(dtvals) != 1: + raise ValueError("Multiple DTSTART values specified:" + + value) + dtstart = dtvals[0] + 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: + rset.exdate(value) + 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/site-packages/dateutil/tz/__init__.py b/venv/Lib/site-packages/dateutil/tz/__init__.py new file mode 100644 index 00000000..af1352c4 --- /dev/null +++ b/venv/Lib/site-packages/dateutil/tz/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +from .tz import * +from .tz import __doc__ + +__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", + "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz", + "enfold", "datetime_ambiguous", "datetime_exists", + "resolve_imaginary", "UTC", "DeprecatedTzFormatWarning"] + + +class DeprecatedTzFormatWarning(Warning): + """Warning raised when time zones are parsed from deprecated formats.""" diff --git a/venv/Lib/site-packages/dateutil/tz/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/dateutil/tz/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39efd78846836fc606a568cf8c3d63873ce9e761 GIT binary patch literal 679 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dateutil/tz/__pycache__/tz.cpython-36.pyc b/venv/Lib/site-packages/dateutil/tz/__pycache__/tz.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..497d00eeedb532ca12c36b447c3185a5e9927396 GIT binary patch literal 45442 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êv9izx8d(K_FJ5z7;bdJ#K@+sN#dy#D6Q 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dateutil/tz/_common.py b/venv/Lib/site-packages/dateutil/tz/_common.py new file mode 100644 index 00000000..e6ac1183 --- /dev/null +++ b/venv/Lib/site-packages/dateutil/tz/_common.py @@ -0,0 +1,419 @@ +from six import PY2 + +from functools import wraps + +from datetime import datetime, timedelta, tzinfo + + +ZERO = timedelta(0) + +__all__ = ['tzname_in_python2', 'enfold'] + + +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 + """ + if PY2: + @wraps(namefunc) + def adjust_encoding(*args, **kwargs): + name = namefunc(*args, **kwargs) + if name is not None: + name = name.encode() + + return name + + return adjust_encoding + else: + return namefunc + + +# The following is adapted from Alexander Belopolsky's tz library +# https://github.com/abalkin/tz +if hasattr(datetime, 'fold'): + # This is the pre-python 3.6 fold situation + def enfold(dt, fold=1): + """ + Provides a unified interface for assigning the ``fold`` attribute to + datetimes both before and after the implementation of PEP-495. + + :param fold: + The value for the ``fold`` attribute in the returned datetime. This + should be either 0 or 1. + + :return: + Returns an object for which ``getattr(dt, 'fold', 0)`` returns + ``fold`` for all versions of Python. In versions prior to + Python 3.6, this is a ``_DatetimeWithFold`` object, which is a + subclass of :py:class:`datetime.datetime` with the ``fold`` + attribute added, if ``fold`` is 1. + + .. versionadded:: 2.6.0 + """ + return dt.replace(fold=fold) + +else: + class _DatetimeWithFold(datetime): + """ + This is a class designed to provide a PEP 495-compliant interface for + Python versions before 3.6. It is used only for dates in a fold, so + the ``fold`` attribute is fixed at ``1``. + + .. versionadded:: 2.6.0 + """ + __slots__ = () + + def replace(self, *args, **kwargs): + """ + Return a datetime with the same attributes, except for those + attributes given new values by whichever keyword arguments are + specified. Note that tzinfo=None can be specified to create a naive + datetime from an aware datetime with no conversion of date and time + data. + + This is reimplemented in ``_DatetimeWithFold`` because pypy3 will + return a ``datetime.datetime`` even if ``fold`` is unchanged. + """ + argnames = ( + 'year', 'month', 'day', 'hour', 'minute', 'second', + 'microsecond', 'tzinfo' + ) + + for arg, argname in zip(args, argnames): + if argname in kwargs: + raise TypeError('Duplicate argument: {}'.format(argname)) + + kwargs[argname] = arg + + for argname in argnames: + if argname not in kwargs: + kwargs[argname] = getattr(self, argname) + + dt_class = self.__class__ if kwargs.get('fold', 1) else datetime + + return dt_class(**kwargs) + + @property + def fold(self): + return 1 + + def enfold(dt, fold=1): + """ + Provides a unified interface for assigning the ``fold`` attribute to + datetimes both before and after the implementation of PEP-495. + + :param fold: + The value for the ``fold`` attribute in the returned datetime. This + should be either 0 or 1. + + :return: + Returns an object for which ``getattr(dt, 'fold', 0)`` returns + ``fold`` for all versions of Python. In versions prior to + Python 3.6, this is a ``_DatetimeWithFold`` object, which is a + subclass of :py:class:`datetime.datetime` with the ``fold`` + attribute added, if ``fold`` is 1. + + .. versionadded:: 2.6.0 + """ + if getattr(dt, 'fold', 0) == fold: + return dt + + args = dt.timetuple()[:6] + args += (dt.microsecond, dt.tzinfo) + + if fold: + return _DatetimeWithFold(*args) + else: + return datetime(*args) + + +def _validate_fromutc_inputs(f): + """ + The CPython version of ``fromutc`` checks that the input is a ``datetime`` + object and that ``self`` is attached as its ``tzinfo``. + """ + @wraps(f) + def fromutc(self, dt): + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + return f(self, dt) + + return fromutc + + +class _tzinfo(tzinfo): + """ + Base class for all ``dateutil`` ``tzinfo`` objects. + """ + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + + dt = dt.replace(tzinfo=self) + + wall_0 = enfold(dt, fold=0) + wall_1 = enfold(dt, fold=1) + + same_offset = wall_0.utcoffset() == wall_1.utcoffset() + same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None) + + return same_dt and not same_offset + + def _fold_status(self, dt_utc, dt_wall): + """ + Determine the fold status of a "wall" datetime, given a representation + of the same datetime as a (naive) UTC datetime. This is calculated based + on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all + datetimes, and that this offset is the actual number of hours separating + ``dt_utc`` and ``dt_wall``. + + :param dt_utc: + Representation of the datetime as UTC + + :param dt_wall: + Representation of the datetime as "wall time". This parameter must + either have a `fold` attribute or have a fold-naive + :class:`datetime.tzinfo` attached, otherwise the calculation may + fail. + """ + if self.is_ambiguous(dt_wall): + delta_wall = dt_wall - dt_utc + _fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst())) + else: + _fold = 0 + + return _fold + + def _fold(self, dt): + return getattr(dt, 'fold', 0) + + def _fromutc(self, dt): + """ + Given a timezone-aware datetime in a given timezone, calculates a + timezone-aware datetime in a new timezone. + + Since this is the one time that we *know* we have an unambiguous + datetime object, we take this opportunity to determine whether the + datetime is ambiguous and in a "fold" state (e.g. if it's the first + occurrence, chronologically, of the ambiguous datetime). + + :param dt: + A timezone-aware :class:`datetime.datetime` object. + """ + + # Re-implement the algorithm from Python's datetime.py + dtoff = dt.utcoffset() + if dtoff is None: + raise ValueError("fromutc() requires a non-None utcoffset() " + "result") + + # The original datetime.py code assumes that `dst()` defaults to + # zero during ambiguous times. PEP 495 inverts this presumption, so + # for pre-PEP 495 versions of python, we need to tweak the algorithm. + dtdst = dt.dst() + if dtdst is None: + raise ValueError("fromutc() requires a non-None dst() result") + delta = dtoff - dtdst + + dt += delta + # Set fold=1 so we can default to being in the fold for + # ambiguous dates. + dtdst = enfold(dt, fold=1).dst() + if dtdst is None: + raise ValueError("fromutc(): dt.dst gave inconsistent " + "results; cannot convert") + return dt + dtdst + + @_validate_fromutc_inputs + def fromutc(self, dt): + """ + Given a timezone-aware datetime in a given timezone, calculates a + timezone-aware datetime in a new timezone. + + Since this is the one time that we *know* we have an unambiguous + datetime object, we take this opportunity to determine whether the + datetime is ambiguous and in a "fold" state (e.g. if it's the first + occurrence, chronologically, of the ambiguous datetime). + + :param dt: + A timezone-aware :class:`datetime.datetime` object. + """ + dt_wall = self._fromutc(dt) + + # Calculate the fold status given the two datetimes. + _fold = self._fold_status(dt, dt_wall) + + # Set the default fold value for ambiguous dates + return enfold(dt_wall, fold=_fold) + + +class tzrangebase(_tzinfo): + """ + This is an abstract base class for time zones represented by an annual + transition into and out of DST. Child classes should implement the following + methods: + + * ``__init__(self, *args, **kwargs)`` + * ``transitions(self, year)`` - this is expected to return a tuple of + datetimes representing the DST on and off transitions in standard + time. + + A fully initialized ``tzrangebase`` subclass should also provide the + following attributes: + * ``hasdst``: Boolean whether or not the zone uses DST. + * ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects + representing the respective UTC offsets. + * ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short + abbreviations in DST and STD, respectively. + * ``_hasdst``: Whether or not the zone has DST. + + .. versionadded:: 2.6.0 + """ + def __init__(self): + raise NotImplementedError('tzrangebase is an abstract base class') + + def utcoffset(self, dt): + isdst = self._isdst(dt) + + if isdst is None: + return None + elif isdst: + return self._dst_offset + else: + return self._std_offset + + def dst(self, dt): + isdst = self._isdst(dt) + + if isdst is None: + return None + elif isdst: + return self._dst_base_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 fromutc(self, dt): + """ Given a datetime in UTC, return local time """ + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + # Get transitions - if there are none, fixed offset + transitions = self.transitions(dt.year) + if transitions is None: + return dt + self.utcoffset(dt) + + # Get the transition times in UTC + dston, dstoff = transitions + + dston -= self._std_offset + dstoff -= self._std_offset + + utc_transitions = (dston, dstoff) + dt_utc = dt.replace(tzinfo=None) + + isdst = self._naive_isdst(dt_utc, utc_transitions) + + if isdst: + dt_wall = dt + self._dst_offset + else: + dt_wall = dt + self._std_offset + + _fold = int(not isdst and self.is_ambiguous(dt_wall)) + + return enfold(dt_wall, fold=_fold) + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + if not self.hasdst: + return False + + start, end = self.transitions(dt.year) + + dt = dt.replace(tzinfo=None) + return (end <= dt < end + self._dst_base_offset) + + def _isdst(self, dt): + if not self.hasdst: + return False + elif dt is None: + return None + + transitions = self.transitions(dt.year) + + if transitions is None: + return False + + dt = dt.replace(tzinfo=None) + + isdst = self._naive_isdst(dt, transitions) + + # Handle ambiguous dates + if not isdst and self.is_ambiguous(dt): + return not self._fold(dt) + else: + return isdst + + def _naive_isdst(self, dt, transitions): + dston, dstoff = transitions + + dt = dt.replace(tzinfo=None) + + if dston < dstoff: + isdst = dston <= dt < dstoff + else: + isdst = not dstoff <= dt < dston + + return isdst + + @property + def _dst_base_offset(self): + return self._dst_offset - self._std_offset + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(...)" % self.__class__.__name__ + + __reduce__ = object.__reduce__ diff --git a/venv/Lib/site-packages/dateutil/tz/_factories.py b/venv/Lib/site-packages/dateutil/tz/_factories.py new file mode 100644 index 00000000..f8a65891 --- /dev/null +++ b/venv/Lib/site-packages/dateutil/tz/_factories.py @@ -0,0 +1,80 @@ +from datetime import timedelta +import weakref +from collections import OrderedDict + +from six.moves import _thread + + +class _TzSingleton(type): + def __init__(cls, *args, **kwargs): + cls.__instance = None + super(_TzSingleton, cls).__init__(*args, **kwargs) + + def __call__(cls): + if cls.__instance is None: + cls.__instance = super(_TzSingleton, cls).__call__() + return cls.__instance + + +class _TzFactory(type): + def instance(cls, *args, **kwargs): + """Alternate constructor that returns a fresh instance""" + return type.__call__(cls, *args, **kwargs) + + +class _TzOffsetFactory(_TzFactory): + def __init__(cls, *args, **kwargs): + cls.__instances = weakref.WeakValueDictionary() + cls.__strong_cache = OrderedDict() + cls.__strong_cache_size = 8 + + cls._cache_lock = _thread.allocate_lock() + + def __call__(cls, name, offset): + if isinstance(offset, timedelta): + key = (name, offset.total_seconds()) + else: + key = (name, offset) + + instance = cls.__instances.get(key, None) + if instance is None: + instance = cls.__instances.setdefault(key, + cls.instance(name, offset)) + + # This lock may not be necessary in Python 3. See GH issue #901 + with cls._cache_lock: + cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) + + # Remove an item if the strong cache is overpopulated + if len(cls.__strong_cache) > cls.__strong_cache_size: + cls.__strong_cache.popitem(last=False) + + return instance + + +class _TzStrFactory(_TzFactory): + def __init__(cls, *args, **kwargs): + cls.__instances = weakref.WeakValueDictionary() + cls.__strong_cache = OrderedDict() + cls.__strong_cache_size = 8 + + cls.__cache_lock = _thread.allocate_lock() + + def __call__(cls, s, posix_offset=False): + key = (s, posix_offset) + instance = cls.__instances.get(key, None) + + if instance is None: + instance = cls.__instances.setdefault(key, + cls.instance(s, posix_offset)) + + # This lock may not be necessary in Python 3. See GH issue #901 + with cls.__cache_lock: + cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) + + # Remove an item if the strong cache is overpopulated + if len(cls.__strong_cache) > cls.__strong_cache_size: + cls.__strong_cache.popitem(last=False) + + return instance + diff --git a/venv/Lib/site-packages/dateutil/tz/tz.py b/venv/Lib/site-packages/dateutil/tz/tz.py new file mode 100644 index 00000000..af81e88e --- /dev/null +++ b/venv/Lib/site-packages/dateutil/tz/tz.py @@ -0,0 +1,1849 @@ +# -*- coding: utf-8 -*- +""" +This module offers timezone implementations subclassing the abstract +:py:class:`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 +import bisect +import weakref +from collections import OrderedDict + +import six +from six import string_types +from six.moves import _thread +from ._common import tzname_in_python2, _tzinfo +from ._common import tzrangebase, enfold +from ._common import _validate_fromutc_inputs + +from ._factories import _TzSingleton, _TzOffsetFactory +from ._factories import _TzStrFactory +try: + from .win import tzwin, tzwinlocal +except ImportError: + tzwin = tzwinlocal = None + +# For warning about rounding tzinfo +from warnings import warn + +ZERO = datetime.timedelta(0) +EPOCH = datetime.datetime.utcfromtimestamp(0) +EPOCHORDINAL = EPOCH.toordinal() + + +@six.add_metaclass(_TzSingleton) +class tzutc(datetime.tzinfo): + """ + This is a tzinfo object that represents the UTC time zone. + + **Examples:** + + .. doctest:: + + >>> from datetime import * + >>> from dateutil.tz import * + + >>> datetime.now() + datetime.datetime(2003, 9, 27, 9, 40, 1, 521290) + + >>> datetime.now(tzutc()) + datetime.datetime(2003, 9, 27, 12, 40, 12, 156379, tzinfo=tzutc()) + + >>> datetime.now(tzutc()).tzname() + 'UTC' + + .. versionchanged:: 2.7.0 + ``tzutc()`` is now a singleton, so the result of ``tzutc()`` will + always return the same object. + + .. doctest:: + + >>> from dateutil.tz import tzutc, UTC + >>> tzutc() is tzutc() + True + >>> tzutc() is UTC + True + """ + def utcoffset(self, dt): + return ZERO + + def dst(self, dt): + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return "UTC" + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + return False + + @_validate_fromutc_inputs + def fromutc(self, dt): + """ + Fast track version of fromutc() returns the original ``dt`` object for + any valid :py:class:`datetime.datetime` object. + """ + return dt + + def __eq__(self, other): + if not isinstance(other, (tzutc, tzoffset)): + return NotImplemented + + return (isinstance(other, tzutc) or + (isinstance(other, tzoffset) and other._offset == ZERO)) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s()" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + + +#: Convenience constant providing a :class:`tzutc()` instance +#: +#: .. versionadded:: 2.7.0 +UTC = tzutc() + + +@six.add_metaclass(_TzOffsetFactory) +class tzoffset(datetime.tzinfo): + """ + A simple class for representing a fixed offset from UTC. + + :param name: + The timezone name, to be returned when ``tzname()`` is called. + :param offset: + The time zone offset in seconds, or (since version 2.6.0, represented + as a :py:class:`datetime.timedelta` object). + """ + def __init__(self, name, offset): + self._name = name + + try: + # Allow a timedelta + offset = offset.total_seconds() + except (TypeError, AttributeError): + pass + + self._offset = datetime.timedelta(seconds=_get_supported_offset(offset)) + + def utcoffset(self, dt): + return self._offset + + def dst(self, dt): + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._name + + @_validate_fromutc_inputs + def fromutc(self, dt): + return dt + self._offset + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + return False + + def __eq__(self, other): + if not isinstance(other, tzoffset): + return NotImplemented + + return self._offset == other._offset + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(%s, %s)" % (self.__class__.__name__, + repr(self._name), + int(self._offset.total_seconds())) + + __reduce__ = object.__reduce__ + + +class tzlocal(_tzinfo): + """ + A :class:`tzinfo` subclass built around the ``time`` timezone functions. + """ + def __init__(self): + super(tzlocal, self).__init__() + + 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 + + self._dst_saved = self._dst_offset - self._std_offset + self._hasdst = bool(self._dst_saved) + self._tznames = tuple(time.tzname) + + def utcoffset(self, dt): + if dt is None and self._hasdst: + return None + + if self._isdst(dt): + return self._dst_offset + else: + return self._std_offset + + def dst(self, dt): + if dt is None and self._hasdst: + return None + + if self._isdst(dt): + return self._dst_offset - self._std_offset + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._tznames[self._isdst(dt)] + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + naive_dst = self._naive_is_dst(dt) + return (not naive_dst and + (naive_dst != self._naive_is_dst(dt - self._dst_saved))) + + def _naive_is_dst(self, dt): + timestamp = _datetime_to_timestamp(dt) + return time.localtime(timestamp + time.timezone).tm_isdst + + def _isdst(self, dt, fold_naive=True): + # 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: + # + if not self._hasdst: + return False + + # Check for ambiguous times: + dstval = self._naive_is_dst(dt) + fold = getattr(dt, 'fold', None) + + if self.is_ambiguous(dt): + if fold is not None: + return not self._fold(dt) + else: + return True + + return dstval + + def __eq__(self, other): + if isinstance(other, tzlocal): + return (self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset) + elif isinstance(other, tzutc): + return (not self._hasdst and + self._tznames[0] in {'UTC', 'GMT'} and + self._std_offset == ZERO) + elif isinstance(other, tzoffset): + return (not self._hasdst and + self._tznames[0] == other._name and + self._std_offset == other._offset) + else: + return NotImplemented + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s()" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + + +class _ttinfo(object): + __slots__ = ["offset", "delta", "isdst", "abbr", + "isstd", "isgmt", "dstoffset"] + + 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 NotImplemented + + 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 and + self.dstoffset == other.dstoffset) + + __hash__ = None + + def __ne__(self, other): + return not (self == 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(object): + """ + Lightweight class for holding the relevant transition and time zone + information read from binary tzfiles. + """ + attrs = ['trans_list', 'trans_list_utc', 'trans_idx', 'ttinfo_list', + 'ttinfo_std', 'ttinfo_dst', 'ttinfo_before', 'ttinfo_first'] + + def __init__(self, **kwargs): + for attr in self.attrs: + setattr(self, attr, kwargs.get(attr, None)) + + +class tzfile(_tzinfo): + """ + This is a ``tzinfo`` subclass that allows one to use the ``tzfile(5)`` + format timezone files to extract current and historical zone information. + + :param fileobj: + This can be an opened file stream or a file name that the time zone + information can be read from. + + :param filename: + This is an optional parameter specifying the source of the time zone + information in the event that ``fileobj`` is a file object. If omitted + and ``fileobj`` is a file stream, this parameter will be set either to + ``fileobj``'s ``name`` attribute or to ``repr(fileobj)``. + + See `Sources for Time Zone and Daylight Saving Time Data + `_ for more information. + Time zone files can be compiled from the `IANA Time Zone database files + `_ with the `zic time zone compiler + `_ + + .. note:: + + Only construct a ``tzfile`` directly if you have a specific timezone + file on disk that you want to read into a Python ``tzinfo`` object. + If you want to get a ``tzfile`` representing a specific IANA zone, + (e.g. ``'America/New_York'``), you should call + :func:`dateutil.tz.gettz` with the zone identifier. + + + **Examples:** + + Using the US Eastern time zone as an example, we can see that a ``tzfile`` + provides time zone information for the standard Daylight Saving offsets: + + .. testsetup:: tzfile + + from dateutil.tz import gettz + from datetime import datetime + + .. doctest:: tzfile + + >>> NYC = gettz('America/New_York') + >>> NYC + tzfile('/usr/share/zoneinfo/America/New_York') + + >>> print(datetime(2016, 1, 3, tzinfo=NYC)) # EST + 2016-01-03 00:00:00-05:00 + + >>> print(datetime(2016, 7, 7, tzinfo=NYC)) # EDT + 2016-07-07 00:00:00-04:00 + + + The ``tzfile`` structure contains a fully history of the time zone, + so historical dates will also have the right offsets. For example, before + the adoption of the UTC standards, New York used local solar mean time: + + .. doctest:: tzfile + + >>> print(datetime(1901, 4, 12, tzinfo=NYC)) # LMT + 1901-04-12 00:00:00-04:56 + + And during World War II, New York was on "Eastern War Time", which was a + state of permanent daylight saving time: + + .. doctest:: tzfile + + >>> print(datetime(1944, 2, 7, tzinfo=NYC)) # EWT + 1944-02-07 00:00:00-04:00 + + """ + + def __init__(self, fileobj, filename=None): + super(tzfile, self).__init__() + + 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) + + if fileobj is not None: + if not file_opened_here: + fileobj = _nullcontext(fileobj) + + with fileobj as file_stream: + tzobj = self._read_tzfile(file_stream) + + self._set_tzdata(tzobj) + + def _set_tzdata(self, tzobj): + """ Set the time zone data of this object from a _tzfile object """ + # Copy the relevant attributes over as private attributes + for attr in _tzfile.attrs: + setattr(self, '_' + attr, getattr(tzobj, attr)) + + def _read_tzfile(self, fileobj): + out = _tzfile() + + # 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). + 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: + out.trans_list_utc = list(struct.unpack(">%dl" % timecnt, + fileobj.read(timecnt*4))) + else: + out.trans_list_utc = [] + + # 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: + out.trans_idx = struct.unpack(">%dB" % timecnt, + fileobj.read(timecnt)) + else: + out.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 seek for correct file position) + if leapcnt: + fileobj.seek(leapcnt * 8, os.SEEK_CUR) + + # 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)) + + # Build ttinfo list + out.ttinfo_list = [] + for i in range(typecnt): + gmtoff, isdst, abbrind = ttinfo[i] + gmtoff = _get_supported_offset(gmtoff) + tti = _ttinfo() + tti.offset = gmtoff + tti.dstoffset = datetime.timedelta(0) + 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) + out.ttinfo_list.append(tti) + + # Replace ttinfo indexes for ttinfo objects. + out.trans_idx = [out.ttinfo_list[idx] for idx in out.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. + out.ttinfo_std = None + out.ttinfo_dst = None + out.ttinfo_before = None + if out.ttinfo_list: + if not out.trans_list_utc: + out.ttinfo_std = out.ttinfo_first = out.ttinfo_list[0] + else: + for i in range(timecnt-1, -1, -1): + tti = out.trans_idx[i] + if not out.ttinfo_std and not tti.isdst: + out.ttinfo_std = tti + elif not out.ttinfo_dst and tti.isdst: + out.ttinfo_dst = tti + + if out.ttinfo_std and out.ttinfo_dst: + break + else: + if out.ttinfo_dst and not out.ttinfo_std: + out.ttinfo_std = out.ttinfo_dst + + for tti in out.ttinfo_list: + if not tti.isdst: + out.ttinfo_before = tti + break + else: + out.ttinfo_before = out.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. + lastdst = None + lastoffset = None + lastdstoffset = None + lastbaseoffset = None + out.trans_list = [] + + for i, tti in enumerate(out.trans_idx): + offset = tti.offset + dstoffset = 0 + + if lastdst is not None: + if tti.isdst: + if not lastdst: + dstoffset = offset - lastoffset + + if not dstoffset and lastdstoffset: + dstoffset = lastdstoffset + + tti.dstoffset = datetime.timedelta(seconds=dstoffset) + lastdstoffset = dstoffset + + # If a time zone changes its base offset during a DST transition, + # then you need to adjust by the previous base offset to get the + # transition time in local time. Otherwise you use the current + # base offset. Ideally, I would have some mathematical proof of + # why this is true, but I haven't really thought about it enough. + baseoffset = offset - dstoffset + adjustment = baseoffset + if (lastbaseoffset is not None and baseoffset != lastbaseoffset + and tti.isdst != lastdst): + # The base DST has changed + adjustment = lastbaseoffset + + lastdst = tti.isdst + lastoffset = offset + lastbaseoffset = baseoffset + + out.trans_list.append(out.trans_list_utc[i] + adjustment) + + out.trans_idx = tuple(out.trans_idx) + out.trans_list = tuple(out.trans_list) + out.trans_list_utc = tuple(out.trans_list_utc) + + return out + + def _find_last_transition(self, dt, in_utc=False): + # If there's no list, there are no transitions to find + if not self._trans_list: + return None + + timestamp = _datetime_to_timestamp(dt) + + # Find where the timestamp fits in the transition list - if the + # timestamp is a transition time, it's part of the "after" period. + trans_list = self._trans_list_utc if in_utc else self._trans_list + idx = bisect.bisect_right(trans_list, timestamp) + + # We want to know when the previous transition was, so subtract off 1 + return idx - 1 + + def _get_ttinfo(self, idx): + # For no list or after the last transition, default to _ttinfo_std + if idx is None or (idx + 1) >= len(self._trans_list): + return self._ttinfo_std + + # If there is a list and the time is before it, return _ttinfo_before + if idx < 0: + return self._ttinfo_before + + return self._trans_idx[idx] + + def _find_ttinfo(self, dt): + idx = self._resolve_ambiguous_time(dt) + + return self._get_ttinfo(idx) + + def fromutc(self, dt): + """ + The ``tzfile`` implementation of :py:func:`datetime.tzinfo.fromutc`. + + :param dt: + A :py:class:`datetime.datetime` object. + + :raises TypeError: + Raised if ``dt`` is not a :py:class:`datetime.datetime` object. + + :raises ValueError: + Raised if this is called with a ``dt`` which does not have this + ``tzinfo`` attached. + + :return: + Returns a :py:class:`datetime.datetime` object representing the + wall time in ``self``'s time zone. + """ + # These isinstance checks are in datetime.tzinfo, so we'll preserve + # them, even if we don't care about duck typing. + if not isinstance(dt, datetime.datetime): + raise TypeError("fromutc() requires a datetime argument") + + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + # First treat UTC as wall time and get the transition we're in. + idx = self._find_last_transition(dt, in_utc=True) + tti = self._get_ttinfo(idx) + + dt_out = dt + datetime.timedelta(seconds=tti.offset) + + fold = self.is_ambiguous(dt_out, idx=idx) + + return enfold(dt_out, fold=int(fold)) + + def is_ambiguous(self, dt, idx=None): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + if idx is None: + idx = self._find_last_transition(dt) + + # Calculate the difference in offsets from current to previous + timestamp = _datetime_to_timestamp(dt) + tti = self._get_ttinfo(idx) + + if idx is None or idx <= 0: + return False + + od = self._get_ttinfo(idx - 1).offset - tti.offset + tt = self._trans_list[idx] # Transition time + + return timestamp < tt + od + + def _resolve_ambiguous_time(self, dt): + idx = self._find_last_transition(dt) + + # If we have no transitions, return the index + _fold = self._fold(dt) + if idx is None or idx == 0: + return idx + + # If it's ambiguous and we're in a fold, shift to a different index. + idx_offset = int(not _fold and self.is_ambiguous(dt, idx)) + + return idx - idx_offset + + 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 dt is None: + return None + + 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.dstoffset + + @tzname_in_python2 + def tzname(self, dt): + if not self._ttinfo_std or dt is None: + return None + return self._find_ttinfo(dt).abbr + + def __eq__(self, other): + if not isinstance(other, tzfile): + return NotImplemented + return (self._trans_list == other._trans_list and + self._trans_idx == other._trans_idx and + self._ttinfo_list == other._ttinfo_list) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._filename)) + + def __reduce__(self): + return self.__reduce_ex__(None) + + def __reduce_ex__(self, protocol): + return (self.__class__, (None, self._filename), self.__dict__) + + +class tzrange(tzrangebase): + """ + The ``tzrange`` object is a time zone specified by a set of offsets and + abbreviations, equivalent to the way the ``TZ`` variable can be specified + in POSIX-like systems, but using Python delta objects to specify DST + start, end and offsets. + + :param stdabbr: + The abbreviation for standard time (e.g. ``'EST'``). + + :param stdoffset: + An integer or :class:`datetime.timedelta` object or equivalent + specifying the base offset from UTC. + + If unspecified, +00:00 is used. + + :param dstabbr: + The abbreviation for DST / "Summer" time (e.g. ``'EDT'``). + + If specified, with no other DST information, DST is assumed to occur + and the default behavior or ``dstoffset``, ``start`` and ``end`` is + used. If unspecified and no other DST information is specified, it + is assumed that this zone has no DST. + + If this is unspecified and other DST information is *is* specified, + DST occurs in the zone but the time zone abbreviation is left + unchanged. + + :param dstoffset: + A an integer or :class:`datetime.timedelta` object or equivalent + specifying the UTC offset during DST. If unspecified and any other DST + information is specified, it is assumed to be the STD offset +1 hour. + + :param start: + A :class:`relativedelta.relativedelta` object or equivalent specifying + the time and time of year that daylight savings time starts. To + specify, for example, that DST starts at 2AM on the 2nd Sunday in + March, pass: + + ``relativedelta(hours=2, month=3, day=1, weekday=SU(+2))`` + + If unspecified and any other DST information is specified, the default + value is 2 AM on the first Sunday in April. + + :param end: + A :class:`relativedelta.relativedelta` object or equivalent + representing the time and time of year that daylight savings time + ends, with the same specification method as in ``start``. One note is + that this should point to the first time in the *standard* zone, so if + a transition occurs at 2AM in the DST zone and the clocks are set back + 1 hour to 1AM, set the ``hours`` parameter to +1. + + + **Examples:** + + .. testsetup:: tzrange + + from dateutil.tz import tzrange, tzstr + + .. doctest:: tzrange + + >>> tzstr('EST5EDT') == tzrange("EST", -18000, "EDT") + True + + >>> from dateutil.relativedelta import * + >>> range1 = tzrange("EST", -18000, "EDT") + >>> range2 = tzrange("EST", -18000, "EDT", -14400, + ... relativedelta(hours=+2, month=4, day=1, + ... weekday=SU(+1)), + ... relativedelta(hours=+1, month=10, day=31, + ... weekday=SU(-1))) + >>> tzstr('EST5EDT') == range1 == range2 + True + + """ + def __init__(self, stdabbr, stdoffset=None, + dstabbr=None, dstoffset=None, + start=None, end=None): + + global relativedelta + from dateutil import relativedelta + + self._std_abbr = stdabbr + self._dst_abbr = dstabbr + + try: + stdoffset = stdoffset.total_seconds() + except (TypeError, AttributeError): + pass + + try: + dstoffset = dstoffset.total_seconds() + except (TypeError, AttributeError): + pass + + 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 + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = bool(self._start_delta) + + def transitions(self, year): + """ + For a given year, get the DST on and off transition times, expressed + always on the standard time side. For zones with no transitions, this + function returns ``None``. + + :param year: + The year whose transitions you would like to query. + + :return: + Returns a :class:`tuple` of :class:`datetime.datetime` objects, + ``(dston, dstoff)`` for zones with an annual DST transition, or + ``None`` for fixed offset zones. + """ + if not self.hasdst: + return None + + base_year = datetime.datetime(year, 1, 1) + + start = base_year + self._start_delta + end = base_year + self._end_delta + + return (start, end) + + def __eq__(self, other): + if not isinstance(other, tzrange): + return NotImplemented + + 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) + + @property + def _dst_base_offset(self): + return self._dst_base_offset_ + + +@six.add_metaclass(_TzStrFactory) +class tzstr(tzrange): + """ + ``tzstr`` objects are time zone objects specified by a time-zone string as + it would be passed to a ``TZ`` variable on POSIX-style systems (see + the `GNU C Library: TZ Variable`_ for more details). + + There is one notable exception, which is that POSIX-style time zones use an + inverted offset format, so normally ``GMT+3`` would be parsed as an offset + 3 hours *behind* GMT. The ``tzstr`` time zone object will parse this as an + offset 3 hours *ahead* of GMT. If you would like to maintain the POSIX + behavior, pass a ``True`` value to ``posix_offset``. + + The :class:`tzrange` object provides the same functionality, but is + specified using :class:`relativedelta.relativedelta` objects. rather than + strings. + + :param s: + A time zone string in ``TZ`` variable format. This can be a + :class:`bytes` (2.x: :class:`str`), :class:`str` (2.x: + :class:`unicode`) or a stream emitting unicode characters + (e.g. :class:`StringIO`). + + :param posix_offset: + Optional. If set to ``True``, interpret strings such as ``GMT+3`` or + ``UTC+3`` as being 3 hours *behind* UTC rather than ahead, per the + POSIX standard. + + .. caution:: + + Prior to version 2.7.0, this function also supported time zones + in the format: + + * ``EST5EDT,4,0,6,7200,10,0,26,7200,3600`` + * ``EST5EDT,4,1,0,7200,10,-1,0,7200,3600`` + + This format is non-standard and has been deprecated; this function + will raise a :class:`DeprecatedTZFormatWarning` until + support is removed in a future version. + + .. _`GNU C Library: TZ Variable`: + https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + """ + def __init__(self, s, posix_offset=False): + global parser + from dateutil.parser import _parser as parser + + self._s = s + + res = parser._parsetz(s) + if res is None or res.any_unused_tokens: + 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") and not posix_offset: + 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) + + self.hasdst = bool(self._start_delta) + + def _delta(self, x, isend=0): + from dateutil import relativedelta + 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(_tzinfo): + def __init__(self, tzid, comps=[]): + super(_tzicalvtz, self).__init__() + + self._tzid = tzid + self._comps = comps + self._cachedate = [] + self._cachecomp = [] + self._cache_lock = _thread.allocate_lock() + + def _find_comp(self, dt): + if len(self._comps) == 1: + return self._comps[0] + + dt = dt.replace(tzinfo=None) + + try: + with self._cache_lock: + return self._cachecomp[self._cachedate.index( + (dt, self._fold(dt)))] + except ValueError: + pass + + lastcompdt = None + lastcomp = None + + for comp in self._comps: + compdt = self._find_compdt(comp, dt) + + 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] + + with self._cache_lock: + self._cachedate.insert(0, (dt, self._fold(dt))) + self._cachecomp.insert(0, lastcomp) + + if len(self._cachedate) > 10: + self._cachedate.pop() + self._cachecomp.pop() + + return lastcomp + + def _find_compdt(self, comp, dt): + if comp.tzoffsetdiff < ZERO and self._fold(dt): + dt -= comp.tzoffsetdiff + + compdt = comp.rrule.before(dt, inc=True) + + return compdt + + 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): + """ + This object is designed to parse an iCalendar-style ``VTIMEZONE`` structure + as set out in `RFC 5545`_ Section 4.6.5 into one or more `tzinfo` objects. + + :param `fileobj`: + A file or stream in iCalendar format, which should be UTF-8 encoded + with CRLF endings. + + .. _`RFC 5545`: https://tools.ietf.org/html/rfc5545 + """ + def __init__(self, fileobj): + global 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') + else: + self._s = getattr(fileobj, 'name', repr(fileobj)) + fileobj = _nullcontext(fileobj) + + self._vtz = {} + + with fileobj as fobj: + self._parse_rfc(fobj.read()) + + def keys(self): + """ + Retrieves the available time zones as a list. + """ + return list(self._vtz.keys()) + + def get(self, tzid=None): + """ + Retrieve a :py:class:`datetime.tzinfo` object by its ``tzid``. + + :param tzid: + If there is exactly one time zone available, omitting ``tzid`` + or passing :py:const:`None` value returns it. Otherwise a valid + key (which can be retrieved from :func:`keys`) is required. + + :raises ValueError: + Raised if ``tzid`` is not specified but there are either more + or fewer than 1 zone defined. + + :returns: + Returns either a :py:class:`datetime.tzinfo` object representing + the relevant time zone or :py:const:`None` if the ``tzid`` was + not found. + """ + if tzid is None: + if len(self._vtz) == 0: + raise ValueError("no timezones defined") + elif len(self._vtz) > 1: + raise ValueError("more than one timezone available") + tzid = next(iter(self._vtz)) + + 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": + # DTSTART in VTIMEZONE takes a subset of valid RRULE + # values under RFC 5545. + for parm in parms: + if parm != 'VALUE=DATE-TIME': + msg = ('Unsupported DTSTART param in ' + + 'VTIMEZONE: ' + parm) + raise ValueError(msg) + 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", + "/usr/share/lib/zoneinfo", + "/etc/zoneinfo"] +else: + TZFILES = [] + TZPATHS = [] + + +def __get_gettz(): + tzlocal_classes = (tzlocal,) + if tzwinlocal is not None: + tzlocal_classes += (tzwinlocal,) + + class GettzFunc(object): + """ + Retrieve a time zone object from a string representation + + This function is intended to retrieve the :py:class:`tzinfo` subclass + that best represents the time zone that would be used if a POSIX + `TZ variable`_ were set to the same value. + + If no argument or an empty string is passed to ``gettz``, local time + is returned: + + .. code-block:: python3 + + >>> gettz() + tzfile('/etc/localtime') + + This function is also the preferred way to map IANA tz database keys + to :class:`tzfile` objects: + + .. code-block:: python3 + + >>> gettz('Pacific/Kiritimati') + tzfile('/usr/share/zoneinfo/Pacific/Kiritimati') + + On Windows, the standard is extended to include the Windows-specific + zone names provided by the operating system: + + .. code-block:: python3 + + >>> gettz('Egypt Standard Time') + tzwin('Egypt Standard Time') + + Passing a GNU ``TZ`` style string time zone specification returns a + :class:`tzstr` object: + + .. code-block:: python3 + + >>> gettz('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') + tzstr('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') + + :param name: + A time zone name (IANA, or, on Windows, Windows keys), location of + a ``tzfile(5)`` zoneinfo file or ``TZ`` variable style time zone + specifier. An empty string, no argument or ``None`` is interpreted + as local time. + + :return: + Returns an instance of one of ``dateutil``'s :py:class:`tzinfo` + subclasses. + + .. versionchanged:: 2.7.0 + + After version 2.7.0, any two calls to ``gettz`` using the same + input strings will return the same object: + + .. code-block:: python3 + + >>> tz.gettz('America/Chicago') is tz.gettz('America/Chicago') + True + + In addition to improving performance, this ensures that + `"same zone" semantics`_ are used for datetimes in the same zone. + + + .. _`TZ variable`: + https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + + .. _`"same zone" semantics`: + https://blog.ganssle.io/articles/2018/02/aware-datetime-arithmetic.html + """ + def __init__(self): + + self.__instances = weakref.WeakValueDictionary() + self.__strong_cache_size = 8 + self.__strong_cache = OrderedDict() + self._cache_lock = _thread.allocate_lock() + + def __call__(self, name=None): + with self._cache_lock: + rv = self.__instances.get(name, None) + + if rv is None: + rv = self.nocache(name=name) + if not (name is None + or isinstance(rv, tzlocal_classes) + or rv is None): + # tzlocal is slightly more complicated than the other + # time zone providers because it depends on environment + # at construction time, so don't cache that. + # + # We also cannot store weak references to None, so we + # will also not store that. + self.__instances[name] = rv + else: + # No need for strong caching, return immediately + return rv + + self.__strong_cache[name] = self.__strong_cache.pop(name, rv) + + if len(self.__strong_cache) > self.__strong_cache_size: + self.__strong_cache.popitem(last=False) + + return rv + + def set_cache_size(self, size): + with self._cache_lock: + self.__strong_cache_size = size + while len(self.__strong_cache) > size: + self.__strong_cache.popitem(last=False) + + def cache_clear(self): + with self._cache_lock: + self.__instances = weakref.WeakValueDictionary() + self.__strong_cache.clear() + + @staticmethod + def nocache(name=None): + """A non-cached version of gettz""" + 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: + try: + if name.startswith(":"): + name = name[1:] + except TypeError as e: + if isinstance(name, bytes): + new_msg = "gettz argument should be str, not bytes" + six.raise_from(TypeError(new_msg), e) + else: + raise + 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, UnicodeEncodeError): + # UnicodeEncodeError is for Python 2.7 compat + tz = None + + if not tz: + from dateutil.zoneinfo import get_zonefile_instance + tz = get_zonefile_instance().get(name) + + if not tz: + for c in name: + # name is not a tzstr unless it has at least + # one offset. For short values of "name", an + # explicit for loop seems to be the fastest way + # To determine if a string contains a digit + if c in "0123456789": + try: + tz = tzstr(name) + except ValueError: + pass + break + else: + if name in ("GMT", "UTC"): + tz = UTC + elif name in time.tzname: + tz = tzlocal() + return tz + + return GettzFunc() + + +gettz = __get_gettz() +del __get_gettz + + +def datetime_exists(dt, tz=None): + """ + Given a datetime and a time zone, determine whether or not a given datetime + would fall in a gap. + + :param dt: + A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` + is provided.) + + :param tz: + A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If + ``None`` or not provided, the datetime's own time zone will be used. + + :return: + Returns a boolean value whether or not the "wall time" exists in + ``tz``. + + .. versionadded:: 2.7.0 + """ + if tz is None: + if dt.tzinfo is None: + raise ValueError('Datetime is naive and no time zone provided.') + tz = dt.tzinfo + + dt = dt.replace(tzinfo=None) + + # This is essentially a test of whether or not the datetime can survive + # a round trip to UTC. + dt_rt = dt.replace(tzinfo=tz).astimezone(UTC).astimezone(tz) + dt_rt = dt_rt.replace(tzinfo=None) + + return dt == dt_rt + + +def datetime_ambiguous(dt, tz=None): + """ + Given a datetime and a time zone, determine whether or not a given datetime + is ambiguous (i.e if there are two times differentiated only by their DST + status). + + :param dt: + A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` + is provided.) + + :param tz: + A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If + ``None`` or not provided, the datetime's own time zone will be used. + + :return: + Returns a boolean value whether or not the "wall time" is ambiguous in + ``tz``. + + .. versionadded:: 2.6.0 + """ + if tz is None: + if dt.tzinfo is None: + raise ValueError('Datetime is naive and no time zone provided.') + + tz = dt.tzinfo + + # If a time zone defines its own "is_ambiguous" function, we'll use that. + is_ambiguous_fn = getattr(tz, 'is_ambiguous', None) + if is_ambiguous_fn is not None: + try: + return tz.is_ambiguous(dt) + except Exception: + pass + + # If it doesn't come out and tell us it's ambiguous, we'll just check if + # the fold attribute has any effect on this particular date and time. + dt = dt.replace(tzinfo=tz) + wall_0 = enfold(dt, fold=0) + wall_1 = enfold(dt, fold=1) + + same_offset = wall_0.utcoffset() == wall_1.utcoffset() + same_dst = wall_0.dst() == wall_1.dst() + + return not (same_offset and same_dst) + + +def resolve_imaginary(dt): + """ + Given a datetime that may be imaginary, return an existing datetime. + + This function assumes that an imaginary datetime represents what the + wall time would be in a zone had the offset transition not occurred, so + it will always fall forward by the transition's change in offset. + + .. doctest:: + + >>> from dateutil import tz + >>> from datetime import datetime + >>> NYC = tz.gettz('America/New_York') + >>> print(tz.resolve_imaginary(datetime(2017, 3, 12, 2, 30, tzinfo=NYC))) + 2017-03-12 03:30:00-04:00 + + >>> KIR = tz.gettz('Pacific/Kiritimati') + >>> print(tz.resolve_imaginary(datetime(1995, 1, 1, 12, 30, tzinfo=KIR))) + 1995-01-02 12:30:00+14:00 + + As a note, :func:`datetime.astimezone` is guaranteed to produce a valid, + existing datetime, so a round-trip to and from UTC is sufficient to get + an extant datetime, however, this generally "falls back" to an earlier time + rather than falling forward to the STD side (though no guarantees are made + about this behavior). + + :param dt: + A :class:`datetime.datetime` which may or may not exist. + + :return: + Returns an existing :class:`datetime.datetime`. If ``dt`` was not + imaginary, the datetime returned is guaranteed to be the same object + passed to the function. + + .. versionadded:: 2.7.0 + """ + if dt.tzinfo is not None and not datetime_exists(dt): + + curr_offset = (dt + datetime.timedelta(hours=24)).utcoffset() + old_offset = (dt - datetime.timedelta(hours=24)).utcoffset() + + dt += curr_offset - old_offset + + return dt + + +def _datetime_to_timestamp(dt): + """ + Convert a :class:`datetime.datetime` object to an epoch timestamp in + seconds since January 1, 1970, ignoring the time zone. + """ + return (dt.replace(tzinfo=None) - EPOCH).total_seconds() + + +if sys.version_info >= (3, 6): + def _get_supported_offset(second_offset): + return second_offset +else: + def _get_supported_offset(second_offset): + # For python pre-3.6, 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 or https://bugs.python.org/issue5288 + # for some information. + old_offset = second_offset + calculated_offset = 60 * ((second_offset + 30) // 60) + return calculated_offset + + +try: + # Python 3.7 feature + from contextlib import nullcontext as _nullcontext +except ImportError: + class _nullcontext(object): + """ + Class for wrapping contexts so that they are passed through in a + with statement. + """ + def __init__(self, context): + self.context = context + + def __enter__(self): + return self.context + + def __exit__(*args, **kwargs): + pass + +# vim:ts=4:sw=4:et diff --git a/venv/Lib/site-packages/dateutil/tz/win.py b/venv/Lib/site-packages/dateutil/tz/win.py new file mode 100644 index 00000000..cde07ba7 --- /dev/null +++ b/venv/Lib/site-packages/dateutil/tz/win.py @@ -0,0 +1,370 @@ +# -*- coding: utf-8 -*- +""" +This module provides an interface to the native time zone data on Windows, +including :py:class:`datetime.tzinfo` implementations. + +Attempting to import this module on a non-Windows platform will raise an +:py:obj:`ImportError`. +""" +# 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 tzrangebase + +__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 is 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(tzrangebase): + """tzinfo class based on win32's timezones available in the registry.""" + def __init__(self): + raise NotImplementedError('tzwinbase is an abstract base class') + + def __eq__(self, other): + # Compare on all relevant dimensions, including name. + if not isinstance(other, tzwinbase): + return NotImplemented + + return (self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset 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._std_abbr == other._std_abbr and + self._dst_abbr == other._dst_abbr) + + @staticmethod + def list(): + """Return a list of all time zones known to the system.""" + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + with winreg.OpenKey(handle, TZKEYNAME) as tzkey: + result = [winreg.EnumKey(tzkey, i) + for i in range(winreg.QueryInfoKey(tzkey)[0])] + return result + + def display(self): + """ + Return the display name of the time zone. + """ + return self._display + + def transitions(self, year): + """ + For a given year, get the DST on and off transition times, expressed + always on the standard time side. For zones with no transitions, this + function returns ``None``. + + :param year: + The year whose transitions you would like to query. + + :return: + Returns a :class:`tuple` of :class:`datetime.datetime` objects, + ``(dston, dstoff)`` for zones with an annual DST transition, or + ``None`` for fixed offset zones. + """ + + if not self.hasdst: + return None + + dston = picknthweekday(year, self._dstmonth, self._dstdayofweek, + self._dsthour, self._dstminute, + self._dstweeknumber) + + dstoff = picknthweekday(year, self._stdmonth, self._stddayofweek, + self._stdhour, self._stdminute, + self._stdweeknumber) + + # Ambiguous dates default to the STD side + dstoff -= self._dst_base_offset + + return dston, dstoff + + def _get_hasdst(self): + return self._dstmonth != 0 + + @property + def _dst_base_offset(self): + return self._dst_base_offset_ + + +class tzwin(tzwinbase): + """ + Time zone object created from the zone info in the Windows registry + + These are similar to :py:class:`dateutil.tz.tzrange` objects in that + the time zone data is provided in the format of a single offset rule + for either 0 or 2 time zone transitions per year. + + :param: name + The name of a Windows time zone key, e.g. "Eastern Standard Time". + The full list of keys can be retrieved with :func:`tzwin.list`. + """ + + def __init__(self, name): + self._name = name + + 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._std_abbr = keydict["Std"] + self._dst_abbr = keydict["Dlt"] + + self._display = keydict["Display"] + + # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm + tup = struct.unpack("=3l16h", keydict["TZI"]) + stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 + dstoffset = stdoffset-tup[2] # + DaylightBias * -1 + self._std_offset = datetime.timedelta(minutes=stdoffset) + self._dst_offset = datetime.timedelta(minutes=dstoffset) + + # 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] + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = self._get_hasdst() + + def __repr__(self): + return "tzwin(%s)" % repr(self._name) + + def __reduce__(self): + return (self.__class__, (self._name,)) + + +class tzwinlocal(tzwinbase): + """ + Class representing the local time zone information in the Windows registry + + While :class:`dateutil.tz.tzlocal` makes system calls (via the :mod:`time` + module) to retrieve time zone information, ``tzwinlocal`` retrieves the + rules directly from the Windows registry and creates an object like + :class:`dateutil.tz.tzwin`. + + Because Windows does not have an equivalent of :func:`time.tzset`, on + Windows, :class:`dateutil.tz.tzlocal` instances will always reflect the + time zone settings *at the time that the process was started*, meaning + changes to the machine's time zone settings during the run of a program + on Windows will **not** be reflected by :class:`dateutil.tz.tzlocal`. + Because ``tzwinlocal`` reads the registry directly, it is unaffected by + this issue. + """ + def __init__(self): + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: + keydict = valuestodict(tzlocalkey) + + self._std_abbr = keydict["StandardName"] + self._dst_abbr = keydict["DaylightName"] + + try: + tzkeyname = text_type('{kn}\\{sn}').format(kn=TZKEYNAME, + sn=self._std_abbr) + with winreg.OpenKey(handle, tzkeyname) as tzkey: + _keydict = valuestodict(tzkey) + self._display = _keydict["Display"] + except OSError: + self._display = None + + stdoffset = -keydict["Bias"]-keydict["StandardBias"] + dstoffset = stdoffset-keydict["DaylightBias"] + + self._std_offset = datetime.timedelta(minutes=stdoffset) + self._dst_offset = datetime.timedelta(minutes=dstoffset) + + # 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] + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = self._get_hasdst() + + def __repr__(self): + return "tzwinlocal()" + + def __str__(self): + # str will return the standard name, not the daylight name. + return "tzwinlocal(%s)" % repr(self._std_abbr) + + 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/site-packages/dateutil/tzwin.py b/venv/Lib/site-packages/dateutil/tzwin.py new file mode 100644 index 00000000..cebc673e --- /dev/null +++ b/venv/Lib/site-packages/dateutil/tzwin.py @@ -0,0 +1,2 @@ +# tzwin has moved to dateutil.tz.win +from .tz.win import * diff --git a/venv/Lib/site-packages/dateutil/utils.py b/venv/Lib/site-packages/dateutil/utils.py new file mode 100644 index 00000000..44d9c994 --- /dev/null +++ b/venv/Lib/site-packages/dateutil/utils.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +""" +This module offers general convenience and utility functions for dealing with +datetimes. + +.. versionadded:: 2.7.0 +""" +from __future__ import unicode_literals + +from datetime import datetime, time + + +def today(tzinfo=None): + """ + Returns a :py:class:`datetime` representing the current day at midnight + + :param tzinfo: + The time zone to attach (also used to determine the current day). + + :return: + A :py:class:`datetime.datetime` object representing the current day + at midnight. + """ + + dt = datetime.now(tzinfo) + return datetime.combine(dt.date(), time(0, tzinfo=tzinfo)) + + +def default_tzinfo(dt, tzinfo): + """ + Sets the ``tzinfo`` parameter on naive datetimes only + + This is useful for example when you are provided a datetime that may have + either an implicit or explicit time zone, such as when parsing a time zone + string. + + .. doctest:: + + >>> from dateutil.tz import tzoffset + >>> from dateutil.parser import parse + >>> from dateutil.utils import default_tzinfo + >>> dflt_tz = tzoffset("EST", -18000) + >>> print(default_tzinfo(parse('2014-01-01 12:30 UTC'), dflt_tz)) + 2014-01-01 12:30:00+00:00 + >>> print(default_tzinfo(parse('2014-01-01 12:30'), dflt_tz)) + 2014-01-01 12:30:00-05:00 + + :param dt: + The datetime on which to replace the time zone + + :param tzinfo: + The :py:class:`datetime.tzinfo` subclass instance to assign to + ``dt`` if (and only if) it is naive. + + :return: + Returns an aware :py:class:`datetime.datetime`. + """ + if dt.tzinfo is not None: + return dt + else: + return dt.replace(tzinfo=tzinfo) + + +def within_delta(dt1, dt2, delta): + """ + Useful for comparing two datetimes that may a negilible difference + to be considered equal. + """ + delta = abs(delta) + difference = dt1 - dt2 + return -delta <= difference <= delta diff --git a/venv/Lib/site-packages/dateutil/zoneinfo/__init__.py b/venv/Lib/site-packages/dateutil/zoneinfo/__init__.py new file mode 100644 index 00000000..34f11ad6 --- /dev/null +++ b/venv/Lib/site-packages/dateutil/zoneinfo/__init__.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +import warnings +import json + +from tarfile import TarFile +from pkgutil import get_data +from io import BytesIO + +from dateutil.tz import tzfile as _tzfile + +__all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata"] + +ZONEFILENAME = "dateutil-zoneinfo.tar.gz" +METADATA_FN = 'METADATA' + + +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 TarFile.open(fileobj=zonefile_stream) as tf: + self.zones = {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()} + 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 = {} + self.metadata = None + + def get(self, name, default=None): + """ + Wrapper for :func:`ZoneInfoFile.zones.get`. This is a convenience method + for retrieving zones from the zone dictionary. + + :param name: + The name of the zone to retrieve. (Generally IANA zone names) + + :param default: + The value to return in the event of a missing key. + + .. versionadded:: 2.6.0 + + """ + return self.zones.get(name, default) + + +# 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: Remove after deprecation period. +_CLASS_ZONE_INSTANCE = [] + + +def get_zonefile_instance(new_instance=False): + """ + This is a convenience function which provides a :class:`ZoneInfoFile` + instance using the data provided by the ``dateutil`` package. By default, it + caches a single instance of the ZoneInfoFile object and returns that. + + :param new_instance: + If ``True``, a new instance of :class:`ZoneInfoFile` is instantiated and + used as the cached instance for the next call. Otherwise, new instances + are created only as necessary. + + :return: + Returns a :class:`ZoneInfoFile` object. + + .. versionadded:: 2.6 + """ + if new_instance: + zif = None + else: + zif = getattr(get_zonefile_instance, '_cached_instance', None) + + if zif is None: + zif = ZoneInfoFile(getzoneinfofile_stream()) + + get_zonefile_instance._cached_instance = zif + + return zif + + +def gettz(name): + """ + This retrieves a time zone from the local zoneinfo tarball that is packaged + with dateutil. + + :param name: + An IANA-style time zone name, as found in the zoneinfo file. + + :return: + Returns a :class:`dateutil.tz.tzfile` time zone object. + + .. warning:: + It is generally inadvisable to use this function, and it is only + provided for API compatibility with earlier versions. This is *not* + equivalent to ``dateutil.tz.gettz()``, which selects an appropriate + time zone based on the inputs, favoring system zoneinfo. This is ONLY + for accessing the dateutil-specific zoneinfo (which may be out of + date compared to the system zoneinfo). + + .. deprecated:: 2.6 + If you need to use a specific zoneinfofile over the system zoneinfo, + instantiate a :class:`dateutil.zoneinfo.ZoneInfoFile` object and call + :func:`dateutil.zoneinfo.ZoneInfoFile.get(name)` instead. + + Use :func:`get_zonefile_instance` to retrieve an instance of the + dateutil-provided zoneinfo. + """ + warnings.warn("zoneinfo.gettz() will be removed in future versions, " + "to use the dateutil-provided zoneinfo files, instantiate a " + "ZoneInfoFile object and use ZoneInfoFile.zones.get() " + "instead. See the documentation for details.", + DeprecationWarning) + + 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 + + .. deprecated:: 2.6 + See deprecation warning in :func:`zoneinfo.gettz`. To get metadata, + query the attribute ``zoneinfo.ZoneInfoFile.metadata``. + """ + warnings.warn("zoneinfo.gettz_db_metadata() will be removed in future " + "versions, to use the dateutil-provided zoneinfo files, " + "ZoneInfoFile object and query the 'metadata' attribute " + "instead. See the documentation for details.", + DeprecationWarning) + + if len(_CLASS_ZONE_INSTANCE) == 0: + _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) + return _CLASS_ZONE_INSTANCE[0].metadata diff --git a/venv/Lib/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b331920ce3d87b201db494df62e5380c725b6cac GIT binary patch literal 5611 zcmb_gTXWmS6~>JuL{qYSjqOZZq?6QQDlx7*&16*9W5%{TY8^U_m2twV1tIKG0tEu} zVkwo3)E9XsX=gHh$V(pj+?oD?{*FF&@Y<*Tg*^2;3j&~QrI(pf+6Az{o;{cEe0zW& zFD};G*Z=+-{WVSdhc@@KaDERh`4c*(F+J3}*-`K6xHdv#Xm(AV#>~(fR=O2+ZiUrh zty@#)mCzn8bn7^;vKq5@&F(q2&~31Kckz+N&auV|jWu}v#OyAxMYi-p>n>sZJX^;2 zGCz;;E!|zWdPd1<6v?`~4Jdj?Y+tWDWbmaz7BE87xX$8Y_nqE)`m+iPiEtRU*gE$NBY?liSOc(~Pm(B5kMq(=TTaB>eVxr~l${Z?tGwH3TB+^2q@e54v|-G~@UfdVT=)6d z3$r_73GVZ^AfJrkG!;uro8lXoJ^LaCUt2@^Tap-8Z1p(`*ra^ z*N)-R=?z&KRGB`g!Iw7L0zF08e2R9g{Z9Kti;Pdyb<^lfFKxc_#Nk53V&$`2$IW%; zvmYHd>wgY7x(3kXO{i-f*;OZ3CNV9uriG!}d zT9p~hMADc@J&RRvW~p?sp+6wJ2?%chNgZE>5;Mv16k8IP{1x31K)b&n{w#q*oa$1oT_Wq=MHe8ye%(5TWdrR*6BdrZ(Y`4xX+mpUpU^${;7fy! zUm4FW%0z_EE>f_PR)d7DQj2h%p3BL=-RLOfrsw!?rSN=74>gU{Xx&PUX&*ui+0Q^g zsQ;#ZhXua$H+^Yc2^2MQX((9;J1_e#nPAU@fsi!`1PKh)BFa7YoH*;ix%C z36@qF_hSZ+50W@aD`D(0XqGc|YX5>yR6;f@N~i95(w*GzW;ul^up;R$=2Kh})NK@( zcr5dRks0+S))o{cBDT!`WPb8pbee7>FYEe}Ro5Fv0})$RdAej=GphRZ%9+vgDaGhV zM{4)c5@JM9lu9-vjG}#SABfej$?>^%Ij4p6UCxXsP`XJWV4~cfoC7GShnt znW(~dHdqK@6np1zFYx!Ak%$ih#tBmEe&{91Y4r7!q`Z`J#>)K!BjmG}PiD@}M1`u? z%j=C^&!K|E+XXn@pExr=uR4KL0)5ZlQ)`mEkR#Mtt(GHf-ro)gpZn7BB1VVE2 z$r*BU)bhik;wz!G*>`%qEivZ3-YP7N_~EIg&a{~FE7L~?AT34<7wIrgXn+*pjxvIk z0x9okJi_LHn_<{;p5R&>1_@t1wRTd-^91C80BM~#Li#{<#C@J5Wp1u#R+m(?@E-@ zybsY7^=ZqNUNss9&~8+X0_jxonP>{>B$=uahMoGv&^2jF)CjOK3aAk?lya-dZ9jz;d}N5g=fffZE3Z?~Kn#A+rzhjwh!p zP0HMis_FLfWis`S;T)pevLT&7R^+RDCn6sm8!NZeIQ1xmVxf0rs-sAPBs)WfZ#P zN~{$)EsRmmO#fC~&kzR4f&vgY!H4jiO4R-s8x1e;yIQ8m91vbv!6EMhYp1z3aN0ou^yv&i#jJl?u>^&5=EoORA2+D*#nWbLEVBou`i$ zL$vHI_Vua-jz^8`W2pFnoApQ z<|^KFN^>YBDC7TYJTH`Z7VAKe_$hUFsQVdpR61vs3E^uFpk*au=m{)+TW#5g6h*yY6P+RP=s%sc_ARD>Kj0}=FUDl)?624h>vCT zokhLVD!tNbwkczmrZ3Me&C0Mqw6trU56We2E zCcs)wFB+s$RTcCNdfO-Ht5kE_D_()Co^i;6qdD{AGw1jFoG;eayzYg6fBMHp=s)z% zW5N0x7$O5f5WxuwmNO1;wKoEzn$5thW-G8@Hj-Lu2R7EaX5yr7;8trZsi$7xrH!Bg zdrWE($dN^C;ylAalenb*3uSE9n0E>JU7{d z>>=(W*g>C~#qam%U=}CDmt*S3nWQ`m6JN$DEm)>cMht{cLP=*bPOj=xeR#UqmLcEv zJRhDBbT$i9xFsrcr&d5^008huoTdKp@kr*KILyKhJ6-A8|;wy6vzl$D2atgH!(Gr*bA`Ap#quxHCO zCo$&^Sa}`Xyw>|cP%e5RVl&d~=J~BqhP~SiklH(>Y2M4@d}o&B;c!aH&XX~v$qpIk z;&T~Kd#iHvRsr|;vW%Vl`pG{12{hplt4QE_play`*b#Es5dy}&HuVPWi$ zeTARA(#$=C(Cs)QG?S_>7?=GiJrRZV8wm);GD12kI$U zrYtf8SGpt0(OH5MWrl<(TZ}b6l+9DgDQ%xo?g|ddlwqXo5#wnn15cMM;%t@+<)&1I zP|GlI^eGq%>M0|$L@$*gN6OME1a)mncU34%6&^t^LmpA7YET}264a|nD^%TWW4;Cv z@>Uh*o3b}yBep>x+r=&1#1;$>uj37D;4PS4{GqXhFTuMF*tM{4@GGEw3PlY`U9Foi z#CZ@Un!wj*YVb`7P$Y)t6)}NTRzVJQ8T~O$^3e?F1nCYED#nb-IE(z03K2$hiTa8z zVdm4PLz>H&Wj+sML5V*&@x^SAb2bF^&fD+k<@WO8D;-Ty++;JLZ^k6>FC#txspB6| zJ`Qu?pRk#qQLrU7i-puAOya?CX8@dL+!qYk%7FUZ*FcAufViRb!Rh0eP~pe&iq?ze zC!%rz*T5$C54wlne!UNbt5MD~24G-O1gTYf#-Rh3A-Jj=OQCzgFM{|KhJgPn=z|A6 zsLt9geiaUu5Z=@)UyDm1l=rpz@uVAU+9GqQIv9qFL1=k9qRsSjn?*yXhzeI+6 ZeTlUjy0c;fgbN^8{+hUHteYDj{SVM9`;`Cy literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz b/venv/Lib/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..89e83517b562451622cea7cbe1dbfac5c3d2513e GIT binary patch literal 153315 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%0D!KtMVr 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

yja~WxFc`yfrj%BE^ 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%0Ay}5phF%Yq4Q1qkIf-;CS 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 a$1xo%1ox9%9ceNF__ldZ2ky8&yz@WAsp(Gu literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dateutil/zoneinfo/rebuild.py b/venv/Lib/site-packages/dateutil/zoneinfo/rebuild.py new file mode 100644 index 00000000..78f0d1a0 --- /dev/null +++ b/venv/Lib/site-packages/dateutil/zoneinfo/rebuild.py @@ -0,0 +1,53 @@ +import logging +import os +import tempfile +import shutil +import json +from subprocess import check_call +from tarfile import TarFile + +from dateutil.zoneinfo import METADATA_FN, ZONEFILENAME + + +def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None): + """Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar* + + filename is the timezone tarball from ``ftp.iana.org/tz``. + + """ + tmpdir = tempfile.mkdtemp() + zonedir = os.path.join(tmpdir, "zoneinfo") + moduledir = os.path.dirname(__file__) + try: + with TarFile.open(filename) as tf: + for name in zonegroups: + tf.extract(name, tmpdir) + filepaths = [os.path.join(tmpdir, n) for n in zonegroups] + try: + check_call(["zic", "-d", zonedir] + filepaths) + except OSError as e: + _print_on_nosuchfile(e) + raise + # write metadata file + with open(os.path.join(zonedir, METADATA_FN), 'w') as f: + json.dump(metadata, f, indent=4, sort_keys=True) + target = os.path.join(moduledir, ZONEFILENAME) + with TarFile.open(target, "w:%s" % format) as tf: + for entry in os.listdir(zonedir): + entrypath = os.path.join(zonedir, entry) + tf.add(entrypath, entry) + finally: + shutil.rmtree(tmpdir) + + +def _print_on_nosuchfile(e): + """Print helpful troubleshooting message + + e is an exception raised by subprocess.check_call() + + """ + if e.errno == 2: + logging.error( + "Could not find zic. Perhaps you need to install " + "libc-bin or some other package that provides it, " + "or it's not in your PATH?") diff --git a/venv/Lib/site-packages/decouple.py b/venv/Lib/site-packages/decouple.py new file mode 100644 index 00000000..496302e5 --- /dev/null +++ b/venv/Lib/site-packages/decouple.py @@ -0,0 +1,234 @@ +# coding: utf-8 +import os +import sys +import string +from shlex import shlex +from io import open +from collections import OrderedDict +from distutils.util import strtobool + +# Useful for very coarse version differentiation. +PY3 = sys.version_info[0] == 3 + +if PY3: + from configparser import ConfigParser + text_type = str +else: + from ConfigParser import SafeConfigParser as ConfigParser + text_type = unicode + +DEFAULT_ENCODING = 'UTF-8' + +class UndefinedValueError(Exception): + pass + + +class Undefined(object): + """ + Class to represent undefined type. + """ + pass + + +# Reference instance to represent undefined values +undefined = Undefined() + + +class Config(object): + """ + Handle .env file format used by Foreman. + """ + + def __init__(self, repository): + self.repository = repository + + def _cast_boolean(self, value): + """ + Helper to convert config values to boolean as ConfigParser do. + """ + value = str(value) + return bool(value) if value == '' else bool(strtobool(value)) + + @staticmethod + def _cast_do_nothing(value): + return value + + def get(self, option, default=undefined, cast=undefined): + """ + Return the value for option or default if defined. + """ + + # We can't avoid __contains__ because value may be empty. + if option in os.environ: + value = os.environ[option] + elif option in self.repository: + value = self.repository[option] + else: + if isinstance(default, Undefined): + raise UndefinedValueError('{} not found. Declare it as envvar or define a default value.'.format(option)) + + value = default + + if isinstance(cast, Undefined): + cast = self._cast_do_nothing + elif cast is bool: + cast = self._cast_boolean + + return cast(value) + + def __call__(self, *args, **kwargs): + """ + Convenient shortcut to get. + """ + return self.get(*args, **kwargs) + + +class RepositoryEmpty(object): + def __init__(self, source='', encoding=DEFAULT_ENCODING): + pass + + def __contains__(self, key): + return False + + def __getitem__(self, key): + return None + + +class RepositoryIni(RepositoryEmpty): + """ + Retrieves option keys from .ini files. + """ + SECTION = 'settings' + + def __init__(self, source, encoding=DEFAULT_ENCODING): + self.parser = ConfigParser() + with open(source, encoding=encoding) as file_: + self.parser.readfp(file_) + + def __contains__(self, key): + return (key in os.environ or + self.parser.has_option(self.SECTION, key)) + + def __getitem__(self, key): + return self.parser.get(self.SECTION, key) + + +class RepositoryEnv(RepositoryEmpty): + """ + Retrieves option keys from .env files with fall back to os.environ. + """ + def __init__(self, source, encoding=DEFAULT_ENCODING): + self.data = {} + + with open(source, encoding=encoding) as file_: + for line in file_: + line = line.strip() + if not line or line.startswith('#') or '=' not in line: + continue + k, v = line.split('=', 1) + k = k.strip() + v = v.strip() + if len(v) >= 2 and ((v[0] == "'" and v[-1] == "'") or (v[0] == '"' and v[-1] == '"')): + v = v.strip('\'"') + self.data[k] = v + + def __contains__(self, key): + return key in os.environ or key in self.data + + def __getitem__(self, key): + return self.data[key] + + +class AutoConfig(object): + """ + Autodetects the config file and type. + + Parameters + ---------- + search_path : str, optional + Initial search path. If empty, the default search path is the + caller's path. + + """ + SUPPORTED = OrderedDict([ + ('settings.ini', RepositoryIni), + ('.env', RepositoryEnv), + ]) + + encoding = DEFAULT_ENCODING + + def __init__(self, search_path=None): + self.search_path = search_path + self.config = None + + def _find_file(self, path): + # look for all files in the current path + for configfile in self.SUPPORTED: + filename = os.path.join(path, configfile) + if os.path.isfile(filename): + return filename + + # search the parent + parent = os.path.dirname(path) + if parent and parent != os.path.abspath(os.sep): + return self._find_file(parent) + + # reached root without finding any files. + return '' + + def _load(self, path): + # Avoid unintended permission errors + try: + filename = self._find_file(os.path.abspath(path)) + except Exception: + filename = '' + Repository = self.SUPPORTED.get(os.path.basename(filename), RepositoryEmpty) + + self.config = Config(Repository(filename, encoding=self.encoding)) + + def _caller_path(self): + # MAGIC! Get the caller's module path. + frame = sys._getframe() + path = os.path.dirname(frame.f_back.f_back.f_code.co_filename) + return path + + def __call__(self, *args, **kwargs): + if not self.config: + self._load(self.search_path or self._caller_path()) + + return self.config(*args, **kwargs) + + +# A pré-instantiated AutoConfig to improve decouple's usability +# now just import config and start using with no configuration. +config = AutoConfig() + +# Helpers + +class Csv(object): + """ + Produces a csv parser that return a list of transformed elements. + """ + + def __init__(self, cast=text_type, delimiter=',', strip=string.whitespace, post_process=list): + """ + Parameters: + cast -- callable that transforms the item just before it's added to the list. + delimiter -- string of delimiters chars passed to shlex. + strip -- string of non-relevant characters to be passed to str.strip after the split. + post_process -- callable to post process all casted values. Default is `list`. + """ + self.cast = cast + self.delimiter = delimiter + self.strip = strip + self.post_process = post_process + + def __call__(self, value): + """The actual transformation""" + transform = lambda s: self.cast(s.strip(self.strip)) + + splitter = shlex(value, posix=True) + splitter.whitespace = self.delimiter + splitter.whitespace_split = True + + return self.post_process(transform(s) for s in splitter) diff --git a/venv/Lib/site-packages/dotenv/__init__.py b/venv/Lib/site-packages/dotenv/__init__.py new file mode 100644 index 00000000..b88d9bc2 --- /dev/null +++ b/venv/Lib/site-packages/dotenv/__init__.py @@ -0,0 +1,46 @@ +from .compat import IS_TYPE_CHECKING +from .main import load_dotenv, get_key, set_key, unset_key, find_dotenv, dotenv_values + +if IS_TYPE_CHECKING: + from typing import Any, Optional + + +def load_ipython_extension(ipython): + # type: (Any) -> None + from .ipython import load_ipython_extension + load_ipython_extension(ipython) + + +def get_cli_string(path=None, action=None, key=None, value=None, quote=None): + # type: (Optional[str], Optional[str], Optional[str], Optional[str], Optional[str]) -> str + """Returns a string suitable for running as a shell script. + + Useful for converting a arguments passed to a fabric task + to be passed to a `local` or `run` command. + """ + command = ['dotenv'] + if quote: + command.append('-q %s' % quote) + if path: + command.append('-f %s' % path) + if action: + command.append(action) + if key: + command.append(key) + if value: + if ' ' in value: + command.append('"%s"' % value) + else: + command.append(value) + + return ' '.join(command).strip() + + +__all__ = ['get_cli_string', + 'load_dotenv', + 'dotenv_values', + 'get_key', + 'set_key', + 'unset_key', + 'find_dotenv', + 'load_ipython_extension'] diff --git a/venv/Lib/site-packages/dotenv/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/dotenv/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72300a29ae3c9c59db27979fb030395e32862736 GIT binary patch literal 1175 zcmZuwTWi!n6wc(n*-Pt-R`DSed|07Hupp?2Wi2X7EnXfBi|I5oyUlhoNoSJkF6mSK zGlD-z=1qL^FZkq{*;Oid!kjrdIrsDBZoeOWzxM6J1JAO4SeGaS`7s4J|({Lfa>BkLq2ozE!g zlMabUm-I-V49FVsr(rT6!5eFM$`l`!HOj1(T zl%J_!O6&B5&Q%~+LZ{(>#O@^Le=YkXQ%^k_0QI=s~2RE&6a7 z`HLk6ApbfT-5*Fz{Z0pX> zy`9s=WHw%QHcr!==XIKHS923xZ%?|wEW3w{W>5D1#0bFX#9M(fRB zyRCcGG}&6>w$V7PwbCHLw5COR2(7((v(1|$7;#(Y#1}`#%WIvb7mtjOV+QBWQ0rZw znSEiw2>r#^*R*Z~mobiIEpk4MWs}$Vh|zdbidZz9Yb7=Y$CR;HW+JcZ?I?ut!6qMI8uCSDp#B~W7CiqC?SzKdzV!T0lME|ikWP0w0F{D0(+=p>i z76s;H+t?@ma^4GX>omS4;hMnp3&jg}^OoEkI?BgYMLALKtjxLcbW0Tg*R61U3?DNM zHsxs4dIpWkJ8b~YDp=()pjfgsSdAKJkY_AUm&4JJkvD-^fsb4?Se*!NeL}QuN4B^D z--TYW&^Qnp2SRUO=+%n=*wEY8G+a|YB!=RIhP`qNob#pUf1FG-<=68HW-KBdtE-uY tw#li2G{uai>7_1o#lo}_J}gSoF#1UUG*Z{tL$U3mbtm+r>o&Ul{04OQBYOY< literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dotenv/__pycache__/cli.cpython-36.pyc b/venv/Lib/site-packages/dotenv/__pycache__/cli.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..83ba108614b2d7ccce9b8e3684bcf3297253fa79 GIT binary patch literal 3970 zcmZ`+TW=f372YdJQIt$8vSiD*^+mR6n~aMDO<=f&14(I(7>aAjaI=xitT{tzsl8A$ zyOKyyPxPyR0{sJh?O*6GDfX>MAL_r5m-aie6eT+D3Ok&gIlFVtcfNDc7c(>Ee=Yv= z&;K~5X+LXct~tz~;gULb)mafI*UUZFS03C=33`cX9?rwp~j|%2G*~z z3Y$6B*(^JEY%94cJJ04YyUOO+XKa2OIYF86!g9?((R=`#fMTxpwD0)RiQ>fZBbj(Xz?pO4Cp%6n@SV7u z_;J*5%CFMM>H6Ih8RzE27k-57+nY`jJ8l2aNp^hM_yDG9H6yD&-)L_B@N%uW@`tsR z@1C#!J}V?~6Q|a!Y$iXyx$gxjmsxR}C(T{n%ZhR|nM$M46h=%poBLcy=+kW0%*^~I z>SfjoUnZHg>bH`3TpX+HSz?;s~jhNff|EzjUwQme(-&+CXb+1AU-%z?LW4 zz)&;eiN;KD$LfEh&X6tPcR>y5q!Pq{(s0N_2c9Cmm3v0YI&Be$j(3V+BeTAJ{$ee& z|M>d*%{8K-8Nch0=UT~OW)b$8tswZjCN4vzU%p#;y!Beb$6IncPTAHs-R`QFcw6MV zU~7|y-7Rpio#f1a!@V9+WBy{wC? zn6-$QblIa@LGlz2ITV+)BR$avND@cJ;hlleF^1Zz_I7E@I5Ll{#Ozqi7?=aAZ7d*_ z=-S?uHz$zYSoLK$@Op4lphzY`7N7=Bf37^*5EpPx-(Hr#TbAoJTP)&Hkb;@*CkV{U zhSsg!%;K$`82cA#{}S!bj$*}=O$N4S`fjH0E7vR=~Xba9*Z zOlScglmy>P!DE)$N~JMFNDG2Q!SMoe8)It>m6U~}jvwL*>! z87uD6Kq!eN5-7^F)#6fqjE)KFK7xR(V9<(XFk)V}^m#+v$8*b|Rb1BSRzQ4>hcOTa z%s0RcFkxVdhstO?56lUpRe@v{jJEsBuXrMSguk*|0q(RBI27z-6ed_PX?hXIpPDZ2 z;jw0&v7C%{D>=>bH2N!}5vtE1CCeDJ0vWALet@U3$>eu3TbaCy$*IX>-Avx6Z#DX+~xRJkA ziA8l(iOpYSaVpb4zaEdRQ8CdNIlp+C9tD-Gj~I|m zW1CV##~x}bMl8(9XSR2Ms&m$D=q_aX4lar*Q#HzE2NwkL zP&sifq`b3oxwp@D3NkOdKY;;1sc(3Uq2l00k~(Q%KFN>Kp)Wn*g*-vkRqxlwmwFs& z^4Ot9>qnW0w`!TR-J}EZU~mu+j-T8G=O({~gC9H8olU$29ewJ>yukUSW+OCsl8R_T zEA%E@^E80Zp{e9qIah%+sM%R@bh`Kof^M-DGHQ1- zy_@OHkGLp)1CbE7r2o*g1!QM%(NKRSy<({Stki6Vo{z5g!+&wr9th%MX3Cz-${^8* zLrO0dBH8rwup5hHO^8@z1(`4|L}nidv>2*{WCf_$^%6ljU&VB0(Fv*(5vt~>nyb<- z^-zL}2O9TY7dcQ&(+bs4F+-qvX5qs-nbnE?C@Y}&v<_Hiy~LVZSlNmF z7I$Y)+W7npx6<{bL?Ek7t9mp+?U-s?+!FF@N{LMsX+dQxPukP3*I&N+er0WAV{KK% z?HH^Gd~TkFS|tG0AeEk=lTHKvlqeyoB$N>N6~oXg`Ye8{MpeIvUzA9*fLU3mzHwHK f3sVKXS}ve&RP-w|^t*(v664G)#%r^sTV?%!ed+1s literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dotenv/__pycache__/compat.cpython-36.pyc b/venv/Lib/site-packages/dotenv/__pycache__/compat.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cfe26082c4bb77f9c260f6de00c973e389d1ce24 GIT binary patch literal 1105 zcmZuv(QeZ)6t$f+P1|*<#u!3K$eIvPq*jPc0wIJzr9cIgi59`CYHf&}HER+F+ui6i zp2kP;3;YKkf%1lU+Ar|Lb-IT!u;d$G-y9Ha_0yD2M%t(Y{ z{27ApP_k+xO{Qge4TBG5tR8xgju3#`U1@#?+-iS&t37HxZ?|4_y3d~0t<2H$6e>v@ z60%AsPKDHMDTS=#{O%i)MF{=ww(bw#DlXMf?TQ&2K2Fmop7w?>h3`efK9AF38m5g| zl6wA>v&O-M^Qduq^X}%y!(ea7gyzZq&=+y)>1I05YM~lIX+H8N+@C@RhMX#cL4@EM zUd9fVCGenYyU#yq-%v2ie2q-G5?A4%kz6~1=h0-(I!5UV!^a@0Va^D!w+?s86jnR& z1>@ACD%Y0k38%`7IX&>^6xsz}(oj?Akt-TV5$7q;b|^&>^F&i2;rDS9uEsP7Bi_t` z4+}FLH12ilc4lpD-^m=FSIR6kSJ}l~u1zYOH3tyr0mvFs8c5~ZE{qFI;5Ebu(67K* zIbcx1#w$eD;Pij~YY+kc%ZC3yyuaq{z4E4KSieoeT!t_(Ttk)I(A0}0_t?By&hz_| zP(gss6c1(*4S+P`L6|_>RC)(=c8ai&QPB_F_aK*!XL3CdVtssyy6;6HqhXSRZq4;X zBy{7PVnmu}&m6{0Q=p2oKs~QZ5p*$nU5D(2Fs_ZuL|y>DUXfJ;va}D=8H!{SCeX9N zm6o|Dp~wemQ0w$Yf6c!vm?SfExxzWxnaXDFW&SMggIKUx#2=d3Q>$QHvP^8eOqMLS K{p3vfFJ-n!5dLVuzg19bEl zreq)(VmLr)b=J2PdI1)(5r7N|nr4Wva5O924z*M;BQtb(m)* zPYR>xzJ16f&8d{IC!k`nDtM{%&{~qcF7>dcLUQv$Dsa7chs{Bb?#;83C9G1sywJV8 zqI)GZk?cnZi^nkK3lJQ|n4#E#3=nr2W)34J(`y$r$PTn%r*&D#JFLh0Y;fJ-9y?&e zE40P?U+l9HWc`629KTqrWmP_{)U)!UaVAw0PA1g_7b0U^hf-C7CrYe%bMLrhSxOa; zR2A)aMEq?g6{HniP?6EIoJ+{fqZT~vE_q&q(~G7%nSjr;O7iCJ-+uNWM=8y7Y22Ri zXtz!GT4<7*s^xq9RpU+psOF1PPDQ$mvJ(EO;8eod`8rx=izOG)yvi9D5}qPbOCG(t z;pElW)t%DRLAz>QbL}%er>k6vAz+(2Ak1)K4q!fkDSrf^&<3634c)2!&t!HJ$c#ON@-bE zDl$}vOn$J+IRNPIQ3&=xkGmp(V6@j}Bu1bJ!w&5qKYu1fC3MG9SM-eKmJzo>K+>+{ z`CPk(zS=eU+mC-k;$yJCfBxj*><0;;&E%q5vDw#k{gf&?`?i9r%wF=Mp4C}BS(PCFi81s+7=Yv< zh=8~dg@-+-l_9L{j?}cH8Rls?VC#MW3#d9$&@oUi4E1uFkG|&%AUn{^Q0s0RdSv^3 zdNOw6q1f5mOF~-zV;uWCaQ+jZzHOU+XH588xCta&IrrdfY1!zAPhovB9vVpIE46}F zOoTDTAMRkBrqG{L8-pNVXfZMOXMFVkE&p6g7R44%ODngaS!wyy^2dN@Prnhgp^Grk Z>fp<^K)`p1)$Hv%hxm|;NFN7Be*tVGjFtcZ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dotenv/__pycache__/main.cpython-36.pyc b/venv/Lib/site-packages/dotenv/__pycache__/main.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b08154e19ca15a04b49b6cf315346cb525f17ec GIT binary patch literal 8508 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 PSUzK&vyPS*%GLh@xf@&x literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dotenv/__pycache__/parser.cpython-36.pyc b/venv/Lib/site-packages/dotenv/__pycache__/parser.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b75fc4e4882016dfb1456374962a36e71c0169a2 GIT binary patch literal 5747 zcmZ`-TXPf16`r1(Mq^nRumQ8c8XIiOfS0`_q^M<=V7!a7tRQQ%%L>6{P>*fNy3sS@ zq7r#nkvyb|s;x@pEk7W6&p$|2p7NYZ^;;@W`~{x!oo;DlIjrnHr|;+VIbWado{uLd z{r?>Q_oLs>8peN(eZNWMuOr3(VuryD&gw=&9X zCAX0&Wg1?|Yh+8=hF|g<6Qzkpu9Ry`mL?ndQl1(7Bkpkb6)PQz4)IK%m8QVGh}91B z>}#`h1SKD}N5Ln+r@?dJGvJfp$H4R8$H5PQp8%f%p9Mb*eiHl$_h{ll(n!pIT=Pw4WL)osQ0; z?o2d~F*e35d}lOo*hX}2&;OB$gct7x~gFqjZ5U^8)xqzQQkn zU*Z?}CGb_g%0B@AfM4bxf?wur(2 zJ?TWvN+j(q%px7tvtp7|{D|8~Ye?}iki@95#OxV;hMd)7H;ldsHCeG>OH)KLQ)xBY z)p{gol+4^KZmiw8xem?UI1$z6j+}rL5eD1!a3}t&A&#Qk+v3{Q^1V0`aXH>;b$I!g z?e@(u3Cp)zm9SpkiW=>5yV}0gX|}`4Uc@gw+l`|7rOTgO`Q%A=yH@6{Bx*h_w?lzB zR@*P6-w5}jKtwyybJ`=ZT8$}YkniS}%c9)u(Z48kt#Z6lVB!e1_zxbHH>vUAN_S@Y z^Q+PGc1t8_<%PoM-R#oxgGWmbFBF!#6UbdFFO*}Hx|7S-(taz2?%}0nj4zj$depnH zg6@asb=5qo=Ba94%`m6s6}l%dU^K-7vPu!iJUObSNmJ!}=L+59!+xqoFRI+cps8~C z0pyqG!^hiAkvx6a&FGqk0D-9PQ~?5U7^{|cz1oD^Zd99`++*!UFQoG{takvH)}W_! zA|YC$z@*&B#Zw?X)-wX52kZryU1QFaPGuKv7st_|U`acU>f7Rd z6jY5umnPGwlU@*1o7E%;?w}MCLJiYp-NQrStHbsRZ8!Q6Pw}5ap@%R_q4hPP-YyF2 zAV|-Gc|b0#WmW3&K^>ep2}QDn5gL|OH=l}EO=Ej6wE*egV>QT!4OoqAF|xu)GPIaO zPwBLhUHFW4*S_^zag=KprgXUtvBi|A>gFyO0&}i{ao$|RLc+v^$x<#BN4JIn}Fpe8hvfJWf0X5=0kphvC zy;P2jF3x}ykzxnGB=RnK zt7zaz3LYL4R34K=n7B?IZi0*?>Aj|RTFD;lGiR{RT%P5=+HVs)2k!Ano(IqJL(p(a zy5B@0kHi=PQtZt`4q>lP=tK0cSt@6zIVbhGv;`*8z9hpb152+x*xk}@gko<9QaTc% zu(8hy%HZUsDpE`kG)$lQtb0i7_wEg9q1ze_pfKJ8Q1uZ7HQC-EP}t%_wCR1aNI8mq z2>f%DA9;b~OK&%f0nX=13#Gx?kt#538;cOBrA7)s4eSWIQs_u!iH}H>f*PsvQ?ju` z1F}*44%x%DG1+N!A}=Mhe8h$td<1IOtlyclRA<&4&Q#1Yi!eW}UdK#V(5bE~l!hvJ zDr2db`fd~XI|DT%{)`mWo-)}qg6Y6hqaI_RO4U7yaZpStTtiT=F;I5gAccn}Q)((* zNUI@ioJyTW|8Y*7lsfy>lXU!vto#azWLJZAu{iIVt(IRfC^es&#bxF z(Pk~vSLWBNt9gBL1Xg&OMCt3))w#jNajw})=C6Vx_l^)&dZf(OWyhbb?jJyxm$>s7e!7OC+Osz(r>vBkO6JEW`ydae;uytBWHgl?ka84z)?{M>? z$=VLW%rWs5>eJQHno}e^MuB5p&+pS36Rc6`jeT~-z-r0eSlLSp5e93* z+Ku>D&*IiBp67e6$~(xrd#SNi9UyNh9wg6v#GX{$EB->hr`(Y{Xf_*a0HGP_2%(C0gCIH3K_+ z!|hEY$@b=+n`T%~^8Woh~%ve(} z*g!BbZ*?Az5T_qcGh?;G&dMfGK-v3OqyQQHpq(eQoz+BtLwiiIMZ+(L+YlWC3W}zn z+x(b`>Gr9%*gugW6Zi3kdGX`Wv^ci#Z13ZphGPJRLdof?u=N@SRJ~jZ|JsxatSs@= zfneb43&GXK_H3%2`2K^Q6J&I3Qp~3UA4~8ZNXcOdz67C%4FFR}+$YI4lKh@8{-M9S zsM+w9i2Cl+%HbwybzeKwEq(!@s=cag_8t}}P7%?O;M!JUO2>H>2WEl>4%I5XFoI{h z)g+4X@gUNt!3i2gr-94_`jxMCvz*x9lMV{h;PXgWhvRpe%Gh{k;0s41Y9@GF;6w{r z+X2Z?*K`CLoaM=|Zs_Nc&Ih9Br1tY;{r3Jl1ggl0;E9by9yZmV%RCi&e3nP8k8MVG z;!zvb4oiZ9Pl^b#45bQ9eGY^!h!k?4AEQTPAFmsH%t3(q7ASe*4+0B5=2+^W)7GLH zargQdOyl;zDsp-i`ZE89BXVLGMownGzIS_T&%xbwQw!6B`ZsD5TzVcuBVE_zps! zEn;z+2A-il+4bj@NPTEipwO1QNzHU*s0g5-q+F#8v~T|_+tPNI-lzH%`x)6Tu7Thx zVot&P7hT`MAq0H<#Ghr_9tC6IC}$JTqqobZtSLkeAjLG9XK7Bj$vgRDQ+ED>m-TY^ IJLE0?4<>%JhX4Qo literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dotenv/__pycache__/version.cpython-36.pyc b/venv/Lib/site-packages/dotenv/__pycache__/version.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d24ad5141a109b36db0ceb5728eb427a666b9b84 GIT binary patch literal 184 zcmXr!<>h)Pej`qtfq~&M5W@i@kmUfx#T-B)g&~R|g)x{xlc|c$K+n)v&%jTU@fLS{ zd|7Hyab|vAeEdp=A|{|hF!9UZ*(xTq7$_A}oSt8r6607<;F4I97~_+loR||6lA2o( zQ;=DpTbfspn4F!OqFbJknwq0)U}A1kSe2F)lagPOnpYMBHCwNs@)n0pZhlH>PO2Tq JRmDKe0060+GN1qe literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/dotenv/cli.py b/venv/Lib/site-packages/dotenv/cli.py new file mode 100644 index 00000000..91a8e3d3 --- /dev/null +++ b/venv/Lib/site-packages/dotenv/cli.py @@ -0,0 +1,145 @@ +import os +import sys +from subprocess import Popen + +try: + import click +except ImportError: + sys.stderr.write('It seems python-dotenv is not installed with cli option. \n' + 'Run pip install "python-dotenv[cli]" to fix this.') + sys.exit(1) + +from .compat import IS_TYPE_CHECKING, to_env +from .main import dotenv_values, get_key, set_key, unset_key +from .version import __version__ + +if IS_TYPE_CHECKING: + from typing import Any, List, Dict + + +@click.group() +@click.option('-f', '--file', default=os.path.join(os.getcwd(), '.env'), + type=click.Path(exists=True), + help="Location of the .env file, defaults to .env file in current working directory.") +@click.option('-q', '--quote', default='always', + type=click.Choice(['always', 'never', 'auto']), + help="Whether to quote or not the variable values. Default mode is always. This does not affect parsing.") +@click.version_option(version=__version__) +@click.pass_context +def cli(ctx, file, quote): + # type: (click.Context, Any, Any) -> None + '''This script is used to set, get or unset values from a .env file.''' + ctx.obj = {} + ctx.obj['FILE'] = file + ctx.obj['QUOTE'] = quote + + +@cli.command() +@click.pass_context +def list(ctx): + # type: (click.Context) -> None + '''Display all the stored key/value.''' + file = ctx.obj['FILE'] + dotenv_as_dict = dotenv_values(file) + for k, v in dotenv_as_dict.items(): + click.echo('%s=%s' % (k, v)) + + +@cli.command() +@click.pass_context +@click.argument('key', required=True) +@click.argument('value', required=True) +def set(ctx, key, value): + # type: (click.Context, Any, Any) -> None + '''Store the given key/value.''' + file = ctx.obj['FILE'] + quote = ctx.obj['QUOTE'] + success, key, value = set_key(file, key, value, quote) + if success: + click.echo('%s=%s' % (key, value)) + else: + exit(1) + + +@cli.command() +@click.pass_context +@click.argument('key', required=True) +def get(ctx, key): + # type: (click.Context, Any) -> None + '''Retrieve the value for the given key.''' + file = ctx.obj['FILE'] + stored_value = get_key(file, key) + if stored_value: + click.echo('%s=%s' % (key, stored_value)) + else: + exit(1) + + +@cli.command() +@click.pass_context +@click.argument('key', required=True) +def unset(ctx, key): + # type: (click.Context, Any) -> None + '''Removes the given key.''' + file = ctx.obj['FILE'] + quote = ctx.obj['QUOTE'] + success, key = unset_key(file, key, quote) + if success: + click.echo("Successfully removed %s" % key) + else: + exit(1) + + +@cli.command(context_settings={'ignore_unknown_options': True}) +@click.pass_context +@click.argument('commandline', nargs=-1, type=click.UNPROCESSED) +def run(ctx, commandline): + # type: (click.Context, List[str]) -> None + """Run command with environment variables present.""" + file = ctx.obj['FILE'] + dotenv_as_dict = {to_env(k): to_env(v) for (k, v) in dotenv_values(file).items() if v is not None} + + if not commandline: + click.echo('No command given.') + exit(1) + ret = run_command(commandline, dotenv_as_dict) + exit(ret) + + +def run_command(command, env): + # type: (List[str], Dict[str, str]) -> int + """Run command in sub process. + + Runs the command in a sub process with the variables from `env` + added in the current environment variables. + + Parameters + ---------- + command: List[str] + The command and it's parameters + env: Dict + The additional environment variables + + Returns + ------- + int + The return code of the command + + """ + # copy the current environment variables and add the vales from + # `env` + cmd_env = os.environ.copy() + cmd_env.update(env) + + p = Popen(command, + universal_newlines=True, + bufsize=0, + shell=False, + env=cmd_env) + _, _ = p.communicate() + + return p.returncode + + +if __name__ == "__main__": + cli() diff --git a/venv/Lib/site-packages/dotenv/compat.py b/venv/Lib/site-packages/dotenv/compat.py new file mode 100644 index 00000000..f8089bf4 --- /dev/null +++ b/venv/Lib/site-packages/dotenv/compat.py @@ -0,0 +1,49 @@ +import sys + +PY2 = sys.version_info[0] == 2 # type: bool + +if PY2: + from StringIO import StringIO # noqa +else: + from io import StringIO # noqa + + +def is_type_checking(): + # type: () -> bool + try: + from typing import TYPE_CHECKING + except ImportError: + return False + return TYPE_CHECKING + + +IS_TYPE_CHECKING = is_type_checking() + + +if IS_TYPE_CHECKING: + from typing import Text + + +def to_env(text): + # type: (Text) -> str + """ + Encode a string the same way whether it comes from the environment or a `.env` file. + """ + if PY2: + return text.encode(sys.getfilesystemencoding() or "utf-8") + else: + return text + + +def to_text(string): + # type: (str) -> Text + """ + Make a string Unicode if it isn't already. + + This is useful for defining raw unicode strings because `ur"foo"` isn't valid in + Python 3. + """ + if PY2: + return string.decode("utf-8") + else: + return string diff --git a/venv/Lib/site-packages/dotenv/ipython.py b/venv/Lib/site-packages/dotenv/ipython.py new file mode 100644 index 00000000..7f1b13d6 --- /dev/null +++ b/venv/Lib/site-packages/dotenv/ipython.py @@ -0,0 +1,41 @@ +from __future__ import print_function + +from IPython.core.magic import Magics, line_magic, magics_class # type: ignore +from IPython.core.magic_arguments import (argument, magic_arguments, # type: ignore + parse_argstring) # type: ignore + +from .main import find_dotenv, load_dotenv + + +@magics_class +class IPythonDotEnv(Magics): + + @magic_arguments() + @argument( + '-o', '--override', action='store_true', + help="Indicate to override existing variables" + ) + @argument( + '-v', '--verbose', action='store_true', + help="Indicate function calls to be verbose" + ) + @argument('dotenv_path', nargs='?', type=str, default='.env', + help='Search in increasingly higher folders for the `dotenv_path`') + @line_magic + def dotenv(self, line): + args = parse_argstring(self.dotenv, line) + # Locate the .env file + dotenv_path = args.dotenv_path + try: + dotenv_path = find_dotenv(dotenv_path, True, True) + except IOError: + print("cannot find .env file") + return + + # Load the .env file + load_dotenv(dotenv_path, verbose=args.verbose, override=args.override) + + +def load_ipython_extension(ipython): + """Register the %dotenv magic.""" + ipython.register_magics(IPythonDotEnv) diff --git a/venv/Lib/site-packages/dotenv/main.py b/venv/Lib/site-packages/dotenv/main.py new file mode 100644 index 00000000..7fbd24f8 --- /dev/null +++ b/venv/Lib/site-packages/dotenv/main.py @@ -0,0 +1,323 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, print_function, unicode_literals + +import io +import logging +import os +import re +import shutil +import sys +import tempfile +from collections import OrderedDict +from contextlib import contextmanager + +from .compat import IS_TYPE_CHECKING, PY2, StringIO, to_env +from .parser import Binding, parse_stream + +logger = logging.getLogger(__name__) + +if IS_TYPE_CHECKING: + from typing import ( + Dict, Iterator, Match, Optional, Pattern, Union, Text, IO, Tuple + ) + if sys.version_info >= (3, 6): + _PathLike = os.PathLike + else: + _PathLike = Text + + if sys.version_info >= (3, 0): + _StringIO = StringIO + else: + _StringIO = StringIO[Text] + +__posix_variable = re.compile( + r""" + \$\{ + (?P[^\}:]*) + (?::- + (?P[^\}]*) + )? + \} + """, + re.VERBOSE, +) # type: Pattern[Text] + + +def with_warn_for_invalid_lines(mappings): + # type: (Iterator[Binding]) -> Iterator[Binding] + for mapping in mappings: + if mapping.error: + logger.warning( + "Python-dotenv could not parse statement starting at line %s", + mapping.original.line, + ) + yield mapping + + +class DotEnv(): + + def __init__(self, dotenv_path, verbose=False, encoding=None, interpolate=True): + # type: (Union[Text, _PathLike, _StringIO], bool, Union[None, Text], bool) -> None + self.dotenv_path = dotenv_path # type: Union[Text,_PathLike, _StringIO] + self._dict = None # type: Optional[Dict[Text, Optional[Text]]] + self.verbose = verbose # type: bool + self.encoding = encoding # type: Union[None, Text] + self.interpolate = interpolate # type: bool + + @contextmanager + def _get_stream(self): + # type: () -> Iterator[IO[Text]] + if isinstance(self.dotenv_path, StringIO): + yield self.dotenv_path + elif os.path.isfile(self.dotenv_path): + with io.open(self.dotenv_path, encoding=self.encoding) as stream: + yield stream + else: + if self.verbose: + logger.warning("File doesn't exist %s", self.dotenv_path) + yield StringIO('') + + def dict(self): + # type: () -> Dict[Text, Optional[Text]] + """Return dotenv as dict""" + if self._dict: + return self._dict + + values = OrderedDict(self.parse()) + self._dict = resolve_nested_variables(values) if self.interpolate else values + return self._dict + + def parse(self): + # type: () -> Iterator[Tuple[Text, Optional[Text]]] + with self._get_stream() as stream: + for mapping in with_warn_for_invalid_lines(parse_stream(stream)): + if mapping.key is not None: + yield mapping.key, mapping.value + + def set_as_environment_variables(self, override=False): + # type: (bool) -> bool + """ + Load the current dotenv as system environemt variable. + """ + for k, v in self.dict().items(): + if k in os.environ and not override: + continue + if v is not None: + os.environ[to_env(k)] = to_env(v) + + return True + + def get(self, key): + # type: (Text) -> Optional[Text] + """ + """ + data = self.dict() + + if key in data: + return data[key] + + if self.verbose: + logger.warning("Key %s not found in %s.", key, self.dotenv_path) + + return None + + +def get_key(dotenv_path, key_to_get): + # type: (Union[Text, _PathLike], Text) -> Optional[Text] + """ + Gets the value of a given key from the given .env + + If the .env path given doesn't exist, fails + """ + return DotEnv(dotenv_path, verbose=True).get(key_to_get) + + +@contextmanager +def rewrite(path): + # type: (_PathLike) -> Iterator[Tuple[IO[Text], IO[Text]]] + try: + with tempfile.NamedTemporaryFile(mode="w+", delete=False) as dest: + with io.open(path) as source: + yield (source, dest) # type: ignore + except BaseException: + if os.path.isfile(dest.name): + os.unlink(dest.name) + raise + else: + shutil.move(dest.name, path) + + +def set_key(dotenv_path, key_to_set, value_to_set, quote_mode="always"): + # type: (_PathLike, Text, Text, Text) -> Tuple[Optional[bool], Text, Text] + """ + Adds or Updates a key/value to the given .env + + If the .env path given doesn't exist, fails instead of risking creating + an orphan .env somewhere in the filesystem + """ + value_to_set = value_to_set.strip("'").strip('"') + if not os.path.exists(dotenv_path): + logger.warning("Can't write to %s - it doesn't exist.", dotenv_path) + return None, key_to_set, value_to_set + + if " " in value_to_set: + quote_mode = "always" + + if quote_mode == "always": + value_out = '"{}"'.format(value_to_set.replace('"', '\\"')) + else: + value_out = value_to_set + line_out = "{}={}\n".format(key_to_set, value_out) + + with rewrite(dotenv_path) as (source, dest): + replaced = False + for mapping in with_warn_for_invalid_lines(parse_stream(source)): + if mapping.key == key_to_set: + dest.write(line_out) + replaced = True + else: + dest.write(mapping.original.string) + if not replaced: + dest.write(line_out) + + return True, key_to_set, value_to_set + + +def unset_key(dotenv_path, key_to_unset, quote_mode="always"): + # type: (_PathLike, Text, Text) -> Tuple[Optional[bool], Text] + """ + Removes a given key from the given .env + + If the .env path given doesn't exist, fails + If the given key doesn't exist in the .env, fails + """ + if not os.path.exists(dotenv_path): + logger.warning("Can't delete from %s - it doesn't exist.", dotenv_path) + return None, key_to_unset + + removed = False + with rewrite(dotenv_path) as (source, dest): + for mapping in with_warn_for_invalid_lines(parse_stream(source)): + if mapping.key == key_to_unset: + removed = True + else: + dest.write(mapping.original.string) + + if not removed: + logger.warning("Key %s not removed from %s - key doesn't exist.", key_to_unset, dotenv_path) + return None, key_to_unset + + return removed, key_to_unset + + +def resolve_nested_variables(values): + # type: (Dict[Text, Optional[Text]]) -> Dict[Text, Optional[Text]] + def _replacement(name, default): + # type: (Text, Optional[Text]) -> Text + """ + get appropriate value for a variable name. + first search in environ, if not found, + then look into the dotenv variables + """ + default = default if default is not None else "" + ret = os.getenv(name, new_values.get(name, default)) + return ret # type: ignore + + def _re_sub_callback(match): + # type: (Match[Text]) -> Text + """ + From a match object gets the variable name and returns + the correct replacement + """ + matches = match.groupdict() + return _replacement(name=matches["name"], default=matches["default"]) # type: ignore + + new_values = {} + + for k, v in values.items(): + new_values[k] = __posix_variable.sub(_re_sub_callback, v) if v is not None else None + + return new_values + + +def _walk_to_root(path): + # type: (Text) -> Iterator[Text] + """ + Yield directories starting from the given directory up to the root + """ + if not os.path.exists(path): + raise IOError('Starting path not found') + + if os.path.isfile(path): + path = os.path.dirname(path) + + last_dir = None + current_dir = os.path.abspath(path) + while last_dir != current_dir: + yield current_dir + parent_dir = os.path.abspath(os.path.join(current_dir, os.path.pardir)) + last_dir, current_dir = current_dir, parent_dir + + +def find_dotenv(filename='.env', raise_error_if_not_found=False, usecwd=False): + # type: (Text, bool, bool) -> Text + """ + Search in increasingly higher folders for the given file + + Returns path to the file if found, or an empty string otherwise + """ + + def _is_interactive(): + """ Decide whether this is running in a REPL or IPython notebook """ + main = __import__('__main__', None, None, fromlist=['__file__']) + return not hasattr(main, '__file__') + + if usecwd or _is_interactive() or getattr(sys, 'frozen', False): + # Should work without __file__, e.g. in REPL or IPython notebook. + path = os.getcwd() + else: + # will work for .py files + frame = sys._getframe() + # find first frame that is outside of this file + if PY2 and not __file__.endswith('.py'): + # in Python2 __file__ extension could be .pyc or .pyo (this doesn't account + # for edge case of Python compiled for non-standard extension) + current_file = __file__.rsplit('.', 1)[0] + '.py' + else: + current_file = __file__ + + while frame.f_code.co_filename == current_file: + assert frame.f_back is not None + frame = frame.f_back + frame_filename = frame.f_code.co_filename + path = os.path.dirname(os.path.abspath(frame_filename)) + + for dirname in _walk_to_root(path): + check_path = os.path.join(dirname, filename) + if os.path.isfile(check_path): + return check_path + + if raise_error_if_not_found: + raise IOError('File not found') + + return '' + + +def load_dotenv(dotenv_path=None, stream=None, verbose=False, override=False, interpolate=True, **kwargs): + # type: (Union[Text, _PathLike, None], Optional[_StringIO], bool, bool, bool, Union[None, Text]) -> bool + """Parse a .env file and then load all the variables found as environment variables. + + - *dotenv_path*: absolute or relative path to .env file. + - *stream*: `StringIO` object with .env content. + - *verbose*: whether to output the warnings related to missing .env file etc. Defaults to `False`. + - *override*: where to override the system environment variables with the variables in `.env` file. + Defaults to `False`. + """ + f = dotenv_path or stream or find_dotenv() + return DotEnv(f, verbose=verbose, interpolate=interpolate, **kwargs).set_as_environment_variables(override=override) + + +def dotenv_values(dotenv_path=None, stream=None, verbose=False, interpolate=True, **kwargs): + # type: (Union[Text, _PathLike, None], Optional[_StringIO], bool, bool, Union[None, Text]) -> Dict[Text, Optional[Text]] # noqa: E501 + f = dotenv_path or stream or find_dotenv() + return DotEnv(f, verbose=verbose, interpolate=interpolate, **kwargs).dict() diff --git a/venv/Lib/site-packages/dotenv/parser.py b/venv/Lib/site-packages/dotenv/parser.py new file mode 100644 index 00000000..2c93cbd0 --- /dev/null +++ b/venv/Lib/site-packages/dotenv/parser.py @@ -0,0 +1,237 @@ +import codecs +import re + +from .compat import IS_TYPE_CHECKING, to_text + +if IS_TYPE_CHECKING: + from typing import ( # noqa:F401 + IO, Iterator, Match, NamedTuple, Optional, Pattern, Sequence, Text, + Tuple + ) + + +def make_regex(string, extra_flags=0): + # type: (str, int) -> Pattern[Text] + return re.compile(to_text(string), re.UNICODE | extra_flags) + + +_newline = make_regex(r"(\r\n|\n|\r)") +_multiline_whitespace = make_regex(r"\s*", extra_flags=re.MULTILINE) +_whitespace = make_regex(r"[^\S\r\n]*") +_export = make_regex(r"(?:export[^\S\r\n]+)?") +_single_quoted_key = make_regex(r"'([^']+)'") +_unquoted_key = make_regex(r"([^=\#\s]+)") +_equal_sign = make_regex(r"(=[^\S\r\n]*)") +_single_quoted_value = make_regex(r"'((?:\\'|[^'])*)'") +_double_quoted_value = make_regex(r'"((?:\\"|[^"])*)"') +_unquoted_value_part = make_regex(r"([^ \r\n]*)") +_comment = make_regex(r"(?:[^\S\r\n]*#[^\r\n]*)?") +_end_of_line = make_regex(r"[^\S\r\n]*(?:\r\n|\n|\r|$)") +_rest_of_line = make_regex(r"[^\r\n]*(?:\r|\n|\r\n)?") +_double_quote_escapes = make_regex(r"\\[\\'\"abfnrtv]") +_single_quote_escapes = make_regex(r"\\[\\']") + + +try: + # this is necessary because we only import these from typing + # when we are type checking, and the linter is upset if we + # re-import + import typing + + Original = typing.NamedTuple( + "Original", + [ + ("string", typing.Text), + ("line", int), + ], + ) + + Binding = typing.NamedTuple( + "Binding", + [ + ("key", typing.Optional[typing.Text]), + ("value", typing.Optional[typing.Text]), + ("original", Original), + ("error", bool), + ], + ) +except ImportError: + from collections import namedtuple + Original = namedtuple( # type: ignore + "Original", + [ + "string", + "line", + ], + ) + Binding = namedtuple( # type: ignore + "Binding", + [ + "key", + "value", + "original", + "error", + ], + ) + + +class Position: + def __init__(self, chars, line): + # type: (int, int) -> None + self.chars = chars + self.line = line + + @classmethod + def start(cls): + # type: () -> Position + return cls(chars=0, line=1) + + def set(self, other): + # type: (Position) -> None + self.chars = other.chars + self.line = other.line + + def advance(self, string): + # type: (Text) -> None + self.chars += len(string) + self.line += len(re.findall(_newline, string)) + + +class Error(Exception): + pass + + +class Reader: + def __init__(self, stream): + # type: (IO[Text]) -> None + self.string = stream.read() + self.position = Position.start() + self.mark = Position.start() + + def has_next(self): + # type: () -> bool + return self.position.chars < len(self.string) + + def set_mark(self): + # type: () -> None + self.mark.set(self.position) + + def get_marked(self): + # type: () -> Original + return Original( + string=self.string[self.mark.chars:self.position.chars], + line=self.mark.line, + ) + + def peek(self, count): + # type: (int) -> Text + return self.string[self.position.chars:self.position.chars + count] + + def read(self, count): + # type: (int) -> Text + result = self.string[self.position.chars:self.position.chars + count] + if len(result) < count: + raise Error("read: End of string") + self.position.advance(result) + return result + + def read_regex(self, regex): + # type: (Pattern[Text]) -> Sequence[Text] + match = regex.match(self.string, self.position.chars) + if match is None: + raise Error("read_regex: Pattern not found") + self.position.advance(self.string[match.start():match.end()]) + return match.groups() + + +def decode_escapes(regex, string): + # type: (Pattern[Text], Text) -> Text + def decode_match(match): + # type: (Match[Text]) -> Text + return codecs.decode(match.group(0), 'unicode-escape') # type: ignore + + return regex.sub(decode_match, string) + + +def parse_key(reader): + # type: (Reader) -> Optional[Text] + char = reader.peek(1) + if char == "#": + return None + elif char == "'": + (key,) = reader.read_regex(_single_quoted_key) + else: + (key,) = reader.read_regex(_unquoted_key) + return key + + +def parse_unquoted_value(reader): + # type: (Reader) -> Text + value = u"" + while True: + (part,) = reader.read_regex(_unquoted_value_part) + value += part + after = reader.peek(2) + if len(after) < 2 or after[0] in u"\r\n" or after[1] in u" #\r\n": + return value + value += reader.read(2) + + +def parse_value(reader): + # type: (Reader) -> Text + char = reader.peek(1) + if char == u"'": + (value,) = reader.read_regex(_single_quoted_value) + return decode_escapes(_single_quote_escapes, value) + elif char == u'"': + (value,) = reader.read_regex(_double_quoted_value) + return decode_escapes(_double_quote_escapes, value) + elif char in (u"", u"\n", u"\r"): + return u"" + else: + return parse_unquoted_value(reader) + + +def parse_binding(reader): + # type: (Reader) -> Binding + reader.set_mark() + try: + reader.read_regex(_multiline_whitespace) + if not reader.has_next(): + return Binding( + key=None, + value=None, + original=reader.get_marked(), + error=False, + ) + reader.read_regex(_export) + key = parse_key(reader) + reader.read_regex(_whitespace) + if reader.peek(1) == "=": + reader.read_regex(_equal_sign) + value = parse_value(reader) # type: Optional[Text] + else: + value = None + reader.read_regex(_comment) + reader.read_regex(_end_of_line) + return Binding( + key=key, + value=value, + original=reader.get_marked(), + error=False, + ) + except Error: + reader.read_regex(_rest_of_line) + return Binding( + key=None, + value=None, + original=reader.get_marked(), + error=True, + ) + + +def parse_stream(stream): + # type: (IO[Text]) -> Iterator[Binding] + reader = Reader(stream) + while reader.has_next(): + yield parse_binding(reader) diff --git a/venv/Lib/site-packages/dotenv/py.typed b/venv/Lib/site-packages/dotenv/py.typed new file mode 100644 index 00000000..7632ecf7 --- /dev/null +++ b/venv/Lib/site-packages/dotenv/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561 diff --git a/venv/Lib/site-packages/dotenv/version.py b/venv/Lib/site-packages/dotenv/version.py new file mode 100644 index 00000000..f23a6b39 --- /dev/null +++ b/venv/Lib/site-packages/dotenv/version.py @@ -0,0 +1 @@ +__version__ = "0.13.0" diff --git a/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/INSTALLER b/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/LICENSE b/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/LICENSE new file mode 100644 index 00000000..1e65815c --- /dev/null +++ b/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/LICENSE @@ -0,0 +1,54 @@ +Copyright 2017- Paul Ganssle +Copyright 2017- dateutil contributors (see AUTHORS file) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +The above license applies to all contributions after 2017-12-01, as well as +all contributions that have been re-licensed (see AUTHORS file for the list of +contributors who have re-licensed their code). +-------------------------------------------------------------------------------- +dateutil - Extensions to the standard Python datetime module. + +Copyright (c) 2003-2011 - Gustavo Niemeyer +Copyright (c) 2012-2014 - Tomi Pieviläinen +Copyright (c) 2014-2016 - Yaron de Leeuw +Copyright (c) 2015- - Paul Ganssle +Copyright (c) 2015- - dateutil contributors (see AUTHORS file) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The above BSD License Applies to all code, even that also covered by Apache 2.0. \ No newline at end of file diff --git a/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/METADATA b/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/METADATA new file mode 100644 index 00000000..2e476a14 --- /dev/null +++ b/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/METADATA @@ -0,0 +1,200 @@ +Metadata-Version: 2.1 +Name: python-dateutil +Version: 2.8.1 +Summary: Extensions to the standard Python datetime module +Home-page: https://dateutil.readthedocs.io +Author: Gustavo Niemeyer +Author-email: gustavo@niemeyer.net +Maintainer: Paul Ganssle +Maintainer-email: dateutil@python.org +License: Dual License +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Topic :: Software Development :: Libraries +Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,>=2.7 +Description-Content-Type: text/x-rst +Requires-Dist: six (>=1.5) + +dateutil - powerful extensions to datetime +========================================== + +|pypi| |support| |licence| + +|gitter| |readthedocs| + +|travis| |appveyor| |pipelines| |coverage| + +.. |pypi| image:: https://img.shields.io/pypi/v/python-dateutil.svg?style=flat-square + :target: https://pypi.org/project/python-dateutil/ + :alt: pypi version + +.. |support| image:: https://img.shields.io/pypi/pyversions/python-dateutil.svg?style=flat-square + :target: https://pypi.org/project/python-dateutil/ + :alt: supported Python version + +.. |travis| image:: https://img.shields.io/travis/dateutil/dateutil/master.svg?style=flat-square&label=Travis%20Build + :target: https://travis-ci.org/dateutil/dateutil + :alt: travis build status + +.. |appveyor| image:: https://img.shields.io/appveyor/ci/dateutil/dateutil/master.svg?style=flat-square&logo=appveyor + :target: https://ci.appveyor.com/project/dateutil/dateutil + :alt: appveyor build status + +.. |pipelines| image:: https://dev.azure.com/pythondateutilazure/dateutil/_apis/build/status/dateutil.dateutil?branchName=master + :target: https://dev.azure.com/pythondateutilazure/dateutil/_build/latest?definitionId=1&branchName=master + :alt: azure pipelines build status + +.. |coverage| image:: https://codecov.io/github/dateutil/dateutil/coverage.svg?branch=master + :target: https://codecov.io/github/dateutil/dateutil?branch=master + :alt: Code coverage + +.. |gitter| image:: https://badges.gitter.im/dateutil/dateutil.svg + :alt: Join the chat at https://gitter.im/dateutil/dateutil + :target: https://gitter.im/dateutil/dateutil + +.. |licence| image:: https://img.shields.io/pypi/l/python-dateutil.svg?style=flat-square + :target: https://pypi.org/project/python-dateutil/ + :alt: licence + +.. |readthedocs| image:: https://img.shields.io/readthedocs/dateutil/latest.svg?style=flat-square&label=Read%20the%20Docs + :alt: Read the documentation at https://dateutil.readthedocs.io/en/latest/ + :target: https://dateutil.readthedocs.io/en/latest/ + +The `dateutil` module provides powerful extensions to +the standard `datetime` module, available in Python. + +Installation +============ +`dateutil` can be installed from PyPI using `pip` (note that the package name is +different from the importable name):: + + pip install python-dateutil + +Download +======== +dateutil is available on PyPI +https://pypi.org/project/python-dateutil/ + +The documentation is hosted at: +https://dateutil.readthedocs.io/en/stable/ + +Code +==== +The code and issue tracker are hosted on GitHub: +https://github.com/dateutil/dateutil/ + +Features +======== + +* Computing of relative deltas (next month, next year, + next Monday, last week of month, etc); +* Computing of relative deltas between two given + date and/or datetime objects; +* Computing of dates based on very flexible recurrence rules, + using a superset of the `iCalendar `_ + specification. Parsing of RFC strings is supported as well. +* Generic parsing of dates in almost any string format; +* Timezone (tzinfo) implementations for tzfile(5) format + files (/etc/localtime, /usr/share/zoneinfo, etc), TZ + environment string (in all known formats), iCalendar + format files, given ranges (with help from relative deltas), + local machine timezone, fixed offset timezone, UTC timezone, + and Windows registry-based time zones. +* Internal up-to-date world timezone information based on + Olson's database. +* Computing of Easter Sunday dates for any given year, + using Western, Orthodox or Julian algorithms; +* A comprehensive test suite. + +Quick example +============= +Here's a snapshot, just to give an idea about the power of the +package. For more examples, look at the documentation. + +Suppose you want to know how much time is left, in +years/months/days/etc, before the next easter happening on a +year with a Friday 13th in August, and you want to get today's +date out of the "date" unix system command. Here is the code: + +.. code-block:: python3 + + >>> from dateutil.relativedelta import * + >>> from dateutil.easter import * + >>> from dateutil.rrule import * + >>> from dateutil.parser import * + >>> from datetime import * + >>> now = parse("Sat Oct 11 17:13:46 UTC 2003") + >>> today = now.date() + >>> year = rrule(YEARLY,dtstart=now,bymonth=8,bymonthday=13,byweekday=FR)[0].year + >>> rdelta = relativedelta(easter(year), today) + >>> print("Today is: %s" % today) + Today is: 2003-10-11 + >>> print("Year with next Aug 13th on a Friday is: %s" % year) + Year with next Aug 13th on a Friday is: 2004 + >>> print("How far is the Easter of that year: %s" % rdelta) + How far is the Easter of that year: relativedelta(months=+6) + >>> print("And the Easter of that year is: %s" % (today+rdelta)) + And the Easter of that year is: 2004-04-11 + +Being exactly 6 months ahead was **really** a coincidence :) + +Contributing +============ + +We welcome many types of contributions - bug reports, pull requests (code, infrastructure or documentation fixes). For more information about how to contribute to the project, see the ``CONTRIBUTING.md`` file in the repository. + + +Author +====== +The dateutil module was written by Gustavo Niemeyer +in 2003. + +It is maintained by: + +* Gustavo Niemeyer 2003-2011 +* Tomi Pieviläinen 2012-2014 +* Yaron de Leeuw 2014-2016 +* Paul Ganssle 2015- + +Starting with version 2.4.1, all source and binary distributions will be signed +by a PGP key that has, at the very least, been signed by the key which made the +previous release. A table of release signing keys can be found below: + +=========== ============================ +Releases Signing key fingerprint +=========== ============================ +2.4.1- `6B49 ACBA DCF6 BD1C A206 67AB CD54 FCE3 D964 BEFB`_ (|pgp_mirror|_) +=========== ============================ + + +Contact +======= +Our mailing list is available at `dateutil@python.org `_. As it is hosted by the PSF, it is subject to the `PSF code of +conduct `_. + +License +======= + +All contributions after December 1, 2017 released under dual license - either `Apache 2.0 License `_ or the `BSD 3-Clause License `_. Contributions before December 1, 2017 - except those those explicitly relicensed - are released only under the BSD 3-Clause License. + + +.. _6B49 ACBA DCF6 BD1C A206 67AB CD54 FCE3 D964 BEFB: + https://pgp.mit.edu/pks/lookup?op=vindex&search=0xCD54FCE3D964BEFB + +.. |pgp_mirror| replace:: mirror +.. _pgp_mirror: https://sks-keyservers.net/pks/lookup?op=vindex&search=0xCD54FCE3D964BEFB + + diff --git a/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/RECORD b/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/RECORD new file mode 100644 index 00000000..449f0f79 --- /dev/null +++ b/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/RECORD @@ -0,0 +1,44 @@ +dateutil/__init__.py,sha256=lXElASqwYGwqlrSWSeX19JwF5Be9tNecDa9ebk-0gmk,222 +dateutil/__pycache__/__init__.cpython-36.pyc,, +dateutil/__pycache__/_common.cpython-36.pyc,, +dateutil/__pycache__/_version.cpython-36.pyc,, +dateutil/__pycache__/easter.cpython-36.pyc,, +dateutil/__pycache__/relativedelta.cpython-36.pyc,, +dateutil/__pycache__/rrule.cpython-36.pyc,, +dateutil/__pycache__/tzwin.cpython-36.pyc,, +dateutil/__pycache__/utils.cpython-36.pyc,, +dateutil/_common.py,sha256=77w0yytkrxlYbSn--lDVPUMabUXRR9I3lBv_vQRUqUY,932 +dateutil/_version.py,sha256=U1JNX8P5pUNBtcStwfGyAUIMMHGZXhiTDTVXgAUWxs4,116 +dateutil/easter.py,sha256=0liVsgqSx-NPhaFevOJaYgEbrSu2oQQ2o9m_OEBdc-s,2684 +dateutil/parser/__init__.py,sha256=wWk6GFuxTpjoggCGtgkceJoti4pVjl4_fHQXpNOaSYg,1766 +dateutil/parser/__pycache__/__init__.cpython-36.pyc,, +dateutil/parser/__pycache__/_parser.cpython-36.pyc,, +dateutil/parser/__pycache__/isoparser.cpython-36.pyc,, +dateutil/parser/_parser.py,sha256=F0w8h9txvatnYAmeJ1MMbIAvZHRzy3iFjv-AZqRovNs,58804 +dateutil/parser/isoparser.py,sha256=BeEEqIeqhcgik5Cp1_G5Aztsqayp-MAr3aVqAKo1XRc,13098 +dateutil/relativedelta.py,sha256=GjVxqpAVWnG67rdbf7pkoIlJvQqmju9NSfGCcqblc7U,24904 +dateutil/rrule.py,sha256=dStRcOIj8jul-BurMKguc_IBckY-Qci1K6EYqNW8eUg,66514 +dateutil/tz/__init__.py,sha256=F-Mz13v6jYseklQf9Te9J6nzcLDmq47gORa61K35_FA,444 +dateutil/tz/__pycache__/__init__.cpython-36.pyc,, +dateutil/tz/__pycache__/_common.cpython-36.pyc,, +dateutil/tz/__pycache__/_factories.cpython-36.pyc,, +dateutil/tz/__pycache__/tz.cpython-36.pyc,, +dateutil/tz/__pycache__/win.cpython-36.pyc,, +dateutil/tz/_common.py,sha256=cgzDTANsOXvEc86cYF77EsliuSab8Puwpsl5-bX3_S4,12977 +dateutil/tz/_factories.py,sha256=unb6XQNXrPMveksTCU-Ag8jmVZs4SojoPUcAHpWnrvU,2569 +dateutil/tz/tz.py,sha256=npaGnA2M2LGUUerXzAml9rMM-BE771igYFcFETeC3JE,62851 +dateutil/tz/win.py,sha256=xJszWgSwE1xPx_HJj4ZkepyukC_hNy016WMcXhbRaB8,12935 +dateutil/tzwin.py,sha256=7Ar4vdQCnnM0mKR3MUjbIKsZrBVfHgdwsJZc_mGYRew,59 +dateutil/utils.py,sha256=Agvhi7i3HuJdwHYCe9lDS63l_LNFUUlB2hmR3ZKNYwE,1959 +dateutil/zoneinfo/__init__.py,sha256=KYg0pthCMjcp5MXSEiBJn3nMjZeNZav7rlJw5-tz1S4,5889 +dateutil/zoneinfo/__pycache__/__init__.cpython-36.pyc,, +dateutil/zoneinfo/__pycache__/rebuild.cpython-36.pyc,, +dateutil/zoneinfo/dateutil-zoneinfo.tar.gz,sha256=6bZJKrN3mhnCqMgQgFSllQNNbtld9AnuPaRIXWoSH4o,153315 +dateutil/zoneinfo/rebuild.py,sha256=2uFJQiW3Fl8fVogrSXisJMpLeHI1zGwpvBFF43QdeF0,1719 +python_dateutil-2.8.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +python_dateutil-2.8.1.dist-info/LICENSE,sha256=ugD1Gg2SgjtaHN4n2LW50jIeZ-2NqbwWPv-W1eF-V34,2889 +python_dateutil-2.8.1.dist-info/METADATA,sha256=u7pGPxvY3bP0MsvsWab9OeTybTnbLX011vZxRW12I1Y,7988 +python_dateutil-2.8.1.dist-info/RECORD,, +python_dateutil-2.8.1.dist-info/WHEEL,sha256=8zNYZbwQSXoB9IfXOjPfeNwvAsALAjffgk27FqvCWbo,110 +python_dateutil-2.8.1.dist-info/top_level.txt,sha256=4tjdWkhRZvF7LA_BYe_L9gB2w_p2a-z5y6ArjaRkot8,9 +python_dateutil-2.8.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 diff --git a/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/WHEEL b/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/WHEEL new file mode 100644 index 00000000..8b701e93 --- /dev/null +++ b/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.6) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/top_level.txt b/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/top_level.txt new file mode 100644 index 00000000..66501480 --- /dev/null +++ b/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/top_level.txt @@ -0,0 +1 @@ +dateutil diff --git a/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/zip-safe b/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/zip-safe new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/venv/Lib/site-packages/python_dateutil-2.8.1.dist-info/zip-safe @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/PKG-INFO b/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/PKG-INFO new file mode 100644 index 00000000..f8717ffe --- /dev/null +++ b/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/PKG-INFO @@ -0,0 +1,418 @@ +Metadata-Version: 1.1 +Name: python-decouple +Version: 3.3 +Summary: Strict separation of settings from code. +Home-page: http://github.com/henriquebastos/python-decouple/ +Author: Henrique Bastos +Author-email: henrique@bastos.net +License: MIT +Description: Python Decouple: Strict separation of settings from code + ======================================================== + + *Decouple* helps you to organize your settings so that you can + change parameters without having to redeploy your app. + + It also makes it easy for you to: + + #. store parameters in *ini* or *.env* files; + #. define comprehensive default values; + #. properly convert values to the correct data type; + #. have **only one** configuration module to rule all your instances. + + It was originally designed for Django, but became an independent generic tool + for separating settings from code. + + .. image:: https://img.shields.io/travis/henriquebastos/python-decouple.svg + :target: https://travis-ci.org/henriquebastos/python-decouple + :alt: Build Status + + .. image:: https://landscape.io/github/henriquebastos/python-decouple/master/landscape.png + :target: https://landscape.io/github/henriquebastos/python-decouple/master + :alt: Code Health + + .. image:: https://img.shields.io/pypi/v/python-decouple.svg + :target: https://pypi.python.org/pypi/python-decouple/ + :alt: Latest PyPI version + + + + .. contents:: Summary + + + Why? + ==== + + Web framework's settings stores many different kinds of parameters: + + * Locale and i18n; + * Middlewares and Installed Apps; + * Resource handles to the database, Memcached, and other backing services; + * Credentials to external services such as Amazon S3 or Twitter; + * Per-deploy values such as the canonical hostname for the instance. + + The first 2 are *project settings* the last 3 are *instance settings*. + + You should be able to change *instance settings* without redeploying your app. + + Why not just use environment variables? + --------------------------------------- + + *Envvars* works, but since ``os.environ`` only returns strings, it's tricky. + + Let's say you have an *envvar* ``DEBUG=False``. If you run: + + .. code-block:: python + + if os.environ['DEBUG']: + print True + else: + print False + + It will print **True**, because ``os.environ['DEBUG']`` returns the **string** ``"False"``. + Since it's a non-empty string, it will be evaluated as True. + + *Decouple* provides a solution that doesn't look like a workaround: ``config('DEBUG', cast=bool)``. + + Usage + ===== + + Install: + + .. code-block:: console + + pip install python-decouple + + + Then use it on your ``settings.py``. + + #. Import the ``config`` object: + + .. code-block:: python + + from decouple import config + + #. Retrieve the configuration parameters: + + .. code-block:: python + + SECRET_KEY = config('SECRET_KEY') + DEBUG = config('DEBUG', default=False, cast=bool) + EMAIL_HOST = config('EMAIL_HOST', default='localhost') + EMAIL_PORT = config('EMAIL_PORT', default=25, cast=int) + + Encodings + --------- + Decouple's default encoding is `UTF-8`. + + But you can specify your preferred encoding. + + Since `config` is lazy and only opens the configuration file when it's first needed, you have the chance to change + it's encoding right after import. + + .. code-block:: python + + from decouple import config + config.encoding = 'cp1251' + SECRET_KEY = config('SECRET_KEY') + + If you wish to fallback to your system's default encoding do: + + .. code-block:: python + + import locale + from decouple import config + config.encoding = locale.getpreferredencoding(False) + SECRET_KEY = config('SECRET_KEY') + + Where the settings data are stored? + ----------------------------------- + + *Decouple* supports both *.ini* and *.env* files. + + Ini file + ~~~~~~~~ + + Simply create a ``settings.ini`` next to your configuration module in the form: + + .. code-block:: ini + + [settings] + DEBUG=True + TEMPLATE_DEBUG=%(DEBUG)s + SECRET_KEY=ARANDOMSECRETKEY + DATABASE_URL=mysql://myuser:mypassword@myhost/mydatabase + PERCENTILE=90%% + #COMMENTED=42 + + *Note*: Since ``ConfigParser`` supports *string interpolation*, to represent the character ``%`` you need to escape it as ``%%``. + + Env file + ~~~~~~~~ + + Simply create a ``.env`` text file on your repository's root directory in the form: + + .. code-block:: console + + DEBUG=True + TEMPLATE_DEBUG=True + SECRET_KEY=ARANDOMSECRETKEY + DATABASE_URL=mysql://myuser:mypassword@myhost/mydatabase + PERCENTILE=90% + #COMMENTED=42 + + Example: How do I use it with Django? + ------------------------------------- + + Given that I have a ``.env`` file at my repository root directory, here is a snippet of my ``settings.py``. + + I also recommend using `pathlib `_ + and `dj-database-url `_. + + .. code-block:: python + + # coding: utf-8 + from decouple import config + from unipath import Path + from dj_database_url import parse as db_url + + + BASE_DIR = Path(__file__).parent + + DEBUG = config('DEBUG', default=False, cast=bool) + TEMPLATE_DEBUG = DEBUG + + DATABASES = { + 'default': config( + 'DATABASE_URL', + default='sqlite:///' + BASE_DIR.child('db.sqlite3'), + cast=db_url + ) + } + + TIME_ZONE = 'America/Sao_Paulo' + USE_L10N = True + USE_TZ = True + + SECRET_KEY = config('SECRET_KEY') + + EMAIL_HOST = config('EMAIL_HOST', default='localhost') + EMAIL_PORT = config('EMAIL_PORT', default=25, cast=int) + EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD', default='') + EMAIL_HOST_USER = config('EMAIL_HOST_USER', default='') + EMAIL_USE_TLS = config('EMAIL_USE_TLS', default=False, cast=bool) + + # ... + + Attention with *undefined* parameters + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + On the above example, all configuration parameters except ``SECRET_KEY = config('SECRET_KEY')`` + have a default value to fallback if it does not exist on the ``.env`` file. + + If ``SECRET_KEY`` is not present in the ``.env``, *decouple* will raise an ``UndefinedValueError``. + + This *fail fast* policy helps you avoid chasing misbehaviors when you eventually forget a parameter. + + Overriding config files with environment variables + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Sometimes you may want to change a parameter value without having to edit the ``.ini`` or ``.env`` files. + + Since version 3.0, *decouple* respects the *unix way*. + Therefore environment variables have precedence over config files. + + To override a config parameter you can simply do: + + .. code-block:: console + + DEBUG=True python manage.py + + + How it works? + ============= + + *Decouple* always searches for *Options* in this order: + + #. Environment variables; + #. Repository: ini or .env file; + #. default argument passed to config. + + There are 4 classes doing the magic: + + + - ``Config`` + + Coordinates all the configuration retrieval. + + - ``RepositoryIni`` + + Can read values from ``os.environ`` and ini files, in that order. + + **Note:** Since version 3.0 *decouple* respects unix precedence of environment variables *over* config files. + + - ``RepositoryEnv`` + + Can read values from ``os.environ`` and ``.env`` files. + + **Note:** Since version 3.0 *decouple* respects unix precedence of environment variables *over* config files. + + - ``AutoConfig`` + + This is a *lazy* ``Config`` factory that detects which configuration repository you're using. + + It recursively searches up your configuration module path looking for a + ``settings.ini`` or a ``.env`` file. + + Optionally, it accepts ``search_path`` argument to explicitly define + where the search starts. + + The **config** object is an instance of ``AutoConfig`` that instantiates a ``Config`` with the proper ``Repository`` + on the first time it is used. + + + Understanding the CAST argument + ------------------------------- + + By default, all values returned by ``decouple`` are ``strings``, after all they are + read from ``text files`` or the ``envvars``. + + However, your Python code may expect some other value type, for example: + + * Django's ``DEBUG`` expects a boolean ``True`` or ``False``. + * Django's ``EMAIL_PORT`` expects an ``integer``. + * Django's ``ALLOWED_HOSTS`` expects a ``list`` of hostnames. + * Django's ``SECURE_PROXY_SSL_HEADER`` expects a ``tuple`` with two elements, the name of the header to look for and the required value. + + To meet this need, the ``config`` function accepts a ``cast`` argument which + receives any *callable*, that will be used to *transform* the string value + into something else. + + Let's see some examples for the above mentioned cases: + + .. code-block:: python + + >>> os.environ['DEBUG'] = 'False' + >>> config('DEBUG', cast=bool) + False + + >>> os.environ['EMAIL_PORT'] = '42' + >>> config('EMAIL_PORT', cast=int) + 42 + + >>> os.environ['ALLOWED_HOSTS'] = '.localhost, .herokuapp.com' + >>> config('ALLOWED_HOSTS', cast=lambda v: [s.strip() for s in v.split(',')]) + ['.localhost', '.herokuapp.com'] + + >>> os.environ['SECURE_PROXY_SSL_HEADER'] = 'HTTP_X_FORWARDED_PROTO, https' + >>> config('SECURE_PROXY_SSL_HEADER', cast=Csv(post_process=tuple)) + ('HTTP_X_FORWARDED_PROTO', 'https') + + As you can see, ``cast`` is very flexible. But the last example got a bit complex. + + Built in Csv Helper + ~~~~~~~~~~~~~~~~~~~ + + To address the complexity of the last example, *Decouple* comes with an extensible *Csv helper*. + + Let's improve the last example: + + .. code-block:: python + + >>> from decouple import Csv + >>> os.environ['ALLOWED_HOSTS'] = '.localhost, .herokuapp.com' + >>> config('ALLOWED_HOSTS', cast=Csv()) + ['.localhost', '.herokuapp.com'] + + You can also have a `default` value that must be a string to be processed by `Csv`. + + .. code-block:: python + + >>> from decouple import Csv + >>> config('ALLOWED_HOSTS', default='127.0.0.1', cast=Csv()) + ['127.0.0.1'] + + You can also parametrize the *Csv Helper* to return other types of data. + + .. code-block:: python + + >>> os.environ['LIST_OF_INTEGERS'] = '1,2,3,4,5' + >>> config('LIST_OF_INTEGERS', cast=Csv(int)) + [1, 2, 3, 4, 5] + + >>> os.environ['COMPLEX_STRING'] = '%virtual_env%\t *important stuff*\t trailing spaces ' + >>> csv = Csv(cast=lambda s: s.upper(), delimiter='\t', strip=' %*') + >>> csv(os.environ['COMPLEX_STRING']) + ['VIRTUAL_ENV', 'IMPORTANT STUFF', 'TRAILING SPACES'] + + By default *Csv* returns a ``list``, but you can get a ``tuple`` or whatever you want using the ``post_process`` argument: + + .. code-block:: python + + >>> os.environ['SECURE_PROXY_SSL_HEADER'] = 'HTTP_X_FORWARDED_PROTO, https' + >>> config('SECURE_PROXY_SSL_HEADER', cast=Csv(post_process=tuple)) + ('HTTP_X_FORWARDED_PROTO', 'https') + + + Contribute + ========== + + Your contribution is welcome. + + Setup your development environment: + + .. code-block:: console + + git clone git@github.com:henriquebastos/python-decouple.git + cd python-decouple + python -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt + tox + + *Decouple* supports both Python 2.7 and 3.6. Make sure you have both installed. + + I use `pyenv `_ to + manage multiple Python versions and I described my workspace setup on this article: + `The definitive guide to setup my Python workspace + `_ + + You can submit pull requests and issues for discussion. However I only + consider merging tested code. + + + License + ======= + + The MIT License (MIT) + + Copyright (c) 2017 Henrique Bastos + + 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. + +Platform: any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Framework :: Django +Classifier: Framework :: Flask +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Topic :: Software Development :: Libraries diff --git a/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/SOURCES.txt b/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/SOURCES.txt new file mode 100644 index 00000000..17a939a1 --- /dev/null +++ b/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/SOURCES.txt @@ -0,0 +1,11 @@ +LICENSE +MANIFEST.in +README.rst +decouple.py +setup.cfg +setup.py +python_decouple.egg-info/PKG-INFO +python_decouple.egg-info/SOURCES.txt +python_decouple.egg-info/dependency_links.txt +python_decouple.egg-info/not-zip-safe +python_decouple.egg-info/top_level.txt \ No newline at end of file diff --git a/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/dependency_links.txt b/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/installed-files.txt b/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/installed-files.txt new file mode 100644 index 00000000..fe00016a --- /dev/null +++ b/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/installed-files.txt @@ -0,0 +1,7 @@ +..\__pycache__\decouple.cpython-36.pyc +..\decouple.py +PKG-INFO +SOURCES.txt +dependency_links.txt +not-zip-safe +top_level.txt diff --git a/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/not-zip-safe b/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/not-zip-safe new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/top_level.txt b/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/top_level.txt new file mode 100644 index 00000000..25fe02cb --- /dev/null +++ b/venv/Lib/site-packages/python_decouple-3.3-py3.6.egg-info/top_level.txt @@ -0,0 +1 @@ +decouple diff --git a/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/INSTALLER b/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/LICENSE b/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/LICENSE new file mode 100644 index 00000000..39372fee --- /dev/null +++ b/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/LICENSE @@ -0,0 +1,87 @@ +python-dotenv +Copyright (c) 2014, Saurabh Kumar + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of python-dotenv nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +django-dotenv-rw +Copyright (c) 2013, Ted Tieken + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of django-dotenv nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Original django-dotenv +Copyright (c) 2013, Jacob Kaplan-Moss + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of django-dotenv nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/METADATA b/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/METADATA new file mode 100644 index 00000000..f8d1ad64 --- /dev/null +++ b/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/METADATA @@ -0,0 +1,516 @@ +Metadata-Version: 2.1 +Name: python-dotenv +Version: 0.13.0 +Summary: Add .env support to your django/flask apps in development and deployments +Home-page: http://github.com/theskumar/python-dotenv +Author: Saurabh Kumar +Author-email: me+github@saurabh-kumar.com +License: UNKNOWN +Keywords: environment variables,deployments,settings,env,dotenv,configurations,python +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Topic :: System :: Systems Administration +Classifier: Topic :: Utilities +Classifier: Environment :: Web Environment +Description-Content-Type: text/markdown +Requires-Dist: typing ; python_version < "3.5" +Provides-Extra: cli +Requires-Dist: click (>=5.0) ; extra == 'cli' + +``` + _______ .__ __. ____ ____ + | ____|| \ | | \ \ / / + | |__ | \| | \ \/ / + | __| | . ` | \ / + __ | |____ | |\ | \ / + (__)|_______||__| \__| \__/ +``` +python-dotenv | [![Build Status](https://travis-ci.org/theskumar/python-dotenv.svg?branch=master)](https://travis-ci.org/theskumar/python-dotenv) [![Coverage Status](https://coveralls.io/repos/theskumar/python-dotenv/badge.svg?branch=master)](https://coveralls.io/r/theskumar/python-dotenv?branch=master) [![PyPI version](https://badge.fury.io/py/python-dotenv.svg)](http://badge.fury.io/py/python-dotenv) [![Say Thanks!](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)](https://saythanks.io/to/theskumar) +=============================================================================== + +Reads the key-value pair from `.env` file and adds them to environment +variable. It is great for managing app settings during development and +in production using [12-factor](http://12factor.net/) principles. + +> Do one thing, do it well! + +## Usages + +The easiest and most common usage consists on calling `load_dotenv` when +the application starts, which will load environment variables from a +file named `.env` in the current directory or any of its parents or from +the path specificied; after that, you can just call the +environment-related method you need as provided by `os.getenv`. + +`.env` looks like this: + +```shell +# a comment that will be ignored. +REDIS_ADDRESS=localhost:6379 +MEANING_OF_LIFE=42 +MULTILINE_VAR="hello\nworld" +``` + +You can optionally prefix each line with the word `export`, which is totally ignored by this library, but might allow you to [`source`](https://bash.cyberciti.biz/guide/Source_command) the file in bash. + +``` +export S3_BUCKET=YOURS3BUCKET +export SECRET_KEY=YOURSECRETKEYGOESHERE +``` + +Python-dotenv can interpolate variables using POSIX variable expansion. + +The value of a variable is the first of the values defined in the following list: + +- Value of that variable in the environment. +- Value of that variable in the `.env` file. +- Default value, if provided. +- Empty string. + +Ensure that variables are surrounded with `{}` like `${HOME}` as bare +variables such as `$HOME` are not expanded. + +```shell +CONFIG_PATH=${HOME}/.config/foo +DOMAIN=example.org +EMAIL=admin@${DOMAIN} +DEBUG=${DEBUG:-false} +``` + +## Getting started + +Install the latest version with: + +```shell +pip install -U python-dotenv +``` + +Assuming you have created the `.env` file along-side your settings +module. + + . + ├── .env + └── settings.py + +Add the following code to your `settings.py`: + +```python +# settings.py +from dotenv import load_dotenv +load_dotenv() + +# OR, the same with increased verbosity +load_dotenv(verbose=True) + +# OR, explicitly providing path to '.env' +from pathlib import Path # python3 only +env_path = Path('.') / '.env' +load_dotenv(dotenv_path=env_path) +``` + +At this point, parsed key/value from the `.env` file is now present as +system environment variable and they can be conveniently accessed via +`os.getenv()`: + +```python +# settings.py +import os +SECRET_KEY = os.getenv("EMAIL") +DATABASE_PASSWORD = os.getenv("DATABASE_PASSWORD") +``` + +`load_dotenv` does not override existing System environment variables. To +override, pass `override=True` to `load_dotenv()`. + +`load_dotenv` also accepts `encoding` parameter to open the `.env` file. The default encoding is platform dependent (whatever `locale.getpreferredencoding()` returns), but any encoding supported by Python can be used. See the [codecs](https://docs.python.org/3/library/codecs.html#standard-encodings) module for the list of supported encodings. + +You can use `find_dotenv()` method that will try to find a `.env` file +by (a) guessing where to start using `__file__` or the working directory +-- allowing this to work in non-file contexts such as IPython notebooks +and the REPL, and then (b) walking up the directory tree looking for the +specified file -- called `.env` by default. + +```python +from dotenv import load_dotenv, find_dotenv +load_dotenv(find_dotenv()) +``` + +### In-memory filelikes + +It is possible to not rely on the filesystem to parse filelikes from +other sources (e.g. from a network storage). `load_dotenv` and +`dotenv_values` accepts a filelike `stream`. Just be sure to rewind it +before passing. + +```python +>>> from io import StringIO # Python2: from StringIO import StringIO +>>> from dotenv import dotenv_values +>>> filelike = StringIO('SPAM=EGGS\n') +>>> filelike.seek(0) +>>> parsed = dotenv_values(stream=filelike) +>>> parsed['SPAM'] +'EGGS' +``` + +The returned value is dictionary with key-value pairs. + +`dotenv_values` could be useful if you need to *consume* the envfile but +not *apply* it directly into the system environment. + +### Django + +If you are using Django, you should add the above loader script at the +top of `wsgi.py` and `manage.py`. + + +## IPython Support + +You can use dotenv with IPython. You can either let the dotenv search +for `.env` with `%dotenv` or provide the path to the `.env` file explicitly; see +below for usages. + + %load_ext dotenv + + # Use find_dotenv to locate the file + %dotenv + + # Specify a particular file + %dotenv relative/or/absolute/path/to/.env + + # Use '-o' to indicate override of existing variables + %dotenv -o + + # Use '-v' to turn verbose mode on + %dotenv -v + + +## Command-line Interface + +For command-line support, use the CLI option during installation: + +```shell +pip install -U "python-dotenv[cli]" +``` + +A CLI interface `dotenv` is also included, which helps you manipulate +the `.env` file without manually opening it. The same CLI installed on +remote machine combined with fabric (discussed later) will enable you to +update your settings on a remote server; handy, isn't it! + +``` +Usage: dotenv [OPTIONS] COMMAND [ARGS]... + + This script is used to set, get or unset values from a .env file. + +Options: + -f, --file PATH Location of the .env file, defaults to .env + file in current working directory. + -q, --quote [always|never|auto] + Whether to quote or not the variable values. + Default mode is always. This does not affect + parsing. + --help Show this message and exit. + +Commands: + get Retrive the value for the given key. + list Display all the stored key/value. + run Run command with environment variables from .env file present + set Store the given key/value. + unset Removes the given key. +``` + + +### Setting config on Remote Servers + +We make use of excellent [Fabric](http://www.fabfile.org/) to accomplish +this. Add a config task to your local fabfile; `dotenv_path` is the +location of the absolute path of `.env` file on the remote server. + +```python +# fabfile.py + +import dotenv +from fabric.api import task, run, env + +# absolute path to the location of .env on remote server. +env.dotenv_path = '/opt/myapp/.env' + +@task +def config(action=None, key=None, value=None): + '''Manage project configuration via .env + + e.g: fab config:set,, + fab config:get, + fab config:unset, + fab config:list + ''' + run('touch %(dotenv_path)s' % env) + command = dotenv.get_cli_string(env.dotenv_path, action, key, value) + run(command) +``` + +Usage is designed to mirror the Heroku config API very closely. + +Get all your remote config info with `fab config`: + + $ fab config + foo="bar" + +Set remote config variables with `fab config:set,,`: + + $ fab config:set,hello,world + +Get a single remote config variables with `fab config:get,`: + + $ fab config:get,hello + +Delete a remote config variables with `fab config:unset,`: + + $ fab config:unset,hello + +Thanks entirely to fabric and not one bit to this project, you can chain +commands like so: +`fab config:set,, config:set,,` + + $ fab config:set,hello,world config:set,foo,bar config:set,fizz=buzz + + +## Related Projects + +- [Honcho](https://github.com/nickstenning/honcho) - For managing + Procfile-based applications. +- [django-dotenv](https://github.com/jpadilla/django-dotenv) +- [django-environ](https://github.com/joke2k/django-environ) +- [django-configuration](https://github.com/jezdez/django-configurations) +- [dump-env](https://github.com/sobolevn/dump-env) +- [environs](https://github.com/sloria/environs) +- [dynaconf](https://github.com/rochacbruno/dynaconf) + + +## Acknowledgements + +This project is currently maintained by [Saurabh Kumar](https://saurabh-kumar.com) and [Bertrand Bonnefoy-Claudet](https://github.com/bbc2) and would not +have been possible without the support of these [awesome +people](https://github.com/theskumar/python-dotenv/graphs/contributors). + +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this +project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +*No unreleased change at this time.* + +## [0.13.0] - 2020-04-16 + +### Added + +- Add support for a Bash-like default value in variable expansion (#248 by [@bbc2]). + +## [0.12.0] - 2020-02-28 + +### Changed + +- Use current working directory to find `.env` when bundled by PyInstaller (#213 by + [@gergelyk]). + +### Fixed + +- Fix escaping of quoted values written by `set_key` (#236 by [@bbc2]). +- Fix `dotenv run` crashing on environment variables without values (#237 by [@yannham]). +- Remove warning when last line is empty (#238 by [@bbc2]). + +## [0.11.0] - 2020-02-07 + +### Added + +- Add `interpolate` argument to `load_dotenv` and `dotenv_values` to disable interpolation + (#232 by [@ulyssessouza]). + +### Changed + +- Use logging instead of warnings (#231 by [@bbc2]). + +### Fixed + +- Fix installation in non-UTF-8 environments (#225 by [@altendky]). +- Fix PyPI classifiers (#228 by [@bbc2]). + +## [0.10.5] - 2020-01-19 + +### Fixed + +- Fix handling of malformed lines and lines without a value (#222 by [@bbc2]): + - Don't print warning when key has no value. + - Reject more malformed lines (e.g. "A: B", "a='b',c"). +- Fix handling of lines with just a comment (#224 by [@bbc2]). + +## [0.10.4] - 2020-01-17 + +### Added + +- Make typing optional (#179 by [@techalchemy]). +- Print a warning on malformed line (#211 by [@bbc2]). +- Support keys without a value (#220 by [@ulyssessouza]). + +## 0.10.3 + +- Improve interactive mode detection ([@andrewsmith])([#183]). +- Refactor parser to fix parsing inconsistencies ([@bbc2])([#170]). + - Interpret escapes as control characters only in double-quoted strings. + - Interpret `#` as start of comment only if preceded by whitespace. + +## 0.10.2 + +- Add type hints and expose them to users ([@qnighy])([#172]) +- `load_dotenv` and `dotenv_values` now accept an `encoding` parameter, defaults to `None` + ([@theskumar])([@earlbread])([#161]) +- Fix `str`/`unicode` inconsistency in Python 2: values are always `str` now. ([@bbc2])([#121]) +- Fix Unicode error in Python 2, introduced in 0.10.0. ([@bbc2])([#176]) + +## 0.10.1 +- Fix parsing of variable without a value ([@asyncee])([@bbc2])([#158]) + +## 0.10.0 + +- Add support for UTF-8 in unquoted values ([@bbc2])([#148]) +- Add support for trailing comments ([@bbc2])([#148]) +- Add backslashes support in values ([@bbc2])([#148]) +- Add support for newlines in values ([@bbc2])([#148]) +- Force environment variables to str with Python2 on Windows ([@greyli]) +- Drop Python 3.3 support ([@greyli]) +- Fix stderr/-out/-in redirection ([@venthur]) + + +## 0.9.0 + +- Add `--version` parameter to cli ([@venthur]) +- Enable loading from current directory ([@cjauvin]) +- Add 'dotenv run' command for calling arbitrary shell script with .env ([@venthur]) + +## 0.8.1 + +- Add tests for docs ([@Flimm]) +- Make 'cli' support optional. Use `pip install python-dotenv[cli]`. ([@theskumar]) + +## 0.8.0 + +- `set_key` and `unset_key` only modified the affected file instead of + parsing and re-writing file, this causes comments and other file + entact as it is. +- Add support for `export` prefix in the line. +- Internal refractoring ([@theskumar]) +- Allow `load_dotenv` and `dotenv_values` to work with `StringIO())` ([@alanjds])([@theskumar])([#78]) + +## 0.7.1 + +- Remove hard dependency on iPython ([@theskumar]) + +## 0.7.0 + +- Add support to override system environment variable via .env. + ([@milonimrod](https://github.com/milonimrod)) + ([\#63](https://github.com/theskumar/python-dotenv/issues/63)) +- Disable ".env not found" warning by default + ([@maxkoryukov](https://github.com/maxkoryukov)) + ([\#57](https://github.com/theskumar/python-dotenv/issues/57)) + +## 0.6.5 + +- Add support for special characters `\`. + ([@pjona](https://github.com/pjona)) + ([\#60](https://github.com/theskumar/python-dotenv/issues/60)) + +## 0.6.4 + +- Fix issue with single quotes ([@Flimm]) + ([\#52](https://github.com/theskumar/python-dotenv/issues/52)) + +## 0.6.3 + +- Handle unicode exception in setup.py + ([\#46](https://github.com/theskumar/python-dotenv/issues/46)) + +## 0.6.2 + +- Fix dotenv list command ([@ticosax](https://github.com/ticosax)) +- Add iPython Suport + ([@tillahoffmann](https://github.com/tillahoffmann)) + +## 0.6.0 + +- Drop support for Python 2.6 +- Handle escaped charaters and newlines in quoted values. (Thanks + [@iameugenejo](https://github.com/iameugenejo)) +- Remove any spaces around unquoted key/value. (Thanks + [@paulochf](https://github.com/paulochf)) +- Added POSIX variable expansion. (Thanks + [@hugochinchilla](https://github.com/hugochinchilla)) + +## 0.5.1 + +- Fix find\_dotenv - it now start search from the file where this + function is called from. + +## 0.5.0 + +- Add `find_dotenv` method that will try to find a `.env` file. + (Thanks [@isms](https://github.com/isms)) + +## 0.4.0 + +- cli: Added `-q/--quote` option to control the behaviour of quotes + around values in `.env`. (Thanks + [@hugochinchilla](https://github.com/hugochinchilla)). +- Improved test coverage. + +[#78]: https://github.com/theskumar/python-dotenv/issues/78 +[#121]: https://github.com/theskumar/python-dotenv/issues/121 +[#148]: https://github.com/theskumar/python-dotenv/issues/148 +[#158]: https://github.com/theskumar/python-dotenv/issues/158 +[#170]: https://github.com/theskumar/python-dotenv/issues/170 +[#172]: https://github.com/theskumar/python-dotenv/issues/172 +[#176]: https://github.com/theskumar/python-dotenv/issues/176 +[#183]: https://github.com/theskumar/python-dotenv/issues/183 + +[@Flimm]: https://github.com/Flimm +[@alanjds]: https://github.com/alanjds +[@altendky]: https://github.com/altendky +[@andrewsmith]: https://github.com/andrewsmith +[@asyncee]: https://github.com/asyncee +[@bbc2]: https://github.com/bbc2 +[@cjauvin]: https://github.com/cjauvin +[@earlbread]: https://github.com/earlbread +[@gergelyk]: https://github.com/gergelyk +[@greyli]: https://github.com/greyli +[@qnighy]: https://github.com/qnighy +[@techalchemy]: https://github.com/techalchemy +[@theskumar]: https://github.com/theskumar +[@ulyssessouza]: https://github.com/ulyssessouza +[@venthur]: https://github.com/venthur +[@yannham]: https://github.com/yannham + +[Unreleased]: https://github.com/theskumar/python-dotenv/compare/v0.13.0...HEAD +[0.13.0]: https://github.com/theskumar/python-dotenv/compare/v0.12.0...v0.13.0 +[0.12.0]: https://github.com/theskumar/python-dotenv/compare/v0.11.0...v0.12.0 +[0.11.0]: https://github.com/theskumar/python-dotenv/compare/v0.10.5...v0.11.0 +[0.10.5]: https://github.com/theskumar/python-dotenv/compare/v0.10.4...v0.10.5 +[0.10.4]: https://github.com/theskumar/python-dotenv/compare/v0.10.3...v0.10.4 + + diff --git a/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/RECORD b/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/RECORD new file mode 100644 index 00000000..bd982a55 --- /dev/null +++ b/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/RECORD @@ -0,0 +1,23 @@ +../../Scripts/dotenv.exe,sha256=dw0T34FXAUzXkNkWW-HgU_p0emUWRHEMu6QuJi593yM,106359 +dotenv/__init__.py,sha256=7Do7SaK79Arfb8Wkyfrn-7wCj2k8VkE6quTyz88a0xc,1339 +dotenv/__pycache__/__init__.cpython-36.pyc,, +dotenv/__pycache__/cli.cpython-36.pyc,, +dotenv/__pycache__/compat.cpython-36.pyc,, +dotenv/__pycache__/ipython.cpython-36.pyc,, +dotenv/__pycache__/main.cpython-36.pyc,, +dotenv/__pycache__/parser.cpython-36.pyc,, +dotenv/__pycache__/version.cpython-36.pyc,, +dotenv/cli.py,sha256=tuz5Y8cgYvY8GULgOQP9VTh1OxTYBxx6sZyToWVRw40,4026 +dotenv/compat.py,sha256=IhMmBmXjtrs7VdKaDKNCjie-TAgDC2EI5p0BSrNfRo8,964 +dotenv/ipython.py,sha256=Olsq1Q8qwEW1xwfTwlqaJ6tjXM7fIgexXltmAsoaTq4,1342 +dotenv/main.py,sha256=GcspkzRjOPAOyuGklW_Tqwe4AZDtaiKVhpBeWu_CQUY,10830 +dotenv/parser.py,sha256=5_qwL94nf6LtH0Tgs_pxlau-8bZQHN9yk8OlQl3qiJ8,6534 +dotenv/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26 +dotenv/version.py,sha256=DgpLNbv0e1LIEOOe54Db8_390i9pelMEFEnsBsNmyhA,23 +python_dotenv-0.13.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +python_dotenv-0.13.0.dist-info/LICENSE,sha256=0nIJqz0WJ4Ko-OOHK5s1PEngksmqRnpkUiiDQH2NEDA,4600 +python_dotenv-0.13.0.dist-info/METADATA,sha256=kRhodSYEgZa7WVGk9YtkDC38N8kkXJ8otxIpliv8Zr0,17107 +python_dotenv-0.13.0.dist-info/RECORD,, +python_dotenv-0.13.0.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 +python_dotenv-0.13.0.dist-info/entry_points.txt,sha256=c80zWSdgWj260RWtcIS4k0kmahyXf4elttqHYusYwdk,61 +python_dotenv-0.13.0.dist-info/top_level.txt,sha256=eyqUH4SHJNr6ahOYlxIunTr4XinE8Z5ajWLdrK3r0D8,7 diff --git a/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/WHEEL b/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/WHEEL new file mode 100644 index 00000000..ef99c6cf --- /dev/null +++ b/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.34.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/entry_points.txt b/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/entry_points.txt new file mode 100644 index 00000000..4b398770 --- /dev/null +++ b/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/entry_points.txt @@ -0,0 +1,4 @@ + + [console_scripts] + dotenv=dotenv.cli:cli + \ No newline at end of file diff --git a/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/top_level.txt b/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/top_level.txt new file mode 100644 index 00000000..fe7c01aa --- /dev/null +++ b/venv/Lib/site-packages/python_dotenv-0.13.0.dist-info/top_level.txt @@ -0,0 +1 @@ +dotenv diff --git a/venv/Lib/site-packages/urllib3-1.25.9.dist-info/INSTALLER b/venv/Lib/site-packages/urllib3-1.25.9.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/Lib/site-packages/urllib3-1.25.9.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/urllib3-1.25.9.dist-info/LICENSE.txt b/venv/Lib/site-packages/urllib3-1.25.9.dist-info/LICENSE.txt new file mode 100644 index 00000000..c89cf27b --- /dev/null +++ b/venv/Lib/site-packages/urllib3-1.25.9.dist-info/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2008-2019 Andrey Petrov and contributors (see CONTRIBUTORS.txt) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/Lib/site-packages/urllib3-1.25.9.dist-info/METADATA b/venv/Lib/site-packages/urllib3-1.25.9.dist-info/METADATA new file mode 100644 index 00000000..12fceb26 --- /dev/null +++ b/venv/Lib/site-packages/urllib3-1.25.9.dist-info/METADATA @@ -0,0 +1,1262 @@ +Metadata-Version: 2.1 +Name: urllib3 +Version: 1.25.9 +Summary: HTTP library with thread-safe connection pooling, file post, and more. +Home-page: https://urllib3.readthedocs.io/ +Author: Andrey Petrov +Author-email: andrey.petrov@shazow.net +License: MIT +Project-URL: Documentation, https://urllib3.readthedocs.io/ +Project-URL: Code, https://github.com/urllib3/urllib3 +Project-URL: Issue tracker, https://github.com/urllib3/urllib3/issues +Keywords: urllib httplib threadsafe filepost http https ssl pooling +Platform: UNKNOWN +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4 +Provides-Extra: brotli +Requires-Dist: brotlipy (>=0.6.0) ; extra == 'brotli' +Provides-Extra: secure +Requires-Dist: certifi ; extra == 'secure' +Requires-Dist: cryptography (>=1.3.4) ; extra == 'secure' +Requires-Dist: idna (>=2.0.0) ; extra == 'secure' +Requires-Dist: pyOpenSSL (>=0.14) ; extra == 'secure' +Requires-Dist: ipaddress ; (python_version == "2.7") and extra == 'secure' +Provides-Extra: socks +Requires-Dist: PySocks (!=1.5.7,<2.0,>=1.5.6) ; extra == 'socks' + +urllib3 +======= + +urllib3 is a powerful, *sanity-friendly* HTTP client for Python. Much of the +Python ecosystem already uses urllib3 and you should too. +urllib3 brings many critical features that are missing from the Python +standard libraries: + +- Thread safety. +- Connection pooling. +- Client-side SSL/TLS verification. +- File uploads with multipart encoding. +- Helpers for retrying requests and dealing with HTTP redirects. +- Support for gzip, deflate, and brotli encoding. +- Proxy support for HTTP and SOCKS. +- 100% test coverage. + +urllib3 is powerful and easy to use:: + + >>> import urllib3 + >>> http = urllib3.PoolManager() + >>> r = http.request('GET', 'http://httpbin.org/robots.txt') + >>> r.status + 200 + >>> r.data + 'User-agent: *\nDisallow: /deny\n' + + +Installing +---------- + +urllib3 can be installed with `pip `_:: + + $ pip install urllib3 + +Alternatively, you can grab the latest source code from `GitHub `_:: + + $ git clone git://github.com/urllib3/urllib3.git + $ python setup.py install + + +Documentation +------------- + +urllib3 has usage and reference documentation at `urllib3.readthedocs.io `_. + + +Contributing +------------ + +urllib3 happily accepts contributions. Please see our +`contributing documentation `_ +for some tips on getting started. + + +Security Disclosures +-------------------- + +To report a security vulnerability, please use the +`Tidelift security contact `_. +Tidelift will coordinate the fix and disclosure with maintainers. + +Maintainers +----------- + +- `@sethmlarson `_ (Seth M. Larson) +- `@pquentin `_ (Quentin Pradet) +- `@theacodes `_ (Thea Flowers) +- `@haikuginger `_ (Jess Shapiro) +- `@lukasa `_ (Cory Benfield) +- `@sigmavirus24 `_ (Ian Stapleton Cordasco) +- `@shazow `_ (Andrey Petrov) + +👋 + + +Sponsorship +----------- + +.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png + :width: 75 + :alt: Tidelift + +.. list-table:: + :widths: 10 100 + + * - |tideliftlogo| + - Professional support for urllib3 is available as part of the `Tidelift + Subscription`_. Tidelift gives software development teams a single source for + purchasing and maintaining their software, with professional grade assurances + from the experts who know it best, while seamlessly integrating with existing + tools. + +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=readme + +If your company benefits from this library, please consider `sponsoring its +development `_. + +Sponsors include: + +- Abbott (2018-2019), sponsored `@sethmlarson `_'s work on urllib3. +- Google Cloud Platform (2018-2019), sponsored `@theacodes `_'s work on urllib3. +- Akamai (2017-2018), sponsored `@haikuginger `_'s work on urllib3 +- Hewlett Packard Enterprise (2016-2017), sponsored `@Lukasa’s `_ work on urllib3. + + +Changes +======= + +1.25.9 (2020-04-16) +------------------- + +* Added ``InvalidProxyConfigurationWarning`` which is raised when + erroneously specifying an HTTPS proxy URL. urllib3 doesn't currently + support connecting to HTTPS proxies but will soon be able to + and we would like users to migrate properly without much breakage. + + See `this GitHub issue `_ + for more information on how to fix your proxy config. (Pull #1851) + +* Drain connection after ``PoolManager`` redirect (Pull #1817) + +* Ensure ``load_verify_locations`` raises ``SSLError`` for all backends (Pull #1812) + +* Rename ``VerifiedHTTPSConnection`` to ``HTTPSConnection`` (Pull #1805) + +* Allow the CA certificate data to be passed as a string (Pull #1804) + +* Raise ``ValueError`` if method contains control characters (Pull #1800) + +* Add ``__repr__`` to ``Timeout`` (Pull #1795) + + +1.25.8 (2020-01-20) +------------------- + +* Drop support for EOL Python 3.4 (Pull #1774) + +* Optimize _encode_invalid_chars (Pull #1787) + + +1.25.7 (2019-11-11) +------------------- + +* Preserve ``chunked`` parameter on retries (Pull #1715, Pull #1734) + +* Allow unset ``SERVER_SOFTWARE`` in App Engine (Pull #1704, Issue #1470) + +* Fix issue where URL fragment was sent within the request target. (Pull #1732) + +* Fix issue where an empty query section in a URL would fail to parse. (Pull #1732) + +* Remove TLS 1.3 support in SecureTransport due to Apple removing support (Pull #1703) + + +1.25.6 (2019-09-24) +------------------- + +* Fix issue where tilde (``~``) characters were incorrectly + percent-encoded in the path. (Pull #1692) + + +1.25.5 (2019-09-19) +------------------- + +* Add mitigation for BPO-37428 affecting Python <3.7.4 and OpenSSL 1.1.1+ which + caused certificate verification to be enabled when using ``cert_reqs=CERT_NONE``. + (Issue #1682) + + +1.25.4 (2019-09-19) +------------------- + +* Propagate Retry-After header settings to subsequent retries. (Pull #1607) + +* Fix edge case where Retry-After header was still respected even when + explicitly opted out of. (Pull #1607) + +* Remove dependency on ``rfc3986`` for URL parsing. + +* Fix issue where URLs containing invalid characters within ``Url.auth`` would + raise an exception instead of percent-encoding those characters. + +* Add support for ``HTTPResponse.auto_close = False`` which makes HTTP responses + work well with BufferedReaders and other ``io`` module features. (Pull #1652) + +* Percent-encode invalid characters in URL for ``HTTPConnectionPool.request()`` (Pull #1673) + + +1.25.3 (2019-05-23) +------------------- + +* Change ``HTTPSConnection`` to load system CA certificates + when ``ca_certs``, ``ca_cert_dir``, and ``ssl_context`` are + unspecified. (Pull #1608, Issue #1603) + +* Upgrade bundled rfc3986 to v1.3.2. (Pull #1609, Issue #1605) + + +1.25.2 (2019-04-28) +------------------- + +* Change ``is_ipaddress`` to not detect IPvFuture addresses. (Pull #1583) + +* Change ``parse_url`` to percent-encode invalid characters within the + path, query, and target components. (Pull #1586) + + +1.25.1 (2019-04-24) +------------------- + +* Add support for Google's ``Brotli`` package. (Pull #1572, Pull #1579) + +* Upgrade bundled rfc3986 to v1.3.1 (Pull #1578) + + +1.25 (2019-04-22) +----------------- + +* Require and validate certificates by default when using HTTPS (Pull #1507) + +* Upgraded ``urllib3.utils.parse_url()`` to be RFC 3986 compliant. (Pull #1487) + +* Added support for ``key_password`` for ``HTTPSConnectionPool`` to use + encrypted ``key_file`` without creating your own ``SSLContext`` object. (Pull #1489) + +* Add TLSv1.3 support to CPython, pyOpenSSL, and SecureTransport ``SSLContext`` + implementations. (Pull #1496) + +* Switched the default multipart header encoder from RFC 2231 to HTML 5 working draft. (Issue #303, PR #1492) + +* Fixed issue where OpenSSL would block if an encrypted client private key was + given and no password was given. Instead an ``SSLError`` is raised. (Pull #1489) + +* Added support for Brotli content encoding. It is enabled automatically if + ``brotlipy`` package is installed which can be requested with + ``urllib3[brotli]`` extra. (Pull #1532) + +* Drop ciphers using DSS key exchange from default TLS cipher suites. + Improve default ciphers when using SecureTransport. (Pull #1496) + +* Implemented a more efficient ``HTTPResponse.__iter__()`` method. (Issue #1483) + +1.24.3 (2019-05-01) +------------------- + +* Apply fix for CVE-2019-9740. (Pull #1591) + +1.24.2 (2019-04-17) +------------------- + +* Don't load system certificates by default when any other ``ca_certs``, ``ca_certs_dir`` or + ``ssl_context`` parameters are specified. + +* Remove Authorization header regardless of case when redirecting to cross-site. (Issue #1510) + +* Add support for IPv6 addresses in subjectAltName section of certificates. (Issue #1269) + + +1.24.1 (2018-11-02) +------------------- + +* Remove quadratic behavior within ``GzipDecoder.decompress()`` (Issue #1467) + +* Restored functionality of ``ciphers`` parameter for ``create_urllib3_context()``. (Issue #1462) + + +1.24 (2018-10-16) +----------------- + +* Allow key_server_hostname to be specified when initializing a PoolManager to allow custom SNI to be overridden. (Pull #1449) + +* Test against Python 3.7 on AppVeyor. (Pull #1453) + +* Early-out ipv6 checks when running on App Engine. (Pull #1450) + +* Change ambiguous description of backoff_factor (Pull #1436) + +* Add ability to handle multiple Content-Encodings (Issue #1441 and Pull #1442) + +* Skip DNS names that can't be idna-decoded when using pyOpenSSL (Issue #1405). + +* Add a server_hostname parameter to HTTPSConnection which allows for + overriding the SNI hostname sent in the handshake. (Pull #1397) + +* Drop support for EOL Python 2.6 (Pull #1429 and Pull #1430) + +* Fixed bug where responses with header Content-Type: message/* erroneously + raised HeaderParsingError, resulting in a warning being logged. (Pull #1439) + +* Move urllib3 to src/urllib3 (Pull #1409) + + +1.23 (2018-06-04) +----------------- + +* Allow providing a list of headers to strip from requests when redirecting + to a different host. Defaults to the ``Authorization`` header. Different + headers can be set via ``Retry.remove_headers_on_redirect``. (Issue #1316) + +* Fix ``util.selectors._fileobj_to_fd`` to accept ``long`` (Issue #1247). + +* Dropped Python 3.3 support. (Pull #1242) + +* Put the connection back in the pool when calling stream() or read_chunked() on + a chunked HEAD response. (Issue #1234) + +* Fixed pyOpenSSL-specific ssl client authentication issue when clients + attempted to auth via certificate + chain (Issue #1060) + +* Add the port to the connectionpool connect print (Pull #1251) + +* Don't use the ``uuid`` module to create multipart data boundaries. (Pull #1380) + +* ``read_chunked()`` on a closed response returns no chunks. (Issue #1088) + +* Add Python 2.6 support to ``contrib.securetransport`` (Pull #1359) + +* Added support for auth info in url for SOCKS proxy (Pull #1363) + + +1.22 (2017-07-20) +----------------- + +* Fixed missing brackets in ``HTTP CONNECT`` when connecting to IPv6 address via + IPv6 proxy. (Issue #1222) + +* Made the connection pool retry on ``SSLError``. The original ``SSLError`` + is available on ``MaxRetryError.reason``. (Issue #1112) + +* Drain and release connection before recursing on retry/redirect. Fixes + deadlocks with a blocking connectionpool. (Issue #1167) + +* Fixed compatibility for cookiejar. (Issue #1229) + +* pyopenssl: Use vendored version of ``six``. (Issue #1231) + + +1.21.1 (2017-05-02) +------------------- + +* Fixed SecureTransport issue that would cause long delays in response body + delivery. (Pull #1154) + +* Fixed regression in 1.21 that threw exceptions when users passed the + ``socket_options`` flag to the ``PoolManager``. (Issue #1165) + +* Fixed regression in 1.21 that threw exceptions when users passed the + ``assert_hostname`` or ``assert_fingerprint`` flag to the ``PoolManager``. + (Pull #1157) + + +1.21 (2017-04-25) +----------------- + +* Improved performance of certain selector system calls on Python 3.5 and + later. (Pull #1095) + +* Resolved issue where the PyOpenSSL backend would not wrap SysCallError + exceptions appropriately when sending data. (Pull #1125) + +* Selectors now detects a monkey-patched select module after import for modules + that patch the select module like eventlet, greenlet. (Pull #1128) + +* Reduced memory consumption when streaming zlib-compressed responses + (as opposed to raw deflate streams). (Pull #1129) + +* Connection pools now use the entire request context when constructing the + pool key. (Pull #1016) + +* ``PoolManager.connection_from_*`` methods now accept a new keyword argument, + ``pool_kwargs``, which are merged with the existing ``connection_pool_kw``. + (Pull #1016) + +* Add retry counter for ``status_forcelist``. (Issue #1147) + +* Added ``contrib`` module for using SecureTransport on macOS: + ``urllib3.contrib.securetransport``. (Pull #1122) + +* urllib3 now only normalizes the case of ``http://`` and ``https://`` schemes: + for schemes it does not recognise, it assumes they are case-sensitive and + leaves them unchanged. + (Issue #1080) + + +1.20 (2017-01-19) +----------------- + +* Added support for waiting for I/O using selectors other than select, + improving urllib3's behaviour with large numbers of concurrent connections. + (Pull #1001) + +* Updated the date for the system clock check. (Issue #1005) + +* ConnectionPools now correctly consider hostnames to be case-insensitive. + (Issue #1032) + +* Outdated versions of PyOpenSSL now cause the PyOpenSSL contrib module + to fail when it is injected, rather than at first use. (Pull #1063) + +* Outdated versions of cryptography now cause the PyOpenSSL contrib module + to fail when it is injected, rather than at first use. (Issue #1044) + +* Automatically attempt to rewind a file-like body object when a request is + retried or redirected. (Pull #1039) + +* Fix some bugs that occur when modules incautiously patch the queue module. + (Pull #1061) + +* Prevent retries from occurring on read timeouts for which the request method + was not in the method whitelist. (Issue #1059) + +* Changed the PyOpenSSL contrib module to lazily load idna to avoid + unnecessarily bloating the memory of programs that don't need it. (Pull + #1076) + +* Add support for IPv6 literals with zone identifiers. (Pull #1013) + +* Added support for socks5h:// and socks4a:// schemes when working with SOCKS + proxies, and controlled remote DNS appropriately. (Issue #1035) + + +1.19.1 (2016-11-16) +------------------- + +* Fixed AppEngine import that didn't function on Python 3.5. (Pull #1025) + + +1.19 (2016-11-03) +----------------- + +* urllib3 now respects Retry-After headers on 413, 429, and 503 responses when + using the default retry logic. (Pull #955) + +* Remove markers from setup.py to assist ancient setuptools versions. (Issue + #986) + +* Disallow superscripts and other integerish things in URL ports. (Issue #989) + +* Allow urllib3's HTTPResponse.stream() method to continue to work with + non-httplib underlying FPs. (Pull #990) + +* Empty filenames in multipart headers are now emitted as such, rather than + being suppressed. (Issue #1015) + +* Prefer user-supplied Host headers on chunked uploads. (Issue #1009) + + +1.18.1 (2016-10-27) +------------------- + +* CVE-2016-9015. Users who are using urllib3 version 1.17 or 1.18 along with + PyOpenSSL injection and OpenSSL 1.1.0 *must* upgrade to this version. This + release fixes a vulnerability whereby urllib3 in the above configuration + would silently fail to validate TLS certificates due to erroneously setting + invalid flags in OpenSSL's ``SSL_CTX_set_verify`` function. These erroneous + flags do not cause a problem in OpenSSL versions before 1.1.0, which + interprets the presence of any flag as requesting certificate validation. + + There is no PR for this patch, as it was prepared for simultaneous disclosure + and release. The master branch received the same fix in PR #1010. + + +1.18 (2016-09-26) +----------------- + +* Fixed incorrect message for IncompleteRead exception. (PR #973) + +* Accept ``iPAddress`` subject alternative name fields in TLS certificates. + (Issue #258) + +* Fixed consistency of ``HTTPResponse.closed`` between Python 2 and 3. + (Issue #977) + +* Fixed handling of wildcard certificates when using PyOpenSSL. (Issue #979) + + +1.17 (2016-09-06) +----------------- + +* Accept ``SSLContext`` objects for use in SSL/TLS negotiation. (Issue #835) + +* ConnectionPool debug log now includes scheme, host, and port. (Issue #897) + +* Substantially refactored documentation. (Issue #887) + +* Used URLFetch default timeout on AppEngine, rather than hardcoding our own. + (Issue #858) + +* Normalize the scheme and host in the URL parser (Issue #833) + +* ``HTTPResponse`` contains the last ``Retry`` object, which now also + contains retries history. (Issue #848) + +* Timeout can no longer be set as boolean, and must be greater than zero. + (PR #924) + +* Removed pyasn1 and ndg-httpsclient from dependencies used for PyOpenSSL. We + now use cryptography and idna, both of which are already dependencies of + PyOpenSSL. (PR #930) + +* Fixed infinite loop in ``stream`` when amt=None. (Issue #928) + +* Try to use the operating system's certificates when we are using an + ``SSLContext``. (PR #941) + +* Updated cipher suite list to allow ChaCha20+Poly1305. AES-GCM is preferred to + ChaCha20, but ChaCha20 is then preferred to everything else. (PR #947) + +* Updated cipher suite list to remove 3DES-based cipher suites. (PR #958) + +* Removed the cipher suite fallback to allow HIGH ciphers. (PR #958) + +* Implemented ``length_remaining`` to determine remaining content + to be read. (PR #949) + +* Implemented ``enforce_content_length`` to enable exceptions when + incomplete data chunks are received. (PR #949) + +* Dropped connection start, dropped connection reset, redirect, forced retry, + and new HTTPS connection log levels to DEBUG, from INFO. (PR #967) + + +1.16 (2016-06-11) +----------------- + +* Disable IPv6 DNS when IPv6 connections are not possible. (Issue #840) + +* Provide ``key_fn_by_scheme`` pool keying mechanism that can be + overridden. (Issue #830) + +* Normalize scheme and host to lowercase for pool keys, and include + ``source_address``. (Issue #830) + +* Cleaner exception chain in Python 3 for ``_make_request``. + (Issue #861) + +* Fixed installing ``urllib3[socks]`` extra. (Issue #864) + +* Fixed signature of ``ConnectionPool.close`` so it can actually safely be + called by subclasses. (Issue #873) + +* Retain ``release_conn`` state across retries. (Issues #651, #866) + +* Add customizable ``HTTPConnectionPool.ResponseCls``, which defaults to + ``HTTPResponse`` but can be replaced with a subclass. (Issue #879) + + +1.15.1 (2016-04-11) +------------------- + +* Fix packaging to include backports module. (Issue #841) + + +1.15 (2016-04-06) +----------------- + +* Added Retry(raise_on_status=False). (Issue #720) + +* Always use setuptools, no more distutils fallback. (Issue #785) + +* Dropped support for Python 3.2. (Issue #786) + +* Chunked transfer encoding when requesting with ``chunked=True``. + (Issue #790) + +* Fixed regression with IPv6 port parsing. (Issue #801) + +* Append SNIMissingWarning messages to allow users to specify it in + the PYTHONWARNINGS environment variable. (Issue #816) + +* Handle unicode headers in Py2. (Issue #818) + +* Log certificate when there is a hostname mismatch. (Issue #820) + +* Preserve order of request/response headers. (Issue #821) + + +1.14 (2015-12-29) +----------------- + +* contrib: SOCKS proxy support! (Issue #762) + +* Fixed AppEngine handling of transfer-encoding header and bug + in Timeout defaults checking. (Issue #763) + + +1.13.1 (2015-12-18) +------------------- + +* Fixed regression in IPv6 + SSL for match_hostname. (Issue #761) + + +1.13 (2015-12-14) +----------------- + +* Fixed ``pip install urllib3[secure]`` on modern pip. (Issue #706) + +* pyopenssl: Fixed SSL3_WRITE_PENDING error. (Issue #717) + +* pyopenssl: Support for TLSv1.1 and TLSv1.2. (Issue #696) + +* Close connections more defensively on exception. (Issue #734) + +* Adjusted ``read_chunked`` to handle gzipped, chunk-encoded bodies without + repeatedly flushing the decoder, to function better on Jython. (Issue #743) + +* Accept ``ca_cert_dir`` for SSL-related PoolManager configuration. (Issue #758) + + +1.12 (2015-09-03) +----------------- + +* Rely on ``six`` for importing ``httplib`` to work around + conflicts with other Python 3 shims. (Issue #688) + +* Add support for directories of certificate authorities, as supported by + OpenSSL. (Issue #701) + +* New exception: ``NewConnectionError``, raised when we fail to establish + a new connection, usually ``ECONNREFUSED`` socket error. + + +1.11 (2015-07-21) +----------------- + +* When ``ca_certs`` is given, ``cert_reqs`` defaults to + ``'CERT_REQUIRED'``. (Issue #650) + +* ``pip install urllib3[secure]`` will install Certifi and + PyOpenSSL as dependencies. (Issue #678) + +* Made ``HTTPHeaderDict`` usable as a ``headers`` input value + (Issues #632, #679) + +* Added `urllib3.contrib.appengine `_ + which has an ``AppEngineManager`` for using ``URLFetch`` in a + Google AppEngine environment. (Issue #664) + +* Dev: Added test suite for AppEngine. (Issue #631) + +* Fix performance regression when using PyOpenSSL. (Issue #626) + +* Passing incorrect scheme (e.g. ``foo://``) will raise + ``ValueError`` instead of ``AssertionError`` (backwards + compatible for now, but please migrate). (Issue #640) + +* Fix pools not getting replenished when an error occurs during a + request using ``release_conn=False``. (Issue #644) + +* Fix pool-default headers not applying for url-encoded requests + like GET. (Issue #657) + +* log.warning in Python 3 when headers are skipped due to parsing + errors. (Issue #642) + +* Close and discard connections if an error occurs during read. + (Issue #660) + +* Fix host parsing for IPv6 proxies. (Issue #668) + +* Separate warning type SubjectAltNameWarning, now issued once + per host. (Issue #671) + +* Fix ``httplib.IncompleteRead`` not getting converted to + ``ProtocolError`` when using ``HTTPResponse.stream()`` + (Issue #674) + +1.10.4 (2015-05-03) +------------------- + +* Migrate tests to Tornado 4. (Issue #594) + +* Append default warning configuration rather than overwrite. + (Issue #603) + +* Fix streaming decoding regression. (Issue #595) + +* Fix chunked requests losing state across keep-alive connections. + (Issue #599) + +* Fix hanging when chunked HEAD response has no body. (Issue #605) + + +1.10.3 (2015-04-21) +------------------- + +* Emit ``InsecurePlatformWarning`` when SSLContext object is missing. + (Issue #558) + +* Fix regression of duplicate header keys being discarded. + (Issue #563) + +* ``Response.stream()`` returns a generator for chunked responses. + (Issue #560) + +* Set upper-bound timeout when waiting for a socket in PyOpenSSL. + (Issue #585) + +* Work on platforms without `ssl` module for plain HTTP requests. + (Issue #587) + +* Stop relying on the stdlib's default cipher list. (Issue #588) + + +1.10.2 (2015-02-25) +------------------- + +* Fix file descriptor leakage on retries. (Issue #548) + +* Removed RC4 from default cipher list. (Issue #551) + +* Header performance improvements. (Issue #544) + +* Fix PoolManager not obeying redirect retry settings. (Issue #553) + + +1.10.1 (2015-02-10) +------------------- + +* Pools can be used as context managers. (Issue #545) + +* Don't re-use connections which experienced an SSLError. (Issue #529) + +* Don't fail when gzip decoding an empty stream. (Issue #535) + +* Add sha256 support for fingerprint verification. (Issue #540) + +* Fixed handling of header values containing commas. (Issue #533) + + +1.10 (2014-12-14) +----------------- + +* Disabled SSLv3. (Issue #473) + +* Add ``Url.url`` property to return the composed url string. (Issue #394) + +* Fixed PyOpenSSL + gevent ``WantWriteError``. (Issue #412) + +* ``MaxRetryError.reason`` will always be an exception, not string. + (Issue #481) + +* Fixed SSL-related timeouts not being detected as timeouts. (Issue #492) + +* Py3: Use ``ssl.create_default_context()`` when available. (Issue #473) + +* Emit ``InsecureRequestWarning`` for *every* insecure HTTPS request. + (Issue #496) + +* Emit ``SecurityWarning`` when certificate has no ``subjectAltName``. + (Issue #499) + +* Close and discard sockets which experienced SSL-related errors. + (Issue #501) + +* Handle ``body`` param in ``.request(...)``. (Issue #513) + +* Respect timeout with HTTPS proxy. (Issue #505) + +* PyOpenSSL: Handle ZeroReturnError exception. (Issue #520) + + +1.9.1 (2014-09-13) +------------------ + +* Apply socket arguments before binding. (Issue #427) + +* More careful checks if fp-like object is closed. (Issue #435) + +* Fixed packaging issues of some development-related files not + getting included. (Issue #440) + +* Allow performing *only* fingerprint verification. (Issue #444) + +* Emit ``SecurityWarning`` if system clock is waaay off. (Issue #445) + +* Fixed PyOpenSSL compatibility with PyPy. (Issue #450) + +* Fixed ``BrokenPipeError`` and ``ConnectionError`` handling in Py3. + (Issue #443) + + + +1.9 (2014-07-04) +---------------- + +* Shuffled around development-related files. If you're maintaining a distro + package of urllib3, you may need to tweak things. (Issue #415) + +* Unverified HTTPS requests will trigger a warning on the first request. See + our new `security documentation + `_ for details. + (Issue #426) + +* New retry logic and ``urllib3.util.retry.Retry`` configuration object. + (Issue #326) + +* All raised exceptions should now wrapped in a + ``urllib3.exceptions.HTTPException``-extending exception. (Issue #326) + +* All errors during a retry-enabled request should be wrapped in + ``urllib3.exceptions.MaxRetryError``, including timeout-related exceptions + which were previously exempt. Underlying error is accessible from the + ``.reason`` property. (Issue #326) + +* ``urllib3.exceptions.ConnectionError`` renamed to + ``urllib3.exceptions.ProtocolError``. (Issue #326) + +* Errors during response read (such as IncompleteRead) are now wrapped in + ``urllib3.exceptions.ProtocolError``. (Issue #418) + +* Requesting an empty host will raise ``urllib3.exceptions.LocationValueError``. + (Issue #417) + +* Catch read timeouts over SSL connections as + ``urllib3.exceptions.ReadTimeoutError``. (Issue #419) + +* Apply socket arguments before connecting. (Issue #427) + + +1.8.3 (2014-06-23) +------------------ + +* Fix TLS verification when using a proxy in Python 3.4.1. (Issue #385) + +* Add ``disable_cache`` option to ``urllib3.util.make_headers``. (Issue #393) + +* Wrap ``socket.timeout`` exception with + ``urllib3.exceptions.ReadTimeoutError``. (Issue #399) + +* Fixed proxy-related bug where connections were being reused incorrectly. + (Issues #366, #369) + +* Added ``socket_options`` keyword parameter which allows to define + ``setsockopt`` configuration of new sockets. (Issue #397) + +* Removed ``HTTPConnection.tcp_nodelay`` in favor of + ``HTTPConnection.default_socket_options``. (Issue #397) + +* Fixed ``TypeError`` bug in Python 2.6.4. (Issue #411) + + +1.8.2 (2014-04-17) +------------------ + +* Fix ``urllib3.util`` not being included in the package. + + +1.8.1 (2014-04-17) +------------------ + +* Fix AppEngine bug of HTTPS requests going out as HTTP. (Issue #356) + +* Don't install ``dummyserver`` into ``site-packages`` as it's only needed + for the test suite. (Issue #362) + +* Added support for specifying ``source_address``. (Issue #352) + + +1.8 (2014-03-04) +---------------- + +* Improved url parsing in ``urllib3.util.parse_url`` (properly parse '@' in + username, and blank ports like 'hostname:'). + +* New ``urllib3.connection`` module which contains all the HTTPConnection + objects. + +* Several ``urllib3.util.Timeout``-related fixes. Also changed constructor + signature to a more sensible order. [Backwards incompatible] + (Issues #252, #262, #263) + +* Use ``backports.ssl_match_hostname`` if it's installed. (Issue #274) + +* Added ``.tell()`` method to ``urllib3.response.HTTPResponse`` which + returns the number of bytes read so far. (Issue #277) + +* Support for platforms without threading. (Issue #289) + +* Expand default-port comparison in ``HTTPConnectionPool.is_same_host`` + to allow a pool with no specified port to be considered equal to to an + HTTP/HTTPS url with port 80/443 explicitly provided. (Issue #305) + +* Improved default SSL/TLS settings to avoid vulnerabilities. + (Issue #309) + +* Fixed ``urllib3.poolmanager.ProxyManager`` not retrying on connect errors. + (Issue #310) + +* Disable Nagle's Algorithm on the socket for non-proxies. A subset of requests + will send the entire HTTP request ~200 milliseconds faster; however, some of + the resulting TCP packets will be smaller. (Issue #254) + +* Increased maximum number of SubjectAltNames in ``urllib3.contrib.pyopenssl`` + from the default 64 to 1024 in a single certificate. (Issue #318) + +* Headers are now passed and stored as a custom + ``urllib3.collections_.HTTPHeaderDict`` object rather than a plain ``dict``. + (Issue #329, #333) + +* Headers no longer lose their case on Python 3. (Issue #236) + +* ``urllib3.contrib.pyopenssl`` now uses the operating system's default CA + certificates on inject. (Issue #332) + +* Requests with ``retries=False`` will immediately raise any exceptions without + wrapping them in ``MaxRetryError``. (Issue #348) + +* Fixed open socket leak with SSL-related failures. (Issue #344, #348) + + +1.7.1 (2013-09-25) +------------------ + +* Added granular timeout support with new ``urllib3.util.Timeout`` class. + (Issue #231) + +* Fixed Python 3.4 support. (Issue #238) + + +1.7 (2013-08-14) +---------------- + +* More exceptions are now pickle-able, with tests. (Issue #174) + +* Fixed redirecting with relative URLs in Location header. (Issue #178) + +* Support for relative urls in ``Location: ...`` header. (Issue #179) + +* ``urllib3.response.HTTPResponse`` now inherits from ``io.IOBase`` for bonus + file-like functionality. (Issue #187) + +* Passing ``assert_hostname=False`` when creating a HTTPSConnectionPool will + skip hostname verification for SSL connections. (Issue #194) + +* New method ``urllib3.response.HTTPResponse.stream(...)`` which acts as a + generator wrapped around ``.read(...)``. (Issue #198) + +* IPv6 url parsing enforces brackets around the hostname. (Issue #199) + +* Fixed thread race condition in + ``urllib3.poolmanager.PoolManager.connection_from_host(...)`` (Issue #204) + +* ``ProxyManager`` requests now include non-default port in ``Host: ...`` + header. (Issue #217) + +* Added HTTPS proxy support in ``ProxyManager``. (Issue #170 #139) + +* New ``RequestField`` object can be passed to the ``fields=...`` param which + can specify headers. (Issue #220) + +* Raise ``urllib3.exceptions.ProxyError`` when connecting to proxy fails. + (Issue #221) + +* Use international headers when posting file names. (Issue #119) + +* Improved IPv6 support. (Issue #203) + + +1.6 (2013-04-25) +---------------- + +* Contrib: Optional SNI support for Py2 using PyOpenSSL. (Issue #156) + +* ``ProxyManager`` automatically adds ``Host: ...`` header if not given. + +* Improved SSL-related code. ``cert_req`` now optionally takes a string like + "REQUIRED" or "NONE". Same with ``ssl_version`` takes strings like "SSLv23" + The string values reflect the suffix of the respective constant variable. + (Issue #130) + +* Vendored ``socksipy`` now based on Anorov's fork which handles unexpectedly + closed proxy connections and larger read buffers. (Issue #135) + +* Ensure the connection is closed if no data is received, fixes connection leak + on some platforms. (Issue #133) + +* Added SNI support for SSL/TLS connections on Py32+. (Issue #89) + +* Tests fixed to be compatible with Py26 again. (Issue #125) + +* Added ability to choose SSL version by passing an ``ssl.PROTOCOL_*`` constant + to the ``ssl_version`` parameter of ``HTTPSConnectionPool``. (Issue #109) + +* Allow an explicit content type to be specified when encoding file fields. + (Issue #126) + +* Exceptions are now pickleable, with tests. (Issue #101) + +* Fixed default headers not getting passed in some cases. (Issue #99) + +* Treat "content-encoding" header value as case-insensitive, per RFC 2616 + Section 3.5. (Issue #110) + +* "Connection Refused" SocketErrors will get retried rather than raised. + (Issue #92) + +* Updated vendored ``six``, no longer overrides the global ``six`` module + namespace. (Issue #113) + +* ``urllib3.exceptions.MaxRetryError`` contains a ``reason`` property holding + the exception that prompted the final retry. If ``reason is None`` then it + was due to a redirect. (Issue #92, #114) + +* Fixed ``PoolManager.urlopen()`` from not redirecting more than once. + (Issue #149) + +* Don't assume ``Content-Type: text/plain`` for multi-part encoding parameters + that are not files. (Issue #111) + +* Pass `strict` param down to ``httplib.HTTPConnection``. (Issue #122) + +* Added mechanism to verify SSL certificates by fingerprint (md5, sha1) or + against an arbitrary hostname (when connecting by IP or for misconfigured + servers). (Issue #140) + +* Streaming decompression support. (Issue #159) + + +1.5 (2012-08-02) +---------------- + +* Added ``urllib3.add_stderr_logger()`` for quickly enabling STDERR debug + logging in urllib3. + +* Native full URL parsing (including auth, path, query, fragment) available in + ``urllib3.util.parse_url(url)``. + +* Built-in redirect will switch method to 'GET' if status code is 303. + (Issue #11) + +* ``urllib3.PoolManager`` strips the scheme and host before sending the request + uri. (Issue #8) + +* New ``urllib3.exceptions.DecodeError`` exception for when automatic decoding, + based on the Content-Type header, fails. + +* Fixed bug with pool depletion and leaking connections (Issue #76). Added + explicit connection closing on pool eviction. Added + ``urllib3.PoolManager.clear()``. + +* 99% -> 100% unit test coverage. + + +1.4 (2012-06-16) +---------------- + +* Minor AppEngine-related fixes. + +* Switched from ``mimetools.choose_boundary`` to ``uuid.uuid4()``. + +* Improved url parsing. (Issue #73) + +* IPv6 url support. (Issue #72) + + +1.3 (2012-03-25) +---------------- + +* Removed pre-1.0 deprecated API. + +* Refactored helpers into a ``urllib3.util`` submodule. + +* Fixed multipart encoding to support list-of-tuples for keys with multiple + values. (Issue #48) + +* Fixed multiple Set-Cookie headers in response not getting merged properly in + Python 3. (Issue #53) + +* AppEngine support with Py27. (Issue #61) + +* Minor ``encode_multipart_formdata`` fixes related to Python 3 strings vs + bytes. + + +1.2.2 (2012-02-06) +------------------ + +* Fixed packaging bug of not shipping ``test-requirements.txt``. (Issue #47) + + +1.2.1 (2012-02-05) +------------------ + +* Fixed another bug related to when ``ssl`` module is not available. (Issue #41) + +* Location parsing errors now raise ``urllib3.exceptions.LocationParseError`` + which inherits from ``ValueError``. + + +1.2 (2012-01-29) +---------------- + +* Added Python 3 support (tested on 3.2.2) + +* Dropped Python 2.5 support (tested on 2.6.7, 2.7.2) + +* Use ``select.poll`` instead of ``select.select`` for platforms that support + it. + +* Use ``Queue.LifoQueue`` instead of ``Queue.Queue`` for more aggressive + connection reusing. Configurable by overriding ``ConnectionPool.QueueCls``. + +* Fixed ``ImportError`` during install when ``ssl`` module is not available. + (Issue #41) + +* Fixed ``PoolManager`` redirects between schemes (such as HTTP -> HTTPS) not + completing properly. (Issue #28, uncovered by Issue #10 in v1.1) + +* Ported ``dummyserver`` to use ``tornado`` instead of ``webob`` + + ``eventlet``. Removed extraneous unsupported dummyserver testing backends. + Added socket-level tests. + +* More tests. Achievement Unlocked: 99% Coverage. + + +1.1 (2012-01-07) +---------------- + +* Refactored ``dummyserver`` to its own root namespace module (used for + testing). + +* Added hostname verification for ``VerifiedHTTPSConnection`` by vendoring in + Py32's ``ssl_match_hostname``. (Issue #25) + +* Fixed cross-host HTTP redirects when using ``PoolManager``. (Issue #10) + +* Fixed ``decode_content`` being ignored when set through ``urlopen``. (Issue + #27) + +* Fixed timeout-related bugs. (Issues #17, #23) + + +1.0.2 (2011-11-04) +------------------ + +* Fixed typo in ``VerifiedHTTPSConnection`` which would only present as a bug if + you're using the object manually. (Thanks pyos) + +* Made RecentlyUsedContainer (and consequently PoolManager) more thread-safe by + wrapping the access log in a mutex. (Thanks @christer) + +* Made RecentlyUsedContainer more dict-like (corrected ``__delitem__`` and + ``__getitem__`` behaviour), with tests. Shouldn't affect core urllib3 code. + + +1.0.1 (2011-10-10) +------------------ + +* Fixed a bug where the same connection would get returned into the pool twice, + causing extraneous "HttpConnectionPool is full" log warnings. + + +1.0 (2011-10-08) +---------------- + +* Added ``PoolManager`` with LRU expiration of connections (tested and + documented). +* Added ``ProxyManager`` (needs tests, docs, and confirmation that it works + with HTTPS proxies). +* Added optional partial-read support for responses when + ``preload_content=False``. You can now make requests and just read the headers + without loading the content. +* Made response decoding optional (default on, same as before). +* Added optional explicit boundary string for ``encode_multipart_formdata``. +* Convenience request methods are now inherited from ``RequestMethods``. Old + helpers like ``get_url`` and ``post_url`` should be abandoned in favour of + the new ``request(method, url, ...)``. +* Refactored code to be even more decoupled, reusable, and extendable. +* License header added to ``.py`` files. +* Embiggened the documentation: Lots of Sphinx-friendly docstrings in the code + and docs in ``docs/`` and on https://urllib3.readthedocs.io/. +* Embettered all the things! +* Started writing this file. + + +0.4.1 (2011-07-17) +------------------ + +* Minor bug fixes, code cleanup. + + +0.4 (2011-03-01) +---------------- + +* Better unicode support. +* Added ``VerifiedHTTPSConnection``. +* Added ``NTLMConnectionPool`` in contrib. +* Minor improvements. + + +0.3.1 (2010-07-13) +------------------ + +* Added ``assert_host_name`` optional parameter. Now compatible with proxies. + + +0.3 (2009-12-10) +---------------- + +* Added HTTPS support. +* Minor bug fixes. +* Refactored, broken backwards compatibility with 0.2. +* API to be treated as stable from this version forward. + + +0.2 (2008-11-17) +---------------- + +* Added unit tests. +* Bug fixes. + + +0.1 (2008-11-16) +---------------- + +* First release. + + diff --git a/venv/Lib/site-packages/urllib3-1.25.9.dist-info/RECORD b/venv/Lib/site-packages/urllib3-1.25.9.dist-info/RECORD new file mode 100644 index 00000000..6687c5ad --- /dev/null +++ b/venv/Lib/site-packages/urllib3-1.25.9.dist-info/RECORD @@ -0,0 +1,78 @@ +urllib3-1.25.9.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +urllib3-1.25.9.dist-info/LICENSE.txt,sha256=fA0TbuBYU4mt8tJWcbuZaHofdZKfRlt_Fu4_Ado3JV4,1115 +urllib3-1.25.9.dist-info/METADATA,sha256=QVc-HCXpe7Dm_RDmd-GpzKT-LvxBgwsPsLEiE5kUjEI,39852 +urllib3-1.25.9.dist-info/RECORD,, +urllib3-1.25.9.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 +urllib3-1.25.9.dist-info/top_level.txt,sha256=EMiXL2sKrTcmrMxIHTqdc3ET54pQI2Y072LexFEemvo,8 +urllib3/__init__.py,sha256=rdFZCO1L7e8861ZTvo8AiSKwxCe9SnWQUQwJ599YV9c,2683 +urllib3/__pycache__/__init__.cpython-36.pyc,, +urllib3/__pycache__/_collections.cpython-36.pyc,, +urllib3/__pycache__/connection.cpython-36.pyc,, +urllib3/__pycache__/connectionpool.cpython-36.pyc,, +urllib3/__pycache__/exceptions.cpython-36.pyc,, +urllib3/__pycache__/fields.cpython-36.pyc,, +urllib3/__pycache__/filepost.cpython-36.pyc,, +urllib3/__pycache__/poolmanager.cpython-36.pyc,, +urllib3/__pycache__/request.cpython-36.pyc,, +urllib3/__pycache__/response.cpython-36.pyc,, +urllib3/_collections.py,sha256=GouVsNzwg6jADZTmimMI6oqmwKSswnMo9dh5tGNVWO4,10792 +urllib3/connection.py,sha256=Fln8a_bkegdNMkFoSOwyI0PJvL1OqzVUO6ifihKOTpc,14461 +urllib3/connectionpool.py,sha256=egdaX-Db_LVXifDxv3JY0dHIpQqDv0wC0_9Eeh8FkPM,35725 +urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +urllib3/contrib/__pycache__/__init__.cpython-36.pyc,, +urllib3/contrib/__pycache__/_appengine_environ.cpython-36.pyc,, +urllib3/contrib/__pycache__/appengine.cpython-36.pyc,, +urllib3/contrib/__pycache__/ntlmpool.cpython-36.pyc,, +urllib3/contrib/__pycache__/pyopenssl.cpython-36.pyc,, +urllib3/contrib/__pycache__/securetransport.cpython-36.pyc,, +urllib3/contrib/__pycache__/socks.cpython-36.pyc,, +urllib3/contrib/_appengine_environ.py,sha256=bDbyOEhW2CKLJcQqAKAyrEHN-aklsyHFKq6vF8ZFsmk,957 +urllib3/contrib/_securetransport/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +urllib3/contrib/_securetransport/__pycache__/__init__.cpython-36.pyc,, +urllib3/contrib/_securetransport/__pycache__/bindings.cpython-36.pyc,, +urllib3/contrib/_securetransport/__pycache__/low_level.cpython-36.pyc,, +urllib3/contrib/_securetransport/bindings.py,sha256=mullWYFaghBdRWla6HYU-TBgFRTPLBEfxj3jplbeJmQ,16886 +urllib3/contrib/_securetransport/low_level.py,sha256=V7GnujxnWZh2N2sMsV5N4d9Imymokkm3zBwgt77_bSE,11956 +urllib3/contrib/appengine.py,sha256=9RyUW5vKy4VPa2imtwBNWYKILrypr-K6UXEHUYsf0JY,11010 +urllib3/contrib/ntlmpool.py,sha256=a402AwGN_Ll3N-4ur_AS6UrU-ycUtlnYqoBF76lORg8,4160 +urllib3/contrib/pyopenssl.py,sha256=qQKqQXvlSvpCa2yEPxpdv18lS71SMESr9XzH9K9x3KI,16565 +urllib3/contrib/securetransport.py,sha256=vBDFjSnH2gWa-ztMKVaiwW46K1mlDZKqvo_VAonfdcY,32401 +urllib3/contrib/socks.py,sha256=nzDMgDIFJWVubKHqvIn2-SKCO91hhJInP92WgHChGzA,7036 +urllib3/exceptions.py,sha256=D2Jvab7M7m_n0rnmBmq481paoVT32VvVeB6VeQM0y-w,7172 +urllib3/fields.py,sha256=kroD76QK-GdHHW7f_AUN4XxDC3OQPI2FFrS9eSL4BCs,8553 +urllib3/filepost.py,sha256=vj0qbrpT1AFzvvW4SuC8M5kJiw7wftHcSr-7b8UpPpw,2440 +urllib3/packages/__init__.py,sha256=h4BLhD4tLaBx1adaDtKXfupsgqY0wWLXb_f1_yVlV6A,108 +urllib3/packages/__pycache__/__init__.cpython-36.pyc,, +urllib3/packages/__pycache__/six.cpython-36.pyc,, +urllib3/packages/backports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +urllib3/packages/backports/__pycache__/__init__.cpython-36.pyc,, +urllib3/packages/backports/__pycache__/makefile.cpython-36.pyc,, +urllib3/packages/backports/makefile.py,sha256=005wrvH-_pWSnTFqQ2sdzzh4zVCtQUUQ4mR2Yyxwc0A,1418 +urllib3/packages/six.py,sha256=adx4z-eM_D0Vvu0IIqVzFACQ_ux9l64y7DkSEfbxCDs,32536 +urllib3/packages/ssl_match_hostname/__init__.py,sha256=ywgKMtfHi1-DrXlzPfVAhzsLzzqcK7GT6eLgdode1Fg,688 +urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-36.pyc,, +urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-36.pyc,, +urllib3/packages/ssl_match_hostname/_implementation.py,sha256=6dZ-q074g7XhsJ27MFCgkct8iVNZB3sMZvKhf-KUVy0,5679 +urllib3/poolmanager.py,sha256=iWEAIGrVNGoOmQyfiFwCqG-IyYy6GIQ-jJ9QCsX9li4,17861 +urllib3/request.py,sha256=hhoHvEEatyd9Tn5EbGjQ0emn-ENMCyY591yNWTneINA,6018 +urllib3/response.py,sha256=eo1Sfkn2x44FtjgP3qwwDsG9ak84spQAxEGy7Ovd4Pc,28221 +urllib3/util/__init__.py,sha256=bWNaav_OT-1L7-sxm59cGb59rDORlbhb_4noduM5m0U,1038 +urllib3/util/__pycache__/__init__.cpython-36.pyc,, +urllib3/util/__pycache__/connection.cpython-36.pyc,, +urllib3/util/__pycache__/queue.cpython-36.pyc,, +urllib3/util/__pycache__/request.cpython-36.pyc,, +urllib3/util/__pycache__/response.cpython-36.pyc,, +urllib3/util/__pycache__/retry.cpython-36.pyc,, +urllib3/util/__pycache__/ssl_.cpython-36.pyc,, +urllib3/util/__pycache__/timeout.cpython-36.pyc,, +urllib3/util/__pycache__/url.cpython-36.pyc,, +urllib3/util/__pycache__/wait.cpython-36.pyc,, +urllib3/util/connection.py,sha256=NsxUAKQ98GKywta--zg57CdVpeTCI6N-GElCq78Dl8U,4637 +urllib3/util/queue.py,sha256=myTX3JDHntglKQNBf3b6dasHH-uF-W59vzGSQiFdAfI,497 +urllib3/util/request.py,sha256=C-6-AWffxZG03AdRGoY59uqsn4CVItKU6gjxz7Hc3Mc,3815 +urllib3/util/response.py,sha256=_WbTQr8xRQuJuY2rTIZxVdJD6mnEOtQupjaK_bF_Vj8,2573 +urllib3/util/retry.py,sha256=3wbv7SdzYNOxPcBiFkPCubTbK1_6vWSepznOXirhUfA,15543 +urllib3/util/ssl_.py,sha256=R64MEN6Bh-YJq8b14kCb6hbV8L1p8oq4rcZiBow3tTQ,14511 +urllib3/util/timeout.py,sha256=3qawUo-TZq4q7tyeRToMIOdNGEOBjOOQVq7nHnLryP4,9947 +urllib3/util/url.py,sha256=jvkBGN64wo_Mx6Q6JYpFCGxamxbI2NdFoNQVTr7PUOM,13964 +urllib3/util/wait.py,sha256=k46KzqIYu3Vnzla5YW3EvtInNlU_QycFqQAghIOxoAg,5406 diff --git a/venv/Lib/site-packages/urllib3-1.25.9.dist-info/WHEEL b/venv/Lib/site-packages/urllib3-1.25.9.dist-info/WHEEL new file mode 100644 index 00000000..ef99c6cf --- /dev/null +++ b/venv/Lib/site-packages/urllib3-1.25.9.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.34.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/urllib3-1.25.9.dist-info/top_level.txt b/venv/Lib/site-packages/urllib3-1.25.9.dist-info/top_level.txt new file mode 100644 index 00000000..a42590be --- /dev/null +++ b/venv/Lib/site-packages/urllib3-1.25.9.dist-info/top_level.txt @@ -0,0 +1 @@ +urllib3 diff --git a/venv/Lib/site-packages/urllib3/__init__.py b/venv/Lib/site-packages/urllib3/__init__.py new file mode 100644 index 00000000..667e9bce --- /dev/null +++ b/venv/Lib/site-packages/urllib3/__init__.py @@ -0,0 +1,86 @@ +""" +urllib3 - Thread-safe connection pooling and re-using. +""" +from __future__ import absolute_import +import warnings + +from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, connection_from_url + +from . import exceptions +from .filepost import encode_multipart_formdata +from .poolmanager import PoolManager, ProxyManager, proxy_from_url +from .response import HTTPResponse +from .util.request import make_headers +from .util.url import get_host +from .util.timeout import Timeout +from .util.retry import Retry + + +# Set default logging handler to avoid "No handler found" warnings. +import logging +from logging import NullHandler + +__author__ = "Andrey Petrov (andrey.petrov@shazow.net)" +__license__ = "MIT" +__version__ = "1.25.9" + +__all__ = ( + "HTTPConnectionPool", + "HTTPSConnectionPool", + "PoolManager", + "ProxyManager", + "HTTPResponse", + "Retry", + "Timeout", + "add_stderr_logger", + "connection_from_url", + "disable_warnings", + "encode_multipart_formdata", + "get_host", + "make_headers", + "proxy_from_url", +) + +logging.getLogger(__name__).addHandler(NullHandler()) + + +def add_stderr_logger(level=logging.DEBUG): + """ + Helper for quickly adding a StreamHandler to the logger. Useful for + debugging. + + Returns the handler after adding it. + """ + # This method needs to be in this __init__.py to get the __name__ correct + # even if urllib3 is vendored within another package. + logger = logging.getLogger(__name__) + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) + logger.addHandler(handler) + logger.setLevel(level) + logger.debug("Added a stderr logging handler to logger: %s", __name__) + return handler + + +# ... Clean up. +del NullHandler + + +# All warning filters *must* be appended unless you're really certain that they +# shouldn't be: otherwise, it's very hard for users to use most Python +# mechanisms to silence them. +# SecurityWarning's always go off by default. +warnings.simplefilter("always", exceptions.SecurityWarning, append=True) +# SubjectAltNameWarning's should go off once per host +warnings.simplefilter("default", exceptions.SubjectAltNameWarning, append=True) +# InsecurePlatformWarning's don't vary between requests, so we keep it default. +warnings.simplefilter("default", exceptions.InsecurePlatformWarning, append=True) +# SNIMissingWarnings should go off only once. +warnings.simplefilter("default", exceptions.SNIMissingWarning, append=True) + + +def disable_warnings(category=exceptions.HTTPWarning): + """ + Helper for quickly disabling all urllib3 warnings. + """ + warnings.simplefilter("ignore", category) diff --git a/venv/Lib/site-packages/urllib3/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/urllib3/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89fada26eaf411647b0bb3b80dd1c9a6cd9af15a GIT binary patch literal 2172 zcma)7S#R4$5aywSI(^5r-Ge!tSZ%B#OH!xBig+qk^w}%3tVHXP1)WCJj(6IGh>o%+AhyGrHYqRCm_?{P~xvVfw64PD`GU8g`6^rFOWu;^YiyaXcq^K(vrBx{Th)Any~o$QHRM$c z>)txPn{1tLcpIpj^WLZP-UoES`;cDuK7#d;3zyH#(G}P@GtNxYK+V;@NnN`3tLa_) zX#?!hCcO-m(RF%-Uj5z19ZGWzH8NjX@Ww#`brg|WoC2aD2Z4q!4G&G z3Az6Yi($0vZ1GX2b8!FM%>jDJmXz-BP37ye{Jw~|j}f8cD!k}HtRhHIT7$3`QSf=n zWDpY}{eC1kC6eG#HRb#<3CRFNRzDEYi}6fq#!9;Ax2|G%0!fUH0X^3_IR<}-HwU3k zat0v%VU$SZ%bkEjlq$CP1f&=vQ`=7&dxXhgASPQoAr&xo5Ab93+}%PoitSj7_mUx* zL?`VKWGk~D?{+4oTkX%jXn!pVm?BYBq@oA{+Yog{<`tPkk}VKQ{X}9=!e`L{Zzf7g zKTm^%95L`uhzPL~iExyS>IKnIq=uxYYM_4h^tq4k@(w~b!v?Y z$QP-3hRZZg%~9zTcWADBsv(IwhgxWdjdf}vaO}x%RTqDcfW;tOtcCk54SL6H>|)#+ zE$*R2IPqzwTp77?2yT9rw)-@Je#(@g_Cw()9Sk(Ew2aA4MVM%lVV-N3^d%lObsxyK z-ZNr%GGv^py9@-suGig<_ zvXXE^RzW!I>HKGo?<>!~pEdq9+^)FZ6h0UmebAl^>HnDmZ; zZl4SR*!Dm|_$9e@)SWhV*Y|@kkiOrJ$6^WneyXsb@M2V|ren^VRa|v5D-m`=#z_Y; zULtV}AXIk|$-h-D;C@JTErMxoXGAFZa7qMJ*b z|5rb?Zf0fQr%?}UU-kWdis(cf2z83Mfh23b9%5=_UE<8ub;n+n1FHIQ3i(XQOtBPzW>*9m&zJ2GE>#m zpq>hOWmH$ZQSr>V%OjdH_(s7p*+Wu5Fgw<=Rmb--KGRij%(_)DaoL9Xu2rxX?dFnI JUbOVG{{dpzRy+Uz literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/__pycache__/_collections.cpython-36.pyc b/venv/Lib/site-packages/urllib3/__pycache__/_collections.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f2e48944de639b1e01f480b904a6047208da18c GIT binary patch literal 10665 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 Yvt9-mT+ru?rD6pM^b+KfHy0lLABX|IE&u=k literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/__pycache__/connection.cpython-36.pyc b/venv/Lib/site-packages/urllib3/__pycache__/connection.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..493f1a6e5507bbaaf5b52aa85f31a729138b4b0a GIT binary patch literal 10215 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 O`zvKBKa*7r{r>>TU%Zq6 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/__pycache__/connectionpool.cpython-36.pyc b/venv/Lib/site-packages/urllib3/__pycache__/connectionpool.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4be91a7d002895c9bee0638627f7b0d8caef326f GIT binary patch literal 24019 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 B*ERqE literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/__pycache__/exceptions.cpython-36.pyc b/venv/Lib/site-packages/urllib3/__pycache__/exceptions.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ae44012d120a7597820eb06d321e2e824800e22 GIT binary patch literal 11099 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 Bkrw~} literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/__pycache__/filepost.cpython-36.pyc b/venv/Lib/site-packages/urllib3/__pycache__/filepost.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b4b5fda8d3b48c3898f7604cf601fe56b2f97043 GIT binary patch literal 2733 zcmbtWUvC>l5WiiYFPDp*q;4n#L0PI6VH6umr3#r)C{2+f(Uj6ch2_>h->#E$?q7HJ z@=q6DQlUQaJ@^iM5Ol9R$S|DTcYA_SpUAy3|U^aBR zPQlLvb78GpBZjDq>)bvfe0JIB&U1&)JvX`wyvFPBUgYz90p1sQgD>(6&q=qz=|iJ= z@ffTznj~Lvhbj(IE&6_##8Pj-+RWGcTB!B=$E1|0D1Q%@$q-2Gd+{t4N`K>vfFD#- zJ!*Y+jCb@dY{Nat>$PYvkE8R}^^M!N9FTgp<24a|t#3BxnOTc^RRD95}CJgkp+5n=#eVQCm zc?n&d8Q@S7+1f0*y^k`oW<0raNI{P3D#&#*`Xj{X*;QQ~TN(Y;C})o$Ga(H0;FrRC z>sm-x3l{GP$wnA!)pm+657PwmM6tkEn#Cjb;r0sKX)>KckQ4)NGVXg?trRQ@DUd3* zcDw+B+8O*jaYTEf_-slICdX_*%p4ywKxsGfL~f$eGD@>P7zZO%xrwZt0YBt(BN>PN z(u!s!w@TeP@$+iI77;ATMU-#g)_(D+9}mX@OkPH(&I2)Qht#M|9q_+R<-4#ljle7E zy#imR5xC3=r_YU(IIKY|9zreST4t29rs=joLE(*dn{kmy;enrdr^BwdZCb-`z7< zVY^$tw*{4`QyHn(k13lt_&=slS;$k*WA5WnINk=58BZELdPojwMlw1o^BV9J@|bKC zp@u}D?N4&Dos*q&3Gi}ubyGxQFOj!8sE`6Njf2al_g3xI01I2)YEMP~pE2?>93tOC z!jK@SoTj9FAEv7?Cbl$9`34GHLW1}t-vrVm67PO)Z$5X%?iPGk77(N6kR>Xw0O#TT z@WFLdb_a&~9f&rL4D=u;+qPT+EHMtrUZyD`2w+EYSjhlcw8=r{6099T@6y!_pd0X} zGPaIjjTpeq9GcqBOg#%I^He*T30PFF%#CZXb}-!qUx*Pos28PRH@1fga?dV5E@p6^ zdFDKaSIgNUoII`{8Ci9NeavznoYd3Z2w|JK1^hscLnpu*84OA!7z`l0p`kz_2o|P+ z_7hj?kD-%BJK4c@{UBgN!7!&0xp{fpsnL_FK1JS1 z3p&?+0Dkd`8wXaabxqZsA{Fb-fsNBv3%Gys-RDy~zPP5E^AfAH=vxJJ=G9$@3=uKnpRl^7f_`+ zjQPHN2haQf=QS^mpyif(&ezGZDAJ@q&450J91s5z_9_O%n6rpYEILmbv;lAI0X4En z9Xd}Nv_>V?7a&aYV(#?&qZGhU^!pMEOwIwxElfmD$vRG`9~b_}ZTwT1Kx%Lg_<_we O0zW!mu^oHKZu|v-U%zJn literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/__pycache__/poolmanager.cpython-36.pyc b/venv/Lib/site-packages/urllib3/__pycache__/poolmanager.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be8498c6ff8d52d80db44da87452d261df91774c GIT binary patch literal 13752 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 Ng0|+qW!#+${}=YD?3@4q literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/__pycache__/request.cpython-36.pyc b/venv/Lib/site-packages/urllib3/__pycache__/request.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d93234920a51e90a731d99baac2788c0d581358 GIT binary patch literal 5565 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/_collections.py b/venv/Lib/site-packages/urllib3/_collections.py new file mode 100644 index 00000000..019d1511 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/_collections.py @@ -0,0 +1,336 @@ +from __future__ import absolute_import + +try: + from collections.abc import Mapping, MutableMapping +except ImportError: + from collections import Mapping, MutableMapping +try: + from threading import RLock +except ImportError: # Platform-specific: No threads available + + class RLock: + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_value, traceback): + pass + + +from collections import OrderedDict +from .exceptions import InvalidHeader +from .packages.six import iterkeys, itervalues, PY3 + + +__all__ = ["RecentlyUsedContainer", "HTTPHeaderDict"] + + +_Null = object() + + +class RecentlyUsedContainer(MutableMapping): + """ + Provides a thread-safe dict-like container which maintains up to + ``maxsize`` keys while throwing away the least-recently-used keys beyond + ``maxsize``. + + :param maxsize: + Maximum number of recent elements to retain. + + :param dispose_func: + Every time an item is evicted from the container, + ``dispose_func(value)`` is called. Callback which will get called + """ + + ContainerCls = OrderedDict + + def __init__(self, maxsize=10, dispose_func=None): + self._maxsize = maxsize + self.dispose_func = dispose_func + + self._container = self.ContainerCls() + self.lock = RLock() + + def __getitem__(self, key): + # Re-insert the item, moving it to the end of the eviction line. + with self.lock: + item = self._container.pop(key) + self._container[key] = item + return item + + def __setitem__(self, key, value): + evicted_value = _Null + with self.lock: + # Possibly evict the existing value of 'key' + evicted_value = self._container.get(key, _Null) + self._container[key] = value + + # If we didn't evict an existing value, we might have to evict the + # least recently used item from the beginning of the container. + if len(self._container) > self._maxsize: + _key, evicted_value = self._container.popitem(last=False) + + if self.dispose_func and evicted_value is not _Null: + self.dispose_func(evicted_value) + + def __delitem__(self, key): + with self.lock: + value = self._container.pop(key) + + if self.dispose_func: + self.dispose_func(value) + + def __len__(self): + with self.lock: + return len(self._container) + + def __iter__(self): + raise NotImplementedError( + "Iteration over this class is unlikely to be threadsafe." + ) + + def clear(self): + with self.lock: + # Copy pointers to all values, then wipe the mapping + values = list(itervalues(self._container)) + self._container.clear() + + if self.dispose_func: + for value in values: + self.dispose_func(value) + + def keys(self): + with self.lock: + return list(iterkeys(self._container)) + + +class HTTPHeaderDict(MutableMapping): + """ + :param headers: + An iterable of field-value pairs. Must not contain multiple field names + when compared case-insensitively. + + :param kwargs: + Additional field-value pairs to pass in to ``dict.update``. + + A ``dict`` like container for storing HTTP Headers. + + Field names are stored and compared case-insensitively in compliance with + RFC 7230. Iteration provides the first case-sensitive key seen for each + case-insensitive pair. + + Using ``__setitem__`` syntax overwrites fields that compare equal + case-insensitively in order to maintain ``dict``'s api. For fields that + compare equal, instead create a new ``HTTPHeaderDict`` and use ``.add`` + in a loop. + + If multiple fields that are equal case-insensitively are passed to the + constructor or ``.update``, the behavior is undefined and some will be + lost. + + >>> headers = HTTPHeaderDict() + >>> headers.add('Set-Cookie', 'foo=bar') + >>> headers.add('set-cookie', 'baz=quxx') + >>> headers['content-length'] = '7' + >>> headers['SET-cookie'] + 'foo=bar, baz=quxx' + >>> headers['Content-Length'] + '7' + """ + + def __init__(self, headers=None, **kwargs): + super(HTTPHeaderDict, self).__init__() + self._container = OrderedDict() + if headers is not None: + if isinstance(headers, HTTPHeaderDict): + self._copy_from(headers) + else: + self.extend(headers) + if kwargs: + self.extend(kwargs) + + def __setitem__(self, key, val): + self._container[key.lower()] = [key, val] + return self._container[key.lower()] + + def __getitem__(self, key): + val = self._container[key.lower()] + return ", ".join(val[1:]) + + def __delitem__(self, key): + del self._container[key.lower()] + + def __contains__(self, key): + return key.lower() in self._container + + def __eq__(self, other): + if not isinstance(other, Mapping) and not hasattr(other, "keys"): + return False + if not isinstance(other, type(self)): + other = type(self)(other) + return dict((k.lower(), v) for k, v in self.itermerged()) == dict( + (k.lower(), v) for k, v in other.itermerged() + ) + + def __ne__(self, other): + return not self.__eq__(other) + + if not PY3: # Python 2 + iterkeys = MutableMapping.iterkeys + itervalues = MutableMapping.itervalues + + __marker = object() + + def __len__(self): + return len(self._container) + + def __iter__(self): + # Only provide the originally cased names + for vals in self._container.values(): + yield vals[0] + + def pop(self, key, default=__marker): + """D.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised. + """ + # Using the MutableMapping function directly fails due to the private marker. + # Using ordinary dict.pop would expose the internal structures. + # So let's reinvent the wheel. + try: + value = self[key] + except KeyError: + if default is self.__marker: + raise + return default + else: + del self[key] + return value + + def discard(self, key): + try: + del self[key] + except KeyError: + pass + + def add(self, key, val): + """Adds a (name, value) pair, doesn't overwrite the value if it already + exists. + + >>> headers = HTTPHeaderDict(foo='bar') + >>> headers.add('Foo', 'baz') + >>> headers['foo'] + 'bar, baz' + """ + key_lower = key.lower() + new_vals = [key, val] + # Keep the common case aka no item present as fast as possible + vals = self._container.setdefault(key_lower, new_vals) + if new_vals is not vals: + vals.append(val) + + def extend(self, *args, **kwargs): + """Generic import function for any type of header-like object. + Adapted version of MutableMapping.update in order to insert items + with self.add instead of self.__setitem__ + """ + if len(args) > 1: + raise TypeError( + "extend() takes at most 1 positional " + "arguments ({0} given)".format(len(args)) + ) + other = args[0] if len(args) >= 1 else () + + if isinstance(other, HTTPHeaderDict): + for key, val in other.iteritems(): + self.add(key, val) + elif isinstance(other, Mapping): + for key in other: + self.add(key, other[key]) + elif hasattr(other, "keys"): + for key in other.keys(): + self.add(key, other[key]) + else: + for key, value in other: + self.add(key, value) + + for key, value in kwargs.items(): + self.add(key, value) + + def getlist(self, key, default=__marker): + """Returns a list of all the values for the named field. Returns an + empty list if the key doesn't exist.""" + try: + vals = self._container[key.lower()] + except KeyError: + if default is self.__marker: + return [] + return default + else: + return vals[1:] + + # Backwards compatibility for httplib + getheaders = getlist + getallmatchingheaders = getlist + iget = getlist + + # Backwards compatibility for http.cookiejar + get_all = getlist + + def __repr__(self): + return "%s(%s)" % (type(self).__name__, dict(self.itermerged())) + + def _copy_from(self, other): + for key in other: + val = other.getlist(key) + if isinstance(val, list): + # Don't need to convert tuples + val = list(val) + self._container[key.lower()] = [key] + val + + def copy(self): + clone = type(self)() + clone._copy_from(self) + return clone + + def iteritems(self): + """Iterate over all header lines, including duplicate ones.""" + for key in self: + vals = self._container[key.lower()] + for val in vals[1:]: + yield vals[0], val + + def itermerged(self): + """Iterate over all headers, merging duplicate ones together.""" + for key in self: + val = self._container[key.lower()] + yield val[0], ", ".join(val[1:]) + + def items(self): + return list(self.iteritems()) + + @classmethod + def from_httplib(cls, message): # Python 2 + """Read headers from a Python 2 httplib message object.""" + # python2.7 does not expose a proper API for exporting multiheaders + # efficiently. This function re-reads raw lines from the message + # object and extracts the multiheaders properly. + obs_fold_continued_leaders = (" ", "\t") + headers = [] + + for line in message.headers: + if line.startswith(obs_fold_continued_leaders): + if not headers: + # We received a header line that starts with OWS as described + # in RFC-7230 S3.2.4. This indicates a multiline header, but + # there exists no previous header to which we can attach it. + raise InvalidHeader( + "Header continuation with no previous header: %s" % line + ) + else: + key, value = headers[-1] + headers[-1] = (key, value + " " + line.strip()) + continue + + key, value = line.split(":", 1) + headers.append((key, value.strip())) + + return cls(headers) diff --git a/venv/Lib/site-packages/urllib3/connection.py b/venv/Lib/site-packages/urllib3/connection.py new file mode 100644 index 00000000..6da1cf4b --- /dev/null +++ b/venv/Lib/site-packages/urllib3/connection.py @@ -0,0 +1,423 @@ +from __future__ import absolute_import +import re +import datetime +import logging +import os +import socket +from socket import error as SocketError, timeout as SocketTimeout +import warnings +from .packages import six +from .packages.six.moves.http_client import HTTPConnection as _HTTPConnection +from .packages.six.moves.http_client import HTTPException # noqa: F401 + +try: # Compiled with SSL? + import ssl + + BaseSSLError = ssl.SSLError +except (ImportError, AttributeError): # Platform-specific: No SSL. + ssl = None + + class BaseSSLError(BaseException): + pass + + +try: + # Python 3: not a no-op, we're adding this to the namespace so it can be imported. + ConnectionError = ConnectionError +except NameError: + # Python 2 + class ConnectionError(Exception): + pass + + +from .exceptions import ( + NewConnectionError, + ConnectTimeoutError, + SubjectAltNameWarning, + SystemTimeWarning, +) +from .packages.ssl_match_hostname import match_hostname, CertificateError + +from .util.ssl_ import ( + resolve_cert_reqs, + resolve_ssl_version, + assert_fingerprint, + create_urllib3_context, + ssl_wrap_socket, +) + + +from .util import connection + +from ._collections import HTTPHeaderDict + +log = logging.getLogger(__name__) + +port_by_scheme = {"http": 80, "https": 443} + +# When it comes time to update this value as a part of regular maintenance +# (ie test_recent_date is failing) update it to ~6 months before the current date. +RECENT_DATE = datetime.date(2019, 1, 1) + +_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") + + +class DummyConnection(object): + """Used to detect a failed ConnectionCls import.""" + + pass + + +class HTTPConnection(_HTTPConnection, object): + """ + Based on httplib.HTTPConnection but provides an extra constructor + backwards-compatibility layer between older and newer Pythons. + + Additional keyword parameters are used to configure attributes of the connection. + Accepted parameters include: + + - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool` + - ``source_address``: Set the source address for the current connection. + - ``socket_options``: Set specific options on the underlying socket. If not specified, then + defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling + Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. + + For example, if you wish to enable TCP Keep Alive in addition to the defaults, + you might pass:: + + HTTPConnection.default_socket_options + [ + (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), + ] + + Or you may want to disable the defaults by passing an empty list (e.g., ``[]``). + """ + + default_port = port_by_scheme["http"] + + #: Disable Nagle's algorithm by default. + #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]`` + default_socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)] + + #: Whether this connection verifies the host's certificate. + is_verified = False + + def __init__(self, *args, **kw): + if not six.PY2: + kw.pop("strict", None) + + # Pre-set source_address. + self.source_address = kw.get("source_address") + + #: The socket options provided by the user. If no options are + #: provided, we use the default options. + self.socket_options = kw.pop("socket_options", self.default_socket_options) + + _HTTPConnection.__init__(self, *args, **kw) + + @property + def host(self): + """ + Getter method to remove any trailing dots that indicate the hostname is an FQDN. + + In general, SSL certificates don't include the trailing dot indicating a + fully-qualified domain name, and thus, they don't validate properly when + checked against a domain name that includes the dot. In addition, some + servers may not expect to receive the trailing dot when provided. + + However, the hostname with trailing dot is critical to DNS resolution; doing a + lookup with the trailing dot will properly only resolve the appropriate FQDN, + whereas a lookup without a trailing dot will search the system's search domain + list. Thus, it's important to keep the original host around for use only in + those cases where it's appropriate (i.e., when doing DNS lookup to establish the + actual TCP connection across which we're going to send HTTP requests). + """ + return self._dns_host.rstrip(".") + + @host.setter + def host(self, value): + """ + Setter for the `host` property. + + We assume that only urllib3 uses the _dns_host attribute; httplib itself + only uses `host`, and it seems reasonable that other libraries follow suit. + """ + self._dns_host = value + + def _new_conn(self): + """ Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + extra_kw = {} + if self.source_address: + extra_kw["source_address"] = self.source_address + + if self.socket_options: + extra_kw["socket_options"] = self.socket_options + + try: + conn = connection.create_connection( + (self._dns_host, self.port), self.timeout, **extra_kw + ) + + except SocketTimeout: + raise ConnectTimeoutError( + self, + "Connection to %s timed out. (connect timeout=%s)" + % (self.host, self.timeout), + ) + + except SocketError as e: + raise NewConnectionError( + self, "Failed to establish a new connection: %s" % e + ) + + return conn + + def _prepare_conn(self, conn): + self.sock = conn + # Google App Engine's httplib does not define _tunnel_host + if getattr(self, "_tunnel_host", None): + # TODO: Fix tunnel so it doesn't depend on self.sock state. + self._tunnel() + # Mark this connection as not reusable + self.auto_open = 0 + + def connect(self): + conn = self._new_conn() + self._prepare_conn(conn) + + def putrequest(self, method, url, *args, **kwargs): + """Send a request to the server""" + match = _CONTAINS_CONTROL_CHAR_RE.search(method) + if match: + raise ValueError( + "Method cannot contain non-token characters %r (found at least %r)" + % (method, match.group()) + ) + + return _HTTPConnection.putrequest(self, method, url, *args, **kwargs) + + def request_chunked(self, method, url, body=None, headers=None): + """ + Alternative to the common request method, which sends the + body with chunked encoding and not as one block + """ + headers = HTTPHeaderDict(headers if headers is not None else {}) + skip_accept_encoding = "accept-encoding" in headers + skip_host = "host" in headers + self.putrequest( + method, url, skip_accept_encoding=skip_accept_encoding, skip_host=skip_host + ) + for header, value in headers.items(): + self.putheader(header, value) + if "transfer-encoding" not in headers: + self.putheader("Transfer-Encoding", "chunked") + self.endheaders() + + if body is not None: + stringish_types = six.string_types + (bytes,) + if isinstance(body, stringish_types): + body = (body,) + for chunk in body: + if not chunk: + continue + if not isinstance(chunk, bytes): + chunk = chunk.encode("utf8") + len_str = hex(len(chunk))[2:] + self.send(len_str.encode("utf-8")) + self.send(b"\r\n") + self.send(chunk) + self.send(b"\r\n") + + # After the if clause, to always have a closed body + self.send(b"0\r\n\r\n") + + +class HTTPSConnection(HTTPConnection): + default_port = port_by_scheme["https"] + + cert_reqs = None + ca_certs = None + ca_cert_dir = None + ca_cert_data = None + ssl_version = None + assert_fingerprint = None + + def __init__( + self, + host, + port=None, + key_file=None, + cert_file=None, + key_password=None, + strict=None, + timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + ssl_context=None, + server_hostname=None, + **kw + ): + + HTTPConnection.__init__(self, host, port, strict=strict, timeout=timeout, **kw) + + self.key_file = key_file + self.cert_file = cert_file + self.key_password = key_password + self.ssl_context = ssl_context + self.server_hostname = server_hostname + + # Required property for Google AppEngine 1.9.0 which otherwise causes + # HTTPS requests to go out as HTTP. (See Issue #356) + self._protocol = "https" + + def set_cert( + self, + key_file=None, + cert_file=None, + cert_reqs=None, + key_password=None, + ca_certs=None, + assert_hostname=None, + assert_fingerprint=None, + ca_cert_dir=None, + ca_cert_data=None, + ): + """ + This method should only be called once, before the connection is used. + """ + # If cert_reqs is not provided we'll assume CERT_REQUIRED unless we also + # have an SSLContext object in which case we'll use its verify_mode. + if cert_reqs is None: + if self.ssl_context is not None: + cert_reqs = self.ssl_context.verify_mode + else: + cert_reqs = resolve_cert_reqs(None) + + self.key_file = key_file + self.cert_file = cert_file + self.cert_reqs = cert_reqs + self.key_password = key_password + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + self.ca_certs = ca_certs and os.path.expanduser(ca_certs) + self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) + self.ca_cert_data = ca_cert_data + + def connect(self): + # Add certificate verification + conn = self._new_conn() + hostname = self.host + + # Google App Engine's httplib does not define _tunnel_host + if getattr(self, "_tunnel_host", None): + self.sock = conn + # Calls self._set_hostport(), so self.host is + # self._tunnel_host below. + self._tunnel() + # Mark this connection as not reusable + self.auto_open = 0 + + # Override the host with the one we're requesting data from. + hostname = self._tunnel_host + + server_hostname = hostname + if self.server_hostname is not None: + server_hostname = self.server_hostname + + is_time_off = datetime.date.today() < RECENT_DATE + if is_time_off: + warnings.warn( + ( + "System time is way off (before {0}). This will probably " + "lead to SSL verification errors" + ).format(RECENT_DATE), + SystemTimeWarning, + ) + + # Wrap socket using verification with the root certs in + # trusted_root_certs + default_ssl_context = False + if self.ssl_context is None: + default_ssl_context = True + self.ssl_context = create_urllib3_context( + ssl_version=resolve_ssl_version(self.ssl_version), + cert_reqs=resolve_cert_reqs(self.cert_reqs), + ) + + context = self.ssl_context + context.verify_mode = resolve_cert_reqs(self.cert_reqs) + + # Try to load OS default certs if none are given. + # Works well on Windows (requires Python3.4+) + if ( + not self.ca_certs + and not self.ca_cert_dir + and not self.ca_cert_data + and default_ssl_context + and hasattr(context, "load_default_certs") + ): + context.load_default_certs() + + self.sock = ssl_wrap_socket( + sock=conn, + keyfile=self.key_file, + certfile=self.cert_file, + key_password=self.key_password, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + ca_cert_data=self.ca_cert_data, + server_hostname=server_hostname, + ssl_context=context, + ) + + if self.assert_fingerprint: + assert_fingerprint( + self.sock.getpeercert(binary_form=True), self.assert_fingerprint + ) + elif ( + context.verify_mode != ssl.CERT_NONE + and not getattr(context, "check_hostname", False) + and self.assert_hostname is not False + ): + # While urllib3 attempts to always turn off hostname matching from + # the TLS library, this cannot always be done. So we check whether + # the TLS Library still thinks it's matching hostnames. + cert = self.sock.getpeercert() + if not cert.get("subjectAltName", ()): + warnings.warn( + ( + "Certificate for {0} has no `subjectAltName`, falling back to check for a " + "`commonName` for now. This feature is being removed by major browsers and " + "deprecated by RFC 2818. (See https://github.com/urllib3/urllib3/issues/497 " + "for details.)".format(hostname) + ), + SubjectAltNameWarning, + ) + _match_hostname(cert, self.assert_hostname or server_hostname) + + self.is_verified = ( + context.verify_mode == ssl.CERT_REQUIRED + or self.assert_fingerprint is not None + ) + + +def _match_hostname(cert, asserted_hostname): + try: + match_hostname(cert, asserted_hostname) + except CertificateError as e: + log.warning( + "Certificate did not match expected hostname: %s. Certificate: %s", + asserted_hostname, + cert, + ) + # Add cert to exception and reraise so client code can inspect + # the cert when catching the exception, if they want to + e._peer_cert = cert + raise + + +if not ssl: + HTTPSConnection = DummyConnection # noqa: F811 + + +VerifiedHTTPSConnection = HTTPSConnection diff --git a/venv/Lib/site-packages/urllib3/connectionpool.py b/venv/Lib/site-packages/urllib3/connectionpool.py new file mode 100644 index 00000000..5f044dbd --- /dev/null +++ b/venv/Lib/site-packages/urllib3/connectionpool.py @@ -0,0 +1,1033 @@ +from __future__ import absolute_import +import errno +import logging +import sys +import warnings + +from socket import error as SocketError, timeout as SocketTimeout +import socket + + +from .exceptions import ( + ClosedPoolError, + ProtocolError, + EmptyPoolError, + HeaderParsingError, + HostChangedError, + LocationValueError, + MaxRetryError, + ProxyError, + ReadTimeoutError, + SSLError, + TimeoutError, + InsecureRequestWarning, + NewConnectionError, +) +from .packages.ssl_match_hostname import CertificateError +from .packages import six +from .packages.six.moves import queue +from .connection import ( + port_by_scheme, + DummyConnection, + HTTPConnection, + HTTPSConnection, + VerifiedHTTPSConnection, + HTTPException, + BaseSSLError, +) +from .request import RequestMethods +from .response import HTTPResponse + +from .util.connection import is_connection_dropped +from .util.request import set_file_position +from .util.response import assert_header_parsing +from .util.retry import Retry +from .util.timeout import Timeout +from .util.url import ( + get_host, + parse_url, + Url, + _normalize_host as normalize_host, + _encode_target, +) +from .util.queue import LifoQueue + + +xrange = six.moves.xrange + +log = logging.getLogger(__name__) + +_Default = object() + + +# Pool objects +class ConnectionPool(object): + """ + Base class for all connection pools, such as + :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`. + + .. note:: + ConnectionPool.urlopen() does not normalize or percent-encode target URIs + which is useful if your target server doesn't support percent-encoded + target URIs. + """ + + scheme = None + QueueCls = LifoQueue + + def __init__(self, host, port=None): + if not host: + raise LocationValueError("No host specified.") + + self.host = _normalize_host(host, scheme=self.scheme) + self._proxy_host = host.lower() + self.port = port + + def __str__(self): + return "%s(host=%r, port=%r)" % (type(self).__name__, self.host, self.port) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + # Return False to re-raise any potential exceptions + return False + + def close(self): + """ + Close all pooled connections and disable the pool. + """ + pass + + +# This is taken from http://hg.python.org/cpython/file/7aaba721ebc0/Lib/socket.py#l252 +_blocking_errnos = {errno.EAGAIN, errno.EWOULDBLOCK} + + +class HTTPConnectionPool(ConnectionPool, RequestMethods): + """ + Thread-safe connection pool for one host. + + :param host: + Host used for this HTTP Connection (e.g. "localhost"), passed into + :class:`httplib.HTTPConnection`. + + :param port: + Port used for this HTTP Connection (None is equivalent to 80), passed + into :class:`httplib.HTTPConnection`. + + :param strict: + Causes BadStatusLine to be raised if the status line can't be parsed + as a valid HTTP/1.0 or 1.1 status line, passed into + :class:`httplib.HTTPConnection`. + + .. note:: + Only works in Python 2. This parameter is ignored in Python 3. + + :param timeout: + Socket timeout in seconds for each individual connection. This can + be a float or integer, which sets the timeout for the HTTP request, + or an instance of :class:`urllib3.util.Timeout` which gives you more + fine-grained control over request timeouts. After the constructor has + been parsed, this is always a `urllib3.util.Timeout` object. + + :param maxsize: + Number of connections to save that can be reused. More than 1 is useful + in multithreaded situations. If ``block`` is set to False, more + connections will be created but they will not be saved once they've + been used. + + :param block: + If set to True, no more than ``maxsize`` connections will be used at + a time. When no free connections are available, the call will block + until a connection has been released. This is a useful side effect for + particular multithreaded situations where one does not want to use more + than maxsize connections per host to prevent flooding. + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + + :param retries: + Retry configuration to use by default with requests in this pool. + + :param _proxy: + Parsed proxy URL, should not be used directly, instead, see + :class:`urllib3.connectionpool.ProxyManager`" + + :param _proxy_headers: + A dictionary with proxy headers, should not be used directly, + instead, see :class:`urllib3.connectionpool.ProxyManager`" + + :param \\**conn_kw: + Additional parameters are used to create fresh :class:`urllib3.connection.HTTPConnection`, + :class:`urllib3.connection.HTTPSConnection` instances. + """ + + scheme = "http" + ConnectionCls = HTTPConnection + ResponseCls = HTTPResponse + + def __init__( + self, + host, + port=None, + strict=False, + timeout=Timeout.DEFAULT_TIMEOUT, + maxsize=1, + block=False, + headers=None, + retries=None, + _proxy=None, + _proxy_headers=None, + **conn_kw + ): + ConnectionPool.__init__(self, host, port) + RequestMethods.__init__(self, headers) + + self.strict = strict + + if not isinstance(timeout, Timeout): + timeout = Timeout.from_float(timeout) + + if retries is None: + retries = Retry.DEFAULT + + self.timeout = timeout + self.retries = retries + + self.pool = self.QueueCls(maxsize) + self.block = block + + self.proxy = _proxy + self.proxy_headers = _proxy_headers or {} + + # Fill the queue up so that doing get() on it will block properly + for _ in xrange(maxsize): + self.pool.put(None) + + # These are mostly for testing and debugging purposes. + self.num_connections = 0 + self.num_requests = 0 + self.conn_kw = conn_kw + + if self.proxy: + # Enable Nagle's algorithm for proxies, to avoid packet fragmentation. + # We cannot know if the user has added default socket options, so we cannot replace the + # list. + self.conn_kw.setdefault("socket_options", []) + + def _new_conn(self): + """ + Return a fresh :class:`HTTPConnection`. + """ + self.num_connections += 1 + log.debug( + "Starting new HTTP connection (%d): %s:%s", + self.num_connections, + self.host, + self.port or "80", + ) + + conn = self.ConnectionCls( + host=self.host, + port=self.port, + timeout=self.timeout.connect_timeout, + strict=self.strict, + **self.conn_kw + ) + return conn + + def _get_conn(self, timeout=None): + """ + Get a connection. Will return a pooled connection if one is available. + + If no connections are available and :prop:`.block` is ``False``, then a + fresh connection is returned. + + :param timeout: + Seconds to wait before giving up and raising + :class:`urllib3.exceptions.EmptyPoolError` if the pool is empty and + :prop:`.block` is ``True``. + """ + conn = None + try: + conn = self.pool.get(block=self.block, timeout=timeout) + + except AttributeError: # self.pool is None + raise ClosedPoolError(self, "Pool is closed.") + + except queue.Empty: + if self.block: + raise EmptyPoolError( + self, + "Pool reached maximum size and no more connections are allowed.", + ) + pass # Oh well, we'll create a new connection then + + # If this is a persistent connection, check if it got disconnected + if conn and is_connection_dropped(conn): + log.debug("Resetting dropped connection: %s", self.host) + conn.close() + if getattr(conn, "auto_open", 1) == 0: + # This is a proxied connection that has been mutated by + # httplib._tunnel() and cannot be reused (since it would + # attempt to bypass the proxy) + conn = None + + return conn or self._new_conn() + + def _put_conn(self, conn): + """ + Put a connection back into the pool. + + :param conn: + Connection object for the current host and port as returned by + :meth:`._new_conn` or :meth:`._get_conn`. + + If the pool is already full, the connection is closed and discarded + because we exceeded maxsize. If connections are discarded frequently, + then maxsize should be increased. + + If the pool is closed, then the connection will be closed and discarded. + """ + try: + self.pool.put(conn, block=False) + return # Everything is dandy, done. + except AttributeError: + # self.pool is None. + pass + except queue.Full: + # This should never happen if self.block == True + log.warning("Connection pool is full, discarding connection: %s", self.host) + + # Connection never got put back into the pool, close it. + if conn: + conn.close() + + def _validate_conn(self, conn): + """ + Called right before a request is made, after the socket is created. + """ + pass + + def _prepare_proxy(self, conn): + # Nothing to do for HTTP connections. + pass + + def _get_timeout(self, timeout): + """ Helper that always returns a :class:`urllib3.util.Timeout` """ + if timeout is _Default: + return self.timeout.clone() + + if isinstance(timeout, Timeout): + return timeout.clone() + else: + # User passed us an int/float. This is for backwards compatibility, + # can be removed later + return Timeout.from_float(timeout) + + def _raise_timeout(self, err, url, timeout_value): + """Is the error actually a timeout? Will raise a ReadTimeout or pass""" + + if isinstance(err, SocketTimeout): + raise ReadTimeoutError( + self, url, "Read timed out. (read timeout=%s)" % timeout_value + ) + + # See the above comment about EAGAIN in Python 3. In Python 2 we have + # to specifically catch it and throw the timeout error + if hasattr(err, "errno") and err.errno in _blocking_errnos: + raise ReadTimeoutError( + self, url, "Read timed out. (read timeout=%s)" % timeout_value + ) + + # Catch possible read timeouts thrown as SSL errors. If not the + # case, rethrow the original. We need to do this because of: + # http://bugs.python.org/issue10272 + if "timed out" in str(err) or "did not complete (read)" in str( + err + ): # Python < 2.7.4 + raise ReadTimeoutError( + self, url, "Read timed out. (read timeout=%s)" % timeout_value + ) + + def _make_request( + self, conn, method, url, timeout=_Default, chunked=False, **httplib_request_kw + ): + """ + Perform a request on a given urllib connection object taken from our + pool. + + :param conn: + a connection from one of our connection pools + + :param timeout: + Socket timeout in seconds for the request. This can be a + float or integer, which will set the same timeout value for + the socket connect and the socket read, or an instance of + :class:`urllib3.util.Timeout`, which gives you more fine-grained + control over your timeouts. + """ + self.num_requests += 1 + + timeout_obj = self._get_timeout(timeout) + timeout_obj.start_connect() + conn.timeout = timeout_obj.connect_timeout + + # Trigger any extra validation we need to do. + try: + self._validate_conn(conn) + except (SocketTimeout, BaseSSLError) as e: + # Py2 raises this as a BaseSSLError, Py3 raises it as socket timeout. + self._raise_timeout(err=e, url=url, timeout_value=conn.timeout) + raise + + # conn.request() calls httplib.*.request, not the method in + # urllib3.request. It also calls makefile (recv) on the socket. + if chunked: + conn.request_chunked(method, url, **httplib_request_kw) + else: + conn.request(method, url, **httplib_request_kw) + + # Reset the timeout for the recv() on the socket + read_timeout = timeout_obj.read_timeout + + # App Engine doesn't have a sock attr + if getattr(conn, "sock", None): + # In Python 3 socket.py will catch EAGAIN and return None when you + # try and read into the file pointer created by http.client, which + # instead raises a BadStatusLine exception. Instead of catching + # the exception and assuming all BadStatusLine exceptions are read + # timeouts, check for a zero timeout before making the request. + if read_timeout == 0: + raise ReadTimeoutError( + self, url, "Read timed out. (read timeout=%s)" % read_timeout + ) + if read_timeout is Timeout.DEFAULT_TIMEOUT: + conn.sock.settimeout(socket.getdefaulttimeout()) + else: # None or a value + conn.sock.settimeout(read_timeout) + + # Receive the response from the server + try: + try: + # Python 2.7, use buffering of HTTP responses + httplib_response = conn.getresponse(buffering=True) + except TypeError: + # Python 3 + try: + httplib_response = conn.getresponse() + except BaseException as e: + # Remove the TypeError from the exception chain in + # Python 3 (including for exceptions like SystemExit). + # Otherwise it looks like a bug in the code. + six.raise_from(e, None) + except (SocketTimeout, BaseSSLError, SocketError) as e: + self._raise_timeout(err=e, url=url, timeout_value=read_timeout) + raise + + # AppEngine doesn't have a version attr. + http_version = getattr(conn, "_http_vsn_str", "HTTP/?") + log.debug( + '%s://%s:%s "%s %s %s" %s %s', + self.scheme, + self.host, + self.port, + method, + url, + http_version, + httplib_response.status, + httplib_response.length, + ) + + try: + assert_header_parsing(httplib_response.msg) + except (HeaderParsingError, TypeError) as hpe: # Platform-specific: Python 3 + log.warning( + "Failed to parse headers (url=%s): %s", + self._absolute_url(url), + hpe, + exc_info=True, + ) + + return httplib_response + + def _absolute_url(self, path): + return Url(scheme=self.scheme, host=self.host, port=self.port, path=path).url + + def close(self): + """ + Close all pooled connections and disable the pool. + """ + if self.pool is None: + return + # Disable access to the pool + old_pool, self.pool = self.pool, None + + try: + while True: + conn = old_pool.get(block=False) + if conn: + conn.close() + + except queue.Empty: + pass # Done. + + def is_same_host(self, url): + """ + Check if the given ``url`` is a member of the same host as this + connection pool. + """ + if url.startswith("/"): + return True + + # TODO: Add optional support for socket.gethostbyname checking. + scheme, host, port = get_host(url) + if host is not None: + host = _normalize_host(host, scheme=scheme) + + # Use explicit default port for comparison when none is given + if self.port and not port: + port = port_by_scheme.get(scheme) + elif not self.port and port == port_by_scheme.get(scheme): + port = None + + return (scheme, host, port) == (self.scheme, self.host, self.port) + + def urlopen( + self, + method, + url, + body=None, + headers=None, + retries=None, + redirect=True, + assert_same_host=True, + timeout=_Default, + pool_timeout=None, + release_conn=None, + chunked=False, + body_pos=None, + **response_kw + ): + """ + Get a connection from the pool and perform an HTTP request. This is the + lowest level call for making a request, so you'll need to specify all + the raw details. + + .. note:: + + More commonly, it's appropriate to use a convenience method provided + by :class:`.RequestMethods`, such as :meth:`request`. + + .. note:: + + `release_conn` will only behave as expected if + `preload_content=False` because we want to make + `preload_content=False` the default behaviour someday soon without + breaking backwards compatibility. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param body: + Data to send in the request body (useful for creating + POST requests, see HTTPConnectionPool.post_url for + more convenience). + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + Pass ``None`` to retry until you receive a response. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param assert_same_host: + If ``True``, will make sure that the host of the pool requests is + consistent else will raise HostChangedError. When False, you can + use the pool on an HTTP proxy and request foreign hosts. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param pool_timeout: + If set and the pool is set to block=True, then this method will + block for ``pool_timeout`` seconds and raise EmptyPoolError if no + connection is available within the time period. + + :param release_conn: + If False, then the urlopen call will not release the connection + back into the pool once a response is received (but will release if + you read the entire contents of the response such as when + `preload_content=True`). This is useful if you're not preloading + the response's content immediately. You will need to call + ``r.release_conn()`` on the response ``r`` to return the connection + back into the pool. If None, it takes the value of + ``response_kw.get('preload_content', True)``. + + :param chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param int body_pos: + Position to seek to in file-like body in the event of a retry or + redirect. Typically this won't need to be set because urllib3 will + auto-populate the value when needed. + + :param \\**response_kw: + Additional parameters are passed to + :meth:`urllib3.response.HTTPResponse.from_httplib` + """ + if headers is None: + headers = self.headers + + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if release_conn is None: + release_conn = response_kw.get("preload_content", True) + + # Check host + if assert_same_host and not self.is_same_host(url): + raise HostChangedError(self, url, retries) + + # Ensure that the URL we're connecting to is properly encoded + if url.startswith("/"): + url = six.ensure_str(_encode_target(url)) + else: + url = six.ensure_str(parse_url(url).url) + + conn = None + + # Track whether `conn` needs to be released before + # returning/raising/recursing. Update this variable if necessary, and + # leave `release_conn` constant throughout the function. That way, if + # the function recurses, the original value of `release_conn` will be + # passed down into the recursive call, and its value will be respected. + # + # See issue #651 [1] for details. + # + # [1] + release_this_conn = release_conn + + # Merge the proxy headers. Only do this in HTTP. We have to copy the + # headers dict so we can safely change it without those changes being + # reflected in anyone else's copy. + if self.scheme == "http": + headers = headers.copy() + headers.update(self.proxy_headers) + + # Must keep the exception bound to a separate variable or else Python 3 + # complains about UnboundLocalError. + err = None + + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + + # Rewind body position, if needed. Record current position + # for future rewinds in the event of a redirect/retry. + body_pos = set_file_position(body, body_pos) + + try: + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) + conn = self._get_conn(timeout=pool_timeout) + + conn.timeout = timeout_obj.connect_timeout + + is_new_proxy_conn = self.proxy is not None and not getattr( + conn, "sock", None + ) + if is_new_proxy_conn: + self._prepare_proxy(conn) + + # Make the request on the httplib connection object. + httplib_response = self._make_request( + conn, + method, + url, + timeout=timeout_obj, + body=body, + headers=headers, + chunked=chunked, + ) + + # If we're going to release the connection in ``finally:``, then + # the response doesn't need to know about the connection. Otherwise + # it will also try to release it and we'll have a double-release + # mess. + response_conn = conn if not release_conn else None + + # Pass method to Response for length checking + response_kw["request_method"] = method + + # Import httplib's response into our own wrapper object + response = self.ResponseCls.from_httplib( + httplib_response, + pool=self, + connection=response_conn, + retries=retries, + **response_kw + ) + + # Everything went great! + clean_exit = True + + except queue.Empty: + # Timed out by queue. + raise EmptyPoolError(self, "No pool connections are available.") + + except ( + TimeoutError, + HTTPException, + SocketError, + ProtocolError, + BaseSSLError, + SSLError, + CertificateError, + ) as e: + # Discard the connection for these exceptions. It will be + # replaced during the next _get_conn() call. + clean_exit = False + if isinstance(e, (BaseSSLError, CertificateError)): + e = SSLError(e) + elif isinstance(e, (SocketError, NewConnectionError)) and self.proxy: + e = ProxyError("Cannot connect to proxy.", e) + elif isinstance(e, (SocketError, HTTPException)): + e = ProtocolError("Connection aborted.", e) + + retries = retries.increment( + method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2] + ) + retries.sleep() + + # Keep track of the error for the retry warning. + err = e + + finally: + if not clean_exit: + # We hit some kind of exception, handled or otherwise. We need + # to throw the connection away unless explicitly told not to. + # Close the connection, set the variable to None, and make sure + # we put the None back in the pool to avoid leaking it. + conn = conn and conn.close() + release_this_conn = True + + if release_this_conn: + # Put the connection back to be reused. If the connection is + # expired then it will be None, which will get replaced with a + # fresh connection during _get_conn. + self._put_conn(conn) + + if not conn: + # Try again + log.warning( + "Retrying (%r) after connection broken by '%r': %s", retries, err, url + ) + return self.urlopen( + method, + url, + body, + headers, + retries, + redirect, + assert_same_host, + timeout=timeout, + pool_timeout=pool_timeout, + release_conn=release_conn, + chunked=chunked, + body_pos=body_pos, + **response_kw + ) + + # Handle redirect? + redirect_location = redirect and response.get_redirect_location() + if redirect_location: + if response.status == 303: + method = "GET" + + try: + retries = retries.increment(method, url, response=response, _pool=self) + except MaxRetryError: + if retries.raise_on_redirect: + response.drain_conn() + raise + return response + + response.drain_conn() + retries.sleep_for_retry(response) + log.debug("Redirecting %s -> %s", url, redirect_location) + return self.urlopen( + method, + redirect_location, + body, + headers, + retries=retries, + redirect=redirect, + assert_same_host=assert_same_host, + timeout=timeout, + pool_timeout=pool_timeout, + release_conn=release_conn, + chunked=chunked, + body_pos=body_pos, + **response_kw + ) + + # Check if we should retry the HTTP response. + has_retry_after = bool(response.getheader("Retry-After")) + if retries.is_retry(method, response.status, has_retry_after): + try: + retries = retries.increment(method, url, response=response, _pool=self) + except MaxRetryError: + if retries.raise_on_status: + response.drain_conn() + raise + return response + + response.drain_conn() + retries.sleep(response) + log.debug("Retry: %s", url) + return self.urlopen( + method, + url, + body, + headers, + retries=retries, + redirect=redirect, + assert_same_host=assert_same_host, + timeout=timeout, + pool_timeout=pool_timeout, + release_conn=release_conn, + chunked=chunked, + body_pos=body_pos, + **response_kw + ) + + return response + + +class HTTPSConnectionPool(HTTPConnectionPool): + """ + Same as :class:`.HTTPConnectionPool`, but HTTPS. + + When Python is compiled with the :mod:`ssl` module, then + :class:`.VerifiedHTTPSConnection` is used, which *can* verify certificates, + instead of :class:`.HTTPSConnection`. + + :class:`.VerifiedHTTPSConnection` uses one of ``assert_fingerprint``, + ``assert_hostname`` and ``host`` in this order to verify connections. + If ``assert_hostname`` is False, no verification is done. + + The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``, + ``ca_cert_dir``, ``ssl_version``, ``key_password`` are only used if :mod:`ssl` + is available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade + the connection socket into an SSL socket. + """ + + scheme = "https" + ConnectionCls = HTTPSConnection + + def __init__( + self, + host, + port=None, + strict=False, + timeout=Timeout.DEFAULT_TIMEOUT, + maxsize=1, + block=False, + headers=None, + retries=None, + _proxy=None, + _proxy_headers=None, + key_file=None, + cert_file=None, + cert_reqs=None, + key_password=None, + ca_certs=None, + ssl_version=None, + assert_hostname=None, + assert_fingerprint=None, + ca_cert_dir=None, + **conn_kw + ): + + HTTPConnectionPool.__init__( + self, + host, + port, + strict, + timeout, + maxsize, + block, + headers, + retries, + _proxy, + _proxy_headers, + **conn_kw + ) + + self.key_file = key_file + self.cert_file = cert_file + self.cert_reqs = cert_reqs + self.key_password = key_password + self.ca_certs = ca_certs + self.ca_cert_dir = ca_cert_dir + self.ssl_version = ssl_version + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + + def _prepare_conn(self, conn): + """ + Prepare the ``connection`` for :meth:`urllib3.util.ssl_wrap_socket` + and establish the tunnel if proxy is used. + """ + + if isinstance(conn, VerifiedHTTPSConnection): + conn.set_cert( + key_file=self.key_file, + key_password=self.key_password, + cert_file=self.cert_file, + cert_reqs=self.cert_reqs, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + assert_hostname=self.assert_hostname, + assert_fingerprint=self.assert_fingerprint, + ) + conn.ssl_version = self.ssl_version + return conn + + def _prepare_proxy(self, conn): + """ + Establish tunnel connection early, because otherwise httplib + would improperly set Host: header to proxy's IP:port. + """ + conn.set_tunnel(self._proxy_host, self.port, self.proxy_headers) + conn.connect() + + def _new_conn(self): + """ + Return a fresh :class:`httplib.HTTPSConnection`. + """ + self.num_connections += 1 + log.debug( + "Starting new HTTPS connection (%d): %s:%s", + self.num_connections, + self.host, + self.port or "443", + ) + + if not self.ConnectionCls or self.ConnectionCls is DummyConnection: + raise SSLError( + "Can't connect to HTTPS URL because the SSL module is not available." + ) + + actual_host = self.host + actual_port = self.port + if self.proxy is not None: + actual_host = self.proxy.host + actual_port = self.proxy.port + + conn = self.ConnectionCls( + host=actual_host, + port=actual_port, + timeout=self.timeout.connect_timeout, + strict=self.strict, + cert_file=self.cert_file, + key_file=self.key_file, + key_password=self.key_password, + **self.conn_kw + ) + + return self._prepare_conn(conn) + + def _validate_conn(self, conn): + """ + Called right before a request is made, after the socket is created. + """ + super(HTTPSConnectionPool, self)._validate_conn(conn) + + # Force connect early to allow us to validate the connection. + if not getattr(conn, "sock", None): # AppEngine might not have `.sock` + conn.connect() + + if not conn.is_verified: + warnings.warn( + ( + "Unverified HTTPS request is being made to host '%s'. " + "Adding certificate verification is strongly advised. See: " + "https://urllib3.readthedocs.io/en/latest/advanced-usage.html" + "#ssl-warnings" % conn.host + ), + InsecureRequestWarning, + ) + + +def connection_from_url(url, **kw): + """ + Given a url, return an :class:`.ConnectionPool` instance of its host. + + This is a shortcut for not having to parse out the scheme, host, and port + of the url before creating an :class:`.ConnectionPool` instance. + + :param url: + Absolute URL string that must include the scheme. Port is optional. + + :param \\**kw: + Passes additional parameters to the constructor of the appropriate + :class:`.ConnectionPool`. Useful for specifying things like + timeout, maxsize, headers, etc. + + Example:: + + >>> conn = connection_from_url('http://google.com/') + >>> r = conn.request('GET', '/') + """ + scheme, host, port = get_host(url) + port = port or port_by_scheme.get(scheme, 80) + if scheme == "https": + return HTTPSConnectionPool(host, port=port, **kw) + else: + return HTTPConnectionPool(host, port=port, **kw) + + +def _normalize_host(host, scheme): + """ + Normalize hosts for comparisons and use with sockets. + """ + + host = normalize_host(host, scheme) + + # httplib doesn't like it when we include brackets in IPv6 addresses + # Specifically, if we include brackets but also pass the port then + # httplib crazily doubles up the square brackets on the Host header. + # Instead, we need to make sure we never pass ``None`` as the port. + # However, for backward compatibility reasons we can't actually + # *assert* that. See http://bugs.python.org/issue28539 + if host.startswith("[") and host.endswith("]"): + host = host[1:-1] + return host diff --git a/DiscordToken/blank.py b/venv/Lib/site-packages/urllib3/contrib/__init__.py similarity index 100% rename from DiscordToken/blank.py rename to venv/Lib/site-packages/urllib3/contrib/__init__.py diff --git a/venv/Lib/site-packages/urllib3/contrib/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/urllib3/contrib/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9fa7e4bbbd5c95f52f964633e851b74597a7e3c3 GIT binary patch literal 172 zcmXr!<>hjeyb;F$1dl-k3@`#24nSPY0whuxf*CX!{Z=v*frJsnuSjRBn9$f( literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/contrib/__pycache__/_appengine_environ.cpython-36.pyc b/venv/Lib/site-packages/urllib3/contrib/__pycache__/_appengine_environ.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61bc1775921c331a031bc4e3eda4a46b7921616a GIT binary patch literal 1381 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 RmN{F%_qW;}ZWZMBe*jE45dZ)H literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/contrib/__pycache__/ntlmpool.cpython-36.pyc b/venv/Lib/site-packages/urllib3/contrib/__pycache__/ntlmpool.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1293b6478a447337336794c1c37a5af5822520c5 GIT binary patch literal 3218 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 ZK}OnYV?V9rGFl;nH>ol6{{R{768``I literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/contrib/__pycache__/securetransport.cpython-36.pyc b/venv/Lib/site-packages/urllib3/contrib/__pycache__/securetransport.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..14de036976db6a1d6d77bd69317dc04e8ba62475 GIT binary patch literal 19698 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/contrib/__pycache__/socks.cpython-36.pyc b/venv/Lib/site-packages/urllib3/contrib/__pycache__/socks.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..876ff3d2cf4e24893e479f3c9adea11931fdfca4 GIT binary patch literal 5488 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/contrib/_appengine_environ.py b/venv/Lib/site-packages/urllib3/contrib/_appengine_environ.py new file mode 100644 index 00000000..8765b907 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/contrib/_appengine_environ.py @@ -0,0 +1,36 @@ +""" +This module provides means to detect the App Engine environment. +""" + +import os + + +def is_appengine(): + return is_local_appengine() or is_prod_appengine() + + +def is_appengine_sandbox(): + """Reports if the app is running in the first generation sandbox. + + The second generation runtimes are technically still in a sandbox, but it + is much less restrictive, so generally you shouldn't need to check for it. + see https://cloud.google.com/appengine/docs/standard/runtimes + """ + return is_appengine() and os.environ["APPENGINE_RUNTIME"] == "python27" + + +def is_local_appengine(): + return "APPENGINE_RUNTIME" in os.environ and os.environ.get( + "SERVER_SOFTWARE", "" + ).startswith("Development/") + + +def is_prod_appengine(): + return "APPENGINE_RUNTIME" in os.environ and os.environ.get( + "SERVER_SOFTWARE", "" + ).startswith("Google App Engine/") + + +def is_prod_appengine_mvms(): + """Deprecated.""" + return False diff --git a/venv/Lib/site-packages/urllib3/contrib/_securetransport/__init__.py b/venv/Lib/site-packages/urllib3/contrib/_securetransport/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/venv/Lib/site-packages/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b04ffde49e9a218170e59d48238ea33d6e74c43 GIT binary patch literal 189 zcmXr!<>hjeyb;F$1dl-k3@`#24nSPY0whuxf*CX!{Z=v*frJsnuPkS)n9$0IYB|qW}N^ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-36.pyc b/venv/Lib/site-packages/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a8864625cae164c857ded73b5abaef88a972577 GIT binary patch literal 10256 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 F{tqKK)b{`Y literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/contrib/_securetransport/bindings.py b/venv/Lib/site-packages/urllib3/contrib/_securetransport/bindings.py new file mode 100644 index 00000000..d9b67333 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/contrib/_securetransport/bindings.py @@ -0,0 +1,493 @@ +""" +This module uses ctypes to bind a whole bunch of functions and constants from +SecureTransport. The goal here is to provide the low-level API to +SecureTransport. These are essentially the C-level functions and constants, and +they're pretty gross to work with. + +This code is a bastardised version of the code found in Will Bond's oscrypto +library. An enormous debt is owed to him for blazing this trail for us. For +that reason, this code should be considered to be covered both by urllib3's +license and by oscrypto's: + + Copyright (c) 2015-2016 Will Bond + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +""" +from __future__ import absolute_import + +import platform +from ctypes.util import find_library +from ctypes import ( + c_void_p, + c_int32, + c_char_p, + c_size_t, + c_byte, + c_uint32, + c_ulong, + c_long, + c_bool, +) +from ctypes import CDLL, POINTER, CFUNCTYPE + + +security_path = find_library("Security") +if not security_path: + raise ImportError("The library Security could not be found") + + +core_foundation_path = find_library("CoreFoundation") +if not core_foundation_path: + raise ImportError("The library CoreFoundation could not be found") + + +version = platform.mac_ver()[0] +version_info = tuple(map(int, version.split("."))) +if version_info < (10, 8): + raise OSError( + "Only OS X 10.8 and newer are supported, not %s.%s" + % (version_info[0], version_info[1]) + ) + +Security = CDLL(security_path, use_errno=True) +CoreFoundation = CDLL(core_foundation_path, use_errno=True) + +Boolean = c_bool +CFIndex = c_long +CFStringEncoding = c_uint32 +CFData = c_void_p +CFString = c_void_p +CFArray = c_void_p +CFMutableArray = c_void_p +CFDictionary = c_void_p +CFError = c_void_p +CFType = c_void_p +CFTypeID = c_ulong + +CFTypeRef = POINTER(CFType) +CFAllocatorRef = c_void_p + +OSStatus = c_int32 + +CFDataRef = POINTER(CFData) +CFStringRef = POINTER(CFString) +CFArrayRef = POINTER(CFArray) +CFMutableArrayRef = POINTER(CFMutableArray) +CFDictionaryRef = POINTER(CFDictionary) +CFArrayCallBacks = c_void_p +CFDictionaryKeyCallBacks = c_void_p +CFDictionaryValueCallBacks = c_void_p + +SecCertificateRef = POINTER(c_void_p) +SecExternalFormat = c_uint32 +SecExternalItemType = c_uint32 +SecIdentityRef = POINTER(c_void_p) +SecItemImportExportFlags = c_uint32 +SecItemImportExportKeyParameters = c_void_p +SecKeychainRef = POINTER(c_void_p) +SSLProtocol = c_uint32 +SSLCipherSuite = c_uint32 +SSLContextRef = POINTER(c_void_p) +SecTrustRef = POINTER(c_void_p) +SSLConnectionRef = c_uint32 +SecTrustResultType = c_uint32 +SecTrustOptionFlags = c_uint32 +SSLProtocolSide = c_uint32 +SSLConnectionType = c_uint32 +SSLSessionOption = c_uint32 + + +try: + Security.SecItemImport.argtypes = [ + CFDataRef, + CFStringRef, + POINTER(SecExternalFormat), + POINTER(SecExternalItemType), + SecItemImportExportFlags, + POINTER(SecItemImportExportKeyParameters), + SecKeychainRef, + POINTER(CFArrayRef), + ] + Security.SecItemImport.restype = OSStatus + + Security.SecCertificateGetTypeID.argtypes = [] + Security.SecCertificateGetTypeID.restype = CFTypeID + + Security.SecIdentityGetTypeID.argtypes = [] + Security.SecIdentityGetTypeID.restype = CFTypeID + + Security.SecKeyGetTypeID.argtypes = [] + Security.SecKeyGetTypeID.restype = CFTypeID + + Security.SecCertificateCreateWithData.argtypes = [CFAllocatorRef, CFDataRef] + Security.SecCertificateCreateWithData.restype = SecCertificateRef + + Security.SecCertificateCopyData.argtypes = [SecCertificateRef] + Security.SecCertificateCopyData.restype = CFDataRef + + Security.SecCopyErrorMessageString.argtypes = [OSStatus, c_void_p] + Security.SecCopyErrorMessageString.restype = CFStringRef + + Security.SecIdentityCreateWithCertificate.argtypes = [ + CFTypeRef, + SecCertificateRef, + POINTER(SecIdentityRef), + ] + Security.SecIdentityCreateWithCertificate.restype = OSStatus + + Security.SecKeychainCreate.argtypes = [ + c_char_p, + c_uint32, + c_void_p, + Boolean, + c_void_p, + POINTER(SecKeychainRef), + ] + Security.SecKeychainCreate.restype = OSStatus + + Security.SecKeychainDelete.argtypes = [SecKeychainRef] + Security.SecKeychainDelete.restype = OSStatus + + Security.SecPKCS12Import.argtypes = [ + CFDataRef, + CFDictionaryRef, + POINTER(CFArrayRef), + ] + Security.SecPKCS12Import.restype = OSStatus + + SSLReadFunc = CFUNCTYPE(OSStatus, SSLConnectionRef, c_void_p, POINTER(c_size_t)) + SSLWriteFunc = CFUNCTYPE( + OSStatus, SSLConnectionRef, POINTER(c_byte), POINTER(c_size_t) + ) + + Security.SSLSetIOFuncs.argtypes = [SSLContextRef, SSLReadFunc, SSLWriteFunc] + Security.SSLSetIOFuncs.restype = OSStatus + + Security.SSLSetPeerID.argtypes = [SSLContextRef, c_char_p, c_size_t] + Security.SSLSetPeerID.restype = OSStatus + + Security.SSLSetCertificate.argtypes = [SSLContextRef, CFArrayRef] + Security.SSLSetCertificate.restype = OSStatus + + Security.SSLSetCertificateAuthorities.argtypes = [SSLContextRef, CFTypeRef, Boolean] + Security.SSLSetCertificateAuthorities.restype = OSStatus + + Security.SSLSetConnection.argtypes = [SSLContextRef, SSLConnectionRef] + Security.SSLSetConnection.restype = OSStatus + + Security.SSLSetPeerDomainName.argtypes = [SSLContextRef, c_char_p, c_size_t] + Security.SSLSetPeerDomainName.restype = OSStatus + + Security.SSLHandshake.argtypes = [SSLContextRef] + Security.SSLHandshake.restype = OSStatus + + Security.SSLRead.argtypes = [SSLContextRef, c_char_p, c_size_t, POINTER(c_size_t)] + Security.SSLRead.restype = OSStatus + + Security.SSLWrite.argtypes = [SSLContextRef, c_char_p, c_size_t, POINTER(c_size_t)] + Security.SSLWrite.restype = OSStatus + + Security.SSLClose.argtypes = [SSLContextRef] + Security.SSLClose.restype = OSStatus + + Security.SSLGetNumberSupportedCiphers.argtypes = [SSLContextRef, POINTER(c_size_t)] + Security.SSLGetNumberSupportedCiphers.restype = OSStatus + + Security.SSLGetSupportedCiphers.argtypes = [ + SSLContextRef, + POINTER(SSLCipherSuite), + POINTER(c_size_t), + ] + Security.SSLGetSupportedCiphers.restype = OSStatus + + Security.SSLSetEnabledCiphers.argtypes = [ + SSLContextRef, + POINTER(SSLCipherSuite), + c_size_t, + ] + Security.SSLSetEnabledCiphers.restype = OSStatus + + Security.SSLGetNumberEnabledCiphers.argtype = [SSLContextRef, POINTER(c_size_t)] + Security.SSLGetNumberEnabledCiphers.restype = OSStatus + + Security.SSLGetEnabledCiphers.argtypes = [ + SSLContextRef, + POINTER(SSLCipherSuite), + POINTER(c_size_t), + ] + Security.SSLGetEnabledCiphers.restype = OSStatus + + Security.SSLGetNegotiatedCipher.argtypes = [SSLContextRef, POINTER(SSLCipherSuite)] + Security.SSLGetNegotiatedCipher.restype = OSStatus + + Security.SSLGetNegotiatedProtocolVersion.argtypes = [ + SSLContextRef, + POINTER(SSLProtocol), + ] + Security.SSLGetNegotiatedProtocolVersion.restype = OSStatus + + Security.SSLCopyPeerTrust.argtypes = [SSLContextRef, POINTER(SecTrustRef)] + Security.SSLCopyPeerTrust.restype = OSStatus + + Security.SecTrustSetAnchorCertificates.argtypes = [SecTrustRef, CFArrayRef] + Security.SecTrustSetAnchorCertificates.restype = OSStatus + + Security.SecTrustSetAnchorCertificatesOnly.argstypes = [SecTrustRef, Boolean] + Security.SecTrustSetAnchorCertificatesOnly.restype = OSStatus + + Security.SecTrustEvaluate.argtypes = [SecTrustRef, POINTER(SecTrustResultType)] + Security.SecTrustEvaluate.restype = OSStatus + + Security.SecTrustGetCertificateCount.argtypes = [SecTrustRef] + Security.SecTrustGetCertificateCount.restype = CFIndex + + Security.SecTrustGetCertificateAtIndex.argtypes = [SecTrustRef, CFIndex] + Security.SecTrustGetCertificateAtIndex.restype = SecCertificateRef + + Security.SSLCreateContext.argtypes = [ + CFAllocatorRef, + SSLProtocolSide, + SSLConnectionType, + ] + Security.SSLCreateContext.restype = SSLContextRef + + Security.SSLSetSessionOption.argtypes = [SSLContextRef, SSLSessionOption, Boolean] + Security.SSLSetSessionOption.restype = OSStatus + + Security.SSLSetProtocolVersionMin.argtypes = [SSLContextRef, SSLProtocol] + Security.SSLSetProtocolVersionMin.restype = OSStatus + + Security.SSLSetProtocolVersionMax.argtypes = [SSLContextRef, SSLProtocol] + Security.SSLSetProtocolVersionMax.restype = OSStatus + + Security.SecCopyErrorMessageString.argtypes = [OSStatus, c_void_p] + Security.SecCopyErrorMessageString.restype = CFStringRef + + Security.SSLReadFunc = SSLReadFunc + Security.SSLWriteFunc = SSLWriteFunc + Security.SSLContextRef = SSLContextRef + Security.SSLProtocol = SSLProtocol + Security.SSLCipherSuite = SSLCipherSuite + Security.SecIdentityRef = SecIdentityRef + Security.SecKeychainRef = SecKeychainRef + Security.SecTrustRef = SecTrustRef + Security.SecTrustResultType = SecTrustResultType + Security.SecExternalFormat = SecExternalFormat + Security.OSStatus = OSStatus + + Security.kSecImportExportPassphrase = CFStringRef.in_dll( + Security, "kSecImportExportPassphrase" + ) + Security.kSecImportItemIdentity = CFStringRef.in_dll( + Security, "kSecImportItemIdentity" + ) + + # CoreFoundation time! + CoreFoundation.CFRetain.argtypes = [CFTypeRef] + CoreFoundation.CFRetain.restype = CFTypeRef + + CoreFoundation.CFRelease.argtypes = [CFTypeRef] + CoreFoundation.CFRelease.restype = None + + CoreFoundation.CFGetTypeID.argtypes = [CFTypeRef] + CoreFoundation.CFGetTypeID.restype = CFTypeID + + CoreFoundation.CFStringCreateWithCString.argtypes = [ + CFAllocatorRef, + c_char_p, + CFStringEncoding, + ] + CoreFoundation.CFStringCreateWithCString.restype = CFStringRef + + CoreFoundation.CFStringGetCStringPtr.argtypes = [CFStringRef, CFStringEncoding] + CoreFoundation.CFStringGetCStringPtr.restype = c_char_p + + CoreFoundation.CFStringGetCString.argtypes = [ + CFStringRef, + c_char_p, + CFIndex, + CFStringEncoding, + ] + CoreFoundation.CFStringGetCString.restype = c_bool + + CoreFoundation.CFDataCreate.argtypes = [CFAllocatorRef, c_char_p, CFIndex] + CoreFoundation.CFDataCreate.restype = CFDataRef + + CoreFoundation.CFDataGetLength.argtypes = [CFDataRef] + CoreFoundation.CFDataGetLength.restype = CFIndex + + CoreFoundation.CFDataGetBytePtr.argtypes = [CFDataRef] + CoreFoundation.CFDataGetBytePtr.restype = c_void_p + + CoreFoundation.CFDictionaryCreate.argtypes = [ + CFAllocatorRef, + POINTER(CFTypeRef), + POINTER(CFTypeRef), + CFIndex, + CFDictionaryKeyCallBacks, + CFDictionaryValueCallBacks, + ] + CoreFoundation.CFDictionaryCreate.restype = CFDictionaryRef + + CoreFoundation.CFDictionaryGetValue.argtypes = [CFDictionaryRef, CFTypeRef] + CoreFoundation.CFDictionaryGetValue.restype = CFTypeRef + + CoreFoundation.CFArrayCreate.argtypes = [ + CFAllocatorRef, + POINTER(CFTypeRef), + CFIndex, + CFArrayCallBacks, + ] + CoreFoundation.CFArrayCreate.restype = CFArrayRef + + CoreFoundation.CFArrayCreateMutable.argtypes = [ + CFAllocatorRef, + CFIndex, + CFArrayCallBacks, + ] + CoreFoundation.CFArrayCreateMutable.restype = CFMutableArrayRef + + CoreFoundation.CFArrayAppendValue.argtypes = [CFMutableArrayRef, c_void_p] + CoreFoundation.CFArrayAppendValue.restype = None + + CoreFoundation.CFArrayGetCount.argtypes = [CFArrayRef] + CoreFoundation.CFArrayGetCount.restype = CFIndex + + CoreFoundation.CFArrayGetValueAtIndex.argtypes = [CFArrayRef, CFIndex] + CoreFoundation.CFArrayGetValueAtIndex.restype = c_void_p + + CoreFoundation.kCFAllocatorDefault = CFAllocatorRef.in_dll( + CoreFoundation, "kCFAllocatorDefault" + ) + CoreFoundation.kCFTypeArrayCallBacks = c_void_p.in_dll( + CoreFoundation, "kCFTypeArrayCallBacks" + ) + CoreFoundation.kCFTypeDictionaryKeyCallBacks = c_void_p.in_dll( + CoreFoundation, "kCFTypeDictionaryKeyCallBacks" + ) + CoreFoundation.kCFTypeDictionaryValueCallBacks = c_void_p.in_dll( + CoreFoundation, "kCFTypeDictionaryValueCallBacks" + ) + + CoreFoundation.CFTypeRef = CFTypeRef + CoreFoundation.CFArrayRef = CFArrayRef + CoreFoundation.CFStringRef = CFStringRef + CoreFoundation.CFDictionaryRef = CFDictionaryRef + +except (AttributeError): + raise ImportError("Error initializing ctypes") + + +class CFConst(object): + """ + A class object that acts as essentially a namespace for CoreFoundation + constants. + """ + + kCFStringEncodingUTF8 = CFStringEncoding(0x08000100) + + +class SecurityConst(object): + """ + A class object that acts as essentially a namespace for Security constants. + """ + + kSSLSessionOptionBreakOnServerAuth = 0 + + kSSLProtocol2 = 1 + kSSLProtocol3 = 2 + kTLSProtocol1 = 4 + kTLSProtocol11 = 7 + kTLSProtocol12 = 8 + # SecureTransport does not support TLS 1.3 even if there's a constant for it + kTLSProtocol13 = 10 + kTLSProtocolMaxSupported = 999 + + kSSLClientSide = 1 + kSSLStreamType = 0 + + kSecFormatPEMSequence = 10 + + kSecTrustResultInvalid = 0 + kSecTrustResultProceed = 1 + # This gap is present on purpose: this was kSecTrustResultConfirm, which + # is deprecated. + kSecTrustResultDeny = 3 + kSecTrustResultUnspecified = 4 + kSecTrustResultRecoverableTrustFailure = 5 + kSecTrustResultFatalTrustFailure = 6 + kSecTrustResultOtherError = 7 + + errSSLProtocol = -9800 + errSSLWouldBlock = -9803 + errSSLClosedGraceful = -9805 + errSSLClosedNoNotify = -9816 + errSSLClosedAbort = -9806 + + errSSLXCertChainInvalid = -9807 + errSSLCrypto = -9809 + errSSLInternal = -9810 + errSSLCertExpired = -9814 + errSSLCertNotYetValid = -9815 + errSSLUnknownRootCert = -9812 + errSSLNoRootCert = -9813 + errSSLHostNameMismatch = -9843 + errSSLPeerHandshakeFail = -9824 + errSSLPeerUserCancelled = -9839 + errSSLWeakPeerEphemeralDHKey = -9850 + errSSLServerAuthCompleted = -9841 + errSSLRecordOverflow = -9847 + + errSecVerifyFailed = -67808 + errSecNoTrustSettings = -25263 + errSecItemNotFound = -25300 + errSecInvalidTrustSettings = -25262 + + # Cipher suites. We only pick the ones our default cipher string allows. + # Source: https://developer.apple.com/documentation/security/1550981-ssl_cipher_suite_values + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030 + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9 + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8 + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024 + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028 + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014 + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B + TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039 + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023 + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027 + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009 + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013 + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067 + TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033 + TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D + TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C + TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D + TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C + TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035 + TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F + TLS_AES_128_GCM_SHA256 = 0x1301 + TLS_AES_256_GCM_SHA384 = 0x1302 + TLS_AES_128_CCM_8_SHA256 = 0x1305 + TLS_AES_128_CCM_SHA256 = 0x1304 diff --git a/venv/Lib/site-packages/urllib3/contrib/_securetransport/low_level.py b/venv/Lib/site-packages/urllib3/contrib/_securetransport/low_level.py new file mode 100644 index 00000000..e60168ca --- /dev/null +++ b/venv/Lib/site-packages/urllib3/contrib/_securetransport/low_level.py @@ -0,0 +1,328 @@ +""" +Low-level helpers for the SecureTransport bindings. + +These are Python functions that are not directly related to the high-level APIs +but are necessary to get them to work. They include a whole bunch of low-level +CoreFoundation messing about and memory management. The concerns in this module +are almost entirely about trying to avoid memory leaks and providing +appropriate and useful assistance to the higher-level code. +""" +import base64 +import ctypes +import itertools +import re +import os +import ssl +import tempfile + +from .bindings import Security, CoreFoundation, CFConst + + +# This regular expression is used to grab PEM data out of a PEM bundle. +_PEM_CERTS_RE = re.compile( + b"-----BEGIN CERTIFICATE-----\n(.*?)\n-----END CERTIFICATE-----", re.DOTALL +) + + +def _cf_data_from_bytes(bytestring): + """ + Given a bytestring, create a CFData object from it. This CFData object must + be CFReleased by the caller. + """ + return CoreFoundation.CFDataCreate( + CoreFoundation.kCFAllocatorDefault, bytestring, len(bytestring) + ) + + +def _cf_dictionary_from_tuples(tuples): + """ + Given a list of Python tuples, create an associated CFDictionary. + """ + dictionary_size = len(tuples) + + # We need to get the dictionary keys and values out in the same order. + keys = (t[0] for t in tuples) + values = (t[1] for t in tuples) + cf_keys = (CoreFoundation.CFTypeRef * dictionary_size)(*keys) + cf_values = (CoreFoundation.CFTypeRef * dictionary_size)(*values) + + return CoreFoundation.CFDictionaryCreate( + CoreFoundation.kCFAllocatorDefault, + cf_keys, + cf_values, + dictionary_size, + CoreFoundation.kCFTypeDictionaryKeyCallBacks, + CoreFoundation.kCFTypeDictionaryValueCallBacks, + ) + + +def _cf_string_to_unicode(value): + """ + Creates a Unicode string from a CFString object. Used entirely for error + reporting. + + Yes, it annoys me quite a lot that this function is this complex. + """ + value_as_void_p = ctypes.cast(value, ctypes.POINTER(ctypes.c_void_p)) + + string = CoreFoundation.CFStringGetCStringPtr( + value_as_void_p, CFConst.kCFStringEncodingUTF8 + ) + if string is None: + buffer = ctypes.create_string_buffer(1024) + result = CoreFoundation.CFStringGetCString( + value_as_void_p, buffer, 1024, CFConst.kCFStringEncodingUTF8 + ) + if not result: + raise OSError("Error copying C string from CFStringRef") + string = buffer.value + if string is not None: + string = string.decode("utf-8") + return string + + +def _assert_no_error(error, exception_class=None): + """ + Checks the return code and throws an exception if there is an error to + report + """ + if error == 0: + return + + cf_error_string = Security.SecCopyErrorMessageString(error, None) + output = _cf_string_to_unicode(cf_error_string) + CoreFoundation.CFRelease(cf_error_string) + + if output is None or output == u"": + output = u"OSStatus %s" % error + + if exception_class is None: + exception_class = ssl.SSLError + + raise exception_class(output) + + +def _cert_array_from_pem(pem_bundle): + """ + Given a bundle of certs in PEM format, turns them into a CFArray of certs + that can be used to validate a cert chain. + """ + # Normalize the PEM bundle's line endings. + pem_bundle = pem_bundle.replace(b"\r\n", b"\n") + + der_certs = [ + base64.b64decode(match.group(1)) for match in _PEM_CERTS_RE.finditer(pem_bundle) + ] + if not der_certs: + raise ssl.SSLError("No root certificates specified") + + cert_array = CoreFoundation.CFArrayCreateMutable( + CoreFoundation.kCFAllocatorDefault, + 0, + ctypes.byref(CoreFoundation.kCFTypeArrayCallBacks), + ) + if not cert_array: + raise ssl.SSLError("Unable to allocate memory!") + + try: + for der_bytes in der_certs: + certdata = _cf_data_from_bytes(der_bytes) + if not certdata: + raise ssl.SSLError("Unable to allocate memory!") + cert = Security.SecCertificateCreateWithData( + CoreFoundation.kCFAllocatorDefault, certdata + ) + CoreFoundation.CFRelease(certdata) + if not cert: + raise ssl.SSLError("Unable to build cert object!") + + CoreFoundation.CFArrayAppendValue(cert_array, cert) + CoreFoundation.CFRelease(cert) + except Exception: + # We need to free the array before the exception bubbles further. + # We only want to do that if an error occurs: otherwise, the caller + # should free. + CoreFoundation.CFRelease(cert_array) + + return cert_array + + +def _is_cert(item): + """ + Returns True if a given CFTypeRef is a certificate. + """ + expected = Security.SecCertificateGetTypeID() + return CoreFoundation.CFGetTypeID(item) == expected + + +def _is_identity(item): + """ + Returns True if a given CFTypeRef is an identity. + """ + expected = Security.SecIdentityGetTypeID() + return CoreFoundation.CFGetTypeID(item) == expected + + +def _temporary_keychain(): + """ + This function creates a temporary Mac keychain that we can use to work with + credentials. This keychain uses a one-time password and a temporary file to + store the data. We expect to have one keychain per socket. The returned + SecKeychainRef must be freed by the caller, including calling + SecKeychainDelete. + + Returns a tuple of the SecKeychainRef and the path to the temporary + directory that contains it. + """ + # Unfortunately, SecKeychainCreate requires a path to a keychain. This + # means we cannot use mkstemp to use a generic temporary file. Instead, + # we're going to create a temporary directory and a filename to use there. + # This filename will be 8 random bytes expanded into base64. We also need + # some random bytes to password-protect the keychain we're creating, so we + # ask for 40 random bytes. + random_bytes = os.urandom(40) + filename = base64.b16encode(random_bytes[:8]).decode("utf-8") + password = base64.b16encode(random_bytes[8:]) # Must be valid UTF-8 + tempdirectory = tempfile.mkdtemp() + + keychain_path = os.path.join(tempdirectory, filename).encode("utf-8") + + # We now want to create the keychain itself. + keychain = Security.SecKeychainRef() + status = Security.SecKeychainCreate( + keychain_path, len(password), password, False, None, ctypes.byref(keychain) + ) + _assert_no_error(status) + + # Having created the keychain, we want to pass it off to the caller. + return keychain, tempdirectory + + +def _load_items_from_file(keychain, path): + """ + Given a single file, loads all the trust objects from it into arrays and + the keychain. + Returns a tuple of lists: the first list is a list of identities, the + second a list of certs. + """ + certificates = [] + identities = [] + result_array = None + + with open(path, "rb") as f: + raw_filedata = f.read() + + try: + filedata = CoreFoundation.CFDataCreate( + CoreFoundation.kCFAllocatorDefault, raw_filedata, len(raw_filedata) + ) + result_array = CoreFoundation.CFArrayRef() + result = Security.SecItemImport( + filedata, # cert data + None, # Filename, leaving it out for now + None, # What the type of the file is, we don't care + None, # what's in the file, we don't care + 0, # import flags + None, # key params, can include passphrase in the future + keychain, # The keychain to insert into + ctypes.byref(result_array), # Results + ) + _assert_no_error(result) + + # A CFArray is not very useful to us as an intermediary + # representation, so we are going to extract the objects we want + # and then free the array. We don't need to keep hold of keys: the + # keychain already has them! + result_count = CoreFoundation.CFArrayGetCount(result_array) + for index in range(result_count): + item = CoreFoundation.CFArrayGetValueAtIndex(result_array, index) + item = ctypes.cast(item, CoreFoundation.CFTypeRef) + + if _is_cert(item): + CoreFoundation.CFRetain(item) + certificates.append(item) + elif _is_identity(item): + CoreFoundation.CFRetain(item) + identities.append(item) + finally: + if result_array: + CoreFoundation.CFRelease(result_array) + + CoreFoundation.CFRelease(filedata) + + return (identities, certificates) + + +def _load_client_cert_chain(keychain, *paths): + """ + Load certificates and maybe keys from a number of files. Has the end goal + of returning a CFArray containing one SecIdentityRef, and then zero or more + SecCertificateRef objects, suitable for use as a client certificate trust + chain. + """ + # Ok, the strategy. + # + # This relies on knowing that macOS will not give you a SecIdentityRef + # unless you have imported a key into a keychain. This is a somewhat + # artificial limitation of macOS (for example, it doesn't necessarily + # affect iOS), but there is nothing inside Security.framework that lets you + # get a SecIdentityRef without having a key in a keychain. + # + # So the policy here is we take all the files and iterate them in order. + # Each one will use SecItemImport to have one or more objects loaded from + # it. We will also point at a keychain that macOS can use to work with the + # private key. + # + # Once we have all the objects, we'll check what we actually have. If we + # already have a SecIdentityRef in hand, fab: we'll use that. Otherwise, + # we'll take the first certificate (which we assume to be our leaf) and + # ask the keychain to give us a SecIdentityRef with that cert's associated + # key. + # + # We'll then return a CFArray containing the trust chain: one + # SecIdentityRef and then zero-or-more SecCertificateRef objects. The + # responsibility for freeing this CFArray will be with the caller. This + # CFArray must remain alive for the entire connection, so in practice it + # will be stored with a single SSLSocket, along with the reference to the + # keychain. + certificates = [] + identities = [] + + # Filter out bad paths. + paths = (path for path in paths if path) + + try: + for file_path in paths: + new_identities, new_certs = _load_items_from_file(keychain, file_path) + identities.extend(new_identities) + certificates.extend(new_certs) + + # Ok, we have everything. The question is: do we have an identity? If + # not, we want to grab one from the first cert we have. + if not identities: + new_identity = Security.SecIdentityRef() + status = Security.SecIdentityCreateWithCertificate( + keychain, certificates[0], ctypes.byref(new_identity) + ) + _assert_no_error(status) + identities.append(new_identity) + + # We now want to release the original certificate, as we no longer + # need it. + CoreFoundation.CFRelease(certificates.pop(0)) + + # We now need to build a new CFArray that holds the trust chain. + trust_chain = CoreFoundation.CFArrayCreateMutable( + CoreFoundation.kCFAllocatorDefault, + 0, + ctypes.byref(CoreFoundation.kCFTypeArrayCallBacks), + ) + for item in itertools.chain(identities, certificates): + # ArrayAppendValue does a CFRetain on the item. That's fine, + # because the finally block will release our other refs to them. + CoreFoundation.CFArrayAppendValue(trust_chain, item) + + return trust_chain + finally: + for obj in itertools.chain(identities, certificates): + CoreFoundation.CFRelease(obj) diff --git a/venv/Lib/site-packages/urllib3/contrib/appengine.py b/venv/Lib/site-packages/urllib3/contrib/appengine.py new file mode 100644 index 00000000..9b7044ff --- /dev/null +++ b/venv/Lib/site-packages/urllib3/contrib/appengine.py @@ -0,0 +1,314 @@ +""" +This module provides a pool manager that uses Google App Engine's +`URLFetch Service `_. + +Example usage:: + + from urllib3 import PoolManager + from urllib3.contrib.appengine import AppEngineManager, is_appengine_sandbox + + if is_appengine_sandbox(): + # AppEngineManager uses AppEngine's URLFetch API behind the scenes + http = AppEngineManager() + else: + # PoolManager uses a socket-level API behind the scenes + http = PoolManager() + + r = http.request('GET', 'https://google.com/') + +There are `limitations `_ to the URLFetch service and it may not be +the best choice for your application. There are three options for using +urllib3 on Google App Engine: + +1. You can use :class:`AppEngineManager` with URLFetch. URLFetch is + cost-effective in many circumstances as long as your usage is within the + limitations. +2. You can use a normal :class:`~urllib3.PoolManager` by enabling sockets. + Sockets also have `limitations and restrictions + `_ and have a lower free quota than URLFetch. + To use sockets, be sure to specify the following in your ``app.yaml``:: + + env_variables: + GAE_USE_SOCKETS_HTTPLIB : 'true' + +3. If you are using `App Engine Flexible +`_, you can use the standard +:class:`PoolManager` without any configuration or special environment variables. +""" + +from __future__ import absolute_import +import io +import logging +import warnings +from ..packages.six.moves.urllib.parse import urljoin + +from ..exceptions import ( + HTTPError, + HTTPWarning, + MaxRetryError, + ProtocolError, + TimeoutError, + SSLError, +) + +from ..request import RequestMethods +from ..response import HTTPResponse +from ..util.timeout import Timeout +from ..util.retry import Retry +from . import _appengine_environ + +try: + from google.appengine.api import urlfetch +except ImportError: + urlfetch = None + + +log = logging.getLogger(__name__) + + +class AppEnginePlatformWarning(HTTPWarning): + pass + + +class AppEnginePlatformError(HTTPError): + pass + + +class AppEngineManager(RequestMethods): + """ + Connection manager for Google App Engine sandbox applications. + + This manager uses the URLFetch service directly instead of using the + emulated httplib, and is subject to URLFetch limitations as described in + the App Engine documentation `here + `_. + + Notably it will raise an :class:`AppEnginePlatformError` if: + * URLFetch is not available. + * If you attempt to use this on App Engine Flexible, as full socket + support is available. + * If a request size is more than 10 megabytes. + * If a response size is more than 32 megabtyes. + * If you use an unsupported request method such as OPTIONS. + + Beyond those cases, it will raise normal urllib3 errors. + """ + + def __init__( + self, + headers=None, + retries=None, + validate_certificate=True, + urlfetch_retries=True, + ): + if not urlfetch: + raise AppEnginePlatformError( + "URLFetch is not available in this environment." + ) + + warnings.warn( + "urllib3 is using URLFetch on Google App Engine sandbox instead " + "of sockets. To use sockets directly instead of URLFetch see " + "https://urllib3.readthedocs.io/en/latest/reference/urllib3.contrib.html.", + AppEnginePlatformWarning, + ) + + RequestMethods.__init__(self, headers) + self.validate_certificate = validate_certificate + self.urlfetch_retries = urlfetch_retries + + self.retries = retries or Retry.DEFAULT + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + # Return False to re-raise any potential exceptions + return False + + def urlopen( + self, + method, + url, + body=None, + headers=None, + retries=None, + redirect=True, + timeout=Timeout.DEFAULT_TIMEOUT, + **response_kw + ): + + retries = self._get_retries(retries, redirect) + + try: + follow_redirects = redirect and retries.redirect != 0 and retries.total + response = urlfetch.fetch( + url, + payload=body, + method=method, + headers=headers or {}, + allow_truncated=False, + follow_redirects=self.urlfetch_retries and follow_redirects, + deadline=self._get_absolute_timeout(timeout), + validate_certificate=self.validate_certificate, + ) + except urlfetch.DeadlineExceededError as e: + raise TimeoutError(self, e) + + except urlfetch.InvalidURLError as e: + if "too large" in str(e): + raise AppEnginePlatformError( + "URLFetch request too large, URLFetch only " + "supports requests up to 10mb in size.", + e, + ) + raise ProtocolError(e) + + except urlfetch.DownloadError as e: + if "Too many redirects" in str(e): + raise MaxRetryError(self, url, reason=e) + raise ProtocolError(e) + + except urlfetch.ResponseTooLargeError as e: + raise AppEnginePlatformError( + "URLFetch response too large, URLFetch only supports" + "responses up to 32mb in size.", + e, + ) + + except urlfetch.SSLCertificateError as e: + raise SSLError(e) + + except urlfetch.InvalidMethodError as e: + raise AppEnginePlatformError( + "URLFetch does not support method: %s" % method, e + ) + + http_response = self._urlfetch_response_to_http_response( + response, retries=retries, **response_kw + ) + + # Handle redirect? + redirect_location = redirect and http_response.get_redirect_location() + if redirect_location: + # Check for redirect response + if self.urlfetch_retries and retries.raise_on_redirect: + raise MaxRetryError(self, url, "too many redirects") + else: + if http_response.status == 303: + method = "GET" + + try: + retries = retries.increment( + method, url, response=http_response, _pool=self + ) + except MaxRetryError: + if retries.raise_on_redirect: + raise MaxRetryError(self, url, "too many redirects") + return http_response + + retries.sleep_for_retry(http_response) + log.debug("Redirecting %s -> %s", url, redirect_location) + redirect_url = urljoin(url, redirect_location) + return self.urlopen( + method, + redirect_url, + body, + headers, + retries=retries, + redirect=redirect, + timeout=timeout, + **response_kw + ) + + # Check if we should retry the HTTP response. + has_retry_after = bool(http_response.getheader("Retry-After")) + if retries.is_retry(method, http_response.status, has_retry_after): + retries = retries.increment(method, url, response=http_response, _pool=self) + log.debug("Retry: %s", url) + retries.sleep(http_response) + return self.urlopen( + method, + url, + body=body, + headers=headers, + retries=retries, + redirect=redirect, + timeout=timeout, + **response_kw + ) + + return http_response + + def _urlfetch_response_to_http_response(self, urlfetch_resp, **response_kw): + + if is_prod_appengine(): + # Production GAE handles deflate encoding automatically, but does + # not remove the encoding header. + content_encoding = urlfetch_resp.headers.get("content-encoding") + + if content_encoding == "deflate": + del urlfetch_resp.headers["content-encoding"] + + transfer_encoding = urlfetch_resp.headers.get("transfer-encoding") + # We have a full response's content, + # so let's make sure we don't report ourselves as chunked data. + if transfer_encoding == "chunked": + encodings = transfer_encoding.split(",") + encodings.remove("chunked") + urlfetch_resp.headers["transfer-encoding"] = ",".join(encodings) + + original_response = HTTPResponse( + # In order for decoding to work, we must present the content as + # a file-like object. + body=io.BytesIO(urlfetch_resp.content), + msg=urlfetch_resp.header_msg, + headers=urlfetch_resp.headers, + status=urlfetch_resp.status_code, + **response_kw + ) + + return HTTPResponse( + body=io.BytesIO(urlfetch_resp.content), + headers=urlfetch_resp.headers, + status=urlfetch_resp.status_code, + original_response=original_response, + **response_kw + ) + + def _get_absolute_timeout(self, timeout): + if timeout is Timeout.DEFAULT_TIMEOUT: + return None # Defer to URLFetch's default. + if isinstance(timeout, Timeout): + if timeout._read is not None or timeout._connect is not None: + warnings.warn( + "URLFetch does not support granular timeout settings, " + "reverting to total or default URLFetch timeout.", + AppEnginePlatformWarning, + ) + return timeout.total + return timeout + + def _get_retries(self, retries, redirect): + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if retries.connect or retries.read or retries.redirect: + warnings.warn( + "URLFetch only supports total retries and does not " + "recognize connect, read, or redirect retry parameters.", + AppEnginePlatformWarning, + ) + + return retries + + +# Alias methods from _appengine_environ to maintain public API interface. + +is_appengine = _appengine_environ.is_appengine +is_appengine_sandbox = _appengine_environ.is_appengine_sandbox +is_local_appengine = _appengine_environ.is_local_appengine +is_prod_appengine = _appengine_environ.is_prod_appengine +is_prod_appengine_mvms = _appengine_environ.is_prod_appengine_mvms diff --git a/venv/Lib/site-packages/urllib3/contrib/ntlmpool.py b/venv/Lib/site-packages/urllib3/contrib/ntlmpool.py new file mode 100644 index 00000000..1fd242a6 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/contrib/ntlmpool.py @@ -0,0 +1,121 @@ +""" +NTLM authenticating pool, contributed by erikcederstran + +Issue #10, see: http://code.google.com/p/urllib3/issues/detail?id=10 +""" +from __future__ import absolute_import + +from logging import getLogger +from ntlm import ntlm + +from .. import HTTPSConnectionPool +from ..packages.six.moves.http_client import HTTPSConnection + + +log = getLogger(__name__) + + +class NTLMConnectionPool(HTTPSConnectionPool): + """ + Implements an NTLM authentication version of an urllib3 connection pool + """ + + scheme = "https" + + def __init__(self, user, pw, authurl, *args, **kwargs): + """ + authurl is a random URL on the server that is protected by NTLM. + user is the Windows user, probably in the DOMAIN\\username format. + pw is the password for the user. + """ + super(NTLMConnectionPool, self).__init__(*args, **kwargs) + self.authurl = authurl + self.rawuser = user + user_parts = user.split("\\", 1) + self.domain = user_parts[0].upper() + self.user = user_parts[1] + self.pw = pw + + def _new_conn(self): + # Performs the NTLM handshake that secures the connection. The socket + # must be kept open while requests are performed. + self.num_connections += 1 + log.debug( + "Starting NTLM HTTPS connection no. %d: https://%s%s", + self.num_connections, + self.host, + self.authurl, + ) + + headers = {"Connection": "Keep-Alive"} + req_header = "Authorization" + resp_header = "www-authenticate" + + conn = HTTPSConnection(host=self.host, port=self.port) + + # Send negotiation message + headers[req_header] = "NTLM %s" % ntlm.create_NTLM_NEGOTIATE_MESSAGE( + self.rawuser + ) + log.debug("Request headers: %s", headers) + conn.request("GET", self.authurl, None, headers) + res = conn.getresponse() + reshdr = dict(res.getheaders()) + log.debug("Response status: %s %s", res.status, res.reason) + log.debug("Response headers: %s", reshdr) + log.debug("Response data: %s [...]", res.read(100)) + + # Remove the reference to the socket, so that it can not be closed by + # the response object (we want to keep the socket open) + res.fp = None + + # Server should respond with a challenge message + auth_header_values = reshdr[resp_header].split(", ") + auth_header_value = None + for s in auth_header_values: + if s[:5] == "NTLM ": + auth_header_value = s[5:] + if auth_header_value is None: + raise Exception( + "Unexpected %s response header: %s" % (resp_header, reshdr[resp_header]) + ) + + # Send authentication message + ServerChallenge, NegotiateFlags = ntlm.parse_NTLM_CHALLENGE_MESSAGE( + auth_header_value + ) + auth_msg = ntlm.create_NTLM_AUTHENTICATE_MESSAGE( + ServerChallenge, self.user, self.domain, self.pw, NegotiateFlags + ) + headers[req_header] = "NTLM %s" % auth_msg + log.debug("Request headers: %s", headers) + conn.request("GET", self.authurl, None, headers) + res = conn.getresponse() + log.debug("Response status: %s %s", res.status, res.reason) + log.debug("Response headers: %s", dict(res.getheaders())) + log.debug("Response data: %s [...]", res.read()[:100]) + if res.status != 200: + if res.status == 401: + raise Exception("Server rejected request: wrong username or password") + raise Exception("Wrong server response: %s %s" % (res.status, res.reason)) + + res.fp = None + log.debug("Connection established") + return conn + + def urlopen( + self, + method, + url, + body=None, + headers=None, + retries=3, + redirect=True, + assert_same_host=True, + ): + if headers is None: + headers = {} + headers["Connection"] = "Keep-Alive" + return super(NTLMConnectionPool, self).urlopen( + method, url, body, headers, retries, redirect, assert_same_host + ) diff --git a/venv/Lib/site-packages/urllib3/contrib/pyopenssl.py b/venv/Lib/site-packages/urllib3/contrib/pyopenssl.py new file mode 100644 index 00000000..81a80651 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/contrib/pyopenssl.py @@ -0,0 +1,501 @@ +""" +SSL with SNI_-support for Python 2. Follow these instructions if you would +like to verify SSL certificates in Python 2. Note, the default libraries do +*not* do certificate checking; you need to do additional work to validate +certificates yourself. + +This needs the following packages installed: + +* pyOpenSSL (tested with 16.0.0) +* cryptography (minimum 1.3.4, from pyopenssl) +* idna (minimum 2.0, from cryptography) + +However, pyopenssl depends on cryptography, which depends on idna, so while we +use all three directly here we end up having relatively few packages required. + +You can install them with the following command: + + pip install pyopenssl cryptography idna + +To activate certificate checking, call +:func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code +before you begin making HTTP requests. This can be done in a ``sitecustomize`` +module, or at any other time before your application begins using ``urllib3``, +like this:: + + try: + import urllib3.contrib.pyopenssl + urllib3.contrib.pyopenssl.inject_into_urllib3() + except ImportError: + pass + +Now you can use :mod:`urllib3` as you normally would, and it will support SNI +when the required modules are installed. + +Activating this module also has the positive side effect of disabling SSL/TLS +compression in Python 2 (see `CRIME attack`_). + +If you want to configure the default list of supported cipher suites, you can +set the ``urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST`` variable. + +.. _sni: https://en.wikipedia.org/wiki/Server_Name_Indication +.. _crime attack: https://en.wikipedia.org/wiki/CRIME_(security_exploit) +""" +from __future__ import absolute_import + +import OpenSSL.SSL +from cryptography import x509 +from cryptography.hazmat.backends.openssl import backend as openssl_backend +from cryptography.hazmat.backends.openssl.x509 import _Certificate + +try: + from cryptography.x509 import UnsupportedExtension +except ImportError: + # UnsupportedExtension is gone in cryptography >= 2.1.0 + class UnsupportedExtension(Exception): + pass + + +from socket import timeout, error as SocketError +from io import BytesIO + +try: # Platform-specific: Python 2 + from socket import _fileobject +except ImportError: # Platform-specific: Python 3 + _fileobject = None + from ..packages.backports.makefile import backport_makefile + +import logging +import ssl +from ..packages import six +import sys + +from .. import util + + +__all__ = ["inject_into_urllib3", "extract_from_urllib3"] + +# SNI always works. +HAS_SNI = True + +# Map from urllib3 to PyOpenSSL compatible parameter-values. +_openssl_versions = { + util.PROTOCOL_TLS: OpenSSL.SSL.SSLv23_METHOD, + ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD, +} + +if hasattr(ssl, "PROTOCOL_SSLv3") and hasattr(OpenSSL.SSL, "SSLv3_METHOD"): + _openssl_versions[ssl.PROTOCOL_SSLv3] = OpenSSL.SSL.SSLv3_METHOD + +if hasattr(ssl, "PROTOCOL_TLSv1_1") and hasattr(OpenSSL.SSL, "TLSv1_1_METHOD"): + _openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD + +if hasattr(ssl, "PROTOCOL_TLSv1_2") and hasattr(OpenSSL.SSL, "TLSv1_2_METHOD"): + _openssl_versions[ssl.PROTOCOL_TLSv1_2] = OpenSSL.SSL.TLSv1_2_METHOD + + +_stdlib_to_openssl_verify = { + ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE, + ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER, + ssl.CERT_REQUIRED: OpenSSL.SSL.VERIFY_PEER + + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, +} +_openssl_to_stdlib_verify = dict((v, k) for k, v in _stdlib_to_openssl_verify.items()) + +# OpenSSL will only write 16K at a time +SSL_WRITE_BLOCKSIZE = 16384 + +orig_util_HAS_SNI = util.HAS_SNI +orig_util_SSLContext = util.ssl_.SSLContext + + +log = logging.getLogger(__name__) + + +def inject_into_urllib3(): + "Monkey-patch urllib3 with PyOpenSSL-backed SSL-support." + + _validate_dependencies_met() + + util.SSLContext = PyOpenSSLContext + util.ssl_.SSLContext = PyOpenSSLContext + util.HAS_SNI = HAS_SNI + util.ssl_.HAS_SNI = HAS_SNI + util.IS_PYOPENSSL = True + util.ssl_.IS_PYOPENSSL = True + + +def extract_from_urllib3(): + "Undo monkey-patching by :func:`inject_into_urllib3`." + + util.SSLContext = orig_util_SSLContext + util.ssl_.SSLContext = orig_util_SSLContext + util.HAS_SNI = orig_util_HAS_SNI + util.ssl_.HAS_SNI = orig_util_HAS_SNI + util.IS_PYOPENSSL = False + util.ssl_.IS_PYOPENSSL = False + + +def _validate_dependencies_met(): + """ + Verifies that PyOpenSSL's package-level dependencies have been met. + Throws `ImportError` if they are not met. + """ + # Method added in `cryptography==1.1`; not available in older versions + from cryptography.x509.extensions import Extensions + + if getattr(Extensions, "get_extension_for_class", None) is None: + raise ImportError( + "'cryptography' module missing required functionality. " + "Try upgrading to v1.3.4 or newer." + ) + + # pyOpenSSL 0.14 and above use cryptography for OpenSSL bindings. The _x509 + # attribute is only present on those versions. + from OpenSSL.crypto import X509 + + x509 = X509() + if getattr(x509, "_x509", None) is None: + raise ImportError( + "'pyOpenSSL' module missing required functionality. " + "Try upgrading to v0.14 or newer." + ) + + +def _dnsname_to_stdlib(name): + """ + Converts a dNSName SubjectAlternativeName field to the form used by the + standard library on the given Python version. + + Cryptography produces a dNSName as a unicode string that was idna-decoded + from ASCII bytes. We need to idna-encode that string to get it back, and + then on Python 3 we also need to convert to unicode via UTF-8 (the stdlib + uses PyUnicode_FromStringAndSize on it, which decodes via UTF-8). + + If the name cannot be idna-encoded then we return None signalling that + the name given should be skipped. + """ + + def idna_encode(name): + """ + Borrowed wholesale from the Python Cryptography Project. It turns out + that we can't just safely call `idna.encode`: it can explode for + wildcard names. This avoids that problem. + """ + import idna + + try: + for prefix in [u"*.", u"."]: + if name.startswith(prefix): + name = name[len(prefix) :] + return prefix.encode("ascii") + idna.encode(name) + return idna.encode(name) + except idna.core.IDNAError: + return None + + # Don't send IPv6 addresses through the IDNA encoder. + if ":" in name: + return name + + name = idna_encode(name) + if name is None: + return None + elif sys.version_info >= (3, 0): + name = name.decode("utf-8") + return name + + +def get_subj_alt_name(peer_cert): + """ + Given an PyOpenSSL certificate, provides all the subject alternative names. + """ + # Pass the cert to cryptography, which has much better APIs for this. + if hasattr(peer_cert, "to_cryptography"): + cert = peer_cert.to_cryptography() + else: + # This is technically using private APIs, but should work across all + # relevant versions before PyOpenSSL got a proper API for this. + cert = _Certificate(openssl_backend, peer_cert._x509) + + # We want to find the SAN extension. Ask Cryptography to locate it (it's + # faster than looping in Python) + try: + ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName).value + except x509.ExtensionNotFound: + # No such extension, return the empty list. + return [] + except ( + x509.DuplicateExtension, + UnsupportedExtension, + x509.UnsupportedGeneralNameType, + UnicodeError, + ) as e: + # A problem has been found with the quality of the certificate. Assume + # no SAN field is present. + log.warning( + "A problem was encountered with the certificate that prevented " + "urllib3 from finding the SubjectAlternativeName field. This can " + "affect certificate validation. The error was %s", + e, + ) + return [] + + # We want to return dNSName and iPAddress fields. We need to cast the IPs + # back to strings because the match_hostname function wants them as + # strings. + # Sadly the DNS names need to be idna encoded and then, on Python 3, UTF-8 + # decoded. This is pretty frustrating, but that's what the standard library + # does with certificates, and so we need to attempt to do the same. + # We also want to skip over names which cannot be idna encoded. + names = [ + ("DNS", name) + for name in map(_dnsname_to_stdlib, ext.get_values_for_type(x509.DNSName)) + if name is not None + ] + names.extend( + ("IP Address", str(name)) for name in ext.get_values_for_type(x509.IPAddress) + ) + + return names + + +class WrappedSocket(object): + """API-compatibility wrapper for Python OpenSSL's Connection-class. + + Note: _makefile_refs, _drop() and _reuse() are needed for the garbage + collector of pypy. + """ + + def __init__(self, connection, socket, suppress_ragged_eofs=True): + self.connection = connection + self.socket = socket + self.suppress_ragged_eofs = suppress_ragged_eofs + self._makefile_refs = 0 + self._closed = False + + def fileno(self): + return self.socket.fileno() + + # Copy-pasted from Python 3.5 source code + def _decref_socketios(self): + if self._makefile_refs > 0: + self._makefile_refs -= 1 + if self._closed: + self.close() + + def recv(self, *args, **kwargs): + try: + data = self.connection.recv(*args, **kwargs) + except OpenSSL.SSL.SysCallError as e: + if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"): + return b"" + else: + raise SocketError(str(e)) + except OpenSSL.SSL.ZeroReturnError: + if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: + return b"" + else: + raise + except OpenSSL.SSL.WantReadError: + if not util.wait_for_read(self.socket, self.socket.gettimeout()): + raise timeout("The read operation timed out") + else: + return self.recv(*args, **kwargs) + + # TLS 1.3 post-handshake authentication + except OpenSSL.SSL.Error as e: + raise ssl.SSLError("read error: %r" % e) + else: + return data + + def recv_into(self, *args, **kwargs): + try: + return self.connection.recv_into(*args, **kwargs) + except OpenSSL.SSL.SysCallError as e: + if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"): + return 0 + else: + raise SocketError(str(e)) + except OpenSSL.SSL.ZeroReturnError: + if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: + return 0 + else: + raise + except OpenSSL.SSL.WantReadError: + if not util.wait_for_read(self.socket, self.socket.gettimeout()): + raise timeout("The read operation timed out") + else: + return self.recv_into(*args, **kwargs) + + # TLS 1.3 post-handshake authentication + except OpenSSL.SSL.Error as e: + raise ssl.SSLError("read error: %r" % e) + + def settimeout(self, timeout): + return self.socket.settimeout(timeout) + + def _send_until_done(self, data): + while True: + try: + return self.connection.send(data) + except OpenSSL.SSL.WantWriteError: + if not util.wait_for_write(self.socket, self.socket.gettimeout()): + raise timeout() + continue + except OpenSSL.SSL.SysCallError as e: + raise SocketError(str(e)) + + def sendall(self, data): + total_sent = 0 + while total_sent < len(data): + sent = self._send_until_done( + data[total_sent : total_sent + SSL_WRITE_BLOCKSIZE] + ) + total_sent += sent + + def shutdown(self): + # FIXME rethrow compatible exceptions should we ever use this + self.connection.shutdown() + + def close(self): + if self._makefile_refs < 1: + try: + self._closed = True + return self.connection.close() + except OpenSSL.SSL.Error: + return + else: + self._makefile_refs -= 1 + + def getpeercert(self, binary_form=False): + x509 = self.connection.get_peer_certificate() + + if not x509: + return x509 + + if binary_form: + return OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, x509) + + return { + "subject": ((("commonName", x509.get_subject().CN),),), + "subjectAltName": get_subj_alt_name(x509), + } + + def version(self): + return self.connection.get_protocol_version_name() + + def _reuse(self): + self._makefile_refs += 1 + + def _drop(self): + if self._makefile_refs < 1: + self.close() + else: + self._makefile_refs -= 1 + + +if _fileobject: # Platform-specific: Python 2 + + def makefile(self, mode, bufsize=-1): + self._makefile_refs += 1 + return _fileobject(self, mode, bufsize, close=True) + + +else: # Platform-specific: Python 3 + makefile = backport_makefile + +WrappedSocket.makefile = makefile + + +class PyOpenSSLContext(object): + """ + I am a wrapper class for the PyOpenSSL ``Context`` object. I am responsible + for translating the interface of the standard library ``SSLContext`` object + to calls into PyOpenSSL. + """ + + def __init__(self, protocol): + self.protocol = _openssl_versions[protocol] + self._ctx = OpenSSL.SSL.Context(self.protocol) + self._options = 0 + self.check_hostname = False + + @property + def options(self): + return self._options + + @options.setter + def options(self, value): + self._options = value + self._ctx.set_options(value) + + @property + def verify_mode(self): + return _openssl_to_stdlib_verify[self._ctx.get_verify_mode()] + + @verify_mode.setter + def verify_mode(self, value): + self._ctx.set_verify(_stdlib_to_openssl_verify[value], _verify_callback) + + def set_default_verify_paths(self): + self._ctx.set_default_verify_paths() + + def set_ciphers(self, ciphers): + if isinstance(ciphers, six.text_type): + ciphers = ciphers.encode("utf-8") + self._ctx.set_cipher_list(ciphers) + + def load_verify_locations(self, cafile=None, capath=None, cadata=None): + if cafile is not None: + cafile = cafile.encode("utf-8") + if capath is not None: + capath = capath.encode("utf-8") + try: + self._ctx.load_verify_locations(cafile, capath) + if cadata is not None: + self._ctx.load_verify_locations(BytesIO(cadata)) + except OpenSSL.SSL.Error as e: + raise ssl.SSLError("unable to load trusted certificates: %r" % e) + + def load_cert_chain(self, certfile, keyfile=None, password=None): + self._ctx.use_certificate_chain_file(certfile) + if password is not None: + if not isinstance(password, six.binary_type): + password = password.encode("utf-8") + self._ctx.set_passwd_cb(lambda *_: password) + self._ctx.use_privatekey_file(keyfile or certfile) + + def wrap_socket( + self, + sock, + server_side=False, + do_handshake_on_connect=True, + suppress_ragged_eofs=True, + server_hostname=None, + ): + cnx = OpenSSL.SSL.Connection(self._ctx, sock) + + if isinstance(server_hostname, six.text_type): # Platform-specific: Python 3 + server_hostname = server_hostname.encode("utf-8") + + if server_hostname is not None: + cnx.set_tlsext_host_name(server_hostname) + + cnx.set_connect_state() + + while True: + try: + cnx.do_handshake() + except OpenSSL.SSL.WantReadError: + if not util.wait_for_read(sock, sock.gettimeout()): + raise timeout("select timed out") + continue + except OpenSSL.SSL.Error as e: + raise ssl.SSLError("bad handshake: %r" % e) + break + + return WrappedSocket(cnx, sock) + + +def _verify_callback(cnx, x509, err_no, err_depth, return_code): + return err_no == 0 diff --git a/venv/Lib/site-packages/urllib3/contrib/securetransport.py b/venv/Lib/site-packages/urllib3/contrib/securetransport.py new file mode 100644 index 00000000..a6b7e94a --- /dev/null +++ b/venv/Lib/site-packages/urllib3/contrib/securetransport.py @@ -0,0 +1,864 @@ +""" +SecureTranport support for urllib3 via ctypes. + +This makes platform-native TLS available to urllib3 users on macOS without the +use of a compiler. This is an important feature because the Python Package +Index is moving to become a TLSv1.2-or-higher server, and the default OpenSSL +that ships with macOS is not capable of doing TLSv1.2. The only way to resolve +this is to give macOS users an alternative solution to the problem, and that +solution is to use SecureTransport. + +We use ctypes here because this solution must not require a compiler. That's +because pip is not allowed to require a compiler either. + +This is not intended to be a seriously long-term solution to this problem. +The hope is that PEP 543 will eventually solve this issue for us, at which +point we can retire this contrib module. But in the short term, we need to +solve the impending tire fire that is Python on Mac without this kind of +contrib module. So...here we are. + +To use this module, simply import and inject it:: + + import urllib3.contrib.securetransport + urllib3.contrib.securetransport.inject_into_urllib3() + +Happy TLSing! + +This code is a bastardised version of the code found in Will Bond's oscrypto +library. An enormous debt is owed to him for blazing this trail for us. For +that reason, this code should be considered to be covered both by urllib3's +license and by oscrypto's: + + Copyright (c) 2015-2016 Will Bond + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +""" +from __future__ import absolute_import + +import contextlib +import ctypes +import errno +import os.path +import shutil +import socket +import ssl +import threading +import weakref + +from .. import util +from ._securetransport.bindings import Security, SecurityConst, CoreFoundation +from ._securetransport.low_level import ( + _assert_no_error, + _cert_array_from_pem, + _temporary_keychain, + _load_client_cert_chain, +) + +try: # Platform-specific: Python 2 + from socket import _fileobject +except ImportError: # Platform-specific: Python 3 + _fileobject = None + from ..packages.backports.makefile import backport_makefile + +__all__ = ["inject_into_urllib3", "extract_from_urllib3"] + +# SNI always works +HAS_SNI = True + +orig_util_HAS_SNI = util.HAS_SNI +orig_util_SSLContext = util.ssl_.SSLContext + +# This dictionary is used by the read callback to obtain a handle to the +# calling wrapped socket. This is a pretty silly approach, but for now it'll +# do. I feel like I should be able to smuggle a handle to the wrapped socket +# directly in the SSLConnectionRef, but for now this approach will work I +# guess. +# +# We need to lock around this structure for inserts, but we don't do it for +# reads/writes in the callbacks. The reasoning here goes as follows: +# +# 1. It is not possible to call into the callbacks before the dictionary is +# populated, so once in the callback the id must be in the dictionary. +# 2. The callbacks don't mutate the dictionary, they only read from it, and +# so cannot conflict with any of the insertions. +# +# This is good: if we had to lock in the callbacks we'd drastically slow down +# the performance of this code. +_connection_refs = weakref.WeakValueDictionary() +_connection_ref_lock = threading.Lock() + +# Limit writes to 16kB. This is OpenSSL's limit, but we'll cargo-cult it over +# for no better reason than we need *a* limit, and this one is right there. +SSL_WRITE_BLOCKSIZE = 16384 + +# This is our equivalent of util.ssl_.DEFAULT_CIPHERS, but expanded out to +# individual cipher suites. We need to do this because this is how +# SecureTransport wants them. +CIPHER_SUITES = [ + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + SecurityConst.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + SecurityConst.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + SecurityConst.TLS_AES_256_GCM_SHA384, + SecurityConst.TLS_AES_128_GCM_SHA256, + SecurityConst.TLS_RSA_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_RSA_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_AES_128_CCM_8_SHA256, + SecurityConst.TLS_AES_128_CCM_SHA256, + SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA256, + SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA256, + SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA, +] + +# Basically this is simple: for PROTOCOL_SSLv23 we turn it into a low of +# TLSv1 and a high of TLSv1.2. For everything else, we pin to that version. +# TLSv1 to 1.2 are supported on macOS 10.8+ +_protocol_to_min_max = { + util.PROTOCOL_TLS: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12) +} + +if hasattr(ssl, "PROTOCOL_SSLv2"): + _protocol_to_min_max[ssl.PROTOCOL_SSLv2] = ( + SecurityConst.kSSLProtocol2, + SecurityConst.kSSLProtocol2, + ) +if hasattr(ssl, "PROTOCOL_SSLv3"): + _protocol_to_min_max[ssl.PROTOCOL_SSLv3] = ( + SecurityConst.kSSLProtocol3, + SecurityConst.kSSLProtocol3, + ) +if hasattr(ssl, "PROTOCOL_TLSv1"): + _protocol_to_min_max[ssl.PROTOCOL_TLSv1] = ( + SecurityConst.kTLSProtocol1, + SecurityConst.kTLSProtocol1, + ) +if hasattr(ssl, "PROTOCOL_TLSv1_1"): + _protocol_to_min_max[ssl.PROTOCOL_TLSv1_1] = ( + SecurityConst.kTLSProtocol11, + SecurityConst.kTLSProtocol11, + ) +if hasattr(ssl, "PROTOCOL_TLSv1_2"): + _protocol_to_min_max[ssl.PROTOCOL_TLSv1_2] = ( + SecurityConst.kTLSProtocol12, + SecurityConst.kTLSProtocol12, + ) + + +def inject_into_urllib3(): + """ + Monkey-patch urllib3 with SecureTransport-backed SSL-support. + """ + util.SSLContext = SecureTransportContext + util.ssl_.SSLContext = SecureTransportContext + util.HAS_SNI = HAS_SNI + util.ssl_.HAS_SNI = HAS_SNI + util.IS_SECURETRANSPORT = True + util.ssl_.IS_SECURETRANSPORT = True + + +def extract_from_urllib3(): + """ + Undo monkey-patching by :func:`inject_into_urllib3`. + """ + util.SSLContext = orig_util_SSLContext + util.ssl_.SSLContext = orig_util_SSLContext + util.HAS_SNI = orig_util_HAS_SNI + util.ssl_.HAS_SNI = orig_util_HAS_SNI + util.IS_SECURETRANSPORT = False + util.ssl_.IS_SECURETRANSPORT = False + + +def _read_callback(connection_id, data_buffer, data_length_pointer): + """ + SecureTransport read callback. This is called by ST to request that data + be returned from the socket. + """ + wrapped_socket = None + try: + wrapped_socket = _connection_refs.get(connection_id) + if wrapped_socket is None: + return SecurityConst.errSSLInternal + base_socket = wrapped_socket.socket + + requested_length = data_length_pointer[0] + + timeout = wrapped_socket.gettimeout() + error = None + read_count = 0 + + try: + while read_count < requested_length: + if timeout is None or timeout >= 0: + if not util.wait_for_read(base_socket, timeout): + raise socket.error(errno.EAGAIN, "timed out") + + remaining = requested_length - read_count + buffer = (ctypes.c_char * remaining).from_address( + data_buffer + read_count + ) + chunk_size = base_socket.recv_into(buffer, remaining) + read_count += chunk_size + if not chunk_size: + if not read_count: + return SecurityConst.errSSLClosedGraceful + break + except (socket.error) as e: + error = e.errno + + if error is not None and error != errno.EAGAIN: + data_length_pointer[0] = read_count + if error == errno.ECONNRESET or error == errno.EPIPE: + return SecurityConst.errSSLClosedAbort + raise + + data_length_pointer[0] = read_count + + if read_count != requested_length: + return SecurityConst.errSSLWouldBlock + + return 0 + except Exception as e: + if wrapped_socket is not None: + wrapped_socket._exception = e + return SecurityConst.errSSLInternal + + +def _write_callback(connection_id, data_buffer, data_length_pointer): + """ + SecureTransport write callback. This is called by ST to request that data + actually be sent on the network. + """ + wrapped_socket = None + try: + wrapped_socket = _connection_refs.get(connection_id) + if wrapped_socket is None: + return SecurityConst.errSSLInternal + base_socket = wrapped_socket.socket + + bytes_to_write = data_length_pointer[0] + data = ctypes.string_at(data_buffer, bytes_to_write) + + timeout = wrapped_socket.gettimeout() + error = None + sent = 0 + + try: + while sent < bytes_to_write: + if timeout is None or timeout >= 0: + if not util.wait_for_write(base_socket, timeout): + raise socket.error(errno.EAGAIN, "timed out") + chunk_sent = base_socket.send(data) + sent += chunk_sent + + # This has some needless copying here, but I'm not sure there's + # much value in optimising this data path. + data = data[chunk_sent:] + except (socket.error) as e: + error = e.errno + + if error is not None and error != errno.EAGAIN: + data_length_pointer[0] = sent + if error == errno.ECONNRESET or error == errno.EPIPE: + return SecurityConst.errSSLClosedAbort + raise + + data_length_pointer[0] = sent + + if sent != bytes_to_write: + return SecurityConst.errSSLWouldBlock + + return 0 + except Exception as e: + if wrapped_socket is not None: + wrapped_socket._exception = e + return SecurityConst.errSSLInternal + + +# We need to keep these two objects references alive: if they get GC'd while +# in use then SecureTransport could attempt to call a function that is in freed +# memory. That would be...uh...bad. Yeah, that's the word. Bad. +_read_callback_pointer = Security.SSLReadFunc(_read_callback) +_write_callback_pointer = Security.SSLWriteFunc(_write_callback) + + +class WrappedSocket(object): + """ + API-compatibility wrapper for Python's OpenSSL wrapped socket object. + + Note: _makefile_refs, _drop(), and _reuse() are needed for the garbage + collector of PyPy. + """ + + def __init__(self, socket): + self.socket = socket + self.context = None + self._makefile_refs = 0 + self._closed = False + self._exception = None + self._keychain = None + self._keychain_dir = None + self._client_cert_chain = None + + # We save off the previously-configured timeout and then set it to + # zero. This is done because we use select and friends to handle the + # timeouts, but if we leave the timeout set on the lower socket then + # Python will "kindly" call select on that socket again for us. Avoid + # that by forcing the timeout to zero. + self._timeout = self.socket.gettimeout() + self.socket.settimeout(0) + + @contextlib.contextmanager + def _raise_on_error(self): + """ + A context manager that can be used to wrap calls that do I/O from + SecureTransport. If any of the I/O callbacks hit an exception, this + context manager will correctly propagate the exception after the fact. + This avoids silently swallowing those exceptions. + + It also correctly forces the socket closed. + """ + self._exception = None + + # We explicitly don't catch around this yield because in the unlikely + # event that an exception was hit in the block we don't want to swallow + # it. + yield + if self._exception is not None: + exception, self._exception = self._exception, None + self.close() + raise exception + + def _set_ciphers(self): + """ + Sets up the allowed ciphers. By default this matches the set in + util.ssl_.DEFAULT_CIPHERS, at least as supported by macOS. This is done + custom and doesn't allow changing at this time, mostly because parsing + OpenSSL cipher strings is going to be a freaking nightmare. + """ + ciphers = (Security.SSLCipherSuite * len(CIPHER_SUITES))(*CIPHER_SUITES) + result = Security.SSLSetEnabledCiphers( + self.context, ciphers, len(CIPHER_SUITES) + ) + _assert_no_error(result) + + def _custom_validate(self, verify, trust_bundle): + """ + Called when we have set custom validation. We do this in two cases: + first, when cert validation is entirely disabled; and second, when + using a custom trust DB. + """ + # If we disabled cert validation, just say: cool. + if not verify: + return + + # We want data in memory, so load it up. + if os.path.isfile(trust_bundle): + with open(trust_bundle, "rb") as f: + trust_bundle = f.read() + + cert_array = None + trust = Security.SecTrustRef() + + try: + # Get a CFArray that contains the certs we want. + cert_array = _cert_array_from_pem(trust_bundle) + + # Ok, now the hard part. We want to get the SecTrustRef that ST has + # created for this connection, shove our CAs into it, tell ST to + # ignore everything else it knows, and then ask if it can build a + # chain. This is a buuuunch of code. + result = Security.SSLCopyPeerTrust(self.context, ctypes.byref(trust)) + _assert_no_error(result) + if not trust: + raise ssl.SSLError("Failed to copy trust reference") + + result = Security.SecTrustSetAnchorCertificates(trust, cert_array) + _assert_no_error(result) + + result = Security.SecTrustSetAnchorCertificatesOnly(trust, True) + _assert_no_error(result) + + trust_result = Security.SecTrustResultType() + result = Security.SecTrustEvaluate(trust, ctypes.byref(trust_result)) + _assert_no_error(result) + finally: + if trust: + CoreFoundation.CFRelease(trust) + + if cert_array is not None: + CoreFoundation.CFRelease(cert_array) + + # Ok, now we can look at what the result was. + successes = ( + SecurityConst.kSecTrustResultUnspecified, + SecurityConst.kSecTrustResultProceed, + ) + if trust_result.value not in successes: + raise ssl.SSLError( + "certificate verify failed, error code: %d" % trust_result.value + ) + + def handshake( + self, + server_hostname, + verify, + trust_bundle, + min_version, + max_version, + client_cert, + client_key, + client_key_passphrase, + ): + """ + Actually performs the TLS handshake. This is run automatically by + wrapped socket, and shouldn't be needed in user code. + """ + # First, we do the initial bits of connection setup. We need to create + # a context, set its I/O funcs, and set the connection reference. + self.context = Security.SSLCreateContext( + None, SecurityConst.kSSLClientSide, SecurityConst.kSSLStreamType + ) + result = Security.SSLSetIOFuncs( + self.context, _read_callback_pointer, _write_callback_pointer + ) + _assert_no_error(result) + + # Here we need to compute the handle to use. We do this by taking the + # id of self modulo 2**31 - 1. If this is already in the dictionary, we + # just keep incrementing by one until we find a free space. + with _connection_ref_lock: + handle = id(self) % 2147483647 + while handle in _connection_refs: + handle = (handle + 1) % 2147483647 + _connection_refs[handle] = self + + result = Security.SSLSetConnection(self.context, handle) + _assert_no_error(result) + + # If we have a server hostname, we should set that too. + if server_hostname: + if not isinstance(server_hostname, bytes): + server_hostname = server_hostname.encode("utf-8") + + result = Security.SSLSetPeerDomainName( + self.context, server_hostname, len(server_hostname) + ) + _assert_no_error(result) + + # Setup the ciphers. + self._set_ciphers() + + # Set the minimum and maximum TLS versions. + result = Security.SSLSetProtocolVersionMin(self.context, min_version) + _assert_no_error(result) + + result = Security.SSLSetProtocolVersionMax(self.context, max_version) + _assert_no_error(result) + + # If there's a trust DB, we need to use it. We do that by telling + # SecureTransport to break on server auth. We also do that if we don't + # want to validate the certs at all: we just won't actually do any + # authing in that case. + if not verify or trust_bundle is not None: + result = Security.SSLSetSessionOption( + self.context, SecurityConst.kSSLSessionOptionBreakOnServerAuth, True + ) + _assert_no_error(result) + + # If there's a client cert, we need to use it. + if client_cert: + self._keychain, self._keychain_dir = _temporary_keychain() + self._client_cert_chain = _load_client_cert_chain( + self._keychain, client_cert, client_key + ) + result = Security.SSLSetCertificate(self.context, self._client_cert_chain) + _assert_no_error(result) + + while True: + with self._raise_on_error(): + result = Security.SSLHandshake(self.context) + + if result == SecurityConst.errSSLWouldBlock: + raise socket.timeout("handshake timed out") + elif result == SecurityConst.errSSLServerAuthCompleted: + self._custom_validate(verify, trust_bundle) + continue + else: + _assert_no_error(result) + break + + def fileno(self): + return self.socket.fileno() + + # Copy-pasted from Python 3.5 source code + def _decref_socketios(self): + if self._makefile_refs > 0: + self._makefile_refs -= 1 + if self._closed: + self.close() + + def recv(self, bufsiz): + buffer = ctypes.create_string_buffer(bufsiz) + bytes_read = self.recv_into(buffer, bufsiz) + data = buffer[:bytes_read] + return data + + def recv_into(self, buffer, nbytes=None): + # Read short on EOF. + if self._closed: + return 0 + + if nbytes is None: + nbytes = len(buffer) + + buffer = (ctypes.c_char * nbytes).from_buffer(buffer) + processed_bytes = ctypes.c_size_t(0) + + with self._raise_on_error(): + result = Security.SSLRead( + self.context, buffer, nbytes, ctypes.byref(processed_bytes) + ) + + # There are some result codes that we want to treat as "not always + # errors". Specifically, those are errSSLWouldBlock, + # errSSLClosedGraceful, and errSSLClosedNoNotify. + if result == SecurityConst.errSSLWouldBlock: + # If we didn't process any bytes, then this was just a time out. + # However, we can get errSSLWouldBlock in situations when we *did* + # read some data, and in those cases we should just read "short" + # and return. + if processed_bytes.value == 0: + # Timed out, no data read. + raise socket.timeout("recv timed out") + elif result in ( + SecurityConst.errSSLClosedGraceful, + SecurityConst.errSSLClosedNoNotify, + ): + # The remote peer has closed this connection. We should do so as + # well. Note that we don't actually return here because in + # principle this could actually be fired along with return data. + # It's unlikely though. + self.close() + else: + _assert_no_error(result) + + # Ok, we read and probably succeeded. We should return whatever data + # was actually read. + return processed_bytes.value + + def settimeout(self, timeout): + self._timeout = timeout + + def gettimeout(self): + return self._timeout + + def send(self, data): + processed_bytes = ctypes.c_size_t(0) + + with self._raise_on_error(): + result = Security.SSLWrite( + self.context, data, len(data), ctypes.byref(processed_bytes) + ) + + if result == SecurityConst.errSSLWouldBlock and processed_bytes.value == 0: + # Timed out + raise socket.timeout("send timed out") + else: + _assert_no_error(result) + + # We sent, and probably succeeded. Tell them how much we sent. + return processed_bytes.value + + def sendall(self, data): + total_sent = 0 + while total_sent < len(data): + sent = self.send(data[total_sent : total_sent + SSL_WRITE_BLOCKSIZE]) + total_sent += sent + + def shutdown(self): + with self._raise_on_error(): + Security.SSLClose(self.context) + + def close(self): + # TODO: should I do clean shutdown here? Do I have to? + if self._makefile_refs < 1: + self._closed = True + if self.context: + CoreFoundation.CFRelease(self.context) + self.context = None + if self._client_cert_chain: + CoreFoundation.CFRelease(self._client_cert_chain) + self._client_cert_chain = None + if self._keychain: + Security.SecKeychainDelete(self._keychain) + CoreFoundation.CFRelease(self._keychain) + shutil.rmtree(self._keychain_dir) + self._keychain = self._keychain_dir = None + return self.socket.close() + else: + self._makefile_refs -= 1 + + def getpeercert(self, binary_form=False): + # Urgh, annoying. + # + # Here's how we do this: + # + # 1. Call SSLCopyPeerTrust to get hold of the trust object for this + # connection. + # 2. Call SecTrustGetCertificateAtIndex for index 0 to get the leaf. + # 3. To get the CN, call SecCertificateCopyCommonName and process that + # string so that it's of the appropriate type. + # 4. To get the SAN, we need to do something a bit more complex: + # a. Call SecCertificateCopyValues to get the data, requesting + # kSecOIDSubjectAltName. + # b. Mess about with this dictionary to try to get the SANs out. + # + # This is gross. Really gross. It's going to be a few hundred LoC extra + # just to repeat something that SecureTransport can *already do*. So my + # operating assumption at this time is that what we want to do is + # instead to just flag to urllib3 that it shouldn't do its own hostname + # validation when using SecureTransport. + if not binary_form: + raise ValueError("SecureTransport only supports dumping binary certs") + trust = Security.SecTrustRef() + certdata = None + der_bytes = None + + try: + # Grab the trust store. + result = Security.SSLCopyPeerTrust(self.context, ctypes.byref(trust)) + _assert_no_error(result) + if not trust: + # Probably we haven't done the handshake yet. No biggie. + return None + + cert_count = Security.SecTrustGetCertificateCount(trust) + if not cert_count: + # Also a case that might happen if we haven't handshaked. + # Handshook? Handshaken? + return None + + leaf = Security.SecTrustGetCertificateAtIndex(trust, 0) + assert leaf + + # Ok, now we want the DER bytes. + certdata = Security.SecCertificateCopyData(leaf) + assert certdata + + data_length = CoreFoundation.CFDataGetLength(certdata) + data_buffer = CoreFoundation.CFDataGetBytePtr(certdata) + der_bytes = ctypes.string_at(data_buffer, data_length) + finally: + if certdata: + CoreFoundation.CFRelease(certdata) + if trust: + CoreFoundation.CFRelease(trust) + + return der_bytes + + def version(self): + protocol = Security.SSLProtocol() + result = Security.SSLGetNegotiatedProtocolVersion( + self.context, ctypes.byref(protocol) + ) + _assert_no_error(result) + if protocol.value == SecurityConst.kTLSProtocol13: + raise ssl.SSLError("SecureTransport does not support TLS 1.3") + elif protocol.value == SecurityConst.kTLSProtocol12: + return "TLSv1.2" + elif protocol.value == SecurityConst.kTLSProtocol11: + return "TLSv1.1" + elif protocol.value == SecurityConst.kTLSProtocol1: + return "TLSv1" + elif protocol.value == SecurityConst.kSSLProtocol3: + return "SSLv3" + elif protocol.value == SecurityConst.kSSLProtocol2: + return "SSLv2" + else: + raise ssl.SSLError("Unknown TLS version: %r" % protocol) + + def _reuse(self): + self._makefile_refs += 1 + + def _drop(self): + if self._makefile_refs < 1: + self.close() + else: + self._makefile_refs -= 1 + + +if _fileobject: # Platform-specific: Python 2 + + def makefile(self, mode, bufsize=-1): + self._makefile_refs += 1 + return _fileobject(self, mode, bufsize, close=True) + + +else: # Platform-specific: Python 3 + + def makefile(self, mode="r", buffering=None, *args, **kwargs): + # We disable buffering with SecureTransport because it conflicts with + # the buffering that ST does internally (see issue #1153 for more). + buffering = 0 + return backport_makefile(self, mode, buffering, *args, **kwargs) + + +WrappedSocket.makefile = makefile + + +class SecureTransportContext(object): + """ + I am a wrapper class for the SecureTransport library, to translate the + interface of the standard library ``SSLContext`` object to calls into + SecureTransport. + """ + + def __init__(self, protocol): + self._min_version, self._max_version = _protocol_to_min_max[protocol] + self._options = 0 + self._verify = False + self._trust_bundle = None + self._client_cert = None + self._client_key = None + self._client_key_passphrase = None + + @property + def check_hostname(self): + """ + SecureTransport cannot have its hostname checking disabled. For more, + see the comment on getpeercert() in this file. + """ + return True + + @check_hostname.setter + def check_hostname(self, value): + """ + SecureTransport cannot have its hostname checking disabled. For more, + see the comment on getpeercert() in this file. + """ + pass + + @property + def options(self): + # TODO: Well, crap. + # + # So this is the bit of the code that is the most likely to cause us + # trouble. Essentially we need to enumerate all of the SSL options that + # users might want to use and try to see if we can sensibly translate + # them, or whether we should just ignore them. + return self._options + + @options.setter + def options(self, value): + # TODO: Update in line with above. + self._options = value + + @property + def verify_mode(self): + return ssl.CERT_REQUIRED if self._verify else ssl.CERT_NONE + + @verify_mode.setter + def verify_mode(self, value): + self._verify = True if value == ssl.CERT_REQUIRED else False + + def set_default_verify_paths(self): + # So, this has to do something a bit weird. Specifically, what it does + # is nothing. + # + # This means that, if we had previously had load_verify_locations + # called, this does not undo that. We need to do that because it turns + # out that the rest of the urllib3 code will attempt to load the + # default verify paths if it hasn't been told about any paths, even if + # the context itself was sometime earlier. We resolve that by just + # ignoring it. + pass + + def load_default_certs(self): + return self.set_default_verify_paths() + + def set_ciphers(self, ciphers): + # For now, we just require the default cipher string. + if ciphers != util.ssl_.DEFAULT_CIPHERS: + raise ValueError("SecureTransport doesn't support custom cipher strings") + + def load_verify_locations(self, cafile=None, capath=None, cadata=None): + # OK, we only really support cadata and cafile. + if capath is not None: + raise ValueError("SecureTransport does not support cert directories") + + # Raise if cafile does not exist. + if cafile is not None: + with open(cafile): + pass + + self._trust_bundle = cafile or cadata + + def load_cert_chain(self, certfile, keyfile=None, password=None): + self._client_cert = certfile + self._client_key = keyfile + self._client_cert_passphrase = password + + def wrap_socket( + self, + sock, + server_side=False, + do_handshake_on_connect=True, + suppress_ragged_eofs=True, + server_hostname=None, + ): + # So, what do we do here? Firstly, we assert some properties. This is a + # stripped down shim, so there is some functionality we don't support. + # See PEP 543 for the real deal. + assert not server_side + assert do_handshake_on_connect + assert suppress_ragged_eofs + + # Ok, we're good to go. Now we want to create the wrapped socket object + # and store it in the appropriate place. + wrapped_socket = WrappedSocket(sock) + + # Now we can handshake + wrapped_socket.handshake( + server_hostname, + self._verify, + self._trust_bundle, + self._min_version, + self._max_version, + self._client_cert, + self._client_key, + self._client_key_passphrase, + ) + return wrapped_socket diff --git a/venv/Lib/site-packages/urllib3/contrib/socks.py b/venv/Lib/site-packages/urllib3/contrib/socks.py new file mode 100644 index 00000000..9e97f7aa --- /dev/null +++ b/venv/Lib/site-packages/urllib3/contrib/socks.py @@ -0,0 +1,210 @@ +# -*- coding: utf-8 -*- +""" +This module contains provisional support for SOCKS proxies from within +urllib3. This module supports SOCKS4, SOCKS4A (an extension of SOCKS4), and +SOCKS5. To enable its functionality, either install PySocks or install this +module with the ``socks`` extra. + +The SOCKS implementation supports the full range of urllib3 features. It also +supports the following SOCKS features: + +- SOCKS4A (``proxy_url='socks4a://...``) +- SOCKS4 (``proxy_url='socks4://...``) +- SOCKS5 with remote DNS (``proxy_url='socks5h://...``) +- SOCKS5 with local DNS (``proxy_url='socks5://...``) +- Usernames and passwords for the SOCKS proxy + + .. note:: + It is recommended to use ``socks5h://`` or ``socks4a://`` schemes in + your ``proxy_url`` to ensure that DNS resolution is done from the remote + server instead of client-side when connecting to a domain name. + +SOCKS4 supports IPv4 and domain names with the SOCKS4A extension. SOCKS5 +supports IPv4, IPv6, and domain names. + +When connecting to a SOCKS4 proxy the ``username`` portion of the ``proxy_url`` +will be sent as the ``userid`` section of the SOCKS request:: + + proxy_url="socks4a://@proxy-host" + +When connecting to a SOCKS5 proxy the ``username`` and ``password`` portion +of the ``proxy_url`` will be sent as the username/password to authenticate +with the proxy:: + + proxy_url="socks5h://:@proxy-host" + +""" +from __future__ import absolute_import + +try: + import socks +except ImportError: + import warnings + from ..exceptions import DependencyWarning + + warnings.warn( + ( + "SOCKS support in urllib3 requires the installation of optional " + "dependencies: specifically, PySocks. For more information, see " + "https://urllib3.readthedocs.io/en/latest/contrib.html#socks-proxies" + ), + DependencyWarning, + ) + raise + +from socket import error as SocketError, timeout as SocketTimeout + +from ..connection import HTTPConnection, HTTPSConnection +from ..connectionpool import HTTPConnectionPool, HTTPSConnectionPool +from ..exceptions import ConnectTimeoutError, NewConnectionError +from ..poolmanager import PoolManager +from ..util.url import parse_url + +try: + import ssl +except ImportError: + ssl = None + + +class SOCKSConnection(HTTPConnection): + """ + A plain-text HTTP connection that connects via a SOCKS proxy. + """ + + def __init__(self, *args, **kwargs): + self._socks_options = kwargs.pop("_socks_options") + super(SOCKSConnection, self).__init__(*args, **kwargs) + + def _new_conn(self): + """ + Establish a new connection via the SOCKS proxy. + """ + extra_kw = {} + if self.source_address: + extra_kw["source_address"] = self.source_address + + if self.socket_options: + extra_kw["socket_options"] = self.socket_options + + try: + conn = socks.create_connection( + (self.host, self.port), + proxy_type=self._socks_options["socks_version"], + proxy_addr=self._socks_options["proxy_host"], + proxy_port=self._socks_options["proxy_port"], + proxy_username=self._socks_options["username"], + proxy_password=self._socks_options["password"], + proxy_rdns=self._socks_options["rdns"], + timeout=self.timeout, + **extra_kw + ) + + except SocketTimeout: + raise ConnectTimeoutError( + self, + "Connection to %s timed out. (connect timeout=%s)" + % (self.host, self.timeout), + ) + + except socks.ProxyError as e: + # This is fragile as hell, but it seems to be the only way to raise + # useful errors here. + if e.socket_err: + error = e.socket_err + if isinstance(error, SocketTimeout): + raise ConnectTimeoutError( + self, + "Connection to %s timed out. (connect timeout=%s)" + % (self.host, self.timeout), + ) + else: + raise NewConnectionError( + self, "Failed to establish a new connection: %s" % error + ) + else: + raise NewConnectionError( + self, "Failed to establish a new connection: %s" % e + ) + + except SocketError as e: # Defensive: PySocks should catch all these. + raise NewConnectionError( + self, "Failed to establish a new connection: %s" % e + ) + + return conn + + +# We don't need to duplicate the Verified/Unverified distinction from +# urllib3/connection.py here because the HTTPSConnection will already have been +# correctly set to either the Verified or Unverified form by that module. This +# means the SOCKSHTTPSConnection will automatically be the correct type. +class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection): + pass + + +class SOCKSHTTPConnectionPool(HTTPConnectionPool): + ConnectionCls = SOCKSConnection + + +class SOCKSHTTPSConnectionPool(HTTPSConnectionPool): + ConnectionCls = SOCKSHTTPSConnection + + +class SOCKSProxyManager(PoolManager): + """ + A version of the urllib3 ProxyManager that routes connections via the + defined SOCKS proxy. + """ + + pool_classes_by_scheme = { + "http": SOCKSHTTPConnectionPool, + "https": SOCKSHTTPSConnectionPool, + } + + def __init__( + self, + proxy_url, + username=None, + password=None, + num_pools=10, + headers=None, + **connection_pool_kw + ): + parsed = parse_url(proxy_url) + + if username is None and password is None and parsed.auth is not None: + split = parsed.auth.split(":") + if len(split) == 2: + username, password = split + if parsed.scheme == "socks5": + socks_version = socks.PROXY_TYPE_SOCKS5 + rdns = False + elif parsed.scheme == "socks5h": + socks_version = socks.PROXY_TYPE_SOCKS5 + rdns = True + elif parsed.scheme == "socks4": + socks_version = socks.PROXY_TYPE_SOCKS4 + rdns = False + elif parsed.scheme == "socks4a": + socks_version = socks.PROXY_TYPE_SOCKS4 + rdns = True + else: + raise ValueError("Unable to determine SOCKS version from %s" % proxy_url) + + self.proxy_url = proxy_url + + socks_options = { + "socks_version": socks_version, + "proxy_host": parsed.host, + "proxy_port": parsed.port, + "username": username, + "password": password, + "rdns": rdns, + } + connection_pool_kw["_socks_options"] = socks_options + + super(SOCKSProxyManager, self).__init__( + num_pools, headers, **connection_pool_kw + ) + + self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme diff --git a/venv/Lib/site-packages/urllib3/exceptions.py b/venv/Lib/site-packages/urllib3/exceptions.py new file mode 100644 index 00000000..5cc4d8a4 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/exceptions.py @@ -0,0 +1,272 @@ +from __future__ import absolute_import +from .packages.six.moves.http_client import IncompleteRead as httplib_IncompleteRead + +# Base Exceptions + + +class HTTPError(Exception): + "Base exception used by this module." + pass + + +class HTTPWarning(Warning): + "Base warning used by this module." + pass + + +class PoolError(HTTPError): + "Base exception for errors caused within a pool." + + def __init__(self, pool, message): + self.pool = pool + HTTPError.__init__(self, "%s: %s" % (pool, message)) + + def __reduce__(self): + # For pickling purposes. + return self.__class__, (None, None) + + +class RequestError(PoolError): + "Base exception for PoolErrors that have associated URLs." + + def __init__(self, pool, url, message): + self.url = url + PoolError.__init__(self, pool, message) + + def __reduce__(self): + # For pickling purposes. + return self.__class__, (None, self.url, None) + + +class SSLError(HTTPError): + "Raised when SSL certificate fails in an HTTPS connection." + pass + + +class ProxyError(HTTPError): + "Raised when the connection to a proxy fails." + + def __init__(self, message, error, *args): + super(ProxyError, self).__init__(message, error, *args) + self.original_error = error + + +class DecodeError(HTTPError): + "Raised when automatic decoding based on Content-Type fails." + pass + + +class ProtocolError(HTTPError): + "Raised when something unexpected happens mid-request/response." + pass + + +#: Renamed to ProtocolError but aliased for backwards compatibility. +ConnectionError = ProtocolError + + +# Leaf Exceptions + + +class MaxRetryError(RequestError): + """Raised when the maximum number of retries is exceeded. + + :param pool: The connection pool + :type pool: :class:`~urllib3.connectionpool.HTTPConnectionPool` + :param string url: The requested Url + :param exceptions.Exception reason: The underlying error + + """ + + def __init__(self, pool, url, reason=None): + self.reason = reason + + message = "Max retries exceeded with url: %s (Caused by %r)" % (url, reason) + + RequestError.__init__(self, pool, url, message) + + +class HostChangedError(RequestError): + "Raised when an existing pool gets a request for a foreign host." + + def __init__(self, pool, url, retries=3): + message = "Tried to open a foreign host with url: %s" % url + RequestError.__init__(self, pool, url, message) + self.retries = retries + + +class TimeoutStateError(HTTPError): + """ Raised when passing an invalid state to a timeout """ + + pass + + +class TimeoutError(HTTPError): + """ Raised when a socket timeout error occurs. + + Catching this error will catch both :exc:`ReadTimeoutErrors + ` and :exc:`ConnectTimeoutErrors `. + """ + + pass + + +class ReadTimeoutError(TimeoutError, RequestError): + "Raised when a socket timeout occurs while receiving data from a server" + pass + + +# This timeout error does not have a URL attached and needs to inherit from the +# base HTTPError +class ConnectTimeoutError(TimeoutError): + "Raised when a socket timeout occurs while connecting to a server" + pass + + +class NewConnectionError(ConnectTimeoutError, PoolError): + "Raised when we fail to establish a new connection. Usually ECONNREFUSED." + pass + + +class EmptyPoolError(PoolError): + "Raised when a pool runs out of connections and no more are allowed." + pass + + +class ClosedPoolError(PoolError): + "Raised when a request enters a pool after the pool has been closed." + pass + + +class LocationValueError(ValueError, HTTPError): + "Raised when there is something wrong with a given URL input." + pass + + +class LocationParseError(LocationValueError): + "Raised when get_host or similar fails to parse the URL input." + + def __init__(self, location): + message = "Failed to parse: %s" % location + HTTPError.__init__(self, message) + + self.location = location + + +class ResponseError(HTTPError): + "Used as a container for an error reason supplied in a MaxRetryError." + GENERIC_ERROR = "too many error responses" + SPECIFIC_ERROR = "too many {status_code} error responses" + + +class SecurityWarning(HTTPWarning): + "Warned when performing security reducing actions" + pass + + +class SubjectAltNameWarning(SecurityWarning): + "Warned when connecting to a host with a certificate missing a SAN." + pass + + +class InsecureRequestWarning(SecurityWarning): + "Warned when making an unverified HTTPS request." + pass + + +class SystemTimeWarning(SecurityWarning): + "Warned when system time is suspected to be wrong" + pass + + +class InsecurePlatformWarning(SecurityWarning): + "Warned when certain SSL configuration is not available on a platform." + pass + + +class SNIMissingWarning(HTTPWarning): + "Warned when making a HTTPS request without SNI available." + pass + + +class DependencyWarning(HTTPWarning): + """ + Warned when an attempt is made to import a module with missing optional + dependencies. + """ + + pass + + +class InvalidProxyConfigurationWarning(HTTPWarning): + """ + Warned when using an HTTPS proxy and an HTTPS URL. Currently + urllib3 doesn't support HTTPS proxies and the proxy will be + contacted via HTTP instead. This warning can be fixed by + changing your HTTPS proxy URL into an HTTP proxy URL. + + If you encounter this warning read this: + https://github.com/urllib3/urllib3/issues/1850 + """ + + pass + + +class ResponseNotChunked(ProtocolError, ValueError): + "Response needs to be chunked in order to read it as chunks." + pass + + +class BodyNotHttplibCompatible(HTTPError): + """ + Body should be httplib.HTTPResponse like (have an fp attribute which + returns raw chunks) for read_chunked(). + """ + + pass + + +class IncompleteRead(HTTPError, httplib_IncompleteRead): + """ + Response length doesn't match expected Content-Length + + Subclass of http_client.IncompleteRead to allow int value + for `partial` to avoid creating large objects on streamed + reads. + """ + + def __init__(self, partial, expected): + super(IncompleteRead, self).__init__(partial, expected) + + def __repr__(self): + return "IncompleteRead(%i bytes read, %i more expected)" % ( + self.partial, + self.expected, + ) + + +class InvalidHeader(HTTPError): + "The header provided was somehow invalid." + pass + + +class ProxySchemeUnknown(AssertionError, ValueError): + "ProxyManager does not support the supplied scheme" + # TODO(t-8ch): Stop inheriting from AssertionError in v2.0. + + def __init__(self, scheme): + message = "Not supported proxy scheme %s" % scheme + super(ProxySchemeUnknown, self).__init__(message) + + +class HeaderParsingError(HTTPError): + "Raised by assert_header_parsing, but we convert it to a log.warning statement." + + def __init__(self, defects, unparsed_data): + message = "%s, unparsed data: %r" % (defects or "Unknown", unparsed_data) + super(HeaderParsingError, self).__init__(message) + + +class UnrewindableBodyError(HTTPError): + "urllib3 encountered an error when trying to rewind a body" + pass diff --git a/venv/Lib/site-packages/urllib3/fields.py b/venv/Lib/site-packages/urllib3/fields.py new file mode 100644 index 00000000..8715b220 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/fields.py @@ -0,0 +1,273 @@ +from __future__ import absolute_import +import email.utils +import mimetypes +import re + +from .packages import six + + +def guess_content_type(filename, default="application/octet-stream"): + """ + Guess the "Content-Type" of a file. + + :param filename: + The filename to guess the "Content-Type" of using :mod:`mimetypes`. + :param default: + If no "Content-Type" can be guessed, default to `default`. + """ + if filename: + return mimetypes.guess_type(filename)[0] or default + return default + + +def format_header_param_rfc2231(name, value): + """ + Helper function to format and quote a single header parameter using the + strategy defined in RFC 2231. + + Particularly useful for header parameters which might contain + non-ASCII values, like file names. This follows RFC 2388 Section 4.4. + + :param name: + The name of the parameter, a string expected to be ASCII only. + :param value: + The value of the parameter, provided as ``bytes`` or `str``. + :ret: + An RFC-2231-formatted unicode string. + """ + if isinstance(value, six.binary_type): + value = value.decode("utf-8") + + if not any(ch in value for ch in '"\\\r\n'): + result = u'%s="%s"' % (name, value) + try: + result.encode("ascii") + except (UnicodeEncodeError, UnicodeDecodeError): + pass + else: + return result + + if six.PY2: # Python 2: + value = value.encode("utf-8") + + # encode_rfc2231 accepts an encoded string and returns an ascii-encoded + # string in Python 2 but accepts and returns unicode strings in Python 3 + value = email.utils.encode_rfc2231(value, "utf-8") + value = "%s*=%s" % (name, value) + + if six.PY2: # Python 2: + value = value.decode("utf-8") + + return value + + +_HTML5_REPLACEMENTS = { + u"\u0022": u"%22", + # Replace "\" with "\\". + u"\u005C": u"\u005C\u005C", + u"\u005C": u"\u005C\u005C", +} + +# All control characters from 0x00 to 0x1F *except* 0x1B. +_HTML5_REPLACEMENTS.update( + { + six.unichr(cc): u"%{:02X}".format(cc) + for cc in range(0x00, 0x1F + 1) + if cc not in (0x1B,) + } +) + + +def _replace_multiple(value, needles_and_replacements): + def replacer(match): + return needles_and_replacements[match.group(0)] + + pattern = re.compile( + r"|".join([re.escape(needle) for needle in needles_and_replacements.keys()]) + ) + + result = pattern.sub(replacer, value) + + return result + + +def format_header_param_html5(name, value): + """ + Helper function to format and quote a single header parameter using the + HTML5 strategy. + + Particularly useful for header parameters which might contain + non-ASCII values, like file names. This follows the `HTML5 Working Draft + Section 4.10.22.7`_ and matches the behavior of curl and modern browsers. + + .. _HTML5 Working Draft Section 4.10.22.7: + https://w3c.github.io/html/sec-forms.html#multipart-form-data + + :param name: + The name of the parameter, a string expected to be ASCII only. + :param value: + The value of the parameter, provided as ``bytes`` or `str``. + :ret: + A unicode string, stripped of troublesome characters. + """ + if isinstance(value, six.binary_type): + value = value.decode("utf-8") + + value = _replace_multiple(value, _HTML5_REPLACEMENTS) + + return u'%s="%s"' % (name, value) + + +# For backwards-compatibility. +format_header_param = format_header_param_html5 + + +class RequestField(object): + """ + A data container for request body parameters. + + :param name: + The name of this request field. Must be unicode. + :param data: + The data/value body. + :param filename: + An optional filename of the request field. Must be unicode. + :param headers: + An optional dict-like object of headers to initially use for the field. + :param header_formatter: + An optional callable that is used to encode and format the headers. By + default, this is :func:`format_header_param_html5`. + """ + + def __init__( + self, + name, + data, + filename=None, + headers=None, + header_formatter=format_header_param_html5, + ): + self._name = name + self._filename = filename + self.data = data + self.headers = {} + if headers: + self.headers = dict(headers) + self.header_formatter = header_formatter + + @classmethod + def from_tuples(cls, fieldname, value, header_formatter=format_header_param_html5): + """ + A :class:`~urllib3.fields.RequestField` factory from old-style tuple parameters. + + Supports constructing :class:`~urllib3.fields.RequestField` from + parameter of key/value strings AND key/filetuple. A filetuple is a + (filename, data, MIME type) tuple where the MIME type is optional. + For example:: + + 'foo': 'bar', + 'fakefile': ('foofile.txt', 'contents of foofile'), + 'realfile': ('barfile.txt', open('realfile').read()), + 'typedfile': ('bazfile.bin', open('bazfile').read(), 'image/jpeg'), + 'nonamefile': 'contents of nonamefile field', + + Field names and filenames must be unicode. + """ + if isinstance(value, tuple): + if len(value) == 3: + filename, data, content_type = value + else: + filename, data = value + content_type = guess_content_type(filename) + else: + filename = None + content_type = None + data = value + + request_param = cls( + fieldname, data, filename=filename, header_formatter=header_formatter + ) + request_param.make_multipart(content_type=content_type) + + return request_param + + def _render_part(self, name, value): + """ + Overridable helper function to format a single header parameter. By + default, this calls ``self.header_formatter``. + + :param name: + The name of the parameter, a string expected to be ASCII only. + :param value: + The value of the parameter, provided as a unicode string. + """ + + return self.header_formatter(name, value) + + def _render_parts(self, header_parts): + """ + Helper function to format and quote a single header. + + Useful for single headers that are composed of multiple items. E.g., + 'Content-Disposition' fields. + + :param header_parts: + A sequence of (k, v) tuples or a :class:`dict` of (k, v) to format + as `k1="v1"; k2="v2"; ...`. + """ + parts = [] + iterable = header_parts + if isinstance(header_parts, dict): + iterable = header_parts.items() + + for name, value in iterable: + if value is not None: + parts.append(self._render_part(name, value)) + + return u"; ".join(parts) + + def render_headers(self): + """ + Renders the headers for this request field. + """ + lines = [] + + sort_keys = ["Content-Disposition", "Content-Type", "Content-Location"] + for sort_key in sort_keys: + if self.headers.get(sort_key, False): + lines.append(u"%s: %s" % (sort_key, self.headers[sort_key])) + + for header_name, header_value in self.headers.items(): + if header_name not in sort_keys: + if header_value: + lines.append(u"%s: %s" % (header_name, header_value)) + + lines.append(u"\r\n") + return u"\r\n".join(lines) + + def make_multipart( + self, content_disposition=None, content_type=None, content_location=None + ): + """ + Makes this request field into a multipart request field. + + This method overrides "Content-Disposition", "Content-Type" and + "Content-Location" headers to the request parameter. + + :param content_type: + The 'Content-Type' of the request body. + :param content_location: + The 'Content-Location' of the request body. + + """ + self.headers["Content-Disposition"] = content_disposition or u"form-data" + self.headers["Content-Disposition"] += u"; ".join( + [ + u"", + self._render_parts( + ((u"name", self._name), (u"filename", self._filename)) + ), + ] + ) + self.headers["Content-Type"] = content_type + self.headers["Content-Location"] = content_location diff --git a/venv/Lib/site-packages/urllib3/filepost.py b/venv/Lib/site-packages/urllib3/filepost.py new file mode 100644 index 00000000..b7b00992 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/filepost.py @@ -0,0 +1,98 @@ +from __future__ import absolute_import +import binascii +import codecs +import os + +from io import BytesIO + +from .packages import six +from .packages.six import b +from .fields import RequestField + +writer = codecs.lookup("utf-8")[3] + + +def choose_boundary(): + """ + Our embarrassingly-simple replacement for mimetools.choose_boundary. + """ + boundary = binascii.hexlify(os.urandom(16)) + if not six.PY2: + boundary = boundary.decode("ascii") + return boundary + + +def iter_field_objects(fields): + """ + Iterate over fields. + + Supports list of (k, v) tuples and dicts, and lists of + :class:`~urllib3.fields.RequestField`. + + """ + if isinstance(fields, dict): + i = six.iteritems(fields) + else: + i = iter(fields) + + for field in i: + if isinstance(field, RequestField): + yield field + else: + yield RequestField.from_tuples(*field) + + +def iter_fields(fields): + """ + .. deprecated:: 1.6 + + Iterate over fields. + + The addition of :class:`~urllib3.fields.RequestField` makes this function + obsolete. Instead, use :func:`iter_field_objects`, which returns + :class:`~urllib3.fields.RequestField` objects. + + Supports list of (k, v) tuples and dicts. + """ + if isinstance(fields, dict): + return ((k, v) for k, v in six.iteritems(fields)) + + return ((k, v) for k, v in fields) + + +def encode_multipart_formdata(fields, boundary=None): + """ + Encode a dictionary of ``fields`` using the multipart/form-data MIME format. + + :param fields: + Dictionary of fields or list of (key, :class:`~urllib3.fields.RequestField`). + + :param boundary: + If not specified, then a random boundary will be generated using + :func:`urllib3.filepost.choose_boundary`. + """ + body = BytesIO() + if boundary is None: + boundary = choose_boundary() + + for field in iter_field_objects(fields): + body.write(b("--%s\r\n" % (boundary))) + + writer(body).write(field.render_headers()) + data = field.data + + if isinstance(data, int): + data = str(data) # Backwards compatibility + + if isinstance(data, six.text_type): + writer(body).write(data) + else: + body.write(data) + + body.write(b"\r\n") + + body.write(b("--%s--\r\n" % (boundary))) + + content_type = str("multipart/form-data; boundary=%s" % boundary) + + return body.getvalue(), content_type diff --git a/venv/Lib/site-packages/urllib3/packages/__init__.py b/venv/Lib/site-packages/urllib3/packages/__init__.py new file mode 100644 index 00000000..fce4caa6 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/packages/__init__.py @@ -0,0 +1,5 @@ +from __future__ import absolute_import + +from . import ssl_match_hostname + +__all__ = ("ssl_match_hostname",) diff --git a/venv/Lib/site-packages/urllib3/packages/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/urllib3/packages/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61bc7051c42b4e3a0a7f0c2762cb70cafa596a26 GIT binary patch literal 298 zcmX|5O-sZu6inJ(cUg83ym|B5Lj_szAR;b$6fdin5`3ghY>O{V$Oqz2@*hdAp7t+z zGOgf)nK#Tlc(dJXR=d-$7bJvyC;U&A)NO)0OC*S(NEEFoB_b0!W-71pq>rkZnD0rk zIHW65(0I&ir!ja5y)xSR1O4+)oI_CDHyw1^`GG5GXDNCt=(LM@TmifWAFKqh^bM~Mcj_gS)!%T0=Fc{vvNm0IcQexS9 qIb8CKngxs4*IPEGyp@gt^h56f&do<$UMnpE%A2vf8(Xfkb@mIM3Q;=% literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/packages/__pycache__/six.cpython-36.pyc b/venv/Lib/site-packages/urllib3/packages/__pycache__/six.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07a11863bd84c128e08886150133bd87744da1b4 GIT binary patch literal 26525 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 DO?EQj literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/packages/backports/__pycache__/makefile.cpython-36.pyc b/venv/Lib/site-packages/urllib3/packages/backports/__pycache__/makefile.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..22c70c883ee1bfea668e80d6b8b29ab7c279599e GIT binary patch literal 1273 zcmZuwQEMAV5Z=8z-JMRdaMM!gQ#XN{3SvQUlRzjXR$_x8I3coY5TfUmcF#KNba(8o zs@kmjQ1vOlpfCLag+BCWuA9bW$&4e4l77DCC+ zlume(W<2`-KNLmXwHr}mPTpLpagmcfa(*t0(S)nb=JRthH5-HL>hVM(qsNpy|HL=@A_OgoP|)h6LW zUk#ocJiOjX^KWUEGBPa~C!Y!Od68#VL_8$(hh#`-mKAf(+6LXg7|^k>{QCYZAI_45 z3(kmCFzKlz)0tFc$jLCxskplN2dLdNuH?!@U(;;H4}>U$Y3)nNg-VOO(uD)@`mK0< zLu0`cX`Hk$AHF;|+&_JF5_eAz4-ekPy`%ntX?1VYzCC+GQ;4?FIc5BgekM`{`ql~m zP942I6SOQr^=y&tE#pa^CE}j#LVZ;TTwoLOd{i(i`P`mWnjq)%EX}zI<`Bt3rw{Qrn3EYFv@(@QAY9;>`vGO+9T22VQ)^i(d;y%P^*Q#rC4AicfAdXovieu3P jlkr!tyBcQYTorzY5$hFx35}X%{HqW&Y2P}ebDG}24>@$6 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/packages/backports/makefile.py b/venv/Lib/site-packages/urllib3/packages/backports/makefile.py new file mode 100644 index 00000000..a3156a69 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/packages/backports/makefile.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +""" +backports.makefile +~~~~~~~~~~~~~~~~~~ + +Backports the Python 3 ``socket.makefile`` method for use with anything that +wants to create a "fake" socket object. +""" +import io + +from socket import SocketIO + + +def backport_makefile( + self, mode="r", buffering=None, encoding=None, errors=None, newline=None +): + """ + Backport of ``socket.makefile`` from Python 3.5. + """ + if not set(mode) <= {"r", "w", "b"}: + raise ValueError("invalid mode %r (only r, w, b allowed)" % (mode,)) + writing = "w" in mode + reading = "r" in mode or not writing + assert reading or writing + binary = "b" in mode + rawmode = "" + if reading: + rawmode += "r" + if writing: + rawmode += "w" + raw = SocketIO(self, rawmode) + self._makefile_refs += 1 + if buffering is None: + buffering = -1 + if buffering < 0: + buffering = io.DEFAULT_BUFFER_SIZE + if buffering == 0: + if not binary: + raise ValueError("unbuffered streams must be binary") + return raw + if reading and writing: + buffer = io.BufferedRWPair(raw, raw, buffering) + elif reading: + buffer = io.BufferedReader(raw, buffering) + else: + assert writing + buffer = io.BufferedWriter(raw, buffering) + if binary: + return buffer + text = io.TextIOWrapper(buffer, encoding, errors, newline) + text.mode = mode + return text diff --git a/venv/Lib/site-packages/urllib3/packages/six.py b/venv/Lib/site-packages/urllib3/packages/six.py new file mode 100644 index 00000000..31442409 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/packages/six.py @@ -0,0 +1,1021 @@ +# Copyright (c) 2010-2019 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""Utilities for writing code that runs on Python 2 and 3""" + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson " +__version__ = "1.12.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = (str,) + integer_types = (int,) + class_types = (type,) + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = (basestring,) + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + def __len__(self): + return 1 << 31 + + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + + get_source = get_code # same as get_code + + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute( + "filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse" + ), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("getoutput", "commands", "subprocess"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute( + "reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload" + ), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute( + "zip_longest", "itertools", "itertools", "izip_longest", "zip_longest" + ), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule( + "email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart" + ), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [MovedModule("winreg", "_winreg")] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute( + "unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes" + ), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("splitvalue", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module( + Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", + "moves.urllib.parse", +) + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module( + Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", + "moves.urllib.error", +) + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), + MovedAttribute("parse_http_list", "urllib2", "urllib.request"), + MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module( + Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", + "moves.urllib.request", +) + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module( + Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", + "moves.urllib.response", +) + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser") +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = ( + _urllib_robotparser_moved_attributes +) + +_importer._add_module( + Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", + "moves.urllib.robotparser", +) + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ["parse", "error", "request", "response", "robotparser"] + + +_importer._add_module( + Module_six_moves_urllib(__name__ + ".moves.urllib"), "moves.urllib" +) + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + + def advance_iterator(it): + return it.next() + + +next = advance_iterator + + +try: + callable = callable +except NameError: + + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc( + get_unbound_function, """Get the function out of a possibly unbound function""" +) + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc( + iterlists, "Return an iterator over the (key, [values]) pairs of a dictionary." +) + + +if PY3: + + def b(s): + return s.encode("latin-1") + + def u(s): + return s + + unichr = chr + import struct + + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + + StringIO = io.StringIO + BytesIO = io.BytesIO + del io + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + + def b(s): + return s + + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r"\\", r"\\\\"), "unicode_escape") + + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + try: + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + finally: + value = None + tb = None + + +else: + + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_( + """def reraise(tp, value, tb=None): + try: + raise tp, value, tb + finally: + tb = None +""" + ) + + +if sys.version_info[:2] == (3, 2): + exec_( + """def raise_from(value, from_value): + try: + if from_value is None: + raise value + raise value from from_value + finally: + value = None +""" + ) +elif sys.version_info[:2] > (3, 2): + exec_( + """def raise_from(value, from_value): + try: + raise value from from_value + finally: + value = None +""" + ) +else: + + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if ( + isinstance(fp, file) + and isinstance(data, unicode) + and fp.encoding is not None + ): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) + + +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + + def wraps( + wrapped, + assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES, + ): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + + return wrapper + + +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(type): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + + return type.__new__(metaclass, "temporary_class", (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get("__slots__") + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop("__dict__", None) + orig_vars.pop("__weakref__", None) + if hasattr(cls, "__qualname__"): + orig_vars["__qualname__"] = cls.__qualname__ + return metaclass(cls.__name__, cls.__bases__, orig_vars) + + return wrapper + + +def ensure_binary(s, encoding="utf-8", errors="strict"): + """Coerce **s** to six.binary_type. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> encoded to `bytes` + - `bytes` -> `bytes` + """ + if isinstance(s, text_type): + return s.encode(encoding, errors) + elif isinstance(s, binary_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + +def ensure_str(s, encoding="utf-8", errors="strict"): + """Coerce *s* to `str`. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) + if PY2 and isinstance(s, text_type): + s = s.encode(encoding, errors) + elif PY3 and isinstance(s, binary_type): + s = s.decode(encoding, errors) + return s + + +def ensure_text(s, encoding="utf-8", errors="strict"): + """Coerce *s* to six.text_type. + + For Python 2: + - `unicode` -> `unicode` + - `str` -> `unicode` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if isinstance(s, binary_type): + return s.decode(encoding, errors) + elif isinstance(s, text_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if "__str__" not in klass.__dict__: + raise ValueError( + "@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % klass.__name__ + ) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode("utf-8") + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if ( + type(importer).__name__ == "_SixMetaPathImporter" + and importer.name == __name__ + ): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/venv/Lib/site-packages/urllib3/packages/ssl_match_hostname/__init__.py b/venv/Lib/site-packages/urllib3/packages/ssl_match_hostname/__init__.py new file mode 100644 index 00000000..75b6bb1c --- /dev/null +++ b/venv/Lib/site-packages/urllib3/packages/ssl_match_hostname/__init__.py @@ -0,0 +1,19 @@ +import sys + +try: + # Our match_hostname function is the same as 3.5's, so we only want to + # import the match_hostname function if it's at least that good. + if sys.version_info < (3, 5): + raise ImportError("Fallback to vendored code") + + from ssl import CertificateError, match_hostname +except ImportError: + try: + # Backport of the function from a pypi module + from backports.ssl_match_hostname import CertificateError, match_hostname + except ImportError: + # Our vendored copy + from ._implementation import CertificateError, match_hostname + +# Not needed, but documenting what we provide. +__all__ = ("CertificateError", "match_hostname") diff --git a/venv/Lib/site-packages/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f00210847fe0065e4c2a3f44fffe29e3afaf4158 GIT binary patch literal 559 zcmZWlO^Z`86ixD8XF8n^9q4ADi!Mayq60GELPQ*96a<-#iuwqK^yN-kgL1Fjan`pDA)3U6`^eO{eNm@|xJIk^r0c!Y4}dUiJTIc5Z0y{M9Ts8nuHi zyIaLuV%ZzALDj3eioPJD{WWGGSL*_C-9h+x@>FU)k@b9-+;9n2xd3WdI|U=w-oAj4 zn(L;P37&-DLVILns%K*6VzSb}nxaQ2Gi0|B3-}8pWP7Z)_j_#-SFt@g?&r+_a1 n1>piGX_649e${p#8mAT-9_2rYeS|J!MUQq_pB_<`kqwI9FI|>1 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-36.pyc b/venv/Lib/site-packages/urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b88dd3e82f7b3603684609574723199086aed952 GIT binary patch literal 3266 zcmZ`*L2n$l6&`YCc1Nq#+BICq4tyY-IQ6R1Dz<9XFw!7`EC()B$+hgb-71-mhFtBe zW@pAEHrrK1!|J48s?rD4q2D%aJwAR#iXhwfj5Il#psi?d2oBvsx4n zA|35)`QZdBlBkP+^aVRO)|E1lQ6#`QIzqNafT~zjIpm->2DC)nc_M9WPgdgnYOJ{&{ zJSIn8>2dd(bL8WTbNm~HTr2$n;WhNu%X&%oJ-MMNuWO7xChwtqL(01PE9UTyos!sk zA7@(nytYrJF5Q98gJVLR>{HBC<1@!Zy@p;T$$#2@T^|Ice@rT^t7lsVh#Ee74BLov zaJ{TOaK5~J6r5^cUfc8H`{xekXz(VV8_brjwTrjjvu({|dNfnc4CYE`;+QXt`+u-e zW`iY)<6Lqp>6|@zxW-m)-?_WMl$gZaO6ND71$owUs}?6o~lkFbgz@ky`{b$ zrc1IH+hHG^j9Q$GvV$HP(vkKMWPobZkooZfWMd_e)+b!d(B2BdM<9w9q>g0_lMInu!<}Zab zwWugW#!W-%NNV*m(S1XuFg2m#s1T+O8$-U|}$Ypw+$Q#g( zt?TV$zdlM8`X52i2zX77sl1`-z~%nF1Ia6q0^H@^-`ykhBUEaeZ*BG%KT>g$n3~cu zi8X*C5LuktxPcEXPN+7KA3(G1n??c{f}=C|)5QQ(hsrd+1|T3+!zAnFrasCNj2CTU z+-B>u))R=eA)oiyqsJ`bTv{Miok^fivw(>ML{*&V^pNpfsO(poanVaM!J>>s z>0T}oM?==jCDZ*xRatUtnmM;SzwO*%i|oeMNZW*oT{TT2QX1*UG}EldhSlj6LyE~5Qp_yi`OS!Y-qI>MYqaeDKDbXEK5Z!&WTKd~~r}aa$`e@B2tr`Tn zAILtZM>M0~I1_@LKrVpK?KyxY?_js|0BGGEdgNP}YN3T@GkS(0* z+Sy-2ceW&@%bVAnQ=I^wkx`u27cCFw|t z4%^C@8et-(7Aixh_n(P5)m9b5YL>nx*)-O;Qs_g5Jk0X5M6z)1HpMa_z&4S+A~99D z$PTi9ah_>$putJH&jczV+1mP(r{UJ)r;oNaS2wI;)$nBfkIx=GS%0u;T57aA08y@{ zdebJU3*H(x;F;A4qL})Lf@y$OhIwYSDk20la7rb(Hb;1=IK7H-nTlIq9&fPZmc|0O?+lg)s?y8`{0wwz4}v2)aoiK&ksmY Sqm}Uu>bV!a#!65Nnm+*qQEyuS literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/packages/ssl_match_hostname/_implementation.py b/venv/Lib/site-packages/urllib3/packages/ssl_match_hostname/_implementation.py new file mode 100644 index 00000000..689208d3 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/packages/ssl_match_hostname/_implementation.py @@ -0,0 +1,160 @@ +"""The match_hostname() function from Python 3.3.3, essential when using SSL.""" + +# Note: This file is under the PSF license as the code comes from the python +# stdlib. http://docs.python.org/3/license.html + +import re +import sys + +# ipaddress has been backported to 2.6+ in pypi. If it is installed on the +# system, use it to handle IPAddress ServerAltnames (this was added in +# python-3.5) otherwise only do DNS matching. This allows +# backports.ssl_match_hostname to continue to be used in Python 2.7. +try: + import ipaddress +except ImportError: + ipaddress = None + +__version__ = "3.5.0.1" + + +class CertificateError(ValueError): + pass + + +def _dnsname_match(dn, hostname, max_wildcards=1): + """Matching according to RFC 6125, section 6.4.3 + + http://tools.ietf.org/html/rfc6125#section-6.4.3 + """ + pats = [] + if not dn: + return False + + # Ported from python3-syntax: + # leftmost, *remainder = dn.split(r'.') + parts = dn.split(r".") + leftmost = parts[0] + remainder = parts[1:] + + wildcards = leftmost.count("*") + if wildcards > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survey of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn) + ) + + # speed up common case w/o wildcards + if not wildcards: + return dn.lower() == hostname.lower() + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in which + # the wildcard character comprises a label other than the left-most label. + if leftmost == "*": + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append("[^.]+") + elif leftmost.startswith("xn--") or hostname.startswith("xn--"): + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + pats.append(re.escape(leftmost)) + else: + # Otherwise, '*' matches any dotless string, e.g. www* + pats.append(re.escape(leftmost).replace(r"\*", "[^.]*")) + + # add the remaining fragments, ignore any wildcards + for frag in remainder: + pats.append(re.escape(frag)) + + pat = re.compile(r"\A" + r"\.".join(pats) + r"\Z", re.IGNORECASE) + return pat.match(hostname) + + +def _to_unicode(obj): + if isinstance(obj, str) and sys.version_info < (3,): + obj = unicode(obj, encoding="ascii", errors="strict") + return obj + + +def _ipaddress_match(ipname, host_ip): + """Exact matching of IP addresses. + + RFC 6125 explicitly doesn't define an algorithm for this + (section 1.7.2 - "Out of Scope"). + """ + # OpenSSL may add a trailing newline to a subjectAltName's IP address + # Divergence from upstream: ipaddress can't handle byte str + ip = ipaddress.ip_address(_to_unicode(ipname).rstrip()) + return ip == host_ip + + +def match_hostname(cert, hostname): + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 + rules are followed, but IP addresses are not accepted for *hostname*. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError( + "empty or no certificate, match_hostname needs a " + "SSL socket or SSL context with either " + "CERT_OPTIONAL or CERT_REQUIRED" + ) + try: + # Divergence from upstream: ipaddress can't handle byte str + host_ip = ipaddress.ip_address(_to_unicode(hostname)) + except ValueError: + # Not an IP address (common case) + host_ip = None + except UnicodeError: + # Divergence from upstream: Have to deal with ipaddress not taking + # byte strings. addresses should be all ascii, so we consider it not + # an ipaddress in this case + host_ip = None + except AttributeError: + # Divergence from upstream: Make ipaddress library optional + if ipaddress is None: + host_ip = None + else: + raise + dnsnames = [] + san = cert.get("subjectAltName", ()) + for key, value in san: + if key == "DNS": + if host_ip is None and _dnsname_match(value, hostname): + return + dnsnames.append(value) + elif key == "IP Address": + if host_ip is not None and _ipaddress_match(value, host_ip): + return + dnsnames.append(value) + if not dnsnames: + # The subject is only checked when there is no dNSName entry + # in subjectAltName + for sub in cert.get("subject", ()): + for key, value in sub: + # XXX according to RFC 2818, the most specific Common Name + # must be used. + if key == "commonName": + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if len(dnsnames) > 1: + raise CertificateError( + "hostname %r " + "doesn't match either of %s" % (hostname, ", ".join(map(repr, dnsnames))) + ) + elif len(dnsnames) == 1: + raise CertificateError("hostname %r doesn't match %r" % (hostname, dnsnames[0])) + else: + raise CertificateError( + "no appropriate commonName or subjectAltName fields were found" + ) diff --git a/venv/Lib/site-packages/urllib3/poolmanager.py b/venv/Lib/site-packages/urllib3/poolmanager.py new file mode 100644 index 00000000..e2bd3bd8 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/poolmanager.py @@ -0,0 +1,492 @@ +from __future__ import absolute_import +import collections +import functools +import logging +import warnings + +from ._collections import RecentlyUsedContainer +from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool +from .connectionpool import port_by_scheme +from .exceptions import ( + LocationValueError, + MaxRetryError, + ProxySchemeUnknown, + InvalidProxyConfigurationWarning, +) +from .packages import six +from .packages.six.moves.urllib.parse import urljoin +from .request import RequestMethods +from .util.url import parse_url +from .util.retry import Retry + + +__all__ = ["PoolManager", "ProxyManager", "proxy_from_url"] + + +log = logging.getLogger(__name__) + +SSL_KEYWORDS = ( + "key_file", + "cert_file", + "cert_reqs", + "ca_certs", + "ssl_version", + "ca_cert_dir", + "ssl_context", + "key_password", +) + +# All known keyword arguments that could be provided to the pool manager, its +# pools, or the underlying connections. This is used to construct a pool key. +_key_fields = ( + "key_scheme", # str + "key_host", # str + "key_port", # int + "key_timeout", # int or float or Timeout + "key_retries", # int or Retry + "key_strict", # bool + "key_block", # bool + "key_source_address", # str + "key_key_file", # str + "key_key_password", # str + "key_cert_file", # str + "key_cert_reqs", # str + "key_ca_certs", # str + "key_ssl_version", # str + "key_ca_cert_dir", # str + "key_ssl_context", # instance of ssl.SSLContext or urllib3.util.ssl_.SSLContext + "key_maxsize", # int + "key_headers", # dict + "key__proxy", # parsed proxy url + "key__proxy_headers", # dict + "key_socket_options", # list of (level (int), optname (int), value (int or str)) tuples + "key__socks_options", # dict + "key_assert_hostname", # bool or string + "key_assert_fingerprint", # str + "key_server_hostname", # str +) + +#: The namedtuple class used to construct keys for the connection pool. +#: All custom key schemes should include the fields in this key at a minimum. +PoolKey = collections.namedtuple("PoolKey", _key_fields) + + +def _default_key_normalizer(key_class, request_context): + """ + Create a pool key out of a request context dictionary. + + According to RFC 3986, both the scheme and host are case-insensitive. + Therefore, this function normalizes both before constructing the pool + key for an HTTPS request. If you wish to change this behaviour, provide + alternate callables to ``key_fn_by_scheme``. + + :param key_class: + The class to use when constructing the key. This should be a namedtuple + with the ``scheme`` and ``host`` keys at a minimum. + :type key_class: namedtuple + :param request_context: + A dictionary-like object that contain the context for a request. + :type request_context: dict + + :return: A namedtuple that can be used as a connection pool key. + :rtype: PoolKey + """ + # Since we mutate the dictionary, make a copy first + context = request_context.copy() + context["scheme"] = context["scheme"].lower() + context["host"] = context["host"].lower() + + # These are both dictionaries and need to be transformed into frozensets + for key in ("headers", "_proxy_headers", "_socks_options"): + if key in context and context[key] is not None: + context[key] = frozenset(context[key].items()) + + # The socket_options key may be a list and needs to be transformed into a + # tuple. + socket_opts = context.get("socket_options") + if socket_opts is not None: + context["socket_options"] = tuple(socket_opts) + + # Map the kwargs to the names in the namedtuple - this is necessary since + # namedtuples can't have fields starting with '_'. + for key in list(context.keys()): + context["key_" + key] = context.pop(key) + + # Default to ``None`` for keys missing from the context + for field in key_class._fields: + if field not in context: + context[field] = None + + return key_class(**context) + + +#: A dictionary that maps a scheme to a callable that creates a pool key. +#: This can be used to alter the way pool keys are constructed, if desired. +#: Each PoolManager makes a copy of this dictionary so they can be configured +#: globally here, or individually on the instance. +key_fn_by_scheme = { + "http": functools.partial(_default_key_normalizer, PoolKey), + "https": functools.partial(_default_key_normalizer, PoolKey), +} + +pool_classes_by_scheme = {"http": HTTPConnectionPool, "https": HTTPSConnectionPool} + + +class PoolManager(RequestMethods): + """ + Allows for arbitrary requests while transparently keeping track of + necessary connection pools for you. + + :param num_pools: + Number of connection pools to cache before discarding the least + recently used pool. + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + + :param \\**connection_pool_kw: + Additional parameters are used to create fresh + :class:`urllib3.connectionpool.ConnectionPool` instances. + + Example:: + + >>> manager = PoolManager(num_pools=2) + >>> r = manager.request('GET', 'http://google.com/') + >>> r = manager.request('GET', 'http://google.com/mail') + >>> r = manager.request('GET', 'http://yahoo.com/') + >>> len(manager.pools) + 2 + + """ + + proxy = None + + def __init__(self, num_pools=10, headers=None, **connection_pool_kw): + RequestMethods.__init__(self, headers) + self.connection_pool_kw = connection_pool_kw + self.pools = RecentlyUsedContainer(num_pools, dispose_func=lambda p: p.close()) + + # Locally set the pool classes and keys so other PoolManagers can + # override them. + self.pool_classes_by_scheme = pool_classes_by_scheme + self.key_fn_by_scheme = key_fn_by_scheme.copy() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.clear() + # Return False to re-raise any potential exceptions + return False + + def _new_pool(self, scheme, host, port, request_context=None): + """ + Create a new :class:`ConnectionPool` based on host, port, scheme, and + any additional pool keyword arguments. + + If ``request_context`` is provided, it is provided as keyword arguments + to the pool class used. This method is used to actually create the + connection pools handed out by :meth:`connection_from_url` and + companion methods. It is intended to be overridden for customization. + """ + pool_cls = self.pool_classes_by_scheme[scheme] + if request_context is None: + request_context = self.connection_pool_kw.copy() + + # Although the context has everything necessary to create the pool, + # this function has historically only used the scheme, host, and port + # in the positional args. When an API change is acceptable these can + # be removed. + for key in ("scheme", "host", "port"): + request_context.pop(key, None) + + if scheme == "http": + for kw in SSL_KEYWORDS: + request_context.pop(kw, None) + + return pool_cls(host, port, **request_context) + + def clear(self): + """ + Empty our store of pools and direct them all to close. + + This will not affect in-flight connections, but they will not be + re-used after completion. + """ + self.pools.clear() + + def connection_from_host(self, host, port=None, scheme="http", pool_kwargs=None): + """ + Get a :class:`ConnectionPool` based on the host, port, and scheme. + + If ``port`` isn't given, it will be derived from the ``scheme`` using + ``urllib3.connectionpool.port_by_scheme``. If ``pool_kwargs`` is + provided, it is merged with the instance's ``connection_pool_kw`` + variable and used to create the new connection pool, if one is + needed. + """ + + if not host: + raise LocationValueError("No host specified.") + + request_context = self._merge_pool_kwargs(pool_kwargs) + request_context["scheme"] = scheme or "http" + if not port: + port = port_by_scheme.get(request_context["scheme"].lower(), 80) + request_context["port"] = port + request_context["host"] = host + + return self.connection_from_context(request_context) + + def connection_from_context(self, request_context): + """ + Get a :class:`ConnectionPool` based on the request context. + + ``request_context`` must at least contain the ``scheme`` key and its + value must be a key in ``key_fn_by_scheme`` instance variable. + """ + scheme = request_context["scheme"].lower() + pool_key_constructor = self.key_fn_by_scheme[scheme] + pool_key = pool_key_constructor(request_context) + + return self.connection_from_pool_key(pool_key, request_context=request_context) + + def connection_from_pool_key(self, pool_key, request_context=None): + """ + Get a :class:`ConnectionPool` based on the provided pool key. + + ``pool_key`` should be a namedtuple that only contains immutable + objects. At a minimum it must have the ``scheme``, ``host``, and + ``port`` fields. + """ + with self.pools.lock: + # If the scheme, host, or port doesn't match existing open + # connections, open a new ConnectionPool. + pool = self.pools.get(pool_key) + if pool: + return pool + + # Make a fresh ConnectionPool of the desired type + scheme = request_context["scheme"] + host = request_context["host"] + port = request_context["port"] + pool = self._new_pool(scheme, host, port, request_context=request_context) + self.pools[pool_key] = pool + + return pool + + def connection_from_url(self, url, pool_kwargs=None): + """ + Similar to :func:`urllib3.connectionpool.connection_from_url`. + + If ``pool_kwargs`` is not provided and a new pool needs to be + constructed, ``self.connection_pool_kw`` is used to initialize + the :class:`urllib3.connectionpool.ConnectionPool`. If ``pool_kwargs`` + is provided, it is used instead. Note that if a new pool does not + need to be created for the request, the provided ``pool_kwargs`` are + not used. + """ + u = parse_url(url) + return self.connection_from_host( + u.host, port=u.port, scheme=u.scheme, pool_kwargs=pool_kwargs + ) + + def _merge_pool_kwargs(self, override): + """ + Merge a dictionary of override values for self.connection_pool_kw. + + This does not modify self.connection_pool_kw and returns a new dict. + Any keys in the override dictionary with a value of ``None`` are + removed from the merged dictionary. + """ + base_pool_kwargs = self.connection_pool_kw.copy() + if override: + for key, value in override.items(): + if value is None: + try: + del base_pool_kwargs[key] + except KeyError: + pass + else: + base_pool_kwargs[key] = value + return base_pool_kwargs + + def urlopen(self, method, url, redirect=True, **kw): + """ + Same as :meth:`urllib3.connectionpool.HTTPConnectionPool.urlopen` + with custom cross-host redirect logic and only sends the request-uri + portion of the ``url``. + + The given ``url`` parameter must be absolute, such that an appropriate + :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it. + """ + u = parse_url(url) + conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme) + + kw["assert_same_host"] = False + kw["redirect"] = False + + if "headers" not in kw: + kw["headers"] = self.headers.copy() + + if self.proxy is not None and u.scheme == "http": + response = conn.urlopen(method, url, **kw) + else: + response = conn.urlopen(method, u.request_uri, **kw) + + redirect_location = redirect and response.get_redirect_location() + if not redirect_location: + return response + + # Support relative URLs for redirecting. + redirect_location = urljoin(url, redirect_location) + + # RFC 7231, Section 6.4.4 + if response.status == 303: + method = "GET" + + retries = kw.get("retries") + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect) + + # Strip headers marked as unsafe to forward to the redirected location. + # Check remove_headers_on_redirect to avoid a potential network call within + # conn.is_same_host() which may use socket.gethostbyname() in the future. + if retries.remove_headers_on_redirect and not conn.is_same_host( + redirect_location + ): + headers = list(six.iterkeys(kw["headers"])) + for header in headers: + if header.lower() in retries.remove_headers_on_redirect: + kw["headers"].pop(header, None) + + try: + retries = retries.increment(method, url, response=response, _pool=conn) + except MaxRetryError: + if retries.raise_on_redirect: + response.drain_conn() + raise + return response + + kw["retries"] = retries + kw["redirect"] = redirect + + log.info("Redirecting %s -> %s", url, redirect_location) + + response.drain_conn() + return self.urlopen(method, redirect_location, **kw) + + +class ProxyManager(PoolManager): + """ + Behaves just like :class:`PoolManager`, but sends all requests through + the defined proxy, using the CONNECT method for HTTPS URLs. + + :param proxy_url: + The URL of the proxy to be used. + + :param proxy_headers: + A dictionary containing headers that will be sent to the proxy. In case + of HTTP they are being sent with each request, while in the + HTTPS/CONNECT case they are sent only once. Could be used for proxy + authentication. + + Example: + >>> proxy = urllib3.ProxyManager('http://localhost:3128/') + >>> r1 = proxy.request('GET', 'http://google.com/') + >>> r2 = proxy.request('GET', 'http://httpbin.org/') + >>> len(proxy.pools) + 1 + >>> r3 = proxy.request('GET', 'https://httpbin.org/') + >>> r4 = proxy.request('GET', 'https://twitter.com/') + >>> len(proxy.pools) + 3 + + """ + + def __init__( + self, + proxy_url, + num_pools=10, + headers=None, + proxy_headers=None, + **connection_pool_kw + ): + + if isinstance(proxy_url, HTTPConnectionPool): + proxy_url = "%s://%s:%i" % ( + proxy_url.scheme, + proxy_url.host, + proxy_url.port, + ) + proxy = parse_url(proxy_url) + if not proxy.port: + port = port_by_scheme.get(proxy.scheme, 80) + proxy = proxy._replace(port=port) + + if proxy.scheme not in ("http", "https"): + raise ProxySchemeUnknown(proxy.scheme) + + self.proxy = proxy + self.proxy_headers = proxy_headers or {} + + connection_pool_kw["_proxy"] = self.proxy + connection_pool_kw["_proxy_headers"] = self.proxy_headers + + super(ProxyManager, self).__init__(num_pools, headers, **connection_pool_kw) + + def connection_from_host(self, host, port=None, scheme="http", pool_kwargs=None): + if scheme == "https": + return super(ProxyManager, self).connection_from_host( + host, port, scheme, pool_kwargs=pool_kwargs + ) + + return super(ProxyManager, self).connection_from_host( + self.proxy.host, self.proxy.port, self.proxy.scheme, pool_kwargs=pool_kwargs + ) + + def _set_proxy_headers(self, url, headers=None): + """ + Sets headers needed by proxies: specifically, the Accept and Host + headers. Only sets headers not provided by the user. + """ + headers_ = {"Accept": "*/*"} + + netloc = parse_url(url).netloc + if netloc: + headers_["Host"] = netloc + + if headers: + headers_.update(headers) + return headers_ + + def _validate_proxy_scheme_url_selection(self, url_scheme): + if url_scheme == "https" and self.proxy.scheme == "https": + warnings.warn( + "Your proxy configuration specified an HTTPS scheme for the proxy. " + "Are you sure you want to use HTTPS to contact the proxy? " + "This most likely indicates an error in your configuration. " + "Read this issue for more info: " + "https://github.com/urllib3/urllib3/issues/1850", + InvalidProxyConfigurationWarning, + stacklevel=3, + ) + + def urlopen(self, method, url, redirect=True, **kw): + "Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute." + u = parse_url(url) + self._validate_proxy_scheme_url_selection(u.scheme) + + if u.scheme == "http": + # For proxied HTTPS requests, httplib sets the necessary headers + # on the CONNECT to the proxy. For HTTP, we'll definitely + # need to set 'Host' at the very least. + headers = kw.get("headers", self.headers) + kw["headers"] = self._set_proxy_headers(url, headers) + + return super(ProxyManager, self).urlopen(method, url, redirect=redirect, **kw) + + +def proxy_from_url(url, **kw): + return ProxyManager(proxy_url=url, **kw) diff --git a/venv/Lib/site-packages/urllib3/request.py b/venv/Lib/site-packages/urllib3/request.py new file mode 100644 index 00000000..55f160bb --- /dev/null +++ b/venv/Lib/site-packages/urllib3/request.py @@ -0,0 +1,171 @@ +from __future__ import absolute_import + +from .filepost import encode_multipart_formdata +from .packages.six.moves.urllib.parse import urlencode + + +__all__ = ["RequestMethods"] + + +class RequestMethods(object): + """ + Convenience mixin for classes who implement a :meth:`urlopen` method, such + as :class:`~urllib3.connectionpool.HTTPConnectionPool` and + :class:`~urllib3.poolmanager.PoolManager`. + + Provides behavior for making common types of HTTP request methods and + decides which type of request field encoding to use. + + Specifically, + + :meth:`.request_encode_url` is for sending requests whose fields are + encoded in the URL (such as GET, HEAD, DELETE). + + :meth:`.request_encode_body` is for sending requests whose fields are + encoded in the *body* of the request using multipart or www-form-urlencoded + (such as for POST, PUT, PATCH). + + :meth:`.request` is for making any kind of request, it will look up the + appropriate encoding format and use one of the above two methods to make + the request. + + Initializer parameters: + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + """ + + _encode_url_methods = {"DELETE", "GET", "HEAD", "OPTIONS"} + + def __init__(self, headers=None): + self.headers = headers or {} + + def urlopen( + self, + method, + url, + body=None, + headers=None, + encode_multipart=True, + multipart_boundary=None, + **kw + ): # Abstract + raise NotImplementedError( + "Classes extending RequestMethods must implement " + "their own ``urlopen`` method." + ) + + def request(self, method, url, fields=None, headers=None, **urlopen_kw): + """ + Make a request using :meth:`urlopen` with the appropriate encoding of + ``fields`` based on the ``method`` used. + + This is a convenience method that requires the least amount of manual + effort. It can be used in most situations, while still having the + option to drop down to more specific methods when necessary, such as + :meth:`request_encode_url`, :meth:`request_encode_body`, + or even the lowest level :meth:`urlopen`. + """ + method = method.upper() + + urlopen_kw["request_url"] = url + + if method in self._encode_url_methods: + return self.request_encode_url( + method, url, fields=fields, headers=headers, **urlopen_kw + ) + else: + return self.request_encode_body( + method, url, fields=fields, headers=headers, **urlopen_kw + ) + + def request_encode_url(self, method, url, fields=None, headers=None, **urlopen_kw): + """ + Make a request using :meth:`urlopen` with the ``fields`` encoded in + the url. This is useful for request methods like GET, HEAD, DELETE, etc. + """ + if headers is None: + headers = self.headers + + extra_kw = {"headers": headers} + extra_kw.update(urlopen_kw) + + if fields: + url += "?" + urlencode(fields) + + return self.urlopen(method, url, **extra_kw) + + def request_encode_body( + self, + method, + url, + fields=None, + headers=None, + encode_multipart=True, + multipart_boundary=None, + **urlopen_kw + ): + """ + Make a request using :meth:`urlopen` with the ``fields`` encoded in + the body. This is useful for request methods like POST, PUT, PATCH, etc. + + When ``encode_multipart=True`` (default), then + :meth:`urllib3.filepost.encode_multipart_formdata` is used to encode + the payload with the appropriate content type. Otherwise + :meth:`urllib.urlencode` is used with the + 'application/x-www-form-urlencoded' content type. + + Multipart encoding must be used when posting files, and it's reasonably + safe to use it in other times too. However, it may break request + signing, such as with OAuth. + + Supports an optional ``fields`` parameter of key/value strings AND + key/filetuple. A filetuple is a (filename, data, MIME type) tuple where + the MIME type is optional. For example:: + + fields = { + 'foo': 'bar', + 'fakefile': ('foofile.txt', 'contents of foofile'), + 'realfile': ('barfile.txt', open('realfile').read()), + 'typedfile': ('bazfile.bin', open('bazfile').read(), + 'image/jpeg'), + 'nonamefile': 'contents of nonamefile field', + } + + When uploading a file, providing a filename (the first parameter of the + tuple) is optional but recommended to best mimic behavior of browsers. + + Note that if ``headers`` are supplied, the 'Content-Type' header will + be overwritten because it depends on the dynamic random boundary string + which is used to compose the body of the request. The random boundary + string can be explicitly set with the ``multipart_boundary`` parameter. + """ + if headers is None: + headers = self.headers + + extra_kw = {"headers": {}} + + if fields: + if "body" in urlopen_kw: + raise TypeError( + "request got values for both 'fields' and 'body', can only specify one." + ) + + if encode_multipart: + body, content_type = encode_multipart_formdata( + fields, boundary=multipart_boundary + ) + else: + body, content_type = ( + urlencode(fields), + "application/x-www-form-urlencoded", + ) + + extra_kw["body"] = body + extra_kw["headers"] = {"Content-Type": content_type} + + extra_kw["headers"].update(headers) + extra_kw.update(urlopen_kw) + + return self.urlopen(method, url, **extra_kw) diff --git a/venv/Lib/site-packages/urllib3/response.py b/venv/Lib/site-packages/urllib3/response.py new file mode 100644 index 00000000..7dc9b93c --- /dev/null +++ b/venv/Lib/site-packages/urllib3/response.py @@ -0,0 +1,821 @@ +from __future__ import absolute_import +from contextlib import contextmanager +import zlib +import io +import logging +from socket import timeout as SocketTimeout +from socket import error as SocketError + +try: + import brotli +except ImportError: + brotli = None + +from ._collections import HTTPHeaderDict +from .exceptions import ( + BodyNotHttplibCompatible, + ProtocolError, + DecodeError, + ReadTimeoutError, + ResponseNotChunked, + IncompleteRead, + InvalidHeader, + HTTPError, +) +from .packages.six import string_types as basestring, PY3 +from .packages.six.moves import http_client as httplib +from .connection import HTTPException, BaseSSLError +from .util.response import is_fp_closed, is_response_to_head + +log = logging.getLogger(__name__) + + +class DeflateDecoder(object): + def __init__(self): + self._first_try = True + self._data = b"" + self._obj = zlib.decompressobj() + + def __getattr__(self, name): + return getattr(self._obj, name) + + def decompress(self, data): + if not data: + return data + + if not self._first_try: + return self._obj.decompress(data) + + self._data += data + try: + decompressed = self._obj.decompress(data) + if decompressed: + self._first_try = False + self._data = None + return decompressed + except zlib.error: + self._first_try = False + self._obj = zlib.decompressobj(-zlib.MAX_WBITS) + try: + return self.decompress(self._data) + finally: + self._data = None + + +class GzipDecoderState(object): + + FIRST_MEMBER = 0 + OTHER_MEMBERS = 1 + SWALLOW_DATA = 2 + + +class GzipDecoder(object): + def __init__(self): + self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) + self._state = GzipDecoderState.FIRST_MEMBER + + def __getattr__(self, name): + return getattr(self._obj, name) + + def decompress(self, data): + ret = bytearray() + if self._state == GzipDecoderState.SWALLOW_DATA or not data: + return bytes(ret) + while True: + try: + ret += self._obj.decompress(data) + except zlib.error: + previous_state = self._state + # Ignore data after the first error + self._state = GzipDecoderState.SWALLOW_DATA + if previous_state == GzipDecoderState.OTHER_MEMBERS: + # Allow trailing garbage acceptable in other gzip clients + return bytes(ret) + raise + data = self._obj.unused_data + if not data: + return bytes(ret) + self._state = GzipDecoderState.OTHER_MEMBERS + self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) + + +if brotli is not None: + + class BrotliDecoder(object): + # Supports both 'brotlipy' and 'Brotli' packages + # since they share an import name. The top branches + # are for 'brotlipy' and bottom branches for 'Brotli' + def __init__(self): + self._obj = brotli.Decompressor() + + def decompress(self, data): + if hasattr(self._obj, "decompress"): + return self._obj.decompress(data) + return self._obj.process(data) + + def flush(self): + if hasattr(self._obj, "flush"): + return self._obj.flush() + return b"" + + +class MultiDecoder(object): + """ + From RFC7231: + If one or more encodings have been applied to a representation, the + sender that applied the encodings MUST generate a Content-Encoding + header field that lists the content codings in the order in which + they were applied. + """ + + def __init__(self, modes): + self._decoders = [_get_decoder(m.strip()) for m in modes.split(",")] + + def flush(self): + return self._decoders[0].flush() + + def decompress(self, data): + for d in reversed(self._decoders): + data = d.decompress(data) + return data + + +def _get_decoder(mode): + if "," in mode: + return MultiDecoder(mode) + + if mode == "gzip": + return GzipDecoder() + + if brotli is not None and mode == "br": + return BrotliDecoder() + + return DeflateDecoder() + + +class HTTPResponse(io.IOBase): + """ + HTTP Response container. + + Backwards-compatible to httplib's HTTPResponse but the response ``body`` is + loaded and decoded on-demand when the ``data`` property is accessed. This + class is also compatible with the Python standard library's :mod:`io` + module, and can hence be treated as a readable object in the context of that + framework. + + Extra parameters for behaviour not present in httplib.HTTPResponse: + + :param preload_content: + If True, the response's body will be preloaded during construction. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param original_response: + When this HTTPResponse wrapper is generated from an httplib.HTTPResponse + object, it's convenient to include the original for debug purposes. It's + otherwise unused. + + :param retries: + The retries contains the last :class:`~urllib3.util.retry.Retry` that + was used during the request. + + :param enforce_content_length: + Enforce content length checking. Body returned by server must match + value of Content-Length header, if present. Otherwise, raise error. + """ + + CONTENT_DECODERS = ["gzip", "deflate"] + if brotli is not None: + CONTENT_DECODERS += ["br"] + REDIRECT_STATUSES = [301, 302, 303, 307, 308] + + def __init__( + self, + body="", + headers=None, + status=0, + version=0, + reason=None, + strict=0, + preload_content=True, + decode_content=True, + original_response=None, + pool=None, + connection=None, + msg=None, + retries=None, + enforce_content_length=False, + request_method=None, + request_url=None, + auto_close=True, + ): + + if isinstance(headers, HTTPHeaderDict): + self.headers = headers + else: + self.headers = HTTPHeaderDict(headers) + self.status = status + self.version = version + self.reason = reason + self.strict = strict + self.decode_content = decode_content + self.retries = retries + self.enforce_content_length = enforce_content_length + self.auto_close = auto_close + + self._decoder = None + self._body = None + self._fp = None + self._original_response = original_response + self._fp_bytes_read = 0 + self.msg = msg + self._request_url = request_url + + if body and isinstance(body, (basestring, bytes)): + self._body = body + + self._pool = pool + self._connection = connection + + if hasattr(body, "read"): + self._fp = body + + # Are we using the chunked-style of transfer encoding? + self.chunked = False + self.chunk_left = None + tr_enc = self.headers.get("transfer-encoding", "").lower() + # Don't incur the penalty of creating a list and then discarding it + encodings = (enc.strip() for enc in tr_enc.split(",")) + if "chunked" in encodings: + self.chunked = True + + # Determine length of response + self.length_remaining = self._init_length(request_method) + + # If requested, preload the body. + if preload_content and not self._body: + self._body = self.read(decode_content=decode_content) + + def get_redirect_location(self): + """ + Should we redirect and where to? + + :returns: Truthy redirect location string if we got a redirect status + code and valid location. ``None`` if redirect status and no + location. ``False`` if not a redirect status code. + """ + if self.status in self.REDIRECT_STATUSES: + return self.headers.get("location") + + return False + + def release_conn(self): + if not self._pool or not self._connection: + return + + self._pool._put_conn(self._connection) + self._connection = None + + def drain_conn(self): + """ + Read and discard any remaining HTTP response data in the response connection. + + Unread data in the HTTPResponse connection blocks the connection from being released back to the pool. + """ + try: + self.read() + except (HTTPError, SocketError, BaseSSLError, HTTPException): + pass + + @property + def data(self): + # For backwords-compat with earlier urllib3 0.4 and earlier. + if self._body: + return self._body + + if self._fp: + return self.read(cache_content=True) + + @property + def connection(self): + return self._connection + + def isclosed(self): + return is_fp_closed(self._fp) + + def tell(self): + """ + Obtain the number of bytes pulled over the wire so far. May differ from + the amount of content returned by :meth:``HTTPResponse.read`` if bytes + are encoded on the wire (e.g, compressed). + """ + return self._fp_bytes_read + + def _init_length(self, request_method): + """ + Set initial length value for Response content if available. + """ + length = self.headers.get("content-length") + + if length is not None: + if self.chunked: + # This Response will fail with an IncompleteRead if it can't be + # received as chunked. This method falls back to attempt reading + # the response before raising an exception. + log.warning( + "Received response with both Content-Length and " + "Transfer-Encoding set. This is expressly forbidden " + "by RFC 7230 sec 3.3.2. Ignoring Content-Length and " + "attempting to process response as Transfer-Encoding: " + "chunked." + ) + return None + + try: + # RFC 7230 section 3.3.2 specifies multiple content lengths can + # be sent in a single Content-Length header + # (e.g. Content-Length: 42, 42). This line ensures the values + # are all valid ints and that as long as the `set` length is 1, + # all values are the same. Otherwise, the header is invalid. + lengths = set([int(val) for val in length.split(",")]) + if len(lengths) > 1: + raise InvalidHeader( + "Content-Length contained multiple " + "unmatching values (%s)" % length + ) + length = lengths.pop() + except ValueError: + length = None + else: + if length < 0: + length = None + + # Convert status to int for comparison + # In some cases, httplib returns a status of "_UNKNOWN" + try: + status = int(self.status) + except ValueError: + status = 0 + + # Check for responses that shouldn't include a body + if status in (204, 304) or 100 <= status < 200 or request_method == "HEAD": + length = 0 + + return length + + def _init_decoder(self): + """ + Set-up the _decoder attribute if necessary. + """ + # Note: content-encoding value should be case-insensitive, per RFC 7230 + # Section 3.2 + content_encoding = self.headers.get("content-encoding", "").lower() + if self._decoder is None: + if content_encoding in self.CONTENT_DECODERS: + self._decoder = _get_decoder(content_encoding) + elif "," in content_encoding: + encodings = [ + e.strip() + for e in content_encoding.split(",") + if e.strip() in self.CONTENT_DECODERS + ] + if len(encodings): + self._decoder = _get_decoder(content_encoding) + + DECODER_ERROR_CLASSES = (IOError, zlib.error) + if brotli is not None: + DECODER_ERROR_CLASSES += (brotli.error,) + + def _decode(self, data, decode_content, flush_decoder): + """ + Decode the data passed in and potentially flush the decoder. + """ + if not decode_content: + return data + + try: + if self._decoder: + data = self._decoder.decompress(data) + except self.DECODER_ERROR_CLASSES as e: + content_encoding = self.headers.get("content-encoding", "").lower() + raise DecodeError( + "Received response with content-encoding: %s, but " + "failed to decode it." % content_encoding, + e, + ) + if flush_decoder: + data += self._flush_decoder() + + return data + + def _flush_decoder(self): + """ + Flushes the decoder. Should only be called if the decoder is actually + being used. + """ + if self._decoder: + buf = self._decoder.decompress(b"") + return buf + self._decoder.flush() + + return b"" + + @contextmanager + def _error_catcher(self): + """ + Catch low-level python exceptions, instead re-raising urllib3 + variants, so that low-level exceptions are not leaked in the + high-level api. + + On exit, release the connection back to the pool. + """ + clean_exit = False + + try: + try: + yield + + except SocketTimeout: + # FIXME: Ideally we'd like to include the url in the ReadTimeoutError but + # there is yet no clean way to get at it from this context. + raise ReadTimeoutError(self._pool, None, "Read timed out.") + + except BaseSSLError as e: + # FIXME: Is there a better way to differentiate between SSLErrors? + if "read operation timed out" not in str(e): # Defensive: + # This shouldn't happen but just in case we're missing an edge + # case, let's avoid swallowing SSL errors. + raise + + raise ReadTimeoutError(self._pool, None, "Read timed out.") + + except (HTTPException, SocketError) as e: + # This includes IncompleteRead. + raise ProtocolError("Connection broken: %r" % e, e) + + # If no exception is thrown, we should avoid cleaning up + # unnecessarily. + clean_exit = True + finally: + # If we didn't terminate cleanly, we need to throw away our + # connection. + if not clean_exit: + # The response may not be closed but we're not going to use it + # anymore so close it now to ensure that the connection is + # released back to the pool. + if self._original_response: + self._original_response.close() + + # Closing the response may not actually be sufficient to close + # everything, so if we have a hold of the connection close that + # too. + if self._connection: + self._connection.close() + + # If we hold the original response but it's closed now, we should + # return the connection back to the pool. + if self._original_response and self._original_response.isclosed(): + self.release_conn() + + def read(self, amt=None, decode_content=None, cache_content=False): + """ + Similar to :meth:`httplib.HTTPResponse.read`, but with two additional + parameters: ``decode_content`` and ``cache_content``. + + :param amt: + How much of the content to read. If specified, caching is skipped + because it doesn't make sense to cache partial content as the full + response. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param cache_content: + If True, will save the returned data such that the same result is + returned despite of the state of the underlying file object. This + is useful if you want the ``.data`` property to continue working + after having ``.read()`` the file object. (Overridden if ``amt`` is + set.) + """ + self._init_decoder() + if decode_content is None: + decode_content = self.decode_content + + if self._fp is None: + return + + flush_decoder = False + fp_closed = getattr(self._fp, "closed", False) + + with self._error_catcher(): + if amt is None: + # cStringIO doesn't like amt=None + data = self._fp.read() if not fp_closed else b"" + flush_decoder = True + else: + cache_content = False + data = self._fp.read(amt) if not fp_closed else b"" + if ( + amt != 0 and not data + ): # Platform-specific: Buggy versions of Python. + # Close the connection when no data is returned + # + # This is redundant to what httplib/http.client _should_ + # already do. However, versions of python released before + # December 15, 2012 (http://bugs.python.org/issue16298) do + # not properly close the connection in all cases. There is + # no harm in redundantly calling close. + self._fp.close() + flush_decoder = True + if self.enforce_content_length and self.length_remaining not in ( + 0, + None, + ): + # This is an edge case that httplib failed to cover due + # to concerns of backward compatibility. We're + # addressing it here to make sure IncompleteRead is + # raised during streaming, so all calls with incorrect + # Content-Length are caught. + raise IncompleteRead(self._fp_bytes_read, self.length_remaining) + + if data: + self._fp_bytes_read += len(data) + if self.length_remaining is not None: + self.length_remaining -= len(data) + + data = self._decode(data, decode_content, flush_decoder) + + if cache_content: + self._body = data + + return data + + def stream(self, amt=2 ** 16, decode_content=None): + """ + A generator wrapper for the read() method. A call will block until + ``amt`` bytes have been read from the connection or until the + connection is closed. + + :param amt: + How much of the content to read. The generator will return up to + much data per iteration, but may return less. This is particularly + likely when using compressed data. However, the empty string will + never be returned. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + if self.chunked and self.supports_chunked_reads(): + for line in self.read_chunked(amt, decode_content=decode_content): + yield line + else: + while not is_fp_closed(self._fp): + data = self.read(amt=amt, decode_content=decode_content) + + if data: + yield data + + @classmethod + def from_httplib(ResponseCls, r, **response_kw): + """ + Given an :class:`httplib.HTTPResponse` instance ``r``, return a + corresponding :class:`urllib3.response.HTTPResponse` object. + + Remaining parameters are passed to the HTTPResponse constructor, along + with ``original_response=r``. + """ + headers = r.msg + + if not isinstance(headers, HTTPHeaderDict): + if PY3: + headers = HTTPHeaderDict(headers.items()) + else: + # Python 2.7 + headers = HTTPHeaderDict.from_httplib(headers) + + # HTTPResponse objects in Python 3 don't have a .strict attribute + strict = getattr(r, "strict", 0) + resp = ResponseCls( + body=r, + headers=headers, + status=r.status, + version=r.version, + reason=r.reason, + strict=strict, + original_response=r, + **response_kw + ) + return resp + + # Backwards-compatibility methods for httplib.HTTPResponse + def getheaders(self): + return self.headers + + def getheader(self, name, default=None): + return self.headers.get(name, default) + + # Backwards compatibility for http.cookiejar + def info(self): + return self.headers + + # Overrides from io.IOBase + def close(self): + if not self.closed: + self._fp.close() + + if self._connection: + self._connection.close() + + if not self.auto_close: + io.IOBase.close(self) + + @property + def closed(self): + if not self.auto_close: + return io.IOBase.closed.__get__(self) + elif self._fp is None: + return True + elif hasattr(self._fp, "isclosed"): + return self._fp.isclosed() + elif hasattr(self._fp, "closed"): + return self._fp.closed + else: + return True + + def fileno(self): + if self._fp is None: + raise IOError("HTTPResponse has no file to get a fileno from") + elif hasattr(self._fp, "fileno"): + return self._fp.fileno() + else: + raise IOError( + "The file-like object this HTTPResponse is wrapped " + "around has no file descriptor" + ) + + def flush(self): + if ( + self._fp is not None + and hasattr(self._fp, "flush") + and not getattr(self._fp, "closed", False) + ): + return self._fp.flush() + + def readable(self): + # This method is required for `io` module compatibility. + return True + + def readinto(self, b): + # This method is required for `io` module compatibility. + temp = self.read(len(b)) + if len(temp) == 0: + return 0 + else: + b[: len(temp)] = temp + return len(temp) + + def supports_chunked_reads(self): + """ + Checks if the underlying file-like object looks like a + httplib.HTTPResponse object. We do this by testing for the fp + attribute. If it is present we assume it returns raw chunks as + processed by read_chunked(). + """ + return hasattr(self._fp, "fp") + + def _update_chunk_length(self): + # First, we'll figure out length of a chunk and then + # we'll try to read it from socket. + if self.chunk_left is not None: + return + line = self._fp.fp.readline() + line = line.split(b";", 1)[0] + try: + self.chunk_left = int(line, 16) + except ValueError: + # Invalid chunked protocol response, abort. + self.close() + raise httplib.IncompleteRead(line) + + def _handle_chunk(self, amt): + returned_chunk = None + if amt is None: + chunk = self._fp._safe_read(self.chunk_left) + returned_chunk = chunk + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + elif amt < self.chunk_left: + value = self._fp._safe_read(amt) + self.chunk_left = self.chunk_left - amt + returned_chunk = value + elif amt == self.chunk_left: + value = self._fp._safe_read(amt) + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + returned_chunk = value + else: # amt > self.chunk_left + returned_chunk = self._fp._safe_read(self.chunk_left) + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + return returned_chunk + + def read_chunked(self, amt=None, decode_content=None): + """ + Similar to :meth:`HTTPResponse.read`, but with an additional + parameter: ``decode_content``. + + :param amt: + How much of the content to read. If specified, caching is skipped + because it doesn't make sense to cache partial content as the full + response. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + self._init_decoder() + # FIXME: Rewrite this method and make it a class with a better structured logic. + if not self.chunked: + raise ResponseNotChunked( + "Response is not chunked. " + "Header 'transfer-encoding: chunked' is missing." + ) + if not self.supports_chunked_reads(): + raise BodyNotHttplibCompatible( + "Body should be httplib.HTTPResponse like. " + "It should have have an fp attribute which returns raw chunks." + ) + + with self._error_catcher(): + # Don't bother reading the body of a HEAD request. + if self._original_response and is_response_to_head(self._original_response): + self._original_response.close() + return + + # If a response is already read and closed + # then return immediately. + if self._fp.fp is None: + return + + while True: + self._update_chunk_length() + if self.chunk_left == 0: + break + chunk = self._handle_chunk(amt) + decoded = self._decode( + chunk, decode_content=decode_content, flush_decoder=False + ) + if decoded: + yield decoded + + if decode_content: + # On CPython and PyPy, we should never need to flush the + # decoder. However, on Jython we *might* need to, so + # lets defensively do it anyway. + decoded = self._flush_decoder() + if decoded: # Platform-specific: Jython. + yield decoded + + # Chunk content ends with \r\n: discard it. + while True: + line = self._fp.fp.readline() + if not line: + # Some sites may not end with '\r\n'. + break + if line == b"\r\n": + break + + # We read everything; close the "file". + if self._original_response: + self._original_response.close() + + def geturl(self): + """ + Returns the URL that was the source of this response. + If the request that generated this response redirected, this method + will return the final redirect location. + """ + if self.retries is not None and len(self.retries.history): + return self.retries.history[-1].redirect_location + else: + return self._request_url + + def __iter__(self): + buffer = [] + for chunk in self.stream(decode_content=True): + if b"\n" in chunk: + chunk = chunk.split(b"\n") + yield b"".join(buffer) + chunk[0] + b"\n" + for x in chunk[1:-1]: + yield x + b"\n" + if chunk[-1]: + buffer = [chunk[-1]] + else: + buffer = [] + else: + buffer.append(chunk) + if buffer: + yield b"".join(buffer) diff --git a/venv/Lib/site-packages/urllib3/util/__init__.py b/venv/Lib/site-packages/urllib3/util/__init__.py new file mode 100644 index 00000000..a96c73a9 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/util/__init__.py @@ -0,0 +1,46 @@ +from __future__ import absolute_import + +# For backwards compatibility, provide imports that used to be here. +from .connection import is_connection_dropped +from .request import make_headers +from .response import is_fp_closed +from .ssl_ import ( + SSLContext, + HAS_SNI, + IS_PYOPENSSL, + IS_SECURETRANSPORT, + assert_fingerprint, + resolve_cert_reqs, + resolve_ssl_version, + ssl_wrap_socket, + PROTOCOL_TLS, +) +from .timeout import current_time, Timeout + +from .retry import Retry +from .url import get_host, parse_url, split_first, Url +from .wait import wait_for_read, wait_for_write + +__all__ = ( + "HAS_SNI", + "IS_PYOPENSSL", + "IS_SECURETRANSPORT", + "SSLContext", + "PROTOCOL_TLS", + "Retry", + "Timeout", + "Url", + "assert_fingerprint", + "current_time", + "is_connection_dropped", + "is_fp_closed", + "get_host", + "parse_url", + "make_headers", + "resolve_cert_reqs", + "resolve_ssl_version", + "split_first", + "ssl_wrap_socket", + "wait_for_read", + "wait_for_write", +) diff --git a/venv/Lib/site-packages/urllib3/util/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/urllib3/util/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0aab9bd2496ecd7b3fa722d88767e12072cf0ccb GIT binary patch literal 1137 zcmchWOK;Oa5XbF2>O7oBlcrBv3Y;QQBvgq*g%DM!M5U^!;uMLM#mbGht<_z7*5S)w(L~5hnOpa+K1ej3WogQ0CdpD7t4vB3!w-BBT`_ve zqL?X-IlOo->5OL@*6X$r1V^JRHSCMA-uwN42FHiiKMd%}$MMO*F|67iWWmAcbaF7A z>>mdwdb?(HhAWDF*0%qB&p73 zSIk)dWHO$PN8=+pJqm^;>(6qfSZb(A1hd{0nB@kVuTGdzH^YjpT`)s0Gi_{LMoKf9 zD{h-w@&sE_P%ob<4u92}M_9@f^b^Il{U@EP#4uF@^;8|vKzN7_qK8;SEF+qTE&}Gs zQ45GZVhK@2_=pw)M@+R5D~MIV@o@0p_8M;62Bqh@$rYnim0)|@xSx&U1l9w4a;8nu zyRCsuGE2cxJejSaL3d*OB~YQm{Yi^MqF&ZRtH7i?kx)2WRcKbBq%DI#4+JX|LSbzM z4Y}9JdXz?-Q~J$O&tSd4&kv)$@D!TUp}xrSINX==U1Xy0D4Rt*oH8LpnaG_yl@ZKS zyfeRKjPG0+mcEK!o`tYCPtJD3+$21tG)ZA1=nHvcYp+EX=bXL4k4WzU{Ic)j{k+*N O`R=OQEj#YN-M;|sUKYmy literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/util/__pycache__/connection.cpython-36.pyc b/venv/Lib/site-packages/urllib3/util/__pycache__/connection.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b7ddffd477b1e407f9ac60f2f9c38e92e3f5d3f GIT binary patch literal 3141 zcmaJ@TW=f372X@kB}GxP;wEw729vfxOw|^WIM4&p7FBhr-8!^j(v__+Sq?cvX{qJz zW@c!K!ZHt09^3wc{(-)>f1y9255>L)DB$-#<*DDyQWpn7cQLzjJ?G3h-?=fMgQl_ z>sYh8(rdW%pRwXu$~mct9oy2c-uuF0F0N9c1Z-*q&oi#3B9^2prksQ$qK-KkCUMM% zDokP$O2R|}AsL}DBl0+zMvM%wCrTt|8#P0ul~O?`=3L86Xv(c&M5S!?kCjTJaIo>H z+uh&3qTDCRU<@V;3hhMk7J<@88c_v3O(coJ6Hd0$bk`{8K172wr(qNk8lBRaB*Mr} z`u#l`N#5@hl^E_3SLPmN#_7Udp^N zkal5jJ%^RjX-ta;_^@5&u}nU|Ha3VNjrZ^!!luX8{1d!>1GUYjq?>+@yo zZ_FNH-OZ|5En5O=8>*Z&GWWu{^bC^S z@e;87FWZ{0*w*=b*~%Q$fc&cXC-&AaG{V8-z2_dcWwY}?S@~iVI zkCY-6XS zS4uhL-l|8<&QG2O%OEl{i&UCJV}-WOOj0Czooz z_O2o%Y{f*|jeyYy9}pd_U_0_5Q9O#IVe_=3F$C$&XmPpX@OSoS=gw!Jx6j|#fde25 z<598*Vvr^#TJtl6J*k?@c@4%9N}h)5I4?;h!Zcr_QIwo=797zq7|}@>&GK@A%6#bt z&3vizbo&oMr~7Po>(l(L0NM(QZ9#z>nZFbK?#a{NZaoQhcK5cPKj{YD$Di&#eclCI zZ7mbxa!+e2uPl_1m$*q~B@_Cat9fOiH&I3G$Qy5(NsqO=TygZY;&M+bEO$XEFE4K3 z!r5|%j&fh0P_vZhrBoy;$*V&eBbw3)&mBokP|a;_qH6J3){Jirky4S2uWj`{rsc=@ zSZlsr!|RpG&bx?zp6%ETx8|6+V#QfA5z@DR>>yS;;uknI)VWtb?xL9pc@G2RRtved z&a94g_I73?@-^m8W{nZO{(=bf<=O#C5kwWcja;F1sqX}(H{C+~%B2BZbiQ{J@isWT zM3XvwSUPS)QZqZ$6Z+BBfo?{k+vq53Y|oMB#VUI495GyTKmSKpC(Fx-WYRd7;ZY3l z13XU?aYDi(7(i``pJ_-Ncew$h`vj>7F`=;2uL^r^Z0!Zl+nxR0ZB0}=)}^~6#~KPI zL#JH8mnMXgopuNAO2zQlB-cTrj>+TwSDPlJ`_fEq;bg-&dW^cNDEqU8xMCmTjSDuL>#`gR6pSfC01TNN#gStMI}S zw{?M}oUtpO&3=l%gN8Jri>+}9rI$N0iHUi0=fH|@qqr&~A?+8%)k`aOUH~5A3+%4w zWR!tgvvj`sgB6yXNyJ$Kd@rI=ywFLJdNxRVPiQlDo4(NbJim=pe_bm|=z5Tx73%Vj zsSLvO)n@K56t^kv=rh&bvmrNmLfpj|5G42+9Eyj!r^}NYj#LdUDMeV2`i$jxwpZ3b zESDWymk>T$8MD4EHnD#0wpt%GSMpjAjHak0c@PNgJ%wnVu6j*nWNIW6N)X8g=1!&< z5%1{D+UX3=jl~RM@u8l(meBN!zx%(OBy1Y-Uuy+P?K8T}S+(n~e^)zr{S)8!m;Cqs E2a8Ky5dZ)H literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/util/__pycache__/queue.cpython-36.pyc b/venv/Lib/site-packages/urllib3/util/__pycache__/queue.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba450a02ec611ae9ec4024920ac4ab1d7ef91c6d GIT binary patch literal 1013 zcmZuwO>fgc5Zw>kanrOE=!Ku)LnELP2UMX}t;D5@K!pRA#me<=g2VbFUYn09xAf2O zkI-H@^~jYIGn)n(=t}c;Julw(W;{9E*%_WZ`u_eWCFBR`Ef3De5atX(5=k|QiDZ&% zTCkW=JbRiKUhGk#*xYZ%?pHxPcuAx$gKHuK<;{Z?k3&f>NHn;Co5sDIr46`JyZj&IbG5fg*mwGA9I>ENb6daGGc3OFK<=x zuec}buD#!r-t9>PmBBE&g*%4(Y_3cvy!u;`*HsNAW3B%y%~ZP&#HPr;5>S&Uw4o4X zS|}mxP>7h((v`HPejn?i~c|ScBf1hX`n@2iV)j2f(~RMO`KxzNt?x!T-WOfj86`IsUDn;&(|2~<2M&+c SrM$rY8O(=tL`N)OA^#00mDyte literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/util/__pycache__/request.cpython-36.pyc b/venv/Lib/site-packages/urllib3/util/__pycache__/request.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..130b7a36632987c0b6e0d004e409aef6add730b2 GIT binary patch literal 3307 zcmbtX-EQN?73NSBB~$WGwwor|rtQ>iplTs$6J+5QLb9msbqW`Tc9Gp}HiA%k#2H(( zDUv%gT(49a1>&F=eSlu{4f;j}Zws`@E99!@3?)l;Q}iMs*J1s zD$L#f+N2KDmB{PYzOdrEjwR~no_t9x{QJ_1y_c3)*I8CI6rM2PCLlFlF7vI=OWang< zuZOV~r$YLAk_rV>Z@-A8xCmp;hLPAy_~by!L{96c(=csuaS|~tdec&CC<7Pof~Nwn z1Mfb(>TfWF)}=cmaz|S;qREvl@5cAc+=lsmPR>d*az!pl>}cnb%*f2;_9xcNmU}ZB z<|UY0vd0~mmtkI#5qEW2yR*_2oI9)V$`yIXg54_YI`S{`UFX*B8tj(kU%3aAHJG~q zP@UC4RFA^*Yy`LjnO5{9k@Qr=LNXo3r&NkpnNXUJ1mi-gj%UcbDU)nW&xJ^R7KN`x z*DNEa!xMUZ{9I<@__#$G=ZZFhB#uR(!zAvSBW-h}G3R-cB?|(PYTwuf?qBlVXE7ZL z%IGjjBEjMojY6ec6vS0p0%_-3gvI?-OBO3MlA=1Hja9QXmgGmkhhBtH1bSV8Q7BI3-fF!8Nr^qArb-wmEalD-YhVaTH_iUx&eM+ie@$ULKVhJvp5iW34%L!Fw}DgLo?sp-QPcW`rJS0?LT>Z*!!(4 zL6Vha7;={j%;?QZZsaU76J-CJDJ3@w752%!rO)PDx4k%Dzu``KWp44mTk>7F@7ovq z-N6emmKvzjB;$kKG=0o88yqD8iw4idI31*6+RoyX1?PgdFGfN{?Ncq{9rn?1Fc0NH zro(73&$FF$lGj(g>keF`?!#c+cZfqgV#8av9a3F+@4+1RigkGpr~@o(_~36HUQ>`h zhslg^a=s?-LJ@-bl})U;tH})NG$&Ujv)7x{XV#g0WnB`e#P2(nE@|h)hH89NRAXJf zBv6s8B8BG%vVg{k%GC8*BIC(ATNf-&t#CpUu&_TH^((4mQgYqI& z5IYy50g0Gt4d`j4m0(jyR4aV3LY-tflO}Z*9D2EVVFa|D$_weXjN@yR&_ZRB*VOed# zV3n(+PPV`>b#kZV+4ASGb`5_CKCa={@PV*%!tJZlr9HE`4W{S;{6u3vfWX_>4-o4M z>s7h}{O=aPbEx}Y0r;=dmsY$D=iKwEJl16n75^&|Z|Mpz>nig2p@l2l1_Qa%e;EVq zuHuS~4OCM;L6{j0gw!mIukBX=`&5QwCc|h_6gf%mldhvDy=$3oS_kl2iS7BbWYT(F) zJHxnPS7EF=HZfF9w`ZQ_o!s;NA`E?BVwTBWNTy=vp~=YoIwn2nYE(ysZ%lqRvge+7 zgB>0u2vtbGSsay#j>7)ieilQ$;r=+`8ARLAEa$aD)3_JfxrO%_wN0)w5oe@Z(F-+^ pCU&cz6wDqGSW>@$p$?gH%VbLo+705`RoivU{~0FCyuIq3{{X*UwpjoG literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/util/__pycache__/response.cpython-36.pyc b/venv/Lib/site-packages/urllib3/util/__pycache__/response.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d70dd748bf8aca0b03c70e7feacc59b5dc49abb9 GIT binary patch literal 1940 zcmZWqTW=#Z6t-t3$#j}_m#&bOixvy5kRs6tR0*L7A*ybrf}m7vcePYer_R`uOyjw% z?ddipecABJ58;iUgXWc|{so>m_DtH8oygZ?pL2Y^@0{#xYy`U>{C&C+AoLGf`5c&k z2ThrdA%+D?@d?I=Im{JK>YjLZ?uk~~K54_e#oE84lMd@JA4Z?8u>i(3w*DOTx;LN& z^>Fooj#Ms6&BG)ua;a~O9oS!=XkCPnNO-1UxplxPim2GEOddM&d;9ZIMhl$w%ir(j-wN61n26AJ}_?f=Zei z>AQQwhKP}iDVaI`R~iU!SIFRQVrEOFWGRnD4tPGhd<^K6~kkm z7Q-Sbw#%%b(Uh}o2m=?}W6iU#=$E5mDTPQzJHt{ZVko&P@=Wo5F{`?X3S)z?iAcvl z_aSu1cU&KD;B?RsYuHCsKPXYDL0;$uvvq0A#=<>9$*J98HG7rvR70+EMbuhAj^y9px!*jHN}xTw8dAf=;_6d=SwPA_1eVqt%b$~RuE}p-T=%OE<1w63ht>L+ z>#(3RkyF-l@5~v(X(=6gur_oxiuRN++JBEihsS)QgrMmJ= zEUFIZr;t0Avnc?OfNEW}Ml;P-^VM`Pe68?3#MYa&}=R|g^8(tz;isu z+A#R>9O~5+D&8EQy9>wzGl%N+myPxRs#ndj*Ckh$X4Fv5<}7en)fA@WV1M`7w&eCy zjo1Orm;ff2>#xhi5-=uEEB3Q7RHr3h;DJ!_qCCrLHq>0RYchxgL}PG|Z@)Cg&#bFA z=9QIatoOXC6Q)pbb0)XUuA5|lNLdtI_MFOJdR?`fL#CFk1pX0JpXHfQ=N5n;;E3{} zLu+juU+;4Tj=F>L9w jm|2Z0B(PptCivf^IV%PK*8H0(`13^u_O9!H;y?WlLc<-4 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/util/__pycache__/retry.cpython-36.pyc b/venv/Lib/site-packages/urllib3/util/__pycache__/retry.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d08f9e7beafd89ac6e2599cae4f961bfe82229c GIT binary patch literal 12923 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/util/__pycache__/ssl_.cpython-36.pyc b/venv/Lib/site-packages/urllib3/util/__pycache__/ssl_.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de3ef1e3f2168ed4c3121a77a25920dcbb453984 GIT binary patch literal 10028 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/util/__pycache__/timeout.cpython-36.pyc b/venv/Lib/site-packages/urllib3/util/__pycache__/timeout.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0812c21883f7503c9e0c891ad1f0e5d2c48b3e8c GIT binary patch literal 8817 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/util/__pycache__/url.cpython-36.pyc b/venv/Lib/site-packages/urllib3/util/__pycache__/url.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a02e7431e08a2cf9baf5dc787fc5a4f26a38a86b GIT binary patch literal 10582 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 E51zGs#sB~S literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/util/__pycache__/wait.cpython-36.pyc b/venv/Lib/site-packages/urllib3/util/__pycache__/wait.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5adc4ec9967d2e44166e0ab339583413d87c524f GIT binary patch literal 3123 zcmc&$TW=f36`q;Ba79xREi1O{B+9-tErZxe>Y!+YG>Ge3fdPXGBn3(dB8wGgD6P2M zrDun_7|Rw21*9#|JheZf|E7<{zV@a53wg?SW+gfh@=)ZZOP(_`XJ%*5`OanL;qr3x z(cOQ4wfP!j|6xCPTIhcYD*go~ndB+!Q7ckD7CpghnbULZoZEBH+vqu|H}-ozXZtJ* zU8WkNrrzU>ewcMJ>(T5VSSG$?(v!Yy96G(=GbRJ+T|lCi=1#Av+~XVa^^vD9QHfzzH3+?YYF;U<$@XdmU>SWLg{E(UFo{m$RfDS_E2&}#;fIOj8wH4UFbdWX%#nLZD$^l+IG#yNI}0+IJu z&Snm0FMd}#$DR~-nemSsBS-Ii&9YyVf_B(oGmgE*i0LCKs9%3!B=niY-09{%q7|`= zo&M;2$)0Z#?PCm^oo?kM*-(>zJ3-|Q(>yksP^%i-yLFiOg-X@HRGzX?d(2BV=bV*7|GM<9Miy76bW5-F z=I+clZt0iKT=bn;V;0Poj8`@$vJ5~=HX=m##V^Ox!i2>{4U*w`7@IIvF$8(0!h9G~ z-u!k?Pu07j%^BP8_*H9Xno;~*0vJywDwCSRM59VEi$moeb*R<{&!pru|w%35JZr47G7AZ~NQ06myytoa@hD9|Q6MMMH4__G7ps;#^?r548mKEl_a}tVFDjmUJ5}{|%dwE|6V%IQcK^ zqiQ+HQ!1g{fCV&&@`K|VSDW|^nMWiZ(zb*R#c#l0{LHSs2K0lEQ>tRcCl4;)>IC|A zykgy7`JZn;d-k+jHK^VGVo(1ZQ&phVVNw`m*_*VIs!7M!lqvcxVj(e`V(vIDMz-`= z{W@HCXwIs+{$uL&Fn!JM*6Yx}<+MEg9_9xi`w(_S+aO!-uX4;*4CKUSmWOqG-B-`2 z$w{25%!KhtoTTwVs-O;~Ye(oiXexhnN)_XJgsx{(Kzt3hj6O-Ni8REYU+vMP%X3-a!3yOkF+SzdX0aF2h;v?AA5U?b>+R+Ut0AHWK=E zO0GOIMkfbTQ)jMT$IKPFi-W7ic`Z7MPgHc8>rs*&+Q`tP9BIj1R5aw%8ux{Mi1ES> zvdsZmZ0YPUY!1g69*b<9^d>v>8^*)fHGV9}x{`lO=acAKbvq5arS)00dJVv;aTFIZ zjHoT6e6~PdIzvQ!u@aE5Vsn4G9AtV_~^W{77;nW@f-kZNg|3`29IF6YCf zu$xh2y&GcZEu__A&E+8-s`0gS9qeC|Te>G7uu0NhG=s044LcpgQv0pJ*{v z?*FI7AzAi6THL_ef4s67;4IVIe3sdIREWX*kUW1}9eW>Kv<_;=Isw zM$}h^8&AiRTwmTU8&MP^!=q~H$=N_nOp<4|E>x`(-1#uV{RxpmWzL>EHlS^=*6~eV z)8raW+Wx?2HW6*D3qG!|nV(R3C@9!b&!{TnPSNHod`&c+_C4G&{NPUWR&zaYgXLhg G+4vs9y}n-n literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/urllib3/util/connection.py b/venv/Lib/site-packages/urllib3/util/connection.py new file mode 100644 index 00000000..86f0a3b0 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/util/connection.py @@ -0,0 +1,138 @@ +from __future__ import absolute_import +import socket +from .wait import NoWayToWaitForSocketError, wait_for_read +from ..contrib import _appengine_environ + + +def is_connection_dropped(conn): # Platform-specific + """ + Returns True if the connection is dropped and should be closed. + + :param conn: + :class:`httplib.HTTPConnection` object. + + Note: For platforms like AppEngine, this will always return ``False`` to + let the platform handle connection recycling transparently for us. + """ + sock = getattr(conn, "sock", False) + if sock is False: # Platform-specific: AppEngine + return False + if sock is None: # Connection already closed (such as by httplib). + return True + try: + # Returns True if readable, which here means it's been dropped + return wait_for_read(sock, timeout=0.0) + except NoWayToWaitForSocketError: # Platform-specific: AppEngine + return False + + +# This function is copied from socket.py in the Python 2.7 standard +# library test suite. Added to its signature is only `socket_options`. +# One additional modification is that we avoid binding to IPv6 servers +# discovered in DNS if the system doesn't have IPv6 functionality. +def create_connection( + address, + timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + source_address=None, + socket_options=None, +): + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith("["): + host = host.strip("[]") + err = None + + # Using the value from allowed_gai_family() in the context of getaddrinfo lets + # us select whether to work with IPv4 DNS records, IPv6 records, or both. + # The original create_connection function always returns all records. + family = allowed_gai_family() + + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + _set_socket_options(sock, socket_options) + + if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + sock.connect(sa) + return sock + + except socket.error as e: + err = e + if sock is not None: + sock.close() + sock = None + + if err is not None: + raise err + + raise socket.error("getaddrinfo returns an empty list") + + +def _set_socket_options(sock, options): + if options is None: + return + + for opt in options: + sock.setsockopt(*opt) + + +def allowed_gai_family(): + """This function is designed to work in the context of + getaddrinfo, where family=socket.AF_UNSPEC is the default and + will perform a DNS search for both IPv6 and IPv4 records.""" + + family = socket.AF_INET + if HAS_IPV6: + family = socket.AF_UNSPEC + return family + + +def _has_ipv6(host): + """ Returns True if the system can bind an IPv6 address. """ + sock = None + has_ipv6 = False + + # App Engine doesn't support IPV6 sockets and actually has a quota on the + # number of sockets that can be used, so just early out here instead of + # creating a socket needlessly. + # See https://github.com/urllib3/urllib3/issues/1446 + if _appengine_environ.is_appengine_sandbox(): + return False + + if socket.has_ipv6: + # has_ipv6 returns true if cPython was compiled with IPv6 support. + # It does not tell us if the system has IPv6 support enabled. To + # determine that we must bind to an IPv6 address. + # https://github.com/urllib3/urllib3/pull/611 + # https://bugs.python.org/issue658327 + try: + sock = socket.socket(socket.AF_INET6) + sock.bind((host, 0)) + has_ipv6 = True + except Exception: + pass + + if sock: + sock.close() + return has_ipv6 + + +HAS_IPV6 = _has_ipv6("::1") diff --git a/venv/Lib/site-packages/urllib3/util/queue.py b/venv/Lib/site-packages/urllib3/util/queue.py new file mode 100644 index 00000000..d3d379a1 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/util/queue.py @@ -0,0 +1,21 @@ +import collections +from ..packages import six +from ..packages.six.moves import queue + +if six.PY2: + # Queue is imported for side effects on MS Windows. See issue #229. + import Queue as _unused_module_Queue # noqa: F401 + + +class LifoQueue(queue.Queue): + def _init(self, _): + self.queue = collections.deque() + + def _qsize(self, len=len): + return len(self.queue) + + def _put(self, item): + self.queue.append(item) + + def _get(self): + return self.queue.pop() diff --git a/venv/Lib/site-packages/urllib3/util/request.py b/venv/Lib/site-packages/urllib3/util/request.py new file mode 100644 index 00000000..3b7bb54d --- /dev/null +++ b/venv/Lib/site-packages/urllib3/util/request.py @@ -0,0 +1,135 @@ +from __future__ import absolute_import +from base64 import b64encode + +from ..packages.six import b, integer_types +from ..exceptions import UnrewindableBodyError + +ACCEPT_ENCODING = "gzip,deflate" +try: + import brotli as _unused_module_brotli # noqa: F401 +except ImportError: + pass +else: + ACCEPT_ENCODING += ",br" + +_FAILEDTELL = object() + + +def make_headers( + keep_alive=None, + accept_encoding=None, + user_agent=None, + basic_auth=None, + proxy_basic_auth=None, + disable_cache=None, +): + """ + Shortcuts for generating request headers. + + :param keep_alive: + If ``True``, adds 'connection: keep-alive' header. + + :param accept_encoding: + Can be a boolean, list, or string. + ``True`` translates to 'gzip,deflate'. + List will get joined by comma. + String will be used as provided. + + :param user_agent: + String representing the user-agent you want, such as + "python-urllib3/0.6" + + :param basic_auth: + Colon-separated username:password string for 'authorization: basic ...' + auth header. + + :param proxy_basic_auth: + Colon-separated username:password string for 'proxy-authorization: basic ...' + auth header. + + :param disable_cache: + If ``True``, adds 'cache-control: no-cache' header. + + Example:: + + >>> make_headers(keep_alive=True, user_agent="Batman/1.0") + {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'} + >>> make_headers(accept_encoding=True) + {'accept-encoding': 'gzip,deflate'} + """ + headers = {} + if accept_encoding: + if isinstance(accept_encoding, str): + pass + elif isinstance(accept_encoding, list): + accept_encoding = ",".join(accept_encoding) + else: + accept_encoding = ACCEPT_ENCODING + headers["accept-encoding"] = accept_encoding + + if user_agent: + headers["user-agent"] = user_agent + + if keep_alive: + headers["connection"] = "keep-alive" + + if basic_auth: + headers["authorization"] = "Basic " + b64encode(b(basic_auth)).decode("utf-8") + + if proxy_basic_auth: + headers["proxy-authorization"] = "Basic " + b64encode( + b(proxy_basic_auth) + ).decode("utf-8") + + if disable_cache: + headers["cache-control"] = "no-cache" + + return headers + + +def set_file_position(body, pos): + """ + If a position is provided, move file to that point. + Otherwise, we'll attempt to record a position for future use. + """ + if pos is not None: + rewind_body(body, pos) + elif getattr(body, "tell", None) is not None: + try: + pos = body.tell() + except (IOError, OSError): + # This differentiates from None, allowing us to catch + # a failed `tell()` later when trying to rewind the body. + pos = _FAILEDTELL + + return pos + + +def rewind_body(body, body_pos): + """ + Attempt to rewind body to a certain position. + Primarily used for request redirects and retries. + + :param body: + File-like object that supports seek. + + :param int pos: + Position to seek to in file. + """ + body_seek = getattr(body, "seek", None) + if body_seek is not None and isinstance(body_pos, integer_types): + try: + body_seek(body_pos) + except (IOError, OSError): + raise UnrewindableBodyError( + "An error occurred when rewinding request body for redirect/retry." + ) + elif body_pos is _FAILEDTELL: + raise UnrewindableBodyError( + "Unable to record file position for rewinding " + "request body during a redirect/retry." + ) + else: + raise ValueError( + "body_pos must be of type integer, instead it was %s." % type(body_pos) + ) diff --git a/venv/Lib/site-packages/urllib3/util/response.py b/venv/Lib/site-packages/urllib3/util/response.py new file mode 100644 index 00000000..715868dd --- /dev/null +++ b/venv/Lib/site-packages/urllib3/util/response.py @@ -0,0 +1,86 @@ +from __future__ import absolute_import +from ..packages.six.moves import http_client as httplib + +from ..exceptions import HeaderParsingError + + +def is_fp_closed(obj): + """ + Checks whether a given file-like object is closed. + + :param obj: + The file-like object to check. + """ + + try: + # Check `isclosed()` first, in case Python3 doesn't set `closed`. + # GH Issue #928 + return obj.isclosed() + except AttributeError: + pass + + try: + # Check via the official file-like-object way. + return obj.closed + except AttributeError: + pass + + try: + # Check if the object is a container for another file-like object that + # gets released on exhaustion (e.g. HTTPResponse). + return obj.fp is None + except AttributeError: + pass + + raise ValueError("Unable to determine whether fp is closed.") + + +def assert_header_parsing(headers): + """ + Asserts whether all headers have been successfully parsed. + Extracts encountered errors from the result of parsing headers. + + Only works on Python 3. + + :param headers: Headers to verify. + :type headers: `httplib.HTTPMessage`. + + :raises urllib3.exceptions.HeaderParsingError: + If parsing errors are found. + """ + + # This will fail silently if we pass in the wrong kind of parameter. + # To make debugging easier add an explicit check. + if not isinstance(headers, httplib.HTTPMessage): + raise TypeError("expected httplib.Message, got {0}.".format(type(headers))) + + defects = getattr(headers, "defects", None) + get_payload = getattr(headers, "get_payload", None) + + unparsed_data = None + if get_payload: + # get_payload is actually email.message.Message.get_payload; + # we're only interested in the result if it's not a multipart message + if not headers.is_multipart(): + payload = get_payload() + + if isinstance(payload, (bytes, str)): + unparsed_data = payload + + if defects or unparsed_data: + raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data) + + +def is_response_to_head(response): + """ + Checks whether the request of a response has been a HEAD-request. + Handles the quirks of AppEngine. + + :param conn: + :type conn: :class:`httplib.HTTPResponse` + """ + # FIXME: Can we do this somehow without accessing private httplib _method? + method = response._method + if isinstance(method, int): # Platform-specific: Appengine + return method == 3 + return method.upper() == "HEAD" diff --git a/venv/Lib/site-packages/urllib3/util/retry.py b/venv/Lib/site-packages/urllib3/util/retry.py new file mode 100644 index 00000000..ee30c91b --- /dev/null +++ b/venv/Lib/site-packages/urllib3/util/retry.py @@ -0,0 +1,453 @@ +from __future__ import absolute_import +import time +import logging +from collections import namedtuple +from itertools import takewhile +import email +import re + +from ..exceptions import ( + ConnectTimeoutError, + MaxRetryError, + ProtocolError, + ReadTimeoutError, + ResponseError, + InvalidHeader, + ProxyError, +) +from ..packages import six + + +log = logging.getLogger(__name__) + + +# Data structure for representing the metadata of requests that result in a retry. +RequestHistory = namedtuple( + "RequestHistory", ["method", "url", "error", "status", "redirect_location"] +) + + +class Retry(object): + """ Retry configuration. + + Each retry attempt will create a new Retry object with updated values, so + they can be safely reused. + + Retries can be defined as a default for a pool:: + + retries = Retry(connect=5, read=2, redirect=5) + http = PoolManager(retries=retries) + response = http.request('GET', 'http://example.com/') + + Or per-request (which overrides the default for the pool):: + + response = http.request('GET', 'http://example.com/', retries=Retry(10)) + + Retries can be disabled by passing ``False``:: + + response = http.request('GET', 'http://example.com/', retries=False) + + Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless + retries are disabled, in which case the causing exception will be raised. + + :param int total: + Total number of retries to allow. Takes precedence over other counts. + + Set to ``None`` to remove this constraint and fall back on other + counts. It's a good idea to set this to some sensibly-high value to + account for unexpected edge cases and avoid infinite retry loops. + + Set to ``0`` to fail on the first retry. + + Set to ``False`` to disable and imply ``raise_on_redirect=False``. + + :param int connect: + How many connection-related errors to retry on. + + These are errors raised before the request is sent to the remote server, + which we assume has not triggered the server to process the request. + + Set to ``0`` to fail on the first retry of this type. + + :param int read: + How many times to retry on read errors. + + These errors are raised after the request was sent to the server, so the + request may have side-effects. + + Set to ``0`` to fail on the first retry of this type. + + :param int redirect: + How many redirects to perform. Limit this to avoid infinite redirect + loops. + + A redirect is a HTTP response with a status code 301, 302, 303, 307 or + 308. + + Set to ``0`` to fail on the first retry of this type. + + Set to ``False`` to disable and imply ``raise_on_redirect=False``. + + :param int status: + How many times to retry on bad status codes. + + These are retries made on responses, where status code matches + ``status_forcelist``. + + Set to ``0`` to fail on the first retry of this type. + + :param iterable method_whitelist: + Set of uppercased HTTP method verbs that we should retry on. + + By default, we only retry on methods which are considered to be + idempotent (multiple requests with the same parameters end with the + same state). See :attr:`Retry.DEFAULT_METHOD_WHITELIST`. + + Set to a ``False`` value to retry on any verb. + + :param iterable status_forcelist: + A set of integer HTTP status codes that we should force a retry on. + A retry is initiated if the request method is in ``method_whitelist`` + and the response status code is in ``status_forcelist``. + + By default, this is disabled with ``None``. + + :param float backoff_factor: + A backoff factor to apply between attempts after the second try + (most errors are resolved immediately by a second try without a + delay). urllib3 will sleep for:: + + {backoff factor} * (2 ** ({number of total retries} - 1)) + + seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep + for [0.0s, 0.2s, 0.4s, ...] between retries. It will never be longer + than :attr:`Retry.BACKOFF_MAX`. + + By default, backoff is disabled (set to 0). + + :param bool raise_on_redirect: Whether, if the number of redirects is + exhausted, to raise a MaxRetryError, or to return a response with a + response code in the 3xx range. + + :param bool raise_on_status: Similar meaning to ``raise_on_redirect``: + whether we should raise an exception, or return a response, + if status falls in ``status_forcelist`` range and retries have + been exhausted. + + :param tuple history: The history of the request encountered during + each call to :meth:`~Retry.increment`. The list is in the order + the requests occurred. Each list item is of class :class:`RequestHistory`. + + :param bool respect_retry_after_header: + Whether to respect Retry-After header on status codes defined as + :attr:`Retry.RETRY_AFTER_STATUS_CODES` or not. + + :param iterable remove_headers_on_redirect: + Sequence of headers to remove from the request when a response + indicating a redirect is returned before firing off the redirected + request. + """ + + DEFAULT_METHOD_WHITELIST = frozenset( + ["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE"] + ) + + RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503]) + + DEFAULT_REDIRECT_HEADERS_BLACKLIST = frozenset(["Authorization"]) + + #: Maximum backoff time. + BACKOFF_MAX = 120 + + def __init__( + self, + total=10, + connect=None, + read=None, + redirect=None, + status=None, + method_whitelist=DEFAULT_METHOD_WHITELIST, + status_forcelist=None, + backoff_factor=0, + raise_on_redirect=True, + raise_on_status=True, + history=None, + respect_retry_after_header=True, + remove_headers_on_redirect=DEFAULT_REDIRECT_HEADERS_BLACKLIST, + ): + + self.total = total + self.connect = connect + self.read = read + self.status = status + + if redirect is False or total is False: + redirect = 0 + raise_on_redirect = False + + self.redirect = redirect + self.status_forcelist = status_forcelist or set() + self.method_whitelist = method_whitelist + self.backoff_factor = backoff_factor + self.raise_on_redirect = raise_on_redirect + self.raise_on_status = raise_on_status + self.history = history or tuple() + self.respect_retry_after_header = respect_retry_after_header + self.remove_headers_on_redirect = frozenset( + [h.lower() for h in remove_headers_on_redirect] + ) + + def new(self, **kw): + params = dict( + total=self.total, + connect=self.connect, + read=self.read, + redirect=self.redirect, + status=self.status, + method_whitelist=self.method_whitelist, + status_forcelist=self.status_forcelist, + backoff_factor=self.backoff_factor, + raise_on_redirect=self.raise_on_redirect, + raise_on_status=self.raise_on_status, + history=self.history, + remove_headers_on_redirect=self.remove_headers_on_redirect, + respect_retry_after_header=self.respect_retry_after_header, + ) + params.update(kw) + return type(self)(**params) + + @classmethod + def from_int(cls, retries, redirect=True, default=None): + """ Backwards-compatibility for the old retries format.""" + if retries is None: + retries = default if default is not None else cls.DEFAULT + + if isinstance(retries, Retry): + return retries + + redirect = bool(redirect) and None + new_retries = cls(retries, redirect=redirect) + log.debug("Converted retries value: %r -> %r", retries, new_retries) + return new_retries + + def get_backoff_time(self): + """ Formula for computing the current backoff + + :rtype: float + """ + # We want to consider only the last consecutive errors sequence (Ignore redirects). + consecutive_errors_len = len( + list( + takewhile(lambda x: x.redirect_location is None, reversed(self.history)) + ) + ) + if consecutive_errors_len <= 1: + return 0 + + backoff_value = self.backoff_factor * (2 ** (consecutive_errors_len - 1)) + return min(self.BACKOFF_MAX, backoff_value) + + def parse_retry_after(self, retry_after): + # Whitespace: https://tools.ietf.org/html/rfc7230#section-3.2.4 + if re.match(r"^\s*[0-9]+\s*$", retry_after): + seconds = int(retry_after) + else: + retry_date_tuple = email.utils.parsedate(retry_after) + if retry_date_tuple is None: + raise InvalidHeader("Invalid Retry-After header: %s" % retry_after) + retry_date = time.mktime(retry_date_tuple) + seconds = retry_date - time.time() + + if seconds < 0: + seconds = 0 + + return seconds + + def get_retry_after(self, response): + """ Get the value of Retry-After in seconds. """ + + retry_after = response.getheader("Retry-After") + + if retry_after is None: + return None + + return self.parse_retry_after(retry_after) + + def sleep_for_retry(self, response=None): + retry_after = self.get_retry_after(response) + if retry_after: + time.sleep(retry_after) + return True + + return False + + def _sleep_backoff(self): + backoff = self.get_backoff_time() + if backoff <= 0: + return + time.sleep(backoff) + + def sleep(self, response=None): + """ Sleep between retry attempts. + + This method will respect a server's ``Retry-After`` response header + and sleep the duration of the time requested. If that is not present, it + will use an exponential backoff. By default, the backoff factor is 0 and + this method will return immediately. + """ + + if self.respect_retry_after_header and response: + slept = self.sleep_for_retry(response) + if slept: + return + + self._sleep_backoff() + + def _is_connection_error(self, err): + """ Errors when we're fairly sure that the server did not receive the + request, so it should be safe to retry. + """ + if isinstance(err, ProxyError): + err = err.original_error + return isinstance(err, ConnectTimeoutError) + + def _is_read_error(self, err): + """ Errors that occur after the request has been started, so we should + assume that the server began processing it. + """ + return isinstance(err, (ReadTimeoutError, ProtocolError)) + + def _is_method_retryable(self, method): + """ Checks if a given HTTP method should be retried upon, depending if + it is included on the method whitelist. + """ + if self.method_whitelist and method.upper() not in self.method_whitelist: + return False + + return True + + def is_retry(self, method, status_code, has_retry_after=False): + """ Is this method/status code retryable? (Based on whitelists and control + variables such as the number of total retries to allow, whether to + respect the Retry-After header, whether this header is present, and + whether the returned status code is on the list of status codes to + be retried upon on the presence of the aforementioned header) + """ + if not self._is_method_retryable(method): + return False + + if self.status_forcelist and status_code in self.status_forcelist: + return True + + return ( + self.total + and self.respect_retry_after_header + and has_retry_after + and (status_code in self.RETRY_AFTER_STATUS_CODES) + ) + + def is_exhausted(self): + """ Are we out of retries? """ + retry_counts = (self.total, self.connect, self.read, self.redirect, self.status) + retry_counts = list(filter(None, retry_counts)) + if not retry_counts: + return False + + return min(retry_counts) < 0 + + def increment( + self, + method=None, + url=None, + response=None, + error=None, + _pool=None, + _stacktrace=None, + ): + """ Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.HTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise six.reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + connect = self.connect + read = self.read + redirect = self.redirect + status_count = self.status + cause = "unknown" + status = None + redirect_location = None + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise six.reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False or not self._is_method_retryable(method): + raise six.reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = "too many redirects" + redirect_location = response.get_redirect_location() + status = response.status + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and a the given method is in the whitelist + cause = ResponseError.GENERIC_ERROR + if response and response.status: + if status_count is not None: + status_count -= 1 + cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) + status = response.status + + history = self.history + ( + RequestHistory(method, url, error, status, redirect_location), + ) + + new_retry = self.new( + total=total, + connect=connect, + read=read, + redirect=redirect, + status=status_count, + history=history, + ) + + if new_retry.is_exhausted(): + raise MaxRetryError(_pool, url, error or ResponseError(cause)) + + log.debug("Incremented Retry for (url='%s'): %r", url, new_retry) + + return new_retry + + def __repr__(self): + return ( + "{cls.__name__}(total={self.total}, connect={self.connect}, " + "read={self.read}, redirect={self.redirect}, status={self.status})" + ).format(cls=type(self), self=self) + + +# For backwards compatibility (equivalent to pre-v1.9): +Retry.DEFAULT = Retry(3) diff --git a/venv/Lib/site-packages/urllib3/util/ssl_.py b/venv/Lib/site-packages/urllib3/util/ssl_.py new file mode 100644 index 00000000..f7e2b705 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/util/ssl_.py @@ -0,0 +1,414 @@ +from __future__ import absolute_import +import errno +import warnings +import hmac +import sys + +from binascii import hexlify, unhexlify +from hashlib import md5, sha1, sha256 + +from .url import IPV4_RE, BRACELESS_IPV6_ADDRZ_RE +from ..exceptions import SSLError, InsecurePlatformWarning, SNIMissingWarning +from ..packages import six + + +SSLContext = None +HAS_SNI = False +IS_PYOPENSSL = False +IS_SECURETRANSPORT = False + +# Maps the length of a digest to a possible hash function producing this digest +HASHFUNC_MAP = {32: md5, 40: sha1, 64: sha256} + + +def _const_compare_digest_backport(a, b): + """ + Compare two digests of equal length in constant time. + + The digests must be of type str/bytes. + Returns True if the digests match, and False otherwise. + """ + result = abs(len(a) - len(b)) + for l, r in zip(bytearray(a), bytearray(b)): + result |= l ^ r + return result == 0 + + +_const_compare_digest = getattr(hmac, "compare_digest", _const_compare_digest_backport) + +try: # Test for SSL features + import ssl + from ssl import wrap_socket, CERT_REQUIRED + from ssl import HAS_SNI # Has SNI? +except ImportError: + pass + +try: # Platform-specific: Python 3.6 + from ssl import PROTOCOL_TLS + + PROTOCOL_SSLv23 = PROTOCOL_TLS +except ImportError: + try: + from ssl import PROTOCOL_SSLv23 as PROTOCOL_TLS + + PROTOCOL_SSLv23 = PROTOCOL_TLS + except ImportError: + PROTOCOL_SSLv23 = PROTOCOL_TLS = 2 + + +try: + from ssl import OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION +except ImportError: + OP_NO_SSLv2, OP_NO_SSLv3 = 0x1000000, 0x2000000 + OP_NO_COMPRESSION = 0x20000 + + +# A secure default. +# Sources for more information on TLS ciphers: +# +# - https://wiki.mozilla.org/Security/Server_Side_TLS +# - https://www.ssllabs.com/projects/best-practices/index.html +# - https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ +# +# The general intent is: +# - prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE), +# - prefer ECDHE over DHE for better performance, +# - prefer any AES-GCM and ChaCha20 over any AES-CBC for better performance and +# security, +# - prefer AES-GCM over ChaCha20 because hardware-accelerated AES is common, +# - disable NULL authentication, MD5 MACs, DSS, and other +# insecure ciphers for security reasons. +# - NOTE: TLS 1.3 cipher suites are managed through a different interface +# not exposed by CPython (yet!) and are enabled by default if they're available. +DEFAULT_CIPHERS = ":".join( + [ + "ECDHE+AESGCM", + "ECDHE+CHACHA20", + "DHE+AESGCM", + "DHE+CHACHA20", + "ECDH+AESGCM", + "DH+AESGCM", + "ECDH+AES", + "DH+AES", + "RSA+AESGCM", + "RSA+AES", + "!aNULL", + "!eNULL", + "!MD5", + "!DSS", + ] +) + +try: + from ssl import SSLContext # Modern SSL? +except ImportError: + + class SSLContext(object): # Platform-specific: Python 2 + def __init__(self, protocol_version): + self.protocol = protocol_version + # Use default values from a real SSLContext + self.check_hostname = False + self.verify_mode = ssl.CERT_NONE + self.ca_certs = None + self.options = 0 + self.certfile = None + self.keyfile = None + self.ciphers = None + + def load_cert_chain(self, certfile, keyfile): + self.certfile = certfile + self.keyfile = keyfile + + def load_verify_locations(self, cafile=None, capath=None, cadata=None): + self.ca_certs = cafile + + if capath is not None: + raise SSLError("CA directories not supported in older Pythons") + + if cadata is not None: + raise SSLError("CA data not supported in older Pythons") + + def set_ciphers(self, cipher_suite): + self.ciphers = cipher_suite + + def wrap_socket(self, socket, server_hostname=None, server_side=False): + warnings.warn( + "A true SSLContext object is not available. This prevents " + "urllib3 from configuring SSL appropriately and may cause " + "certain SSL connections to fail. You can upgrade to a newer " + "version of Python to solve this. For more information, see " + "https://urllib3.readthedocs.io/en/latest/advanced-usage.html" + "#ssl-warnings", + InsecurePlatformWarning, + ) + kwargs = { + "keyfile": self.keyfile, + "certfile": self.certfile, + "ca_certs": self.ca_certs, + "cert_reqs": self.verify_mode, + "ssl_version": self.protocol, + "server_side": server_side, + } + return wrap_socket(socket, ciphers=self.ciphers, **kwargs) + + +def assert_fingerprint(cert, fingerprint): + """ + Checks if given fingerprint matches the supplied certificate. + + :param cert: + Certificate as bytes object. + :param fingerprint: + Fingerprint as string of hexdigits, can be interspersed by colons. + """ + + fingerprint = fingerprint.replace(":", "").lower() + digest_length = len(fingerprint) + hashfunc = HASHFUNC_MAP.get(digest_length) + if not hashfunc: + raise SSLError("Fingerprint of invalid length: {0}".format(fingerprint)) + + # We need encode() here for py32; works on py2 and p33. + fingerprint_bytes = unhexlify(fingerprint.encode()) + + cert_digest = hashfunc(cert).digest() + + if not _const_compare_digest(cert_digest, fingerprint_bytes): + raise SSLError( + 'Fingerprints did not match. Expected "{0}", got "{1}".'.format( + fingerprint, hexlify(cert_digest) + ) + ) + + +def resolve_cert_reqs(candidate): + """ + Resolves the argument to a numeric constant, which can be passed to + the wrap_socket function/method from the ssl module. + Defaults to :data:`ssl.CERT_REQUIRED`. + If given a string it is assumed to be the name of the constant in the + :mod:`ssl` module or its abbreviation. + (So you can specify `REQUIRED` instead of `CERT_REQUIRED`. + If it's neither `None` nor a string we assume it is already the numeric + constant which can directly be passed to wrap_socket. + """ + if candidate is None: + return CERT_REQUIRED + + if isinstance(candidate, str): + res = getattr(ssl, candidate, None) + if res is None: + res = getattr(ssl, "CERT_" + candidate) + return res + + return candidate + + +def resolve_ssl_version(candidate): + """ + like resolve_cert_reqs + """ + if candidate is None: + return PROTOCOL_TLS + + if isinstance(candidate, str): + res = getattr(ssl, candidate, None) + if res is None: + res = getattr(ssl, "PROTOCOL_" + candidate) + return res + + return candidate + + +def create_urllib3_context( + ssl_version=None, cert_reqs=None, options=None, ciphers=None +): + """All arguments have the same meaning as ``ssl_wrap_socket``. + + By default, this function does a lot of the same work that + ``ssl.create_default_context`` does on Python 3.4+. It: + + - Disables SSLv2, SSLv3, and compression + - Sets a restricted set of server ciphers + + If you wish to enable SSLv3, you can do:: + + from urllib3.util import ssl_ + context = ssl_.create_urllib3_context() + context.options &= ~ssl_.OP_NO_SSLv3 + + You can do the same to enable compression (substituting ``COMPRESSION`` + for ``SSLv3`` in the last line above). + + :param ssl_version: + The desired protocol version to use. This will default to + PROTOCOL_SSLv23 which will negotiate the highest protocol that both + the server and your installation of OpenSSL support. + :param cert_reqs: + Whether to require the certificate verification. This defaults to + ``ssl.CERT_REQUIRED``. + :param options: + Specific OpenSSL options. These default to ``ssl.OP_NO_SSLv2``, + ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``. + :param ciphers: + Which cipher suites to allow the server to select. + :returns: + Constructed SSLContext object with specified options + :rtype: SSLContext + """ + context = SSLContext(ssl_version or PROTOCOL_TLS) + + context.set_ciphers(ciphers or DEFAULT_CIPHERS) + + # Setting the default here, as we may have no ssl module on import + cert_reqs = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs + + if options is None: + options = 0 + # SSLv2 is easily broken and is considered harmful and dangerous + options |= OP_NO_SSLv2 + # SSLv3 has several problems and is now dangerous + options |= OP_NO_SSLv3 + # Disable compression to prevent CRIME attacks for OpenSSL 1.0+ + # (issue #309) + options |= OP_NO_COMPRESSION + + context.options |= options + + # Enable post-handshake authentication for TLS 1.3, see GH #1634. PHA is + # necessary for conditional client cert authentication with TLS 1.3. + # The attribute is None for OpenSSL <= 1.1.0 or does not exist in older + # versions of Python. We only enable on Python 3.7.4+ or if certificate + # verification is enabled to work around Python issue #37428 + # See: https://bugs.python.org/issue37428 + if (cert_reqs == ssl.CERT_REQUIRED or sys.version_info >= (3, 7, 4)) and getattr( + context, "post_handshake_auth", None + ) is not None: + context.post_handshake_auth = True + + context.verify_mode = cert_reqs + if ( + getattr(context, "check_hostname", None) is not None + ): # Platform-specific: Python 3.2 + # We do our own verification, including fingerprints and alternative + # hostnames. So disable it here + context.check_hostname = False + return context + + +def ssl_wrap_socket( + sock, + keyfile=None, + certfile=None, + cert_reqs=None, + ca_certs=None, + server_hostname=None, + ssl_version=None, + ciphers=None, + ssl_context=None, + ca_cert_dir=None, + key_password=None, + ca_cert_data=None, +): + """ + All arguments except for server_hostname, ssl_context, and ca_cert_dir have + the same meaning as they do when using :func:`ssl.wrap_socket`. + + :param server_hostname: + When SNI is supported, the expected hostname of the certificate + :param ssl_context: + A pre-made :class:`SSLContext` object. If none is provided, one will + be created using :func:`create_urllib3_context`. + :param ciphers: + A string of ciphers we wish the client to support. + :param ca_cert_dir: + A directory containing CA certificates in multiple separate files, as + supported by OpenSSL's -CApath flag or the capath argument to + SSLContext.load_verify_locations(). + :param key_password: + Optional password if the keyfile is encrypted. + :param ca_cert_data: + Optional string containing CA certificates in PEM format suitable for + passing as the cadata parameter to SSLContext.load_verify_locations() + """ + context = ssl_context + if context is None: + # Note: This branch of code and all the variables in it are no longer + # used by urllib3 itself. We should consider deprecating and removing + # this code. + context = create_urllib3_context(ssl_version, cert_reqs, ciphers=ciphers) + + if ca_certs or ca_cert_dir or ca_cert_data: + try: + context.load_verify_locations(ca_certs, ca_cert_dir, ca_cert_data) + except IOError as e: # Platform-specific: Python 2.7 + raise SSLError(e) + # Py33 raises FileNotFoundError which subclasses OSError + # These are not equivalent unless we check the errno attribute + except OSError as e: # Platform-specific: Python 3.3 and beyond + if e.errno == errno.ENOENT: + raise SSLError(e) + raise + + elif ssl_context is None and hasattr(context, "load_default_certs"): + # try to load OS default certs; works well on Windows (require Python3.4+) + context.load_default_certs() + + # Attempt to detect if we get the goofy behavior of the + # keyfile being encrypted and OpenSSL asking for the + # passphrase via the terminal and instead error out. + if keyfile and key_password is None and _is_key_file_encrypted(keyfile): + raise SSLError("Client private key is encrypted, password is required") + + if certfile: + if key_password is None: + context.load_cert_chain(certfile, keyfile) + else: + context.load_cert_chain(certfile, keyfile, key_password) + + # If we detect server_hostname is an IP address then the SNI + # extension should not be used according to RFC3546 Section 3.1 + # We shouldn't warn the user if SNI isn't available but we would + # not be using SNI anyways due to IP address for server_hostname. + if ( + server_hostname is not None and not is_ipaddress(server_hostname) + ) or IS_SECURETRANSPORT: + if HAS_SNI and server_hostname is not None: + return context.wrap_socket(sock, server_hostname=server_hostname) + + warnings.warn( + "An HTTPS request has been made, but the SNI (Server Name " + "Indication) extension to TLS is not available on this platform. " + "This may cause the server to present an incorrect TLS " + "certificate, which can cause validation failures. You can upgrade to " + "a newer version of Python to solve this. For more information, see " + "https://urllib3.readthedocs.io/en/latest/advanced-usage.html" + "#ssl-warnings", + SNIMissingWarning, + ) + + return context.wrap_socket(sock) + + +def is_ipaddress(hostname): + """Detects whether the hostname given is an IPv4 or IPv6 address. + Also detects IPv6 addresses with Zone IDs. + + :param str hostname: Hostname to examine. + :return: True if the hostname is an IP address, False otherwise. + """ + if not six.PY2 and isinstance(hostname, bytes): + # IDN A-label bytes are ASCII compatible. + hostname = hostname.decode("ascii") + return bool(IPV4_RE.match(hostname) or BRACELESS_IPV6_ADDRZ_RE.match(hostname)) + + +def _is_key_file_encrypted(key_file): + """Detects if a key file is encrypted or not.""" + with open(key_file, "r") as f: + for line in f: + # Look for Proc-Type: 4,ENCRYPTED + if "ENCRYPTED" in line: + return True + + return False diff --git a/venv/Lib/site-packages/urllib3/util/timeout.py b/venv/Lib/site-packages/urllib3/util/timeout.py new file mode 100644 index 00000000..b61fea75 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/util/timeout.py @@ -0,0 +1,261 @@ +from __future__ import absolute_import + +# The default socket timeout, used by httplib to indicate that no timeout was +# specified by the user +from socket import _GLOBAL_DEFAULT_TIMEOUT +import time + +from ..exceptions import TimeoutStateError + +# A sentinel value to indicate that no timeout was specified by the user in +# urllib3 +_Default = object() + + +# Use time.monotonic if available. +current_time = getattr(time, "monotonic", time.time) + + +class Timeout(object): + """ Timeout configuration. + + Timeouts can be defined as a default for a pool:: + + timeout = Timeout(connect=2.0, read=7.0) + http = PoolManager(timeout=timeout) + response = http.request('GET', 'http://example.com/') + + Or per-request (which overrides the default for the pool):: + + response = http.request('GET', 'http://example.com/', timeout=Timeout(10)) + + Timeouts can be disabled by setting all the parameters to ``None``:: + + no_timeout = Timeout(connect=None, read=None) + response = http.request('GET', 'http://example.com/, timeout=no_timeout) + + + :param total: + This combines the connect and read timeouts into one; the read timeout + will be set to the time leftover from the connect attempt. In the + event that both a connect timeout and a total are specified, or a read + timeout and a total are specified, the shorter timeout will be applied. + + Defaults to None. + + :type total: integer, float, or None + + :param connect: + The maximum amount of time (in seconds) to wait for a connection + attempt to a server to succeed. Omitting the parameter will default the + connect timeout to the system default, probably `the global default + timeout in socket.py + `_. + None will set an infinite timeout for connection attempts. + + :type connect: integer, float, or None + + :param read: + The maximum amount of time (in seconds) to wait between consecutive + read operations for a response from the server. Omitting the parameter + will default the read timeout to the system default, probably `the + global default timeout in socket.py + `_. + None will set an infinite timeout. + + :type read: integer, float, or None + + .. note:: + + Many factors can affect the total amount of time for urllib3 to return + an HTTP response. + + For example, Python's DNS resolver does not obey the timeout specified + on the socket. Other factors that can affect total request time include + high CPU load, high swap, the program running at a low priority level, + or other behaviors. + + In addition, the read and total timeouts only measure the time between + read operations on the socket connecting the client and the server, + not the total amount of time for the request to return a complete + response. For most requests, the timeout is raised because the server + has not sent the first byte in the specified time. This is not always + the case; if a server streams one byte every fifteen seconds, a timeout + of 20 seconds will not trigger, even though the request will take + several minutes to complete. + + If your goal is to cut off any request after a set amount of wall clock + time, consider having a second "watcher" thread to cut off a slow + request. + """ + + #: A sentinel object representing the default timeout value + DEFAULT_TIMEOUT = _GLOBAL_DEFAULT_TIMEOUT + + def __init__(self, total=None, connect=_Default, read=_Default): + self._connect = self._validate_timeout(connect, "connect") + self._read = self._validate_timeout(read, "read") + self.total = self._validate_timeout(total, "total") + self._start_connect = None + + def __repr__(self): + return "%s(connect=%r, read=%r, total=%r)" % ( + type(self).__name__, + self._connect, + self._read, + self.total, + ) + + # __str__ provided for backwards compatibility + __str__ = __repr__ + + @classmethod + def _validate_timeout(cls, value, name): + """ Check that a timeout attribute is valid. + + :param value: The timeout value to validate + :param name: The name of the timeout attribute to validate. This is + used to specify in error messages. + :return: The validated and casted version of the given value. + :raises ValueError: If it is a numeric value less than or equal to + zero, or the type is not an integer, float, or None. + """ + if value is _Default: + return cls.DEFAULT_TIMEOUT + + if value is None or value is cls.DEFAULT_TIMEOUT: + return value + + if isinstance(value, bool): + raise ValueError( + "Timeout cannot be a boolean value. It must " + "be an int, float or None." + ) + try: + float(value) + except (TypeError, ValueError): + raise ValueError( + "Timeout value %s was %s, but it must be an " + "int, float or None." % (name, value) + ) + + try: + if value <= 0: + raise ValueError( + "Attempted to set %s timeout to %s, but the " + "timeout cannot be set to a value less " + "than or equal to 0." % (name, value) + ) + except TypeError: + # Python 3 + raise ValueError( + "Timeout value %s was %s, but it must be an " + "int, float or None." % (name, value) + ) + + return value + + @classmethod + def from_float(cls, timeout): + """ Create a new Timeout from a legacy timeout value. + + The timeout value used by httplib.py sets the same timeout on the + connect(), and recv() socket requests. This creates a :class:`Timeout` + object that sets the individual timeouts to the ``timeout`` value + passed to this function. + + :param timeout: The legacy timeout value. + :type timeout: integer, float, sentinel default object, or None + :return: Timeout object + :rtype: :class:`Timeout` + """ + return Timeout(read=timeout, connect=timeout) + + def clone(self): + """ Create a copy of the timeout object + + Timeout properties are stored per-pool but each request needs a fresh + Timeout object to ensure each one has its own start/stop configured. + + :return: a copy of the timeout object + :rtype: :class:`Timeout` + """ + # We can't use copy.deepcopy because that will also create a new object + # for _GLOBAL_DEFAULT_TIMEOUT, which socket.py uses as a sentinel to + # detect the user default. + return Timeout(connect=self._connect, read=self._read, total=self.total) + + def start_connect(self): + """ Start the timeout clock, used during a connect() attempt + + :raises urllib3.exceptions.TimeoutStateError: if you attempt + to start a timer that has been started already. + """ + if self._start_connect is not None: + raise TimeoutStateError("Timeout timer has already been started.") + self._start_connect = current_time() + return self._start_connect + + def get_connect_duration(self): + """ Gets the time elapsed since the call to :meth:`start_connect`. + + :return: Elapsed time in seconds. + :rtype: float + :raises urllib3.exceptions.TimeoutStateError: if you attempt + to get duration for a timer that hasn't been started. + """ + if self._start_connect is None: + raise TimeoutStateError( + "Can't get connect duration for timer that has not started." + ) + return current_time() - self._start_connect + + @property + def connect_timeout(self): + """ Get the value to use when setting a connection timeout. + + This will be a positive float or integer, the value None + (never timeout), or the default system timeout. + + :return: Connect timeout. + :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None + """ + if self.total is None: + return self._connect + + if self._connect is None or self._connect is self.DEFAULT_TIMEOUT: + return self.total + + return min(self._connect, self.total) + + @property + def read_timeout(self): + """ Get the value for the read timeout. + + This assumes some time has elapsed in the connection timeout and + computes the read timeout appropriately. + + If self.total is set, the read timeout is dependent on the amount of + time taken by the connect timeout. If the connection time has not been + established, a :exc:`~urllib3.exceptions.TimeoutStateError` will be + raised. + + :return: Value to use for the read timeout. + :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None + :raises urllib3.exceptions.TimeoutStateError: If :meth:`start_connect` + has not yet been called on this object. + """ + if ( + self.total is not None + and self.total is not self.DEFAULT_TIMEOUT + and self._read is not None + and self._read is not self.DEFAULT_TIMEOUT + ): + # In case the connect timeout has not yet been established. + if self._start_connect is None: + return self._read + return max(0, min(self.total - self.get_connect_duration(), self._read)) + elif self.total is not None and self.total is not self.DEFAULT_TIMEOUT: + return max(0, self.total - self.get_connect_duration()) + else: + return self._read diff --git a/venv/Lib/site-packages/urllib3/util/url.py b/venv/Lib/site-packages/urllib3/util/url.py new file mode 100644 index 00000000..793324e5 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/util/url.py @@ -0,0 +1,430 @@ +from __future__ import absolute_import +import re +from collections import namedtuple + +from ..exceptions import LocationParseError +from ..packages import six + + +url_attrs = ["scheme", "auth", "host", "port", "path", "query", "fragment"] + +# We only want to normalize urls with an HTTP(S) scheme. +# urllib3 infers URLs without a scheme (None) to be http. +NORMALIZABLE_SCHEMES = ("http", "https", None) + +# Almost all of these patterns were derived from the +# 'rfc3986' module: https://github.com/python-hyper/rfc3986 +PERCENT_RE = re.compile(r"%[a-fA-F0-9]{2}") +SCHEME_RE = re.compile(r"^(?:[a-zA-Z][a-zA-Z0-9+-]*:|/)") +URI_RE = re.compile( + r"^(?:([a-zA-Z][a-zA-Z0-9+.-]*):)?" + r"(?://([^\\/?#]*))?" + r"([^?#]*)" + r"(?:\?([^#]*))?" + r"(?:#(.*))?$", + re.UNICODE | re.DOTALL, +) + +IPV4_PAT = r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}" +HEX_PAT = "[0-9A-Fa-f]{1,4}" +LS32_PAT = "(?:{hex}:{hex}|{ipv4})".format(hex=HEX_PAT, ipv4=IPV4_PAT) +_subs = {"hex": HEX_PAT, "ls32": LS32_PAT} +_variations = [ + # 6( h16 ":" ) ls32 + "(?:%(hex)s:){6}%(ls32)s", + # "::" 5( h16 ":" ) ls32 + "::(?:%(hex)s:){5}%(ls32)s", + # [ h16 ] "::" 4( h16 ":" ) ls32 + "(?:%(hex)s)?::(?:%(hex)s:){4}%(ls32)s", + # [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + "(?:(?:%(hex)s:)?%(hex)s)?::(?:%(hex)s:){3}%(ls32)s", + # [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + "(?:(?:%(hex)s:){0,2}%(hex)s)?::(?:%(hex)s:){2}%(ls32)s", + # [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + "(?:(?:%(hex)s:){0,3}%(hex)s)?::%(hex)s:%(ls32)s", + # [ *4( h16 ":" ) h16 ] "::" ls32 + "(?:(?:%(hex)s:){0,4}%(hex)s)?::%(ls32)s", + # [ *5( h16 ":" ) h16 ] "::" h16 + "(?:(?:%(hex)s:){0,5}%(hex)s)?::%(hex)s", + # [ *6( h16 ":" ) h16 ] "::" + "(?:(?:%(hex)s:){0,6}%(hex)s)?::", +] + +UNRESERVED_PAT = r"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._!\-~" +IPV6_PAT = "(?:" + "|".join([x % _subs for x in _variations]) + ")" +ZONE_ID_PAT = "(?:%25|%)(?:[" + UNRESERVED_PAT + "]|%[a-fA-F0-9]{2})+" +IPV6_ADDRZ_PAT = r"\[" + IPV6_PAT + r"(?:" + ZONE_ID_PAT + r")?\]" +REG_NAME_PAT = r"(?:[^\[\]%:/?#]|%[a-fA-F0-9]{2})*" +TARGET_RE = re.compile(r"^(/[^?#]*)(?:\?([^#]*))?(?:#.*)?$") + +IPV4_RE = re.compile("^" + IPV4_PAT + "$") +IPV6_RE = re.compile("^" + IPV6_PAT + "$") +IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT + "$") +BRACELESS_IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT[2:-2] + "$") +ZONE_ID_RE = re.compile("(" + ZONE_ID_PAT + r")\]$") + +SUBAUTHORITY_PAT = (u"^(?:(.*)@)?(%s|%s|%s)(?::([0-9]{0,5}))?$") % ( + REG_NAME_PAT, + IPV4_PAT, + IPV6_ADDRZ_PAT, +) +SUBAUTHORITY_RE = re.compile(SUBAUTHORITY_PAT, re.UNICODE | re.DOTALL) + +UNRESERVED_CHARS = set( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-~" +) +SUB_DELIM_CHARS = set("!$&'()*+,;=") +USERINFO_CHARS = UNRESERVED_CHARS | SUB_DELIM_CHARS | {":"} +PATH_CHARS = USERINFO_CHARS | {"@", "/"} +QUERY_CHARS = FRAGMENT_CHARS = PATH_CHARS | {"?"} + + +class Url(namedtuple("Url", url_attrs)): + """ + Data structure for representing an HTTP URL. Used as a return value for + :func:`parse_url`. Both the scheme and host are normalized as they are + both case-insensitive according to RFC 3986. + """ + + __slots__ = () + + def __new__( + cls, + scheme=None, + auth=None, + host=None, + port=None, + path=None, + query=None, + fragment=None, + ): + if path and not path.startswith("/"): + path = "/" + path + if scheme is not None: + scheme = scheme.lower() + return super(Url, cls).__new__( + cls, scheme, auth, host, port, path, query, fragment + ) + + @property + def hostname(self): + """For backwards-compatibility with urlparse. We're nice like that.""" + return self.host + + @property + def request_uri(self): + """Absolute path including the query string.""" + uri = self.path or "/" + + if self.query is not None: + uri += "?" + self.query + + return uri + + @property + def netloc(self): + """Network location including host and port""" + if self.port: + return "%s:%d" % (self.host, self.port) + return self.host + + @property + def url(self): + """ + Convert self into a url + + This function should more or less round-trip with :func:`.parse_url`. The + returned url may not be exactly the same as the url inputted to + :func:`.parse_url`, but it should be equivalent by the RFC (e.g., urls + with a blank port will have : removed). + + Example: :: + + >>> U = parse_url('http://google.com/mail/') + >>> U.url + 'http://google.com/mail/' + >>> Url('http', 'username:password', 'host.com', 80, + ... '/path', 'query', 'fragment').url + 'http://username:password@host.com:80/path?query#fragment' + """ + scheme, auth, host, port, path, query, fragment = self + url = u"" + + # We use "is not None" we want things to happen with empty strings (or 0 port) + if scheme is not None: + url += scheme + u"://" + if auth is not None: + url += auth + u"@" + if host is not None: + url += host + if port is not None: + url += u":" + str(port) + if path is not None: + url += path + if query is not None: + url += u"?" + query + if fragment is not None: + url += u"#" + fragment + + return url + + def __str__(self): + return self.url + + +def split_first(s, delims): + """ + .. deprecated:: 1.25 + + Given a string and an iterable of delimiters, split on the first found + delimiter. Return two split parts and the matched delimiter. + + If not found, then the first part is the full input string. + + Example:: + + >>> split_first('foo/bar?baz', '?/=') + ('foo', 'bar?baz', '/') + >>> split_first('foo/bar?baz', '123') + ('foo/bar?baz', '', None) + + Scales linearly with number of delims. Not ideal for large number of delims. + """ + min_idx = None + min_delim = None + for d in delims: + idx = s.find(d) + if idx < 0: + continue + + if min_idx is None or idx < min_idx: + min_idx = idx + min_delim = d + + if min_idx is None or min_idx < 0: + return s, "", None + + return s[:min_idx], s[min_idx + 1 :], min_delim + + +def _encode_invalid_chars(component, allowed_chars, encoding="utf-8"): + """Percent-encodes a URI component without reapplying + onto an already percent-encoded component. + """ + if component is None: + return component + + component = six.ensure_text(component) + + # Normalize existing percent-encoded bytes. + # Try to see if the component we're encoding is already percent-encoded + # so we can skip all '%' characters but still encode all others. + component, percent_encodings = PERCENT_RE.subn( + lambda match: match.group(0).upper(), component + ) + + uri_bytes = component.encode("utf-8", "surrogatepass") + is_percent_encoded = percent_encodings == uri_bytes.count(b"%") + encoded_component = bytearray() + + for i in range(0, len(uri_bytes)): + # Will return a single character bytestring on both Python 2 & 3 + byte = uri_bytes[i : i + 1] + byte_ord = ord(byte) + if (is_percent_encoded and byte == b"%") or ( + byte_ord < 128 and byte.decode() in allowed_chars + ): + encoded_component += byte + continue + encoded_component.extend(b"%" + (hex(byte_ord)[2:].encode().zfill(2).upper())) + + return encoded_component.decode(encoding) + + +def _remove_path_dot_segments(path): + # See http://tools.ietf.org/html/rfc3986#section-5.2.4 for pseudo-code + segments = path.split("/") # Turn the path into a list of segments + output = [] # Initialize the variable to use to store output + + for segment in segments: + # '.' is the current directory, so ignore it, it is superfluous + if segment == ".": + continue + # Anything other than '..', should be appended to the output + elif segment != "..": + output.append(segment) + # In this case segment == '..', if we can, we should pop the last + # element + elif output: + output.pop() + + # If the path starts with '/' and the output is empty or the first string + # is non-empty + if path.startswith("/") and (not output or output[0]): + output.insert(0, "") + + # If the path starts with '/.' or '/..' ensure we add one more empty + # string to add a trailing '/' + if path.endswith(("/.", "/..")): + output.append("") + + return "/".join(output) + + +def _normalize_host(host, scheme): + if host: + if isinstance(host, six.binary_type): + host = six.ensure_str(host) + + if scheme in NORMALIZABLE_SCHEMES: + is_ipv6 = IPV6_ADDRZ_RE.match(host) + if is_ipv6: + match = ZONE_ID_RE.search(host) + if match: + start, end = match.span(1) + zone_id = host[start:end] + + if zone_id.startswith("%25") and zone_id != "%25": + zone_id = zone_id[3:] + else: + zone_id = zone_id[1:] + zone_id = "%" + _encode_invalid_chars(zone_id, UNRESERVED_CHARS) + return host[:start].lower() + zone_id + host[end:] + else: + return host.lower() + elif not IPV4_RE.match(host): + return six.ensure_str( + b".".join([_idna_encode(label) for label in host.split(".")]) + ) + return host + + +def _idna_encode(name): + if name and any([ord(x) > 128 for x in name]): + try: + import idna + except ImportError: + six.raise_from( + LocationParseError("Unable to parse URL without the 'idna' module"), + None, + ) + try: + return idna.encode(name.lower(), strict=True, std3_rules=True) + except idna.IDNAError: + six.raise_from( + LocationParseError(u"Name '%s' is not a valid IDNA label" % name), None + ) + return name.lower().encode("ascii") + + +def _encode_target(target): + """Percent-encodes a request target so that there are no invalid characters""" + path, query = TARGET_RE.match(target).groups() + target = _encode_invalid_chars(path, PATH_CHARS) + query = _encode_invalid_chars(query, QUERY_CHARS) + if query is not None: + target += "?" + query + return target + + +def parse_url(url): + """ + Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is + performed to parse incomplete urls. Fields not provided will be None. + This parser is RFC 3986 compliant. + + The parser logic and helper functions are based heavily on + work done in the ``rfc3986`` module. + + :param str url: URL to parse into a :class:`.Url` namedtuple. + + Partly backwards-compatible with :mod:`urlparse`. + + Example:: + + >>> parse_url('http://google.com/mail/') + Url(scheme='http', host='google.com', port=None, path='/mail/', ...) + >>> parse_url('google.com:80') + Url(scheme=None, host='google.com', port=80, path=None, ...) + >>> parse_url('/foo?bar') + Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...) + """ + if not url: + # Empty + return Url() + + source_url = url + if not SCHEME_RE.search(url): + url = "//" + url + + try: + scheme, authority, path, query, fragment = URI_RE.match(url).groups() + normalize_uri = scheme is None or scheme.lower() in NORMALIZABLE_SCHEMES + + if scheme: + scheme = scheme.lower() + + if authority: + auth, host, port = SUBAUTHORITY_RE.match(authority).groups() + if auth and normalize_uri: + auth = _encode_invalid_chars(auth, USERINFO_CHARS) + if port == "": + port = None + else: + auth, host, port = None, None, None + + if port is not None: + port = int(port) + if not (0 <= port <= 65535): + raise LocationParseError(url) + + host = _normalize_host(host, scheme) + + if normalize_uri and path: + path = _remove_path_dot_segments(path) + path = _encode_invalid_chars(path, PATH_CHARS) + if normalize_uri and query: + query = _encode_invalid_chars(query, QUERY_CHARS) + if normalize_uri and fragment: + fragment = _encode_invalid_chars(fragment, FRAGMENT_CHARS) + + except (ValueError, AttributeError): + return six.raise_from(LocationParseError(source_url), None) + + # For the sake of backwards compatibility we put empty + # string values for path if there are any defined values + # beyond the path in the URL. + # TODO: Remove this when we break backwards compatibility. + if not path: + if query is not None or fragment is not None: + path = "" + else: + path = None + + # Ensure that each part of the URL is a `str` for + # backwards compatibility. + if isinstance(url, six.text_type): + ensure_func = six.ensure_text + else: + ensure_func = six.ensure_str + + def ensure_type(x): + return x if x is None else ensure_func(x) + + return Url( + scheme=ensure_type(scheme), + auth=ensure_type(auth), + host=ensure_type(host), + port=port, + path=ensure_type(path), + query=ensure_type(query), + fragment=ensure_type(fragment), + ) + + +def get_host(url): + """ + Deprecated. Use :func:`parse_url` instead. + """ + p = parse_url(url) + return p.scheme or "http", p.hostname, p.port diff --git a/venv/Lib/site-packages/urllib3/util/wait.py b/venv/Lib/site-packages/urllib3/util/wait.py new file mode 100644 index 00000000..d71d2fd7 --- /dev/null +++ b/venv/Lib/site-packages/urllib3/util/wait.py @@ -0,0 +1,153 @@ +import errno +from functools import partial +import select +import sys + +try: + from time import monotonic +except ImportError: + from time import time as monotonic + +__all__ = ["NoWayToWaitForSocketError", "wait_for_read", "wait_for_write"] + + +class NoWayToWaitForSocketError(Exception): + pass + + +# How should we wait on sockets? +# +# There are two types of APIs you can use for waiting on sockets: the fancy +# modern stateful APIs like epoll/kqueue, and the older stateless APIs like +# select/poll. The stateful APIs are more efficient when you have a lots of +# sockets to keep track of, because you can set them up once and then use them +# lots of times. But we only ever want to wait on a single socket at a time +# and don't want to keep track of state, so the stateless APIs are actually +# more efficient. So we want to use select() or poll(). +# +# Now, how do we choose between select() and poll()? On traditional Unixes, +# select() has a strange calling convention that makes it slow, or fail +# altogether, for high-numbered file descriptors. The point of poll() is to fix +# that, so on Unixes, we prefer poll(). +# +# On Windows, there is no poll() (or at least Python doesn't provide a wrapper +# for it), but that's OK, because on Windows, select() doesn't have this +# strange calling convention; plain select() works fine. +# +# So: on Windows we use select(), and everywhere else we use poll(). We also +# fall back to select() in case poll() is somehow broken or missing. + +if sys.version_info >= (3, 5): + # Modern Python, that retries syscalls by default + def _retry_on_intr(fn, timeout): + return fn(timeout) + + +else: + # Old and broken Pythons. + def _retry_on_intr(fn, timeout): + if timeout is None: + deadline = float("inf") + else: + deadline = monotonic() + timeout + + while True: + try: + return fn(timeout) + # OSError for 3 <= pyver < 3.5, select.error for pyver <= 2.7 + except (OSError, select.error) as e: + # 'e.args[0]' incantation works for both OSError and select.error + if e.args[0] != errno.EINTR: + raise + else: + timeout = deadline - monotonic() + if timeout < 0: + timeout = 0 + if timeout == float("inf"): + timeout = None + continue + + +def select_wait_for_socket(sock, read=False, write=False, timeout=None): + if not read and not write: + raise RuntimeError("must specify at least one of read=True, write=True") + rcheck = [] + wcheck = [] + if read: + rcheck.append(sock) + if write: + wcheck.append(sock) + # When doing a non-blocking connect, most systems signal success by + # marking the socket writable. Windows, though, signals success by marked + # it as "exceptional". We paper over the difference by checking the write + # sockets for both conditions. (The stdlib selectors module does the same + # thing.) + fn = partial(select.select, rcheck, wcheck, wcheck) + rready, wready, xready = _retry_on_intr(fn, timeout) + return bool(rready or wready or xready) + + +def poll_wait_for_socket(sock, read=False, write=False, timeout=None): + if not read and not write: + raise RuntimeError("must specify at least one of read=True, write=True") + mask = 0 + if read: + mask |= select.POLLIN + if write: + mask |= select.POLLOUT + poll_obj = select.poll() + poll_obj.register(sock, mask) + + # For some reason, poll() takes timeout in milliseconds + def do_poll(t): + if t is not None: + t *= 1000 + return poll_obj.poll(t) + + return bool(_retry_on_intr(do_poll, timeout)) + + +def null_wait_for_socket(*args, **kwargs): + raise NoWayToWaitForSocketError("no select-equivalent available") + + +def _have_working_poll(): + # Apparently some systems have a select.poll that fails as soon as you try + # to use it, either due to strange configuration or broken monkeypatching + # from libraries like eventlet/greenlet. + try: + poll_obj = select.poll() + _retry_on_intr(poll_obj.poll, 0) + except (AttributeError, OSError): + return False + else: + return True + + +def wait_for_socket(*args, **kwargs): + # We delay choosing which implementation to use until the first time we're + # called. We could do it at import time, but then we might make the wrong + # decision if someone goes wild with monkeypatching select.poll after + # we're imported. + global wait_for_socket + if _have_working_poll(): + wait_for_socket = poll_wait_for_socket + elif hasattr(select, "select"): + wait_for_socket = select_wait_for_socket + else: # Platform-specific: Appengine. + wait_for_socket = null_wait_for_socket + return wait_for_socket(*args, **kwargs) + + +def wait_for_read(sock, timeout=None): + """ Waits for reading to be available on a given socket. + Returns True if the socket is readable, or False if the timeout expired. + """ + return wait_for_socket(sock, read=True, timeout=timeout) + + +def wait_for_write(sock, timeout=None): + """ Waits for writing to be available on a given socket. + Returns True if the socket is readable, or False if the timeout expired. + """ + return wait_for_socket(sock, write=True, timeout=timeout) diff --git a/venv/Scripts/dotenv.exe b/venv/Scripts/dotenv.exe new file mode 100644 index 0000000000000000000000000000000000000000..991fdd6bf6faa9f591e9fcc26d4252f78ff3f948 GIT binary patch literal 106359 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