+ {% endblock %}
+
+Philosophy
+----------
+
+Application logic is for the controller but don't try to make the life
+for the template designer too hard by giving him too few functionality.
+
+For more informations visit the new `Jinja2 webpage`_ and `documentation`_.
+
+.. _sandboxed: https://en.wikipedia.org/wiki/Sandbox_(computer_security)
+.. _Django: https://www.djangoproject.com/
+.. _Jinja2 webpage: http://jinja.pocoo.org/
+.. _documentation: http://jinja.pocoo.org/2/documentation/
+
+
diff --git a/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/INSTALLER b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/LICENSE.txt b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/LICENSE.txt
new file mode 100644
index 0000000..10145a2
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/LICENSE.txt
@@ -0,0 +1,31 @@
+Copyright (c) 2009 by the Jinja Team, see AUTHORS for more details.
+
+Some rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * The names of the contributors may not be used to endorse or
+ promote products derived from this software without specific
+ prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/METADATA b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/METADATA
new file mode 100644
index 0000000..40f2b46
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/METADATA
@@ -0,0 +1,68 @@
+Metadata-Version: 2.0
+Name: Jinja2
+Version: 2.10
+Summary: A small but fast and easy to use stand-alone template engine written in pure python.
+Home-page: http://jinja.pocoo.org/
+Author: Armin Ronacher
+Author-email: armin.ronacher@active-4.com
+License: BSD
+Description-Content-Type: UNKNOWN
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Text Processing :: Markup :: HTML
+Requires-Dist: MarkupSafe (>=0.23)
+Provides-Extra: i18n
+Requires-Dist: Babel (>=0.8); extra == 'i18n'
+
+
+Jinja2
+~~~~~~
+
+Jinja2 is a template engine written in pure Python. It provides a
+`Django`_ inspired non-XML syntax but supports inline expressions and
+an optional `sandboxed`_ environment.
+
+Nutshell
+--------
+
+Here a small example of a Jinja template::
+
+ {% extends 'base.html' %}
+ {% block title %}Memberlist{% endblock %}
+ {% block content %}
+
+ {% endblock %}
+
+Philosophy
+----------
+
+Application logic is for the controller but don't try to make the life
+for the template designer too hard by giving him too few functionality.
+
+For more informations visit the new `Jinja2 webpage`_ and `documentation`_.
+
+.. _sandboxed: https://en.wikipedia.org/wiki/Sandbox_(computer_security)
+.. _Django: https://www.djangoproject.com/
+.. _Jinja2 webpage: http://jinja.pocoo.org/
+.. _documentation: http://jinja.pocoo.org/2/documentation/
+
+
diff --git a/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/RECORD b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/RECORD
new file mode 100644
index 0000000..25bfedf
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/RECORD
@@ -0,0 +1,61 @@
+Jinja2-2.10.dist-info/DESCRIPTION.rst,sha256=b5ckFDoM7vVtz_mAsJD4OPteFKCqE7beu353g4COoYI,978
+Jinja2-2.10.dist-info/LICENSE.txt,sha256=JvzUNv3Io51EiWrAPm8d_SXjhJnEjyDYvB3Tvwqqils,1554
+Jinja2-2.10.dist-info/METADATA,sha256=18EgU8zR6-av-0-5y_gXebzK4GnBB_76lALUsl-6QHM,2258
+Jinja2-2.10.dist-info/RECORD,,
+Jinja2-2.10.dist-info/WHEEL,sha256=kdsN-5OJAZIiHN-iO4Rhl82KyS0bDWf4uBwMbkNafr8,110
+Jinja2-2.10.dist-info/entry_points.txt,sha256=NdzVcOrqyNyKDxD09aERj__3bFx2paZhizFDsKmVhiA,72
+Jinja2-2.10.dist-info/metadata.json,sha256=NPUJ9TMBxVQAv_kTJzvU8HwmP-4XZvbK9mz6_4YUVl4,1473
+Jinja2-2.10.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7
+jinja2/__init__.py,sha256=xJHjaMoy51_KXn1wf0cysH6tUUifUxZCwSOfcJGEYZw,2614
+jinja2/_compat.py,sha256=xP60CE5Qr8FTYcDE1f54tbZLKGvMwYml4-8T7Q4KG9k,2596
+jinja2/_identifier.py,sha256=W1QBSY-iJsyt6oR_nKSuNNCzV95vLIOYgUNPUI1d5gU,1726
+jinja2/asyncfilters.py,sha256=cTDPvrS8Hp_IkwsZ1m9af_lr5nHysw7uTa5gV0NmZVE,4144
+jinja2/asyncsupport.py,sha256=UErQ3YlTLaSjFb94P4MVn08-aVD9jJxty2JVfMRb-1M,7878
+jinja2/bccache.py,sha256=nQldx0ZRYANMyfvOihRoYFKSlUdd5vJkS7BjxNwlOZM,12794
+jinja2/compiler.py,sha256=BqC5U6JxObSRhblyT_a6Tp5GtEU5z3US1a4jLQaxxgo,65386
+jinja2/constants.py,sha256=uwwV8ZUhHhacAuz5PTwckfsbqBaqM7aKfyJL7kGX5YQ,1626
+jinja2/debug.py,sha256=WTVeUFGUa4v6ReCsYv-iVPa3pkNB75OinJt3PfxNdXs,12045
+jinja2/defaults.py,sha256=Em-95hmsJxIenDCZFB1YSvf9CNhe9rBmytN3yUrBcWA,1400
+jinja2/environment.py,sha256=VnkAkqw8JbjZct4tAyHlpBrka2vqB-Z58RAP-32P1ZY,50849
+jinja2/exceptions.py,sha256=_Rj-NVi98Q6AiEjYQOsP8dEIdu5AlmRHzcSNOPdWix4,4428
+jinja2/ext.py,sha256=atMQydEC86tN1zUsdQiHw5L5cF62nDbqGue25Yiu3N4,24500
+jinja2/filters.py,sha256=yOAJk0MsH-_gEC0i0U6NweVQhbtYaC-uE8xswHFLF4w,36528
+jinja2/idtracking.py,sha256=2GbDSzIvGArEBGLkovLkqEfmYxmWsEf8c3QZwM4uNsw,9197
+jinja2/lexer.py,sha256=ySEPoXd1g7wRjsuw23uimS6nkGN5aqrYwcOKxCaVMBQ,28559
+jinja2/loaders.py,sha256=xiTuURKAEObyym0nU8PCIXu_Qp8fn0AJ5oIADUUm-5Q,17382
+jinja2/meta.py,sha256=fmKHxkmZYAOm9QyWWy8EMd6eefAIh234rkBMW2X4ZR8,4340
+jinja2/nativetypes.py,sha256=_sJhS8f-8Q0QMIC0dm1YEdLyxEyoO-kch8qOL5xUDfE,7308
+jinja2/nodes.py,sha256=L10L_nQDfubLhO3XjpF9qz46FSh2clL-3e49ogVlMmA,30853
+jinja2/optimizer.py,sha256=MsdlFACJ0FRdPtjmCAdt7JQ9SGrXFaDNUaslsWQaG3M,1722
+jinja2/parser.py,sha256=lPzTEbcpTRBLw8ii6OYyExHeAhaZLMA05Hpv4ll3ULk,35875
+jinja2/runtime.py,sha256=DHdD38Pq8gj7uWQC5usJyWFoNWL317A9AvXOW_CLB34,27755
+jinja2/sandbox.py,sha256=TVyZHlNqqTzsv9fv2NvJNmSdWRHTguhyMHdxjWms32U,16708
+jinja2/tests.py,sha256=iJQLwbapZr-EKquTG_fVOVdwHUUKf3SX9eNkjQDF8oU,4237
+jinja2/utils.py,sha256=q24VupGZotQ-uOyrJxCaXtDWhZC1RgsQG7kcdmjck2Q,20629
+jinja2/visitor.py,sha256=JD1H1cANA29JcntFfN5fPyqQxB4bI4wC00BzZa-XHks,3316
+Jinja2-2.10.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+jinja2/__pycache__/debug.cpython-35.pyc,,
+jinja2/__pycache__/parser.cpython-35.pyc,,
+jinja2/__pycache__/visitor.cpython-35.pyc,,
+jinja2/__pycache__/__init__.cpython-35.pyc,,
+jinja2/__pycache__/nodes.cpython-35.pyc,,
+jinja2/__pycache__/defaults.cpython-35.pyc,,
+jinja2/__pycache__/ext.cpython-35.pyc,,
+jinja2/__pycache__/environment.cpython-35.pyc,,
+jinja2/__pycache__/constants.cpython-35.pyc,,
+jinja2/__pycache__/bccache.cpython-35.pyc,,
+jinja2/__pycache__/optimizer.cpython-35.pyc,,
+jinja2/__pycache__/utils.cpython-35.pyc,,
+jinja2/__pycache__/meta.cpython-35.pyc,,
+jinja2/__pycache__/runtime.cpython-35.pyc,,
+jinja2/__pycache__/lexer.cpython-35.pyc,,
+jinja2/__pycache__/_compat.cpython-35.pyc,,
+jinja2/__pycache__/_identifier.cpython-35.pyc,,
+jinja2/__pycache__/exceptions.cpython-35.pyc,,
+jinja2/__pycache__/filters.cpython-35.pyc,,
+jinja2/__pycache__/sandbox.cpython-35.pyc,,
+jinja2/__pycache__/tests.cpython-35.pyc,,
+jinja2/__pycache__/nativetypes.cpython-35.pyc,,
+jinja2/__pycache__/compiler.cpython-35.pyc,,
+jinja2/__pycache__/loaders.cpython-35.pyc,,
+jinja2/__pycache__/idtracking.cpython-35.pyc,,
diff --git a/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/WHEEL b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/WHEEL
new file mode 100644
index 0000000..7332a41
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.30.0)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/entry_points.txt b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/entry_points.txt
new file mode 100644
index 0000000..32e6b75
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/entry_points.txt
@@ -0,0 +1,4 @@
+
+ [babel.extractors]
+ jinja2 = jinja2.ext:babel_extract[i18n]
+
\ No newline at end of file
diff --git a/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/metadata.json b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/metadata.json
new file mode 100644
index 0000000..7f5dc38
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/metadata.json
@@ -0,0 +1 @@
+{"classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Text Processing :: Markup :: HTML"], "description_content_type": "UNKNOWN", "extensions": {"python.details": {"contacts": [{"email": "armin.ronacher@active-4.com", "name": "Armin Ronacher", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "project_urls": {"Home": "http://jinja.pocoo.org/"}}, "python.exports": {"babel.extractors": {"jinja2": "jinja2.ext:babel_extract [i18n]"}}}, "extras": ["i18n"], "generator": "bdist_wheel (0.30.0)", "license": "BSD", "metadata_version": "2.0", "name": "Jinja2", "run_requires": [{"extra": "i18n", "requires": ["Babel (>=0.8)"]}, {"requires": ["MarkupSafe (>=0.23)"]}], "summary": "A small but fast and easy to use stand-alone template engine written in pure python.", "version": "2.10"}
\ No newline at end of file
diff --git a/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/top_level.txt b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/top_level.txt
new file mode 100644
index 0000000..7f7afbf
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Jinja2-2.10.dist-info/top_level.txt
@@ -0,0 +1 @@
+jinja2
diff --git a/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/DESCRIPTION.rst b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/DESCRIPTION.rst
new file mode 100644
index 0000000..d1c108c
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/DESCRIPTION.rst
@@ -0,0 +1,20 @@
+This is a Python implementation of John Gruber's Markdown_.
+It is almost completely compliant with the reference implementation,
+though there are a few known issues. See Features_ for information
+on what exactly is supported and what is not. Additional features are
+supported by the `Available Extensions`_.
+
+.. _Markdown: http://daringfireball.net/projects/markdown/
+.. _Features: https://Python-Markdown.github.io#features
+.. _`Available Extensions`: https://Python-Markdown.github.io/extensions/
+
+Support
+=======
+
+You may ask for help and discuss various other issues on the
+`mailing list`_ and report bugs on the `bug tracker`_.
+
+.. _`mailing list`: http://lists.sourceforge.net/lists/listinfo/python-markdown-discuss
+.. _`bug tracker`: http://github.com/Python-Markdown/markdown/issues
+
+
diff --git a/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/INSTALLER b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/LICENSE.txt b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/LICENSE.txt
new file mode 100644
index 0000000..2652d97
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/LICENSE.txt
@@ -0,0 +1,29 @@
+Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+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 Python Markdown Project 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 PYTHON MARKDOWN PROJECT ''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 ANY CONTRIBUTORS TO THE PYTHON MARKDOWN PROJECT
+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/python3.5/site-packages/Markdown-2.6.11.dist-info/METADATA b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/METADATA
new file mode 100644
index 0000000..dec49ea
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/METADATA
@@ -0,0 +1,50 @@
+Metadata-Version: 2.0
+Name: Markdown
+Version: 2.6.11
+Summary: Python implementation of Markdown.
+Home-page: https://Python-Markdown.github.io/
+Author: Waylan Limberg
+Author-email: waylan.limberg@icloud.com
+License: BSD License
+Download-URL: http://pypi.python.org/packages/source/M/Markdown/Markdown-2.6.11-py2.py3-none-any.whl
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Topic :: Communications :: Email :: Filters
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries
+Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
+Classifier: Topic :: Software Development :: Documentation
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Text Processing :: Filters
+Classifier: Topic :: Text Processing :: Markup :: HTML
+
+This is a Python implementation of John Gruber's Markdown_.
+It is almost completely compliant with the reference implementation,
+though there are a few known issues. See Features_ for information
+on what exactly is supported and what is not. Additional features are
+supported by the `Available Extensions`_.
+
+.. _Markdown: http://daringfireball.net/projects/markdown/
+.. _Features: https://Python-Markdown.github.io#features
+.. _`Available Extensions`: https://Python-Markdown.github.io/extensions/
+
+Support
+=======
+
+You may ask for help and discuss various other issues on the
+`mailing list`_ and report bugs on the `bug tracker`_.
+
+.. _`mailing list`: http://lists.sourceforge.net/lists/listinfo/python-markdown-discuss
+.. _`bug tracker`: http://github.com/Python-Markdown/markdown/issues
+
+
diff --git a/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/RECORD b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/RECORD
new file mode 100644
index 0000000..525d5ab
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/RECORD
@@ -0,0 +1,70 @@
+Markdown-2.6.11.dist-info/DESCRIPTION.rst,sha256=Fs7imsKo_Mg9Fs5AtPHcYMfdYNIm6uzNOIqv4x95z1E,793
+Markdown-2.6.11.dist-info/LICENSE.txt,sha256=bxGTy2NHGOZcOlN9biXr1hSCDsDvaTz8EiSBEmONZNo,1645
+Markdown-2.6.11.dist-info/METADATA,sha256=M4_a_oL_7MCANSzdNGrQQxCJ6pqgUl-N3J8VRl3s8sk,2169
+Markdown-2.6.11.dist-info/RECORD,,
+Markdown-2.6.11.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110
+Markdown-2.6.11.dist-info/entry_points.txt,sha256=reURU3Hu4vJ9giJCuffSWdo3RYQNZgdNvmLSF7YJaZs,55
+Markdown-2.6.11.dist-info/metadata.json,sha256=0qD-NBtHfbYd7wJwBd9NOLDZKli4yFAJjRL1U5ykcQc,1599
+Markdown-2.6.11.dist-info/top_level.txt,sha256=IAxs8x618RXoH1uCqeLLxXsDefJvE_mIibr_M4sOlyk,9
+markdown/__init__.py,sha256=Jc0hIRB5AyCHgnIxbWlQZOHd5FqVqVBzYRLayR8Xw8U,20802
+markdown/__main__.py,sha256=h_yJ6LZPQlmIqeyUA_FEUmdoTucwon3cb8Z-ac0a9cc,5137
+markdown/__version__.py,sha256=TVuNwV5KQepT9tjClt5DrZjbRNgFpXO-KLF_PkpAXXI,881
+markdown/blockparser.py,sha256=CM37G8uNufHkk-369PERAcJ4Bt2f9snW6F7nbHydaAA,3517
+markdown/blockprocessors.py,sha256=mwwRigR8mv-ZJ3KXCpiOP4uXC_JW13o9CWWR6XABcIo,23009
+markdown/inlinepatterns.py,sha256=mONZ4tyANlU-5b8kEXjtj7INDKLrWy5U1lRFnAPYX-8,17173
+markdown/odict.py,sha256=UQ-4Ba0BrBe6Ro3Rpcf93y8ewPzLmEUvxYiWxowV7Yk,6044
+markdown/postprocessors.py,sha256=5DdsbNGyEW4-oT8q65sLfZ-plclF6sGOmM0t6HF6hZU,3615
+markdown/preprocessors.py,sha256=etlXNsmtkxZHBCQYspXi1kIMm0_rxY2Kmb-a2tHkHXw,14828
+markdown/serializers.py,sha256=NZN9fU_xE18BsWjTNO2S90GMT_DddyL4p9lk-v3D5Pc,9820
+markdown/treeprocessors.py,sha256=-HOy9VZXb1jQvH1GwvfeGo-sui6XWpwPa5qT_qgQ9zs,14571
+markdown/util.py,sha256=Y-_aOMxrA-w_TOkjba8TLZ9HD6_jf-xq89ecXTtHKcI,5652
+markdown/extensions/__init__.py,sha256=nusbN8FPR5qF3yBx30FHWhLvDKzS-7aGJ07-4BYQuyM,3850
+markdown/extensions/abbr.py,sha256=X4RyRSrtk7OoOZH_sGl9eB6uvjUhOO8lwcPTfHGw-Ww,2914
+markdown/extensions/admonition.py,sha256=ro5ADfKhFSlzdBcxMOPR0qQsbWfa6seZgqSh34FZOBs,3158
+markdown/extensions/attr_list.py,sha256=FRqd4umvnjjOd81FCsFitMX_cXpLtq0URhXbdaYik7g,6262
+markdown/extensions/codehilite.py,sha256=rJ3d3AHYof85x7spRaomzzJHj9kiTK-G4OgBupmSQKc,9764
+markdown/extensions/def_list.py,sha256=iwyQDKq8UUZ6AWr_Fe_1SmPxXPGXMYL69a8Ieya_Cfk,3745
+markdown/extensions/extra.py,sha256=FOHwsDEGcBns55B0A8kYpM4prIABT7ByAx5wLfh325g,5542
+markdown/extensions/fenced_code.py,sha256=wqx6rioNoqHVnVvmuvwDTBS40ZmRLZicIxQLgVxt6rI,4099
+markdown/extensions/footnotes.py,sha256=38uaJiwtXwMir4of7fU6NquSuJfipMyMgfhc594VkhM,15737
+markdown/extensions/headerid.py,sha256=xJXBYddToY32xd223kwLdQ5rmvaEV_NkFPAD0J1i6Ug,3316
+markdown/extensions/meta.py,sha256=Qn_hF-cTO2yzYl11zs9sES5Vr-tnCHcdiJR2T2C0d1w,2395
+markdown/extensions/nl2br.py,sha256=nnpEdfbqFJIc2jSXYSCdrGn-xWT45jYAQO1loDRubWg,854
+markdown/extensions/sane_lists.py,sha256=owrHr89ucGKdh1BxsF6aF-IX-OykjSY0yVThwsLe8ag,1597
+markdown/extensions/smart_strong.py,sha256=xMTpgBF8_PZpevjFb4_FQ_8wHTQ1ElndLyiy34GgK6s,1192
+markdown/extensions/smarty.py,sha256=0EjNrj5Js6-VP2FNEKfEmmxXDzpwo1pqiiptrnEFgps,10365
+markdown/extensions/tables.py,sha256=MBMII464hYnZG271cx9NBMLGJ81p2VVJtMMx9fFAW9w,7847
+markdown/extensions/toc.py,sha256=rcUQn6G2-kn64BdpqyxB2QQOgfIALaa4pEUAX1b5zWo,11041
+markdown/extensions/wikilinks.py,sha256=ifb8winLWgl4yruE-9_GdsM2-AJbsLytRzENOWtBZy8,2867
+../../../bin/markdown_py,sha256=s7yV4cowY1c7avleqOjlFfWPWhZZiqPD9KbDIKNs6vU,275
+Markdown-2.6.11.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+markdown/extensions/__pycache__/fenced_code.cpython-35.pyc,,
+markdown/__pycache__/blockparser.cpython-35.pyc,,
+markdown/extensions/__pycache__/smart_strong.cpython-35.pyc,,
+markdown/extensions/__pycache__/smarty.cpython-35.pyc,,
+markdown/extensions/__pycache__/codehilite.cpython-35.pyc,,
+markdown/extensions/__pycache__/toc.cpython-35.pyc,,
+markdown/__pycache__/__version__.cpython-35.pyc,,
+markdown/extensions/__pycache__/admonition.cpython-35.pyc,,
+markdown/__pycache__/odict.cpython-35.pyc,,
+markdown/extensions/__pycache__/__init__.cpython-35.pyc,,
+markdown/__pycache__/serializers.cpython-35.pyc,,
+markdown/__pycache__/util.cpython-35.pyc,,
+markdown/__pycache__/inlinepatterns.cpython-35.pyc,,
+markdown/extensions/__pycache__/headerid.cpython-35.pyc,,
+markdown/extensions/__pycache__/footnotes.cpython-35.pyc,,
+markdown/__pycache__/postprocessors.cpython-35.pyc,,
+markdown/__pycache__/__main__.cpython-35.pyc,,
+markdown/extensions/__pycache__/abbr.cpython-35.pyc,,
+markdown/extensions/__pycache__/sane_lists.cpython-35.pyc,,
+markdown/extensions/__pycache__/extra.cpython-35.pyc,,
+markdown/__pycache__/treeprocessors.cpython-35.pyc,,
+markdown/extensions/__pycache__/tables.cpython-35.pyc,,
+markdown/extensions/__pycache__/attr_list.cpython-35.pyc,,
+markdown/__pycache__/preprocessors.cpython-35.pyc,,
+markdown/extensions/__pycache__/wikilinks.cpython-35.pyc,,
+markdown/extensions/__pycache__/nl2br.cpython-35.pyc,,
+markdown/extensions/__pycache__/def_list.cpython-35.pyc,,
+markdown/__pycache__/__init__.cpython-35.pyc,,
+markdown/extensions/__pycache__/meta.cpython-35.pyc,,
+markdown/__pycache__/blockprocessors.cpython-35.pyc,,
diff --git a/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/WHEEL b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/WHEEL
new file mode 100644
index 0000000..8b6dd1b
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.29.0)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/entry_points.txt b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/entry_points.txt
new file mode 100644
index 0000000..007b80a
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/entry_points.txt
@@ -0,0 +1,3 @@
+[console_scripts]
+markdown_py = markdown.__main__:run
+
diff --git a/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/metadata.json b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/metadata.json
new file mode 100644
index 0000000..1b5eaf7
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/metadata.json
@@ -0,0 +1 @@
+{"classifiers": ["Development Status :: 5 - Production/Stable", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Communications :: Email :: Filters", "Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries", "Topic :: Internet :: WWW/HTTP :: Site Management", "Topic :: Software Development :: Documentation", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Text Processing :: Filters", "Topic :: Text Processing :: Markup :: HTML"], "download_url": "http://pypi.python.org/packages/source/M/Markdown/Markdown-2.6.11-py2.py3-none-any.whl", "extensions": {"python.commands": {"wrap_console": {"markdown_py": "markdown.__main__:run"}}, "python.details": {"contacts": [{"email": "waylan.limberg@icloud.com", "name": "Waylan Limberg", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "project_urls": {"Home": "https://Python-Markdown.github.io/"}}, "python.exports": {"console_scripts": {"markdown_py": "markdown.__main__:run"}}}, "generator": "bdist_wheel (0.29.0)", "license": "BSD License", "metadata_version": "2.0", "name": "Markdown", "summary": "Python implementation of Markdown.", "version": "2.6.11"}
\ No newline at end of file
diff --git a/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/top_level.txt b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/top_level.txt
new file mode 100644
index 0000000..0918c97
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Markdown-2.6.11.dist-info/top_level.txt
@@ -0,0 +1 @@
+markdown
diff --git a/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/DESCRIPTION.rst b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/DESCRIPTION.rst
new file mode 100644
index 0000000..495211b
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/DESCRIPTION.rst
@@ -0,0 +1,115 @@
+MarkupSafe
+==========
+
+Implements a unicode subclass that supports HTML strings:
+
+.. code-block:: python
+
+ >>> from markupsafe import Markup, escape
+ >>> escape("")
+ Markup(u'<script>alert(document.cookie);</script>')
+ >>> tmpl = Markup("%s")
+ >>> tmpl % "Peter > Lustig"
+ Markup(u'Peter > Lustig')
+
+If you want to make an object unicode that is not yet unicode
+but don't want to lose the taint information, you can use the
+``soft_unicode`` function. (On Python 3 you can also use ``soft_str`` which
+is a different name for the same function).
+
+.. code-block:: python
+
+ >>> from markupsafe import soft_unicode
+ >>> soft_unicode(42)
+ u'42'
+ >>> soft_unicode(Markup('foo'))
+ Markup(u'foo')
+
+HTML Representations
+--------------------
+
+Objects can customize their HTML markup equivalent by overriding
+the ``__html__`` function:
+
+.. code-block:: python
+
+ >>> class Foo(object):
+ ... def __html__(self):
+ ... return 'Nice'
+ ...
+ >>> escape(Foo())
+ Markup(u'Nice')
+ >>> Markup(Foo())
+ Markup(u'Nice')
+
+Silent Escapes
+--------------
+
+Since MarkupSafe 0.10 there is now also a separate escape function
+called ``escape_silent`` that returns an empty string for ``None`` for
+consistency with other systems that return empty strings for ``None``
+when escaping (for instance Pylons' webhelpers).
+
+If you also want to use this for the escape method of the Markup
+object, you can create your own subclass that does that:
+
+.. code-block:: python
+
+ from markupsafe import Markup, escape_silent as escape
+
+ class SilentMarkup(Markup):
+ __slots__ = ()
+
+ @classmethod
+ def escape(cls, s):
+ return cls(escape(s))
+
+New-Style String Formatting
+---------------------------
+
+Starting with MarkupSafe 0.21 new style string formats from Python 2.6 and
+3.x are now fully supported. Previously the escape behavior of those
+functions was spotty at best. The new implementations operates under the
+following algorithm:
+
+1. if an object has an ``__html_format__`` method it is called as
+ replacement for ``__format__`` with the format specifier. It either
+ has to return a string or markup object.
+2. if an object has an ``__html__`` method it is called.
+3. otherwise the default format system of Python kicks in and the result
+ is HTML escaped.
+
+Here is how you can implement your own formatting:
+
+.. code-block:: python
+
+ class User(object):
+
+ def __init__(self, id, username):
+ self.id = id
+ self.username = username
+
+ def __html_format__(self, format_spec):
+ if format_spec == 'link':
+ return Markup('{1}').format(
+ self.id,
+ self.__html__(),
+ )
+ elif format_spec:
+ raise ValueError('Invalid format spec')
+ return self.__html__()
+
+ def __html__(self):
+ return Markup('{0}').format(self.username)
+
+And to format that user:
+
+.. code-block:: python
+
+ >>> user = User(1, 'foo')
+ >>> Markup('
User: {0:link}').format(user)
+ Markup(u'
User: foo')
+
+Markupsafe supports Python 2.6, 2.7 and Python 3.3 and higher.
+
+
diff --git a/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/INSTALLER b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/LICENSE.txt b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/LICENSE.txt
new file mode 100644
index 0000000..5d26938
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/LICENSE.txt
@@ -0,0 +1,33 @@
+Copyright (c) 2010 by Armin Ronacher and contributors. See AUTHORS
+for more details.
+
+Some rights reserved.
+
+Redistribution and use in source and binary forms of the software as well
+as documentation, with or without modification, are permitted provided
+that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+* The names of the contributors may not be used to endorse or
+ promote products derived from this software without specific
+ prior written permission.
+
+THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 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 AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
diff --git a/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/METADATA b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/METADATA
new file mode 100644
index 0000000..725be08
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/METADATA
@@ -0,0 +1,135 @@
+Metadata-Version: 2.0
+Name: MarkupSafe
+Version: 1.0
+Summary: Implements a XML/HTML/XHTML Markup safe string for Python
+Home-page: http://github.com/pallets/markupsafe
+Author: Armin Ronacher
+Author-email: armin.ronacher@active-4.com
+License: BSD
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Text Processing :: Markup :: HTML
+
+MarkupSafe
+==========
+
+Implements a unicode subclass that supports HTML strings:
+
+.. code-block:: python
+
+ >>> from markupsafe import Markup, escape
+ >>> escape("")
+ Markup(u'<script>alert(document.cookie);</script>')
+ >>> tmpl = Markup("%s")
+ >>> tmpl % "Peter > Lustig"
+ Markup(u'Peter > Lustig')
+
+If you want to make an object unicode that is not yet unicode
+but don't want to lose the taint information, you can use the
+``soft_unicode`` function. (On Python 3 you can also use ``soft_str`` which
+is a different name for the same function).
+
+.. code-block:: python
+
+ >>> from markupsafe import soft_unicode
+ >>> soft_unicode(42)
+ u'42'
+ >>> soft_unicode(Markup('foo'))
+ Markup(u'foo')
+
+HTML Representations
+--------------------
+
+Objects can customize their HTML markup equivalent by overriding
+the ``__html__`` function:
+
+.. code-block:: python
+
+ >>> class Foo(object):
+ ... def __html__(self):
+ ... return 'Nice'
+ ...
+ >>> escape(Foo())
+ Markup(u'Nice')
+ >>> Markup(Foo())
+ Markup(u'Nice')
+
+Silent Escapes
+--------------
+
+Since MarkupSafe 0.10 there is now also a separate escape function
+called ``escape_silent`` that returns an empty string for ``None`` for
+consistency with other systems that return empty strings for ``None``
+when escaping (for instance Pylons' webhelpers).
+
+If you also want to use this for the escape method of the Markup
+object, you can create your own subclass that does that:
+
+.. code-block:: python
+
+ from markupsafe import Markup, escape_silent as escape
+
+ class SilentMarkup(Markup):
+ __slots__ = ()
+
+ @classmethod
+ def escape(cls, s):
+ return cls(escape(s))
+
+New-Style String Formatting
+---------------------------
+
+Starting with MarkupSafe 0.21 new style string formats from Python 2.6 and
+3.x are now fully supported. Previously the escape behavior of those
+functions was spotty at best. The new implementations operates under the
+following algorithm:
+
+1. if an object has an ``__html_format__`` method it is called as
+ replacement for ``__format__`` with the format specifier. It either
+ has to return a string or markup object.
+2. if an object has an ``__html__`` method it is called.
+3. otherwise the default format system of Python kicks in and the result
+ is HTML escaped.
+
+Here is how you can implement your own formatting:
+
+.. code-block:: python
+
+ class User(object):
+
+ def __init__(self, id, username):
+ self.id = id
+ self.username = username
+
+ def __html_format__(self, format_spec):
+ if format_spec == 'link':
+ return Markup('{1}').format(
+ self.id,
+ self.__html__(),
+ )
+ elif format_spec:
+ raise ValueError('Invalid format spec')
+ return self.__html__()
+
+ def __html__(self):
+ return Markup('{0}').format(self.username)
+
+And to format that user:
+
+.. code-block:: python
+
+ >>> user = User(1, 'foo')
+ >>> Markup('
User: {0:link}').format(user)
+ Markup(u'
User: foo')
+
+Markupsafe supports Python 2.6, 2.7 and Python 3.3 and higher.
+
+
diff --git a/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/RECORD b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/RECORD
new file mode 100644
index 0000000..6cdd32d
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/RECORD
@@ -0,0 +1,18 @@
+MarkupSafe-1.0.dist-info/DESCRIPTION.rst,sha256=3B3J0YLzzmJQVaWQ_XlVMhGeHA_DvBqysABvul_5fko,3397
+MarkupSafe-1.0.dist-info/LICENSE.txt,sha256=C76IIo_WPSDsCX9k5Y1aCkZRI64TkUChjUBsYLSIJLU,1582
+MarkupSafe-1.0.dist-info/METADATA,sha256=EUwvRzJbtRP3hBMc8Z2TDT44TBDeZdIurbGzIc7FOkg,4182
+MarkupSafe-1.0.dist-info/RECORD,,
+MarkupSafe-1.0.dist-info/WHEEL,sha256=W-4s40OoP4gdldcaQ9BX8jWsxh8jVrPzLp2xl2ekMtI,104
+MarkupSafe-1.0.dist-info/metadata.json,sha256=LPb3W7qq-SH_u1SFzjXQqT8sCGpE-b4NmYAxMcw91e8,924
+MarkupSafe-1.0.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11
+markupsafe/__init__.py,sha256=xtkRdxhzJzgp65wUo1D4DjnazxHU88pPldaAuDekBeY,10697
+markupsafe/_compat.py,sha256=r1HE0CpcAZeb-AiTV9wITR91PeLHn0CzZ_XHkYoozpI,565
+markupsafe/_constants.py,sha256=U_xybFQsyXKCgHSfranJnFzo-z9nn9fuBeSk243sE5Q,4795
+markupsafe/_native.py,sha256=E2Un1ysOf-w45d18YCj8UelT5UP7Vt__IuFPYJ7YRIs,1187
+markupsafe/_speedups.c,sha256=B6Mf6Fn33WqkagfwY7q5ZBSm_vJoHDYxDB0Jp_DP7Jw,5936
+markupsafe/_speedups.cpython-35m-x86_64-linux-gnu.so,sha256=Q7hbHeM525UzP3ykPeqKz4tNqq8UAJu8P_KwGR-tqw4,31088
+MarkupSafe-1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+markupsafe/__pycache__/_native.cpython-35.pyc,,
+markupsafe/__pycache__/__init__.cpython-35.pyc,,
+markupsafe/__pycache__/_constants.cpython-35.pyc,,
+markupsafe/__pycache__/_compat.cpython-35.pyc,,
diff --git a/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/WHEEL b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/WHEEL
new file mode 100644
index 0000000..8668ca7
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.30.0)
+Root-Is-Purelib: false
+Tag: cp35-cp35m-linux_x86_64
+
diff --git a/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/metadata.json b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/metadata.json
new file mode 100644
index 0000000..7c29531
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/metadata.json
@@ -0,0 +1 @@
+{"classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Text Processing :: Markup :: HTML"], "extensions": {"python.details": {"contacts": [{"email": "armin.ronacher@active-4.com", "name": "Armin Ronacher", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "project_urls": {"Home": "http://github.com/pallets/markupsafe"}}}, "generator": "bdist_wheel (0.30.0)", "license": "BSD", "metadata_version": "2.0", "name": "MarkupSafe", "summary": "Implements a XML/HTML/XHTML Markup safe string for Python", "version": "1.0"}
\ No newline at end of file
diff --git a/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/top_level.txt b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/top_level.txt
new file mode 100644
index 0000000..75bf729
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/MarkupSafe-1.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+markupsafe
diff --git a/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/DESCRIPTION.rst b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/DESCRIPTION.rst
new file mode 100644
index 0000000..d56da3f
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/DESCRIPTION.rst
@@ -0,0 +1,19 @@
+Pygments
+~~~~~~~~
+
+Pygments is a syntax highlighting package written in Python.
+
+It is a generic syntax highlighter suitable for use in code hosting, forums,
+wikis or other applications that need to prettify source code. Highlights
+are:
+
+* a wide range of over 300 languages and other text formats is supported
+* special attention is paid to details, increasing quality by a fair amount
+* support for new languages and formats are added easily
+* a number of output formats, presently HTML, LaTeX, RTF, SVG, all image formats that PIL supports and ANSI sequences
+* it is usable as a command-line tool and as a library
+
+:copyright: Copyright 2006-2017 by the Pygments team, see AUTHORS.
+:license: BSD, see LICENSE for details.
+
+
diff --git a/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/INSTALLER b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/METADATA b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/METADATA
new file mode 100644
index 0000000..d6d2b48
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/METADATA
@@ -0,0 +1,41 @@
+Metadata-Version: 2.0
+Name: Pygments
+Version: 2.2.0
+Summary: Pygments is a syntax highlighting package written in Python.
+Home-page: http://pygments.org/
+Author: Georg Brandl
+Author-email: georg@python.org
+License: BSD License
+Keywords: syntax highlighting
+Platform: any
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: End Users/Desktop
+Classifier: Intended Audience :: System Administrators
+Classifier: Development Status :: 6 - Mature
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Operating System :: OS Independent
+Classifier: Topic :: Text Processing :: Filters
+Classifier: Topic :: Utilities
+
+Pygments
+~~~~~~~~
+
+Pygments is a syntax highlighting package written in Python.
+
+It is a generic syntax highlighter suitable for use in code hosting, forums,
+wikis or other applications that need to prettify source code. Highlights
+are:
+
+* a wide range of over 300 languages and other text formats is supported
+* special attention is paid to details, increasing quality by a fair amount
+* support for new languages and formats are added easily
+* a number of output formats, presently HTML, LaTeX, RTF, SVG, all image formats that PIL supports and ANSI sequences
+* it is usable as a command-line tool and as a library
+
+:copyright: Copyright 2006-2017 by the Pygments team, see AUTHORS.
+:license: BSD, see LICENSE for details.
+
+
diff --git a/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/RECORD b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/RECORD
new file mode 100644
index 0000000..3637e12
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/RECORD
@@ -0,0 +1,413 @@
+Pygments-2.2.0.dist-info/DESCRIPTION.rst,sha256=11na64zA7NeImuRAqJouRdVNnXxqCYyzq0PihToBdLc,734
+Pygments-2.2.0.dist-info/METADATA,sha256=ZuPLO3-TIR-2phYVgLv73KpaUu96pQhdj51ijzGHBxg,1517
+Pygments-2.2.0.dist-info/RECORD,,
+Pygments-2.2.0.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110
+Pygments-2.2.0.dist-info/entry_points.txt,sha256=NXt9BRDRv6tAfDwqKM0bDHrrxaIt2f1nxH9CwjyjSKc,54
+Pygments-2.2.0.dist-info/metadata.json,sha256=bYgjnYsKXO6HnrRKNTg6vH7MnSvhQl37KxmzhcMNV3Q,1078
+Pygments-2.2.0.dist-info/top_level.txt,sha256=RjKKqrVIStoebLHdbs0yZ2Lk4rS7cxGguXsLCYvZ2Ak,9
+pygments/__init__.py,sha256=IkWLwTf8uusJPx4c3f3gpHwVJVi0UqeD6mo5wWHHdWs,3145
+pygments/cmdline.py,sha256=_UA78VWUx--QdlHM0OuTu0W3zjDPhoVz8ipmjomf2Ac,19326
+pygments/console.py,sha256=AgkV5lDp2ghozb_J8V_UPZ0uudTWTz3isSK9oUvo750,1809
+pygments/filter.py,sha256=leZuvZlYC79jmrF_7ovSAZL5xohIhj7gx59FQx_F6gw,2038
+pygments/formatter.py,sha256=RaPGA3oLywAbvSkzrj61U5Y8xzYai32bbajg8L3xreE,2948
+pygments/lexer.py,sha256=CrUoLYlQbxO5W0YLH_a1lI8Dkw2l4nRZ5ciy9-R-n0I,31054
+pygments/modeline.py,sha256=83v6_3Zo9RHEc0K8aepm1Sc8Iou7mRcwogl9MLUciiI,1010
+pygments/plugin.py,sha256=NFKzfoPXMA8x6yXqMVbM0k1YKHHeQIwnDEvVlxd-MEQ,1721
+pygments/regexopt.py,sha256=t8y9hRPYQKJysZQhWd_t15aqRAGJSk0ABynKyjmogIQ,3094
+pygments/scanner.py,sha256=Mf-ya9riFJ85N3AiGKvBUsAsjSEfTd0Bqw8KMxhj2ew,3123
+pygments/sphinxext.py,sha256=1nig8VbRlfIDZd1fhOKQmX2KcxyQoexwsOdX3DSGE1I,4655
+pygments/style.py,sha256=R-iSqMPqnMtciPTgPMvqNjUkWtg7CCQTpyyCBSAOv_g,4807
+pygments/token.py,sha256=qowb6oBuyzaRfTyUe7F-LeMcNwRNLEiwjZl9o3ggsxc,6167
+pygments/unistring.py,sha256=rkFlgo6QzdUFWQ-uiOaz6MFfQg1f0-hH_B5F3PFXwXA,51150
+pygments/util.py,sha256=xO8RGLd_-7DoXCL799lWYgyub4whP2pSmnIsx1-wMyA,11900
+pygments/filters/__init__.py,sha256=IlSO7Mm0meA1rfssNwR9u7626sscMnASJlZ94yEy9lA,11573
+pygments/formatters/__init__.py,sha256=g5hpFvO_jy4-OV-pJr0jRP-vktKi6pXJKZ1z_XDnLIE,5099
+pygments/formatters/_mapping.py,sha256=E2DsoS8TZDCv_jJ1mn48rb3Qs4RIr8SJwILEVhYpGk4,6214
+pygments/formatters/bbcode.py,sha256=LYa2w-Ep7475be0GSH4MbuYiwH3gWIqpG4dQFCZLbGk,3314
+pygments/formatters/html.py,sha256=gvCUGAsxerTWRKnXa3a5ezmJg70KLt7wDb57JGjZhug,31759
+pygments/formatters/img.py,sha256=yWhrS8vgisj9XGOWjUrwtd8VZNuAffnBF8IAWl6p44g,19780
+pygments/formatters/irc.py,sha256=ApSLVBfjK78gkzXnMC8UrgnBLQulOvb-fza2e-D_RCI,5775
+pygments/formatters/latex.py,sha256=izNn5NjCTgu0gq8cyvvwNlRwc52CyAwvprN6DwlbUvU,17758
+pygments/formatters/other.py,sha256=p14Jboe9qeJ4xy9Xcb4f90szuQvQ0RI0q9DctZZmXDY,5162
+pygments/formatters/rtf.py,sha256=8HK5CpvqLchEWlbYptQKQ2RxQmayNLYyPOovCYBw4l4,5049
+pygments/formatters/svg.py,sha256=FT-DWvkC7bJ34-szEmh14PoqyqQkUE2jDPPVwOelqYQ,5840
+pygments/formatters/terminal.py,sha256=KkBzLxYMDvfbJXOecTgM1rAIVkYWYkE1a9pNIqtT0AA,4919
+pygments/formatters/terminal256.py,sha256=UKFNPANdnt2eGvYw_fRkyW2gKKXVpL9A5jVgEpGxd3g,10776
+pygments/lexers/__init__.py,sha256=77oWKOFsKWByzG5aMNDsWBJl0lQYSNgIC8XUUV2pCW4,10906
+pygments/lexers/_asy_builtins.py,sha256=zph3J1PBBtWjrvc_hUrmY-vk2WkN4zwT3OICH6nnyN0,27321
+pygments/lexers/_cl_builtins.py,sha256=1KrMgXg50ZsI02_WlDFuHugJzA-1LygeeUmHGBHBIgw,14053
+pygments/lexers/_cocoa_builtins.py,sha256=8b6As01z9rEa14hp7DXSXKHQXnRVI13eWEZ5rcmn5Bk,39982
+pygments/lexers/_csound_builtins.py,sha256=JkAZhrG-lXndYAsxWU2VqPce7MfwDknl7u8cYi3tAjs,21643
+pygments/lexers/_lasso_builtins.py,sha256=5PNX3gtW3f0X8gzoZUR6JW3mYzck3RcO1J6XtkUhY6Q,134534
+pygments/lexers/_lua_builtins.py,sha256=q1bCBDWwe7g-FwonM_xrGibEGdEYJvcdWRVGpYgBPpo,8340
+pygments/lexers/_mapping.py,sha256=_P3TsdkuN1NptMfEQkplLCf5TklMRNDi6Rtl_oc4XR4,54715
+pygments/lexers/_mql_builtins.py,sha256=qa9s_tUqXl16pAKzRDtWsJKzDPoU-bGaGdgrmHx7XOk,24736
+pygments/lexers/_openedge_builtins.py,sha256=0ADwH8PqzC6rJ_SZ8b3j0TgNut1kvLZL8RxpbC3J_nk,48362
+pygments/lexers/_php_builtins.py,sha256=cpkFCfHjPG1zExNaMI1JQY1at54pf5RMu-yCBmtwl78,154368
+pygments/lexers/_postgres_builtins.py,sha256=me5pfqtfUMpXYSvuTYXscyLCYUY9TcmBX8pGwimHG1U,11210
+pygments/lexers/_scilab_builtins.py,sha256=yXpa-XsMiNRgsPpDENWvZFZZJndUGFgv8uioKcfnejI,52405
+pygments/lexers/_sourcemod_builtins.py,sha256=DPFvfIi76GawQ603QG_EAFzl_6mmK5xZxfGWl4qlKFM,27113
+pygments/lexers/_stan_builtins.py,sha256=y6S6OymabXXmgrF3tMVkTxMHzkdyOi-Q_s0M2qdh-Xk,10121
+pygments/lexers/_stata_builtins.py,sha256=Hods-_4P3Ixov_hf9z6oeq-J_W_a8XOo8p--bECWJKo,25139
+pygments/lexers/_tsql_builtins.py,sha256=opXLcAQOoAgcizztFXepEHBABOrGjSvDWU3TxVvJS3M,15484
+pygments/lexers/_vim_builtins.py,sha256=mIYAlwzf687UR8oPcY6y48H-BjTyPNDx1oHiRc5wByw,57090
+pygments/lexers/actionscript.py,sha256=HWGTyjPGho_kZVVTxemN0_5iyhK5wFgFQQ0MYVS0as8,11179
+pygments/lexers/agile.py,sha256=zB0BGCISgQB2b-3ch3QNRgV2O7jq7QPD5nvBbXA_K5U,900
+pygments/lexers/algebra.py,sha256=fhvIdLDS3oW0iMsDSBAzg8l9DuBXxfLRFC53xRIzvHU,7201
+pygments/lexers/ambient.py,sha256=M77HDk4dzs3Amlgwi_ogj1PV1Ch77PyEW6SU5AFtL4w,2557
+pygments/lexers/ampl.py,sha256=6AQASY--hmYXtgkzsthZymRM4bzQ_RQZZC5Wu4RJG-A,4120
+pygments/lexers/apl.py,sha256=Lpv2nI1bSC_VpmckdbpMiuxWx-WVb6FgFWoJ31q002w,3167
+pygments/lexers/archetype.py,sha256=IwyDTh-U673WHQQThuf2dBz0Fc-XlcAP-xAhtnR2DH4,11136
+pygments/lexers/asm.py,sha256=YysaSPQr0Ir79aM5C0eOB7g8-vJ6m2gWdbghsEElGBM,25261
+pygments/lexers/automation.py,sha256=g_laLS9LstvXvrvVQI01fAf0fyZ4-ZHFewdVLtXPXf0,19648
+pygments/lexers/basic.py,sha256=XfeI7Tk78lN7YMqS6bYdRB6CRPa2vxcm5PzA2EbILnc,20306
+pygments/lexers/bibtex.py,sha256=CcIdWMtCUtpHoqXU9JtGYKOmQJscO7AwofABCnEx_lk,4724
+pygments/lexers/business.py,sha256=iJbZjsF_CwJvkWmuDEAUgRhIPN3Vus_sp2m4V9AsKuo,27665
+pygments/lexers/c_cpp.py,sha256=Ty6LTWNG_ZR8eLNmcdBmJfUIJlkAyd033JqIH3Mfric,10523
+pygments/lexers/c_like.py,sha256=AoPSFV_TFPudAnvkV0wLlM00A3_JrddoTd5GRMyeHcY,24124
+pygments/lexers/capnproto.py,sha256=8pl0G3q7k5AJeoe6Yfr2eMs81V8KW1IlduzkRPaNb7A,2188
+pygments/lexers/chapel.py,sha256=dMPHVlkPsEhqOHQmuX8QlxJ48c1Pml8Q7RpOhc9lYUk,3511
+pygments/lexers/clean.py,sha256=IukUXRAQigyPLlD4K1tfZhQ8qH2l6gVn4hGFuHsz_vc,10403
+pygments/lexers/compiled.py,sha256=9d_XDV75Fi_EnrsVPAexfs8LRAysfixpZDNqVEhjGUU,1385
+pygments/lexers/configs.py,sha256=W1OdBJEwZWp7imQE1bC6a4sGIGoKw5LIbmYwau6u6Ds,28266
+pygments/lexers/console.py,sha256=7rwV7Jsci0nC74JDVzNlD5-PlRsfzZspHfYFSC8OKtM,4120
+pygments/lexers/crystal.py,sha256=h6M1BX-vWjCUtPQNXH1KqgqoxuB6geR3kCKp74sIb48,16845
+pygments/lexers/csound.py,sha256=t4bVgLEkNXEFZdfdfB43yWGXjJhT3aiKN9uLHNrTtZA,12544
+pygments/lexers/css.py,sha256=QZadHT6mYr14tMhZKDVRP3uu_AWSmGNIkn7KUr5H_0k,31513
+pygments/lexers/d.py,sha256=G-RV1UPAI8Q6mstQCMa4vNhwop0pG2NO1APHkOLdayA,9530
+pygments/lexers/dalvik.py,sha256=RDuGFuTqCG1PxhBn-I80iJxegrJpUDi_knxCKwN1cKQ,4420
+pygments/lexers/data.py,sha256=HOBr6Xw4QOYI6sD3vvtuSoYRdMaQYBnT0IcgpWUwTx0,18771
+pygments/lexers/diff.py,sha256=n7dxgD0_z0W2LH_zf55E1erBs5iBHFZPZ0bItXSeiLI,4873
+pygments/lexers/dotnet.py,sha256=IxNL30wmyw-Z_QRSUf9G9kbhz017BP5Pjqn2LEcfE9A,27668
+pygments/lexers/dsls.py,sha256=6v76UwDB7PngLM0Ye79mz_cXpg075RBgTzS9VXIsMQw,33336
+pygments/lexers/dylan.py,sha256=YJyoYcXD622vwYxqCNtyoHqi-L67CABzNfcg9ERt1Ic,10421
+pygments/lexers/ecl.py,sha256=bRD_m3B8npOLIyHjxhcWUCXeD9YCw7I6u5WsCUb7Kpc,5875
+pygments/lexers/eiffel.py,sha256=1-7weE5oflU_dzDVhaQFx5ildMbBuCZ3OFXJpJ-U78c,2482
+pygments/lexers/elm.py,sha256=3qhDIU0GFJ4YzuVDyvbHZ39dOyY60QFVpRvDdpDeYUw,2996
+pygments/lexers/erlang.py,sha256=Ja1SxnF-f48ort3oRb4R9aaHXbLJZIp0ItsgI8ITDJM,18936
+pygments/lexers/esoteric.py,sha256=wdtih2rpCV-N79Rf2laxzlqSBMYwzQgeCtBcutTbt1E,9490
+pygments/lexers/ezhil.py,sha256=o3qj60N-fyLGjcho1lrVW4x-CZ0Fu-rDe7tuqInD5oQ,3020
+pygments/lexers/factor.py,sha256=Trq7bM5thNo6BFZ2Tnit82FnWuUK70TL-atOKkLdHng,17864
+pygments/lexers/fantom.py,sha256=AGk3ewVsJ8gA9dU6N7iWGIlJLt5p61vRiJmVhyCj7Zg,9982
+pygments/lexers/felix.py,sha256=94Ffv8uhADGlom_cjKKvlE1QYpDnxRo0EdPGYhlxNTA,9408
+pygments/lexers/forth.py,sha256=uN-Sr5fi4SHnTluGzwmUsPgMFt470gSM212e7M5bH9Q,7144
+pygments/lexers/fortran.py,sha256=QRpUhXg0xnUu8EqsbEG6S33Sn8AWU326RiC8J-r2ZVw,9768
+pygments/lexers/foxpro.py,sha256=AWnHAryB1dUQkU4I48zoJFI8M_DsoIlEmvemDs1O4nM,26236
+pygments/lexers/functional.py,sha256=PPhynqxQZ602rCV-TxNldHRwhjMPfyHkGIOPx0BoVZo,698
+pygments/lexers/go.py,sha256=6IS3PKkqGG18OIlYqDGUp46S1jcfAfjhixNJe8Dd3gQ,3701
+pygments/lexers/grammar_notation.py,sha256=xyJi8t0X8SFElc3HTsmzF4_2mZs-qHW-ur9kZKt48D0,6328
+pygments/lexers/graph.py,sha256=-71ZtKUTq4LX4uUyRWFruA57Z_8lyOas1z1bWdq8eMs,2370
+pygments/lexers/graphics.py,sha256=6AoxbU5ybgg0_Mm48dZlspishe7PuK0pa_8bsPsDVfM,25836
+pygments/lexers/haskell.py,sha256=uOzfXPsn9mBs26GzEm-3qNNNJy6Oxc66lt0NVDVRR-M,31218
+pygments/lexers/haxe.py,sha256=QWR5lBwbdCMrJbFcUAs4SnP66zVV8O-DiT4c4x6XtBA,30953
+pygments/lexers/hdl.py,sha256=h3CYE2ShUOejE7w0d1eSHkGgCS4EXJodOVgjfACWYz8,18699
+pygments/lexers/hexdump.py,sha256=-_u6qAw7yH7b8_25tHNLiybD3r5-OaYYwtf9RsSxfeE,3507
+pygments/lexers/html.py,sha256=1f0HI3wZoGZL9PY6dLdmG8AytfT3oEosKHQp3DuDZmY,19269
+pygments/lexers/idl.py,sha256=gNT6hIlEMD-ZjLZ3ru6RH7Z-X5u7TKQ7lRP_9Sz1SSg,14984
+pygments/lexers/igor.py,sha256=PsDRDxMkwslMjd4GdZmAOXLrblsc2FANBMlNfv-XbVg,19994
+pygments/lexers/inferno.py,sha256=G4uU8YQD9EGvDNCc6Av6LWvyRB8unp5qv47PfLUek_8,3116
+pygments/lexers/installers.py,sha256=QEPP1YcZ38DvErs7xwpChvKQFh1I6fIp0V6MXxIwzeY,12866
+pygments/lexers/int_fiction.py,sha256=m8rYr5egxBSxZ9FKunV_Yl_EtpoRBz1j55J_dtKG9oo,55778
+pygments/lexers/iolang.py,sha256=1kvzEPcJZ2eyF7mu58NT9yyqZobOvx1MlbUxA98bLJs,1904
+pygments/lexers/j.py,sha256=e6oqJQslgSl6q3hfEuTl0mtFnqCj4G29SwcMnTeBDBc,4525
+pygments/lexers/javascript.py,sha256=79c9e2wn7HiJ9S4WvRQ0qymMqj-YV7CS308_pMRNirk,60134
+pygments/lexers/julia.py,sha256=vXU5r_p6ko-I_fKITqiFmMn0rpw0NWujKVGGrndkfOI,14093
+pygments/lexers/jvm.py,sha256=9fjCUGFGApGNMDsrCWzaEiDA_CcoZPDaYAvBHFAh524,66792
+pygments/lexers/lisp.py,sha256=xeF_wQGdm-vkJ2j1JQd9mipAAtjwY3giOEH2cW2j0jk,140673
+pygments/lexers/make.py,sha256=jGkYUD5PD4vG7gC6qExOAp8ZE4KoB0jLMcTF5NzZGQs,7332
+pygments/lexers/markup.py,sha256=KQsXc5K_rn7G7bATKt81yURJjHHi5kCLokJXXom997I,20452
+pygments/lexers/math.py,sha256=TaE1dzOfu6ukxMFAInZI_Ua105LDnxx86Ufk4YdnvfA,700
+pygments/lexers/matlab.py,sha256=GUys_wezfz7n_F9U5l2HZauxlsSxrRh0bkgIjfhZ7ro,29146
+pygments/lexers/ml.py,sha256=GDreW4Pmgqt8BlfbeRG43e3wS3wSm_A7cHPKGeGY9hQ,27891
+pygments/lexers/modeling.py,sha256=4O9hB4wAhjD2HFYa74HsLf9QK4E8kq7k8c5M7WuM1f4,12833
+pygments/lexers/modula2.py,sha256=mNbhlG9lNZRXP9kzx9diJj7l6hiPQVqmOzrfJ53GarU,52561
+pygments/lexers/monte.py,sha256=MNswWqletPzSIg6TcECb-Dcq1bv-eSPhYLsbc-vnJzM,6307
+pygments/lexers/ncl.py,sha256=cvnCcYpC0jpHBrwtNXZhY5HheY3UMThsYbgscHGvZLM,63986
+pygments/lexers/nimrod.py,sha256=OizSj9kHvtFEu0OPXuBrXZCf_ZBMnR4hm3nMbjw50hQ,5174
+pygments/lexers/nit.py,sha256=1SJ45GOd9zQKuWhPuVy-KhLkXQn6V-7ESvjJJ7U9Vpo,2743
+pygments/lexers/nix.py,sha256=dWzLp5QxzLmskDflyMUGidGNau9Vtk8ViK5c6B2O4kI,4031
+pygments/lexers/oberon.py,sha256=CbqkEOdqxvWXfez5FiGeCE-f-Vlt6tu9kqPFEVVXAQc,3733
+pygments/lexers/objective.py,sha256=qCPLLfU3hl-CIOJWAZdQYjeIl1Ox1igZaPRGKowKYbs,22764
+pygments/lexers/ooc.py,sha256=_lnOynAR0vHli7OKWJ4yZs9-hyPZE5DGKwkdkP6SCuM,2999
+pygments/lexers/other.py,sha256=Utlo5esm3DetK9PSOueHu5mEjR25dIN3T_g2-u9cmeA,1768
+pygments/lexers/parasail.py,sha256=Egm3PcuCBjDgwUvvI0-t3oXMz4PPRY0dER3orrrlnfc,2737
+pygments/lexers/parsers.py,sha256=S93llPIzcqL0VU2C_SzFa-pykNcFE37Kvy9_7fNmdg4,27582
+pygments/lexers/pascal.py,sha256=tA4Na6dzTz8nd9-Q_tcBFVDo161qfG9bR4xIIIB8FY0,32646
+pygments/lexers/pawn.py,sha256=eJJfXLHPYEPflh3fKgyq4PD5b52pcqjBnAGDicqHK9w,8094
+pygments/lexers/perl.py,sha256=-k_lftMhedsEIQwmvEZyy8Xbtlgj28_fic6HhN_ZYGI,32012
+pygments/lexers/php.py,sha256=7tgZQvYAnprFRQfAUxOlXgNkeUyOYsvIkM5oW0NSzOU,10730
+pygments/lexers/praat.py,sha256=GpHWS1IyqpvFCbZ7JWXSyxgBl8KAQED8IonkJM-Ml28,12556
+pygments/lexers/prolog.py,sha256=GWAUHvP3eyROfr3ry8g_7OJSNpD3vwfAFTJsrqIL7cQ,12066
+pygments/lexers/python.py,sha256=pn_V-1XyAqoYD4x8Pclk4eP8nkYvacpObY06xwDYSq8,42384
+pygments/lexers/qvt.py,sha256=LnLMM2jTrc5k4kInraQmmui1mgf76Wx5Ln2CfBFqj4I,6111
+pygments/lexers/r.py,sha256=6aUQ8T4qn6qIWuR-D9P_r05CEcP5SJ8wMadzUVrgJ4k,23755
+pygments/lexers/rdf.py,sha256=qbH7suOIdGY435x721iWhJ9QXT3MYPo874Jss8f2WCY,9398
+pygments/lexers/rebol.py,sha256=91kF9ueomN_2-Za8u1nIsuYN6uTQ3T1UbZk3IfRoiD8,18617
+pygments/lexers/resource.py,sha256=uS23PmW5COZfyyCte9s6xLhqZS2uhfH5P3G41QGp5U0,2933
+pygments/lexers/rnc.py,sha256=QvwLWh7IG9LYp9hmD7dI2MM4ikNaeqSQf26LmCBPVLA,1990
+pygments/lexers/roboconf.py,sha256=2__rRqffgFjrqyFFHHjibSg1qHCm2_lIT7lOGu63DNI,2070
+pygments/lexers/robotframework.py,sha256=qKKxIoTAMDnr_iDEnFsF9sjH63uhr1hDQUMStJydMYo,18744
+pygments/lexers/ruby.py,sha256=pukJJddNWR7jC4Uxc_qnOrcFXI5am3d0A8ApiZfeAnQ,22141
+pygments/lexers/rust.py,sha256=q18g2pHP_srVa8ANoyiIoMmXB4XkYI6Xp57rC-V9xmU,7695
+pygments/lexers/sas.py,sha256=oKP_7bIyKUnFS8kDGKsWxHqKhkRBV4emle28pX3MUzE,9449
+pygments/lexers/scripting.py,sha256=1_gD_-cQHrDF-uDEmBlelrUSWV5cQoz79EZ_3RpAh48,67761
+pygments/lexers/shell.py,sha256=H1byqbwQefCUaSDra6EabmI2o15zfpeusc73p1mBDQw,31426
+pygments/lexers/smalltalk.py,sha256=QJHVLNsCWr8zWHoF9C9Lev7LwefFYgI7L4mtY-LWJRU,7215
+pygments/lexers/smv.py,sha256=HnLzDk-zFDXhaHXxFI-ZINv6pW6stkk0IfxVHgStOLY,2802
+pygments/lexers/snobol.py,sha256=glC_g_14iKbHWaYCdkMibhGBNdPbzOgl7MVlJbpZMUI,2756
+pygments/lexers/special.py,sha256=Q1qXU0E8ecPXL_rMnzksLNbcisjOexxjXmo9j5ODBmM,3151
+pygments/lexers/sql.py,sha256=eMnkUuSpfII72c_lCcxOSWkuAuZH7qN6GkwM1xllMpA,29445
+pygments/lexers/stata.py,sha256=XI46JTQw-9YH5TkDB35FfFy8lJVaxr7qbdnOWbFhG3s,3627
+pygments/lexers/supercollider.py,sha256=t5LHZNcKwtgxnnCJnHCSYGjHBGXZzNR_TLF_pPOMrh4,3516
+pygments/lexers/tcl.py,sha256=ulsFqa6OwCKUoJHDvqBLdVHZL31EA5BDI8bf3iB703E,5398
+pygments/lexers/templates.py,sha256=V4ColLZh2NTkxI_zr4J7JnpRAafVNEezy3GWjq8knXs,73457
+pygments/lexers/testing.py,sha256=lup3FuiDthufXo3ujm6HSV4QKBscYoTpNe9K5iHCZ2g,10751
+pygments/lexers/text.py,sha256=7nKWCivruly7wyquaug3-IF8v2fUdXUVSYXHMVUlkdY,977
+pygments/lexers/textedit.py,sha256=1kH4uCnq4j80c0KGSMRJgzRgglm_5ckoiQVZX21qXvc,6057
+pygments/lexers/textfmts.py,sha256=sOQ4vDo5P1SpY_obS7N5oObolY501Tk2va69iDQyWWM,10852
+pygments/lexers/theorem.py,sha256=HmW981GDEA0TM8H7siEv2lszH-U9S7hcExRyom2eX2w,19034
+pygments/lexers/trafficscript.py,sha256=Xyfa407k2_rzvlKuExUcB5h6bC89WN6eQx6K3o3uV3o,1546
+pygments/lexers/typoscript.py,sha256=c6gOzNkvqgixbRKeQO7vXSrEPf6K4pnrFLFQ9SzusA8,8392
+pygments/lexers/urbi.py,sha256=p4gxSltIKKMFL8wJuLQ_cC40zSAC8bqc7aXZT0s5M7Y,5750
+pygments/lexers/varnish.py,sha256=5aSrhCfYFRbZsvKtQuKzJjHGCosabDGua3_LCG-JLlA,7265
+pygments/lexers/verification.py,sha256=Pb3upMUI03UFFsYAmqEzB2VK3N-ppMs1KYExE_tS1yE,3705
+pygments/lexers/web.py,sha256=5RB4JP962BUzuMIHon_H3tRgO458FAROJ91S8uk-gno,918
+pygments/lexers/webmisc.py,sha256=-6FzwUll5VU4oN6rSutfQmqPqD_OvWZ7kZWO1H3WMXo,39891
+pygments/lexers/whiley.py,sha256=YNCb-hXy8rPPVH9ea4q8QAXqncXNPHtgqAqAKPLfHfM,4012
+pygments/lexers/x10.py,sha256=FyCneMc5r_PRL7a0iaAsvgfyrxk2dYSzE9WJa1O4ZBw,1965
+pygments/styles/__init__.py,sha256=bMJA5A-As-KqVlgOVa1ryoivG-fI20nE5mlEvneFqT0,2553
+pygments/styles/abap.py,sha256=GYW80_C20vlhROQCE9RIn8oOy0uFO5VeJJMm7FXCEz8,751
+pygments/styles/algol.py,sha256=mE09x_hUp-ferskH8OLAWO115u9GQQYMR70Y-2zS6Y0,2263
+pygments/styles/algol_nu.py,sha256=psY0F47Bl5LThIeih15ghZvkalijyq6jrUnx84n_TFg,2278
+pygments/styles/arduino.py,sha256=UTcdXehOhcXWda9NlYeW3piKZUbEpQQkx0ARZCC7Ih4,4492
+pygments/styles/autumn.py,sha256=FpFEPsPBnGugtBp4M-3FqKPbKlGomYAbq9HSNiYh3DU,2144
+pygments/styles/borland.py,sha256=QhOMFrDL3PAwEJZkuEt9qzPN8T8cVooRG5vtLROgkRc,1562
+pygments/styles/bw.py,sha256=vBQMjQhou4csJMHTIE5AJCeLESFaE5UqgSPN2ZAli6o,1355
+pygments/styles/colorful.py,sha256=SWzWtEmeEoZ7DNIh6XQeZxDz6bIOlgBuwGFePB2PUmc,2778
+pygments/styles/default.py,sha256=UUN6VGAOB12b_R2ky-6cHfdaXE34xJaNtaHwsBzl1I8,2532
+pygments/styles/emacs.py,sha256=CndsC1mEd6GGVCFA1uKmb5rMqjMiMCqJ1uBE2fgKCQA,2486
+pygments/styles/friendly.py,sha256=0c5ibD9ldewtsKzgjHSV2-FN_iRdDa_bUkVWi8ZnfdE,2515
+pygments/styles/fruity.py,sha256=cSYED092upFc4kekb2m-f34Wh4BWxXyEb8N6rYLZOas,1298
+pygments/styles/igor.py,sha256=MZNAVwgWh5FU2bRbKaXo5uV9_h2IH3gh4Cm3MspnFIc,739
+pygments/styles/lovelace.py,sha256=u0fQW4OUZ37w9Z1A0pzsMYfJX68kxMrp0CbjWJg46pE,3173
+pygments/styles/manni.py,sha256=2CC4G2o6hUYfCceP4O9bpNBgNONW7HCJ--L3khD7jhc,2374
+pygments/styles/monokai.py,sha256=QgSUuhv6DDz7LhyfL9lW5VMIRJIj0ID_GqTJupunWgs,5080
+pygments/styles/murphy.py,sha256=m2bWPIFW8gnR2miA6FF2kYDwXT2sltSJ9MMcA1AEnqg,2751
+pygments/styles/native.py,sha256=V_mDvPG5eUm8V2XSCYsKsqkp0XfxK8gn80wryiUmmIU,1938
+pygments/styles/paraiso_dark.py,sha256=YxdADs3KBND4ZXfYqkQBXGxIs2y4VxjaiKw8f3AdoFo,5641
+pygments/styles/paraiso_light.py,sha256=-HHuYHnJBX7Yehza875DjU4ly95ATC1sh6o_RuqbeuI,5645
+pygments/styles/pastie.py,sha256=6ku_vX2aFP7N9iAsT8a-1lzf9L1DR_j0SE37HVbFoSw,2473
+pygments/styles/perldoc.py,sha256=CJq6FzNG22C3aru3QO2_l6ebksqnAQhSBLeiYYoJjhw,2175
+pygments/styles/rainbow_dash.py,sha256=mPI5Yfl-sRW9Aig_xvs63i3sjWqO5fpihBbiaLozyYM,2480
+pygments/styles/rrt.py,sha256=-UUYhzna_sPcmwUXEtnDoAaUD8cnomk8grWfKOsmJ-M,852
+pygments/styles/sas.py,sha256=yKgZ8AiADLXtVZLZ0JtxlB6BGJAVkcX0DyG8g-R8nSE,1441
+pygments/styles/stata.py,sha256=PH8ckY6Q68QQXjv8wDK_Z-DVbyiWfy5vRLd4HUYd02k,1249
+pygments/styles/tango.py,sha256=Bsn6unVhwiOJ8dc1-csQYHanRjIZ7JOxQndq6Dc-BxE,7096
+pygments/styles/trac.py,sha256=XlcVcvSqIfVeqSsQo34V5B0VLi_FQwlpfaE0Zv7LE0M,1933
+pygments/styles/vim.py,sha256=hgv2HvW3kriTmj0HE7TdzeJhJhxvblqEG-S3ELiZEX4,1976
+pygments/styles/vs.py,sha256=X7jUKNoKNnNE6ewe9vsGgTeSxK4a7x8oBbaA6LNzpGQ,1073
+pygments/styles/xcode.py,sha256=gWl7xboxgvDzARO8-VmBfUX0GOnWWbGU9Su_TAIyEFQ,1501
+../../../bin/pygmentize,sha256=lJEVcJDCh3wZtGGRRsU0VTcfxjHRtqJGEkvNAgVYrwE,276
+Pygments-2.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+pygments/formatters/__pycache__/html.cpython-35.pyc,,
+pygments/lexers/__pycache__/factor.cpython-35.pyc,,
+pygments/__pycache__/console.cpython-35.pyc,,
+pygments/lexers/__pycache__/_cl_builtins.cpython-35.pyc,,
+pygments/lexers/__pycache__/d.cpython-35.pyc,,
+pygments/lexers/__pycache__/ooc.cpython-35.pyc,,
+pygments/lexers/__pycache__/theorem.cpython-35.pyc,,
+pygments/lexers/__pycache__/_sourcemod_builtins.cpython-35.pyc,,
+pygments/__pycache__/formatter.cpython-35.pyc,,
+pygments/lexers/__pycache__/x10.cpython-35.pyc,,
+pygments/__pycache__/unistring.cpython-35.pyc,,
+pygments/lexers/__pycache__/stata.cpython-35.pyc,,
+pygments/__pycache__/sphinxext.cpython-35.pyc,,
+pygments/lexers/__pycache__/rdf.cpython-35.pyc,,
+pygments/lexers/__pycache__/graph.cpython-35.pyc,,
+pygments/lexers/__pycache__/algebra.cpython-35.pyc,,
+pygments/formatters/__pycache__/terminal.cpython-35.pyc,,
+pygments/styles/__pycache__/paraiso_dark.cpython-35.pyc,,
+pygments/lexers/__pycache__/_stata_builtins.cpython-35.pyc,,
+pygments/lexers/__pycache__/_asy_builtins.cpython-35.pyc,,
+pygments/lexers/__pycache__/nix.cpython-35.pyc,,
+pygments/lexers/__pycache__/installers.cpython-35.pyc,,
+pygments/lexers/__pycache__/_scilab_builtins.cpython-35.pyc,,
+pygments/lexers/__pycache__/rnc.cpython-35.pyc,,
+pygments/lexers/__pycache__/lisp.cpython-35.pyc,,
+pygments/styles/__pycache__/algol.cpython-35.pyc,,
+pygments/__pycache__/modeline.cpython-35.pyc,,
+pygments/lexers/__pycache__/jvm.cpython-35.pyc,,
+pygments/formatters/__pycache__/latex.cpython-35.pyc,,
+pygments/styles/__pycache__/rainbow_dash.cpython-35.pyc,,
+pygments/styles/__pycache__/stata.cpython-35.pyc,,
+pygments/lexers/__pycache__/dotnet.cpython-35.pyc,,
+pygments/lexers/__pycache__/dalvik.cpython-35.pyc,,
+pygments/lexers/__pycache__/testing.cpython-35.pyc,,
+pygments/formatters/__pycache__/_mapping.cpython-35.pyc,,
+pygments/lexers/__pycache__/markup.cpython-35.pyc,,
+pygments/lexers/__pycache__/tcl.cpython-35.pyc,,
+pygments/lexers/__pycache__/eiffel.cpython-35.pyc,,
+pygments/lexers/__pycache__/resource.cpython-35.pyc,,
+pygments/lexers/__pycache__/clean.cpython-35.pyc,,
+pygments/lexers/__pycache__/graphics.cpython-35.pyc,,
+pygments/styles/__pycache__/vim.cpython-35.pyc,,
+pygments/lexers/__pycache__/make.cpython-35.pyc,,
+pygments/styles/__pycache__/native.cpython-35.pyc,,
+pygments/lexers/__pycache__/int_fiction.cpython-35.pyc,,
+pygments/styles/__pycache__/autumn.cpython-35.pyc,,
+pygments/lexers/__pycache__/ml.cpython-35.pyc,,
+pygments/lexers/__pycache__/hdl.cpython-35.pyc,,
+pygments/styles/__pycache__/borland.cpython-35.pyc,,
+pygments/styles/__pycache__/tango.cpython-35.pyc,,
+pygments/lexers/__pycache__/automation.cpython-35.pyc,,
+pygments/styles/__pycache__/murphy.cpython-35.pyc,,
+pygments/styles/__pycache__/vs.cpython-35.pyc,,
+pygments/lexers/__pycache__/ampl.cpython-35.pyc,,
+pygments/lexers/__pycache__/_php_builtins.cpython-35.pyc,,
+pygments/lexers/__pycache__/functional.cpython-35.pyc,,
+pygments/lexers/__pycache__/compiled.cpython-35.pyc,,
+pygments/lexers/__pycache__/textfmts.cpython-35.pyc,,
+pygments/styles/__pycache__/sas.cpython-35.pyc,,
+pygments/styles/__pycache__/lovelace.cpython-35.pyc,,
+pygments/lexers/__pycache__/typoscript.cpython-35.pyc,,
+pygments/lexers/__pycache__/j.cpython-35.pyc,,
+pygments/lexers/__pycache__/_lasso_builtins.cpython-35.pyc,,
+pygments/lexers/__pycache__/ambient.cpython-35.pyc,,
+pygments/__pycache__/regexopt.cpython-35.pyc,,
+pygments/styles/__pycache__/fruity.cpython-35.pyc,,
+pygments/lexers/__pycache__/hexdump.cpython-35.pyc,,
+pygments/styles/__pycache__/xcode.cpython-35.pyc,,
+pygments/lexers/__pycache__/whiley.cpython-35.pyc,,
+pygments/lexers/__pycache__/perl.cpython-35.pyc,,
+pygments/lexers/__pycache__/_vim_builtins.cpython-35.pyc,,
+pygments/lexers/__pycache__/matlab.cpython-35.pyc,,
+pygments/__pycache__/filter.cpython-35.pyc,,
+pygments/lexers/__pycache__/nimrod.cpython-35.pyc,,
+pygments/lexers/__pycache__/c_cpp.cpython-35.pyc,,
+pygments/lexers/__pycache__/_stan_builtins.cpython-35.pyc,,
+pygments/lexers/__pycache__/configs.cpython-35.pyc,,
+pygments/formatters/__pycache__/__init__.cpython-35.pyc,,
+pygments/styles/__pycache__/colorful.cpython-35.pyc,,
+pygments/lexers/__pycache__/haskell.cpython-35.pyc,,
+pygments/lexers/__pycache__/sql.cpython-35.pyc,,
+pygments/lexers/__pycache__/fantom.cpython-35.pyc,,
+pygments/lexers/__pycache__/prolog.cpython-35.pyc,,
+pygments/__pycache__/cmdline.cpython-35.pyc,,
+pygments/styles/__pycache__/algol_nu.cpython-35.pyc,,
+pygments/lexers/__pycache__/__init__.cpython-35.pyc,,
+pygments/lexers/__pycache__/ezhil.cpython-35.pyc,,
+pygments/lexers/__pycache__/praat.cpython-35.pyc,,
+pygments/lexers/__pycache__/html.cpython-35.pyc,,
+pygments/lexers/__pycache__/_lua_builtins.cpython-35.pyc,,
+pygments/__pycache__/style.cpython-35.pyc,,
+pygments/__pycache__/util.cpython-35.pyc,,
+pygments/lexers/__pycache__/data.cpython-35.pyc,,
+pygments/lexers/__pycache__/math.cpython-35.pyc,,
+pygments/lexers/__pycache__/_mapping.cpython-35.pyc,,
+pygments/lexers/__pycache__/snobol.cpython-35.pyc,,
+pygments/styles/__pycache__/emacs.cpython-35.pyc,,
+pygments/lexers/__pycache__/diff.cpython-35.pyc,,
+pygments/lexers/__pycache__/agile.cpython-35.pyc,,
+pygments/formatters/__pycache__/img.cpython-35.pyc,,
+pygments/lexers/__pycache__/objective.cpython-35.pyc,,
+pygments/lexers/__pycache__/javascript.cpython-35.pyc,,
+pygments/styles/__pycache__/bw.cpython-35.pyc,,
+pygments/__pycache__/__init__.cpython-35.pyc,,
+pygments/lexers/__pycache__/go.cpython-35.pyc,,
+pygments/lexers/__pycache__/foxpro.cpython-35.pyc,,
+pygments/lexers/__pycache__/modeling.cpython-35.pyc,,
+pygments/lexers/__pycache__/pawn.cpython-35.pyc,,
+pygments/lexers/__pycache__/felix.cpython-35.pyc,,
+pygments/lexers/__pycache__/oberon.cpython-35.pyc,,
+pygments/formatters/__pycache__/bbcode.cpython-35.pyc,,
+pygments/lexers/__pycache__/templates.cpython-35.pyc,,
+pygments/lexers/__pycache__/webmisc.cpython-35.pyc,,
+pygments/lexers/__pycache__/actionscript.cpython-35.pyc,,
+pygments/lexers/__pycache__/monte.cpython-35.pyc,,
+pygments/styles/__pycache__/friendly.cpython-35.pyc,,
+pygments/lexers/__pycache__/inferno.cpython-35.pyc,,
+pygments/lexers/__pycache__/archetype.cpython-35.pyc,,
+pygments/lexers/__pycache__/igor.cpython-35.pyc,,
+pygments/filters/__pycache__/__init__.cpython-35.pyc,,
+pygments/lexers/__pycache__/qvt.cpython-35.pyc,,
+pygments/lexers/__pycache__/other.cpython-35.pyc,,
+pygments/lexers/__pycache__/nit.cpython-35.pyc,,
+pygments/lexers/__pycache__/elm.cpython-35.pyc,,
+pygments/lexers/__pycache__/grammar_notation.cpython-35.pyc,,
+pygments/styles/__pycache__/trac.cpython-35.pyc,,
+pygments/formatters/__pycache__/irc.cpython-35.pyc,,
+pygments/lexers/__pycache__/_cocoa_builtins.cpython-35.pyc,,
+pygments/lexers/__pycache__/ecl.cpython-35.pyc,,
+pygments/__pycache__/scanner.cpython-35.pyc,,
+pygments/lexers/__pycache__/bibtex.cpython-35.pyc,,
+pygments/lexers/__pycache__/forth.cpython-35.pyc,,
+pygments/lexers/__pycache__/web.cpython-35.pyc,,
+pygments/lexers/__pycache__/smalltalk.cpython-35.pyc,,
+pygments/lexers/__pycache__/r.cpython-35.pyc,,
+pygments/styles/__pycache__/__init__.cpython-35.pyc,,
+pygments/lexers/__pycache__/textedit.cpython-35.pyc,,
+pygments/styles/__pycache__/default.cpython-35.pyc,,
+pygments/styles/__pycache__/igor.cpython-35.pyc,,
+pygments/lexers/__pycache__/console.cpython-35.pyc,,
+pygments/lexers/__pycache__/modula2.cpython-35.pyc,,
+pygments/lexers/__pycache__/robotframework.cpython-35.pyc,,
+pygments/lexers/__pycache__/apl.cpython-35.pyc,,
+pygments/lexers/__pycache__/css.cpython-35.pyc,,
+pygments/styles/__pycache__/arduino.cpython-35.pyc,,
+pygments/lexers/__pycache__/rust.cpython-35.pyc,,
+pygments/lexers/__pycache__/parsers.cpython-35.pyc,,
+pygments/lexers/__pycache__/crystal.cpython-35.pyc,,
+pygments/lexers/__pycache__/csound.cpython-35.pyc,,
+pygments/lexers/__pycache__/ncl.cpython-35.pyc,,
+pygments/formatters/__pycache__/svg.cpython-35.pyc,,
+pygments/styles/__pycache__/paraiso_light.cpython-35.pyc,,
+pygments/styles/__pycache__/perldoc.cpython-35.pyc,,
+pygments/lexers/__pycache__/idl.cpython-35.pyc,,
+pygments/lexers/__pycache__/asm.cpython-35.pyc,,
+pygments/lexers/__pycache__/_postgres_builtins.cpython-35.pyc,,
+pygments/lexers/__pycache__/parasail.cpython-35.pyc,,
+pygments/lexers/__pycache__/capnproto.cpython-35.pyc,,
+pygments/formatters/__pycache__/other.cpython-35.pyc,,
+pygments/lexers/__pycache__/chapel.cpython-35.pyc,,
+pygments/lexers/__pycache__/trafficscript.cpython-35.pyc,,
+pygments/lexers/__pycache__/ruby.cpython-35.pyc,,
+pygments/__pycache__/plugin.cpython-35.pyc,,
+pygments/lexers/__pycache__/supercollider.cpython-35.pyc,,
+pygments/lexers/__pycache__/smv.cpython-35.pyc,,
+pygments/styles/__pycache__/pastie.cpython-35.pyc,,
+pygments/formatters/__pycache__/rtf.cpython-35.pyc,,
+pygments/lexers/__pycache__/erlang.cpython-35.pyc,,
+pygments/lexers/__pycache__/varnish.cpython-35.pyc,,
+pygments/lexers/__pycache__/esoteric.cpython-35.pyc,,
+pygments/lexers/__pycache__/business.cpython-35.pyc,,
+pygments/styles/__pycache__/rrt.cpython-35.pyc,,
+pygments/lexers/__pycache__/julia.cpython-35.pyc,,
+pygments/lexers/__pycache__/_openedge_builtins.cpython-35.pyc,,
+pygments/lexers/__pycache__/rebol.cpython-35.pyc,,
+pygments/lexers/__pycache__/urbi.cpython-35.pyc,,
+pygments/lexers/__pycache__/php.cpython-35.pyc,,
+pygments/lexers/__pycache__/_tsql_builtins.cpython-35.pyc,,
+pygments/styles/__pycache__/manni.cpython-35.pyc,,
+pygments/lexers/__pycache__/dylan.cpython-35.pyc,,
+pygments/lexers/__pycache__/fortran.cpython-35.pyc,,
+pygments/lexers/__pycache__/roboconf.cpython-35.pyc,,
+pygments/lexers/__pycache__/special.cpython-35.pyc,,
+pygments/styles/__pycache__/monokai.cpython-35.pyc,,
+pygments/lexers/__pycache__/shell.cpython-35.pyc,,
+pygments/lexers/__pycache__/iolang.cpython-35.pyc,,
+pygments/formatters/__pycache__/terminal256.cpython-35.pyc,,
+pygments/lexers/__pycache__/python.cpython-35.pyc,,
+pygments/lexers/__pycache__/scripting.cpython-35.pyc,,
+pygments/lexers/__pycache__/_csound_builtins.cpython-35.pyc,,
+pygments/lexers/__pycache__/sas.cpython-35.pyc,,
+pygments/__pycache__/lexer.cpython-35.pyc,,
+pygments/lexers/__pycache__/verification.cpython-35.pyc,,
+pygments/lexers/__pycache__/text.cpython-35.pyc,,
+pygments/styles/__pycache__/abap.cpython-35.pyc,,
+pygments/__pycache__/token.cpython-35.pyc,,
+pygments/lexers/__pycache__/basic.cpython-35.pyc,,
+pygments/lexers/__pycache__/pascal.cpython-35.pyc,,
+pygments/lexers/__pycache__/_mql_builtins.cpython-35.pyc,,
+pygments/lexers/__pycache__/c_like.cpython-35.pyc,,
+pygments/lexers/__pycache__/haxe.cpython-35.pyc,,
+pygments/lexers/__pycache__/dsls.cpython-35.pyc,,
diff --git a/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/WHEEL b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/WHEEL
new file mode 100644
index 0000000..8b6dd1b
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.29.0)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/entry_points.txt b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/entry_points.txt
new file mode 100644
index 0000000..756d801
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/entry_points.txt
@@ -0,0 +1,3 @@
+[console_scripts]
+pygmentize = pygments.cmdline:main
+
diff --git a/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/metadata.json b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/metadata.json
new file mode 100644
index 0000000..3f369d3
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/metadata.json
@@ -0,0 +1 @@
+{"classifiers": ["License :: OSI Approved :: BSD License", "Intended Audience :: Developers", "Intended Audience :: End Users/Desktop", "Intended Audience :: System Administrators", "Development Status :: 6 - Mature", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Operating System :: OS Independent", "Topic :: Text Processing :: Filters", "Topic :: Utilities"], "extensions": {"python.commands": {"wrap_console": {"pygmentize": "pygments.cmdline:main"}}, "python.details": {"contacts": [{"email": "georg@python.org", "name": "Georg Brandl", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://pygments.org/"}}, "python.exports": {"console_scripts": {"pygmentize": "pygments.cmdline:main"}}}, "generator": "bdist_wheel (0.29.0)", "keywords": ["syntax", "highlighting"], "license": "BSD License", "metadata_version": "2.0", "name": "Pygments", "platform": "any", "summary": "Pygments is a syntax highlighting package written in Python.", "version": "2.2.0"}
\ No newline at end of file
diff --git a/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/top_level.txt b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/top_level.txt
new file mode 100644
index 0000000..a9f49e0
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Pygments-2.2.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+pygments
diff --git a/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/DESCRIPTION.rst b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/DESCRIPTION.rst
new file mode 100644
index 0000000..3f3c26b
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/DESCRIPTION.rst
@@ -0,0 +1,187 @@
+Unidecode, lossy ASCII transliterations of Unicode text
+=======================================================
+
+It often happens that you have text data in Unicode, but you need to
+represent it in ASCII. For example when integrating with legacy code that
+doesn't support Unicode, or for ease of entry of non-Roman names on a US
+keyboard, or when constructing ASCII machine identifiers from
+human-readable Unicode strings that should still be somewhat intelligible
+(a popular example of this is when making an URL slug from an article
+title).
+
+In most of these examples you could represent Unicode characters as
+`???` or `\\15BA\\15A0\\1610`, to mention two extreme cases. But that's
+nearly useless to someone who actually wants to read what the text says.
+
+What Unidecode provides is a middle road: function `unidecode()` takes
+Unicode data and tries to represent it in ASCII characters (i.e., the
+universally displayable characters between 0x00 and 0x7F), where the
+compromises taken when mapping between two character sets are chosen to be
+near what a human with a US keyboard would choose.
+
+The quality of resulting ASCII representation varies. For languages of
+western origin it should be between perfect and good. On the other hand
+transliteration (i.e., conveying, in Roman letters, the pronunciation
+expressed by the text in some other writing system) of languages like
+Chinese, Japanese or Korean is a very complex issue and this library does
+not even attempt to address it. It draws the line at context-free
+character-by-character mapping. So a good rule of thumb is that the further
+the script you are transliterating is from Latin alphabet, the worse the
+transliteration will be.
+
+Note that this module generally produces better results than simply
+stripping accents from characters (which can be done in Python with
+built-in functions). It is based on hand-tuned character mappings that for
+example also contain ASCII approximations for symbols and non-Latin
+alphabets.
+
+This is a Python port of `Text::Unidecode` Perl module by
+Sean M. Burke .
+
+
+Module content
+--------------
+
+The module exports a function that takes an Unicode object (Python 2.x) or
+string (Python 3.x) and returns a string (that can be encoded to ASCII bytes in
+Python 3.x)::
+
+ >>> from unidecode import unidecode
+ >>> unidecode(u'ko\u017eu\u0161\u010dek')
+ 'kozuscek'
+ >>> unidecode(u'30 \U0001d5c4\U0001d5c6/\U0001d5c1')
+ '30 km/h'
+ >>> unidecode(u"\u5317\u4EB0")
+ 'Bei Jing '
+
+A utility is also included that allows you to transliterate text from the
+command line in several ways. Reading from standard input::
+
+ $ echo hello | unidecode
+ hello
+
+from a command line argument::
+
+ $ unidecode -c hello
+ hello
+
+or from a file::
+
+ $ unidecode hello.txt
+ hello
+
+The default encoding used by the utility depends on your system locale. You can
+specify another encoding with the `-e` argument. See `unidecode --help` for a
+full list of available options.
+
+Requirements
+------------
+
+Nothing except Python itself.
+
+You need a Python build with "wide" Unicode characters (also called "UCS-4
+build") in order for unidecode to work correctly with characters outside of
+Basic Multilingual Plane (BMP). Common characters outside BMP are bold, italic,
+script, etc. variants of the Latin alphabet intended for mathematical notation.
+Surrogate pair encoding of "narrow" builds is not supported in unidecode.
+
+If your Python build supports "wide" Unicode the following expression will
+return True::
+
+ >>> import sys
+ >>> sys.maxunicode > 0xffff
+ True
+
+See PEP 261 for details regarding support for "wide" Unicode characters in
+Python.
+
+
+Installation
+------------
+
+To install the latest version of Unidecode from the Python package index, use
+these commands::
+
+ $ pip install unidecode
+
+To install Unidecode from the source distribution and run unit tests, use::
+
+ $ python setup.py install
+ $ python setup.py test
+
+
+Performance notes
+-----------------
+
+By default, `unidecode` optimizes for the use case where most of the strings
+passed to it are already ASCII-only and no transliteration is necessary (this
+default might change in future versions).
+
+For performance critical applications, two additional functions are exposed:
+
+`unidecode_expect_ascii` is optimized for ASCII-only inputs (approximately 5
+times faster than `unidecode_expect_nonascii` on 10 character strings, more on
+longer strings), but slightly slower for non-ASCII inputs.
+
+`unidecode_expect_nonascii` takes approximately the same amount of time on
+ASCII and non-ASCII inputs, but is slightly faster for non-ASCII inputs than
+`unidecode_expect_ascii`.
+
+Apart from differences in run time, both functions produce identical results.
+For most users of Unidecode, the difference in performance should be
+negligible.
+
+
+Source
+------
+
+You can get the latest development version of Unidecode with::
+
+ $ git clone https://www.tablix.org/~avian/git/unidecode.git
+
+There is also an official mirror of this repository on GitHub at
+https://github.com/avian2/unidecode
+
+
+Contact
+-------
+
+Please send bug reports, patches and suggestions for Unidecode to
+tomaz.solc@tablix.org.
+
+Alternatively, you can also open a ticket or pull request at
+https://github.com/avian2/unidecode
+
+
+Copyright
+---------
+
+Original character transliteration tables:
+
+Copyright 2001, Sean M. Burke , all rights reserved.
+
+Python code and later additions:
+
+Copyright 2018, Tomaz Solc
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc., 51
+Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. The programs and
+documentation in this dist are distributed in the hope that they will be
+useful, but without any warranty; without even the implied warranty of
+merchantability or fitness for a particular purpose.
+
+..
+ vim: set filetype=rst:
+
+
diff --git a/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/INSTALLER b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/LICENSE.txt b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/LICENSE.txt
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/LICENSE.txt
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ , 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/METADATA b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/METADATA
new file mode 100644
index 0000000..fa5548d
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/METADATA
@@ -0,0 +1,211 @@
+Metadata-Version: 2.0
+Name: Unidecode
+Version: 1.0.22
+Summary: ASCII transliterations of Unicode text
+Home-page: UNKNOWN
+Author: Tomaz Solc
+Author-email: tomaz.solc@tablix.org
+License: GPL
+Platform: UNKNOWN
+Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Text Processing
+Classifier: Topic :: Text Processing :: Filters
+
+Unidecode, lossy ASCII transliterations of Unicode text
+=======================================================
+
+It often happens that you have text data in Unicode, but you need to
+represent it in ASCII. For example when integrating with legacy code that
+doesn't support Unicode, or for ease of entry of non-Roman names on a US
+keyboard, or when constructing ASCII machine identifiers from
+human-readable Unicode strings that should still be somewhat intelligible
+(a popular example of this is when making an URL slug from an article
+title).
+
+In most of these examples you could represent Unicode characters as
+`???` or `\\15BA\\15A0\\1610`, to mention two extreme cases. But that's
+nearly useless to someone who actually wants to read what the text says.
+
+What Unidecode provides is a middle road: function `unidecode()` takes
+Unicode data and tries to represent it in ASCII characters (i.e., the
+universally displayable characters between 0x00 and 0x7F), where the
+compromises taken when mapping between two character sets are chosen to be
+near what a human with a US keyboard would choose.
+
+The quality of resulting ASCII representation varies. For languages of
+western origin it should be between perfect and good. On the other hand
+transliteration (i.e., conveying, in Roman letters, the pronunciation
+expressed by the text in some other writing system) of languages like
+Chinese, Japanese or Korean is a very complex issue and this library does
+not even attempt to address it. It draws the line at context-free
+character-by-character mapping. So a good rule of thumb is that the further
+the script you are transliterating is from Latin alphabet, the worse the
+transliteration will be.
+
+Note that this module generally produces better results than simply
+stripping accents from characters (which can be done in Python with
+built-in functions). It is based on hand-tuned character mappings that for
+example also contain ASCII approximations for symbols and non-Latin
+alphabets.
+
+This is a Python port of `Text::Unidecode` Perl module by
+Sean M. Burke .
+
+
+Module content
+--------------
+
+The module exports a function that takes an Unicode object (Python 2.x) or
+string (Python 3.x) and returns a string (that can be encoded to ASCII bytes in
+Python 3.x)::
+
+ >>> from unidecode import unidecode
+ >>> unidecode(u'ko\u017eu\u0161\u010dek')
+ 'kozuscek'
+ >>> unidecode(u'30 \U0001d5c4\U0001d5c6/\U0001d5c1')
+ '30 km/h'
+ >>> unidecode(u"\u5317\u4EB0")
+ 'Bei Jing '
+
+A utility is also included that allows you to transliterate text from the
+command line in several ways. Reading from standard input::
+
+ $ echo hello | unidecode
+ hello
+
+from a command line argument::
+
+ $ unidecode -c hello
+ hello
+
+or from a file::
+
+ $ unidecode hello.txt
+ hello
+
+The default encoding used by the utility depends on your system locale. You can
+specify another encoding with the `-e` argument. See `unidecode --help` for a
+full list of available options.
+
+Requirements
+------------
+
+Nothing except Python itself.
+
+You need a Python build with "wide" Unicode characters (also called "UCS-4
+build") in order for unidecode to work correctly with characters outside of
+Basic Multilingual Plane (BMP). Common characters outside BMP are bold, italic,
+script, etc. variants of the Latin alphabet intended for mathematical notation.
+Surrogate pair encoding of "narrow" builds is not supported in unidecode.
+
+If your Python build supports "wide" Unicode the following expression will
+return True::
+
+ >>> import sys
+ >>> sys.maxunicode > 0xffff
+ True
+
+See PEP 261 for details regarding support for "wide" Unicode characters in
+Python.
+
+
+Installation
+------------
+
+To install the latest version of Unidecode from the Python package index, use
+these commands::
+
+ $ pip install unidecode
+
+To install Unidecode from the source distribution and run unit tests, use::
+
+ $ python setup.py install
+ $ python setup.py test
+
+
+Performance notes
+-----------------
+
+By default, `unidecode` optimizes for the use case where most of the strings
+passed to it are already ASCII-only and no transliteration is necessary (this
+default might change in future versions).
+
+For performance critical applications, two additional functions are exposed:
+
+`unidecode_expect_ascii` is optimized for ASCII-only inputs (approximately 5
+times faster than `unidecode_expect_nonascii` on 10 character strings, more on
+longer strings), but slightly slower for non-ASCII inputs.
+
+`unidecode_expect_nonascii` takes approximately the same amount of time on
+ASCII and non-ASCII inputs, but is slightly faster for non-ASCII inputs than
+`unidecode_expect_ascii`.
+
+Apart from differences in run time, both functions produce identical results.
+For most users of Unidecode, the difference in performance should be
+negligible.
+
+
+Source
+------
+
+You can get the latest development version of Unidecode with::
+
+ $ git clone https://www.tablix.org/~avian/git/unidecode.git
+
+There is also an official mirror of this repository on GitHub at
+https://github.com/avian2/unidecode
+
+
+Contact
+-------
+
+Please send bug reports, patches and suggestions for Unidecode to
+tomaz.solc@tablix.org.
+
+Alternatively, you can also open a ticket or pull request at
+https://github.com/avian2/unidecode
+
+
+Copyright
+---------
+
+Original character transliteration tables:
+
+Copyright 2001, Sean M. Burke , all rights reserved.
+
+Python code and later additions:
+
+Copyright 2018, Tomaz Solc
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc., 51
+Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. The programs and
+documentation in this dist are distributed in the hope that they will be
+useful, but without any warranty; without even the implied warranty of
+merchantability or fitness for a particular purpose.
+
+..
+ vim: set filetype=rst:
+
+
diff --git a/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/RECORD b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/RECORD
new file mode 100644
index 0000000..df458f0
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/RECORD
@@ -0,0 +1,392 @@
+Unidecode-1.0.22.dist-info/DESCRIPTION.rst,sha256=-F5SUD-b0Ya43g1p_eu1gEm8tO4mIpYWhWTgQKMsIqA,6431
+Unidecode-1.0.22.dist-info/LICENSE.txt,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
+Unidecode-1.0.22.dist-info/METADATA,sha256=rKmVrG0-P7Uc9CuCCVDzVcFUKRO05pBxpUuJAd6lVG0,7391
+Unidecode-1.0.22.dist-info/RECORD,,
+Unidecode-1.0.22.dist-info/WHEEL,sha256=kdsN-5OJAZIiHN-iO4Rhl82KyS0bDWf4uBwMbkNafr8,110
+Unidecode-1.0.22.dist-info/entry_points.txt,sha256=ItDp7W6CoSJQAKqdiUkzmecugvKvWzjfN7MMK52GM10,51
+Unidecode-1.0.22.dist-info/metadata.json,sha256=DkonES6HR5OcV_EwAawVVXOuDXcDpPxDyaOblLTrvgo,1178
+Unidecode-1.0.22.dist-info/top_level.txt,sha256=4uYNG2l04s0dm0mEQmPLo2zrjLbhLPKUesLr2dOTdpo,10
+unidecode/__init__.py,sha256=kBAGxkNFR1A9PFMjN4wyfUQpQ8jteUW3xXg-B5SsEf8,3095
+unidecode/util.py,sha256=hR86tO0ggBQdrVhi9bLcX92Q53NeO3ZDCLe6xhYtj8w,1790
+unidecode/x000.py,sha256=dxTi7LcLTlbF3ZGGk9xPjx8sx2M6qPBIs2b5RS2DVNY,3041
+unidecode/x001.py,sha256=ylHh3UVaPtibVuUEEWvdSeDFK0OXrWt4-LnxAgYD6qo,3891
+unidecode/x002.py,sha256=Hoks5fu8uyhwDuJt93BiC6iYv_HVY4bIvKklyAtBoFI,3889
+unidecode/x003.py,sha256=UdskMuqktghKcjLeegwYkyfhDH3lgi_G5rr7ID5W0-s,3875
+unidecode/x004.py,sha256=85Sh4Rem7dRS7kIx6yGQqA7U7MxqCD40DGoqaob9MzI,4071
+unidecode/x005.py,sha256=fxbl8HxRujaQRuiH-09LR5sCQaWzDgvnwwIJ5X8ephA,4013
+unidecode/x006.py,sha256=4fZWzc5DGZVFD1zvHtkywMHykQTiFgA7M6LMowpaGI0,3961
+unidecode/x007.py,sha256=k2kkTXdbKqMFOQy2TlGmRnoRYMCOvqTfjusGV76u9SE,4122
+unidecode/x009.py,sha256=aZrWpXwsS2yIyO8oZIIN3Uo4j0XdpaJq5VGdCu3e8cc,4075
+unidecode/x00a.py,sha256=UrXdvZ-qVMfWRSRW8HwrxfNXB-Jp0lgW2iPs6roZXz4,4121
+unidecode/x00b.py,sha256=MjqdSyCxXLZ6wUrgeS8A-wop9S78EEK9e-BJ4HpAqLA,4132
+unidecode/x00c.py,sha256=y-y0RqVolrIBfsj1DtT_rQUbmxxjoKnWlZDMPPC6Om4,4102
+unidecode/x00d.py,sha256=OVfHvb44WS_aMXSWrZt-693xJ70L4sepcyJFIkl23TY,4121
+unidecode/x00e.py,sha256=9N9w09V225dx1-e8w0HRaIoD66mpDhHgoQ-RlPZznmA,4092
+unidecode/x00f.py,sha256=Vak8Z6Dy6ucFysFSNBt24ZE4h2ZSXQcBLiWCSC27bSA,4061
+unidecode/x010.py,sha256=9vck5PTRcz5Q64F_2dIMQoPGEFTHWjvwq7ZE90nvOK4,4110
+unidecode/x011.py,sha256=8EN-PZS-ythtQlU3HZYNkGePm3oWFzSOgOorkeQUBV4,4135
+unidecode/x012.py,sha256=2onQfsL5e7X4RB9DKehMUcG81gaSav4UwGalXulnxOE,4318
+unidecode/x013.py,sha256=Nl9CnUVkJkXBux5TEia5Vq1nPjTICUUyT77GhkChbrQ,4247
+unidecode/x014.py,sha256=CkrW473dLq_AllpcK8teAUQSeglML_MZ6t9TsK5g9wE,4300
+unidecode/x015.py,sha256=TB6O4l2qPxbmF2dejlxXLqX5tTfjl95cMYx1770GHs0,4329
+unidecode/x016.py,sha256=M9kiUT0ScE5acj-tkomNZ0eND1lvau0i6tJOWepU1FA,4140
+unidecode/x017.py,sha256=nsZP_7vWKOTYY8LOE535g67qwwaMdGblZ-79MbfTNX8,4190
+unidecode/x018.py,sha256=_qBETpive71IdV6nD0md6KaSHaxaSpPUsTTHtVlE4KM,4099
+unidecode/x01d.py,sha256=fzetc6KyWxK7rYl4XARhLKIoG0u6a7y39ooze17G9aw,3608
+unidecode/x01e.py,sha256=oVdWd4v85k-Slc3V0i2NZ4i5G866X6Qw7bKXJDmbXig,3853
+unidecode/x01f.py,sha256=pG-fs1iD7O3vSwIx6Ibz5MhpZI_NsQWEDlHiRpxgZC0,3899
+unidecode/x020.py,sha256=k9PWwEJiWl7yUFTVR89cl2XuYqWDEiVfrXvSqOaeJH4,4010
+unidecode/x021.py,sha256=Ns9H51Q51tDB-mRSERyMN1y2EiE7UPQx9K3BxwaFrQs,4012
+unidecode/x022.py,sha256=OzIXC-VMfUskTtEe5_m3zpmgtKtJSDY0XBZ5C0codi8,4329
+unidecode/x023.py,sha256=FFgn4hJ7Q4SbsxFtHUa8SuQ0VBJ9hkod5QTWaMmkk9U,4341
+unidecode/x024.py,sha256=pMZqUxkwfgz9n9NXpUaAaNr-p9ACNCyfTQKo2PFJ11w,4049
+unidecode/x025.py,sha256=-dvBV3byxxngHQcQx7Jjt9dwtehBaQwRoCnX3ZAzWe0,3871
+unidecode/x026.py,sha256=N7i11hEwuiW9mSEp0Dk4Aa9iIsHsxAYhLAplqZnUMs0,4020
+unidecode/x027.py,sha256=wZ1l328qv5BWbk-FPr34ayyJ4rWQG3EQIsPxd7GilFg,3783
+unidecode/x028.py,sha256=FZPCZ9w3N3WOI42h2gHEQgVOAlLBNTZjMu_KQQkIMdk,5069
+unidecode/x029.py,sha256=TlYRf7ym0R-L7DncmX4RNZX5kKctvGFLbtu8GPkxqpE,3584
+unidecode/x02a.py,sha256=WD3uhv1sNhQB45ugiKUk4Btj3aj8qcH_kpwrV3jRWJw,3589
+unidecode/x02c.py,sha256=zRG2Elxv5SlYrBpJb1NUz7WsJOFAkzwSi991cMyhnJs,3596
+unidecode/x02e.py,sha256=_ntc-U9pwAkGQkFC3zdQgJNUZSv9W4115ouIbINGyw4,4461
+unidecode/x02f.py,sha256=9cxem6MFm7acjUDcmYLk9lbxEyfP2xMjfE3c-6PUEbg,4572
+unidecode/x030.py,sha256=aHVHcoSl5FcxX9QQALaW5n1zYJ0Ymap-wgWuzojXLyY,4037
+unidecode/x031.py,sha256=vRlzxBKPo5s3ZDpUojrXelZKjTEwR8fsnm3vUeB5bp8,4125
+unidecode/x032.py,sha256=BSZV_t8jVsWX_RsoqajWAqigQaHh3wXmoMG5wUZtgos,4485
+unidecode/x033.py,sha256=ZD2k8XlsdicjcaptuQrRt0nXGAFRs7SnF8RidIR9Alg,4573
+unidecode/x04d.py,sha256=d5K-HbR3Gg1VBdMk1GO6PJdoseRRIV5ef4aQbslnsBo,4528
+unidecode/x04e.py,sha256=z04XMxt3y016alep4Xg8Zjh4cvBj6CddjD9Qv6sr6v4,4646
+unidecode/x04f.py,sha256=zEf_S6bDF755svnPRWyreVf2Q4SekYMxIpGf1Jb2alc,4607
+unidecode/x050.py,sha256=MNhEf7TRcQ2CdgkMcFloEGSbTtrsXK-6r6Ru7HDG6hU,4682
+unidecode/x051.py,sha256=VY0jC10xdU7bm21Cig5omd7L-4hiSk_rk2UTR_yTF3g,4685
+unidecode/x052.py,sha256=a09eo_5pL6jpU9TW-zG2w2iXTYp6awtQ4OxGnLdcwKg,4654
+unidecode/x053.py,sha256=so5U-CQ5jRbp7AYZZPCdmkdnNtfNn_-Nl_761eBMtIU,4616
+unidecode/x054.py,sha256=Htu6ZFPTTyBHUU1Ia-mc7Y3Dy4j1cp-fRwag0HGwmwk,4583
+unidecode/x055.py,sha256=WzgwsrHInE1H-O519FOIybJpofzdyfu7w5NZ5I2OtQI,4599
+unidecode/x056.py,sha256=t4ZVJlxic1vcqhrypLWRd3LyIfEuWoPIz343pCrfW7k,4615
+unidecode/x057.py,sha256=ndJuRj4TnvSe6aLX-yzDYHnWEl1JfqA6HnQvzsf2Fyo,4631
+unidecode/x058.py,sha256=nkaS7T1PVlhKlxVd-WrDw4Gx14mciLEZQrUt-9NpkD0,4678
+unidecode/x059.py,sha256=9wAKGpczWuyqMb89px7Ldy1McHecDXd8RMJ7lXwcBCU,4644
+unidecode/x05a.py,sha256=F150z3X248dkDgTq-0lyL-bLRqZZd7U0mkUI6PJRwwM,4636
+unidecode/x05b.py,sha256=LeJj8prX04qvLHFaeG-b2YE9LqIbnUec6pFD-7E918c,4668
+unidecode/x05c.py,sha256=Citt0KhdqvWkErFdpeSyg6x5CviH1opVRPSD6eBWjsA,4612
+unidecode/x05d.py,sha256=w1vKjN5RWPiwP535FCiqakz1IbeB4MGe2ANVM_bcdq4,4670
+unidecode/x05e.py,sha256=6Z7gnAXq9tVwvFIwh632oLQuEiHz1atcqIDREeoqldM,4668
+unidecode/x05f.py,sha256=Ho5tdX7JErho7LjeVCxf29XlWeEpDt5RUJC3nbw2j8M,4660
+unidecode/x060.py,sha256=2x6hmUwn_V3icd1kdfzkkDp5iEdmij7zpUWizfVIE7Q,4642
+unidecode/x061.py,sha256=hwSoPcP4PLy5zPITLdSVaYGwt_fIH9kJPpshKJZi-LA,4662
+unidecode/x062.py,sha256=rH9eYXX_t-Z4-pOy9-lyVm68zXt114X3lAIBI5JG_Qs,4620
+unidecode/x063.py,sha256=n8aXYOPurrEbBzz4DAl-tFOFiqMJ-r1Yt3WpM3ZGTq0,4656
+unidecode/x064.py,sha256=uBebjkUmgQVzK0tKWjxLZwQ1oC9KMEppv0W6caB8v1g,4655
+unidecode/x065.py,sha256=cFnHSLoNwMG6PJvxWWeWShSkHoB9IYTS2LJCc8W0l4I,4638
+unidecode/x066.py,sha256=gV2vx0TqIA44PBOzF02wetf3dxXcXmg8Jr2CtzxMDFU,4677
+unidecode/x067.py,sha256=9ck2UFSv8UL3c0RHPTdV4Rzq7ogZVedwsMAYhGE1lmM,4635
+unidecode/x068.py,sha256=aTAAeHLr5-VnMqNF0h9KC4tFOusV9PpWdywp7xllAA0,4674
+unidecode/x069.py,sha256=8_VMN2vGqNAPrP8iPxICRI9PN81Hts21FM1A4n1_7to,4673
+unidecode/x06a.py,sha256=e7ahJ-j5YvomZvQcIxfAMbHgijbKncYoTN9TakDQqig,4674
+unidecode/x06b.py,sha256=lBRWVhTMJPBKWAyAT23PueHtw9wXTuUG9S2UVWjosr4,4608
+unidecode/x06c.py,sha256=i8xXjlNwhXxvrHdnTbdy-jTfml_fD0uFURctA1BQKk0,4643
+unidecode/x06d.py,sha256=BvgJd7TNj6cL6I5bjPXngi3tJPo0YfhISCHSXvfmsTk,4651
+unidecode/x06e.py,sha256=jZ6VeQbASYGtx0QXySzZzRQf_LqtPAU6jhTo3U_nFTU,4640
+unidecode/x06f.py,sha256=W0A95toB7w7pLrohCaet_d0-S3V84fjzTKgZ6vhUtok,4650
+unidecode/x070.py,sha256=lVM1qXUltqIrKIi0WPH1F5Feuq4M007nm3lOkR_EB2s,4693
+unidecode/x071.py,sha256=v2V3WNKPVEhuJ_RX6rZA45rFIukgMCJ8rqPoUwj05zc,4670
+unidecode/x072.py,sha256=NhOkJEqApO9shkYgwdWVarVUDmWailI4N1vNiLGkOSM,4659
+unidecode/x073.py,sha256=loYg-ZrK1rdy2CkbQfd4qydW8lCeiNywEzT6gLTN-GI,4646
+unidecode/x074.py,sha256=FLIumUZcrCy9Y6eXL5BLpa_hE5HMGbPo-PWtFBh-rBs,4696
+unidecode/x075.py,sha256=P3SrhI5BQ5sJ66hyu_LWDONpuzLZJBKsl7f-A37sJXc,4675
+unidecode/x076.py,sha256=3enaJAMy951MK_yBnhJiOmoTdzU0eJ2uEgoRgZRTUn0,4639
+unidecode/x077.py,sha256=XH_TjHspGSnu4v3qgNOqFNMvZKZcA-HH6q_GWB48Cos,4675
+unidecode/x078.py,sha256=L9XOo8UH_x-prQC_edArnTs-j2asZR940sLaPST2iy0,4648
+unidecode/x079.py,sha256=6e29mgaXPmtMrU8_QRfmZpWZW8o-yEeeIOgAHOoepo8,4602
+unidecode/x07a.py,sha256=fMACYuAsIumgE8XkYnM2795HtFA6weH4YLn7jgJLbbw,4669
+unidecode/x07b.py,sha256=UH8ZXsR11pINz0AaxJ8etTTyaXiCawjuLcctzp4FwZc,4669
+unidecode/x07c.py,sha256=fpqGGsF0-rEVxBeVFf0RM8fjweUlg-9UduAtxAjL5vc,4663
+unidecode/x07d.py,sha256=EKFrTQTNFLGnsm3qI76ALxrxGCcDuyEbapi9j9jy1B4,4678
+unidecode/x07e.py,sha256=n2OG5xe8I-V0pn98Q2E-7PbXSQQI72ozNNUXFnMZHvM,4682
+unidecode/x07f.py,sha256=g455qjG3LBu9ujuuTt5xrRn2djK_iVXAJ4dUVl-bYfs,4664
+unidecode/x080.py,sha256=Fuqy0RgnvfvFFdmGiaHwK2B60UCU5Aw4fyF79kBfhr8,4651
+unidecode/x081.py,sha256=rQg3Hjqo61bEKCpb7TybHDLv2Hgu-_ghKdW6xk9xOhU,4673
+unidecode/x082.py,sha256=sRjOiGrYy2RtqqH_xQdL6_i17I-wJZI6ag7404mL4M8,4649
+unidecode/x083.py,sha256=8hCxGV2o1kFA6hMFvk4Ici_QKynDCYjDWjzCuMyfmHI,4643
+unidecode/x084.py,sha256=jIDgDPhwssUcLgA7N0ZINrB_qZn1P4C7lHyvP7yKA6o,4646
+unidecode/x085.py,sha256=5063XP5F72OEYuqjETqFlN_7IaU1A0feVuvIup9R0rI,4636
+unidecode/x086.py,sha256=ovNLdMRRHm4jngDGXmwG66zZH6l-V-uMtoYnXB_W_QY,4614
+unidecode/x087.py,sha256=-VmLJWGVMGF9BxYD8VcTc8TS83W27qcERuycKCfpLBc,4649
+unidecode/x088.py,sha256=E63aAVUF0B1f-5XL7fOUWqXL2juUJLU9TwO_LHKvd2Q,4645
+unidecode/x089.py,sha256=mrKWneiJ2hIFkM4cu4bU0IQMvTRWgXZ8rsDW575jp9A,4628
+unidecode/x08a.py,sha256=NjMp9ck824PXG2gcJXfi_9oQCFgXhhiallO3bYCtXCE,4647
+unidecode/x08b.py,sha256=W1kAtliqhm5x28Kxc6kUrjzqo-xc_HmYY0BjHhEV2x4,4643
+unidecode/x08c.py,sha256=aDhwugSrvtUhDdwbACfjS0EkBqGojwny-qbrQRJfPhA,4630
+unidecode/x08d.py,sha256=oyydBTJng0ip79lUTBHoTIqGjxmHTb0twkPZqE7LxeU,4636
+unidecode/x08e.py,sha256=w-FysLX-LgmggEuhmPZjyT67-i4_EB8Hx44i_X_Q3Nc,4659
+unidecode/x08f.py,sha256=adygkkCQn4W6YhJUknf2O-2eM_LzH1LfjjpgenbPh80,4651
+unidecode/x090.py,sha256=j-5qrCDDHYKJnbHL5A_fm5ISrdFVgDR5bXQbP18G-14,4631
+unidecode/x091.py,sha256=S8jlVjjPNLPCsSXK8qKXqGGoTLj-LWje5J-f-2AAEXY,4655
+unidecode/x092.py,sha256=uSF8NVYh_UGJE2pcl4JrVU2Prb-T2crGLCE4XQe7DfQ,4648
+unidecode/x093.py,sha256=oMiZM1VfvfKnwVKSJh28iynWJG8iQtKu_1zsrbPdPNs,4666
+unidecode/x094.py,sha256=MShhNv4E9bj9jmQEtWHi_8ZjeS4p2Iz6j3j9kJb5rK0,4661
+unidecode/x095.py,sha256=BQ1R6QwhWjC_Eb7zIbWP0A2ro7bI-t6fTAQWLFMmzAM,4671
+unidecode/x096.py,sha256=N9hLQrZhbTXC9boxDcWu3WESTIB6En82kJkBY-6qBRI,4610
+unidecode/x097.py,sha256=K4waHuw6tNchmcY7Glc-Su6cTG3-iF_X_egYuG-E4fA,4643
+unidecode/x098.py,sha256=CFFcb5gpK7FBqPsFwRoLP0YcYFJBGgh3l-Rf4PzXbjc,4645
+unidecode/x099.py,sha256=e9w1-tsa3tCYYQXn71Ey1bg_n2UYudMQ0y-zSSCdajE,4629
+unidecode/x09a.py,sha256=Z8pQsTc62CWgm0JPnj3kokKKf9_qfzRpo0u5iH61CaE,4623
+unidecode/x09b.py,sha256=piSZ2AAK5GavHJEa8qwI_lrldSSgNhxYvLua0Au_1aA,4655
+unidecode/x09c.py,sha256=NveMhN85_Cm4H1cnfHDTcnSj675MOVBq9Lkjpw3YxA0,4659
+unidecode/x09d.py,sha256=2Sj376QIs8rJ7VDrPW5RELhkJ8LI5JI4NRbFcl4DXlE,4632
+unidecode/x09e.py,sha256=z1bF6AML_d20dQm9HD7YBrnKqTQVjeTTI999hcLEe0M,4615
+unidecode/x09f.py,sha256=T-pS5hli39rA1GDDqZYfyHRupPALqklPXA-1i8pgc1I,4509
+unidecode/x0a0.py,sha256=EpopPuuocybgCcpX19Ii-udqsPXJjSces3360lqJ8vs,4428
+unidecode/x0a1.py,sha256=0hvF77d5E640SujjdHVqy5gMUH85gEdOv80eRvCEAGM,4469
+unidecode/x0a2.py,sha256=9Icpfk_ElebYd_xN09OMziFrpAGPXEUNVmawpnhbBaQ,4503
+unidecode/x0a3.py,sha256=G1lPrnCqYz0s4wsSa1qM0WgrZBWO_beRk3AgK0iVZLA,4521
+unidecode/x0a4.py,sha256=vS-wPpkfMmwRJjXTBYM4BGpzBfDoKWMadNNWaTPYcpI,4437
+unidecode/x0ac.py,sha256=wj7hl88VlCdc_eGpOL4m4CBJILyQqd9atObC5Xvd0aA,4709
+unidecode/x0ad.py,sha256=Rz5rn0fM-CqRjaN4TvSq_1StAQdyAF2WX3cUvcQHaWU,4766
+unidecode/x0ae.py,sha256=jNIBVB-Pw2ZNihAeyWbDIEq9Yt9zlhdfGylfvAaxUks,4875
+unidecode/x0af.py,sha256=Am5YC8Zfrun5NUKxU6LrU2-d5GgkGSBs7fZt2rqSi74,5012
+unidecode/x0b0.py,sha256=1bgHerCDAqIcJHYeGddJjJfRWiHCKtU2B0J-XGvcbbc,4853
+unidecode/x0b1.py,sha256=Six-lzGdvgJx4YsIa0lTusnBEV1zbCKQCquq17TDJoQ,4746
+unidecode/x0b2.py,sha256=HQDbmglNi4QfiRSGucUclgq_4FGpRjbJkWU1JTLAFGc,4680
+unidecode/x0b3.py,sha256=1lqxghVZiiStOAx1IG_vc1zZTXrAa7Z__QY6ZWvo2aA,4741
+unidecode/x0b4.py,sha256=V6BNSTxpyP8VuqF7x5z7bpF3MQAkwZfKtEu6NFr_vSg,4762
+unidecode/x0b5.py,sha256=9NVd2hNLyRlLceVlznba1dreqBGeKU_0gzmkgAw0gyg,4919
+unidecode/x0b6.py,sha256=V_vRsB0GICu9hqhO4pnbPWreDSevJ3bbmLRJkuQUxnE,4996
+unidecode/x0b7.py,sha256=CwBaCBICyVagnFjUpkwabuDvBJw7gAeqkSRpfBAVv8s,4833
+unidecode/x0b8.py,sha256=xYp-xy2LIwq95OWyS9vYMc_Z5od9dud0W1dxeg4P_Jk,4714
+unidecode/x0b9.py,sha256=z3hKNzBq_MeK9V3AyQzaY58cgi0-VGOsLk3-UFmszLQ,4704
+unidecode/x0ba.py,sha256=4gubifoBeJUUrwXEI4litJygekufEycmWDLrJ-Qvs14,4765
+unidecode/x0bb.py,sha256=bsCTABUdC6yTn8_0vhYe5jRP1z_BoAdificB8Y1c1hA,4730
+unidecode/x0bc.py,sha256=AhQvAz7yHlbQ_4c2KOIisq07eZJ5JQn6cV8I31oT9kg,4707
+unidecode/x0bd.py,sha256=IGtyVxIUr1mU3hokn6iUDJhXZezQozVvfWOyf4Pa5dI,4752
+unidecode/x0be.py,sha256=1D-hXu3p3wvOnGVMjEqVsrltYe7UuSwit2yqN5eFizc,4849
+unidecode/x0bf.py,sha256=NkEXqr2ER3BNFkTasDV9CHnkRBuX_Ao5OHGv_NgKAew,5010
+unidecode/x0c0.py,sha256=zDlHpyM0omza5TqGLb8Rhl7Wd-LlV1AjvH_xdnEnNFw,4856
+unidecode/x0c1.py,sha256=AC6xJyx9UblKAGNqGN7AH2Idb3_3vbc-I5U0Myig5fA,4765
+unidecode/x0c2.py,sha256=siRYLA8Cv9Z8XsRp3WQOBdRrPkjJOuEh8z1-3SMXOzQ,4710
+unidecode/x0c3.py,sha256=hlAFe6lsz0aLMixlpeFjV4I-WTIiA3B2BU58yGlTwRg,4975
+unidecode/x0c4.py,sha256=z3xZwSkf5ru1FCdBMHOr5fyglzVdyPhQVtWjq9xInsQ,5024
+unidecode/x0c5.py,sha256=F-DR0eVMRkemOnNXOtDjI5i6gW9136XLmWM_yMVvc84,4581
+unidecode/x0c6.py,sha256=7p_jMrHf3WUa_zANms-RGVN1bAeshgWLkC16_VcSawA,4490
+unidecode/x0c7.py,sha256=5eOAq4jFsPZ-zKO7lHzAGj_EvXdaMC4Kud7gvE-B7Tg,4564
+unidecode/x0c8.py,sha256=wltKvhBgn51jULzwUnEbmyDuK9JvQpQee0uTKK42-20,4733
+unidecode/x0c9.py,sha256=GoARON07wCoHN2wRHb5fvzqE9L3Yme2hKeciynUIAIk,4722
+unidecode/x0ca.py,sha256=BsBZTNj3npIkdo3L9pSEX7XvDT68KV7wFtOOwyEb2So,5007
+unidecode/x0cb.py,sha256=8T7vnJMRmYGyySYthMWz0bgN-MremktGImjejodFeMo,5012
+unidecode/x0cc.py,sha256=GKoHN-4vL4Y3EL42G0xbN74Tgspew1oMvxQtsIa3ess,4749
+unidecode/x0cd.py,sha256=7sZ05OjugbaombMRDYOVxgstZbXMcuX5kHFheKv4W2E,4738
+unidecode/x0ce.py,sha256=mOEHFrsAwIvcTnh7OKVK5qbuXUXHfJOR7D4FtXsQmao,4708
+unidecode/x0cf.py,sha256=H9PeYcbOG68F_yc7zsELUuN05ANfFNOUX-e3-gzx7Ow,4713
+unidecode/x0d0.py,sha256=eULqcGHPmaoEdl0EwRB5wWSu8M43bp4HoFo5gGljacg,4706
+unidecode/x0d1.py,sha256=BClLDAjPgsAX6MJCsuHfmfuhH9qfzUy_vb-d9zBs3Oc,4767
+unidecode/x0d2.py,sha256=e74nqGo4E4sF1sy8qBFu2ecWoRfJdoXI1xRFRPqYEz8,4724
+unidecode/x0d3.py,sha256=8-UmvJ3-ILXo9d3GA-ReOE4OfUenL3tVUJYldZ9gHu0,4705
+unidecode/x0d4.py,sha256=fwUmzksoddTKB8fH2rZMxRK3pJtLrxhcrYpHfBauAwE,4758
+unidecode/x0d5.py,sha256=rANSL5ndzLgSgYJQNEw57AfXpicRe7pvHRlKTPb4-QQ,4680
+unidecode/x0d6.py,sha256=fT8_cRzp7y60IIhn87kM9lLehKGAg5wYmfFOwgGp6e0,4765
+unidecode/x0d7.py,sha256=0zY-KFUnKk-CuYpb1zSYj3QdS6UsfZ_lsemOuRSeDwM,4559
+unidecode/x0f9.py,sha256=2PD0_fpDnaFO9ftICjYSOhnjAfBppjsj1TcLIuYjnCI,4567
+unidecode/x0fa.py,sha256=6X94S2GbR6XOwkzx2SYynZzBMHAbRHC9GvW_vXaTDRU,4406
+unidecode/x0fb.py,sha256=qaAP_vF3_-M--wKkyb0DfBjIdnGKuk4GQLxV7fp2-_4,3838
+unidecode/x0fc.py,sha256=KcyQnyv7gxNeVcAnRwQrm4NlabZE3CrnmtLqXj_7te8,3595
+unidecode/x0fd.py,sha256=fq1BGexi73J3QPUwnL4_LZT4uh8mxYqAgMNtofbfVKE,3764
+unidecode/x0fe.py,sha256=mpt-K-jqk36iouLz5HOcthOQJczqsca9aYkEGhJ6Wk4,3825
+unidecode/x0ff.py,sha256=KGE3aIdJCR-3kAVaXOyuY44M-KfCA9UQt4B9AlEJiq0,3983
+unidecode/x1d4.py,sha256=ZS_7TAX87oGtT7b8tIlWcmeHChVINZ4W9jl9wA6JfmU,3839
+unidecode/x1d5.py,sha256=Cuh3bUzoyp8c8lJ7Y_gLmAKQ03XHMCsgTZf3uE2-G2o,3839
+unidecode/x1d6.py,sha256=6fIRGTFODh3kysq10Xr_8EmG6HZuWztLjr4vitkk8OQ,3974
+unidecode/x1d7.py,sha256=jIs9oZBMbSh5OQvPiyUI4aAdji9EbzeOXigTq4Oq_kY,3645
+unidecode/x1f1.py,sha256=rMT7fIIvB6SkwUEbRiSt_lAgr_W06JQ9Rn6pSPVnl0E,3694
+../../../bin/unidecode,sha256=H8x8BjPCPiW0H4dt472qtISShJOOQ145I8WitW7srwY,274
+Unidecode-1.0.22.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+unidecode/__pycache__/x0ac.cpython-35.pyc,,
+unidecode/__pycache__/x081.cpython-35.pyc,,
+unidecode/__pycache__/x0cf.cpython-35.pyc,,
+unidecode/__pycache__/x029.cpython-35.pyc,,
+unidecode/__pycache__/x054.cpython-35.pyc,,
+unidecode/__pycache__/x02c.cpython-35.pyc,,
+unidecode/__pycache__/x09d.cpython-35.pyc,,
+unidecode/__pycache__/x0c3.cpython-35.pyc,,
+unidecode/__pycache__/x007.cpython-35.pyc,,
+unidecode/__pycache__/x0bc.cpython-35.pyc,,
+unidecode/__pycache__/x009.cpython-35.pyc,,
+unidecode/__pycache__/x09f.cpython-35.pyc,,
+unidecode/__pycache__/__init__.cpython-35.pyc,,
+unidecode/__pycache__/x0ae.cpython-35.pyc,,
+unidecode/__pycache__/x0b1.cpython-35.pyc,,
+unidecode/__pycache__/x003.cpython-35.pyc,,
+unidecode/__pycache__/x057.cpython-35.pyc,,
+unidecode/__pycache__/x071.cpython-35.pyc,,
+unidecode/__pycache__/x099.cpython-35.pyc,,
+unidecode/__pycache__/x032.cpython-35.pyc,,
+unidecode/__pycache__/x090.cpython-35.pyc,,
+unidecode/__pycache__/x05b.cpython-35.pyc,,
+unidecode/__pycache__/x0a3.cpython-35.pyc,,
+unidecode/__pycache__/x017.cpython-35.pyc,,
+unidecode/__pycache__/x09e.cpython-35.pyc,,
+unidecode/__pycache__/x0ca.cpython-35.pyc,,
+unidecode/__pycache__/x05a.cpython-35.pyc,,
+unidecode/__pycache__/x08a.cpython-35.pyc,,
+unidecode/__pycache__/x09c.cpython-35.pyc,,
+unidecode/__pycache__/x09a.cpython-35.pyc,,
+unidecode/__pycache__/x069.cpython-35.pyc,,
+unidecode/__pycache__/x0fe.cpython-35.pyc,,
+unidecode/__pycache__/util.cpython-35.pyc,,
+unidecode/__pycache__/x0f9.cpython-35.pyc,,
+unidecode/__pycache__/x0ce.cpython-35.pyc,,
+unidecode/__pycache__/x027.cpython-35.pyc,,
+unidecode/__pycache__/x0b6.cpython-35.pyc,,
+unidecode/__pycache__/x022.cpython-35.pyc,,
+unidecode/__pycache__/x075.cpython-35.pyc,,
+unidecode/__pycache__/x05d.cpython-35.pyc,,
+unidecode/__pycache__/x010.cpython-35.pyc,,
+unidecode/__pycache__/x0c9.cpython-35.pyc,,
+unidecode/__pycache__/x089.cpython-35.pyc,,
+unidecode/__pycache__/x097.cpython-35.pyc,,
+unidecode/__pycache__/x01e.cpython-35.pyc,,
+unidecode/__pycache__/x096.cpython-35.pyc,,
+unidecode/__pycache__/x0cc.cpython-35.pyc,,
+unidecode/__pycache__/x0b3.cpython-35.pyc,,
+unidecode/__pycache__/x094.cpython-35.pyc,,
+unidecode/__pycache__/x0d1.cpython-35.pyc,,
+unidecode/__pycache__/x0c4.cpython-35.pyc,,
+unidecode/__pycache__/x026.cpython-35.pyc,,
+unidecode/__pycache__/x0a1.cpython-35.pyc,,
+unidecode/__pycache__/x0af.cpython-35.pyc,,
+unidecode/__pycache__/x04e.cpython-35.pyc,,
+unidecode/__pycache__/x083.cpython-35.pyc,,
+unidecode/__pycache__/x0b0.cpython-35.pyc,,
+unidecode/__pycache__/x0b2.cpython-35.pyc,,
+unidecode/__pycache__/x07d.cpython-35.pyc,,
+unidecode/__pycache__/x0cb.cpython-35.pyc,,
+unidecode/__pycache__/x0d5.cpython-35.pyc,,
+unidecode/__pycache__/x016.cpython-35.pyc,,
+unidecode/__pycache__/x028.cpython-35.pyc,,
+unidecode/__pycache__/x08e.cpython-35.pyc,,
+unidecode/__pycache__/x1d6.cpython-35.pyc,,
+unidecode/__pycache__/x021.cpython-35.pyc,,
+unidecode/__pycache__/x025.cpython-35.pyc,,
+unidecode/__pycache__/x0b7.cpython-35.pyc,,
+unidecode/__pycache__/x05c.cpython-35.pyc,,
+unidecode/__pycache__/x077.cpython-35.pyc,,
+unidecode/__pycache__/x012.cpython-35.pyc,,
+unidecode/__pycache__/x0ba.cpython-35.pyc,,
+unidecode/__pycache__/x091.cpython-35.pyc,,
+unidecode/__pycache__/x00e.cpython-35.pyc,,
+unidecode/__pycache__/x09b.cpython-35.pyc,,
+unidecode/__pycache__/x093.cpython-35.pyc,,
+unidecode/__pycache__/x086.cpython-35.pyc,,
+unidecode/__pycache__/x082.cpython-35.pyc,,
+unidecode/__pycache__/x098.cpython-35.pyc,,
+unidecode/__pycache__/x000.cpython-35.pyc,,
+unidecode/__pycache__/x07f.cpython-35.pyc,,
+unidecode/__pycache__/x0ad.cpython-35.pyc,,
+unidecode/__pycache__/x07a.cpython-35.pyc,,
+unidecode/__pycache__/x0a4.cpython-35.pyc,,
+unidecode/__pycache__/x0c6.cpython-35.pyc,,
+unidecode/__pycache__/x005.cpython-35.pyc,,
+unidecode/__pycache__/x04d.cpython-35.pyc,,
+unidecode/__pycache__/x0cd.cpython-35.pyc,,
+unidecode/__pycache__/x00d.cpython-35.pyc,,
+unidecode/__pycache__/x063.cpython-35.pyc,,
+unidecode/__pycache__/x08f.cpython-35.pyc,,
+unidecode/__pycache__/x068.cpython-35.pyc,,
+unidecode/__pycache__/x0a2.cpython-35.pyc,,
+unidecode/__pycache__/x011.cpython-35.pyc,,
+unidecode/__pycache__/x0c5.cpython-35.pyc,,
+unidecode/__pycache__/x018.cpython-35.pyc,,
+unidecode/__pycache__/x1d5.cpython-35.pyc,,
+unidecode/__pycache__/x031.cpython-35.pyc,,
+unidecode/__pycache__/x084.cpython-35.pyc,,
+unidecode/__pycache__/x067.cpython-35.pyc,,
+unidecode/__pycache__/x0d7.cpython-35.pyc,,
+unidecode/__pycache__/x05e.cpython-35.pyc,,
+unidecode/__pycache__/x0d6.cpython-35.pyc,,
+unidecode/__pycache__/x085.cpython-35.pyc,,
+unidecode/__pycache__/x02a.cpython-35.pyc,,
+unidecode/__pycache__/x07e.cpython-35.pyc,,
+unidecode/__pycache__/x066.cpython-35.pyc,,
+unidecode/__pycache__/x074.cpython-35.pyc,,
+unidecode/__pycache__/x065.cpython-35.pyc,,
+unidecode/__pycache__/x1d7.cpython-35.pyc,,
+unidecode/__pycache__/x01d.cpython-35.pyc,,
+unidecode/__pycache__/x06e.cpython-35.pyc,,
+unidecode/__pycache__/x0fc.cpython-35.pyc,,
+unidecode/__pycache__/x0b9.cpython-35.pyc,,
+unidecode/__pycache__/x07b.cpython-35.pyc,,
+unidecode/__pycache__/x013.cpython-35.pyc,,
+unidecode/__pycache__/x092.cpython-35.pyc,,
+unidecode/__pycache__/x053.cpython-35.pyc,,
+unidecode/__pycache__/x07c.cpython-35.pyc,,
+unidecode/__pycache__/x0bb.cpython-35.pyc,,
+unidecode/__pycache__/x06b.cpython-35.pyc,,
+unidecode/__pycache__/x030.cpython-35.pyc,,
+unidecode/__pycache__/x06a.cpython-35.pyc,,
+unidecode/__pycache__/x01f.cpython-35.pyc,,
+unidecode/__pycache__/x0c8.cpython-35.pyc,,
+unidecode/__pycache__/x0fa.cpython-35.pyc,,
+unidecode/__pycache__/x0c1.cpython-35.pyc,,
+unidecode/__pycache__/x00f.cpython-35.pyc,,
+unidecode/__pycache__/x004.cpython-35.pyc,,
+unidecode/__pycache__/x055.cpython-35.pyc,,
+unidecode/__pycache__/x051.cpython-35.pyc,,
+unidecode/__pycache__/x0b8.cpython-35.pyc,,
+unidecode/__pycache__/x050.cpython-35.pyc,,
+unidecode/__pycache__/x0c7.cpython-35.pyc,,
+unidecode/__pycache__/x0c2.cpython-35.pyc,,
+unidecode/__pycache__/x1f1.cpython-35.pyc,,
+unidecode/__pycache__/x08b.cpython-35.pyc,,
+unidecode/__pycache__/x080.cpython-35.pyc,,
+unidecode/__pycache__/x0c0.cpython-35.pyc,,
+unidecode/__pycache__/x0d3.cpython-35.pyc,,
+unidecode/__pycache__/x070.cpython-35.pyc,,
+unidecode/__pycache__/x060.cpython-35.pyc,,
+unidecode/__pycache__/x08c.cpython-35.pyc,,
+unidecode/__pycache__/x08d.cpython-35.pyc,,
+unidecode/__pycache__/x06f.cpython-35.pyc,,
+unidecode/__pycache__/x02f.cpython-35.pyc,,
+unidecode/__pycache__/x02e.cpython-35.pyc,,
+unidecode/__pycache__/x05f.cpython-35.pyc,,
+unidecode/__pycache__/x0bf.cpython-35.pyc,,
+unidecode/__pycache__/x0b4.cpython-35.pyc,,
+unidecode/__pycache__/x0fd.cpython-35.pyc,,
+unidecode/__pycache__/x062.cpython-35.pyc,,
+unidecode/__pycache__/x0bd.cpython-35.pyc,,
+unidecode/__pycache__/x020.cpython-35.pyc,,
+unidecode/__pycache__/x059.cpython-35.pyc,,
+unidecode/__pycache__/x0be.cpython-35.pyc,,
+unidecode/__pycache__/x015.cpython-35.pyc,,
+unidecode/__pycache__/x052.cpython-35.pyc,,
+unidecode/__pycache__/x023.cpython-35.pyc,,
+unidecode/__pycache__/x058.cpython-35.pyc,,
+unidecode/__pycache__/x0d4.cpython-35.pyc,,
+unidecode/__pycache__/x0b5.cpython-35.pyc,,
+unidecode/__pycache__/x079.cpython-35.pyc,,
+unidecode/__pycache__/x002.cpython-35.pyc,,
+unidecode/__pycache__/x072.cpython-35.pyc,,
+unidecode/__pycache__/x1d4.cpython-35.pyc,,
+unidecode/__pycache__/x06c.cpython-35.pyc,,
+unidecode/__pycache__/x078.cpython-35.pyc,,
+unidecode/__pycache__/x076.cpython-35.pyc,,
+unidecode/__pycache__/x064.cpython-35.pyc,,
+unidecode/__pycache__/x0fb.cpython-35.pyc,,
+unidecode/__pycache__/x087.cpython-35.pyc,,
+unidecode/__pycache__/x088.cpython-35.pyc,,
+unidecode/__pycache__/x0d2.cpython-35.pyc,,
+unidecode/__pycache__/x061.cpython-35.pyc,,
+unidecode/__pycache__/x0a0.cpython-35.pyc,,
+unidecode/__pycache__/x00b.cpython-35.pyc,,
+unidecode/__pycache__/x006.cpython-35.pyc,,
+unidecode/__pycache__/x0d0.cpython-35.pyc,,
+unidecode/__pycache__/x033.cpython-35.pyc,,
+unidecode/__pycache__/x0ff.cpython-35.pyc,,
+unidecode/__pycache__/x00a.cpython-35.pyc,,
+unidecode/__pycache__/x095.cpython-35.pyc,,
+unidecode/__pycache__/x056.cpython-35.pyc,,
+unidecode/__pycache__/x001.cpython-35.pyc,,
+unidecode/__pycache__/x024.cpython-35.pyc,,
+unidecode/__pycache__/x04f.cpython-35.pyc,,
+unidecode/__pycache__/x00c.cpython-35.pyc,,
+unidecode/__pycache__/x014.cpython-35.pyc,,
+unidecode/__pycache__/x06d.cpython-35.pyc,,
+unidecode/__pycache__/x073.cpython-35.pyc,,
diff --git a/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/WHEEL b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/WHEEL
new file mode 100644
index 0000000..7332a41
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.30.0)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/entry_points.txt b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/entry_points.txt
new file mode 100644
index 0000000..3016afe
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/entry_points.txt
@@ -0,0 +1,3 @@
+[console_scripts]
+unidecode = unidecode.util:main
+
diff --git a/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/metadata.json b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/metadata.json
new file mode 100644
index 0000000..7a6fc65
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/metadata.json
@@ -0,0 +1 @@
+{"classifiers": ["License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Text Processing", "Topic :: Text Processing :: Filters"], "extensions": {"python.commands": {"wrap_console": {"unidecode": "unidecode.util:main"}}, "python.details": {"contacts": [{"email": "tomaz.solc@tablix.org", "name": "Tomaz Solc", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}}, "python.exports": {"console_scripts": {"unidecode": "unidecode.util:main"}}}, "generator": "bdist_wheel (0.30.0)", "license": "GPL", "metadata_version": "2.0", "name": "Unidecode", "summary": "ASCII transliterations of Unicode text", "version": "1.0.22"}
\ No newline at end of file
diff --git a/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/top_level.txt b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/top_level.txt
new file mode 100644
index 0000000..051b14c
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/Unidecode-1.0.22.dist-info/top_level.txt
@@ -0,0 +1 @@
+unidecode
diff --git a/venv/lib/python3.5/site-packages/__pycache__/easy_install.cpython-35.pyc b/venv/lib/python3.5/site-packages/__pycache__/easy_install.cpython-35.pyc
new file mode 100644
index 0000000..a771154
Binary files /dev/null and b/venv/lib/python3.5/site-packages/__pycache__/easy_install.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/__pycache__/ghp_import.cpython-35.pyc b/venv/lib/python3.5/site-packages/__pycache__/ghp_import.cpython-35.pyc
new file mode 100644
index 0000000..d05724b
Binary files /dev/null and b/venv/lib/python3.5/site-packages/__pycache__/ghp_import.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/__pycache__/six.cpython-35.pyc b/venv/lib/python3.5/site-packages/__pycache__/six.cpython-35.pyc
new file mode 100644
index 0000000..cfe0748
Binary files /dev/null and b/venv/lib/python3.5/site-packages/__pycache__/six.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/DESCRIPTION.rst b/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/DESCRIPTION.rst
new file mode 100644
index 0000000..317e0e1
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/DESCRIPTION.rst
@@ -0,0 +1,74 @@
+[](https://travis-ci.org/jek/blinker)
+
+
+# Blinker
+
+Blinker provides a fast dispatching system that allows any number of
+interested parties to subscribe to events, or "signals".
+
+Signal receivers can subscribe to specific senders or receive signals
+sent by any sender.
+
+ >>> from blinker import signal
+ >>> started = signal('round-started')
+ >>> def each(round):
+ ... print "Round %s!" % round
+ ...
+ >>> started.connect(each)
+
+ >>> def round_two(round):
+ ... print "This is round two."
+ ...
+ >>> started.connect(round_two, sender=2)
+
+ >>> for round in range(1, 4):
+ ... started.send(round)
+ ...
+ Round 1!
+ Round 2!
+ This is round two.
+ Round 3!
+
+See the [Blinker documentation](https://pythonhosted.org/blinker/) for more information.
+
+## Requirements
+
+Blinker requires Python 2.4 or higher, Python 3.0 or higher, or Jython 2.5 or higher.
+
+## Changelog Summary
+
+1.3 (July 3, 2013)
+
+ - The global signal stash behind blinker.signal() is now backed by a
+ regular name-to-Signal dictionary. Previously, weak references were
+ held in the mapping and ephemeral usage in code like
+ ``signal('foo').connect(...)`` could have surprising program behavior
+ depending on import order of modules.
+ - blinker.Namespace is now built on a regular dict. Use
+ blinker.WeakNamespace for the older, weak-referencing behavior.
+ - Signal.connect('text-sender') uses an alternate hashing strategy to
+ avoid sharp edges in text identity.
+
+1.2 (October 26, 2011)
+
+ - Added Signal.receiver_connected and Signal.receiver_disconnected
+ per-Signal signals.
+ - Deprecated the global 'receiver_connected' signal.
+ - Verified Python 3.2 support (no changes needed!)
+
+1.1 (July 21, 2010)
+
+ - Added ``@signal.connect_via(sender)`` decorator
+ - Added ``signal.connected_to`` shorthand name for the
+ ``temporarily_connected_to`` context manager.
+
+1.0 (March 28, 2010)
+
+ - Python 3.x compatibility
+
+0.9 (February 26, 2010)
+
+ - Sphinx docs, project website
+ - Added ``with a_signal.temporarily_connected_to(receiver): ...`` support
+
+
diff --git a/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/INSTALLER b/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/METADATA b/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/METADATA
new file mode 100644
index 0000000..2c4e7cb
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/METADATA
@@ -0,0 +1,103 @@
+Metadata-Version: 2.0
+Name: blinker
+Version: 1.4
+Summary: Fast, simple object-to-object and broadcast signaling
+Home-page: http://pythonhosted.org/blinker/
+Author: Jason Kirtland
+Author-email: jek@discorporate.us
+License: MIT License
+Keywords: signal emit events broadcast
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+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.4
+Classifier: Programming Language :: Python :: 2.5
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.0
+Classifier: Programming Language :: Python :: 3.1
+Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Utilities
+
+[](https://travis-ci.org/jek/blinker)
+
+
+# Blinker
+
+Blinker provides a fast dispatching system that allows any number of
+interested parties to subscribe to events, or "signals".
+
+Signal receivers can subscribe to specific senders or receive signals
+sent by any sender.
+
+ >>> from blinker import signal
+ >>> started = signal('round-started')
+ >>> def each(round):
+ ... print "Round %s!" % round
+ ...
+ >>> started.connect(each)
+
+ >>> def round_two(round):
+ ... print "This is round two."
+ ...
+ >>> started.connect(round_two, sender=2)
+
+ >>> for round in range(1, 4):
+ ... started.send(round)
+ ...
+ Round 1!
+ Round 2!
+ This is round two.
+ Round 3!
+
+See the [Blinker documentation](https://pythonhosted.org/blinker/) for more information.
+
+## Requirements
+
+Blinker requires Python 2.4 or higher, Python 3.0 or higher, or Jython 2.5 or higher.
+
+## Changelog Summary
+
+1.3 (July 3, 2013)
+
+ - The global signal stash behind blinker.signal() is now backed by a
+ regular name-to-Signal dictionary. Previously, weak references were
+ held in the mapping and ephemeral usage in code like
+ ``signal('foo').connect(...)`` could have surprising program behavior
+ depending on import order of modules.
+ - blinker.Namespace is now built on a regular dict. Use
+ blinker.WeakNamespace for the older, weak-referencing behavior.
+ - Signal.connect('text-sender') uses an alternate hashing strategy to
+ avoid sharp edges in text identity.
+
+1.2 (October 26, 2011)
+
+ - Added Signal.receiver_connected and Signal.receiver_disconnected
+ per-Signal signals.
+ - Deprecated the global 'receiver_connected' signal.
+ - Verified Python 3.2 support (no changes needed!)
+
+1.1 (July 21, 2010)
+
+ - Added ``@signal.connect_via(sender)`` decorator
+ - Added ``signal.connected_to`` shorthand name for the
+ ``temporarily_connected_to`` context manager.
+
+1.0 (March 28, 2010)
+
+ - Python 3.x compatibility
+
+0.9 (February 26, 2010)
+
+ - Sphinx docs, project website
+ - Added ``with a_signal.temporarily_connected_to(receiver): ...`` support
+
+
diff --git a/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/RECORD b/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/RECORD
new file mode 100644
index 0000000..08f3ee7
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/RECORD
@@ -0,0 +1,15 @@
+blinker/__init__.py,sha256=vgkMDX61C3h5jhFvxiGJ_FCkajPLZi-KzZr_AAfV-_0,300
+blinker/_saferef.py,sha256=OprBfoWWJd791Qm9OY5c-v1bIo2qLalHah8hqAA18Sw,9223
+blinker/_utilities.py,sha256=ev0IgdfH4wMSmLO-J3gVZSc-umhhmJiQJWdQNy6lyTY,4457
+blinker/base.py,sha256=MJ_qn4ladUGMAU2M1k2OIPfgUi_INhbqfLrvjdsmHVM,16319
+blinker-1.4.dist-info/DESCRIPTION.rst,sha256=3_4DJiX9bY8jfI5iy3cnv1RpTnnxn6H7hZU0XQcn654,2143
+blinker-1.4.dist-info/METADATA,sha256=05VH_-heiIxM9Ws-CV8oKgr_QyeTNAfHrlWXrY0shwE,3308
+blinker-1.4.dist-info/RECORD,,
+blinker-1.4.dist-info/WHEEL,sha256=yUNYBu31xnjtx3h_5Nj3XQqBXj9vmoAZaFMBdSYrEn8,93
+blinker-1.4.dist-info/metadata.json,sha256=aKd8tj1U2KLWLRB75BhdINYrNQT4SP7HX6KQ6zjoFaU,1222
+blinker-1.4.dist-info/top_level.txt,sha256=2NmsENM0J2t9Z8mkjxHDmGMQj7Bm8f5ZTTYe1x1fZtM,8
+blinker-1.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+blinker/__pycache__/_utilities.cpython-35.pyc,,
+blinker/__pycache__/base.cpython-35.pyc,,
+blinker/__pycache__/_saferef.cpython-35.pyc,,
+blinker/__pycache__/__init__.cpython-35.pyc,,
diff --git a/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/WHEEL b/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/WHEEL
new file mode 100644
index 0000000..a056cc0
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.30.0)
+Root-Is-Purelib: true
+Tag: cp35-none-any
+
diff --git a/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/metadata.json b/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/metadata.json
new file mode 100644
index 0000000..4a18217
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/metadata.json
@@ -0,0 +1 @@
+{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.4", "Programming Language :: Python :: 2.5", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.0", "Programming Language :: Python :: 3.1", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Topic :: Software Development :: Libraries", "Topic :: Utilities"], "extensions": {"python.details": {"contacts": [{"email": "jek@discorporate.us", "name": "Jason Kirtland", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://pythonhosted.org/blinker/"}}}, "generator": "bdist_wheel (0.30.0)", "keywords": ["signal", "emit", "events", "broadcast"], "license": "MIT License", "metadata_version": "2.0", "name": "blinker", "summary": "Fast, simple object-to-object and broadcast signaling", "version": "1.4"}
\ No newline at end of file
diff --git a/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/top_level.txt b/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/top_level.txt
new file mode 100644
index 0000000..1ff4ca5
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/blinker-1.4.dist-info/top_level.txt
@@ -0,0 +1 @@
+blinker
diff --git a/venv/lib/python3.5/site-packages/blinker/__init__.py b/venv/lib/python3.5/site-packages/blinker/__init__.py
new file mode 100644
index 0000000..3ea239c
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/blinker/__init__.py
@@ -0,0 +1,22 @@
+from blinker.base import (
+ ANY,
+ NamedSignal,
+ Namespace,
+ Signal,
+ WeakNamespace,
+ receiver_connected,
+ signal,
+)
+
+__all__ = [
+ 'ANY',
+ 'NamedSignal',
+ 'Namespace',
+ 'Signal',
+ 'WeakNamespace',
+ 'receiver_connected',
+ 'signal',
+ ]
+
+
+__version__ = '1.4'
diff --git a/venv/lib/python3.5/site-packages/blinker/__pycache__/__init__.cpython-35.pyc b/venv/lib/python3.5/site-packages/blinker/__pycache__/__init__.cpython-35.pyc
new file mode 100644
index 0000000..e8e64fa
Binary files /dev/null and b/venv/lib/python3.5/site-packages/blinker/__pycache__/__init__.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/blinker/__pycache__/_saferef.cpython-35.pyc b/venv/lib/python3.5/site-packages/blinker/__pycache__/_saferef.cpython-35.pyc
new file mode 100644
index 0000000..d25d8a2
Binary files /dev/null and b/venv/lib/python3.5/site-packages/blinker/__pycache__/_saferef.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/blinker/__pycache__/_utilities.cpython-35.pyc b/venv/lib/python3.5/site-packages/blinker/__pycache__/_utilities.cpython-35.pyc
new file mode 100644
index 0000000..ab99636
Binary files /dev/null and b/venv/lib/python3.5/site-packages/blinker/__pycache__/_utilities.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/blinker/__pycache__/base.cpython-35.pyc b/venv/lib/python3.5/site-packages/blinker/__pycache__/base.cpython-35.pyc
new file mode 100644
index 0000000..c974e1d
Binary files /dev/null and b/venv/lib/python3.5/site-packages/blinker/__pycache__/base.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/blinker/_saferef.py b/venv/lib/python3.5/site-packages/blinker/_saferef.py
new file mode 100644
index 0000000..269e362
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/blinker/_saferef.py
@@ -0,0 +1,234 @@
+# extracted from Louie, http://pylouie.org/
+# updated for Python 3
+#
+# Copyright (c) 2006 Patrick K. O'Brien, Mike C. Fletcher,
+# Matthew R. Scott
+#
+# 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 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.
+#
+"""Refactored 'safe reference from dispatcher.py"""
+
+import operator
+import sys
+import traceback
+import weakref
+
+
+try:
+ callable
+except NameError:
+ def callable(object):
+ return hasattr(object, '__call__')
+
+
+if sys.version_info < (3,):
+ get_self = operator.attrgetter('im_self')
+ get_func = operator.attrgetter('im_func')
+else:
+ get_self = operator.attrgetter('__self__')
+ get_func = operator.attrgetter('__func__')
+
+
+def safe_ref(target, on_delete=None):
+ """Return a *safe* weak reference to a callable target.
+
+ - ``target``: The object to be weakly referenced, if it's a bound
+ method reference, will create a BoundMethodWeakref, otherwise
+ creates a simple weakref.
+
+ - ``on_delete``: If provided, will have a hard reference stored to
+ the callable to be called after the safe reference goes out of
+ scope with the reference object, (either a weakref or a
+ BoundMethodWeakref) as argument.
+ """
+ try:
+ im_self = get_self(target)
+ except AttributeError:
+ if callable(on_delete):
+ return weakref.ref(target, on_delete)
+ else:
+ return weakref.ref(target)
+ else:
+ if im_self is not None:
+ # Turn a bound method into a BoundMethodWeakref instance.
+ # Keep track of these instances for lookup by disconnect().
+ assert hasattr(target, 'im_func') or hasattr(target, '__func__'), (
+ "safe_ref target %r has im_self, but no im_func, "
+ "don't know how to create reference" % target)
+ reference = BoundMethodWeakref(target=target, on_delete=on_delete)
+ return reference
+
+
+class BoundMethodWeakref(object):
+ """'Safe' and reusable weak references to instance methods.
+
+ BoundMethodWeakref objects provide a mechanism for referencing a
+ bound method without requiring that the method object itself
+ (which is normally a transient object) is kept alive. Instead,
+ the BoundMethodWeakref object keeps weak references to both the
+ object and the function which together define the instance method.
+
+ Attributes:
+
+ - ``key``: The identity key for the reference, calculated by the
+ class's calculate_key method applied to the target instance method.
+
+ - ``deletion_methods``: Sequence of callable objects taking single
+ argument, a reference to this object which will be called when
+ *either* the target object or target function is garbage
+ collected (i.e. when this object becomes invalid). These are
+ specified as the on_delete parameters of safe_ref calls.
+
+ - ``weak_self``: Weak reference to the target object.
+
+ - ``weak_func``: Weak reference to the target function.
+
+ Class Attributes:
+
+ - ``_all_instances``: Class attribute pointing to all live
+ BoundMethodWeakref objects indexed by the class's
+ calculate_key(target) method applied to the target objects.
+ This weak value dictionary is used to short-circuit creation so
+ that multiple references to the same (object, function) pair
+ produce the same BoundMethodWeakref instance.
+ """
+
+ _all_instances = weakref.WeakValueDictionary()
+
+ def __new__(cls, target, on_delete=None, *arguments, **named):
+ """Create new instance or return current instance.
+
+ Basically this method of construction allows us to
+ short-circuit creation of references to already- referenced
+ instance methods. The key corresponding to the target is
+ calculated, and if there is already an existing reference,
+ that is returned, with its deletion_methods attribute updated.
+ Otherwise the new instance is created and registered in the
+ table of already-referenced methods.
+ """
+ key = cls.calculate_key(target)
+ current = cls._all_instances.get(key)
+ if current is not None:
+ current.deletion_methods.append(on_delete)
+ return current
+ else:
+ base = super(BoundMethodWeakref, cls).__new__(cls)
+ cls._all_instances[key] = base
+ base.__init__(target, on_delete, *arguments, **named)
+ return base
+
+ def __init__(self, target, on_delete=None):
+ """Return a weak-reference-like instance for a bound method.
+
+ - ``target``: The instance-method target for the weak reference,
+ must have im_self and im_func attributes and be
+ reconstructable via the following, which is true of built-in
+ instance methods::
+
+ target.im_func.__get__( target.im_self )
+
+ - ``on_delete``: Optional callback which will be called when
+ this weak reference ceases to be valid (i.e. either the
+ object or the function is garbage collected). Should take a
+ single argument, which will be passed a pointer to this
+ object.
+ """
+ def remove(weak, self=self):
+ """Set self.isDead to True when method or instance is destroyed."""
+ methods = self.deletion_methods[:]
+ del self.deletion_methods[:]
+ try:
+ del self.__class__._all_instances[self.key]
+ except KeyError:
+ pass
+ for function in methods:
+ try:
+ if callable(function):
+ function(self)
+ except Exception:
+ try:
+ traceback.print_exc()
+ except AttributeError:
+ e = sys.exc_info()[1]
+ print ('Exception during saferef %s '
+ 'cleanup function %s: %s' % (self, function, e))
+ self.deletion_methods = [on_delete]
+ self.key = self.calculate_key(target)
+ im_self = get_self(target)
+ im_func = get_func(target)
+ self.weak_self = weakref.ref(im_self, remove)
+ self.weak_func = weakref.ref(im_func, remove)
+ self.self_name = str(im_self)
+ self.func_name = str(im_func.__name__)
+
+ def calculate_key(cls, target):
+ """Calculate the reference key for this reference.
+
+ Currently this is a two-tuple of the id()'s of the target
+ object and the target function respectively.
+ """
+ return (id(get_self(target)), id(get_func(target)))
+ calculate_key = classmethod(calculate_key)
+
+ def __str__(self):
+ """Give a friendly representation of the object."""
+ return "%s(%s.%s)" % (
+ self.__class__.__name__,
+ self.self_name,
+ self.func_name,
+ )
+
+ __repr__ = __str__
+
+ def __nonzero__(self):
+ """Whether we are still a valid reference."""
+ return self() is not None
+
+ def __cmp__(self, other):
+ """Compare with another reference."""
+ if not isinstance(other, self.__class__):
+ return cmp(self.__class__, type(other))
+ return cmp(self.key, other.key)
+
+ def __call__(self):
+ """Return a strong reference to the bound method.
+
+ If the target cannot be retrieved, then will return None,
+ otherwise returns a bound instance method for our object and
+ function.
+
+ Note: You may call this method any number of times, as it does
+ not invalidate the reference.
+ """
+ target = self.weak_self()
+ if target is not None:
+ function = self.weak_func()
+ if function is not None:
+ return function.__get__(target)
+ return None
diff --git a/venv/lib/python3.5/site-packages/blinker/_utilities.py b/venv/lib/python3.5/site-packages/blinker/_utilities.py
new file mode 100644
index 0000000..056270d
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/blinker/_utilities.py
@@ -0,0 +1,163 @@
+from weakref import ref
+
+from blinker._saferef import BoundMethodWeakref
+
+
+try:
+ callable
+except NameError:
+ def callable(object):
+ return hasattr(object, '__call__')
+
+
+try:
+ from collections import defaultdict
+except:
+ class defaultdict(dict):
+
+ def __init__(self, default_factory=None, *a, **kw):
+ if (default_factory is not None and
+ not hasattr(default_factory, '__call__')):
+ raise TypeError('first argument must be callable')
+ dict.__init__(self, *a, **kw)
+ self.default_factory = default_factory
+
+ def __getitem__(self, key):
+ try:
+ return dict.__getitem__(self, key)
+ except KeyError:
+ return self.__missing__(key)
+
+ def __missing__(self, key):
+ if self.default_factory is None:
+ raise KeyError(key)
+ self[key] = value = self.default_factory()
+ return value
+
+ def __reduce__(self):
+ if self.default_factory is None:
+ args = tuple()
+ else:
+ args = self.default_factory,
+ return type(self), args, None, None, self.items()
+
+ def copy(self):
+ return self.__copy__()
+
+ def __copy__(self):
+ return type(self)(self.default_factory, self)
+
+ def __deepcopy__(self, memo):
+ import copy
+ return type(self)(self.default_factory,
+ copy.deepcopy(self.items()))
+
+ def __repr__(self):
+ return 'defaultdict(%s, %s)' % (self.default_factory,
+ dict.__repr__(self))
+
+
+try:
+ from contextlib import contextmanager
+except ImportError:
+ def contextmanager(fn):
+ def oops(*args, **kw):
+ raise RuntimeError("Python 2.5 or above is required to use "
+ "context managers.")
+ oops.__name__ = fn.__name__
+ return oops
+
+class _symbol(object):
+
+ def __init__(self, name):
+ """Construct a new named symbol."""
+ self.__name__ = self.name = name
+
+ def __reduce__(self):
+ return symbol, (self.name,)
+
+ def __repr__(self):
+ return self.name
+_symbol.__name__ = 'symbol'
+
+
+class symbol(object):
+ """A constant symbol.
+
+ >>> symbol('foo') is symbol('foo')
+ True
+ >>> symbol('foo')
+ foo
+
+ A slight refinement of the MAGICCOOKIE=object() pattern. The primary
+ advantage of symbol() is its repr(). They are also singletons.
+
+ Repeated calls of symbol('name') will all return the same instance.
+
+ """
+ symbols = {}
+
+ def __new__(cls, name):
+ try:
+ return cls.symbols[name]
+ except KeyError:
+ return cls.symbols.setdefault(name, _symbol(name))
+
+
+try:
+ text = (str, unicode)
+except NameError:
+ text = str
+
+
+def hashable_identity(obj):
+ if hasattr(obj, '__func__'):
+ return (id(obj.__func__), id(obj.__self__))
+ elif hasattr(obj, 'im_func'):
+ return (id(obj.im_func), id(obj.im_self))
+ elif isinstance(obj, text):
+ return obj
+ else:
+ return id(obj)
+
+
+WeakTypes = (ref, BoundMethodWeakref)
+
+
+class annotatable_weakref(ref):
+ """A weakref.ref that supports custom instance attributes."""
+
+
+def reference(object, callback=None, **annotations):
+ """Return an annotated weak ref."""
+ if callable(object):
+ weak = callable_reference(object, callback)
+ else:
+ weak = annotatable_weakref(object, callback)
+ for key, value in annotations.items():
+ setattr(weak, key, value)
+ return weak
+
+
+def callable_reference(object, callback=None):
+ """Return an annotated weak ref, supporting bound instance methods."""
+ if hasattr(object, 'im_self') and object.im_self is not None:
+ return BoundMethodWeakref(target=object, on_delete=callback)
+ elif hasattr(object, '__self__') and object.__self__ is not None:
+ return BoundMethodWeakref(target=object, on_delete=callback)
+ return annotatable_weakref(object, callback)
+
+
+class lazy_property(object):
+ """A @property that is only evaluated once."""
+
+ def __init__(self, deferred):
+ self._deferred = deferred
+ self.__doc__ = deferred.__doc__
+
+ def __get__(self, obj, cls):
+ if obj is None:
+ return self
+ value = self._deferred(obj)
+ setattr(obj, self._deferred.__name__, value)
+ return value
diff --git a/venv/lib/python3.5/site-packages/blinker/base.py b/venv/lib/python3.5/site-packages/blinker/base.py
new file mode 100644
index 0000000..cc5880e
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/blinker/base.py
@@ -0,0 +1,455 @@
+# -*- coding: utf-8; fill-column: 76 -*-
+"""Signals and events.
+
+A small implementation of signals, inspired by a snippet of Django signal
+API client code seen in a blog post. Signals are first-class objects and
+each manages its own receivers and message emission.
+
+The :func:`signal` function provides singleton behavior for named signals.
+
+"""
+from warnings import warn
+from weakref import WeakValueDictionary
+
+from blinker._utilities import (
+ WeakTypes,
+ contextmanager,
+ defaultdict,
+ hashable_identity,
+ lazy_property,
+ reference,
+ symbol,
+ )
+
+
+ANY = symbol('ANY')
+ANY.__doc__ = 'Token for "any sender".'
+ANY_ID = 0
+
+
+class Signal(object):
+ """A notification emitter."""
+
+ #: An :obj:`ANY` convenience synonym, allows ``Signal.ANY``
+ #: without an additional import.
+ ANY = ANY
+
+ @lazy_property
+ def receiver_connected(self):
+ """Emitted after each :meth:`connect`.
+
+ The signal sender is the signal instance, and the :meth:`connect`
+ arguments are passed through: *receiver*, *sender*, and *weak*.
+
+ .. versionadded:: 1.2
+
+ """
+ return Signal(doc="Emitted after a receiver connects.")
+
+ @lazy_property
+ def receiver_disconnected(self):
+ """Emitted after :meth:`disconnect`.
+
+ The sender is the signal instance, and the :meth:`disconnect` arguments
+ are passed through: *receiver* and *sender*.
+
+ Note, this signal is emitted **only** when :meth:`disconnect` is
+ called explicitly.
+
+ The disconnect signal can not be emitted by an automatic disconnect
+ (due to a weakly referenced receiver or sender going out of scope),
+ as the receiver and/or sender instances are no longer available for
+ use at the time this signal would be emitted.
+
+ An alternative approach is available by subscribing to
+ :attr:`receiver_connected` and setting up a custom weakref cleanup
+ callback on weak receivers and senders.
+
+ .. versionadded:: 1.2
+
+ """
+ return Signal(doc="Emitted after a receiver disconnects.")
+
+ def __init__(self, doc=None):
+ """
+ :param doc: optional. If provided, will be assigned to the signal's
+ __doc__ attribute.
+
+ """
+ if doc:
+ self.__doc__ = doc
+ #: A mapping of connected receivers.
+ #:
+ #: The values of this mapping are not meaningful outside of the
+ #: internal :class:`Signal` implementation, however the boolean value
+ #: of the mapping is useful as an extremely efficient check to see if
+ #: any receivers are connected to the signal.
+ self.receivers = {}
+ self._by_receiver = defaultdict(set)
+ self._by_sender = defaultdict(set)
+ self._weak_senders = {}
+
+ def connect(self, receiver, sender=ANY, weak=True):
+ """Connect *receiver* to signal events sent by *sender*.
+
+ :param receiver: A callable. Will be invoked by :meth:`send` with
+ `sender=` as a single positional argument and any \*\*kwargs that
+ were provided to a call to :meth:`send`.
+
+ :param sender: Any object or :obj:`ANY`, defaults to ``ANY``.
+ Restricts notifications delivered to *receiver* to only those
+ :meth:`send` emissions sent by *sender*. If ``ANY``, the receiver
+ will always be notified. A *receiver* may be connected to
+ multiple *sender* values on the same Signal through multiple calls
+ to :meth:`connect`.
+
+ :param weak: If true, the Signal will hold a weakref to *receiver*
+ and automatically disconnect when *receiver* goes out of scope or
+ is garbage collected. Defaults to True.
+
+ """
+ receiver_id = hashable_identity(receiver)
+ if weak:
+ receiver_ref = reference(receiver, self._cleanup_receiver)
+ receiver_ref.receiver_id = receiver_id
+ else:
+ receiver_ref = receiver
+ if sender is ANY:
+ sender_id = ANY_ID
+ else:
+ sender_id = hashable_identity(sender)
+
+ self.receivers.setdefault(receiver_id, receiver_ref)
+ self._by_sender[sender_id].add(receiver_id)
+ self._by_receiver[receiver_id].add(sender_id)
+ del receiver_ref
+
+ if sender is not ANY and sender_id not in self._weak_senders:
+ # wire together a cleanup for weakref-able senders
+ try:
+ sender_ref = reference(sender, self._cleanup_sender)
+ sender_ref.sender_id = sender_id
+ except TypeError:
+ pass
+ else:
+ self._weak_senders.setdefault(sender_id, sender_ref)
+ del sender_ref
+
+ # broadcast this connection. if receivers raise, disconnect.
+ if ('receiver_connected' in self.__dict__ and
+ self.receiver_connected.receivers):
+ try:
+ self.receiver_connected.send(self,
+ receiver=receiver,
+ sender=sender,
+ weak=weak)
+ except:
+ self.disconnect(receiver, sender)
+ raise
+ if receiver_connected.receivers and self is not receiver_connected:
+ try:
+ receiver_connected.send(self,
+ receiver_arg=receiver,
+ sender_arg=sender,
+ weak_arg=weak)
+ except:
+ self.disconnect(receiver, sender)
+ raise
+ return receiver
+
+ def connect_via(self, sender, weak=False):
+ """Connect the decorated function as a receiver for *sender*.
+
+ :param sender: Any object or :obj:`ANY`. The decorated function
+ will only receive :meth:`send` emissions sent by *sender*. If
+ ``ANY``, the receiver will always be notified. A function may be
+ decorated multiple times with differing *sender* values.
+
+ :param weak: If true, the Signal will hold a weakref to the
+ decorated function and automatically disconnect when *receiver*
+ goes out of scope or is garbage collected. Unlike
+ :meth:`connect`, this defaults to False.
+
+ The decorated function will be invoked by :meth:`send` with
+ `sender=` as a single positional argument and any \*\*kwargs that
+ were provided to the call to :meth:`send`.
+
+
+ .. versionadded:: 1.1
+
+ """
+ def decorator(fn):
+ self.connect(fn, sender, weak)
+ return fn
+ return decorator
+
+ @contextmanager
+ def connected_to(self, receiver, sender=ANY):
+ """Execute a block with the signal temporarily connected to *receiver*.
+
+ :param receiver: a receiver callable
+ :param sender: optional, a sender to filter on
+
+ This is a context manager for use in the ``with`` statement. It can
+ be useful in unit tests. *receiver* is connected to the signal for
+ the duration of the ``with`` block, and will be disconnected
+ automatically when exiting the block:
+
+ .. testsetup::
+
+ from __future__ import with_statement
+ from blinker import Signal
+ on_ready = Signal()
+ receiver = lambda sender: None
+
+ .. testcode::
+
+ with on_ready.connected_to(receiver):
+ # do stuff
+ on_ready.send(123)
+
+ .. versionadded:: 1.1
+
+ """
+ self.connect(receiver, sender=sender, weak=False)
+ try:
+ yield None
+ except:
+ self.disconnect(receiver)
+ raise
+ else:
+ self.disconnect(receiver)
+
+ def temporarily_connected_to(self, receiver, sender=ANY):
+ """An alias for :meth:`connected_to`.
+
+ :param receiver: a receiver callable
+ :param sender: optional, a sender to filter on
+
+ .. versionadded:: 0.9
+
+ .. versionchanged:: 1.1
+ Renamed to :meth:`connected_to`. ``temporarily_connected_to`` was
+ deprecated in 1.2 and will be removed in a subsequent version.
+
+ """
+ warn("temporarily_connected_to is deprecated; "
+ "use connected_to instead.",
+ DeprecationWarning)
+ return self.connected_to(receiver, sender)
+
+ def send(self, *sender, **kwargs):
+ """Emit this signal on behalf of *sender*, passing on \*\*kwargs.
+
+ Returns a list of 2-tuples, pairing receivers with their return
+ value. The ordering of receiver notification is undefined.
+
+ :param \*sender: Any object or ``None``. If omitted, synonymous
+ with ``None``. Only accepts one positional argument.
+
+ :param \*\*kwargs: Data to be sent to receivers.
+
+ """
+ # Using '*sender' rather than 'sender=None' allows 'sender' to be
+ # used as a keyword argument- i.e. it's an invisible name in the
+ # function signature.
+ if len(sender) == 0:
+ sender = None
+ elif len(sender) > 1:
+ raise TypeError('send() accepts only one positional argument, '
+ '%s given' % len(sender))
+ else:
+ sender = sender[0]
+ if not self.receivers:
+ return []
+ else:
+ return [(receiver, receiver(sender, **kwargs))
+ for receiver in self.receivers_for(sender)]
+
+ def has_receivers_for(self, sender):
+ """True if there is probably a receiver for *sender*.
+
+ Performs an optimistic check only. Does not guarantee that all
+ weakly referenced receivers are still alive. See
+ :meth:`receivers_for` for a stronger search.
+
+ """
+ if not self.receivers:
+ return False
+ if self._by_sender[ANY_ID]:
+ return True
+ if sender is ANY:
+ return False
+ return hashable_identity(sender) in self._by_sender
+
+ def receivers_for(self, sender):
+ """Iterate all live receivers listening for *sender*."""
+ # TODO: test receivers_for(ANY)
+ if self.receivers:
+ sender_id = hashable_identity(sender)
+ if sender_id in self._by_sender:
+ ids = (self._by_sender[ANY_ID] |
+ self._by_sender[sender_id])
+ else:
+ ids = self._by_sender[ANY_ID].copy()
+ for receiver_id in ids:
+ receiver = self.receivers.get(receiver_id)
+ if receiver is None:
+ continue
+ if isinstance(receiver, WeakTypes):
+ strong = receiver()
+ if strong is None:
+ self._disconnect(receiver_id, ANY_ID)
+ continue
+ receiver = strong
+ yield receiver
+
+ def disconnect(self, receiver, sender=ANY):
+ """Disconnect *receiver* from this signal's events.
+
+ :param receiver: a previously :meth:`connected` callable
+
+ :param sender: a specific sender to disconnect from, or :obj:`ANY`
+ to disconnect from all senders. Defaults to ``ANY``.
+
+ """
+ if sender is ANY:
+ sender_id = ANY_ID
+ else:
+ sender_id = hashable_identity(sender)
+ receiver_id = hashable_identity(receiver)
+ self._disconnect(receiver_id, sender_id)
+
+ if ('receiver_disconnected' in self.__dict__ and
+ self.receiver_disconnected.receivers):
+ self.receiver_disconnected.send(self,
+ receiver=receiver,
+ sender=sender)
+
+ def _disconnect(self, receiver_id, sender_id):
+ if sender_id == ANY_ID:
+ if self._by_receiver.pop(receiver_id, False):
+ for bucket in self._by_sender.values():
+ bucket.discard(receiver_id)
+ self.receivers.pop(receiver_id, None)
+ else:
+ self._by_sender[sender_id].discard(receiver_id)
+ self._by_receiver[receiver_id].discard(sender_id)
+
+ def _cleanup_receiver(self, receiver_ref):
+ """Disconnect a receiver from all senders."""
+ self._disconnect(receiver_ref.receiver_id, ANY_ID)
+
+ def _cleanup_sender(self, sender_ref):
+ """Disconnect all receivers from a sender."""
+ sender_id = sender_ref.sender_id
+ assert sender_id != ANY_ID
+ self._weak_senders.pop(sender_id, None)
+ for receiver_id in self._by_sender.pop(sender_id, ()):
+ self._by_receiver[receiver_id].discard(sender_id)
+
+ def _cleanup_bookkeeping(self):
+ """Prune unused sender/receiver bookeeping. Not threadsafe.
+
+ Connecting & disconnecting leave behind a small amount of bookeeping
+ for the receiver and sender values. Typical workloads using Blinker,
+ for example in most web apps, Flask, CLI scripts, etc., are not
+ adversely affected by this bookkeeping.
+
+ With a long-running Python process performing dynamic signal routing
+ with high volume- e.g. connecting to function closures, "senders" are
+ all unique object instances, and doing all of this over and over- you
+ may see memory usage will grow due to extraneous bookeeping. (An empty
+ set() for each stale sender/receiver pair.)
+
+ This method will prune that bookeeping away, with the caveat that such
+ pruning is not threadsafe. The risk is that cleanup of a fully
+ disconnected receiver/sender pair occurs while another thread is
+ connecting that same pair. If you are in the highly dynamic, unique
+ receiver/sender situation that has lead you to this method, that
+ failure mode is perhaps not a big deal for you.
+ """
+ for mapping in (self._by_sender, self._by_receiver):
+ for _id, bucket in list(mapping.items()):
+ if not bucket:
+ mapping.pop(_id, None)
+
+ def _clear_state(self):
+ """Throw away all signal state. Useful for unit tests."""
+ self._weak_senders.clear()
+ self.receivers.clear()
+ self._by_sender.clear()
+ self._by_receiver.clear()
+
+
+receiver_connected = Signal("""\
+Sent by a :class:`Signal` after a receiver connects.
+
+:argument: the Signal that was connected to
+:keyword receiver_arg: the connected receiver
+:keyword sender_arg: the sender to connect to
+:keyword weak_arg: true if the connection to receiver_arg is a weak reference
+
+.. deprecated:: 1.2
+
+As of 1.2, individual signals have their own private
+:attr:`~Signal.receiver_connected` and
+:attr:`~Signal.receiver_disconnected` signals with a slightly simplified
+call signature. This global signal is planned to be removed in 1.6.
+
+""")
+
+
+class NamedSignal(Signal):
+ """A named generic notification emitter."""
+
+ def __init__(self, name, doc=None):
+ Signal.__init__(self, doc)
+
+ #: The name of this signal.
+ self.name = name
+
+ def __repr__(self):
+ base = Signal.__repr__(self)
+ return "%s; %r>" % (base[:-1], self.name)
+
+
+class Namespace(dict):
+ """A mapping of signal names to signals."""
+
+ def signal(self, name, doc=None):
+ """Return the :class:`NamedSignal` *name*, creating it if required.
+
+ Repeated calls to this function will return the same signal object.
+
+ """
+ try:
+ return self[name]
+ except KeyError:
+ return self.setdefault(name, NamedSignal(name, doc))
+
+
+class WeakNamespace(WeakValueDictionary):
+ """A weak mapping of signal names to signals.
+
+ Automatically cleans up unused Signals when the last reference goes out
+ of scope. This namespace implementation exists for a measure of legacy
+ compatibility with Blinker <= 1.2, and may be dropped in the future.
+
+ .. versionadded:: 1.3
+
+ """
+
+ def signal(self, name, doc=None):
+ """Return the :class:`NamedSignal` *name*, creating it if required.
+
+ Repeated calls to this function will return the same signal object.
+
+ """
+ try:
+ return self[name]
+ except KeyError:
+ return self.setdefault(name, NamedSignal(name, doc))
+
+
+signal = Namespace().signal
diff --git a/venv/lib/python3.5/site-packages/dateutil/__init__.py b/venv/lib/python3.5/site-packages/dateutil/__init__.py
new file mode 100644
index 0000000..0defb82
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/__init__.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+try:
+ from ._version import version as __version__
+except ImportError:
+ __version__ = 'unknown'
+
+__all__ = ['easter', 'parser', 'relativedelta', 'rrule', 'tz',
+ 'utils', 'zoneinfo']
diff --git a/venv/lib/python3.5/site-packages/dateutil/__pycache__/__init__.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/__pycache__/__init__.cpython-35.pyc
new file mode 100644
index 0000000..e0fe215
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/__pycache__/__init__.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/__pycache__/_common.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/__pycache__/_common.cpython-35.pyc
new file mode 100644
index 0000000..2d7f98f
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/__pycache__/_common.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/__pycache__/_version.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/__pycache__/_version.cpython-35.pyc
new file mode 100644
index 0000000..060e7b7
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/__pycache__/_version.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/__pycache__/easter.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/__pycache__/easter.cpython-35.pyc
new file mode 100644
index 0000000..e1adbe5
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/__pycache__/easter.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/__pycache__/relativedelta.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/__pycache__/relativedelta.cpython-35.pyc
new file mode 100644
index 0000000..a7ba014
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/__pycache__/relativedelta.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/__pycache__/rrule.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/__pycache__/rrule.cpython-35.pyc
new file mode 100644
index 0000000..58502ab
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/__pycache__/rrule.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/__pycache__/tzwin.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/__pycache__/tzwin.cpython-35.pyc
new file mode 100644
index 0000000..51b9ae9
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/__pycache__/tzwin.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/__pycache__/utils.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/__pycache__/utils.cpython-35.pyc
new file mode 100644
index 0000000..7a1a416
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/__pycache__/utils.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/_common.py b/venv/lib/python3.5/site-packages/dateutil/_common.py
new file mode 100644
index 0000000..4eb2659
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/_common.py
@@ -0,0 +1,43 @@
+"""
+Common code used in multiple modules.
+"""
+
+
+class weekday(object):
+ __slots__ = ["weekday", "n"]
+
+ def __init__(self, weekday, n=None):
+ self.weekday = weekday
+ self.n = n
+
+ def __call__(self, n):
+ if n == self.n:
+ return self
+ else:
+ return self.__class__(self.weekday, n)
+
+ def __eq__(self, other):
+ try:
+ if self.weekday != other.weekday or self.n != other.n:
+ return False
+ except AttributeError:
+ return False
+ return True
+
+ def __hash__(self):
+ return hash((
+ self.weekday,
+ self.n,
+ ))
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __repr__(self):
+ s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday]
+ if not self.n:
+ return s
+ else:
+ return "%s(%+d)" % (s, self.n)
+
+# vim:ts=4:sw=4:et
diff --git a/venv/lib/python3.5/site-packages/dateutil/_version.py b/venv/lib/python3.5/site-packages/dateutil/_version.py
new file mode 100644
index 0000000..ad222b3
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/_version.py
@@ -0,0 +1,4 @@
+# coding: utf-8
+# file generated by setuptools_scm
+# don't change, don't track in version control
+version = '2.7.2'
diff --git a/venv/lib/python3.5/site-packages/dateutil/easter.py b/venv/lib/python3.5/site-packages/dateutil/easter.py
new file mode 100644
index 0000000..53b7c78
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/easter.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+"""
+This module offers a generic easter computing method for any given year, using
+Western, Orthodox or Julian algorithms.
+"""
+
+import datetime
+
+__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"]
+
+EASTER_JULIAN = 1
+EASTER_ORTHODOX = 2
+EASTER_WESTERN = 3
+
+
+def easter(year, method=EASTER_WESTERN):
+ """
+ This method was ported from the work done by GM Arts,
+ on top of the algorithm by Claus Tondering, which was
+ based in part on the algorithm of Ouding (1940), as
+ quoted in "Explanatory Supplement to the Astronomical
+ Almanac", P. Kenneth Seidelmann, editor.
+
+ This algorithm implements three different easter
+ calculation methods:
+
+ 1 - Original calculation in Julian calendar, valid in
+ dates after 326 AD
+ 2 - Original method, with date converted to Gregorian
+ calendar, valid in years 1583 to 4099
+ 3 - Revised method, in Gregorian calendar, valid in
+ years 1583 to 4099 as well
+
+ These methods are represented by the constants:
+
+ * ``EASTER_JULIAN = 1``
+ * ``EASTER_ORTHODOX = 2``
+ * ``EASTER_WESTERN = 3``
+
+ The default method is method 3.
+
+ More about the algorithm may be found at:
+
+ `GM Arts: Easter Algorithms `_
+
+ 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/python3.5/site-packages/dateutil/parser/__init__.py b/venv/lib/python3.5/site-packages/dateutil/parser/__init__.py
new file mode 100644
index 0000000..216762c
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/parser/__init__.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+from ._parser import parse, parser, parserinfo
+from ._parser import DEFAULTPARSER, DEFAULTTZPARSER
+from ._parser import UnknownTimezoneWarning
+
+from ._parser import __doc__
+
+from .isoparser import isoparser, isoparse
+
+__all__ = ['parse', 'parser', 'parserinfo',
+ 'isoparse', 'isoparser',
+ '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/python3.5/site-packages/dateutil/parser/__pycache__/__init__.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/parser/__pycache__/__init__.cpython-35.pyc
new file mode 100644
index 0000000..071e8da
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/parser/__pycache__/__init__.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/parser/__pycache__/_parser.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/parser/__pycache__/_parser.cpython-35.pyc
new file mode 100644
index 0000000..aace9c3
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/parser/__pycache__/_parser.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/parser/__pycache__/isoparser.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/parser/__pycache__/isoparser.cpython-35.pyc
new file mode 100644
index 0000000..f20721c
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/parser/__pycache__/isoparser.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/parser/_parser.py b/venv/lib/python3.5/site-packages/dateutil/parser/_parser.py
new file mode 100644
index 0000000..0eac592
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/parser/_parser.py
@@ -0,0 +1,1534 @@
+# -*- coding: utf-8 -*-
+"""
+This module offers a generic date/time string parser which is able to parse
+most known formats to represent a date and/or time.
+
+This module attempts to be forgiving with regards to unlikely input formats,
+returning a datetime object even for dates which are ambiguous. If an element
+of a date/time stamp is omitted, the following rules are applied:
+
+- If AM or PM is left unspecified, a 24-hour clock is assumed, however, an hour
+ on a 12-hour clock (``0 <= hour <= 12``) *must* be specified if AM or PM is
+ specified.
+- If a time zone is omitted, a timezone-naive datetime is returned.
+
+If any other elements are missing, they are taken from the
+:class:`datetime.datetime` object passed to the parameter ``default``. If this
+results in a day number exceeding the valid number of days per month, the
+value falls back to the end of the month.
+
+Additional resources about date/time string formats can be found below:
+
+- `A summary of the international standard date and time notation
+ `_
+- `W3C Date and Time Formats `_
+- `Time Formats (Planetary Rings Node) `_
+- `CPAN ParseDate module
+ `_
+- `Java SimpleDateFormat Class
+ `_
+"""
+from __future__ import unicode_literals
+
+import datetime
+import re
+import string
+import time
+import warnings
+
+from calendar import monthrange
+from io import StringIO
+
+import six
+from six import binary_type, integer_types, text_type
+
+from decimal import Decimal
+
+from warnings import warn
+
+from .. import relativedelta
+from .. import tz
+
+__all__ = ["parse", "parserinfo"]
+
+
+# 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, (binary_type, 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"]
+ 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):
+ if year < 100 and not century_specified:
+ year += self._century
+ if abs(year - self._year) >= 50:
+ if year < self._year:
+ year += 100
+ else:
+ year -= 100
+ return year
+
+ def validate(self, res):
+ # move to info
+ if res.year is not None:
+ res.year = self.convertyear(res.year, res.century_specified)
+
+ if res.tzoffset == 0 and not res.tzname or res.tzname == 'Z':
+ res.tzname = "UTC"
+ res.tzoffset = 0
+ elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname):
+ res.tzoffset = 0
+ return True
+
+
+class _ymd(list):
+ def __init__(self, *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 leapyear
+ 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_ymd(self, yearfirst, dayfirst):
+ len_ymd = len(self)
+ year, month, day = (None, None, None)
+
+ 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]
+ del self[mstridx]
+
+ if len_ymd > 1 or mstridx is None:
+ if self[0] > 31:
+ year = self[0]
+ else:
+ day = self[0]
+
+ elif len_ymd == 2:
+ # Two members with numbers
+ if self[0] > 31:
+ # 99-01
+ year, month = self
+ elif self[1] > 31:
+ # 01-99
+ month, year = self
+ elif dayfirst and self[1] <= 12:
+ # 13-01
+ day, month = self
+ else:
+ # 01-13
+ month, day = self
+
+ elif len_ymd == 3:
+ # Three members
+ if mstridx == 0:
+ 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 precendence to day-first, since
+ # two-digit years is usually hand-written.
+ day, month, year = self
+
+ elif mstridx == 2:
+ # WTF!?
+ if self[1] > 31:
+ # 01-99-Jan
+ day, year, month = self
+ else:
+ # 99-01-Jan
+ year, day, month = self
+
+ else:
+ if (self[0] > 31 or
+ self.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 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 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 ValueError("Unknown string format:", timestr)
+
+ if len(res) == 0:
+ raise ValueError("String does not contain a date:", timestr)
+
+ ret = self._build_naive(res, default)
+
+ 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" preceeding 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))
+
+ 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 *dont* 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 _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 _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)
+
+ 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
+
+ def _build_tzinfo(self, tzinfos, tzname, tzoffset):
+ if callable(tzinfos):
+ tzdata = tzinfos(tzname, tzoffset)
+ else:
+ tzdata = tzinfos.get(tzname)
+
+ if isinstance(tzdata, datetime.tzinfo):
+ tzinfo = tzdata
+ elif isinstance(tzdata, text_type):
+ tzinfo = tz.tzstr(tzdata)
+ elif isinstance(tzdata, integer_types):
+ tzinfo = tz.tzoffset(tzname, tzdata)
+ else:
+ raise ValueError("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.tzutc())
+
+ elif res.tzoffset == 0:
+ aware = naive.replace(tzinfo=tz.tzutc())
+
+ 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 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 _to_decimal(self, val):
+ try:
+ return Decimal(val)
+ except Exception as e:
+ msg = "Could not convert %s to decimal" % val
+ six.raise_from(ValueError(msg), e)
+
+
+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 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/python3.5/site-packages/dateutil/parser/isoparser.py b/venv/lib/python3.5/site-packages/dateutil/parser/isoparser.py
new file mode 100644
index 0000000..844bb5d
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/parser/isoparser.py
@@ -0,0 +1,402 @@
+# -*- 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.
+"""
+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.sss`` or ``hh:mm:ss.ssssss`` (3-6 sub-second digits)
+
+ Midnight is a special case for `hh`, as the standard supports both
+ 00:00 and 24:00 as a representation.
+
+ .. 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.
+ """
+ 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')
+
+ 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
+ """
+ return time(*self._parse_isotime(timestr))
+
+ @_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
+ _MICROSECOND_END_REGEX = re.compile(b'[-+Z]+')
+ _DATE_SEP = b'-'
+ _TIME_SEP = b':'
+ _MICRO_SEP = b'.'
+
+ 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'-+Z':
+ # 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:
+ # Microsecond
+ if timestr[pos:pos + 1] != self._MICRO_SEP:
+ continue
+
+ pos += 1
+ us_str = self._MICROSECOND_END_REGEX.split(timestr[pos:pos + 6],
+ 1)[0]
+
+ components[comp] = int(us_str) * 10**(6 - len(us_str))
+ pos += len(us_str)
+
+ 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')
+ components[0] = 0
+
+ return components
+
+ def _parse_tzstr(self, tzstr, zero_as_utc=True):
+ if tzstr == b'Z':
+ return tz.tzutc()
+
+ 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.tzutc()
+ 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/python3.5/site-packages/dateutil/relativedelta.py b/venv/lib/python3.5/site-packages/dateutil/relativedelta.py
new file mode 100644
index 0000000..584ed5a
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/relativedelta.py
@@ -0,0 +1,585 @@
+# -*- 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 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 aritmetic operation on the original datetime value
+ with the information in the relativedelta.
+
+ weekday:
+ One of the weekday instances (MO, TU, etc). These instances may
+ receive a parameter N, specifying the Nth weekday, which could
+ be positive or negative (like MO(+1) or MO(-2). Not specifying
+ it is the same as specifying +1. You can also use an integer,
+ where 0=MO.
+
+ leapdays:
+ Will add given days to the date found, if year is a leap
+ year, and the date found is post 28 of february.
+
+ yearday, nlyearday:
+ Set the yearday or the non-leap year day (jump leap days).
+ These are converted to day/month/leapdays information.
+
+ Here is the behavior of operations with relativedelta:
+
+ 1. Calculate the absolute year, using the 'year' argument, or the
+ original datetime year, if the argument is not present.
+
+ 2. Add the relative 'years' argument to the absolute year.
+
+ 3. Do steps 1 and 2 for month/months.
+
+ 4. Calculate the absolute day, using the 'day' argument, or the
+ original datetime day, if the argument is not present. Then,
+ subtract from the day until it fits in the year and month
+ found after their operations.
+
+ 5. Add the relative 'days' argument to the absolute day. Notice
+ that the 'weeks' argument is multiplied by 7 and added to
+ 'days'.
+
+ 6. Do steps 1 and 2 for hour/hours, minute/minutes, second/seconds,
+ microsecond/microseconds.
+
+ 7. If the 'weekday' argument is present, calculate the weekday,
+ with the given (wday, nth) tuple. wday is the index of the
+ weekday (0-6, 0=Mon), and nth is the number of weeks to add
+ forward or backward, depending on its signal. Notice that if
+ the calculated date is already Monday, for example, using
+ (0, 1) or (0, -1) won't change the day.
+ """
+
+ def __init__(self, dt1=None, dt2=None,
+ years=0, months=0, days=0, leapdays=0, weeks=0,
+ hours=0, minutes=0, seconds=0, microseconds=0,
+ year=None, month=None, day=None, weekday=None,
+ yearday=None, nlyearday=None,
+ hour=None, minute=None, second=None, microsecond=None):
+
+ 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/python3.5/site-packages/dateutil/rrule.py b/venv/lib/python3.5/site-packages/dateutil/rrule.py
new file mode 100644
index 0000000..ef4607a
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/rrule.py
@@ -0,0 +1,1669 @@
+# -*- 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
+from .tz import tzutc, tzlocal
+
+# 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 penality.
+ def count(self):
+ """ Returns the number of recurrences in this set. It will have go
+ trough the whole recurrence, if this hasn't been done before. """
+ if self._len is None:
+ for x in self:
+ pass
+ return self._len
+
+ def before(self, dt, inc=False):
+ """ Returns the last recurrence before the given datetime instance. The
+ inc keyword defines what happens if dt is an occurrence. With
+ inc=True, if dt itself is an occurrence, it will be returned. """
+ if self._cache_complete:
+ gen = self._cache
+ else:
+ gen = self
+ last = None
+ if inc:
+ for i in gen:
+ if i > dt:
+ break
+ last = i
+ else:
+ for i in gen:
+ if i >= dt:
+ break
+ last = i
+ return last
+
+ def after(self, dt, inc=False):
+ """ Returns the first recurrence after the given datetime instance. The
+ inc keyword defines what happens if dt is an occurrence. With
+ inc=True, if dt itself is an occurrence, it will be returned. """
+ if self._cache_complete:
+ gen = self._cache
+ else:
+ gen = self
+ if inc:
+ for i in gen:
+ if i >= dt:
+ return i
+ else:
+ for i in gen:
+ if i > dt:
+ return i
+ return None
+
+ def xafter(self, dt, count=None, inc=False):
+ """
+ Generator which yields up to `count` recurrences after the given
+ datetime instance, equivalent to `after`.
+
+ :param dt:
+ The datetime at which to start generating recurrences.
+
+ :param count:
+ The maximum number of recurrences to generate. If `None` (default),
+ dates are generated until the recurrence rule is exhausted.
+
+ :param inc:
+ If `dt` is an instance of the rule and `inc` is `True`, it is
+ included in the output.
+
+ :yields: Yields a sequence of `datetime` objects.
+ """
+
+ if self._cache_complete:
+ gen = self._cache
+ else:
+ gen = self
+
+ # Select the comparison function
+ if inc:
+ comp = lambda dc, dtc: dc >= dtc
+ else:
+ comp = lambda dc, dtc: dc > dtc
+
+ # Generate dates
+ n = 0
+ for d in gen:
+ if comp(d, dt):
+ 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 cache:
+ If given, it must be a boolean value specifying to enable or disable
+ caching of results. If you will use the same rrule instance multiple
+ times, enabling caching will improve the performance considerably.
+ :param dtstart:
+ The recurrence start. Besides being the base for the recurrence,
+ missing parameters in the final recurrence instances will also be
+ extracted from this date. If not given, datetime.now() will be used
+ instead.
+ :param interval:
+ The interval between each freq iteration. For example, when using
+ YEARLY, an interval of 2 means once every two years, but with HOURLY,
+ it means once every two hours. The default interval is 1.
+ :param wkst:
+ The week start day. Must be one of the MO, TU, WE constants, or an
+ integer, specifying the first day of the week. This will affect
+ recurrences based on weekly periods. The default week start is got
+ from calendar.firstweekday(), and may be modified by
+ calendar.setfirstweekday().
+ :param count:
+ How many occurrences will be generated.
+
+ .. note::
+ As of version 2.5.0, the use of the ``until`` keyword together
+ with the ``count`` keyword is deprecated per RFC-5545 Sec. 3.3.10.
+ :param until:
+ If given, this must be a datetime instance, that will specify the
+ limit of the recurrence. The last recurrence in the rule is the greatest
+ datetime that is less than or equal to the value specified in the
+ ``until`` parameter.
+
+ .. note::
+ As of version 2.5.0, the use of the ``until`` keyword together
+ with the ``count`` keyword is deprecated per RFC-5545 Sec. 3.3.10.
+ :param bysetpos:
+ If given, it must be either an integer, or a sequence of integers,
+ positive or negative. Each given integer will specify an occurrence
+ number, corresponding to the nth occurrence of the rule inside the
+ frequency period. For example, a bysetpos of -1 if combined with a
+ MONTHLY frequency, and a byweekday of (MO, TU, WE, TH, FR), will
+ result in the last work day of every month.
+ :param bymonth:
+ If given, it must be either an integer, or a sequence of integers,
+ meaning the months to apply the recurrence to.
+ :param bymonthday:
+ If given, it must be either an integer, or a sequence of integers,
+ meaning the month days to apply the recurrence to.
+ :param byyearday:
+ If given, it must be either an integer, or a sequence of integers,
+ meaning the year days to apply the recurrence to.
+ :param byweekno:
+ If given, it must be either an integer, or a sequence of integers,
+ meaning the week numbers to apply the recurrence to. Week numbers
+ have the meaning described in ISO8601, that is, the first week of
+ the year is that containing at least four days of the new year.
+ :param byweekday:
+ If given, it must be either an integer (0 == MO), a sequence of
+ integers, one of the weekday constants (MO, TU, etc), or a sequence
+ of these constants. When given, these variables will define the
+ weekdays where the recurrence will be applied. It's also possible to
+ use an argument n for the weekday instances, which will mean the nth
+ occurrence of this weekday in the period. For example, with MONTHLY,
+ or with YEARLY and BYMONTH, using FR(+1) in byweekday will specify the
+ first friday of the month where the recurrence happens. Notice that in
+ the RFC documentation, this is specified as BYDAY, but was renamed to
+ avoid the ambiguity of that keyword.
+ :param byhour:
+ If given, it must be either an integer, or a sequence of integers,
+ meaning the hours to apply the recurrence to.
+ :param byminute:
+ If given, it must be either an integer, or a sequence of integers,
+ meaning the minutes to apply the recurrence to.
+ :param bysecond:
+ If given, it must be either an integer, or a sequence of integers,
+ meaning the seconds to apply the recurrence to.
+ :param byeaster:
+ If given, it must be either an integer, or a sequence of integers,
+ positive or negative. Each integer will define an offset from the
+ Easter Sunday. Passing the offset 0 to byeaster will yield the Easter
+ Sunday itself. This is an extension to the RFC specification.
+ """
+ def __init__(self, freq, dtstart=None,
+ interval=1, wkst=None, count=None, until=None, bysetpos=None,
+ bymonth=None, bymonthday=None, byyearday=None, byeaster=None,
+ byweekno=None, byweekday=None,
+ byhour=None, byminute=None, bysecond=None,
+ cache=False):
+ super(rrule, self).__init__(cache)
+ global easter
+ if not dtstart:
+ dtstart = datetime.datetime.now().replace(microsecond=0)
+ elif not isinstance(dtstart, datetime.datetime):
+ dtstart = datetime.datetime.fromordinal(dtstart.toordinal())
+ else:
+ dtstart = dtstart.replace(microsecond=0)
+ self._dtstart = dtstart
+ self._tzinfo = dtstart.tzinfo
+ self._freq = freq
+ self._interval = interval
+ self._count = count
+
+ # Cache the original byxxx rules, if they are provided, as the _byxxx
+ # attributes do not necessarily map to the inputs, and this can be
+ # a problem in generating the strings. Only store things if they've
+ # been supplied (the string retrieval will just use .get())
+ self._original_rule = {}
+
+ if until and not isinstance(until, datetime.datetime):
+ until = datetime.datetime.fromordinal(until.toordinal())
+ self._until = until
+
+ if 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):
+
+ _freq_map = {"YEARLY": YEARLY,
+ "MONTHLY": MONTHLY,
+ "WEEKLY": WEEKLY,
+ "DAILY": DAILY,
+ "HOURLY": HOURLY,
+ "MINUTELY": MINUTELY,
+ "SECONDLY": SECONDLY}
+
+ _weekday_map = {"MO": 0, "TU": 1, "WE": 2, "TH": 3,
+ "FR": 4, "SA": 5, "SU": 6}
+
+ def _handle_int(self, rrkwargs, name, value, **kwargs):
+ rrkwargs[name.lower()] = int(value)
+
+ def _handle_int_list(self, rrkwargs, name, value, **kwargs):
+ rrkwargs[name.lower()] = [int(x) for x in value.split(',')]
+
+ _handle_INTERVAL = _handle_int
+ _handle_COUNT = _handle_int
+ _handle_BYSETPOS = _handle_int_list
+ _handle_BYMONTH = _handle_int_list
+ _handle_BYMONTHDAY = _handle_int_list
+ _handle_BYYEARDAY = _handle_int_list
+ _handle_BYEASTER = _handle_int_list
+ _handle_BYWEEKNO = _handle_int_list
+ _handle_BYHOUR = _handle_int_list
+ _handle_BYMINUTE = _handle_int_list
+ _handle_BYSECOND = _handle_int_list
+
+ def _handle_FREQ(self, rrkwargs, name, value, **kwargs):
+ rrkwargs["freq"] = self._freq_map[value]
+
+ def _handle_UNTIL(self, rrkwargs, name, value, **kwargs):
+ global parser
+ if not parser:
+ from dateutil import parser
+ try:
+ rrkwargs["until"] = parser.parse(value,
+ ignoretz=kwargs.get("ignoretz"),
+ tzinfos=kwargs.get("tzinfos"))
+ except ValueError:
+ raise ValueError("invalid until date")
+
+ def _handle_WKST(self, rrkwargs, name, value, **kwargs):
+ rrkwargs["wkst"] = self._weekday_map[value]
+
+ def _handle_BYWEEKDAY(self, rrkwargs, name, value, **kwargs):
+ """
+ Two ways to specify this: +1MO or MO(+1)
+ """
+ l = []
+ for wday in value.split(','):
+ if '(' in wday:
+ # If it's of the form TH(+1), etc.
+ splt = wday.split('(')
+ w = splt[0]
+ n = int(splt[1][:-1])
+ elif len(wday):
+ # If it's of the form +1MO
+ for i in range(len(wday)):
+ if wday[i] not in '+-0123456789':
+ break
+ n = wday[:i] or None
+ w = wday[i:]
+ if n:
+ n = int(n)
+ else:
+ raise ValueError("Invalid (empty) BYDAY specification.")
+
+ l.append(weekdays[self._weekday_map[w]](n))
+ rrkwargs["byweekday"] = l
+
+ _handle_BYDAY = _handle_BYWEEKDAY
+
+ def _parse_rfc_rrule(self, line,
+ dtstart=None,
+ cache=False,
+ ignoretz=False,
+ tzinfos=None):
+ if line.find(':') != -1:
+ name, value = line.split(':')
+ if name != "RRULE":
+ raise ValueError("unknown parameter name")
+ else:
+ value = line
+ rrkwargs = {}
+ for pair in value.split(';'):
+ name, value = pair.split('=')
+ name = name.upper()
+ value = value.upper()
+ try:
+ getattr(self, "_handle_"+name)(rrkwargs, name, value,
+ ignoretz=ignoretz,
+ tzinfos=tzinfos)
+ except AttributeError:
+ raise ValueError("unknown parameter '%s'" % name)
+ except (KeyError, ValueError):
+ raise ValueError("invalid '%s': %s" % (name, value))
+ return rrule(dtstart=dtstart, cache=cache, **rrkwargs)
+
+ def _parse_rfc(self, s,
+ dtstart=None,
+ cache=False,
+ unfold=False,
+ forceset=False,
+ compatible=False,
+ ignoretz=False,
+ 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":
+ for parm in parms:
+ if parm != "VALUE=DATE-TIME":
+ raise ValueError("unsupported EXDATE parm: "+parm)
+ exdatevals.append(value)
+ elif name == "DTSTART":
+ # RFC 5445 3.8.2.4: The VALUE parameter is optional, but
+ # may be found only once.
+ value_found = False
+ TZID = None
+ valid_values = {"VALUE=DATE-TIME", "VALUE=DATE"}
+ for parm in parms:
+ if parm.startswith("TZID="):
+ try:
+ tzkey = TZID_NAMES[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
+ if parm not in valid_values:
+ raise ValueError("unsupported DTSTART parm: "+parm)
+ else:
+ if value_found:
+ msg = ("Duplicate value parameter found in " +
+ "DTSTART: " + parm)
+ raise ValueError(msg)
+ value_found = True
+ if not parser:
+ from dateutil import parser
+ dtstart = parser.parse(value, ignoretz=ignoretz,
+ tzinfos=tzinfos)
+ if TZID is not None:
+ if dtstart.tzinfo is None:
+ dtstart = dtstart.replace(tzinfo=TZID)
+ else:
+ raise ValueError('DTSTART specifies multiple timezones')
+ else:
+ raise ValueError("unsupported property: "+name)
+ if (forceset or len(rrulevals) > 1 or rdatevals
+ or exrulevals or exdatevals):
+ if not parser and (rdatevals or exdatevals):
+ from dateutil import parser
+ rset = rruleset(cache=cache)
+ for value in rrulevals:
+ rset.rrule(self._parse_rfc_rrule(value, dtstart=dtstart,
+ ignoretz=ignoretz,
+ tzinfos=tzinfos))
+ for value in rdatevals:
+ for datestr in value.split(','):
+ rset.rdate(parser.parse(datestr,
+ ignoretz=ignoretz,
+ tzinfos=tzinfos))
+ for value in exrulevals:
+ rset.exrule(self._parse_rfc_rrule(value, dtstart=dtstart,
+ ignoretz=ignoretz,
+ tzinfos=tzinfos))
+ for value in exdatevals:
+ for datestr in value.split(','):
+ rset.exdate(parser.parse(datestr,
+ ignoretz=ignoretz,
+ tzinfos=tzinfos))
+ if compatible and dtstart:
+ rset.rdate(dtstart)
+ return rset
+ else:
+ return self._parse_rfc_rrule(rrulevals[0],
+ dtstart=dtstart,
+ cache=cache,
+ ignoretz=ignoretz,
+ tzinfos=tzinfos)
+
+ def __call__(self, s, **kwargs):
+ return self._parse_rfc(s, **kwargs)
+
+
+rrulestr = _rrulestr()
+
+# vim:ts=4:sw=4:et
diff --git a/venv/lib/python3.5/site-packages/dateutil/tz/__init__.py b/venv/lib/python3.5/site-packages/dateutil/tz/__init__.py
new file mode 100644
index 0000000..df03deb
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/tz/__init__.py
@@ -0,0 +1,15 @@
+from .tz import *
+
+#: Convenience constant providing a :class:`tzutc()` instance
+#:
+#: .. versionadded:: 2.7.0
+UTC = tzutc()
+
+__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/python3.5/site-packages/dateutil/tz/__pycache__/__init__.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/tz/__pycache__/__init__.cpython-35.pyc
new file mode 100644
index 0000000..dd3b409
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/tz/__pycache__/__init__.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/tz/__pycache__/_common.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/tz/__pycache__/_common.cpython-35.pyc
new file mode 100644
index 0000000..27fe19d
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/tz/__pycache__/_common.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/tz/__pycache__/_factories.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/tz/__pycache__/_factories.cpython-35.pyc
new file mode 100644
index 0000000..75f05b7
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/tz/__pycache__/_factories.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/tz/__pycache__/tz.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/tz/__pycache__/tz.cpython-35.pyc
new file mode 100644
index 0000000..5236751
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/tz/__pycache__/tz.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/tz/__pycache__/win.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/tz/__pycache__/win.cpython-35.pyc
new file mode 100644
index 0000000..55b2c4d
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/tz/__pycache__/win.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/tz/_common.py b/venv/lib/python3.5/site-packages/dateutil/tz/_common.py
new file mode 100644
index 0000000..ccabb7d
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/tz/_common.py
@@ -0,0 +1,415 @@
+from six import PY3
+
+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
+ """
+ def adjust_encoding(*args, **kwargs):
+ name = namefunc(*args, **kwargs)
+ if name is not None and not PY3:
+ name = name.encode()
+
+ return name
+
+ return adjust_encoding
+
+
+# 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
+ occurence, 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
+ occurance, 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/python3.5/site-packages/dateutil/tz/_factories.py b/venv/lib/python3.5/site-packages/dateutil/tz/_factories.py
new file mode 100644
index 0000000..de2e0c1
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/tz/_factories.py
@@ -0,0 +1,49 @@
+from datetime import timedelta
+
+
+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 = {}
+
+ 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))
+ return instance
+
+
+class _TzStrFactory(_TzFactory):
+ def __init__(cls, *args, **kwargs):
+ cls.__instances = {}
+
+ 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))
+ return instance
+
diff --git a/venv/lib/python3.5/site-packages/dateutil/tz/tz.py b/venv/lib/python3.5/site-packages/dateutil/tz/tz.py
new file mode 100644
index 0000000..bfb4b47
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/tz/tz.py
@@ -0,0 +1,1656 @@
+# -*- coding: utf-8 -*-
+"""
+This module offers timezone implementations subclassing the abstract
+:py:`datetime.tzinfo` type. There are classes to handle tzfile format files
+(usually are in :file:`/etc/localtime`, :file:`/usr/share/zoneinfo`, etc), TZ
+environment string (in all known formats), given ranges (with help from
+relative deltas), local machine timezone, fixed offset timezone, and UTC
+timezone.
+"""
+import datetime
+import struct
+import time
+import sys
+import os
+import bisect
+
+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
+
+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__
+
+
+@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=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 thant 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
+ `_
+ """
+
+ 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 = _ContextWrapper(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]
+ # Round to full-minutes if that's not the case. Python's
+ # datetime doesn't accept sub-minute timezones. Check
+ # http://python.org/sf/1447945 for some information.
+ gmtoff = 60 * ((gmtoff + 30) // 60)
+ 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.
+ laststdoffset = None
+ out.trans_list = []
+ for i, tti in enumerate(out.trans_idx):
+ if not tti.isdst:
+ offset = tti.offset
+ laststdoffset = offset
+ else:
+ if laststdoffset is not None:
+ # Store the DST offset as well and update it in the list
+ tti.dstoffset = tti.offset - laststdoffset
+ out.trans_idx[i] = tti
+
+ offset = laststdoffset or 0
+
+ out.trans_list.append(out.trans_list_utc[i] + offset)
+
+ # In case we missed any DST offsets on the way in for some reason, make
+ # a second pass over the list, looking for the /next/ DST offset.
+ laststdoffset = None
+ for i in reversed(range(len(out.trans_idx))):
+ tti = out.trans_idx[i]
+ if tti.isdst:
+ if not (tti.dstoffset or laststdoffset is None):
+ tti.dstoffset = tti.offset - laststdoffset
+ else:
+ laststdoffset = tti.offset
+
+ if not isinstance(tti.dstoffset, datetime.timedelta):
+ tti.dstoffset = datetime.timedelta(seconds=tti.dstoffset)
+
+ out.trans_idx[i] = tti
+
+ 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 = _ContextWrapper(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):
+ def __init__(self):
+
+ self.__instances = {}
+ 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)):
+ # tzlocal is slightly more complicated than the other
+ # time zone providers because it depends on environment
+ # at construction time, so don't cache that.
+ self.__instances[name] = rv
+
+ return rv
+
+ def cache_clear(self):
+ with self._cache_lock:
+ self.__instances = {}
+
+ @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:
+ if name.startswith(":"):
+ name = name[1:]
+ if os.path.isabs(name):
+ if os.path.isfile(name):
+ tz = tzfile(name)
+ else:
+ tz = None
+ else:
+ for path in TZPATHS:
+ filepath = os.path.join(path, name)
+ if not os.path.isfile(filepath):
+ filepath = filepath.replace(' ', '_')
+ if not os.path.isfile(filepath):
+ continue
+ try:
+ tz = tzfile(filepath)
+ break
+ except (IOError, OSError, ValueError):
+ pass
+ else:
+ tz = None
+ if tzwin is not None:
+ try:
+ tz = tzwin(name)
+ except WindowsError:
+ tz = None
+
+ if not tz:
+ from dateutil.zoneinfo import get_zonefile_instance
+ tz = get_zonefile_instance().get(name)
+
+ if not tz:
+ for c in name:
+ # name must have at least one offset to be a tzstr
+ if c in "0123456789":
+ try:
+ tz = tzstr(name)
+ except ValueError:
+ pass
+ break
+ else:
+ if name in ("GMT", "UTC"):
+ tz = tzutc()
+ elif name in time.tzname:
+ tz = tzlocal()
+ return tz
+
+ 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(tzutc()).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:
+ 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()
+
+
+class _ContextWrapper(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/python3.5/site-packages/dateutil/tz/win.py b/venv/lib/python3.5/site-packages/dateutil/tz/win.py
new file mode 100644
index 0000000..def4353
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/tz/win.py
@@ -0,0 +1,331 @@
+# 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 if 114, not -114.
+
+ """
+ resource = self.p_wchar()
+ lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR)
+ nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0)
+ return resource[:nchar]
+
+ def name_from_string(self, tzname_str):
+ """
+ Parse strings as returned from the Windows registry into the time zone
+ name as defined in the registry.
+
+ >>> from dateutil.tzwin import tzres
+ >>> tzr = tzres()
+ >>> print(tzr.name_from_string('@tzres.dll,-251'))
+ 'Dateline Daylight Time'
+ >>> print(tzr.name_from_string('Eastern Standard Time'))
+ 'Eastern Standard Time'
+
+ :param tzname_str:
+ A timezone name string as returned from a Windows registry key.
+
+ :return:
+ Returns the localized timezone string from tzres.dll if the string
+ is of the form `@tzres.dll,-offset`, else returns the input string.
+ """
+ if not tzname_str.startswith('@'):
+ return tzname_str
+
+ name_splt = tzname_str.split(',-')
+ try:
+ offset = int(name_splt[1])
+ except:
+ raise ValueError("Malformed timezone string.")
+
+ return self.load_name(offset)
+
+
+class tzwinbase(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 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):
+
+ 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):
+ 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/python3.5/site-packages/dateutil/tzwin.py b/venv/lib/python3.5/site-packages/dateutil/tzwin.py
new file mode 100644
index 0000000..cebc673
--- /dev/null
+++ b/venv/lib/python3.5/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/python3.5/site-packages/dateutil/utils.py b/venv/lib/python3.5/site-packages/dateutil/utils.py
new file mode 100644
index 0000000..29f8181
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/dateutil/utils.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+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 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/python3.5/site-packages/dateutil/zoneinfo/__init__.py b/venv/lib/python3.5/site-packages/dateutil/zoneinfo/__init__.py
new file mode 100644
index 0000000..34f11ad
--- /dev/null
+++ b/venv/lib/python3.5/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/python3.5/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-35.pyc
new file mode 100644
index 0000000..b73bfa7
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-35.pyc b/venv/lib/python3.5/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-35.pyc
new file mode 100644
index 0000000..dda0415
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz b/venv/lib/python3.5/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz
new file mode 100644
index 0000000..5b76ea1
Binary files /dev/null and b/venv/lib/python3.5/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz differ
diff --git a/venv/lib/python3.5/site-packages/dateutil/zoneinfo/rebuild.py b/venv/lib/python3.5/site-packages/dateutil/zoneinfo/rebuild.py
new file mode 100644
index 0000000..78f0d1a
--- /dev/null
+++ b/venv/lib/python3.5/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/python3.5/site-packages/docutils-0.14.dist-info/DESCRIPTION.rst b/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/DESCRIPTION.rst
new file mode 100644
index 0000000..d1df4d0
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/DESCRIPTION.rst
@@ -0,0 +1,5 @@
+Docutils is a modular system for processing documentation
+into useful formats, such as HTML, XML, and LaTeX. For
+input Docutils supports reStructuredText, an easy-to-read,
+what-you-see-is-what-you-get plaintext markup syntax.
+
diff --git a/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/INSTALLER b/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/METADATA b/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/METADATA
new file mode 100644
index 0000000..b5103b6
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/METADATA
@@ -0,0 +1,54 @@
+Metadata-Version: 2.0
+Name: docutils
+Version: 0.14
+Summary: Docutils -- Python Documentation Utilities
+Home-page: http://docutils.sourceforge.net/
+Author: docutils-develop list
+Author-email: docutils-develop@lists.sourceforge.net
+License: public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)
+Platform: OS-independent
+Classifier: Development Status :: 4 - Beta
+Classifier: Environment :: Console
+Classifier: Intended Audience :: End Users/Desktop
+Classifier: Intended Audience :: Other Audience
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: System Administrators
+Classifier: License :: Public Domain
+Classifier: License :: OSI Approved :: Python Software Foundation License
+Classifier: License :: OSI Approved :: BSD License
+Classifier: License :: OSI Approved :: GNU General Public License (GPL)
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python :: 2.4
+Classifier: Programming Language :: Python :: 2.5
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Documentation
+Classifier: Topic :: Software Development :: Documentation
+Classifier: Topic :: Text Processing
+Classifier: Natural Language :: English
+Classifier: Natural Language :: Afrikaans
+Classifier: Natural Language :: Catalan
+Classifier: Natural Language :: Chinese (Simplified)
+Classifier: Natural Language :: Chinese (Traditional)
+Classifier: Natural Language :: Czech
+Classifier: Natural Language :: Dutch
+Classifier: Natural Language :: Esperanto
+Classifier: Natural Language :: Finnish
+Classifier: Natural Language :: French
+Classifier: Natural Language :: Galician
+Classifier: Natural Language :: German
+Classifier: Natural Language :: Italian
+Classifier: Natural Language :: Japanese
+Classifier: Natural Language :: Polish
+Classifier: Natural Language :: Portuguese (Brazilian)
+Classifier: Natural Language :: Russian
+Classifier: Natural Language :: Slovak
+Classifier: Natural Language :: Spanish
+Classifier: Natural Language :: Swedish
+
+Docutils is a modular system for processing documentation
+into useful formats, such as HTML, XML, and LaTeX. For
+input Docutils supports reStructuredText, an easy-to-read,
+what-you-see-is-what-you-get plaintext markup syntax.
+
diff --git a/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/RECORD b/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/RECORD
new file mode 100644
index 0000000..d72388a
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/RECORD
@@ -0,0 +1,331 @@
+docutils/__init__.py,sha256=qBqQE3DQczLVJe7YInpIyNrienwW0ZMoem0SQ1k_N2g,8963
+docutils/_compat.py,sha256=x6tirqUs-HrHuXl6ggD9m1Hl9y4a1clQfvrkkBtmZRI,1577
+docutils/core.py,sha256=a-uPGEnQqhxHIyWZNfckm8J3xHkS9S1nuBA--JWo664,29576
+docutils/examples.py,sha256=nlPe2PYXNvz5ZAVRu7xHHiV7qML7s-eyUu4Gun7j3SY,3959
+docutils/frontend.py,sha256=pCDFpiVIxnHjYiwr_vFmDhhyWSIv12w9FTPUStX-k0o,36893
+docutils/io.py,sha256=1CB-7SA-t3Em8N9b1wfvmjOTMW3GKHt4s3gIPCWapdE,17449
+docutils/nodes.py,sha256=Rr5AwF7C6MW6oP_aw-ja-Nm72iqpIQ5bAhIqH5qc3tU,77200
+docutils/statemachine.py,sha256=ar1SBcvHQWDcLrTRWYOR-9Js7IuVxOQCHG4K2Fy0Uak,57620
+docutils/languages/__init__.py,sha256=XqnVVaIrbKV0rLBaeFV7W_YkniFTymc8ecmzTD7o2ek,1626
+docutils/languages/af.py,sha256=6wl-XbsF8wbSiBs5IORna4Lz-WPqnnye_6qr8Cbh_1c,1824
+docutils/languages/ca.py,sha256=pnkOJbqff_hPefBYAy4Sw8V1PNRChPyKE6TV_S8SCzU,1906
+docutils/languages/cs.py,sha256=DJLwqe6tQDfFSBkmT1T3Pgc_GEB2Cp0Gu4wGtBeF-os,1894
+docutils/languages/da.py,sha256=9rMaO-mkPO6BbS02nNtStg2LNOBkJGLYsdDYJ_bsav0,1872
+docutils/languages/de.py,sha256=AQTUIl4j3x43kQi5800-lobZHsh06VOsrxfAjS5FHGM,1722
+docutils/languages/en.py,sha256=tWVrPr6593mIzFokX_4Hw1fC6ZWC8GPdgpc72jmQq1g,1848
+docutils/languages/eo.py,sha256=3vzdUkAdQvYylw2wzuDqTfqzZ-8YojrVzSs6ywQxDuk,1930
+docutils/languages/es.py,sha256=yjnBsKY1BqIXSWK7gCAUSc01CgZp72gJNaORQosPEgk,1928
+docutils/languages/fa.py,sha256=h6nMMv_x56PsYLhpTP8ZSs5buohtxu_fRBs4wDZZJBA,1974
+docutils/languages/fi.py,sha256=BAYNU7znDucYvVfaN3BOVVVnTcRGjkIZzurBF11S9J8,1958
+docutils/languages/fr.py,sha256=EcvFQHiD6fRFWiTQ8bBzs8XEqmvn-HLY-srcU6YktcY,1825
+docutils/languages/gl.py,sha256=YbgUuncmDraQCGc1BMH-J8RR3YPf4A1CJARkJaIJ3hQ,2006
+docutils/languages/he.py,sha256=Mg40ypidfnzGiEAZ1c39QOL8kF47gwG9HQpNipy9ruY,2649
+docutils/languages/it.py,sha256=Z3eh86YrmB8aT_Dl56X7W2gqpJgYK0v0QnJ07hekXlM,1808
+docutils/languages/ja.py,sha256=Ga0KgkqV17n-lzQrL2r9EztE0Ew2H4g5G9CZWBIENUw,1908
+docutils/languages/lt.py,sha256=v6cQn5hhPUwpLEwyKi6SuPCm4QGIX5NO6-RqDiiccx0,1935
+docutils/languages/lv.py,sha256=tEqF4ya9h4FznB-uiUKQnNLY2XDFRatxKt2evJPhsDM,1867
+docutils/languages/nl.py,sha256=3CjsGFeJgLZEw2xhHxAc4sYDKRW6ODUZsVyibjl7DmM,1865
+docutils/languages/pl.py,sha256=0RKtO5kWeAaUHI2g4vrLFbQ1ptS1Z8jFdQthAU3ffYA,1866
+docutils/languages/pt_br.py,sha256=aaFGDV0c6uo63Ra2bOqIVX9zCXkbpzZSNXtaB37AnUA,1948
+docutils/languages/ru.py,sha256=duC7XA0wyM21ftlHkT98ALKAQjup4gyslSoObpnSihw,2087
+docutils/languages/sk.py,sha256=Hi8AnKIt3dZdEmMP2-V0LN9WIiAufsTAHE9s7lNGQYE,1826
+docutils/languages/sv.py,sha256=XlNKER3JFarxMixMXawh6MhtDNqC3Ck8vM1CjsjYYcs,2069
+docutils/languages/zh_cn.py,sha256=eCBAdQZG2HBzablBr8X4ns_QCnTX93kwnIxwDa-Xa-U,1989
+docutils/languages/zh_tw.py,sha256=mJCANBTb4-Aj1QdsiYyRCTG4Yej0UquniwxI301_p60,2746
+docutils/parsers/__init__.py,sha256=ZuRqlhdstvG-sjxz1jziO8UFOvnrED_WXvhsSmegdGA,1657
+docutils/parsers/null.py,sha256=LtO7n-E6lNOs4mLXZ2SiShB3C630SoJ80ugd9fh1vXI,445
+docutils/parsers/rst/__init__.py,sha256=ALyJAD5XOFZIWHNNl9ZLlwH-qWBJHgEYiq3LnYxTnwM,16067
+docutils/parsers/rst/roles.py,sha256=DJ5FpLbVB2Yf49FCQWPkIQNewtEoB7GLC3UoYg_VDc4,14779
+docutils/parsers/rst/states.py,sha256=Bc0ijY1vyFzyRVPSK1TplRe0wkfbxgfT-OJkHmZU7NI,131353
+docutils/parsers/rst/tableparser.py,sha256=uX4iq4BWZ5U__swdZ3j8xiIOuRF8YKsygvhof9Oi4oI,21041
+docutils/parsers/rst/directives/__init__.py,sha256=ic_diqfI_ClDnpllBRSePw6MUht1Ayd3hmBd-9NMvic,14064
+docutils/parsers/rst/directives/admonitions.py,sha256=w38T7YfRc0isuF5qmaw-6wOQgHx9xGHJBqu_gDqQCdI,2413
+docutils/parsers/rst/directives/body.py,sha256=fWdkVQaFrgY0hdy_Y55WWPHep3CVAJHVyoWNUCB7cJg,9244
+docutils/parsers/rst/directives/html.py,sha256=ozpMjqjAz6Yx5sM_3zGI2kGTXnU4wD5Bvufb3m3Qr3w,3100
+docutils/parsers/rst/directives/images.py,sha256=_3pkoQFWv8U2hSJBl6Z6p2I5yt8y1R3SEJhW4fYdzaI,6926
+docutils/parsers/rst/directives/misc.py,sha256=QK49JHkVcwsjgepBS96f5LWrWWLnfMMkFFHbxfgRAYY,23757
+docutils/parsers/rst/directives/parts.py,sha256=Mx7y6BwTP1WAD_qnL-g0bjg1_O06NCXgqeYCPfP4JbI,4251
+docutils/parsers/rst/directives/references.py,sha256=1Y1yhe_O2PqLtQUSly-ny291nrQKJgQiO4Hu7Xew9Zo,831
+docutils/parsers/rst/directives/tables.py,sha256=b8b114p-_ff9qlgZD_bUP-PKtjvHQktE-EaCwmcFKN8,22360
+docutils/parsers/rst/include/isoamsa.txt,sha256=CioOAjUSuL_MG2JHqVHst0ceHzF0v3kupB6stgPMD2g,10925
+docutils/parsers/rst/include/isoamsb.txt,sha256=pSD9D42IRcGBcjYXrJUBFicIA7KRtfBhCKnFlJiytC0,7242
+docutils/parsers/rst/include/isoamsc.txt,sha256=wC2J-pHkHJMokFfOg6S5_6TX1LcibltUpLMWkm-OT3U,1723
+docutils/parsers/rst/include/isoamsn.txt,sha256=LPorblUlj9fua5tpigH-lAfqPYj_yXWQiOa91CpBD7w,6721
+docutils/parsers/rst/include/isoamso.txt,sha256=3CB6lhSNhRIAY-ST8hGPRrO-6wxI2jhYnWTU1_uBXyY,3825
+docutils/parsers/rst/include/isoamsr.txt,sha256=3qVvk1ueHuE6jnHp8ypn6UWtZH5Ly2njNDj09Xeh2qU,11763
+docutils/parsers/rst/include/isobox.txt,sha256=tYnVIj92mJ5jiY8H9-S3CzBprjU8scuJZ8BO_eWEdwc,3101
+docutils/parsers/rst/include/isocyr1.txt,sha256=-V1dpOyKxXBzNBxcW7IJp1W-SIkaXd0KVm8TP58a5B8,4241
+docutils/parsers/rst/include/isocyr2.txt,sha256=3pDslLms3yuJgQTcWtu8U1FMrMQ4KgxtDT18Ci6Mvz4,1882
+docutils/parsers/rst/include/isodia.txt,sha256=-qThXeZ-WFGztOuu_koRrlvDJpSkMVB4ivU9yyE2y7Y,869
+docutils/parsers/rst/include/isogrk1.txt,sha256=xnCDDphWarsNmum9SQRvntznPx7m547rRRuQs7PdYpQ,3010
+docutils/parsers/rst/include/isogrk2.txt,sha256=zbi3-LuRB6T0jGj_yffmgm_8_mwz-6ygAYvgie32ls0,1705
+docutils/parsers/rst/include/isogrk3.txt,sha256=X8fGxnVIG1ygvvc9hpYgUHt1d8ANdD-gxn6aKWEaRf0,2880
+docutils/parsers/rst/include/isogrk4-wide.txt,sha256=rpweSoCmHEC1vL44LEeIE8Re2tm5FEAWnae6CO2oe1I,3035
+docutils/parsers/rst/include/isogrk4.txt,sha256=zptf_ntrFuMsuk0OhEjUkRwYrz0YGDuD-_6ReIHOunY,372
+docutils/parsers/rst/include/isolat1.txt,sha256=F5R0rnBefjAbqWFCkWMFpxVKWoSec8VM0KU8DRtMCYI,4397
+docutils/parsers/rst/include/isolat2.txt,sha256=YBBbW3o9HD1JHV3QVfpaFeKnRsh2vPs0dNvpTJSEoSU,8466
+docutils/parsers/rst/include/isomfrk-wide.txt,sha256=EtzR0nFzhkCmf8CKR4hLWSAgbUjHs4sYfpJEyDp8CjY,3334
+docutils/parsers/rst/include/isomfrk.txt,sha256=zZy3M5BmklzpL02dImsAuWA1jVw7nxqx3xyZLKtILfA,519
+docutils/parsers/rst/include/isomopf-wide.txt,sha256=QUhEHzhyuCM3qzrqG5luXuL28Uoqt7I7v-CfzToG8sI,1931
+docutils/parsers/rst/include/isomopf.txt,sha256=MGuLb8WfrDm7GWvdmvYHsWtrWyh6MqRRo7wnpiaia0U,639
+docutils/parsers/rst/include/isomscr-wide.txt,sha256=VHq8miDC5nKS6Rlvv9aWpS1D_naXcfZd1CsEpJ2W8-g,3231
+docutils/parsers/rst/include/isomscr.txt,sha256=WvB9Zek3TqSyIDfw5QmE_uTvXNwjBpYU0EQttckdLJo,776
+docutils/parsers/rst/include/isonum.txt,sha256=DHWd87O6CCaSz10PEGZOxLqpDLHiMZ6--0VR9o0Yt-Q,4066
+docutils/parsers/rst/include/isopub.txt,sha256=oULMcx0Ugjk6EZAMcxHZpZM_MC7kNuzecu-sMAHVZro,4613
+docutils/parsers/rst/include/isotech.txt,sha256=h6z4dEMoVtarA9E489Zru1H29c01hWWGqbfD3wgUmiM,9726
+docutils/parsers/rst/include/mmlalias.txt,sha256=YxSvNJVNsrzgIGNjtAjtx2tVXUuby5twMZxr5Mq3PJo,45428
+docutils/parsers/rst/include/mmlextra-wide.txt,sha256=KKNjVkG-xHbeCagQfQDotJxY5oF6S6Bmk60bEv49NUQ,9010
+docutils/parsers/rst/include/mmlextra.txt,sha256=Y50tQh0fLYClCc0DUVrxr2loU5u5YaUaYbFc9PEya_c,6800
+docutils/parsers/rst/include/README.txt,sha256=_C6pSfeGShQ0evvKKKZUtx4bP4RFG9UoUlgHoMn06DU,666
+docutils/parsers/rst/include/s5defs.txt,sha256=_5JOMpDtaufiZbdxh6QKpICqLvGpB9cypHM-SEt3sKA,1036
+docutils/parsers/rst/include/xhtml1-lat1.txt,sha256=-cEYtq3oOOoLQS6n2KmsAcIs3Y5s78mToBkUXuSrSKA,6112
+docutils/parsers/rst/include/xhtml1-special.txt,sha256=dVNILahp_Jkf0bQVX-gGFcQXst3oR-FvQmAt6lDSTpE,1945
+docutils/parsers/rst/include/xhtml1-symbol.txt,sha256=K-f7hr-LRv1NKOxjZtckBmtMQOiddlnJJ-6DG-YoAzI,7028
+docutils/parsers/rst/languages/__init__.py,sha256=pcpaebmWUtAT1tA-2V3ddpUyfADaSDjFiEhl1FCNWPA,1085
+docutils/parsers/rst/languages/af.py,sha256=xe0BiiJPW_JPzTf4kFbcl6QYZPOLyzkBvUF1T74tJ4s,3672
+docutils/parsers/rst/languages/ca.py,sha256=BzWwjhjlIw7u6dso8QDsfFWrIAz7N2hlUTk3Z0iYdSc,4374
+docutils/parsers/rst/languages/cs.py,sha256=Rqwns8Bk4er1Xz11TL97BgVyg43_cCvQi6BTS6FPhFw,4781
+docutils/parsers/rst/languages/da.py,sha256=_9821hrGVQUPHciU1c79sMvLWAw3NZ8Memwnl19xudI,3685
+docutils/parsers/rst/languages/de.py,sha256=aQrzFPIgANaSmBkm5bVTWvjRtXbaoYG-IR32DgNTBUI,3442
+docutils/parsers/rst/languages/en.py,sha256=z_J8xJ1gBTk5MX6u_bonFGoop_e8VF6jWgq9Tzl5gNY,3317
+docutils/parsers/rst/languages/eo.py,sha256=QBXf1JDJZnOtfHXpxDnyuI_NaauyrvL_CZhpjxIDDgQ,3815
+docutils/parsers/rst/languages/es.py,sha256=6DIBhctgsMhIpgpPR6HX1eBH86M2Wjh3LpvmNF5QNaE,4169
+docutils/parsers/rst/languages/fa.py,sha256=4wY7BM2zRTK0qp2SXtNPoNQk-j3JkEt0Dbvt5dZ50SY,3294
+docutils/parsers/rst/languages/fi.py,sha256=WqG8bTkYqb54WHSdusj7b2vi0D4_pnuDviUs7sOIBvw,3541
+docutils/parsers/rst/languages/fr.py,sha256=iGClr3TykJsRDW-qYax9WLtm9Tf71mX3iKp9a8KX96k,3639
+docutils/parsers/rst/languages/gl.py,sha256=FdAFl6UNj7_lHH1kiJXzn91yVINGXNarCfG2YGCjzVs,3637
+docutils/parsers/rst/languages/he.py,sha256=YLbykuC7-W6buMQhZ6gRQkhxwSaEAoZ9vE2lzljpIfs,3624
+docutils/parsers/rst/languages/it.py,sha256=reGyH9djUffoQlzWU86kcewlYgE1zokm9BsOdhTeDtw,3268
+docutils/parsers/rst/languages/ja.py,sha256=fVSIAuucrA3jXQ9Ii1T-BQIX-kPGmgf-kveEChqpzxs,3780
+docutils/parsers/rst/languages/lt.py,sha256=8q9RWv_VZZKL6j2gV3G5DDwLNLbmqoY0iEIQwM55dN4,3524
+docutils/parsers/rst/languages/lv.py,sha256=OfnTo1Si1V1E_7yNAwkyQMe6y07FsOA_F-1H2Fb6vIU,3381
+docutils/parsers/rst/languages/nl.py,sha256=aJMyq4j8jmd1cghIQIYlR1gfGnNYa0XfGvaeRz6Ea6c,3703
+docutils/parsers/rst/languages/pl.py,sha256=R15HMWtDAVgzkumBC7cpHPA7Lh1i81lb8HkIHhSUZBs,3362
+docutils/parsers/rst/languages/pt_br.py,sha256=x69c3vQ6HmFCgHImEJ39FWh9tiKJNG-tBjeDXdDFOTg,3956
+docutils/parsers/rst/languages/ru.py,sha256=lZLdYjg4XVVGPtIqBrRMvIEcOilj8rfJZD1A81rYGD8,3209
+docutils/parsers/rst/languages/sk.py,sha256=muDmMtFOCg6kKJRsNLBi2W4X4zNr0NBOxoX0S3jpw6M,3917
+docutils/parsers/rst/languages/sv.py,sha256=yyyLJ8PxCnrNwcbHgGpuV3WD4r_UBUq19O7wvQBbn6g,3267
+docutils/parsers/rst/languages/zh_cn.py,sha256=t46kRJ0lpE4HzBqM3D69dZ4L5yIiEmEe3YjAg0NX4A4,3936
+docutils/parsers/rst/languages/zh_tw.py,sha256=PMF36fHm8WXGAB3IJM6o0FrtH5U9G6P7dzhlVmYw-_8,5165
+docutils/readers/__init__.py,sha256=cZO94mGtqkuoe9BIRcTTqjtoOava88ELAcPWRZb7S-U,3465
+docutils/readers/doctree.py,sha256=9QNTk_8x46sDkcSjzQiyFZxN-m9CBO3XA5bLar7OA0Q,1607
+docutils/readers/pep.py,sha256=V9b6SUQ_h74KK11wq5k2icEPGWQuVDG5gCzhWlJdF3Y,1555
+docutils/readers/standalone.py,sha256=6kRgqKD_REEZ_zZE5AW0rLj_rqOhisubS2ADyLjOAJQ,2340
+docutils/transforms/__init__.py,sha256=1m2yYFlSCJMOGsznhuyupa_E7i-3cHaCifx_tDWe9rs,6505
+docutils/transforms/components.py,sha256=F0fXyOZsf_OA0QsX-jIAsk3RLCrkEW9GRR1-l0Nx13o,1993
+docutils/transforms/frontmatter.py,sha256=CrrfDkk8Xt5Ph2zDIehJtUYs0-Ym1LM0HiUsPnOH60c,19552
+docutils/transforms/misc.py,sha256=0jDAIndz8a7C5TVWLkC44N1pVfhwZu4nu-lnk1GmzGg,4882
+docutils/transforms/parts.py,sha256=KqTqEg-Rwbu17Q22DOlY8EHlAzsvw0FCSqawgbpBbDc,6981
+docutils/transforms/peps.py,sha256=NdXwjk5gvzuzWDXred5OEbpPIDn2bWxECrBkYC_gF10,11056
+docutils/transforms/references.py,sha256=vBWYp2GUrsedFarp7Hj4SrYzzf3f_FqE1rAvh7QX5rw,36576
+docutils/transforms/universal.py,sha256=N6tUxerjMl59RJ_VgcvBzF9rmGc20gCibvPwDmBv_hw,11102
+docutils/transforms/writer_aux.py,sha256=4Zd8z8s4jTG2J0xSnm5hj1kiZrKN6FqvGvpJ1cb09oU,2609
+docutils/utils/__init__.py,sha256=WlOBG6CIrl7rYK0daRlY4P-AyhEsItGcdwEvZuDzwr8,29266
+docutils/utils/code_analyzer.py,sha256=DH9cAXX7CtkSc271CMvvTGkAgl62gtowyktGf1_2Agg,4927
+docutils/utils/error_reporting.py,sha256=VgTHGcA5t3YFqTzdiqDi5fdsnCYqlxL8__z9gZ1Sla0,8362
+docutils/utils/punctuation_chars.py,sha256=1T_JudoB_TFYD_JYMYJ-gWDPvJ6PZzez59r2NNyke-c,6364
+docutils/utils/roman.py,sha256=9BbuHddXRSjNS3E4TnaW-SQWaIK7cso-s72aa4R5cWE,2687
+docutils/utils/smartquotes.py,sha256=vO58EtJoQh9_0ZbmQNk_sRNF-ZnGq_ToRRMKXhIf86w,40200
+docutils/utils/urischemes.py,sha256=2Y9dzkd8_mri0isGvPwhX-oDhkVLKbw-T0UstBsqiIc,6275
+docutils/utils/math/__init__.py,sha256=TUGMP6ytwBzbQCHrFSxSl8OcDdbLGfCCkGteBdWm6l0,1755
+docutils/utils/math/latex2mathml.py,sha256=QwKm83KJz281QYxz__nhCl00Uk-UA1RGC2PlvMAa0N4,17407
+docutils/utils/math/math2html.py,sha256=Eknku8dBz4HQEAfSGWun859YyH8mjjTS34hnQ_k4lxQ,178955
+docutils/utils/math/tex2mathml_extern.py,sha256=TxX8Bccx_bqOpTcyWcvY4XvpQKv7HgXcBWkHX0YCB0Q,5635
+docutils/utils/math/tex2unichar.py,sha256=bq0GY1pvCUr4EllEEJk7LNl90wOgp0xofbORTQAj0CM,34481
+docutils/utils/math/unichar2tex.py,sha256=D5EcOS2j7gKSkO56vpnAr_nx1Ji3t6wBKOpiD8uGPd4,16811
+docutils/writers/__init__.py,sha256=m56LRJvyrvD0vOuhY4X9HrrrxRCeICrcWyhn07SV4iU,4666
+docutils/writers/_html_base.py,sha256=zHbxtKfMZ8Rg8Yg-mfKAt07QoqqMRRqrqzaGETCmOWo,63742
+docutils/writers/docutils_xml.py,sha256=kuvXTb6EG1XeeDbsNEQEqDcWZcdiWD6pIiDqqXsC-ME,7679
+docutils/writers/manpage.py,sha256=cbh2x2KGLj2tcgyNau6-87VLmV0c6Hk2fn-aP74nixw,35720
+docutils/writers/null.py,sha256=zyIuah_o8SlqvgbOWLRG9chpeNKky0D13lOTtR1E73U,450
+docutils/writers/pseudoxml.py,sha256=2bRL9zMleW8jDuGaINvM52siZiJWH1-zQfEELJE2oPE,772
+docutils/writers/html4css1/__init__.py,sha256=26CuVU7iGlAIZo1q8e4m3q0Ts7hsrSSEEMbWrVSPJlo,33869
+docutils/writers/html4css1/html4css1.css,sha256=BvqQaXs-bQ6Vx7TVsdXxxLwoiQeXQ-Wn1z7ZXRXk_q8,7210
+docutils/writers/html4css1/template.txt,sha256=HDzUUyAv7gT4ewGQTqfOE2_9HOVyGu9-wCRgsmoCmjQ,114
+docutils/writers/html5_polyglot/__init__.py,sha256=jiVYn8UGzs4WywtvEVn5g9h8ld6aC9UgfDlBc7CBLc8,9074
+docutils/writers/html5_polyglot/math.css,sha256=hGjowMetSh3oAlkm7_EA2pTtkv_Lg_TgwrBkyNs85Co,4783
+docutils/writers/html5_polyglot/minimal.css,sha256=Ong7EeVlkFTsSr-8XBRix_ZQogrW8zvvngCxiS--gTw,6820
+docutils/writers/html5_polyglot/plain.css,sha256=bsHdtR5YGeN-uuouo2VqmKKcDg1dUZmf67GQ88t2YOY,7136
+docutils/writers/html5_polyglot/template.txt,sha256=HDzUUyAv7gT4ewGQTqfOE2_9HOVyGu9-wCRgsmoCmjQ,114
+docutils/writers/latex2e/__init__.py,sha256=Zdh43PEh_OoT2Pr5aoYCawlFMQU4uBxO3uLRX4CXS0Q,128443
+docutils/writers/latex2e/default.tex,sha256=RT2DTIMKaHNP2CPdopBzE1tLR4iQkcUHsJ0UkLYL95k,422
+docutils/writers/latex2e/titlepage.tex,sha256=wxPKr4rylqUamK2U0EJXQTaVQ1392ua5DtSVGDqrNsI,534
+docutils/writers/latex2e/xelatex.tex,sha256=oUEz2Ze3wpoMp0X0iVELcUGNtKVre5Ug5Ujge7OiMgU,672
+docutils/writers/odf_odt/__init__.py,sha256=qOTd0VEAqgQ-3QhfH81cOjSczb1liZe8CBnp4G0jAAw,135235
+docutils/writers/odf_odt/pygmentsformatter.py,sha256=iBsRNxRe-v378-whp_F8CwgvuK7Ck4o_Tx4Kae8NlYw,4671
+docutils/writers/odf_odt/styles.odt,sha256=xKv9z2sd1qNxAH28X-5st5JuDZeTw6jyDOxXohsFrKY,16500
+docutils/writers/pep_html/__init__.py,sha256=fM_-gcMw-VLZLgDEDKqiyaQdMWErf1pNbdwHRFVv2UE,3507
+docutils/writers/pep_html/pep.css,sha256=OtaYGd-h4g9Z6owNa17fBReTdD6k-TOadRk-dQ-jM7k,6366
+docutils/writers/pep_html/template.txt,sha256=CfyVCCoqqB3Y8hOyt7Uxf0XeyTwtFf926L_hJeaS1wU,1294
+docutils/writers/s5_html/__init__.py,sha256=4renodaMpdRlH8Nrp1w_89nyB0m8r-_PsV4CpxkzpRE,14664
+docutils/writers/s5_html/themes/README.txt,sha256=wsty9ouRFxVMrZV2EUTOmfMS_bJJpmA5CqU23fpMccM,301
+docutils/writers/s5_html/themes/big-black/__base__,sha256=WeKnChXCPkrXDs7Xr-Qnf1i-bgFjkeaKJ-ilXV0R5lM,38
+docutils/writers/s5_html/themes/big-black/framing.css,sha256=RctE4TbWO_ctWsmE1LSPdCVRMT1QFlTrNmQNKC4wC2Y,911
+docutils/writers/s5_html/themes/big-black/pretty.css,sha256=VFK99wlPllRBKK0eQ2Yk6RC_-VDMREm-ue2Mp3YMs7A,3606
+docutils/writers/s5_html/themes/big-white/framing.css,sha256=6M4vVFfoErc5LKtuol1RqnId0166VecGy6rYu9cPY9c,906
+docutils/writers/s5_html/themes/big-white/pretty.css,sha256=YO7dDIRq7dXpIdC8pEOf7QaBb9nUvajfWkkQpxt3nJs,3566
+docutils/writers/s5_html/themes/default/blank.gif,sha256=L1YbAqSTduNnms1ZdeN5Cr3_Cey636HhhYx7om4__O8,49
+docutils/writers/s5_html/themes/default/framing.css,sha256=fcGapNDqnTT2w-2HpWTHb5yf9Yq-_31DJuazk7lCSQw,1003
+docutils/writers/s5_html/themes/default/iepngfix.htc,sha256=ve7IwOG4pkjDmYItcuQrUbESmVaY6ACDhhA5YP9d73c,1190
+docutils/writers/s5_html/themes/default/opera.css,sha256=guPZOg_BINv-LjV9_IAM7ILFQ-fKALNjlP1i06e5dmA,261
+docutils/writers/s5_html/themes/default/outline.css,sha256=z3ACJiW3_gnG8XFvX602PMTYvKhbRybqCeoWl3O_pA0,648
+docutils/writers/s5_html/themes/default/pretty.css,sha256=dUbMlVTFGx3ofvXGUmhnKAADhBkHsdU0ysWpxwX-70M,4384
+docutils/writers/s5_html/themes/default/print.css,sha256=INhYRMsY7y2wd9p7tqjcDWBREXHUMO-2ApAWvITyetI,818
+docutils/writers/s5_html/themes/default/s5-core.css,sha256=MrHjKxQ7P7ZFC2FmEq2BnkRWs59YVzUS_ZOw8CHC2Jk,451
+docutils/writers/s5_html/themes/default/slides.css,sha256=VKYQ1Oe8lZ8LHxzPqJiU79J0z295nkmIbzsXL-N_dfQ,283
+docutils/writers/s5_html/themes/default/slides.js,sha256=n6_977F6UKSfpYSDu5RKhS1g7iehHcmxm1jDLD17ME4,15800
+docutils/writers/s5_html/themes/medium-black/__base__,sha256=822LJG-LrdBZY6CA7wsLFCFzsYfxbyz2mr1j6rpb1UA,41
+docutils/writers/s5_html/themes/medium-black/pretty.css,sha256=zoRP67Cmy7JzwnXqKe02GwGzt0p5z509ymlbaxXuPoI,4031
+docutils/writers/s5_html/themes/medium-white/framing.css,sha256=nstQg2Fwdm6TTRHflpYRE7Q3b2jFO5p6tyDXJhsmi0I,944
+docutils/writers/s5_html/themes/medium-white/pretty.css,sha256=SafUPS2T_uel8lPQlvouTXUrgT9QY52nxnBJDt_pmP4,3991
+docutils/writers/s5_html/themes/small-black/__base__,sha256=WmiB80z49RfMsy_7tFI042AfUgyztL5OXI3tap9EfQM,40
+docutils/writers/s5_html/themes/small-black/pretty.css,sha256=G_e83H9lIDHXtUBJRCnsnxSiAhP0zaAafQwSviioUQ8,4030
+docutils/writers/s5_html/themes/small-white/framing.css,sha256=klf55tsjjL7FuzlWi0yY_4bHJWZZhlD36VCevg45o70,941
+docutils/writers/s5_html/themes/small-white/pretty.css,sha256=yaLNE-loYn_nSCuzvGVJWHAbh90dxdO5I06Cv-d4wcM,4001
+docutils/writers/xetex/__init__.py,sha256=LA5XWXAZMoWwo2saGnCeGr0KssXqLkIMCqZV7w9Udlg,5700
+../../../bin/rst2html.py,sha256=_ZSHNn9dmPKAx6L7qZ8XgfPAy3KZv5MNky6n_8V3xiI,584
+../../../bin/rst2html4.py,sha256=u5YWYGbe9xyegHW30S6gzVpAg8nTI9Xu_ZOeve5CbOo,704
+../../../bin/rst2html5.py,sha256=dKqselk9JHHlf61pmgdKglSHe-acL1_GV91vLBtY3AU,1129
+../../../bin/rst2latex.py,sha256=nLGST5HE554VD4EbuhUn2_6kijGgDFcQgOMa4RrFngI,781
+../../../bin/rst2man.py,sha256=cJHLwJTI7kSR-7n_WhIQ_e11oEJr-ZMK97fKy5GZ3q0,590
+../../../bin/rst2odt.py,sha256=iH9HaLPbzEfJU599E2DvAW2n2py69TXfJDOIZEHRsyk,754
+../../../bin/rst2odt_prepstyles.py,sha256=tPj049dHEFj2cCedGmT6jSTtD-ZxLHrE4NJQVFLjZLk,1688
+../../../bin/rst2pseudoxml.py,sha256=9SwwDEOFNz3AqOSI4CEvhieDm_3TgTPN_3FDPz6b5ys,591
+../../../bin/rst2s5.py,sha256=zdT-5LNxruUZqMVe-Wjj1F-eXan9pLDBvxA1k38XRGw,627
+../../../bin/rst2xetex.py,sha256=SzPM2iz6wCAc8Ki3sEnhCq6318jTQziosToib4FIzEw,861
+../../../bin/rst2xml.py,sha256=MKbl4dkpIBjzh4GdITggzJpi-SMGoRaXdkbZs-yd05E,592
+../../../bin/rstpep2html.py,sha256=TlXeA4lyMxtICwwaEERXcrXTleXfwyQE5qbooZLtcSw,660
+docutils-0.14.dist-info/DESCRIPTION.rst,sha256=VEarWAl_dcwpCipAoNacho47mGV-DNvYRvczl9p5HEc,228
+docutils-0.14.dist-info/METADATA,sha256=Jg97XShs6kaAH_NJDM_1coOHkom9DY2sWVyjBqSAlyY,2330
+docutils-0.14.dist-info/metadata.json,sha256=bSiddfruzWAcipcAnf26p4xjSsjWkFxrntb1XgW2wvg,1977
+docutils-0.14.dist-info/RECORD,,
+docutils-0.14.dist-info/top_level.txt,sha256=dPq3jQoxpMOEhrZ1tQh3_-9hqhdvOjUplSdSVDAc95A,9
+docutils-0.14.dist-info/WHEEL,sha256=-aSo8rHuuPDEFzkcqqQ55pDyCjy25bYMLxSiHWKAOTc,92
+docutils-0.14.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+docutils/writers/__pycache__/_html_base.cpython-35.pyc,,
+docutils/writers/__pycache__/null.cpython-35.pyc,,
+docutils/languages/__pycache__/de.cpython-35.pyc,,
+docutils/parsers/rst/directives/__pycache__/misc.cpython-35.pyc,,
+docutils/utils/math/__pycache__/__init__.cpython-35.pyc,,
+docutils/languages/__pycache__/pl.cpython-35.pyc,,
+docutils/parsers/rst/__pycache__/roles.cpython-35.pyc,,
+../../../bin/__pycache__/rst2s5.cpython-35.pyc,,
+docutils/languages/__pycache__/pt_br.cpython-35.pyc,,
+docutils/readers/__pycache__/pep.cpython-35.pyc,,
+docutils/transforms/__pycache__/components.cpython-35.pyc,,
+docutils/__pycache__/statemachine.cpython-35.pyc,,
+docutils/utils/math/__pycache__/latex2mathml.cpython-35.pyc,,
+docutils/utils/__pycache__/punctuation_chars.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/ja.cpython-35.pyc,,
+../../../bin/__pycache__/rst2pseudoxml.cpython-35.pyc,,
+../../../bin/__pycache__/rst2html5.cpython-35.pyc,,
+../../../bin/__pycache__/rst2odt.cpython-35.pyc,,
+docutils/transforms/__pycache__/misc.cpython-35.pyc,,
+docutils/languages/__pycache__/fi.cpython-35.pyc,,
+docutils/languages/__pycache__/ca.cpython-35.pyc,,
+docutils/languages/__pycache__/sv.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/eo.cpython-35.pyc,,
+docutils/transforms/__pycache__/writer_aux.cpython-35.pyc,,
+docutils/utils/__pycache__/urischemes.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/da.cpython-35.pyc,,
+../../../bin/__pycache__/rst2man.cpython-35.pyc,,
+docutils/parsers/rst/__pycache__/tableparser.cpython-35.pyc,,
+docutils/__pycache__/core.cpython-35.pyc,,
+../../../bin/__pycache__/rst2html4.cpython-35.pyc,,
+docutils/parsers/__pycache__/null.cpython-35.pyc,,
+docutils/writers/__pycache__/manpage.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/pt_br.cpython-35.pyc,,
+docutils/parsers/rst/directives/__pycache__/parts.cpython-35.pyc,,
+docutils/__pycache__/frontend.cpython-35.pyc,,
+docutils/utils/__pycache__/__init__.cpython-35.pyc,,
+docutils/languages/__pycache__/it.cpython-35.pyc,,
+docutils/languages/__pycache__/es.cpython-35.pyc,,
+docutils/writers/__pycache__/__init__.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/sk.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/lv.cpython-35.pyc,,
+../../../bin/__pycache__/rst2html.cpython-35.pyc,,
+docutils/parsers/rst/directives/__pycache__/images.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/en.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/fa.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/it.cpython-35.pyc,,
+docutils/__pycache__/nodes.cpython-35.pyc,,
+docutils/languages/__pycache__/nl.cpython-35.pyc,,
+docutils/parsers/__pycache__/__init__.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/nl.cpython-35.pyc,,
+docutils/writers/html5_polyglot/__pycache__/__init__.cpython-35.pyc,,
+docutils/__pycache__/__init__.cpython-35.pyc,,
+docutils/parsers/rst/__pycache__/__init__.cpython-35.pyc,,
+docutils/parsers/rst/directives/__pycache__/tables.cpython-35.pyc,,
+docutils/writers/__pycache__/docutils_xml.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/gl.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/af.cpython-35.pyc,,
+docutils/languages/__pycache__/cs.cpython-35.pyc,,
+docutils/languages/__pycache__/zh_cn.cpython-35.pyc,,
+docutils/transforms/__pycache__/peps.cpython-35.pyc,,
+docutils/languages/__pycache__/ru.cpython-35.pyc,,
+docutils/utils/math/__pycache__/tex2unichar.cpython-35.pyc,,
+docutils/writers/odf_odt/__pycache__/pygmentsformatter.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/es.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/pl.cpython-35.pyc,,
+docutils/utils/math/__pycache__/math2html.cpython-35.pyc,,
+docutils/languages/__pycache__/gl.cpython-35.pyc,,
+docutils/writers/pep_html/__pycache__/__init__.cpython-35.pyc,,
+docutils/__pycache__/_compat.cpython-35.pyc,,
+docutils/parsers/rst/directives/__pycache__/__init__.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/cs.cpython-35.pyc,,
+../../../bin/__pycache__/rst2odt_prepstyles.cpython-35.pyc,,
+docutils/readers/__pycache__/__init__.cpython-35.pyc,,
+docutils/parsers/rst/directives/__pycache__/body.cpython-35.pyc,,
+docutils/languages/__pycache__/af.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/zh_tw.cpython-35.pyc,,
+docutils/parsers/rst/directives/__pycache__/admonitions.cpython-35.pyc,,
+docutils/languages/__pycache__/da.cpython-35.pyc,,
+docutils/languages/__pycache__/__init__.cpython-35.pyc,,
+docutils/languages/__pycache__/zh_tw.cpython-35.pyc,,
+docutils/transforms/__pycache__/references.cpython-35.pyc,,
+../../../bin/__pycache__/rst2xml.cpython-35.pyc,,
+docutils/utils/__pycache__/smartquotes.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/fi.cpython-35.pyc,,
+docutils/__pycache__/io.cpython-35.pyc,,
+docutils/writers/odf_odt/__pycache__/__init__.cpython-35.pyc,,
+docutils/utils/__pycache__/roman.cpython-35.pyc,,
+docutils/readers/__pycache__/standalone.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/de.cpython-35.pyc,,
+docutils/transforms/__pycache__/parts.cpython-35.pyc,,
+docutils/utils/__pycache__/error_reporting.cpython-35.pyc,,
+docutils/writers/xetex/__pycache__/__init__.cpython-35.pyc,,
+docutils/parsers/rst/directives/__pycache__/html.cpython-35.pyc,,
+docutils/utils/__pycache__/code_analyzer.cpython-35.pyc,,
+docutils/parsers/rst/__pycache__/states.cpython-35.pyc,,
+docutils/languages/__pycache__/sk.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/__init__.cpython-35.pyc,,
+docutils/writers/html4css1/__pycache__/__init__.cpython-35.pyc,,
+docutils/languages/__pycache__/he.cpython-35.pyc,,
+docutils/languages/__pycache__/eo.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/lt.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/fr.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/zh_cn.cpython-35.pyc,,
+docutils/languages/__pycache__/fa.cpython-35.pyc,,
+docutils/writers/latex2e/__pycache__/__init__.cpython-35.pyc,,
+docutils/__pycache__/examples.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/ca.cpython-35.pyc,,
+docutils/languages/__pycache__/lv.cpython-35.pyc,,
+docutils/utils/math/__pycache__/unichar2tex.cpython-35.pyc,,
+docutils/readers/__pycache__/doctree.cpython-35.pyc,,
+docutils/languages/__pycache__/ja.cpython-35.pyc,,
+docutils/writers/__pycache__/pseudoxml.cpython-35.pyc,,
+docutils/transforms/__pycache__/frontmatter.cpython-35.pyc,,
+docutils/parsers/rst/directives/__pycache__/references.cpython-35.pyc,,
+../../../bin/__pycache__/rst2xetex.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/he.cpython-35.pyc,,
+docutils/languages/__pycache__/fr.cpython-35.pyc,,
+docutils/writers/s5_html/__pycache__/__init__.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/ru.cpython-35.pyc,,
+docutils/languages/__pycache__/lt.cpython-35.pyc,,
+docutils/utils/math/__pycache__/tex2mathml_extern.cpython-35.pyc,,
+docutils/languages/__pycache__/en.cpython-35.pyc,,
+docutils/transforms/__pycache__/__init__.cpython-35.pyc,,
+../../../bin/__pycache__/rst2latex.cpython-35.pyc,,
+../../../bin/__pycache__/rstpep2html.cpython-35.pyc,,
+docutils/transforms/__pycache__/universal.cpython-35.pyc,,
+docutils/parsers/rst/languages/__pycache__/sv.cpython-35.pyc,,
diff --git a/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/WHEEL b/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/WHEEL
new file mode 100644
index 0000000..4c62fb9
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.24.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/metadata.json b/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/metadata.json
new file mode 100644
index 0000000..4287fc0
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/metadata.json
@@ -0,0 +1 @@
+{"license": "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)", "classifiers": ["Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: End Users/Desktop", "Intended Audience :: Other Audience", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: Public Domain", "License :: OSI Approved :: Python Software Foundation License", "License :: OSI Approved :: BSD License", "License :: OSI Approved :: GNU General Public License (GPL)", "Operating System :: OS Independent", "Programming Language :: Python :: 2.4", "Programming Language :: Python :: 2.5", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Topic :: Documentation", "Topic :: Software Development :: Documentation", "Topic :: Text Processing", "Natural Language :: English", "Natural Language :: Afrikaans", "Natural Language :: Catalan", "Natural Language :: Chinese (Simplified)", "Natural Language :: Chinese (Traditional)", "Natural Language :: Czech", "Natural Language :: Dutch", "Natural Language :: Esperanto", "Natural Language :: Finnish", "Natural Language :: French", "Natural Language :: Galician", "Natural Language :: German", "Natural Language :: Italian", "Natural Language :: Japanese", "Natural Language :: Polish", "Natural Language :: Portuguese (Brazilian)", "Natural Language :: Russian", "Natural Language :: Slovak", "Natural Language :: Spanish", "Natural Language :: Swedish"], "metadata_version": "2.0", "version": "0.14", "name": "docutils", "extensions": {"python.details": {"contacts": [{"email": "docutils-develop@lists.sourceforge.net", "role": "author", "name": "docutils-develop list"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://docutils.sourceforge.net/"}}}, "platform": "OS-independent", "generator": "bdist_wheel (0.24.0)", "summary": "Docutils -- Python Documentation Utilities"}
\ No newline at end of file
diff --git a/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/top_level.txt b/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/top_level.txt
new file mode 100644
index 0000000..5492d76
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/docutils-0.14.dist-info/top_level.txt
@@ -0,0 +1 @@
+docutils
diff --git a/venv/lib/python3.5/site-packages/docutils/__init__.py b/venv/lib/python3.5/site-packages/docutils/__init__.py
new file mode 100644
index 0000000..f2e401e
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/docutils/__init__.py
@@ -0,0 +1,262 @@
+# $Id: __init__.py 8147 2017-08-03 09:01:16Z grubert $
+# Author: David Goodger
+# Copyright: This module has been placed in the public domain.
+
+"""
+This is the Docutils (Python Documentation Utilities) package.
+
+Package Structure
+=================
+
+Modules:
+
+- __init__.py: Contains component base classes, exception classes, and
+ Docutils version information.
+
+- core.py: Contains the ``Publisher`` class and ``publish_*()`` convenience
+ functions.
+
+- frontend.py: Runtime settings (command-line interface, configuration files)
+ processing, for Docutils front-ends.
+
+- io.py: Provides a uniform API for low-level input and output.
+
+- nodes.py: Docutils document tree (doctree) node class library.
+
+- statemachine.py: A finite state machine specialized for
+ regular-expression-based text filters.
+
+Subpackages:
+
+- languages: Language-specific mappings of terms.
+
+- parsers: Syntax-specific input parser modules or packages.
+
+- readers: Context-specific input handlers which understand the data
+ source and manage a parser.
+
+- transforms: Modules used by readers and writers to modify DPS
+ doctrees.
+
+- utils: Contains the ``Reporter`` system warning class and miscellaneous
+ utilities used by readers, writers, and transforms.
+
+ utils/urischemes.py: Contains a complete mapping of known URI addressing
+ scheme names to descriptions.
+
+- utils/math: Contains functions for conversion of mathematical notation
+ between different formats (LaTeX, MathML, text, ...).
+
+- writers: Format-specific output translators.
+"""
+
+import sys
+
+
+__docformat__ = 'reStructuredText'
+
+__version__ = '0.14'
+"""Docutils version identifier (complies with PEP 440)::
+
+ major.minor[.micro][releaselevel[serial]][.dev]
+
+* The major number will be bumped when the project is feature-complete, and
+ later if there is a major change in the design or API.
+* The minor number is bumped whenever there are new features.
+* The micro number is bumped for bug-fix releases. Omitted if micro=0.
+* The releaselevel identifier is used for pre-releases, one of 'a' (alpha),
+ 'b' (beta), or 'rc' (release candidate). Omitted for final releases.
+* The serial release number identifies prereleases; omitted if 0.
+* The '.dev' suffix indicates active development, not a release, before the
+ version indicated.
+
+For version comparison operations, use `__version_info__`
+rather than parsing the text of `__version__`.
+"""
+
+# workaround for Python < 2.6:
+__version_info__ = (0, 14, 0, 'final', 0, True)
+# To add in Docutils 0.15, replacing the line above:
+"""
+from collections import namedtuple
+VersionInfo = namedtuple(
+ 'VersionInfo', 'major minor micro releaselevel serial release')
+__version_info__ = VersionInfo(
+ major=0,
+ minor=15,
+ micro=0,
+ releaselevel='alpha', # development status:
+ # one of 'alpha', 'beta', 'candidate', 'final'
+ serial=0, # pre-release number (0 for final releases)
+ release=False # True for official releases and pre-releases
+ )
+
+Comprehensive version information tuple. Can be used to test for a
+minimally required version, e.g. ::
+
+ if __version_info__ >= (0, 13, 0, 'candidate', 2, True)
+
+or in a self-documenting way like ::
+
+ if __version_info__ >= docutils.VersionInfo(
+ major=0, minor=13, micro=0,
+ releaselevel='candidate', serial=2, release=True)
+"""
+
+__version_details__ = ''
+"""Optional extra version details (e.g. 'snapshot 2005-05-29, r3410').
+(For development and release status see `__version_info__`.)
+"""
+
+
+class ApplicationError(Exception):
+ # Workaround:
+ # In Python < 2.6, unicode() calls `str` on the
+ # arg and therefore, e.g., unicode(StandardError(u'\u234')) fails
+ # with UnicodeDecodeError.
+ if sys.version_info < (2,6):
+ def __unicode__(self):
+ return ', '.join(self.args)
+
+
+class DataError(ApplicationError): pass
+
+
+class SettingsSpec:
+
+ """
+ Runtime setting specification base class.
+
+ SettingsSpec subclass objects used by `docutils.frontend.OptionParser`.
+ """
+
+ settings_spec = ()
+ """Runtime settings specification. Override in subclasses.
+
+ Defines runtime settings and associated command-line options, as used by
+ `docutils.frontend.OptionParser`. This is a tuple of:
+
+ - Option group title (string or `None` which implies no group, just a list
+ of single options).
+
+ - Description (string or `None`).
+
+ - A sequence of option tuples. Each consists of:
+
+ - Help text (string)
+
+ - List of option strings (e.g. ``['-Q', '--quux']``).
+
+ - Dictionary of keyword arguments sent to the OptionParser/OptionGroup
+ ``add_option`` method.
+
+ Runtime setting names are derived implicitly from long option names
+ ('--a-setting' becomes ``settings.a_setting``) or explicitly from the
+ 'dest' keyword argument.
+
+ Most settings will also have a 'validator' keyword & function. The
+ validator function validates setting values (from configuration files
+ and command-line option arguments) and converts them to appropriate
+ types. For example, the ``docutils.frontend.validate_boolean``
+ function, **required by all boolean settings**, converts true values
+ ('1', 'on', 'yes', and 'true') to 1 and false values ('0', 'off',
+ 'no', 'false', and '') to 0. Validators need only be set once per
+ setting. See the `docutils.frontend.validate_*` functions.
+
+ See the optparse docs for more details.
+
+ - More triples of group title, description, options, as many times as
+ needed. Thus, `settings_spec` tuples can be simply concatenated.
+ """
+
+ settings_defaults = None
+ """A dictionary of defaults for settings not in `settings_spec` (internal
+ settings, intended to be inaccessible by command-line and config file).
+ Override in subclasses."""
+
+ settings_default_overrides = None
+ """A dictionary of auxiliary defaults, to override defaults for settings
+ defined in other components. Override in subclasses."""
+
+ relative_path_settings = ()
+ """Settings containing filesystem paths. Override in subclasses.
+ Settings listed here are to be interpreted relative to the current working
+ directory."""
+
+ config_section = None
+ """The name of the config file section specific to this component
+ (lowercase, no brackets). Override in subclasses."""
+
+ config_section_dependencies = None
+ """A list of names of config file sections that are to be applied before
+ `config_section`, in order (from general to specific). In other words,
+ the settings in `config_section` are to be overlaid on top of the settings
+ from these sections. The "general" section is assumed implicitly.
+ Override in subclasses."""
+
+
+class TransformSpec:
+
+ """
+ Runtime transform specification base class.
+
+ TransformSpec subclass objects used by `docutils.transforms.Transformer`.
+ """
+
+ def get_transforms(self):
+ """Transforms required by this class. Override in subclasses."""
+ if self.default_transforms != ():
+ import warnings
+ warnings.warn('default_transforms attribute deprecated.\n'
+ 'Use get_transforms() method instead.',
+ DeprecationWarning)
+ return list(self.default_transforms)
+ return []
+
+ # Deprecated; for compatibility.
+ default_transforms = ()
+
+ unknown_reference_resolvers = ()
+ """List of functions to try to resolve unknown references. Unknown
+ references have a 'refname' attribute which doesn't correspond to any
+ target in the document. Called when the transforms in
+ `docutils.tranforms.references` are unable to find a correct target. The
+ list should contain functions which will try to resolve unknown
+ references, with the following signature::
+
+ def reference_resolver(node):
+ '''Returns boolean: true if resolved, false if not.'''
+
+ If the function is able to resolve the reference, it should also remove
+ the 'refname' attribute and mark the node as resolved::
+
+ del node['refname']
+ node.resolved = 1
+
+ Each function must have a "priority" attribute which will affect the order
+ the unknown_reference_resolvers are run::
+
+ reference_resolver.priority = 100
+
+ Override in subclasses."""
+
+
+class Component(SettingsSpec, TransformSpec):
+
+ """Base class for Docutils components."""
+
+ component_type = None
+ """Name of the component type ('reader', 'parser', 'writer'). Override in
+ subclasses."""
+
+ supported = ()
+ """Names for this component. Override in subclasses."""
+
+ def supports(self, format):
+ """
+ Is `format` supported by this component?
+
+ To be used by transforms to ask the dependent component if it supports
+ a certain input context or output format.
+ """
+ return format in self.supported
diff --git a/venv/lib/python3.5/site-packages/docutils/__pycache__/__init__.cpython-35.pyc b/venv/lib/python3.5/site-packages/docutils/__pycache__/__init__.cpython-35.pyc
new file mode 100644
index 0000000..9803177
Binary files /dev/null and b/venv/lib/python3.5/site-packages/docutils/__pycache__/__init__.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/docutils/__pycache__/_compat.cpython-35.pyc b/venv/lib/python3.5/site-packages/docutils/__pycache__/_compat.cpython-35.pyc
new file mode 100644
index 0000000..228416d
Binary files /dev/null and b/venv/lib/python3.5/site-packages/docutils/__pycache__/_compat.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/docutils/__pycache__/core.cpython-35.pyc b/venv/lib/python3.5/site-packages/docutils/__pycache__/core.cpython-35.pyc
new file mode 100644
index 0000000..fbd2548
Binary files /dev/null and b/venv/lib/python3.5/site-packages/docutils/__pycache__/core.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/docutils/__pycache__/examples.cpython-35.pyc b/venv/lib/python3.5/site-packages/docutils/__pycache__/examples.cpython-35.pyc
new file mode 100644
index 0000000..3400e79
Binary files /dev/null and b/venv/lib/python3.5/site-packages/docutils/__pycache__/examples.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/docutils/__pycache__/frontend.cpython-35.pyc b/venv/lib/python3.5/site-packages/docutils/__pycache__/frontend.cpython-35.pyc
new file mode 100644
index 0000000..6a42cf9
Binary files /dev/null and b/venv/lib/python3.5/site-packages/docutils/__pycache__/frontend.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/docutils/__pycache__/io.cpython-35.pyc b/venv/lib/python3.5/site-packages/docutils/__pycache__/io.cpython-35.pyc
new file mode 100644
index 0000000..b730cb8
Binary files /dev/null and b/venv/lib/python3.5/site-packages/docutils/__pycache__/io.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/docutils/__pycache__/nodes.cpython-35.pyc b/venv/lib/python3.5/site-packages/docutils/__pycache__/nodes.cpython-35.pyc
new file mode 100644
index 0000000..129ca1d
Binary files /dev/null and b/venv/lib/python3.5/site-packages/docutils/__pycache__/nodes.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/docutils/__pycache__/statemachine.cpython-35.pyc b/venv/lib/python3.5/site-packages/docutils/__pycache__/statemachine.cpython-35.pyc
new file mode 100644
index 0000000..6476863
Binary files /dev/null and b/venv/lib/python3.5/site-packages/docutils/__pycache__/statemachine.cpython-35.pyc differ
diff --git a/venv/lib/python3.5/site-packages/docutils/_compat.py b/venv/lib/python3.5/site-packages/docutils/_compat.py
new file mode 100644
index 0000000..1471c89
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/docutils/_compat.py
@@ -0,0 +1,48 @@
+# $Id: _compat.py 7486 2012-07-11 12:25:14Z milde $
+# Author: Georg Brandl
+# Copyright: This module has been placed in the public domain.
+
+"""
+Python 2/3 compatibility definitions.
+
+This module currently provides the following helper symbols:
+
+* bytes (name of byte string type; str in 2.x, bytes in 3.x)
+* b (function converting a string literal to an ASCII byte string;
+ can be also used to convert a Unicode string into a byte string)
+* u_prefix (unicode repr prefix: 'u' in 2.x, '' in 3.x)
+ (Required in docutils/test/test_publisher.py)
+* BytesIO (a StringIO class that works with bytestrings)
+"""
+
+import sys
+
+if sys.version_info < (3,0):
+ b = bytes = str
+ u_prefix = 'u'
+ from io import StringIO as BytesIO
+else:
+ import builtins
+ bytes = builtins.bytes
+ u_prefix = ''
+ def b(s):
+ if isinstance(s, str):
+ return s.encode('latin1')
+ elif isinstance(s, bytes):
+ return s
+ else:
+ raise TypeError("Invalid argument %r for b()" % (s,))
+ # using this hack since 2to3 "fixes" the relative import
+ # when using ``from io import BytesIO``
+ BytesIO = __import__('io').BytesIO
+
+if sys.version_info < (2,5):
+ import builtins
+
+ def __import__(name, globals={}, locals={}, fromlist=[], level=-1):
+ """Compatibility definition for Python 2.4.
+
+ Silently ignore the `level` argument missing in Python < 2.5.
+ """
+ # we need the level arg because the default changed in Python 3.3
+ return builtins.__import__(name, globals, locals, fromlist)
diff --git a/venv/lib/python3.5/site-packages/docutils/core.py b/venv/lib/python3.5/site-packages/docutils/core.py
new file mode 100644
index 0000000..af39db0
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/docutils/core.py
@@ -0,0 +1,665 @@
+# $Id: core.py 8126 2017-06-23 09:34:28Z milde $
+# Author: David Goodger
+# Copyright: This module has been placed in the public domain.
+
+"""
+Calling the ``publish_*`` convenience functions (or instantiating a
+`Publisher` object) with component names will result in default
+behavior. For custom behavior (setting component options), create
+custom component objects first, and pass *them* to
+``publish_*``/`Publisher`. See `The Docutils Publisher`_.
+
+.. _The Docutils Publisher: http://docutils.sf.net/docs/api/publisher.html
+"""
+
+__docformat__ = 'reStructuredText'
+
+import sys
+import pprint
+from docutils import __version__, __version_details__, SettingsSpec
+from docutils import frontend, io, utils, readers, writers
+from docutils.frontend import OptionParser
+from docutils.transforms import Transformer
+from docutils.utils.error_reporting import ErrorOutput, ErrorString
+import docutils.readers.doctree
+
+class Publisher:
+
+ """
+ A facade encapsulating the high-level logic of a Docutils system.
+ """
+
+ def __init__(self, reader=None, parser=None, writer=None,
+ source=None, source_class=io.FileInput,
+ destination=None, destination_class=io.FileOutput,
+ settings=None):
+ """
+ Initial setup. If any of `reader`, `parser`, or `writer` are not
+ specified, the corresponding ``set_...`` method should be called with
+ a component name (`set_reader` sets the parser as well).
+ """
+
+ self.document = None
+ """The document tree (`docutils.nodes` objects)."""
+
+ self.reader = reader
+ """A `docutils.readers.Reader` instance."""
+
+ self.parser = parser
+ """A `docutils.parsers.Parser` instance."""
+
+ self.writer = writer
+ """A `docutils.writers.Writer` instance."""
+
+ for component in 'reader', 'parser', 'writer':
+ assert not isinstance(getattr(self, component), str), (
+ 'passed string "%s" as "%s" parameter; pass an instance, '
+ 'or use the "%s_name" parameter instead (in '
+ 'docutils.core.publish_* convenience functions).'
+ % (getattr(self, component), component, component))
+
+ self.source = source
+ """The source of input data, a `docutils.io.Input` instance."""
+
+ self.source_class = source_class
+ """The class for dynamically created source objects."""
+
+ self.destination = destination
+ """The destination for docutils output, a `docutils.io.Output`
+ instance."""
+
+ self.destination_class = destination_class
+ """The class for dynamically created destination objects."""
+
+ self.settings = settings
+ """An object containing Docutils settings as instance attributes.
+ Set by `self.process_command_line()` or `self.get_settings()`."""
+
+ self._stderr = ErrorOutput()
+
+ def set_reader(self, reader_name, parser, parser_name):
+ """Set `self.reader` by name."""
+ reader_class = readers.get_reader_class(reader_name)
+ self.reader = reader_class(parser, parser_name)
+ self.parser = self.reader.parser
+
+ def set_writer(self, writer_name):
+ """Set `self.writer` by name."""
+ writer_class = writers.get_writer_class(writer_name)
+ self.writer = writer_class()
+
+ def set_components(self, reader_name, parser_name, writer_name):
+ if self.reader is None:
+ self.set_reader(reader_name, self.parser, parser_name)
+ if self.parser is None:
+ if self.reader.parser is None:
+ self.reader.set_parser(parser_name)
+ self.parser = self.reader.parser
+ if self.writer is None:
+ self.set_writer(writer_name)
+
+ def setup_option_parser(self, usage=None, description=None,
+ settings_spec=None, config_section=None,
+ **defaults):
+ if config_section:
+ if not settings_spec:
+ settings_spec = SettingsSpec()
+ settings_spec.config_section = config_section
+ parts = config_section.split()
+ if len(parts) > 1 and parts[-1] == 'application':
+ settings_spec.config_section_dependencies = ['applications']
+ #@@@ Add self.source & self.destination to components in future?
+ option_parser = OptionParser(
+ components=(self.parser, self.reader, self.writer, settings_spec),
+ defaults=defaults, read_config_files=True,
+ usage=usage, description=description)
+ return option_parser
+
+ def get_settings(self, usage=None, description=None,
+ settings_spec=None, config_section=None, **defaults):
+ """
+ Set and return default settings (overrides in `defaults` dict).
+
+ Set components first (`self.set_reader` & `self.set_writer`).
+ Explicitly setting `self.settings` disables command line option
+ processing from `self.publish()`.
+ """
+ option_parser = self.setup_option_parser(
+ usage, description, settings_spec, config_section, **defaults)
+ self.settings = option_parser.get_default_values()
+ return self.settings
+
+ def process_programmatic_settings(self, settings_spec,
+ settings_overrides,
+ config_section):
+ if self.settings is None:
+ defaults = (settings_overrides or {}).copy()
+ # Propagate exceptions by default when used programmatically:
+ defaults.setdefault('traceback', True)
+ self.get_settings(settings_spec=settings_spec,
+ config_section=config_section,
+ **defaults)
+
+ def process_command_line(self, argv=None, usage=None, description=None,
+ settings_spec=None, config_section=None,
+ **defaults):
+ """
+ Pass an empty list to `argv` to avoid reading `sys.argv` (the
+ default).
+
+ Set components first (`self.set_reader` & `self.set_writer`).
+ """
+ option_parser = self.setup_option_parser(
+ usage, description, settings_spec, config_section, **defaults)
+ if argv is None:
+ argv = sys.argv[1:]
+ # converting to Unicode (Python 3 does this automatically):
+ if sys.version_info < (3,0):
+ # TODO: make this failsafe and reversible?
+ argv_encoding = (frontend.locale_encoding or 'ascii')
+ argv = [a.decode(argv_encoding) for a in argv]
+ self.settings = option_parser.parse_args(argv)
+
+ def set_io(self, source_path=None, destination_path=None):
+ if self.source is None:
+ self.set_source(source_path=source_path)
+ if self.destination is None:
+ self.set_destination(destination_path=destination_path)
+
+ def set_source(self, source=None, source_path=None):
+ if source_path is None:
+ source_path = self.settings._source
+ else:
+ self.settings._source = source_path
+ # Raise IOError instead of system exit with `tracback == True`
+ # TODO: change io.FileInput's default behaviour and remove this hack
+ try:
+ self.source = self.source_class(
+ source=source, source_path=source_path,
+ encoding=self.settings.input_encoding)
+ except TypeError:
+ self.source = self.source_class(
+ source=source, source_path=source_path,
+ encoding=self.settings.input_encoding)
+
+ def set_destination(self, destination=None, destination_path=None):
+ if destination_path is None:
+ destination_path = self.settings._destination
+ else:
+ self.settings._destination = destination_path
+ self.destination = self.destination_class(
+ destination=destination, destination_path=destination_path,
+ encoding=self.settings.output_encoding,
+ error_handler=self.settings.output_encoding_error_handler)
+
+ def apply_transforms(self):
+ self.document.transformer.populate_from_components(
+ (self.source, self.reader, self.reader.parser, self.writer,
+ self.destination))
+ self.document.transformer.apply_transforms()
+
+ def publish(self, argv=None, usage=None, description=None,
+ settings_spec=None, settings_overrides=None,
+ config_section=None, enable_exit_status=False):
+ """
+ Process command line options and arguments (if `self.settings` not
+ already set), run `self.reader` and then `self.writer`. Return
+ `self.writer`'s output.
+ """
+ exit = None
+ try:
+ if self.settings is None:
+ self.process_command_line(
+ argv, usage, description, settings_spec, config_section,
+ **(settings_overrides or {}))
+ self.set_io()
+ self.document = self.reader.read(self.source, self.parser,
+ self.settings)
+ self.apply_transforms()
+ output = self.writer.write(self.document, self.destination)
+ self.writer.assemble_parts()
+ except SystemExit as error:
+ exit = 1
+ exit_status = error.code
+ except Exception as error:
+ if not self.settings: # exception too early to report nicely
+ raise
+ if self.settings.traceback: # Propagate exceptions?
+ self.debugging_dumps()
+ raise
+ self.report_Exception(error)
+ exit = True
+ exit_status = 1
+ self.debugging_dumps()
+ if (enable_exit_status and self.document
+ and (self.document.reporter.max_level
+ >= self.settings.exit_status_level)):
+ sys.exit(self.document.reporter.max_level + 10)
+ elif exit:
+ sys.exit(exit_status)
+ return output
+
+ def debugging_dumps(self):
+ if not self.document:
+ return
+ if self.settings.dump_settings:
+ print('\n::: Runtime settings:', file=self._stderr)
+ print(pprint.pformat(self.settings.__dict__), file=self._stderr)
+ if self.settings.dump_internals:
+ print('\n::: Document internals:', file=self._stderr)
+ print(pprint.pformat(self.document.__dict__), file=self._stderr)
+ if self.settings.dump_transforms:
+ print('\n::: Transforms applied:', file=self._stderr)
+ print((' (priority, transform class, '
+ 'pending node details, keyword args)'), file=self._stderr)
+ print(pprint.pformat(
+ [(priority, '%s.%s' % (xclass.__module__, xclass.__name__),
+ pending and pending.details, kwargs)
+ for priority, xclass, pending, kwargs
+ in self.document.transformer.applied]), file=self._stderr)
+ if self.settings.dump_pseudo_xml:
+ print('\n::: Pseudo-XML:', file=self._stderr)
+ print(self.document.pformat().encode(
+ 'raw_unicode_escape'), file=self._stderr)
+
+ def report_Exception(self, error):
+ if isinstance(error, utils.SystemMessage):
+ self.report_SystemMessage(error)
+ elif isinstance(error, UnicodeEncodeError):
+ self.report_UnicodeError(error)
+ elif isinstance(error, io.InputError):
+ self._stderr.write('Unable to open source file for reading:\n'
+ ' %s\n' % ErrorString(error))
+ elif isinstance(error, io.OutputError):
+ self._stderr.write(
+ 'Unable to open destination file for writing:\n'
+ ' %s\n' % ErrorString(error))
+ else:
+ print('%s' % ErrorString(error), file=self._stderr)
+ print(("""\
+Exiting due to error. Use "--traceback" to diagnose.
+Please report errors to .
+Include "--traceback" output, Docutils version (%s%s),
+Python version (%s), your OS type & version, and the
+command line used.""" % (__version__,
+ docutils.__version_details__ and
+ ' [%s]'%docutils.__version_details__ or '',
+ sys.version.split()[0])), file=self._stderr)
+
+ def report_SystemMessage(self, error):
+ print(('Exiting due to level-%s (%s) system message.'
+ % (error.level,
+ utils.Reporter.levels[error.level])), file=self._stderr)
+
+ def report_UnicodeError(self, error):
+ data = error.object[error.start:error.end]
+ self._stderr.write(
+ '%s\n'
+ '\n'
+ 'The specified output encoding (%s) cannot\n'
+ 'handle all of the output.\n'
+ 'Try setting "--output-encoding-error-handler" to\n'
+ '\n'
+ '* "xmlcharrefreplace" (for HTML & XML output);\n'
+ ' the output will contain "%s" and should be usable.\n'
+ '* "backslashreplace" (for other output formats);\n'
+ ' look for "%s" in the output.\n'
+ '* "replace"; look for "?" in the output.\n'
+ '\n'
+ '"--output-encoding-error-handler" is currently set to "%s".\n'
+ '\n'
+ 'Exiting due to error. Use "--traceback" to diagnose.\n'
+ 'If the advice above doesn\'t eliminate the error,\n'
+ 'please report it to .\n'
+ 'Include "--traceback" output, Docutils version (%s),\n'
+ 'Python version (%s), your OS type & version, and the\n'
+ 'command line used.\n'
+ % (ErrorString(error),
+ self.settings.output_encoding,
+ data.encode('ascii', 'xmlcharrefreplace'),
+ data.encode('ascii', 'backslashreplace'),
+ self.settings.output_encoding_error_handler,
+ __version__, sys.version.split()[0]))
+
+default_usage = '%prog [options] [ []]'
+default_description = ('Reads from (default is stdin) and writes to '
+ ' (default is stdout). See '
+ ' for '
+ 'the full reference.')
+
+def publish_cmdline(reader=None, reader_name='standalone',
+ parser=None, parser_name='restructuredtext',
+ writer=None, writer_name='pseudoxml',
+ settings=None, settings_spec=None,
+ settings_overrides=None, config_section=None,
+ enable_exit_status=True, argv=None,
+ usage=default_usage, description=default_description):
+ """
+ Set up & run a `Publisher` for command-line-based file I/O (input and
+ output file paths taken automatically from the command line). Return the
+ encoded string output also.
+
+ Parameters: see `publish_programmatically` for the remainder.
+
+ - `argv`: Command-line argument list to use instead of ``sys.argv[1:]``.
+ - `usage`: Usage string, output if there's a problem parsing the command
+ line.
+ - `description`: Program description, output for the "--help" option
+ (along with command-line option descriptions).
+ """
+ pub = Publisher(reader, parser, writer, settings=settings)
+ pub.set_components(reader_name, parser_name, writer_name)
+ output = pub.publish(
+ argv, usage, description, settings_spec, settings_overrides,
+ config_section=config_section, enable_exit_status=enable_exit_status)
+ return output
+
+def publish_file(source=None, source_path=None,
+ destination=None, destination_path=None,
+ reader=None, reader_name='standalone',
+ parser=None, parser_name='restructuredtext',
+ writer=None, writer_name='pseudoxml',
+ settings=None, settings_spec=None, settings_overrides=None,
+ config_section=None, enable_exit_status=False):
+ """
+ Set up & run a `Publisher` for programmatic use with file-like I/O.
+ Return the encoded string output also.
+
+ Parameters: see `publish_programmatically`.
+ """
+ output, pub = publish_programmatically(
+ source_class=io.FileInput, source=source, source_path=source_path,
+ destination_class=io.FileOutput,
+ destination=destination, destination_path=destination_path,
+ reader=reader, reader_name=reader_name,
+ parser=parser, parser_name=parser_name,
+ writer=writer, writer_name=writer_name,
+ settings=settings, settings_spec=settings_spec,
+ settings_overrides=settings_overrides,
+ config_section=config_section,
+ enable_exit_status=enable_exit_status)
+ return output
+
+def publish_string(source, source_path=None, destination_path=None,
+ reader=None, reader_name='standalone',
+ parser=None, parser_name='restructuredtext',
+ writer=None, writer_name='pseudoxml',
+ settings=None, settings_spec=None,
+ settings_overrides=None, config_section=None,
+ enable_exit_status=False):
+ """
+ Set up & run a `Publisher` for programmatic use with string I/O. Return
+ the encoded string or Unicode string output.
+
+ For encoded string output, be sure to set the 'output_encoding' setting to
+ the desired encoding. Set it to 'unicode' for unencoded Unicode string
+ output. Here's one way::
+
+ publish_string(..., settings_overrides={'output_encoding': 'unicode'})
+
+ Similarly for Unicode string input (`source`)::
+
+ publish_string(..., settings_overrides={'input_encoding': 'unicode'})
+
+ Parameters: see `publish_programmatically`.
+ """
+ output, pub = publish_programmatically(
+ source_class=io.StringInput, source=source, source_path=source_path,
+ destination_class=io.StringOutput,
+ destination=None, destination_path=destination_path,
+ reader=reader, reader_name=reader_name,
+ parser=parser, parser_name=parser_name,
+ writer=writer, writer_name=writer_name,
+ settings=settings, settings_spec=settings_spec,
+ settings_overrides=settings_overrides,
+ config_section=config_section,
+ enable_exit_status=enable_exit_status)
+ return output
+
+def publish_parts(source, source_path=None, source_class=io.StringInput,
+ destination_path=None,
+ reader=None, reader_name='standalone',
+ parser=None, parser_name='restructuredtext',
+ writer=None, writer_name='pseudoxml',
+ settings=None, settings_spec=None,
+ settings_overrides=None, config_section=None,
+ enable_exit_status=False):
+ """
+ Set up & run a `Publisher`, and return a dictionary of document parts.
+ Dictionary keys are the names of parts, and values are Unicode strings;
+ encoding is up to the client. For programmatic use with string I/O.
+
+ For encoded string input, be sure to set the 'input_encoding' setting to
+ the desired encoding. Set it to 'unicode' for unencoded Unicode string
+ input. Here's how::
+
+ publish_parts(..., settings_overrides={'input_encoding': 'unicode'})
+
+ Parameters: see `publish_programmatically`.
+ """
+ output, pub = publish_programmatically(
+ source=source, source_path=source_path, source_class=source_class,
+ destination_class=io.StringOutput,
+ destination=None, destination_path=destination_path,
+ reader=reader, reader_name=reader_name,
+ parser=parser, parser_name=parser_name,
+ writer=writer, writer_name=writer_name,
+ settings=settings, settings_spec=settings_spec,
+ settings_overrides=settings_overrides,
+ config_section=config_section,
+ enable_exit_status=enable_exit_status)
+ return pub.writer.parts
+
+def publish_doctree(source, source_path=None,
+ source_class=io.StringInput,
+ reader=None, reader_name='standalone',
+ parser=None, parser_name='restructuredtext',
+ settings=None, settings_spec=None,
+ settings_overrides=None, config_section=None,
+ enable_exit_status=False):
+ """
+ Set up & run a `Publisher` for programmatic use with string I/O.
+ Return the document tree.
+
+ For encoded string input, be sure to set the 'input_encoding' setting to
+ the desired encoding. Set it to 'unicode' for unencoded Unicode string
+ input. Here's one way::
+
+ publish_doctree(..., settings_overrides={'input_encoding': 'unicode'})
+
+ Parameters: see `publish_programmatically`.
+ """
+ pub = Publisher(reader=reader, parser=parser, writer=None,
+ settings=settings,
+ source_class=source_class,
+ destination_class=io.NullOutput)
+ pub.set_components(reader_name, parser_name, 'null')
+ pub.process_programmatic_settings(
+ settings_spec, settings_overrides, config_section)
+ pub.set_source(source, source_path)
+ pub.set_destination(None, None)
+ output = pub.publish(enable_exit_status=enable_exit_status)
+ return pub.document
+
+def publish_from_doctree(document, destination_path=None,
+ writer=None, writer_name='pseudoxml',
+ settings=None, settings_spec=None,
+ settings_overrides=None, config_section=None,
+ enable_exit_status=False):
+ """
+ Set up & run a `Publisher` to render from an existing document
+ tree data structure, for programmatic use with string I/O. Return
+ the encoded string output.
+
+ Note that document.settings is overridden; if you want to use the settings
+ of the original `document`, pass settings=document.settings.
+
+ Also, new document.transformer and document.reporter objects are
+ generated.
+
+ For encoded string output, be sure to set the 'output_encoding' setting to
+ the desired encoding. Set it to 'unicode' for unencoded Unicode string
+ output. Here's one way::
+
+ publish_from_doctree(
+ ..., settings_overrides={'output_encoding': 'unicode'})
+
+ Parameters: `document` is a `docutils.nodes.document` object, an existing
+ document tree.
+
+ Other parameters: see `publish_programmatically`.
+ """
+ reader = docutils.readers.doctree.Reader(parser_name='null')
+ pub = Publisher(reader, None, writer,
+ source=io.DocTreeInput(document),
+ destination_class=io.StringOutput, settings=settings)
+ if not writer and writer_name:
+ pub.set_writer(writer_name)
+ pub.process_programmatic_settings(
+ settings_spec, settings_overrides, config_section)
+ pub.set_destination(None, destination_path)
+ return pub.publish(enable_exit_status=enable_exit_status)
+
+def publish_cmdline_to_binary(reader=None, reader_name='standalone',
+ parser=None, parser_name='restructuredtext',
+ writer=None, writer_name='pseudoxml',
+ settings=None, settings_spec=None,
+ settings_overrides=None, config_section=None,
+ enable_exit_status=True, argv=None,
+ usage=default_usage, description=default_description,
+ destination=None, destination_class=io.BinaryFileOutput
+ ):
+ """
+ Set up & run a `Publisher` for command-line-based file I/O (input and
+ output file paths taken automatically from the command line). Return the
+ encoded string output also.
+
+ This is just like publish_cmdline, except that it uses
+ io.BinaryFileOutput instead of io.FileOutput.
+
+ Parameters: see `publish_programmatically` for the remainder.
+
+ - `argv`: Command-line argument list to use instead of ``sys.argv[1:]``.
+ - `usage`: Usage string, output if there's a problem parsing the command
+ line.
+ - `description`: Program description, output for the "--help" option
+ (along with command-line option descriptions).
+ """
+ pub = Publisher(reader, parser, writer, settings=settings,
+ destination_class=destination_class)
+ pub.set_components(reader_name, parser_name, writer_name)
+ output = pub.publish(
+ argv, usage, description, settings_spec, settings_overrides,
+ config_section=config_section, enable_exit_status=enable_exit_status)
+ return output
+
+def publish_programmatically(source_class, source, source_path,
+ destination_class, destination, destination_path,
+ reader, reader_name,
+ parser, parser_name,
+ writer, writer_name,
+ settings, settings_spec,
+ settings_overrides, config_section,
+ enable_exit_status):
+ """
+ Set up & run a `Publisher` for custom programmatic use. Return the
+ encoded string output and the Publisher object.
+
+ Applications should not need to call this function directly. If it does
+ seem to be necessary to call this function directly, please write to the
+ Docutils-develop mailing list
+ .
+
+ Parameters:
+
+ * `source_class` **required**: The class for dynamically created source
+ objects. Typically `io.FileInput` or `io.StringInput`.
+
+ * `source`: Type depends on `source_class`:
+
+ - If `source_class` is `io.FileInput`: Either a file-like object
+ (must have 'read' and 'close' methods), or ``None``
+ (`source_path` is opened). If neither `source` nor
+ `source_path` are supplied, `sys.stdin` is used.
+
+ - If `source_class` is `io.StringInput` **required**: The input
+ string, either an encoded 8-bit string (set the
+ 'input_encoding' setting to the correct encoding) or a Unicode
+ string (set the 'input_encoding' setting to 'unicode').
+
+ * `source_path`: Type depends on `source_class`:
+
+ - `io.FileInput`: Path to the input file, opened if no `source`
+ supplied.
+
+ - `io.StringInput`: Optional. Path to the file or object that produced
+ `source`. Only used for diagnostic output.
+
+ * `destination_class` **required**: The class for dynamically created
+ destination objects. Typically `io.FileOutput` or `io.StringOutput`.
+
+ * `destination`: Type depends on `destination_class`:
+
+ - `io.FileOutput`: Either a file-like object (must have 'write' and
+ 'close' methods), or ``None`` (`destination_path` is opened). If
+ neither `destination` nor `destination_path` are supplied,
+ `sys.stdout` is used.
+
+ - `io.StringOutput`: Not used; pass ``None``.
+
+ * `destination_path`: Type depends on `destination_class`:
+
+ - `io.FileOutput`: Path to the output file. Opened if no `destination`
+ supplied.
+
+ - `io.StringOutput`: Path to the file or object which will receive the
+ output; optional. Used for determining relative paths (stylesheets,
+ source links, etc.).
+
+ * `reader`: A `docutils.readers.Reader` object.
+
+ * `reader_name`: Name or alias of the Reader class to be instantiated if
+ no `reader` supplied.
+
+ * `parser`: A `docutils.parsers.Parser` object.
+
+ * `parser_name`: Name or alias of the Parser class to be instantiated if
+ no `parser` supplied.
+
+ * `writer`: A `docutils.writers.Writer` object.
+
+ * `writer_name`: Name or alias of the Writer class to be instantiated if
+ no `writer` supplied.
+
+ * `settings`: A runtime settings (`docutils.frontend.Values`) object, for
+ dotted-attribute access to runtime settings. It's the end result of the
+ `SettingsSpec`, config file, and option processing. If `settings` is
+ passed, it's assumed to be complete and no further setting/config/option
+ processing is done.
+
+ * `settings_spec`: A `docutils.SettingsSpec` subclass or object. Provides
+ extra application-specific settings definitions independently of
+ components. In other words, the application becomes a component, and
+ its settings data is processed along with that of the other components.
+ Used only if no `settings` specified.
+
+ * `settings_overrides`: A dictionary containing application-specific
+ settings defaults that override the defaults of other components.
+ Used only if no `settings` specified.
+
+ * `config_section`: A string, the name of the configuration file section
+ for this application. Overrides the ``config_section`` attribute
+ defined by `settings_spec`. Used only if no `settings` specified.
+
+ * `enable_exit_status`: Boolean; enable exit status at end of processing?
+ """
+ pub = Publisher(reader, parser, writer, settings=settings,
+ source_class=source_class,
+ destination_class=destination_class)
+ pub.set_components(reader_name, parser_name, writer_name)
+ pub.process_programmatic_settings(
+ settings_spec, settings_overrides, config_section)
+ pub.set_source(source, source_path)
+ pub.set_destination(destination, destination_path)
+ output = pub.publish(enable_exit_status=enable_exit_status)
+ return output, pub
diff --git a/venv/lib/python3.5/site-packages/docutils/examples.py b/venv/lib/python3.5/site-packages/docutils/examples.py
new file mode 100644
index 0000000..395dbbf
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/docutils/examples.py
@@ -0,0 +1,97 @@
+# $Id: examples.py 7320 2012-01-19 22:33:02Z milde $
+# Author: David Goodger
+# Copyright: This module has been placed in the public domain.
+
+"""
+This module contains practical examples of Docutils client code.
+
+Importing this module from client code is not recommended; its contents are
+subject to change in future Docutils releases. Instead, it is recommended
+that you copy and paste the parts you need into your own code, modifying as
+necessary.
+"""
+
+from docutils import core, io
+
+
+def html_parts(input_string, source_path=None, destination_path=None,
+ input_encoding='unicode', doctitle=True,
+ initial_header_level=1):
+ """
+ Given an input string, returns a dictionary of HTML document parts.
+
+ Dictionary keys are the names of parts, and values are Unicode strings;
+ encoding is up to the client.
+
+ Parameters:
+
+ - `input_string`: A multi-line text string; required.
+ - `source_path`: Path to the source file or object. Optional, but useful
+ for diagnostic output (system messages).
+ - `destination_path`: Path to the file or object which will receive the
+ output; optional. Used for determining relative paths (stylesheets,
+ source links, etc.).
+ - `input_encoding`: The encoding of `input_string`. If it is an encoded
+ 8-bit string, provide the correct encoding. If it is a Unicode string,
+ use "unicode", the default.
+ - `doctitle`: Disable the promotion of a lone top-level section title to
+ document title (and subsequent section title to document subtitle
+ promotion); enabled by default.
+ - `initial_header_level`: The initial level for header elements (e.g. 1
+ for "
").
+ """
+ overrides = {'input_encoding': input_encoding,
+ 'doctitle_xform': doctitle,
+ 'initial_header_level': initial_header_level}
+ parts = core.publish_parts(
+ source=input_string, source_path=source_path,
+ destination_path=destination_path,
+ writer_name='html', settings_overrides=overrides)
+ return parts
+
+def html_body(input_string, source_path=None, destination_path=None,
+ input_encoding='unicode', output_encoding='unicode',
+ doctitle=True, initial_header_level=1):
+ """
+ Given an input string, returns an HTML fragment as a string.
+
+ The return value is the contents of the element.
+
+ Parameters (see `html_parts()` for the remainder):
+
+ - `output_encoding`: The desired encoding of the output. If a Unicode
+ string is desired, use the default value of "unicode" .
+ """
+ parts = html_parts(
+ input_string=input_string, source_path=source_path,
+ destination_path=destination_path,
+ input_encoding=input_encoding, doctitle=doctitle,
+ initial_header_level=initial_header_level)
+ fragment = parts['html_body']
+ if output_encoding != 'unicode':
+ fragment = fragment.encode(output_encoding)
+ return fragment
+
+def internals(input_string, source_path=None, destination_path=None,
+ input_encoding='unicode', settings_overrides=None):
+ """
+ Return the document tree and publisher, for exploring Docutils internals.
+
+ Parameters: see `html_parts()`.
+ """
+ if settings_overrides:
+ overrides = settings_overrides.copy()
+ else:
+ overrides = {}
+ overrides['input_encoding'] = input_encoding
+ output, pub = core.publish_programmatically(
+ source_class=io.StringInput, source=input_string,
+ source_path=source_path,
+ destination_class=io.NullOutput, destination=None,
+ destination_path=destination_path,
+ reader=None, reader_name='standalone',
+ parser=None, parser_name='restructuredtext',
+ writer=None, writer_name='null',
+ settings=None, settings_spec=None, settings_overrides=overrides,
+ config_section=None, enable_exit_status=None)
+ return pub.writer.document, pub
diff --git a/venv/lib/python3.5/site-packages/docutils/frontend.py b/venv/lib/python3.5/site-packages/docutils/frontend.py
new file mode 100644
index 0000000..d96faf0
--- /dev/null
+++ b/venv/lib/python3.5/site-packages/docutils/frontend.py
@@ -0,0 +1,854 @@
+# $Id: frontend.py 8126 2017-06-23 09:34:28Z milde $
+# Author: David Goodger
+# Copyright: This module has been placed in the public domain.
+
+"""
+Command-line and common processing for Docutils front-end tools.
+
+Exports the following classes:
+
+* `OptionParser`: Standard Docutils command-line processing.
+* `Option`: Customized version of `optparse.Option`; validation support.
+* `Values`: Runtime settings; objects are simple structs
+ (``object.attribute``). Supports cumulative list settings (attributes).
+* `ConfigParser`: Standard Docutils config file processing.
+
+Also exports the following functions:
+
+* Option callbacks: `store_multiple`, `read_config_file`.
+* Setting validators: `validate_encoding`,
+ `validate_encoding_error_handler`,
+ `validate_encoding_and_error_handler`,
+ `validate_boolean`, `validate_ternary`, `validate_threshold`,
+ `validate_colon_separated_string_list`,
+ `validate_comma_separated_string_list`,
+ `validate_dependency_file`.
+* `make_paths_absolute`.
+* SettingSpec manipulation: `filter_settings_spec`.
+"""
+
+__docformat__ = 'reStructuredText'
+
+import os
+import os.path
+import sys
+import warnings
+import configparser as CP
+import codecs
+import optparse
+from optparse import SUPPRESS_HELP
+import docutils
+import docutils.utils
+import docutils.nodes
+from docutils.utils.error_reporting import (locale_encoding, SafeString,
+ ErrorOutput, ErrorString)
+
+
+def store_multiple(option, opt, value, parser, *args, **kwargs):
+ """
+ Store multiple values in `parser.values`. (Option callback.)
+
+ Store `None` for each attribute named in `args`, and store the value for
+ each key (attribute name) in `kwargs`.
+ """
+ for attribute in args:
+ setattr(parser.values, attribute, None)
+ for key, value in list(kwargs.items()):
+ setattr(parser.values, key, value)
+
+def read_config_file(option, opt, value, parser):
+ """
+ Read a configuration file during option processing. (Option callback.)
+ """
+ try:
+ new_settings = parser.get_config_file_settings(value)
+ except ValueError as error:
+ parser.error(error)
+ parser.values.update(new_settings, parser)
+
+def validate_encoding(setting, value, option_parser,
+ config_parser=None, config_section=None):
+ try:
+ codecs.lookup(value)
+ except LookupError:
+ raise LookupError('setting "%s": unknown encoding: "%s"'
+ % (setting, value))
+ return value
+
+def validate_encoding_error_handler(setting, value, option_parser,
+ config_parser=None, config_section=None):
+ try:
+ codecs.lookup_error(value)
+ except LookupError:
+ raise LookupError(
+ 'unknown encoding error handler: "%s" (choices: '
+ '"strict", "ignore", "replace", "backslashreplace", '
+ '"xmlcharrefreplace", and possibly others; see documentation for '
+ 'the Python ``codecs`` module)' % value)
+ return value
+
+def validate_encoding_and_error_handler(
+ setting, value, option_parser, config_parser=None, config_section=None):
+ """
+ Side-effect: if an error handler is included in the value, it is inserted
+ into the appropriate place as if it was a separate setting/option.
+ """
+ if ':' in value:
+ encoding, handler = value.split(':')
+ validate_encoding_error_handler(
+ setting + '_error_handler', handler, option_parser,
+ config_parser, config_section)
+ if config_parser:
+ config_parser.set(config_section, setting + '_error_handler',
+ handler)
+ else:
+ setattr(option_parser.values, setting + '_error_handler', handler)
+ else:
+ encoding = value
+ validate_encoding(setting, encoding, option_parser,
+ config_parser, config_section)
+ return encoding
+
+def validate_boolean(setting, value, option_parser,
+ config_parser=None, config_section=None):
+ """Check/normalize boolean settings:
+ True: '1', 'on', 'yes', 'true'
+ False: '0', 'off', 'no','false', ''
+ """
+ if isinstance(value, bool):
+ return value
+ try:
+ return option_parser.booleans[value.strip().lower()]
+ except KeyError:
+ raise LookupError('unknown boolean value: "%s"' % value)
+
+def validate_ternary(setting, value, option_parser,
+ config_parser=None, config_section=None):
+ """Check/normalize three-value settings:
+ True: '1', 'on', 'yes', 'true'
+ False: '0', 'off', 'no','false', ''
+ any other value: returned as-is.
+ """
+ if isinstance(value, bool) or value is None:
+ return value
+ try:
+ return option_parser.booleans[value.strip().lower()]
+ except KeyError:
+ return value
+
+def validate_nonnegative_int(setting, value, option_parser,
+ config_parser=None, config_section=None):
+ value = int(value)
+ if value < 0:
+ raise ValueError('negative value; must be positive or zero')
+ return value
+
+def validate_threshold(setting, value, option_parser,
+ config_parser=None, config_section=None):
+ try:
+ return int(value)
+ except ValueError:
+ try:
+ return option_parser.thresholds[value.lower()]
+ except (KeyError, AttributeError):
+ raise LookupError('unknown threshold: %r.' % value)
+
+def validate_colon_separated_string_list(
+ setting, value, option_parser, config_parser=None, config_section=None):
+ if not isinstance(value, list):
+ value = value.split(':')
+ else:
+ last = value.pop()
+ value.extend(last.split(':'))
+ return value
+
+def validate_comma_separated_list(setting, value, option_parser,
+ config_parser=None, config_section=None):
+ """Check/normalize list arguments (split at "," and strip whitespace).
+ """
+ # `value` is already a ``list`` when given as command line option
+ # and "action" is "append" and ``unicode`` or ``str`` else.
+ if not isinstance(value, list):
+ value = [value]
+ # this function is called for every option added to `value`
+ # -> split the last item and append the result:
+ last = value.pop()
+ items = [i.strip(' \t\n') for i in last.split(',') if i.strip(' \t\n')]
+ value.extend(items)
+ return value
+
+def validate_url_trailing_slash(
+ setting, value, option_parser, config_parser=None, config_section=None):
+ if not value:
+ return './'
+ elif value.endswith('/'):
+ return value
+ else:
+ return value + '/'
+
+def validate_dependency_file(setting, value, option_parser,
+ config_parser=None, config_section=None):
+ try:
+ return docutils.utils.DependencyList(value)
+ except IOError:
+ return docutils.utils.DependencyList(None)
+
+def validate_strip_class(setting, value, option_parser,
+ config_parser=None, config_section=None):
+ # value is a comma separated string list:
+ value = validate_comma_separated_list(setting, value, option_parser,
+ config_parser, config_section)
+ # validate list elements:
+ for cls in value:
+ normalized = docutils.nodes.make_id(cls)
+ if cls != normalized:
+ raise ValueError('Invalid class value %r (perhaps %r?)'
+ % (cls, normalized))
+ return value
+
+def validate_smartquotes_locales(setting, value, option_parser,
+ config_parser=None, config_section=None):
+ """Check/normalize a comma separated list of smart quote definitions.
+
+ Return a list of (language-tag, quotes) string tuples."""
+
+ # value is a comma separated string list:
+ value = validate_comma_separated_list(setting, value, option_parser,
+ config_parser, config_section)
+ # validate list elements
+ lc_quotes = []
+ for item in value:
+ try:
+ lang, quotes = item.split(':', 1)
+ except AttributeError:
+ # this function is called for every option added to `value`
+ # -> ignore if already a tuple:
+ lc_quotes.append(item)
+ continue
+ except ValueError:
+ raise ValueError('Invalid value "%s".'
+ ' Format is ":".'
+ % item.encode('ascii', 'backslashreplace'))
+ # parse colon separated string list:
+ quotes = quotes.strip()
+ multichar_quotes = quotes.split(':')
+ if len(multichar_quotes) == 4:
+ quotes = multichar_quotes
+ elif len(quotes) != 4:
+ raise ValueError('Invalid value "%s". Please specify 4 quotes\n'
+ ' (primary open/close; secondary open/close).'
+ % item.encode('ascii', 'backslashreplace'))
+ lc_quotes.append((lang,quotes))
+ return lc_quotes
+
+def make_paths_absolute(pathdict, keys, base_path=None):
+ """
+ Interpret filesystem path settings relative to the `base_path` given.
+
+ Paths are values in `pathdict` whose keys are in `keys`. Get `keys` from
+ `OptionParser.relative_path_settings`.
+ """
+ if base_path is None:
+ base_path = os.getcwd() # type(base_path) == unicode
+ # to allow combining non-ASCII cwd with unicode values in `pathdict`
+ for key in keys:
+ if key in pathdict:
+ value = pathdict[key]
+ if isinstance(value, list):
+ value = [make_one_path_absolute(base_path, path)
+ for path in value]
+ elif value:
+ value = make_one_path_absolute(base_path, value)
+ pathdict[key] = value
+
+def make_one_path_absolute(base_path, path):
+ return os.path.abspath(os.path.join(base_path, path))
+
+def filter_settings_spec(settings_spec, *exclude, **replace):
+ """Return a copy of `settings_spec` excluding/replacing some settings.
+
+ `settings_spec` is a tuple of configuration settings with a structure
+ described for docutils.SettingsSpec.settings_spec.
+
+ Optional positional arguments are names of to-be-excluded settings.
+ Keyword arguments are option specification replacements.
+ (See the html4strict writer for an example.)
+ """
+ settings = list(settings_spec)
+ # every third item is a sequence of option tuples
+ for i in range(2, len(settings), 3):
+ newopts = []
+ for opt_spec in settings[i]:
+ # opt_spec is ("", [