diff --git a/bots-venv/bin/activate b/bots-venv/bin/activate new file mode 100644 index 0000000..dcc2c48 --- /dev/null +++ b/bots-venv/bin/activate @@ -0,0 +1,76 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r + fi + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "$1" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + if [ "x(bots-venv) " != x ] ; then + PS1="(bots-venv) ${PS1:-}" + else + if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" + else + PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" + fi + fi + export PS1 +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r +fi diff --git a/bots-venv/bin/activate.csh b/bots-venv/bin/activate.csh new file mode 100644 index 0000000..8caab5e --- /dev/null +++ b/bots-venv/bin/activate.csh @@ -0,0 +1,37 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + if ("bots-venv" != "") then + set env_name = "bots-venv" + else + if (`basename "VIRTUAL_ENV"` == "__") then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + set env_name = `basename \`dirname "$VIRTUAL_ENV"\`` + else + set env_name = `basename "$VIRTUAL_ENV"` + endif + endif + set prompt = "[$env_name] $prompt" + unset env_name +endif + +alias pydoc python -m pydoc + +rehash diff --git a/bots-venv/bin/activate.fish b/bots-venv/bin/activate.fish new file mode 100644 index 0000000..d968834 --- /dev/null +++ b/bots-venv/bin/activate.fish @@ -0,0 +1,75 @@ +# This file must be used with ". bin/activate.fish" *from fish* (http://fishshell.org) +# you cannot run it directly + +function deactivate -d "Exit virtualenv and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + functions -e fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + + set -e VIRTUAL_ENV + if test "$argv[1]" != "nondestructive" + # Self destruct! + functions -e deactivate + end +end + +# unset irrelevant variables +deactivate nondestructive + +set -gx VIRTUAL_ENV "/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# unset PYTHONHOME if set +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # save the current fish_prompt function as the function _old_fish_prompt + functions -c fish_prompt _old_fish_prompt + + # with the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command + set -l old_status $status + + # Prompt override? + if test -n "(bots-venv) " + printf "%s%s" "(bots-venv) " (set_color normal) + else + # ...Otherwise, prepend env + set -l _checkbase (basename "$VIRTUAL_ENV") + if test $_checkbase = "__" + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + printf "%s[%s]%s " (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal) + else + printf "%s(%s)%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal) + end + end + + # Restore the return status of the previous command. + echo "exit $old_status" | . + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/bots-venv/bin/easy_install b/bots-venv/bin/easy_install new file mode 100755 index 0000000..009086e --- /dev/null +++ b/bots-venv/bin/easy_install @@ -0,0 +1,10 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bots-venv/bin/easy_install-3.7 b/bots-venv/bin/easy_install-3.7 new file mode 100755 index 0000000..009086e --- /dev/null +++ b/bots-venv/bin/easy_install-3.7 @@ -0,0 +1,10 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bots-venv/bin/markdown_py b/bots-venv/bin/markdown_py new file mode 100755 index 0000000..45c74ff --- /dev/null +++ b/bots-venv/bin/markdown_py @@ -0,0 +1,10 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from markdown.__main__ import run + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(run()) diff --git a/bots-venv/bin/pelican b/bots-venv/bin/pelican new file mode 100755 index 0000000..a8537f6 --- /dev/null +++ b/bots-venv/bin/pelican @@ -0,0 +1,10 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pelican.__main__ import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bots-venv/bin/pelican-import b/bots-venv/bin/pelican-import new file mode 100755 index 0000000..7fdea61 --- /dev/null +++ b/bots-venv/bin/pelican-import @@ -0,0 +1,10 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pelican.tools.pelican_import import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bots-venv/bin/pelican-plugins b/bots-venv/bin/pelican-plugins new file mode 100755 index 0000000..ff0a9ee --- /dev/null +++ b/bots-venv/bin/pelican-plugins @@ -0,0 +1,10 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pelican.plugins._utils import list_plugins + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(list_plugins()) diff --git a/bots-venv/bin/pelican-quickstart b/bots-venv/bin/pelican-quickstart new file mode 100755 index 0000000..096a111 --- /dev/null +++ b/bots-venv/bin/pelican-quickstart @@ -0,0 +1,10 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pelican.tools.pelican_quickstart import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bots-venv/bin/pelican-themes b/bots-venv/bin/pelican-themes new file mode 100755 index 0000000..37abcc0 --- /dev/null +++ b/bots-venv/bin/pelican-themes @@ -0,0 +1,10 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pelican.tools.pelican_themes import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bots-venv/bin/pip b/bots-venv/bin/pip new file mode 100755 index 0000000..b4809bc --- /dev/null +++ b/bots-venv/bin/pip @@ -0,0 +1,10 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bots-venv/bin/pip3 b/bots-venv/bin/pip3 new file mode 100755 index 0000000..b4809bc --- /dev/null +++ b/bots-venv/bin/pip3 @@ -0,0 +1,10 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bots-venv/bin/pip3.7 b/bots-venv/bin/pip3.7 new file mode 100755 index 0000000..b4809bc --- /dev/null +++ b/bots-venv/bin/pip3.7 @@ -0,0 +1,10 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bots-venv/bin/pygmentize b/bots-venv/bin/pygmentize new file mode 100755 index 0000000..526ea4d --- /dev/null +++ b/bots-venv/bin/pygmentize @@ -0,0 +1,10 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pygments.cmdline import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bots-venv/bin/python b/bots-venv/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/bots-venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/bots-venv/bin/python3 b/bots-venv/bin/python3 new file mode 120000 index 0000000..ae65fda --- /dev/null +++ b/bots-venv/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/bots-venv/bin/rst2html.py b/bots-venv/bin/rst2html.py new file mode 100755 index 0000000..0943135 --- /dev/null +++ b/bots-venv/bin/rst2html.py @@ -0,0 +1,23 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 + +# $Id: rst2html.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML documents from standalone reStructuredText ' + 'sources. ' + default_description) + +publish_cmdline(writer_name='html', description=description) diff --git a/bots-venv/bin/rst2html4.py b/bots-venv/bin/rst2html4.py new file mode 100755 index 0000000..db28559 --- /dev/null +++ b/bots-venv/bin/rst2html4.py @@ -0,0 +1,26 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 + +# $Id: rst2html4.py 7994 2016-12-10 17:41:45Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing (X)HTML. + +The output conforms to XHTML 1.0 transitional +and almost to HTML 4.01 transitional (except for closing empty tags). +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML documents from standalone reStructuredText ' + 'sources. ' + default_description) + +publish_cmdline(writer_name='html4', description=description) diff --git a/bots-venv/bin/rst2html5.py b/bots-venv/bin/rst2html5.py new file mode 100755 index 0000000..5e5d2fa --- /dev/null +++ b/bots-venv/bin/rst2html5.py @@ -0,0 +1,35 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 +# -*- coding: utf8 -*- +# :Copyright: © 2015 Günter Milde. +# :License: Released under the terms of the `2-Clause BSD license`_, in short: +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. +# This file is offered as-is, without any warranty. +# +# .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause +# +# Revision: $Revision: 8410 $ +# Date: $Date: 2019-11-04 22:14:43 +0100 (Mo, 04. Nov 2019) $ + +""" +A minimal front end to the Docutils Publisher, producing HTML 5 documents. + +The output also conforms to XHTML 1.0 transitional +(except for the doctype declaration). +""" + +try: + import locale # module missing in Jython + locale.setlocale(locale.LC_ALL, '') +except locale.Error: + pass + +from docutils.core import publish_cmdline, default_description + +description = (u'Generates HTML 5 documents from standalone ' + u'reStructuredText sources ' + + default_description) + +publish_cmdline(writer_name='html5', description=description) diff --git a/bots-venv/bin/rst2latex.py b/bots-venv/bin/rst2latex.py new file mode 100755 index 0000000..9412e1e --- /dev/null +++ b/bots-venv/bin/rst2latex.py @@ -0,0 +1,26 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 + +# $Id: rst2latex.py 5905 2009-04-16 12:04:49Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing LaTeX. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline + +description = ('Generates LaTeX documents from standalone reStructuredText ' + 'sources. ' + 'Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +publish_cmdline(writer_name='latex', description=description) diff --git a/bots-venv/bin/rst2man.py b/bots-venv/bin/rst2man.py new file mode 100755 index 0000000..5845865 --- /dev/null +++ b/bots-venv/bin/rst2man.py @@ -0,0 +1,26 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 + +# Author: +# Contact: grubert@users.sf.net +# Copyright: This module has been placed in the public domain. + +""" +man.py +====== + +This module provides a simple command line interface that uses the +man page writer to output from ReStructuredText source. +""" + +import locale +try: + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description +from docutils.writers import manpage + +description = ("Generates plain unix manual documents. " + default_description) + +publish_cmdline(writer=manpage.Writer(), description=description) diff --git a/bots-venv/bin/rst2odt.py b/bots-venv/bin/rst2odt.py new file mode 100755 index 0000000..93e39de --- /dev/null +++ b/bots-venv/bin/rst2odt.py @@ -0,0 +1,30 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 + +# $Id: rst2odt.py 5839 2009-01-07 19:09:28Z dkuhlman $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" +A front end to the Docutils Publisher, producing OpenOffice documents. +""" + +import sys +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline_to_binary, default_description +from docutils.writers.odf_odt import Writer, Reader + + +description = ('Generates OpenDocument/OpenOffice/ODF documents from ' + 'standalone reStructuredText sources. ' + default_description) + + +writer = Writer() +reader = Reader() +output = publish_cmdline_to_binary(reader=reader, writer=writer, + description=description) + diff --git a/bots-venv/bin/rst2odt_prepstyles.py b/bots-venv/bin/rst2odt_prepstyles.py new file mode 100755 index 0000000..dd12c96 --- /dev/null +++ b/bots-venv/bin/rst2odt_prepstyles.py @@ -0,0 +1,67 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 + +# $Id: rst2odt_prepstyles.py 8346 2019-08-26 12:11:32Z milde $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" +Fix a word-processor-generated styles.odt for odtwriter use: Drop page size +specifications from styles.xml in STYLE_FILE.odt. +""" + +# Author: Michael Schutte + +from __future__ import print_function + +from lxml import etree +import sys +import zipfile +from tempfile import mkstemp +import shutil +import os + +NAMESPACES = { + "style": "urn:oasis:names:tc:opendocument:xmlns:style:1.0", + "fo": "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" +} + + +def prepstyle(filename): + + zin = zipfile.ZipFile(filename) + styles = zin.read("styles.xml") + + root = etree.fromstring(styles) + for el in root.xpath("//style:page-layout-properties", + namespaces=NAMESPACES): + for attr in el.attrib: + if attr.startswith("{%s}" % NAMESPACES["fo"]): + del el.attrib[attr] + + tempname = mkstemp() + zout = zipfile.ZipFile(os.fdopen(tempname[0], "w"), "w", + zipfile.ZIP_DEFLATED) + + for item in zin.infolist(): + if item.filename == "styles.xml": + zout.writestr(item, etree.tostring(root)) + else: + zout.writestr(item, zin.read(item.filename)) + + zout.close() + zin.close() + shutil.move(tempname[1], filename) + + +def main(): + args = sys.argv[1:] + if len(args) != 1: + print(__doc__, file=sys.stderr) + print("Usage: %s STYLE_FILE.odt\n" % sys.argv[0], file=sys.stderr) + sys.exit(1) + filename = args[0] + prepstyle(filename) + + +if __name__ == '__main__': + main() diff --git a/bots-venv/bin/rst2pseudoxml.py b/bots-venv/bin/rst2pseudoxml.py new file mode 100755 index 0000000..59b3ed2 --- /dev/null +++ b/bots-venv/bin/rst2pseudoxml.py @@ -0,0 +1,23 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 + +# $Id: rst2pseudoxml.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing pseudo-XML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates pseudo-XML from standalone reStructuredText ' + 'sources (for testing purposes). ' + default_description) + +publish_cmdline(description=description) diff --git a/bots-venv/bin/rst2s5.py b/bots-venv/bin/rst2s5.py new file mode 100755 index 0000000..2d6f225 --- /dev/null +++ b/bots-venv/bin/rst2s5.py @@ -0,0 +1,24 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 + +# $Id: rst2s5.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Chris Liechti +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML slides using +the S5 template system. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates S5 (X)HTML slideshow documents from standalone ' + 'reStructuredText sources. ' + default_description) + +publish_cmdline(writer_name='s5', description=description) diff --git a/bots-venv/bin/rst2xetex.py b/bots-venv/bin/rst2xetex.py new file mode 100755 index 0000000..10abc28 --- /dev/null +++ b/bots-venv/bin/rst2xetex.py @@ -0,0 +1,27 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 + +# $Id: rst2xetex.py 7847 2015-03-17 17:30:47Z milde $ +# Author: Guenter Milde +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing Lua/XeLaTeX code. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline + +description = ('Generates LaTeX documents from standalone reStructuredText ' + 'sources for compilation with the Unicode-aware TeX variants ' + 'XeLaTeX or LuaLaTeX. ' + 'Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +publish_cmdline(writer_name='xetex', description=description) diff --git a/bots-venv/bin/rst2xml.py b/bots-venv/bin/rst2xml.py new file mode 100755 index 0000000..6aab33d --- /dev/null +++ b/bots-venv/bin/rst2xml.py @@ -0,0 +1,23 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 + +# $Id: rst2xml.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing Docutils XML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates Docutils-native XML from standalone ' + 'reStructuredText sources. ' + default_description) + +publish_cmdline(writer_name='xml', description=description) diff --git a/bots-venv/bin/rstpep2html.py b/bots-venv/bin/rstpep2html.py new file mode 100755 index 0000000..e5ef4fd --- /dev/null +++ b/bots-venv/bin/rstpep2html.py @@ -0,0 +1,25 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 + +# $Id: rstpep2html.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML from PEP +(Python Enhancement Proposal) documents. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML from reStructuredText-format PEP files. ' + + default_description) + +publish_cmdline(reader_name='pep', writer_name='pep_html', + description=description) diff --git a/bots-venv/bin/unidecode b/bots-venv/bin/unidecode new file mode 100755 index 0000000..6ce3e7c --- /dev/null +++ b/bots-venv/bin/unidecode @@ -0,0 +1,10 @@ +#!/home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from unidecode.util import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/INSTALLER b/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/LICENSE.rst b/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/LICENSE.rst new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/METADATA b/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/METADATA new file mode 100644 index 0000000..55c0f82 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/METADATA @@ -0,0 +1,106 @@ +Metadata-Version: 2.1 +Name: Jinja2 +Version: 2.11.2 +Summary: A very fast and expressive template engine. +Home-page: https://palletsprojects.com/p/jinja/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Code, https://github.com/pallets/jinja +Project-URL: Issue tracker, https://github.com/pallets/jinja/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +Description-Content-Type: text/x-rst +Requires-Dist: MarkupSafe (>=0.23) +Provides-Extra: i18n +Requires-Dist: Babel (>=0.8) ; extra == 'i18n' + +Jinja +===== + +Jinja is a fast, expressive, extensible templating engine. Special +placeholders in the template allow writing code similar to Python +syntax. Then the template is passed data to render the final document. + +It includes: + +- Template inheritance and inclusion. +- Define and import macros within templates. +- HTML templates can use autoescaping to prevent XSS from untrusted + user input. +- A sandboxed environment can safely render untrusted templates. +- AsyncIO support for generating templates and calling async + functions. +- I18N support with Babel. +- Templates are compiled to optimized Python code just-in-time and + cached, or can be compiled ahead-of-time. +- Exceptions point to the correct line in templates to make debugging + easier. +- Extensible filters, tests, functions, and even syntax. + +Jinja's philosophy is that while application logic belongs in Python if +possible, it shouldn't make the template designer's job difficult by +restricting functionality too much. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Jinja2 + +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + +In A Nutshell +------------- + +.. code-block:: jinja + + {% extends "base.html" %} + {% block title %}Members{% endblock %} + {% block content %} + + {% endblock %} + + +Links +----- + +- Website: https://palletsprojects.com/p/jinja/ +- Documentation: https://jinja.palletsprojects.com/ +- Releases: https://pypi.org/project/Jinja2/ +- Code: https://github.com/pallets/jinja +- Issue tracker: https://github.com/pallets/jinja/issues +- Test status: https://dev.azure.com/pallets/jinja/_build +- Official chat: https://discord.gg/t6rrQZH + + diff --git a/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/RECORD b/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/RECORD new file mode 100644 index 0000000..008d7fa --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/RECORD @@ -0,0 +1,61 @@ +Jinja2-2.11.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Jinja2-2.11.2.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Jinja2-2.11.2.dist-info/METADATA,sha256=5ZHRZoIRAMHsJPnqhlJ622_dRPsYePYJ-9EH4-Ry7yI,3535 +Jinja2-2.11.2.dist-info/RECORD,, +Jinja2-2.11.2.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 +Jinja2-2.11.2.dist-info/entry_points.txt,sha256=Qy_DkVo6Xj_zzOtmErrATe8lHZhOqdjpt3e4JJAGyi8,61 +Jinja2-2.11.2.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7 +jinja2/__init__.py,sha256=0QCM_jKKDM10yzSdHRVV4mQbCbDqf0GN0GirAqibn9Y,1549 +jinja2/__pycache__/__init__.cpython-37.pyc,, +jinja2/__pycache__/_compat.cpython-37.pyc,, +jinja2/__pycache__/_identifier.cpython-37.pyc,, +jinja2/__pycache__/asyncfilters.cpython-37.pyc,, +jinja2/__pycache__/asyncsupport.cpython-37.pyc,, +jinja2/__pycache__/bccache.cpython-37.pyc,, +jinja2/__pycache__/compiler.cpython-37.pyc,, +jinja2/__pycache__/constants.cpython-37.pyc,, +jinja2/__pycache__/debug.cpython-37.pyc,, +jinja2/__pycache__/defaults.cpython-37.pyc,, +jinja2/__pycache__/environment.cpython-37.pyc,, +jinja2/__pycache__/exceptions.cpython-37.pyc,, +jinja2/__pycache__/ext.cpython-37.pyc,, +jinja2/__pycache__/filters.cpython-37.pyc,, +jinja2/__pycache__/idtracking.cpython-37.pyc,, +jinja2/__pycache__/lexer.cpython-37.pyc,, +jinja2/__pycache__/loaders.cpython-37.pyc,, +jinja2/__pycache__/meta.cpython-37.pyc,, +jinja2/__pycache__/nativetypes.cpython-37.pyc,, +jinja2/__pycache__/nodes.cpython-37.pyc,, +jinja2/__pycache__/optimizer.cpython-37.pyc,, +jinja2/__pycache__/parser.cpython-37.pyc,, +jinja2/__pycache__/runtime.cpython-37.pyc,, +jinja2/__pycache__/sandbox.cpython-37.pyc,, +jinja2/__pycache__/tests.cpython-37.pyc,, +jinja2/__pycache__/utils.cpython-37.pyc,, +jinja2/__pycache__/visitor.cpython-37.pyc,, +jinja2/_compat.py,sha256=B6Se8HjnXVpzz9-vfHejn-DV2NjaVK-Iewupc5kKlu8,3191 +jinja2/_identifier.py,sha256=EdgGJKi7O1yvr4yFlvqPNEqV6M1qHyQr8Gt8GmVTKVM,1775 +jinja2/asyncfilters.py,sha256=XJtYXTxFvcJ5xwk6SaDL4S0oNnT0wPYvXBCSzc482fI,4250 +jinja2/asyncsupport.py,sha256=ZBFsDLuq3Gtji3Ia87lcyuDbqaHZJRdtShZcqwpFnSQ,7209 +jinja2/bccache.py,sha256=3Pmp4jo65M9FQuIxdxoDBbEDFwe4acDMQf77nEJfrHA,12139 +jinja2/compiler.py,sha256=Ta9W1Lit542wItAHXlDcg0sEOsFDMirCdlFPHAurg4o,66284 +jinja2/constants.py,sha256=RR1sTzNzUmKco6aZicw4JpQpJGCuPuqm1h1YmCNUEFY,1458 +jinja2/debug.py,sha256=neR7GIGGjZH3_ILJGVUYy3eLQCCaWJMXOb7o0kGInWc,8529 +jinja2/defaults.py,sha256=85B6YUUCyWPSdrSeVhcqFVuu_bHUAQXeey--FIwSeVQ,1126 +jinja2/environment.py,sha256=XDSLKc4SqNLMOwTSq3TbWEyA5WyXfuLuVD0wAVjEFwM,50629 +jinja2/exceptions.py,sha256=VjNLawcmf2ODffqVMCQK1cRmvFaUfQWF4u8ouP3QPcE,5425 +jinja2/ext.py,sha256=AtwL5O5enT_L3HR9-oBvhGyUTdGoyaqG_ICtnR_EVd4,26441 +jinja2/filters.py,sha256=_RpPgAlgIj7ExvyDzcHAC3B36cocfWK-1TEketbNeM0,41415 +jinja2/idtracking.py,sha256=J3O4VHsrbf3wzwiBc7Cro26kHb6_5kbULeIOzocchIU,9211 +jinja2/lexer.py,sha256=nUFLRKhhKmmEWkLI65nQePgcQs7qsRdjVYZETMt_v0g,30331 +jinja2/loaders.py,sha256=C-fST_dmFjgWkp0ZuCkrgICAoOsoSIF28wfAFink0oU,17666 +jinja2/meta.py,sha256=QjyYhfNRD3QCXjBJpiPl9KgkEkGXJbAkCUq4-Ur10EQ,4131 +jinja2/nativetypes.py,sha256=Ul__gtVw4xH-0qvUvnCNHedQeNDwmEuyLJztzzSPeRg,2753 +jinja2/nodes.py,sha256=Mk1oJPVgIjnQw9WOqILvcu3rLepcFZ0ahxQm2mbwDwc,31095 +jinja2/optimizer.py,sha256=gQLlMYzvQhluhzmAIFA1tXS0cwgWYOjprN-gTRcHVsc,1457 +jinja2/parser.py,sha256=fcfdqePNTNyvosIvczbytVA332qpsURvYnCGcjDHSkA,35660 +jinja2/runtime.py,sha256=0y-BRyIEZ9ltByL2Id6GpHe1oDRQAwNeQvI0SKobNMw,30618 +jinja2/sandbox.py,sha256=knayyUvXsZ-F0mk15mO2-ehK9gsw04UhB8td-iUOtLc,17127 +jinja2/tests.py,sha256=iO_Y-9Vo60zrVe1lMpSl5sKHqAxe2leZHC08OoZ8K24,4799 +jinja2/utils.py,sha256=OoVMlQe9S2-lWT6jJbTu9tDuDvGNyWUhHDcE51i5_Do,22522 +jinja2/visitor.py,sha256=DUHupl0a4PGp7nxRtZFttUzAi1ccxzqc2hzetPYUz8U,3240 diff --git a/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/WHEEL b/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/WHEEL new file mode 100644 index 0000000..ef99c6c --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.34.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/entry_points.txt b/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/entry_points.txt new file mode 100644 index 0000000..3619483 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[babel.extractors] +jinja2 = jinja2.ext:babel_extract [i18n] + diff --git a/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/top_level.txt b/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/top_level.txt new file mode 100644 index 0000000..7f7afbf --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Jinja2-2.11.2.dist-info/top_level.txt @@ -0,0 +1 @@ +jinja2 diff --git a/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/INSTALLER b/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/LICENSE.md b/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/LICENSE.md new file mode 100644 index 0000000..2652d97 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/LICENSE.md @@ -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/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/METADATA b/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/METADATA new file mode 100644 index 0000000..99396d0 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/METADATA @@ -0,0 +1,57 @@ +Metadata-Version: 2.1 +Name: Markdown +Version: 3.2.2 +Summary: Python implementation of Markdown. +Home-page: https://Python-Markdown.github.io/ +Author: Manfred Stienstra, Yuri takhteyev and Waylan limberg +Author-email: waylan.limberg@icloud.com +Maintainer: Waylan Limberg +Maintainer-email: waylan.limberg@icloud.com +License: BSD License +Download-URL: http://pypi.python.org/packages/source/M/Markdown/Markdown-3.2.2-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 :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: 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 +Requires-Python: >=3.5 +Requires-Dist: importlib-metadata ; python_version < "3.8" +Provides-Extra: testing +Requires-Dist: coverage ; extra == 'testing' +Requires-Dist: pyyaml ; extra == 'testing' + + +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: https://daringfireball.net/projects/markdown/ +.. _Features: https://Python-Markdown.github.io#features +.. _`Available Extensions`: https://Python-Markdown.github.io/extensions/ + +Support +======= + +You may report bugs, ask for help, and discuss various other issues on +the `bug tracker`_. + +.. _`bug tracker`: https://github.com/Python-Markdown/markdown/issues + + diff --git a/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/RECORD b/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/RECORD new file mode 100644 index 0000000..3f8cb13 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/RECORD @@ -0,0 +1,74 @@ +../../../bin/markdown_py,sha256=qKeKc-r7oY5-Fbky1RHMUdhnzJzQ5zSPsiabAys1mnc,273 +Markdown-3.2.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Markdown-3.2.2.dist-info/LICENSE.md,sha256=bxGTy2NHGOZcOlN9biXr1hSCDsDvaTz8EiSBEmONZNo,1645 +Markdown-3.2.2.dist-info/METADATA,sha256=zgAbjRRuBBmQaECWiKBHfwpPd1JjR8qQ3-39oaAHHaI,2409 +Markdown-3.2.2.dist-info/RECORD,, +Markdown-3.2.2.dist-info/WHEEL,sha256=S8S5VL-stOTSZDYxHyf0KP7eds0J72qrK0Evu3TfyAY,92 +Markdown-3.2.2.dist-info/entry_points.txt,sha256=j4jiKg-iwZGImvi8OzotZePWoFbJJ4GrfzDqH03u3SQ,1103 +Markdown-3.2.2.dist-info/top_level.txt,sha256=IAxs8x618RXoH1uCqeLLxXsDefJvE_mIibr_M4sOlyk,9 +markdown/__init__.py,sha256=002-LuHviYzROW2rg_gBGai81nMouUNO9UFj5nSsTSk,2065 +markdown/__main__.py,sha256=MpVK3zlwQ-4AzDzZmIScPB90PpunMGVgS5KBmJuHYTw,5802 +markdown/__meta__.py,sha256=1KPSWlbs623C1QTn8qYSTfbDKrYQFyydYwlRep-5ATk,1630 +markdown/__pycache__/__init__.cpython-37.pyc,, +markdown/__pycache__/__main__.cpython-37.pyc,, +markdown/__pycache__/__meta__.cpython-37.pyc,, +markdown/__pycache__/blockparser.cpython-37.pyc,, +markdown/__pycache__/blockprocessors.cpython-37.pyc,, +markdown/__pycache__/core.cpython-37.pyc,, +markdown/__pycache__/inlinepatterns.cpython-37.pyc,, +markdown/__pycache__/pep562.cpython-37.pyc,, +markdown/__pycache__/postprocessors.cpython-37.pyc,, +markdown/__pycache__/preprocessors.cpython-37.pyc,, +markdown/__pycache__/serializers.cpython-37.pyc,, +markdown/__pycache__/test_tools.cpython-37.pyc,, +markdown/__pycache__/treeprocessors.cpython-37.pyc,, +markdown/__pycache__/util.cpython-37.pyc,, +markdown/blockparser.py,sha256=JpBhOokOoBUGCXolftOc5m1hPcR2y9s9hVd9WSuhHzo,4285 +markdown/blockprocessors.py,sha256=l4gmkAN9b2L340EX0gm24EyWS7UzBviPqX6wYrcgEco,23736 +markdown/core.py,sha256=Hm1VMHvmHJiVMta9y1vFyUx04OWjTP3yRJ-al4Rqz1U,15278 +markdown/extensions/__init__.py,sha256=nw2VtafIf5zHjAcUuykQbaNY6taOmNn7ARn11-Pe080,3661 +markdown/extensions/__pycache__/__init__.cpython-37.pyc,, +markdown/extensions/__pycache__/abbr.cpython-37.pyc,, +markdown/extensions/__pycache__/admonition.cpython-37.pyc,, +markdown/extensions/__pycache__/attr_list.cpython-37.pyc,, +markdown/extensions/__pycache__/codehilite.cpython-37.pyc,, +markdown/extensions/__pycache__/def_list.cpython-37.pyc,, +markdown/extensions/__pycache__/extra.cpython-37.pyc,, +markdown/extensions/__pycache__/fenced_code.cpython-37.pyc,, +markdown/extensions/__pycache__/footnotes.cpython-37.pyc,, +markdown/extensions/__pycache__/legacy_attrs.cpython-37.pyc,, +markdown/extensions/__pycache__/legacy_em.cpython-37.pyc,, +markdown/extensions/__pycache__/md_in_html.cpython-37.pyc,, +markdown/extensions/__pycache__/meta.cpython-37.pyc,, +markdown/extensions/__pycache__/nl2br.cpython-37.pyc,, +markdown/extensions/__pycache__/sane_lists.cpython-37.pyc,, +markdown/extensions/__pycache__/smarty.cpython-37.pyc,, +markdown/extensions/__pycache__/tables.cpython-37.pyc,, +markdown/extensions/__pycache__/toc.cpython-37.pyc,, +markdown/extensions/__pycache__/wikilinks.cpython-37.pyc,, +markdown/extensions/abbr.py,sha256=pqp2HnOR2giT-iYKyqtsp2_eUOWBR0j_hUfjvUV5c88,2916 +markdown/extensions/admonition.py,sha256=HWHHjuYZPAPOg5X8hbpDuSbw8gB6k0odw8GuTT1v_N4,3124 +markdown/extensions/attr_list.py,sha256=m9a1H-S33rV2twtlFYuoxSiCAf22ndU5tziSzNF2dNg,6003 +markdown/extensions/codehilite.py,sha256=ChzzzrRIQZQwhQ7dLEIXDKTnemZzmEJCoNFcw6Sp5gk,9838 +markdown/extensions/def_list.py,sha256=iqRXAEl2XnyF415afCxihAgOmEUOK1hIuBPIK1k7Tzo,3521 +markdown/extensions/extra.py,sha256=udRN8OvSWcq3UwkPygvsFl1RlCVtCJ-ARVg2IwVH6VY,1831 +markdown/extensions/fenced_code.py,sha256=dww9rDu2kQtkoTpjn9BBgeGCTNdE1bMPJ2wgR6695iM,3897 +markdown/extensions/footnotes.py,sha256=a9sb8RoKqFU8p8ZhpTObrn_Uek0hbyPFVGYpRaEDXaw,15339 +markdown/extensions/legacy_attrs.py,sha256=2EaVQkxQoNnP8_lMPvGRBdNda8L4weUQroiyEuVdS-w,2547 +markdown/extensions/legacy_em.py,sha256=9ZMGCTrFh01eiOpnFjS0jVkqgYXiTzCGn-eNvYcvObg,1579 +markdown/extensions/md_in_html.py,sha256=ohSiGcgR5yBqusuTs0opbTO_5fq442fqPK-klFd_qaM,4040 +markdown/extensions/meta.py,sha256=EUfkzM7l7UpH__Or9K3pl8ldVddwndlCZWA3d712RAE,2331 +markdown/extensions/nl2br.py,sha256=wAqTNOuf2L1NzlEvEqoID70n9y-aiYaGLkuyQk3CD0w,783 +markdown/extensions/sane_lists.py,sha256=ZQmCf-247KBexVG0fc62nDvokGkV6W1uavYbieNKSG4,1505 +markdown/extensions/smarty.py,sha256=0padzkVCNACainKw-Xj1S5UfT0125VCTfNejmrCZItA,10238 +markdown/extensions/tables.py,sha256=bicFx_wqhnEx6Y_8MJqA56rh71pt5fOe94oiWbvcobY,7685 +markdown/extensions/toc.py,sha256=C45ZZxlp9uDTE1WW9EvLTKT24H2SY0HM3nUDfGlzgng,13477 +markdown/extensions/wikilinks.py,sha256=GkgT9BY7b1-qW--dIwFAhC9V20RoeF13b7CFdw_V21Q,2812 +markdown/inlinepatterns.py,sha256=EnYq9aU_Hi1gu5e8dcbUxUu0mRz-pHFV79uGQCYbD5I,29378 +markdown/pep562.py,sha256=5UkqT7sb-cQufgbOl_jF-RYUVVHS7VThzlMzR9vrd3I,8917 +markdown/postprocessors.py,sha256=25g6qqpJ4kuiq4RBrGz8RA6GMb7ArUi1AN2VDVnR35U,3738 +markdown/preprocessors.py,sha256=dsmMVPP2afKAZ0s59_mFidM_mCiNfgdBJ9aVDWu_viE,15323 +markdown/serializers.py,sha256=_wQl-iJrPSUEQ4Q1owWYqN9qceVh6TOlAOH_i44BKAQ,6540 +markdown/test_tools.py,sha256=qwF7x7QZa9Wa509Y4KJfmc1O3udrtXgH1AXj33TpDso,6823 +markdown/treeprocessors.py,sha256=NBaYc9TEGP7TBaN6YRROIqE5Lj-AMoAqp0jN-coGW3Q,15401 +markdown/util.py,sha256=3-F0KcHEYOIOayAt8FIeoUxNfLlfMeiSF3EE3dWzxg8,15499 diff --git a/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/WHEEL b/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/WHEEL new file mode 100644 index 0000000..c57a597 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.4) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/entry_points.txt b/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/entry_points.txt new file mode 100644 index 0000000..f49693d --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/entry_points.txt @@ -0,0 +1,23 @@ +[console_scripts] +markdown_py = markdown.__main__:run + +[markdown.extensions] +abbr = markdown.extensions.abbr:AbbrExtension +admonition = markdown.extensions.admonition:AdmonitionExtension +attr_list = markdown.extensions.attr_list:AttrListExtension +codehilite = markdown.extensions.codehilite:CodeHiliteExtension +def_list = markdown.extensions.def_list:DefListExtension +extra = markdown.extensions.extra:ExtraExtension +fenced_code = markdown.extensions.fenced_code:FencedCodeExtension +footnotes = markdown.extensions.footnotes:FootnoteExtension +legacy_attrs = markdown.extensions.legacy_attrs:LegacyAttrExtension +legacy_em = markdown.extensions.legacy_em:LegacyEmExtension +md_in_html = markdown.extensions.md_in_html:MarkdownInHtmlExtension +meta = markdown.extensions.meta:MetaExtension +nl2br = markdown.extensions.nl2br:Nl2BrExtension +sane_lists = markdown.extensions.sane_lists:SaneListExtension +smarty = markdown.extensions.smarty:SmartyExtension +tables = markdown.extensions.tables:TableExtension +toc = markdown.extensions.toc:TocExtension +wikilinks = markdown.extensions.wikilinks:WikiLinkExtension + diff --git a/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/top_level.txt b/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/top_level.txt new file mode 100644 index 0000000..0918c97 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Markdown-3.2.2.dist-info/top_level.txt @@ -0,0 +1 @@ +markdown diff --git a/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/INSTALLER b/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/LICENSE.txt b/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/LICENSE.txt new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/METADATA b/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/METADATA new file mode 100644 index 0000000..b208d93 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/METADATA @@ -0,0 +1,103 @@ +Metadata-Version: 2.1 +Name: MarkupSafe +Version: 1.1.1 +Summary: Safely add untrusted strings to HTML/XML markup. +Home-page: https://palletsprojects.com/p/markupsafe/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: The Pallets Team +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Documentation, https://markupsafe.palletsprojects.com/ +Project-URL: Code, https://github.com/pallets/markupsafe +Project-URL: Issue tracker, https://github.com/pallets/markupsafe/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* + +MarkupSafe +========== + +MarkupSafe implements a text object that escapes characters so it is +safe to use in HTML and XML. Characters that have special meanings are +replaced so that they display as the actual characters. This mitigates +injection attacks, meaning untrusted user input can safely be displayed +on a page. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U MarkupSafe + +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + +Examples +-------- + +.. code-block:: pycon + + >>> from markupsafe import Markup, escape + >>> # escape replaces special characters and wraps in Markup + >>> escape('') + Markup(u'<script>alert(document.cookie);</script>') + >>> # wrap in Markup to mark text "safe" and prevent escaping + >>> Markup('Hello') + Markup('hello') + >>> escape(Markup('Hello')) + Markup('hello') + >>> # Markup is a text subclass (str on Python 3, unicode on Python 2) + >>> # methods and operators escape their arguments + >>> template = Markup("Hello %s") + >>> template % '"World"' + Markup('Hello "World"') + + +Donate +------ + +The Pallets organization develops and supports MarkupSafe and other +libraries that use it. In order to grow the community of contributors +and users, and allow the maintainers to devote more time to the +projects, `please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +* Website: https://palletsprojects.com/p/markupsafe/ +* Documentation: https://markupsafe.palletsprojects.com/ +* License: `BSD-3-Clause `_ +* Releases: https://pypi.org/project/MarkupSafe/ +* Code: https://github.com/pallets/markupsafe +* Issue tracker: https://github.com/pallets/markupsafe/issues +* Test status: + + * Linux, Mac: https://travis-ci.org/pallets/markupsafe + * Windows: https://ci.appveyor.com/project/pallets/markupsafe + +* Test coverage: https://codecov.io/gh/pallets/markupsafe + + diff --git a/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/RECORD b/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/RECORD new file mode 100644 index 0000000..207f391 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/RECORD @@ -0,0 +1,16 @@ +MarkupSafe-1.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +MarkupSafe-1.1.1.dist-info/LICENSE.txt,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +MarkupSafe-1.1.1.dist-info/METADATA,sha256=nJHwJ4_4ka-V39QH883jPrslj6inNdyyNASBXbYgHXQ,3570 +MarkupSafe-1.1.1.dist-info/RECORD,, +MarkupSafe-1.1.1.dist-info/WHEEL,sha256=AhV6RMqZ2IDfreRJKo44QWYxYeP-0Jr0bezzBLQ1eog,109 +MarkupSafe-1.1.1.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +markupsafe/__init__.py,sha256=oTblO5f9KFM-pvnq9bB0HgElnqkJyqHnFN1Nx2NIvnY,10126 +markupsafe/__pycache__/__init__.cpython-37.pyc,, +markupsafe/__pycache__/_compat.cpython-37.pyc,, +markupsafe/__pycache__/_constants.cpython-37.pyc,, +markupsafe/__pycache__/_native.cpython-37.pyc,, +markupsafe/_compat.py,sha256=uEW1ybxEjfxIiuTbRRaJpHsPFf4yQUMMKaPgYEC5XbU,558 +markupsafe/_constants.py,sha256=zo2ajfScG-l1Sb_52EP3MlDCqO7Y1BVHUXXKRsVDRNk,4690 +markupsafe/_native.py,sha256=d-8S_zzYt2y512xYcuSxq0NeG2DUUvG80wVdTn-4KI8,1873 +markupsafe/_speedups.c,sha256=k0fzEIK3CP6MmMqeY0ob43TP90mVN0DTyn7BAl3RqSg,9884 +markupsafe/_speedups.cpython-37m-x86_64-linux-gnu.so,sha256=pz-ucGdAq6kJtq9lEY1kY2Ed6LQjbRrIicdu_i4HFqU,38875 diff --git a/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/WHEEL b/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/WHEEL new file mode 100644 index 0000000..697e432 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.31.1) +Root-Is-Purelib: false +Tag: cp37-cp37m-manylinux1_x86_64 + diff --git a/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/top_level.txt b/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/top_level.txt new file mode 100644 index 0000000..75bf729 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/MarkupSafe-1.1.1.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/AUTHORS b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/AUTHORS new file mode 100644 index 0000000..3dc2234 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/AUTHORS @@ -0,0 +1,231 @@ +Pygments is written and maintained by Georg Brandl . + +Major developers are Tim Hatch and Armin Ronacher +. + +Other contributors, listed alphabetically, are: + +* Sam Aaron -- Ioke lexer +* Ali Afshar -- image formatter +* Thomas Aglassinger -- Easytrieve, JCL, Rexx, Transact-SQL and VBScript + lexers +* Muthiah Annamalai -- Ezhil lexer +* Kumar Appaiah -- Debian control lexer +* Andreas Amann -- AppleScript lexer +* Timothy Armstrong -- Dart lexer fixes +* Jeffrey Arnold -- R/S, Rd, BUGS, Jags, and Stan lexers +* Jeremy Ashkenas -- CoffeeScript lexer +* José Joaquín Atria -- Praat lexer +* Stefan Matthias Aust -- Smalltalk lexer +* Lucas Bajolet -- Nit lexer +* Ben Bangert -- Mako lexers +* Max Battcher -- Darcs patch lexer +* Thomas Baruchel -- APL lexer +* Tim Baumann -- (Literate) Agda lexer +* Paul Baumgart, 280 North, Inc. -- Objective-J lexer +* Michael Bayer -- Myghty lexers +* Thomas Beale -- Archetype lexers +* John Benediktsson -- Factor lexer +* Trevor Bergeron -- mIRC formatter +* Vincent Bernat -- LessCSS lexer +* Christopher Bertels -- Fancy lexer +* Sébastien Bigaret -- QVT Operational lexer +* Jarrett Billingsley -- MiniD lexer +* Adam Blinkinsop -- Haskell, Redcode lexers +* Stéphane Blondon -- SGF and Sieve lexers +* Frits van Bommel -- assembler lexers +* Pierre Bourdon -- bugfixes +* Martijn Braam -- Kernel log lexer +* Matthias Bussonnier -- ANSI style handling for terminal-256 formatter +* chebee7i -- Python traceback lexer improvements +* Hiram Chirino -- Scaml and Jade lexers +* Mauricio Caceres -- SAS and Stata lexers. +* Ian Cooper -- VGL lexer +* David Corbett -- Inform, Jasmin, JSGF, Snowball, and TADS 3 lexers +* Leaf Corcoran -- MoonScript lexer +* Christopher Creutzig -- MuPAD lexer +* Daniël W. Crompton -- Pike lexer +* Pete Curry -- bugfixes +* Bryan Davis -- EBNF lexer +* Bruno Deferrari -- Shen lexer +* Giedrius Dubinskas -- HTML formatter improvements +* Owen Durni -- Haxe lexer +* Alexander Dutton, Oxford University Computing Services -- SPARQL lexer +* James Edwards -- Terraform lexer +* Nick Efford -- Python 3 lexer +* Sven Efftinge -- Xtend lexer +* Artem Egorkine -- terminal256 formatter +* Matthew Fernandez -- CAmkES lexer +* Michael Ficarra -- CPSA lexer +* James H. Fisher -- PostScript lexer +* William S. Fulton -- SWIG lexer +* Carlos Galdino -- Elixir and Elixir Console lexers +* Michael Galloy -- IDL lexer +* Naveen Garg -- Autohotkey lexer +* Simon Garnotel -- FreeFem++ lexer +* Laurent Gautier -- R/S lexer +* Alex Gaynor -- PyPy log lexer +* Richard Gerkin -- Igor Pro lexer +* Alain Gilbert -- TypeScript lexer +* Alex Gilding -- BlitzBasic lexer +* GitHub, Inc -- DASM16, Augeas, TOML, and Slash lexers +* Bertrand Goetzmann -- Groovy lexer +* Krzysiek Goj -- Scala lexer +* Rostyslav Golda -- FloScript lexer +* Andrey Golovizin -- BibTeX lexers +* Matt Good -- Genshi, Cheetah lexers +* Michał Górny -- vim modeline support +* Alex Gosse -- TrafficScript lexer +* Patrick Gotthardt -- PHP namespaces support +* Olivier Guibe -- Asymptote lexer +* Phil Hagelberg -- Fennel lexer +* Florian Hahn -- Boogie lexer +* Martin Harriman -- SNOBOL lexer +* Matthew Harrison -- SVG formatter +* Steven Hazel -- Tcl lexer +* Dan Michael Heggø -- Turtle lexer +* Aslak Hellesøy -- Gherkin lexer +* Greg Hendershott -- Racket lexer +* Justin Hendrick -- ParaSail lexer +* Jordi Gutiérrez Hermoso -- Octave lexer +* David Hess, Fish Software, Inc. -- Objective-J lexer +* Varun Hiremath -- Debian control lexer +* Rob Hoelz -- Perl 6 lexer +* Doug Hogan -- Mscgen lexer +* Ben Hollis -- Mason lexer +* Max Horn -- GAP lexer +* Alastair Houghton -- Lexer inheritance facility +* Tim Howard -- BlitzMax lexer +* Dustin Howett -- Logos lexer +* Ivan Inozemtsev -- Fantom lexer +* Hiroaki Itoh -- Shell console rewrite, Lexers for PowerShell session, + MSDOS session, BC, WDiff +* Brian R. Jackson -- Tea lexer +* Christian Jann -- ShellSession lexer +* Dennis Kaarsemaker -- sources.list lexer +* Dmitri Kabak -- Inferno Limbo lexer +* Igor Kalnitsky -- vhdl lexer +* Colin Kennedy - USD lexer +* Alexander Kit -- MaskJS lexer +* Pekka Klärck -- Robot Framework lexer +* Gerwin Klein -- Isabelle lexer +* Eric Knibbe -- Lasso lexer +* Stepan Koltsov -- Clay lexer +* Adam Koprowski -- Opa lexer +* Benjamin Kowarsch -- Modula-2 lexer +* Domen Kožar -- Nix lexer +* Oleh Krekel -- Emacs Lisp lexer +* Alexander Kriegisch -- Kconfig and AspectJ lexers +* Marek Kubica -- Scheme lexer +* Jochen Kupperschmidt -- Markdown processor +* Gerd Kurzbach -- Modelica lexer +* Jon Larimer, Google Inc. -- Smali lexer +* Olov Lassus -- Dart lexer +* Matt Layman -- TAP lexer +* Kristian Lyngstøl -- Varnish lexers +* Sylvestre Ledru -- Scilab lexer +* Chee Sing Lee -- Flatline lexer +* Mark Lee -- Vala lexer +* Valentin Lorentz -- C++ lexer improvements +* Ben Mabey -- Gherkin lexer +* Angus MacArthur -- QML lexer +* Louis Mandel -- X10 lexer +* Louis Marchand -- Eiffel lexer +* Simone Margaritelli -- Hybris lexer +* Kirk McDonald -- D lexer +* Gordon McGregor -- SystemVerilog lexer +* Stephen McKamey -- Duel/JBST lexer +* Brian McKenna -- F# lexer +* Charles McLaughlin -- Puppet lexer +* Kurt McKee -- Tera Term macro lexer +* Lukas Meuser -- BBCode formatter, Lua lexer +* Cat Miller -- Pig lexer +* Paul Miller -- LiveScript lexer +* Hong Minhee -- HTTP lexer +* Michael Mior -- Awk lexer +* Bruce Mitchener -- Dylan lexer rewrite +* Reuben Morais -- SourcePawn lexer +* Jon Morton -- Rust lexer +* Paulo Moura -- Logtalk lexer +* Mher Movsisyan -- DTD lexer +* Dejan Muhamedagic -- Crmsh lexer +* Ana Nelson -- Ragel, ANTLR, R console lexers +* Kurt Neufeld -- Markdown lexer +* Nam T. Nguyen -- Monokai style +* Jesper Noehr -- HTML formatter "anchorlinenos" +* Mike Nolta -- Julia lexer +* Jonas Obrist -- BBCode lexer +* Edward O'Callaghan -- Cryptol lexer +* David Oliva -- Rebol lexer +* Pat Pannuto -- nesC lexer +* Jon Parise -- Protocol buffers and Thrift lexers +* Benjamin Peterson -- Test suite refactoring +* Ronny Pfannschmidt -- BBCode lexer +* Dominik Picheta -- Nimrod lexer +* Andrew Pinkham -- RTF Formatter Refactoring +* Clément Prévost -- UrbiScript lexer +* Tanner Prynn -- cmdline -x option and loading lexers from files +* Oleh Prypin -- Crystal lexer (based on Ruby lexer) +* Xidorn Quan -- Web IDL lexer +* Elias Rabel -- Fortran fixed form lexer +* raichoo -- Idris lexer +* Kashif Rasul -- CUDA lexer +* Nathan Reed -- HLSL lexer +* Justin Reidy -- MXML lexer +* Norman Richards -- JSON lexer +* Corey Richardson -- Rust lexer updates +* Lubomir Rintel -- GoodData MAQL and CL lexers +* Andre Roberge -- Tango style +* Georg Rollinger -- HSAIL lexer +* Michiel Roos -- TypoScript lexer +* Konrad Rudolph -- LaTeX formatter enhancements +* Mario Ruggier -- Evoque lexers +* Miikka Salminen -- Lovelace style, Hexdump lexer, lexer enhancements +* Stou Sandalski -- NumPy, FORTRAN, tcsh and XSLT lexers +* Matteo Sasso -- Common Lisp lexer +* Joe Schafer -- Ada lexer +* Ken Schutte -- Matlab lexers +* René Schwaiger -- Rainbow Dash style +* Sebastian Schweizer -- Whiley lexer +* Tassilo Schweyer -- Io, MOOCode lexers +* Ted Shaw -- AutoIt lexer +* Joerg Sieker -- ABAP lexer +* Robert Simmons -- Standard ML lexer +* Kirill Simonov -- YAML lexer +* Corbin Simpson -- Monte lexer +* Alexander Smishlajev -- Visual FoxPro lexer +* Steve Spigarelli -- XQuery lexer +* Jerome St-Louis -- eC lexer +* Camil Staps -- Clean and NuSMV lexers; Solarized style +* James Strachan -- Kotlin lexer +* Tom Stuart -- Treetop lexer +* Colin Sullivan -- SuperCollider lexer +* Ben Swift -- Extempore lexer +* Edoardo Tenani -- Arduino lexer +* Tiberius Teng -- default style overhaul +* Jeremy Thurgood -- Erlang, Squid config lexers +* Brian Tiffin -- OpenCOBOL lexer +* Bob Tolbert -- Hy lexer +* Matthias Trute -- Forth lexer +* Erick Tryzelaar -- Felix lexer +* Alexander Udalov -- Kotlin lexer improvements +* Thomas Van Doren -- Chapel lexer +* Daniele Varrazzo -- PostgreSQL lexers +* Abe Voelker -- OpenEdge ABL lexer +* Pepijn de Vos -- HTML formatter CTags support +* Matthias Vallentin -- Bro lexer +* Benoît Vinot -- AMPL lexer +* Linh Vu Hong -- RSL lexer +* Nathan Weizenbaum -- Haml and Sass lexers +* Nathan Whetsell -- Csound lexers +* Dietmar Winkler -- Modelica lexer +* Nils Winter -- Smalltalk lexer +* Davy Wybiral -- Clojure lexer +* Whitney Young -- ObjectiveC lexer +* Diego Zamboni -- CFengine3 lexer +* Enrique Zamudio -- Ceylon lexer +* Alex Zimin -- Nemerle lexer +* Rob Zimmerman -- Kal lexer +* Vincent Zurczak -- Roboconf lexer + +Many thanks for all contributions! diff --git a/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/INSTALLER b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/LICENSE b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/LICENSE new file mode 100644 index 0000000..13d1c74 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2006-2019 by the respective authors (see AUTHORS file). +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +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/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/METADATA b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/METADATA new file mode 100644 index 0000000..ff19f5d --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/METADATA @@ -0,0 +1,48 @@ +Metadata-Version: 2.1 +Name: Pygments +Version: 2.6.1 +Summary: Pygments is a syntax highlighting package written in Python. +Home-page: https://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 :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Operating System :: OS Independent +Classifier: Topic :: Text Processing :: Filters +Classifier: Topic :: Utilities +Requires-Python: >=3.5 + + +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 500 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-2019 by the Pygments team, see AUTHORS. +:license: BSD, see LICENSE for details. + + diff --git a/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/RECORD b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/RECORD new file mode 100644 index 0000000..f0ac73a --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/RECORD @@ -0,0 +1,463 @@ +../../../bin/pygmentize,sha256=6RvQXO-s7wr966AOCrbnxrTCy8omBpXvLVMxqyuZaOk,274 +Pygments-2.6.1.dist-info/AUTHORS,sha256=PVpa2_Oku6BGuiUvutvuPnWGpzxqFy2I8-NIrqCvqUY,8449 +Pygments-2.6.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Pygments-2.6.1.dist-info/LICENSE,sha256=RbiNNEnDeAZZR1i_PEhNnZixKx7MFj9lQx_gf-pgJfA,1331 +Pygments-2.6.1.dist-info/METADATA,sha256=nrRK1IyAspMOw3-uN_SPDbJo1XIALm7ZbRV9aui-FtM,1833 +Pygments-2.6.1.dist-info/RECORD,, +Pygments-2.6.1.dist-info/WHEEL,sha256=p46_5Uhzqz6AzeSosiOnxK-zmFja1i22CrQCjmYe8ec,92 +Pygments-2.6.1.dist-info/entry_points.txt,sha256=NXt9BRDRv6tAfDwqKM0bDHrrxaIt2f1nxH9CwjyjSKc,54 +Pygments-2.6.1.dist-info/top_level.txt,sha256=RjKKqrVIStoebLHdbs0yZ2Lk4rS7cxGguXsLCYvZ2Ak,9 +pygments/__init__.py,sha256=Hmd0jgKuzYTHVCXGJrUr5E7R0tAINEf8WuhKUmC7ITY,3036 +pygments/__main__.py,sha256=JV6RSKzbYgMQHLf0nZGzfq1IXxns2iGunsfkY3jxFKo,372 +pygments/__pycache__/__init__.cpython-37.pyc,, +pygments/__pycache__/__main__.cpython-37.pyc,, +pygments/__pycache__/cmdline.cpython-37.pyc,, +pygments/__pycache__/console.cpython-37.pyc,, +pygments/__pycache__/filter.cpython-37.pyc,, +pygments/__pycache__/formatter.cpython-37.pyc,, +pygments/__pycache__/lexer.cpython-37.pyc,, +pygments/__pycache__/modeline.cpython-37.pyc,, +pygments/__pycache__/plugin.cpython-37.pyc,, +pygments/__pycache__/regexopt.cpython-37.pyc,, +pygments/__pycache__/scanner.cpython-37.pyc,, +pygments/__pycache__/sphinxext.cpython-37.pyc,, +pygments/__pycache__/style.cpython-37.pyc,, +pygments/__pycache__/token.cpython-37.pyc,, +pygments/__pycache__/unistring.cpython-37.pyc,, +pygments/__pycache__/util.cpython-37.pyc,, +pygments/cmdline.py,sha256=-mJqcK1Cic8Z-z-ITdj0yjN9exJdPew8m9BwHvtesJY,19479 +pygments/console.py,sha256=QF0bQHbGeFRSetc3g5JsmGziVHQqIZCprEwNlZFtiRg,1721 +pygments/filter.py,sha256=hu4Qo6zdyMcIprEL3xmZGb-inVe1_vUKvgY9vdAV5JU,2030 +pygments/filters/__init__.py,sha256=L_K0aapWqkqDPBkMVGoXvp17zsv7ddl0bNQdMsK43tg,11534 +pygments/filters/__pycache__/__init__.cpython-37.pyc,, +pygments/formatter.py,sha256=Zyz1t_dRczxwuuQkgkwOIOd2TRZpHMbjVHOL_ch37JQ,2917 +pygments/formatters/__init__.py,sha256=d8AnTX9J39ZKoh2YJIiFcOk79h28T0cJ7Yn6rs4G3UI,5107 +pygments/formatters/__pycache__/__init__.cpython-37.pyc,, +pygments/formatters/__pycache__/_mapping.cpython-37.pyc,, +pygments/formatters/__pycache__/bbcode.cpython-37.pyc,, +pygments/formatters/__pycache__/html.cpython-37.pyc,, +pygments/formatters/__pycache__/img.cpython-37.pyc,, +pygments/formatters/__pycache__/irc.cpython-37.pyc,, +pygments/formatters/__pycache__/latex.cpython-37.pyc,, +pygments/formatters/__pycache__/other.cpython-37.pyc,, +pygments/formatters/__pycache__/rtf.cpython-37.pyc,, +pygments/formatters/__pycache__/svg.cpython-37.pyc,, +pygments/formatters/__pycache__/terminal.cpython-37.pyc,, +pygments/formatters/__pycache__/terminal256.cpython-37.pyc,, +pygments/formatters/_mapping.py,sha256=QvLAVzGeldQ6iE8xeGOYtclUBMV0KclGiINcsCaK538,6175 +pygments/formatters/bbcode.py,sha256=_K7UzwyT70snOYAiT3UkItbXRwQYVuTHpr1AZtRHL6Y,3314 +pygments/formatters/html.py,sha256=Eoa4EJxTmE3g0kgMiNN7Ihh5A9lNWUnBlZ1eXFC-yl4,32625 +pygments/formatters/img.py,sha256=iajPfAvg5cB79wS0Mu3pv5Vy2TgghWQcl1OWIz1NcKg,20701 +pygments/formatters/irc.py,sha256=nU9jSjARuRaZpCuCey7bnRwGTGKeCTEhm_yDDYxzKQ8,5869 +pygments/formatters/latex.py,sha256=IOqv1C-LyWs6v2cgecfZ-CkfNNF6VQqcNkPUs3aHUjU,17711 +pygments/formatters/other.py,sha256=Qfc5OixOxM7YEy0d0NJBT750ukj-uPyhxKtHGTm0Vlc,5140 +pygments/formatters/rtf.py,sha256=z8LTTuEXwx3hpLaG0qeJumZCkUfseLIBsxhZE-0tEKg,5050 +pygments/formatters/svg.py,sha256=QUPMQIhXN4JA6auaUTj6z6JaeffyF7OpVoQ8IENptCo,7279 +pygments/formatters/terminal.py,sha256=q0QuanTWnUr4fuNuxnSnjLwjlyUJSMXqBK58MZCAk8Q,4662 +pygments/formatters/terminal256.py,sha256=x9n-YSOwDZhOaLGmYKLO259ZNBCqSydm_KxZJh2Q-Eg,11126 +pygments/lexer.py,sha256=FBy1KBXYiwf1TYtXN25OSrnLGbm9oAWXCsq6ReqtvNA,31559 +pygments/lexers/__init__.py,sha256=3dDjsioYkLz3fRbX3gV9xoi-SkpRRCtYurrWrylAZCo,11310 +pygments/lexers/__pycache__/__init__.cpython-37.pyc,, +pygments/lexers/__pycache__/_asy_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_cl_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_cocoa_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_csound_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_lasso_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_lua_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_mapping.cpython-37.pyc,, +pygments/lexers/__pycache__/_mql_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_openedge_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_php_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_postgres_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_scilab_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_sourcemod_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_stan_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_stata_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_tsql_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_usd_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_vbscript_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/_vim_builtins.cpython-37.pyc,, +pygments/lexers/__pycache__/actionscript.cpython-37.pyc,, +pygments/lexers/__pycache__/agile.cpython-37.pyc,, +pygments/lexers/__pycache__/algebra.cpython-37.pyc,, +pygments/lexers/__pycache__/ambient.cpython-37.pyc,, +pygments/lexers/__pycache__/ampl.cpython-37.pyc,, +pygments/lexers/__pycache__/apl.cpython-37.pyc,, +pygments/lexers/__pycache__/archetype.cpython-37.pyc,, +pygments/lexers/__pycache__/asm.cpython-37.pyc,, +pygments/lexers/__pycache__/automation.cpython-37.pyc,, +pygments/lexers/__pycache__/basic.cpython-37.pyc,, +pygments/lexers/__pycache__/bibtex.cpython-37.pyc,, +pygments/lexers/__pycache__/boa.cpython-37.pyc,, +pygments/lexers/__pycache__/business.cpython-37.pyc,, +pygments/lexers/__pycache__/c_cpp.cpython-37.pyc,, +pygments/lexers/__pycache__/c_like.cpython-37.pyc,, +pygments/lexers/__pycache__/capnproto.cpython-37.pyc,, +pygments/lexers/__pycache__/chapel.cpython-37.pyc,, +pygments/lexers/__pycache__/clean.cpython-37.pyc,, +pygments/lexers/__pycache__/compiled.cpython-37.pyc,, +pygments/lexers/__pycache__/configs.cpython-37.pyc,, +pygments/lexers/__pycache__/console.cpython-37.pyc,, +pygments/lexers/__pycache__/crystal.cpython-37.pyc,, +pygments/lexers/__pycache__/csound.cpython-37.pyc,, +pygments/lexers/__pycache__/css.cpython-37.pyc,, +pygments/lexers/__pycache__/d.cpython-37.pyc,, +pygments/lexers/__pycache__/dalvik.cpython-37.pyc,, +pygments/lexers/__pycache__/data.cpython-37.pyc,, +pygments/lexers/__pycache__/diff.cpython-37.pyc,, +pygments/lexers/__pycache__/dotnet.cpython-37.pyc,, +pygments/lexers/__pycache__/dsls.cpython-37.pyc,, +pygments/lexers/__pycache__/dylan.cpython-37.pyc,, +pygments/lexers/__pycache__/ecl.cpython-37.pyc,, +pygments/lexers/__pycache__/eiffel.cpython-37.pyc,, +pygments/lexers/__pycache__/elm.cpython-37.pyc,, +pygments/lexers/__pycache__/email.cpython-37.pyc,, +pygments/lexers/__pycache__/erlang.cpython-37.pyc,, +pygments/lexers/__pycache__/esoteric.cpython-37.pyc,, +pygments/lexers/__pycache__/ezhil.cpython-37.pyc,, +pygments/lexers/__pycache__/factor.cpython-37.pyc,, +pygments/lexers/__pycache__/fantom.cpython-37.pyc,, +pygments/lexers/__pycache__/felix.cpython-37.pyc,, +pygments/lexers/__pycache__/floscript.cpython-37.pyc,, +pygments/lexers/__pycache__/forth.cpython-37.pyc,, +pygments/lexers/__pycache__/fortran.cpython-37.pyc,, +pygments/lexers/__pycache__/foxpro.cpython-37.pyc,, +pygments/lexers/__pycache__/freefem.cpython-37.pyc,, +pygments/lexers/__pycache__/functional.cpython-37.pyc,, +pygments/lexers/__pycache__/go.cpython-37.pyc,, +pygments/lexers/__pycache__/grammar_notation.cpython-37.pyc,, +pygments/lexers/__pycache__/graph.cpython-37.pyc,, +pygments/lexers/__pycache__/graphics.cpython-37.pyc,, +pygments/lexers/__pycache__/haskell.cpython-37.pyc,, +pygments/lexers/__pycache__/haxe.cpython-37.pyc,, +pygments/lexers/__pycache__/hdl.cpython-37.pyc,, +pygments/lexers/__pycache__/hexdump.cpython-37.pyc,, +pygments/lexers/__pycache__/html.cpython-37.pyc,, +pygments/lexers/__pycache__/idl.cpython-37.pyc,, +pygments/lexers/__pycache__/igor.cpython-37.pyc,, +pygments/lexers/__pycache__/inferno.cpython-37.pyc,, +pygments/lexers/__pycache__/installers.cpython-37.pyc,, +pygments/lexers/__pycache__/int_fiction.cpython-37.pyc,, +pygments/lexers/__pycache__/iolang.cpython-37.pyc,, +pygments/lexers/__pycache__/j.cpython-37.pyc,, +pygments/lexers/__pycache__/javascript.cpython-37.pyc,, +pygments/lexers/__pycache__/julia.cpython-37.pyc,, +pygments/lexers/__pycache__/jvm.cpython-37.pyc,, +pygments/lexers/__pycache__/lisp.cpython-37.pyc,, +pygments/lexers/__pycache__/make.cpython-37.pyc,, +pygments/lexers/__pycache__/markup.cpython-37.pyc,, +pygments/lexers/__pycache__/math.cpython-37.pyc,, +pygments/lexers/__pycache__/matlab.cpython-37.pyc,, +pygments/lexers/__pycache__/mime.cpython-37.pyc,, +pygments/lexers/__pycache__/ml.cpython-37.pyc,, +pygments/lexers/__pycache__/modeling.cpython-37.pyc,, +pygments/lexers/__pycache__/modula2.cpython-37.pyc,, +pygments/lexers/__pycache__/monte.cpython-37.pyc,, +pygments/lexers/__pycache__/mosel.cpython-37.pyc,, +pygments/lexers/__pycache__/ncl.cpython-37.pyc,, +pygments/lexers/__pycache__/nimrod.cpython-37.pyc,, +pygments/lexers/__pycache__/nit.cpython-37.pyc,, +pygments/lexers/__pycache__/nix.cpython-37.pyc,, +pygments/lexers/__pycache__/oberon.cpython-37.pyc,, +pygments/lexers/__pycache__/objective.cpython-37.pyc,, +pygments/lexers/__pycache__/ooc.cpython-37.pyc,, +pygments/lexers/__pycache__/other.cpython-37.pyc,, +pygments/lexers/__pycache__/parasail.cpython-37.pyc,, +pygments/lexers/__pycache__/parsers.cpython-37.pyc,, +pygments/lexers/__pycache__/pascal.cpython-37.pyc,, +pygments/lexers/__pycache__/pawn.cpython-37.pyc,, +pygments/lexers/__pycache__/perl.cpython-37.pyc,, +pygments/lexers/__pycache__/php.cpython-37.pyc,, +pygments/lexers/__pycache__/pony.cpython-37.pyc,, +pygments/lexers/__pycache__/praat.cpython-37.pyc,, +pygments/lexers/__pycache__/prolog.cpython-37.pyc,, +pygments/lexers/__pycache__/python.cpython-37.pyc,, +pygments/lexers/__pycache__/qvt.cpython-37.pyc,, +pygments/lexers/__pycache__/r.cpython-37.pyc,, +pygments/lexers/__pycache__/rdf.cpython-37.pyc,, +pygments/lexers/__pycache__/rebol.cpython-37.pyc,, +pygments/lexers/__pycache__/resource.cpython-37.pyc,, +pygments/lexers/__pycache__/ride.cpython-37.pyc,, +pygments/lexers/__pycache__/rnc.cpython-37.pyc,, +pygments/lexers/__pycache__/roboconf.cpython-37.pyc,, +pygments/lexers/__pycache__/robotframework.cpython-37.pyc,, +pygments/lexers/__pycache__/ruby.cpython-37.pyc,, +pygments/lexers/__pycache__/rust.cpython-37.pyc,, +pygments/lexers/__pycache__/sas.cpython-37.pyc,, +pygments/lexers/__pycache__/scdoc.cpython-37.pyc,, +pygments/lexers/__pycache__/scripting.cpython-37.pyc,, +pygments/lexers/__pycache__/sgf.cpython-37.pyc,, +pygments/lexers/__pycache__/shell.cpython-37.pyc,, +pygments/lexers/__pycache__/sieve.cpython-37.pyc,, +pygments/lexers/__pycache__/slash.cpython-37.pyc,, +pygments/lexers/__pycache__/smalltalk.cpython-37.pyc,, +pygments/lexers/__pycache__/smv.cpython-37.pyc,, +pygments/lexers/__pycache__/snobol.cpython-37.pyc,, +pygments/lexers/__pycache__/solidity.cpython-37.pyc,, +pygments/lexers/__pycache__/special.cpython-37.pyc,, +pygments/lexers/__pycache__/sql.cpython-37.pyc,, +pygments/lexers/__pycache__/stata.cpython-37.pyc,, +pygments/lexers/__pycache__/supercollider.cpython-37.pyc,, +pygments/lexers/__pycache__/tcl.cpython-37.pyc,, +pygments/lexers/__pycache__/templates.cpython-37.pyc,, +pygments/lexers/__pycache__/teraterm.cpython-37.pyc,, +pygments/lexers/__pycache__/testing.cpython-37.pyc,, +pygments/lexers/__pycache__/text.cpython-37.pyc,, +pygments/lexers/__pycache__/textedit.cpython-37.pyc,, +pygments/lexers/__pycache__/textfmts.cpython-37.pyc,, +pygments/lexers/__pycache__/theorem.cpython-37.pyc,, +pygments/lexers/__pycache__/trafficscript.cpython-37.pyc,, +pygments/lexers/__pycache__/typoscript.cpython-37.pyc,, +pygments/lexers/__pycache__/unicon.cpython-37.pyc,, +pygments/lexers/__pycache__/urbi.cpython-37.pyc,, +pygments/lexers/__pycache__/usd.cpython-37.pyc,, +pygments/lexers/__pycache__/varnish.cpython-37.pyc,, +pygments/lexers/__pycache__/verification.cpython-37.pyc,, +pygments/lexers/__pycache__/web.cpython-37.pyc,, +pygments/lexers/__pycache__/webidl.cpython-37.pyc,, +pygments/lexers/__pycache__/webmisc.cpython-37.pyc,, +pygments/lexers/__pycache__/whiley.cpython-37.pyc,, +pygments/lexers/__pycache__/x10.cpython-37.pyc,, +pygments/lexers/__pycache__/xorg.cpython-37.pyc,, +pygments/lexers/__pycache__/zig.cpython-37.pyc,, +pygments/lexers/_asy_builtins.py,sha256=zO_y8v-bp6kjlIwvbmse79qY8P7qAUhoVObaX9Qy3S8,27311 +pygments/lexers/_cl_builtins.py,sha256=x-mRhM6ukZv0pxYtqCq7SlsezhL8L9fpcCQ-gou0Z9w,14018 +pygments/lexers/_cocoa_builtins.py,sha256=h8CT9tpHtyg7PF1tQsLj0NCoN6o9TzzIOBA5UTadEQs,39962 +pygments/lexers/_csound_builtins.py,sha256=6OzL4rwy_qgAenQ8uN0n2u39NlfoVMSZEOy4tdLNHOE,17619 +pygments/lexers/_lasso_builtins.py,sha256=1jR-3eDhf1CUcPSSEXgbJMymAkQaJqpWIPjYM4rL6Sk,134534 +pygments/lexers/_lua_builtins.py,sha256=VkZNUZW9_lTBBcTFOl8NDepWTBfwha6tkV8BXR1EzaM,8297 +pygments/lexers/_mapping.py,sha256=KQJCLXu0jKEF8g9N8Ysg3giPGOFByjdgiOIEMA1yoqw,58920 +pygments/lexers/_mql_builtins.py,sha256=MS7566jpdiud7gEa_y4iJpHLkqjpo-7Y8WwB9MyMUhY,24737 +pygments/lexers/_openedge_builtins.py,sha256=hCqbIZd_qcBTlLyQGME8mqijUDCIm5P9HtIsv8JCEG8,48362 +pygments/lexers/_php_builtins.py,sha256=6-lirHeDcuo-5U5Yh9D4Fgx0sGmN2gq_dmCzjY3wEQg,154390 +pygments/lexers/_postgres_builtins.py,sha256=OI0j7i72gKoNGJomATjK_P00D7cVT6bpPqeeSB4k0aM,11210 +pygments/lexers/_scilab_builtins.py,sha256=ffgN-8Jj5KLRIwdD9j51zvScUbim2DaYv9WmF8_JCNA,52401 +pygments/lexers/_sourcemod_builtins.py,sha256=BSnleYNoGBZ0IrTJz17r-Z9yz7OHwgJVy1oRvvqXg38,27074 +pygments/lexers/_stan_builtins.py,sha256=BfSr_PiG5QE0-7hUfX4g_jdwugKf1zWtGE2w33FotvA,10481 +pygments/lexers/_stata_builtins.py,sha256=rZ8lopR_vKuDCBeCF9oPf71sHkD6n-tN6T5QpyOVEg4,25228 +pygments/lexers/_tsql_builtins.py,sha256=5qrkZJHk_m1SgTnhCrKp5jXJxexjCaf4GtRkY5_PTEA,15484 +pygments/lexers/_usd_builtins.py,sha256=eiB5M8wMXpllmheEzX3BuId6zXGQWaMcJs92E114P7U,1514 +pygments/lexers/_vbscript_builtins.py,sha256=chotaveFeFC-A6qcRAghQC7fAwrDmV-BKE_TW-hrZwk,4249 +pygments/lexers/_vim_builtins.py,sha256=Il_pjrP0PWUjMLCRPOZPoLgd_3jauvv9SGtBOkzmU2A,57090 +pygments/lexers/actionscript.py,sha256=jQTpfKe0OwRQTknMs132_WhqEDIW7lQbLW0HU5D0cOs,11181 +pygments/lexers/agile.py,sha256=0yI_Bq_-ekqFCiMzkcnJfNQ12iyA4QmPk70RCfl1Xa0,900 +pygments/lexers/algebra.py,sha256=vMjSoC9CgSWUMoaNu7gysQDdAc46t_Y6U4dX2mEzNCc,7201 +pygments/lexers/ambient.py,sha256=1_B2JkmFVgGq-JuEhmrXIu-q5WP2e7Ir5DSpO7qXN9E,2557 +pygments/lexers/ampl.py,sha256=HWeNZxYsNhPuGmW1lgNUxMe5zMtbMQ-xNFoj9oVOvq8,4123 +pygments/lexers/apl.py,sha256=gzIuS7p2Qz-pN5M0i45uvDow_gsNNus5k6zrwe19M9c,3174 +pygments/lexers/archetype.py,sha256=luJBCChBsH6fdJOboz5pVTSNCHh7miLd1xtLnI7TH88,11136 +pygments/lexers/asm.py,sha256=0gX4w_cbEbWWUp0kBla41r0ZMAY8NUxaaeW-faRRiqE,39356 +pygments/lexers/automation.py,sha256=9oR495kiyEbl-ev7PWF4Mw-jvtuSbOkmKJRmOvUzQb8,19640 +pygments/lexers/basic.py,sha256=siXk3fQfTEfJNeSW2sI-rfssoUpyj7drMdMrs5csYrs,27576 +pygments/lexers/bibtex.py,sha256=fxbIyhfV1yrFfd7oyAp-fyss27T0Bfv8VqRdVnLg63Y,4725 +pygments/lexers/boa.py,sha256=OB_W242mpr2vwbhg0MO4BpZcdhjaXuM6ffQ54zn3-ZI,3942 +pygments/lexers/business.py,sha256=onAZDADHM7atSEFKsmODBvPc8GlOTDwPgfsh_hVGXNI,27666 +pygments/lexers/c_cpp.py,sha256=dfPzKNoZeqoprqM4a7pTqWin7scz6VYg5_q9MzbnjH0,10638 +pygments/lexers/c_like.py,sha256=UusXq2S5d0v0CpGsxkmVludmu58WsLZQHWnsb0YwhK4,25080 +pygments/lexers/capnproto.py,sha256=pC3zXFSfYFHEIBq3OqLPGKl71K5HtdWnAEqMz6n8KFY,2194 +pygments/lexers/chapel.py,sha256=qzglX8OKh7aaUgXqAVXFkerNjTMIJKdMbihP_o2VFWk,3908 +pygments/lexers/clean.py,sha256=XG0_2KVyxbRFp-_U5HgT1wN9srL522kOe_9T51HeQmA,6362 +pygments/lexers/compiled.py,sha256=iGwVkCJ-SXoovHegOBSnOG518hHkDudegb9_qS-8vW0,1385 +pygments/lexers/configs.py,sha256=YO1NWPpNENDBN4fr-yBYdBWwIqotSquTeaKfY24a7mk,32127 +pygments/lexers/console.py,sha256=tj_ARAplXlqt2sGb2ycxsOy8xIL4NCAMOd3bZ0Zjojg,4120 +pygments/lexers/crystal.py,sha256=hTz20yWrjuam9JVG9Xxr6I7x50M_sIlfdBs0_gg5hKQ,16845 +pygments/lexers/csound.py,sha256=_OqMoEegHcp0NV7zuiLt6h_aY17adQRlJe1DG-pQP4M,16739 +pygments/lexers/css.py,sha256=EvJYfYeaT-TkDHpqXc4ssz0BoXSnBfWT3Vb9MKCeNQg,31467 +pygments/lexers/d.py,sha256=ZWc365C_vRjee-ATfen2OmHCFW3QMoMNE9JEeTplk94,9686 +pygments/lexers/dalvik.py,sha256=tAoPPa_iRXhWG_MzslSvBE99NlGnkx0WKnwdDQ3XU9o,4420 +pygments/lexers/data.py,sha256=_-dqdUSm7Edmh-zaljABFodHjwMCECW1Y-IlyjnWibM,19072 +pygments/lexers/diff.py,sha256=8jKEVtSA2YKprutpONqFvMKBhK1U_IFdxaScTuRNeU4,4873 +pygments/lexers/dotnet.py,sha256=oGL8kWok74zOLn92iwjOYX6Do9tnk96-YTFlJSdkBaQ,27582 +pygments/lexers/dsls.py,sha256=qv0kHmfQOHkHMnXyFbgYrDjUpsCkuXPLig1hma2zcJ0,35837 +pygments/lexers/dylan.py,sha256=LkWTiLsU561_VQL-PUirryEt7ewbseLRJfN-H1twmiA,10402 +pygments/lexers/ecl.py,sha256=5ivxyk5lzMottCuIxyE7DBvWYJV5KTuaHNRkvOtgM7c,5875 +pygments/lexers/eiffel.py,sha256=He2DwoUqWqMt8_PDzoP3NuBl9AZ9K3_SmpGkIgSzWuI,2482 +pygments/lexers/elm.py,sha256=91CM_h3PPoBLLm2stJqNZi3lgjhZH7NvzNKWdXAe8CA,2997 +pygments/lexers/email.py,sha256=ap9imSi6jbbP7gPBAyc3rcNurVDSNmRKIWv0ByR6VOQ,5207 +pygments/lexers/erlang.py,sha256=bZBqAKFa-EsRpvai0EpwZkKMPCd2q6pDfZapq9gh9Qg,18985 +pygments/lexers/esoteric.py,sha256=I7YEPnQdftxEOasCec8_dxVr7zgypMtoYtds0v2srNQ,9489 +pygments/lexers/ezhil.py,sha256=R26b4iXSpdMkgXewJN2INhJXL0ICXhW2o9fu3bn078U,3020 +pygments/lexers/factor.py,sha256=nBYhJoNLkSxtshGrF08tSQKUq_TtgVp1ukKX4Zromm8,17864 +pygments/lexers/fantom.py,sha256=3OTJDka8qeNRykM1Ki1Lyek6gd-jqOa-l5IgRbX8kSg,9982 +pygments/lexers/felix.py,sha256=DoSGdEntZgG3JUbeBA9fqUtg3lODbqwY3_XS6EIfXt4,9408 +pygments/lexers/floscript.py,sha256=eza4Rw3RI3mFjIIAA2czmi2SlgbcSI1T8pNr7vUd0eY,2667 +pygments/lexers/forth.py,sha256=Yqm9z-PjymjQjaleCW-SNJdCCc_NWeFXMz_XvjtAStI,7179 +pygments/lexers/fortran.py,sha256=XqwbZg25atjNDN8yUnqkxm1nfqbzSgZDqmKUIFNQSHk,9841 +pygments/lexers/foxpro.py,sha256=i1B6wX4U5oY8FJO5BGtTR0RaVWbO6P45PXxndi5HcpE,26236 +pygments/lexers/freefem.py,sha256=bYEPIZ1mysE2Ub9WO8NPHefz-CaGqPiE0WbHZeMHPsQ,27086 +pygments/lexers/functional.py,sha256=gJqzgp1ujTa8Zk5hjzXdutz8vvSJpRxhqTVCkK03Ij0,698 +pygments/lexers/go.py,sha256=aRdc0lsKbF7xxTcUnu35m-_e3SD7s2eBAllq1y7_qY8,3701 +pygments/lexers/grammar_notation.py,sha256=0INMOPjLnMohU0QCUIvBaJdty7A7i1hS4ZItB4ehPnA,7941 +pygments/lexers/graph.py,sha256=v013Gzn_RIuLrEz_DJuUah_vCpv6aVSMZpHGov19BMY,2756 +pygments/lexers/graphics.py,sha256=xfr7jZ_JF81kh-RFxIFSKOa06W4z0YxWzOxXAmrLwMA,38259 +pygments/lexers/haskell.py,sha256=VXzPclm0SiawKT2E4L4ZO8uPKcNYMVDWKY4NPcLKFsg,32245 +pygments/lexers/haxe.py,sha256=uWeORmR1BBCtA_HKRJIhzl26GfkzxzVd7c8or-REw7s,30959 +pygments/lexers/hdl.py,sha256=Xpf_1SJ-Uwf94J6MK_C5wR7JyXQkDKtlNdJ7MLL6uzs,18179 +pygments/lexers/hexdump.py,sha256=7y6XhpOGaVfbtWPSzFxgen8u4sr9sWCbnRUTmvnW1KI,3507 +pygments/lexers/html.py,sha256=B-XSH62dR1GZSJ6E3rDOoF6WO-FcKAnrCqTYvvm8jow,19280 +pygments/lexers/idl.py,sha256=hg7CnizaVt7br6ydWkt4VU9UMNax7gg4ToA3_rnqM1M,14986 +pygments/lexers/igor.py,sha256=FP_3Uz06p1emRB1BqpJ_11KY5k38D5nBLP9nFLnXsHA,30917 +pygments/lexers/inferno.py,sha256=iB07whrTd_qnsABOUalv999QhFYB2nhIHfTp_ECsTxM,3117 +pygments/lexers/installers.py,sha256=QVPOqFLmDydPhBJYmQcyjq6XQvcPb1Hxhpbv5JvgL-M,12866 +pygments/lexers/int_fiction.py,sha256=-jBktm0onIUz_hzsP0lUd3g9aLXJ4KLls0gjIwSB46o,55779 +pygments/lexers/iolang.py,sha256=Sv9qzhNgvVz1xmStZOLm3KTvlcI2A1zywAWQTo6ahs0,1905 +pygments/lexers/j.py,sha256=2wqBgvkxF99yBTdyslEsaeweZuqNO_yNZPjTKRwNTdo,4527 +pygments/lexers/javascript.py,sha256=cOcijZB6rFr1aclYt94LHInEKs1KgZZ4Xg4i2zDvW28,60194 +pygments/lexers/julia.py,sha256=ObRU-RjNe_N6zcQZgq5nws526X_j_4c4KPUFwwROFns,14179 +pygments/lexers/jvm.py,sha256=Qsg2PugXHCD55g_w4GVI4FDFCfOBICYW70xKhWMfNiQ,70347 +pygments/lexers/lisp.py,sha256=oUWEXl8czd_ovmKgkROzATeDjy01jPXAne18zXtEYRY,143609 +pygments/lexers/make.py,sha256=dbnhkZWxESvkvV69TrQEZYdo4yiUGoBBIE-VpXX1uBM,7326 +pygments/lexers/markup.py,sha256=6ACdRUnjI6CGRwes8szHfUjZU-nR7C42y2dbP5EdJeI,20704 +pygments/lexers/math.py,sha256=74YS-Z0zpBP6JYk1fsauYbW7XeZ-XPDTqKakbkX0v1Y,700 +pygments/lexers/matlab.py,sha256=23FUS7UgeE9c0gPr9xnyIBz_0Qr7f8ks8DCumF0fGdU,30403 +pygments/lexers/mime.py,sha256=hf-dShZ8AUSIzTELUEnlel7gnZLZpiOd-OFehEDSba0,7975 +pygments/lexers/ml.py,sha256=SV44RnHSqsCQX7wZHZe_bJtzl0hTFrlY5UF8nhO9ozU,31376 +pygments/lexers/modeling.py,sha256=n4gorBPf3gttlsITHGYeOnrUjUWz3nCh5oLYkDMOnrM,13409 +pygments/lexers/modula2.py,sha256=zenAwJk17hVa1FnOTZHJAwLrDrmcurxu4yw7pUoa_Qk,52561 +pygments/lexers/monte.py,sha256=tIn0lsLdG0iHRX_01KI9OkR4iazyiV5F8H3OlkKdFZQ,6307 +pygments/lexers/mosel.py,sha256=N8J6mCnzTUd4KADnhMAAQ2X5OZGxXI-i2Xvq8HfzjNA,9211 +pygments/lexers/ncl.py,sha256=0U8xDdO0guIlnQKCHKmKQPXv91Jqy1YvrkNoMonaYp4,63986 +pygments/lexers/nimrod.py,sha256=ERUF4NVMUlbirF_FvN8EIXXFRv6RJqchq4rr9vugHPI,5174 +pygments/lexers/nit.py,sha256=FSQCdLNjKUrw_pisiCH-m15EQcz30lv6wvvbTgkrB-Y,2743 +pygments/lexers/nix.py,sha256=RTgXFxL2niA9iG1zLHRWdNZy70he_vE1D0-FcoU1cfw,4031 +pygments/lexers/oberon.py,sha256=HMOnehgSbLaTV6l1e5b44aZttyE2YIfA2hzyj6MW5xU,3733 +pygments/lexers/objective.py,sha256=FA7gniip1eEDC9x1UIvdI8flRtFxehTHId0MlqB0llo,22789 +pygments/lexers/ooc.py,sha256=lP6KSoWFrq9Q7w5F_aRSaLYUryh4nuBcPfnUkwyBQsU,2999 +pygments/lexers/other.py,sha256=0xuOYQ0uI9eLONFTNBv2e-hltZhQcN531NVi7e2AcQQ,1768 +pygments/lexers/parasail.py,sha256=YEgpP3B62qHYOBFcoChOfgzATczrSPj1WyovIgqW3gg,2737 +pygments/lexers/parsers.py,sha256=fhTyqwzifEpFFfW8emQ9WYYBwlUs48Sv_qykCUQoWHE,27590 +pygments/lexers/pascal.py,sha256=YpIQHj54lSJrBFdWSo_nkV8M_dYHfJyJMjLk6W6UNZY,32624 +pygments/lexers/pawn.py,sha256=LN0m73AC00wHyvBlbTPU1k2ihBdmDkfIFq24uAWvsF0,8021 +pygments/lexers/perl.py,sha256=Plh4ovtDulyq5oxJTIijQlJ8Td5ga7s3uQ0sbV7uES8,39155 +pygments/lexers/php.py,sha256=yU7DdvXBQlnEvX6WBb7c9kgSw9COwYp6psvzGmCebs4,10834 +pygments/lexers/pony.py,sha256=h6S-MGKN7q7sk869oWjC1OcgV7zwXloYnGFshhTFxHk,3269 +pygments/lexers/praat.py,sha256=aFOD7K8wEVjcr4Jb3DAGn5AmjhMDSHY8pVC4WQfjGlc,12292 +pygments/lexers/prolog.py,sha256=TNj3F1ossufZ_XKVVrWJlRtPDRU1ExGO6NS0-TBq7gw,12405 +pygments/lexers/python.py,sha256=7VkiN5v5IAIL9bDQGdwtmt2__plhedbEi3rzh397Nec,51187 +pygments/lexers/qvt.py,sha256=_lXPT5SdDEqhCmuq4TcO9JRrP703kIT3a1Y_ZW9NTCY,6097 +pygments/lexers/r.py,sha256=VGb5x89r844B-a_V49wAwu8i0245tbdyLKZWq_wRG74,6276 +pygments/lexers/rdf.py,sha256=RAerwJHNjrtXXtua4UXRfUQkMQ36uqfQZlSj63yoQA8,14608 +pygments/lexers/rebol.py,sha256=3bhOFMMneP38O9aJFjPZlNTS6cwbcnDlJaDbfvF4x1g,18624 +pygments/lexers/resource.py,sha256=xbAErtO3-d4LQJJPnLfhD7Kxz_NVQp4WiYrFu52UX-o,2926 +pygments/lexers/ride.py,sha256=lMlEAtdFILb1vd2WC17UaNwFJqOKb1du7EPG5jwX3Xk,5074 +pygments/lexers/rnc.py,sha256=OxpGllFDAM6Vn_alGiaEKMzQDoqRCrl82ocOO4s6L_k,1990 +pygments/lexers/roboconf.py,sha256=9eZkX5xkajimTV1F5wr0Y8QHPfuEB659Lde8H5AzFfM,2070 +pygments/lexers/robotframework.py,sha256=R0x05_jTPu9bErGS4v_mh-9kyCOG4g4GC-KUvxYkSKo,18646 +pygments/lexers/ruby.py,sha256=rqBelW7OJZIP-J3MVPgQzhXTh3Ey41MjMmpbGQDv390,22168 +pygments/lexers/rust.py,sha256=auhHzIX7VaYzgkj26USy9ZH5DZbPQ1LJYW7YDQB8Wgs,7844 +pygments/lexers/sas.py,sha256=guELd_4GLI1fhZr3Sxtn80Gt6s6ViYFf4jWnK23zzDc,9449 +pygments/lexers/scdoc.py,sha256=raoQeCR0E6sjvT56Lar0Wxc_1u6fB-gFjptjT0jE56g,1983 +pygments/lexers/scripting.py,sha256=kH-Kezddx8HzQMgA2z1ZRB-lcvc9qVyEvZnVjJ_YUBU,69759 +pygments/lexers/sgf.py,sha256=R5Zqd5oVOyUd-NewEXMmACaEO5RX_F7eYUZaJXGTY4g,2024 +pygments/lexers/shell.py,sha256=00dGjndFJ6ZWZzsfKW3nKjIKG-CBwTHH-VYQQs57700,33870 +pygments/lexers/sieve.py,sha256=79MOyJl8iAuvzhphpK-Qu_oybyFTdgzrP6d1Hj9-Lqc,2313 +pygments/lexers/slash.py,sha256=WN2f0VirklPe6djATJtbNMkFGRiuIykKZjqG19Rlgk8,8522 +pygments/lexers/smalltalk.py,sha256=xwRETRB2O_cKHZU9w18QXZpiz87WOw0lULDhMxc9xnA,7215 +pygments/lexers/smv.py,sha256=rbXxBG2rGtm7oPP_Il6v3BbUH3i5q4RtiDaweeN7fLA,2793 +pygments/lexers/snobol.py,sha256=YFOOuPk4yBxg6stlIm6R3UiUgzkMjz06ac7dW3LRxNk,2756 +pygments/lexers/solidity.py,sha256=fW_aQc_HyRawyStUxllYhUn-NYJPCqzDH-ABWTeKcOI,3255 +pygments/lexers/special.py,sha256=N0msqSMphQf0_7Vx9T7kABoHx_KkYLHUxP2FcyYmshg,3149 +pygments/lexers/sql.py,sha256=7evoMDWdBz0kXPIt1jy0YXrQ9KJFYnjN2cslkDrfB88,31823 +pygments/lexers/stata.py,sha256=E46GbEy8ET3yBw1l3KQLSknKW3_qS6Sq3V_hkpVotn0,6459 +pygments/lexers/supercollider.py,sha256=llVW-HUi7m4MNGy4wEp8bF2BJGTXdwF0oNfJfJ_sI8M,3516 +pygments/lexers/tcl.py,sha256=ORf0CBXHwC2MFBpZpcK2sPBCCTyJ3rcwcYOIhN9s0AI,5398 +pygments/lexers/templates.py,sha256=AE6yF5ohbqy52-rn8xUJ5A6OZCkoIs72j7wUnwp25vE,73612 +pygments/lexers/teraterm.py,sha256=2DdFVGyKIF85efcB5QdqqQQNGjqRHoWzVc5psdhSD7c,6310 +pygments/lexers/testing.py,sha256=JfFVWAh_8zaqaiPrewb3CGTmGNuHu6hFR4dtvcFCYRE,10753 +pygments/lexers/text.py,sha256=7cwhjV2GwLRH0CPjlOb7PLVa6XEiRQhDNFU1VO3KNjE,1030 +pygments/lexers/textedit.py,sha256=7F9f0-pAsorZpaFalHOZz5124fsdHCLTAWX_YuwA9XE,6092 +pygments/lexers/textfmts.py,sha256=Ctq-u_o2HVb-xvvsKfpuwkgNzVCNxXJHkirqhpsC8lE,15184 +pygments/lexers/theorem.py,sha256=c51ir2FdsyczFRu059z9wKFZplBETdhwWuWX0Y9wMtM,18908 +pygments/lexers/trafficscript.py,sha256=BYTyTAlD4oDVZ9D1aRrmy4zIC4VJ_n2Lgkgq92DxeJM,1546 +pygments/lexers/typoscript.py,sha256=Leb81-51KKuK9FHoo1xKWJGPqTIsyVoeZkGcsK5tQzU,8224 +pygments/lexers/unicon.py,sha256=xo0E3hnBW0gbdszL6n96Cdzume3l1DI7scgkIQ8koaw,18001 +pygments/lexers/urbi.py,sha256=Zq3PCTC-KI7QYuLZ7NSdikm9-MrAhrYH9DGXVSTT89I,5750 +pygments/lexers/usd.py,sha256=uEPjTqbXu0Ag_qJOB9IEwAGj4-R8_5yBbNRlUPtSlbY,3487 +pygments/lexers/varnish.py,sha256=Y2t_JY7uVz6pH3UvlpIvuaxurH4gRiQrP4Esqw5jPnk,7265 +pygments/lexers/verification.py,sha256=rN6OD2ary21XXvnzUTjknibiM9oF9YjxmLyC7iG6kuo,3932 +pygments/lexers/web.py,sha256=4thoq-m_kGixnDR2baWwN5eEqpFAeH3aRaOMK4J_GOE,918 +pygments/lexers/webidl.py,sha256=TTHSlvRlmdpMPNCMvrrUULY6Y6Q7l53HMR9CGyisq9I,10473 +pygments/lexers/webmisc.py,sha256=pJUyS7bcr77iHQshVzllZmIaZQoVkdGZi1D3FqlJEg0,40054 +pygments/lexers/whiley.py,sha256=J9ZuO8Yv9DYl9Mb6IHyZz2zguGxZXBKxTSwDcxaii8o,4012 +pygments/lexers/x10.py,sha256=Lu35QT0l-objbi6mCm-rxZU_7gO1rZQhjA6JnZ-EBRI,1965 +pygments/lexers/xorg.py,sha256=FDN0czbxMD6YDOqwL6ltspElwMoxxNVKW11OL--keQY,887 +pygments/lexers/zig.py,sha256=C3kbdZ_rJUb0hMK61UiFsjzJVvC_QIPJZ6glZDNPi78,4147 +pygments/modeline.py,sha256=ctgJHLjLF23gklYyo7Nz6P3I3Z8ArewlT5R2n2KNatQ,1010 +pygments/plugin.py,sha256=QFSBZcOqSJqAVQnydwDg8_LG7GzkxUgWjb0FzqoQHEM,1734 +pygments/regexopt.py,sha256=yMZBB3DRudP4AjPGAUpIF__o_NWOK4HrNfFV6h04V1w,3094 +pygments/scanner.py,sha256=tATA_g4QYMfFS2Tb-WIJtr_abdUetPb4tC1k7b0e97w,3115 +pygments/sphinxext.py,sha256=OVWeIgj0mvRslxw5boeo0tBykJHuSi5jSjIWXgAmqgk,4618 +pygments/style.py,sha256=DhwzS-OOt088Zkk-SWY6lBVy-Eh_2AcP2R_FdTYO9oI,5705 +pygments/styles/__init__.py,sha256=TBRYkROPEACN-kE1nQ1ygrhU4efWVShENqI6aqjk5cE,2894 +pygments/styles/__pycache__/__init__.cpython-37.pyc,, +pygments/styles/__pycache__/abap.cpython-37.pyc,, +pygments/styles/__pycache__/algol.cpython-37.pyc,, +pygments/styles/__pycache__/algol_nu.cpython-37.pyc,, +pygments/styles/__pycache__/arduino.cpython-37.pyc,, +pygments/styles/__pycache__/autumn.cpython-37.pyc,, +pygments/styles/__pycache__/borland.cpython-37.pyc,, +pygments/styles/__pycache__/bw.cpython-37.pyc,, +pygments/styles/__pycache__/colorful.cpython-37.pyc,, +pygments/styles/__pycache__/default.cpython-37.pyc,, +pygments/styles/__pycache__/emacs.cpython-37.pyc,, +pygments/styles/__pycache__/friendly.cpython-37.pyc,, +pygments/styles/__pycache__/fruity.cpython-37.pyc,, +pygments/styles/__pycache__/igor.cpython-37.pyc,, +pygments/styles/__pycache__/inkpot.cpython-37.pyc,, +pygments/styles/__pycache__/lovelace.cpython-37.pyc,, +pygments/styles/__pycache__/manni.cpython-37.pyc,, +pygments/styles/__pycache__/monokai.cpython-37.pyc,, +pygments/styles/__pycache__/murphy.cpython-37.pyc,, +pygments/styles/__pycache__/native.cpython-37.pyc,, +pygments/styles/__pycache__/paraiso_dark.cpython-37.pyc,, +pygments/styles/__pycache__/paraiso_light.cpython-37.pyc,, +pygments/styles/__pycache__/pastie.cpython-37.pyc,, +pygments/styles/__pycache__/perldoc.cpython-37.pyc,, +pygments/styles/__pycache__/rainbow_dash.cpython-37.pyc,, +pygments/styles/__pycache__/rrt.cpython-37.pyc,, +pygments/styles/__pycache__/sas.cpython-37.pyc,, +pygments/styles/__pycache__/solarized.cpython-37.pyc,, +pygments/styles/__pycache__/stata_dark.cpython-37.pyc,, +pygments/styles/__pycache__/stata_light.cpython-37.pyc,, +pygments/styles/__pycache__/tango.cpython-37.pyc,, +pygments/styles/__pycache__/trac.cpython-37.pyc,, +pygments/styles/__pycache__/vim.cpython-37.pyc,, +pygments/styles/__pycache__/vs.cpython-37.pyc,, +pygments/styles/__pycache__/xcode.cpython-37.pyc,, +pygments/styles/abap.py,sha256=weNa2ATjBDbWN-EJp36KuapOv_161OYudM6ilzp_5tU,751 +pygments/styles/algol.py,sha256=aVMDywxJ1VRTQ-eYd7CZVQ1BFIWehw2G9OcGg5KmfFI,2263 +pygments/styles/algol_nu.py,sha256=xgZhMlsdR8RppCyaGliUKBWVvianjxt5KrIcWCJDVMM,2278 +pygments/styles/arduino.py,sha256=MtP75GT5SqaAX2PfaC116iPETAPOaD6re6cZ1d9xehQ,4492 +pygments/styles/autumn.py,sha256=setTunOOFJAmdVHab3wmv5OkZmjP6-NVoZjMAyQ2rYY,2144 +pygments/styles/borland.py,sha256=UOFktPmmU_TK6prVMETvVm6FhT01oqsd9_HcG1NZq_Y,1562 +pygments/styles/bw.py,sha256=t0kQytwvh_0SMBcOcmM5foPcc3JWiSd8VWBIXkoP17s,1355 +pygments/styles/colorful.py,sha256=NV-MuEX61J0HH1M0dmurc0RNinp5eA9qIHTjhZ3M6ek,2778 +pygments/styles/default.py,sha256=j124bQ-0TFJaQ2U3ZICWq8_KUOQdjUSxFVknFcpSF40,2532 +pygments/styles/emacs.py,sha256=zNGOC_fHnCZxVphHkieHr7f-zxKkSg_PrFEwWGfQw2U,2486 +pygments/styles/friendly.py,sha256=55qszHEliWiT8h1dW5GjnEA47CpXpJ0BX0C-x6EmZsQ,2515 +pygments/styles/fruity.py,sha256=zkSwyKzmWDs9Jtzgq3rG4DathCH6Pq2JVLuUW8auKXI,1298 +pygments/styles/igor.py,sha256=6GFYt43btx70XZoVDSAqljc1G7UJb6_r9euz0b5nWpY,739 +pygments/styles/inkpot.py,sha256=ecGBxZQw0UhueDHZA06wvgWizu2JzXg9YkYCoLYJuh4,2347 +pygments/styles/lovelace.py,sha256=PBObIz9_gAjMJ8YgNrm-_z2P_wG7moQ1BosKLThJl20,3173 +pygments/styles/manni.py,sha256=EmN6YSp-U-ccxqLqjfnIPg-qkIhUAlSb78tIBvwFCsA,2374 +pygments/styles/monokai.py,sha256=hT5jhhqRQoOmjdK1lZ56hspKke4UDCCiUc3B8m5osLY,5086 +pygments/styles/murphy.py,sha256=ppT--IJLWtcbxKCNRBuusP4zdSmbR8YShosCdd3hpXs,2751 +pygments/styles/native.py,sha256=xkphXXv8PvfbgawNSTR28LcEe1TQxFtdrk_sQcGeo2E,1938 +pygments/styles/paraiso_dark.py,sha256=3a4BSgZQMfB8E2bUMi1WAWkDr98oFUfaPygcsl9B9ZM,5641 +pygments/styles/paraiso_light.py,sha256=QsZyh5oPQb6wYgnoQAkH2MRBkJjRPqAu5De77diOeN8,5645 +pygments/styles/pastie.py,sha256=duELGPs_LEzLbesA39vu0MzxtwkPJ2wnV2rS_clTu2E,2473 +pygments/styles/perldoc.py,sha256=Wf54Io76npBZEsVt8HuM-x7mpzJ7iwPgj5PP_hOf91w,2175 +pygments/styles/rainbow_dash.py,sha256=IlLrIcl76wy4aIiZIRWxMzUILOI9ms7YEX0o6UL9ROc,2480 +pygments/styles/rrt.py,sha256=xQp_B5sDo4BJ4Mzx4PWVK6AW_pZs_XmIoM8zLwpfVTs,852 +pygments/styles/sas.py,sha256=jC6iVFl7-xp0MKwFkPM9QbEInzxVlnhsluPR69iqMZE,1441 +pygments/styles/solarized.py,sha256=f_E9bd-THUcJUJR36hQgbu9BVIjLi6yiI_n07oRu2u4,3747 +pygments/styles/stata_dark.py,sha256=K1AKYh93Jd9E_eWXhDw7-tM6fJbIuFeJcAR5jVE1Nkc,1245 +pygments/styles/stata_light.py,sha256=cN0ulhqteDqKkGnOqAL1aNHy3AvYbmu-fS35XaMptKM,1274 +pygments/styles/tango.py,sha256=1VtAeshYeFh4jWITdb5_wf-7avl1DwtGWrQkvSKqJJo,7096 +pygments/styles/trac.py,sha256=wWJokrY8EWWxJTChPxxYsH_cB-CNN7coa1ZBihzbiG4,1933 +pygments/styles/vim.py,sha256=9PtHne1K4TmKIFcPoM4NY_HRV3naKXRIeEvMC437t7U,1976 +pygments/styles/vs.py,sha256=-mK8_RJJk12gbR-TXP1zedQpflKS2zc9xQQzHbZTB1E,1073 +pygments/styles/xcode.py,sha256=s3NuWSoZ8dRCuU0PU0-aDop4xqgAXP4rVefg5yFgQVg,1501 +pygments/token.py,sha256=J1LOX6vjhiN3pTShN9Mj0MfbWPzhypuPQYZuw29E8As,6167 +pygments/unistring.py,sha256=V4LPrb9dhNBGR-AdEnopDNmwpxFSodqPBuHOlqG9b0g,64569 +pygments/util.py,sha256=586xXHiJGGZxqk5PMBu3vBhE68DLuAe5MBARWrSPGxA,10778 diff --git a/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/WHEEL b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/WHEEL new file mode 100644 index 0000000..3b5c403 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.6) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/entry_points.txt b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/entry_points.txt new file mode 100644 index 0000000..756d801 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +pygmentize = pygments.cmdline:main + diff --git a/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/top_level.txt b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/top_level.txt new file mode 100644 index 0000000..a9f49e0 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Pygments-2.6.1.dist-info/top_level.txt @@ -0,0 +1 @@ +pygments diff --git a/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/DESCRIPTION.rst b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..4b318b2 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/DESCRIPTION.rst @@ -0,0 +1,237 @@ +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: the 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. Unidecode supports Python 2.7 and 3.4 or later. + +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 + +Frequently asked questions +-------------------------- + +German umlauts are transliterated incorrectly + Latin letters "a", "o" and "u" with diaeresis are transliterated by + Unidecode as "a", "o", "u", *not* according to German rules "ae", "oe", + "ue". This is intentional and will not be changed. Rationale is that these + letters are used in languages other than German (for example, Finnish and + Turkish). German text transliterated without the extra "e" is much more + readable than other languages transliterated using German rules. A + workaround is to do your own replacements of these characters before + passing the string to ``unidecode()``. + +Unidecode should support localization (e.g. a language or country parameter, inspecting system locale, etc.) + Language-specific transliteration is a complicated problem and beyond the + scope of this library. Changes related to this will not be accepted. Please + consider using other libraries which do provide this capability, such as + `Unihandecode `_. + +Unidecode should use a permissive license such as MIT or the BSD license. + The maintainer of Unidecode believes that providing access to source code + on redistribution is a fair and reasonable request when basing products on + voluntary work of many contributors. If the license is not suitable for + you, please consider using other libraries, such as `text-unidecode + `_. + +Unidecode produces completely wrong results (e.g. "u" with diaeresis transliterating as "A 1/4 ") + The strings you are passing to Unidecode have been wrongly decoded + somewhere in your program. For example, you might be decoding utf-8 encoded + strings as latin1. With a misconfigured terminal, locale and/or a text + editor this might not be immediately apparent. Inspect your strings with + ``repr()`` and consult the + `Unicode HOWTO `_. + +I've upgraded Unidecode and now some URLs on my website return 404 Not Found. + This is an issue with the software that is running your website, not + Unidecode. Occasionally, new versions of Unidecode library are released + which contain improvements to the transliteration tables. This means that + you cannot rely that ``unidecode()`` output will not change across + different versions of Unidecode library. If you use ``unidecode()`` to + generate URLs for your website, either generate the URL slug once and store + it in the database or lock your dependency of Unidecode to one specific + version. + +Some of the issues in this section are discussed in more detail in `this blog +post `_. + + +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 make sure to read the `Frequently asked questions`_ section above before +contacting the maintainer. + +Bug reports, patches and suggestions for Unidecode can be sent 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 2019, 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/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/INSTALLER b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/LICENSE.txt b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/LICENSE.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.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/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/METADATA b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/METADATA new file mode 100644 index 0000000..c73f056 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/METADATA @@ -0,0 +1,261 @@ +Metadata-Version: 2.0 +Name: Unidecode +Version: 1.1.1 +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.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Text Processing +Classifier: Topic :: Text Processing :: Filters +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* + +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: the 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. Unidecode supports Python 2.7 and 3.4 or later. + +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 + +Frequently asked questions +-------------------------- + +German umlauts are transliterated incorrectly + Latin letters "a", "o" and "u" with diaeresis are transliterated by + Unidecode as "a", "o", "u", *not* according to German rules "ae", "oe", + "ue". This is intentional and will not be changed. Rationale is that these + letters are used in languages other than German (for example, Finnish and + Turkish). German text transliterated without the extra "e" is much more + readable than other languages transliterated using German rules. A + workaround is to do your own replacements of these characters before + passing the string to ``unidecode()``. + +Unidecode should support localization (e.g. a language or country parameter, inspecting system locale, etc.) + Language-specific transliteration is a complicated problem and beyond the + scope of this library. Changes related to this will not be accepted. Please + consider using other libraries which do provide this capability, such as + `Unihandecode `_. + +Unidecode should use a permissive license such as MIT or the BSD license. + The maintainer of Unidecode believes that providing access to source code + on redistribution is a fair and reasonable request when basing products on + voluntary work of many contributors. If the license is not suitable for + you, please consider using other libraries, such as `text-unidecode + `_. + +Unidecode produces completely wrong results (e.g. "u" with diaeresis transliterating as "A 1/4 ") + The strings you are passing to Unidecode have been wrongly decoded + somewhere in your program. For example, you might be decoding utf-8 encoded + strings as latin1. With a misconfigured terminal, locale and/or a text + editor this might not be immediately apparent. Inspect your strings with + ``repr()`` and consult the + `Unicode HOWTO `_. + +I've upgraded Unidecode and now some URLs on my website return 404 Not Found. + This is an issue with the software that is running your website, not + Unidecode. Occasionally, new versions of Unidecode library are released + which contain improvements to the transliteration tables. This means that + you cannot rely that ``unidecode()`` output will not change across + different versions of Unidecode library. If you use ``unidecode()`` to + generate URLs for your website, either generate the URL slug once and store + it in the database or lock your dependency of Unidecode to one specific + version. + +Some of the issues in this section are discussed in more detail in `this blog +post `_. + + +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 make sure to read the `Frequently asked questions`_ section above before +contacting the maintainer. + +Bug reports, patches and suggestions for Unidecode can be sent 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 2019, 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/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/RECORD b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/RECORD new file mode 100644 index 0000000..0719ba4 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/RECORD @@ -0,0 +1,394 @@ +../../../bin/unidecode,sha256=lTbG9QCtfk2-jWUig_WnlGb4t7-1Bljb0pu2DtfOpRw,272 +Unidecode-1.1.1.dist-info/DESCRIPTION.rst,sha256=1qgo-PnPYFf7RjSFGEnFbEL6FdtVq5GJMUDpysakI6A,9474 +Unidecode-1.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Unidecode-1.1.1.dist-info/LICENSE.txt,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092 +Unidecode-1.1.1.dist-info/METADATA,sha256=tYiBHi608dh-oJ4Eev65ipZFvIjgAVbj-peGVHYr73I,10442 +Unidecode-1.1.1.dist-info/RECORD,, +Unidecode-1.1.1.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 +Unidecode-1.1.1.dist-info/entry_points.txt,sha256=ItDp7W6CoSJQAKqdiUkzmecugvKvWzjfN7MMK52GM10,51 +Unidecode-1.1.1.dist-info/metadata.json,sha256=uK7I5vPstQ58Y3hzy2i7dCLyq5GjVCJPIxQk1MZt8x0,1200 +Unidecode-1.1.1.dist-info/top_level.txt,sha256=4uYNG2l04s0dm0mEQmPLo2zrjLbhLPKUesLr2dOTdpo,10 +unidecode/__init__.py,sha256=OdPRbXia-xzzGelCHnVh_zmcHJA2s2wP6RGocPnCihU,3094 +unidecode/__main__.py,sha256=VWYWCclyJsdhtNMQtryMFbgsCZtNUsWcEuS7ZOlH1Jc,40 +unidecode/__pycache__/__init__.cpython-37.pyc,, +unidecode/__pycache__/__main__.cpython-37.pyc,, +unidecode/__pycache__/util.cpython-37.pyc,, +unidecode/__pycache__/x000.cpython-37.pyc,, +unidecode/__pycache__/x001.cpython-37.pyc,, +unidecode/__pycache__/x002.cpython-37.pyc,, +unidecode/__pycache__/x003.cpython-37.pyc,, +unidecode/__pycache__/x004.cpython-37.pyc,, +unidecode/__pycache__/x005.cpython-37.pyc,, +unidecode/__pycache__/x006.cpython-37.pyc,, +unidecode/__pycache__/x007.cpython-37.pyc,, +unidecode/__pycache__/x009.cpython-37.pyc,, +unidecode/__pycache__/x00a.cpython-37.pyc,, +unidecode/__pycache__/x00b.cpython-37.pyc,, +unidecode/__pycache__/x00c.cpython-37.pyc,, +unidecode/__pycache__/x00d.cpython-37.pyc,, +unidecode/__pycache__/x00e.cpython-37.pyc,, +unidecode/__pycache__/x00f.cpython-37.pyc,, +unidecode/__pycache__/x010.cpython-37.pyc,, +unidecode/__pycache__/x011.cpython-37.pyc,, +unidecode/__pycache__/x012.cpython-37.pyc,, +unidecode/__pycache__/x013.cpython-37.pyc,, +unidecode/__pycache__/x014.cpython-37.pyc,, +unidecode/__pycache__/x015.cpython-37.pyc,, +unidecode/__pycache__/x016.cpython-37.pyc,, +unidecode/__pycache__/x017.cpython-37.pyc,, +unidecode/__pycache__/x018.cpython-37.pyc,, +unidecode/__pycache__/x01d.cpython-37.pyc,, +unidecode/__pycache__/x01e.cpython-37.pyc,, +unidecode/__pycache__/x01f.cpython-37.pyc,, +unidecode/__pycache__/x020.cpython-37.pyc,, +unidecode/__pycache__/x021.cpython-37.pyc,, +unidecode/__pycache__/x022.cpython-37.pyc,, +unidecode/__pycache__/x023.cpython-37.pyc,, +unidecode/__pycache__/x024.cpython-37.pyc,, +unidecode/__pycache__/x025.cpython-37.pyc,, +unidecode/__pycache__/x026.cpython-37.pyc,, +unidecode/__pycache__/x027.cpython-37.pyc,, +unidecode/__pycache__/x028.cpython-37.pyc,, +unidecode/__pycache__/x029.cpython-37.pyc,, +unidecode/__pycache__/x02a.cpython-37.pyc,, +unidecode/__pycache__/x02c.cpython-37.pyc,, +unidecode/__pycache__/x02e.cpython-37.pyc,, +unidecode/__pycache__/x02f.cpython-37.pyc,, +unidecode/__pycache__/x030.cpython-37.pyc,, +unidecode/__pycache__/x031.cpython-37.pyc,, +unidecode/__pycache__/x032.cpython-37.pyc,, +unidecode/__pycache__/x033.cpython-37.pyc,, +unidecode/__pycache__/x04d.cpython-37.pyc,, +unidecode/__pycache__/x04e.cpython-37.pyc,, +unidecode/__pycache__/x04f.cpython-37.pyc,, +unidecode/__pycache__/x050.cpython-37.pyc,, +unidecode/__pycache__/x051.cpython-37.pyc,, +unidecode/__pycache__/x052.cpython-37.pyc,, +unidecode/__pycache__/x053.cpython-37.pyc,, +unidecode/__pycache__/x054.cpython-37.pyc,, +unidecode/__pycache__/x055.cpython-37.pyc,, +unidecode/__pycache__/x056.cpython-37.pyc,, +unidecode/__pycache__/x057.cpython-37.pyc,, +unidecode/__pycache__/x058.cpython-37.pyc,, +unidecode/__pycache__/x059.cpython-37.pyc,, +unidecode/__pycache__/x05a.cpython-37.pyc,, +unidecode/__pycache__/x05b.cpython-37.pyc,, +unidecode/__pycache__/x05c.cpython-37.pyc,, +unidecode/__pycache__/x05d.cpython-37.pyc,, +unidecode/__pycache__/x05e.cpython-37.pyc,, +unidecode/__pycache__/x05f.cpython-37.pyc,, +unidecode/__pycache__/x060.cpython-37.pyc,, +unidecode/__pycache__/x061.cpython-37.pyc,, +unidecode/__pycache__/x062.cpython-37.pyc,, +unidecode/__pycache__/x063.cpython-37.pyc,, +unidecode/__pycache__/x064.cpython-37.pyc,, +unidecode/__pycache__/x065.cpython-37.pyc,, +unidecode/__pycache__/x066.cpython-37.pyc,, +unidecode/__pycache__/x067.cpython-37.pyc,, +unidecode/__pycache__/x068.cpython-37.pyc,, +unidecode/__pycache__/x069.cpython-37.pyc,, +unidecode/__pycache__/x06a.cpython-37.pyc,, +unidecode/__pycache__/x06b.cpython-37.pyc,, +unidecode/__pycache__/x06c.cpython-37.pyc,, +unidecode/__pycache__/x06d.cpython-37.pyc,, +unidecode/__pycache__/x06e.cpython-37.pyc,, +unidecode/__pycache__/x06f.cpython-37.pyc,, +unidecode/__pycache__/x070.cpython-37.pyc,, +unidecode/__pycache__/x071.cpython-37.pyc,, +unidecode/__pycache__/x072.cpython-37.pyc,, +unidecode/__pycache__/x073.cpython-37.pyc,, +unidecode/__pycache__/x074.cpython-37.pyc,, +unidecode/__pycache__/x075.cpython-37.pyc,, +unidecode/__pycache__/x076.cpython-37.pyc,, +unidecode/__pycache__/x077.cpython-37.pyc,, +unidecode/__pycache__/x078.cpython-37.pyc,, +unidecode/__pycache__/x079.cpython-37.pyc,, +unidecode/__pycache__/x07a.cpython-37.pyc,, +unidecode/__pycache__/x07b.cpython-37.pyc,, +unidecode/__pycache__/x07c.cpython-37.pyc,, +unidecode/__pycache__/x07d.cpython-37.pyc,, +unidecode/__pycache__/x07e.cpython-37.pyc,, +unidecode/__pycache__/x07f.cpython-37.pyc,, +unidecode/__pycache__/x080.cpython-37.pyc,, +unidecode/__pycache__/x081.cpython-37.pyc,, +unidecode/__pycache__/x082.cpython-37.pyc,, +unidecode/__pycache__/x083.cpython-37.pyc,, +unidecode/__pycache__/x084.cpython-37.pyc,, +unidecode/__pycache__/x085.cpython-37.pyc,, +unidecode/__pycache__/x086.cpython-37.pyc,, +unidecode/__pycache__/x087.cpython-37.pyc,, +unidecode/__pycache__/x088.cpython-37.pyc,, +unidecode/__pycache__/x089.cpython-37.pyc,, +unidecode/__pycache__/x08a.cpython-37.pyc,, +unidecode/__pycache__/x08b.cpython-37.pyc,, +unidecode/__pycache__/x08c.cpython-37.pyc,, +unidecode/__pycache__/x08d.cpython-37.pyc,, +unidecode/__pycache__/x08e.cpython-37.pyc,, +unidecode/__pycache__/x08f.cpython-37.pyc,, +unidecode/__pycache__/x090.cpython-37.pyc,, +unidecode/__pycache__/x091.cpython-37.pyc,, +unidecode/__pycache__/x092.cpython-37.pyc,, +unidecode/__pycache__/x093.cpython-37.pyc,, +unidecode/__pycache__/x094.cpython-37.pyc,, +unidecode/__pycache__/x095.cpython-37.pyc,, +unidecode/__pycache__/x096.cpython-37.pyc,, +unidecode/__pycache__/x097.cpython-37.pyc,, +unidecode/__pycache__/x098.cpython-37.pyc,, +unidecode/__pycache__/x099.cpython-37.pyc,, +unidecode/__pycache__/x09a.cpython-37.pyc,, +unidecode/__pycache__/x09b.cpython-37.pyc,, +unidecode/__pycache__/x09c.cpython-37.pyc,, +unidecode/__pycache__/x09d.cpython-37.pyc,, +unidecode/__pycache__/x09e.cpython-37.pyc,, +unidecode/__pycache__/x09f.cpython-37.pyc,, +unidecode/__pycache__/x0a0.cpython-37.pyc,, +unidecode/__pycache__/x0a1.cpython-37.pyc,, +unidecode/__pycache__/x0a2.cpython-37.pyc,, +unidecode/__pycache__/x0a3.cpython-37.pyc,, +unidecode/__pycache__/x0a4.cpython-37.pyc,, +unidecode/__pycache__/x0ac.cpython-37.pyc,, +unidecode/__pycache__/x0ad.cpython-37.pyc,, +unidecode/__pycache__/x0ae.cpython-37.pyc,, +unidecode/__pycache__/x0af.cpython-37.pyc,, +unidecode/__pycache__/x0b0.cpython-37.pyc,, +unidecode/__pycache__/x0b1.cpython-37.pyc,, +unidecode/__pycache__/x0b2.cpython-37.pyc,, +unidecode/__pycache__/x0b3.cpython-37.pyc,, +unidecode/__pycache__/x0b4.cpython-37.pyc,, +unidecode/__pycache__/x0b5.cpython-37.pyc,, +unidecode/__pycache__/x0b6.cpython-37.pyc,, +unidecode/__pycache__/x0b7.cpython-37.pyc,, +unidecode/__pycache__/x0b8.cpython-37.pyc,, +unidecode/__pycache__/x0b9.cpython-37.pyc,, +unidecode/__pycache__/x0ba.cpython-37.pyc,, +unidecode/__pycache__/x0bb.cpython-37.pyc,, +unidecode/__pycache__/x0bc.cpython-37.pyc,, +unidecode/__pycache__/x0bd.cpython-37.pyc,, +unidecode/__pycache__/x0be.cpython-37.pyc,, +unidecode/__pycache__/x0bf.cpython-37.pyc,, +unidecode/__pycache__/x0c0.cpython-37.pyc,, +unidecode/__pycache__/x0c1.cpython-37.pyc,, +unidecode/__pycache__/x0c2.cpython-37.pyc,, +unidecode/__pycache__/x0c3.cpython-37.pyc,, +unidecode/__pycache__/x0c4.cpython-37.pyc,, +unidecode/__pycache__/x0c5.cpython-37.pyc,, +unidecode/__pycache__/x0c6.cpython-37.pyc,, +unidecode/__pycache__/x0c7.cpython-37.pyc,, +unidecode/__pycache__/x0c8.cpython-37.pyc,, +unidecode/__pycache__/x0c9.cpython-37.pyc,, +unidecode/__pycache__/x0ca.cpython-37.pyc,, +unidecode/__pycache__/x0cb.cpython-37.pyc,, +unidecode/__pycache__/x0cc.cpython-37.pyc,, +unidecode/__pycache__/x0cd.cpython-37.pyc,, +unidecode/__pycache__/x0ce.cpython-37.pyc,, +unidecode/__pycache__/x0cf.cpython-37.pyc,, +unidecode/__pycache__/x0d0.cpython-37.pyc,, +unidecode/__pycache__/x0d1.cpython-37.pyc,, +unidecode/__pycache__/x0d2.cpython-37.pyc,, +unidecode/__pycache__/x0d3.cpython-37.pyc,, +unidecode/__pycache__/x0d4.cpython-37.pyc,, +unidecode/__pycache__/x0d5.cpython-37.pyc,, +unidecode/__pycache__/x0d6.cpython-37.pyc,, +unidecode/__pycache__/x0d7.cpython-37.pyc,, +unidecode/__pycache__/x0f9.cpython-37.pyc,, +unidecode/__pycache__/x0fa.cpython-37.pyc,, +unidecode/__pycache__/x0fb.cpython-37.pyc,, +unidecode/__pycache__/x0fc.cpython-37.pyc,, +unidecode/__pycache__/x0fd.cpython-37.pyc,, +unidecode/__pycache__/x0fe.cpython-37.pyc,, +unidecode/__pycache__/x0ff.cpython-37.pyc,, +unidecode/__pycache__/x1d4.cpython-37.pyc,, +unidecode/__pycache__/x1d5.cpython-37.pyc,, +unidecode/__pycache__/x1d6.cpython-37.pyc,, +unidecode/__pycache__/x1d7.cpython-37.pyc,, +unidecode/__pycache__/x1f1.cpython-37.pyc,, +unidecode/util.py,sha256=qR0aGIMExGrFUGmojfAMdbRklcXrAPvXpU1evPXCa4g,1782 +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=qPEepEySMuoXFtVhrFw-y69wnVQGcFmk_0vtcUzDYTo,3997 +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=EwAYkMVHAFvbKRzsQ-e4cRcvS_eia3kYCM2GcaqkBWY,3701 +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=ImTd4BRRPgCqWmrvJPoikoL0dJMKH8eQgd48vksi60A,4513 +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=CcmetFXD5Xxt9ZY0xKAjQbe16Wlq-YccWvJ_eqdzeTc,3937 diff --git a/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/WHEEL b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/WHEEL new file mode 100644 index 0000000..8b6dd1b --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.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/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/entry_points.txt b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/entry_points.txt new file mode 100644 index 0000000..3016afe --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +unidecode = unidecode.util:main + diff --git a/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/metadata.json b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/metadata.json new file mode 100644 index 0000000..64bbf15 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.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.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "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.29.0)", "license": "GPL", "metadata_version": "2.0", "name": "Unidecode", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "summary": "ASCII transliterations of Unicode text", "version": "1.1.1"} \ No newline at end of file diff --git a/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/top_level.txt b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/top_level.txt new file mode 100644 index 0000000..051b14c --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/Unidecode-1.1.1.dist-info/top_level.txt @@ -0,0 +1 @@ +unidecode diff --git a/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/PKG-INFO b/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/PKG-INFO new file mode 100644 index 0000000..7b58794 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/PKG-INFO @@ -0,0 +1,101 @@ +Metadata-Version: 1.1 +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 +Description: [![Build Status](https://travis-ci.org/jek/blinker.svg?branch=master)](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 + +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 diff --git a/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/SOURCES.txt b/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/SOURCES.txt new file mode 100644 index 0000000..8a2dace --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/SOURCES.txt @@ -0,0 +1,49 @@ +AUTHORS +CHANGES +LICENSE +MANIFEST.in +README.md +setup.cfg +setup.py +blinker/__init__.py +blinker/_saferef.py +blinker/_utilities.py +blinker/base.py +blinker.egg-info/PKG-INFO +blinker.egg-info/SOURCES.txt +blinker.egg-info/dependency_links.txt +blinker.egg-info/top_level.txt +docs/html/genindex.html +docs/html/index.html +docs/html/objects.inv +docs/html/search.html +docs/html/searchindex.js +docs/html/_sources/index.txt +docs/html/_static/basic.css +docs/html/_static/blinker-named.png +docs/html/_static/blinker64.png +docs/html/_static/comment-bright.png +docs/html/_static/comment-close.png +docs/html/_static/comment.png +docs/html/_static/doctools.js +docs/html/_static/down-pressed.png +docs/html/_static/down.png +docs/html/_static/file.png +docs/html/_static/flasky.css +docs/html/_static/jquery.js +docs/html/_static/minus.png +docs/html/_static/plus.png +docs/html/_static/pygments.css +docs/html/_static/searchtools.js +docs/html/_static/underscore.js +docs/html/_static/up-pressed.png +docs/html/_static/up.png +docs/html/_static/websupport.js +docs/source/conf.py +docs/source/index.rst +docs/source/_themes/flask_theme_support.py +docs/text/index.txt +tests/test_context.py +tests/test_saferef.py +tests/test_signals.py +tests/test_utilities.py \ No newline at end of file diff --git a/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/dependency_links.txt b/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/installed-files.txt b/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/installed-files.txt new file mode 100644 index 0000000..94d1b47 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/installed-files.txt @@ -0,0 +1,12 @@ +../blinker/__init__.py +../blinker/__pycache__/__init__.cpython-37.pyc +../blinker/__pycache__/_saferef.cpython-37.pyc +../blinker/__pycache__/_utilities.cpython-37.pyc +../blinker/__pycache__/base.cpython-37.pyc +../blinker/_saferef.py +../blinker/_utilities.py +../blinker/base.py +PKG-INFO +SOURCES.txt +dependency_links.txt +top_level.txt diff --git a/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/top_level.txt b/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/top_level.txt new file mode 100644 index 0000000..1ff4ca5 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/blinker-1.4.egg-info/top_level.txt @@ -0,0 +1 @@ +blinker diff --git a/bots-venv/lib/python3.7/site-packages/blinker/__init__.py b/bots-venv/lib/python3.7/site-packages/blinker/__init__.py new file mode 100644 index 0000000..3ea239c --- /dev/null +++ b/bots-venv/lib/python3.7/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/bots-venv/lib/python3.7/site-packages/blinker/_saferef.py b/bots-venv/lib/python3.7/site-packages/blinker/_saferef.py new file mode 100644 index 0000000..269e362 --- /dev/null +++ b/bots-venv/lib/python3.7/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/bots-venv/lib/python3.7/site-packages/blinker/_utilities.py b/bots-venv/lib/python3.7/site-packages/blinker/_utilities.py new file mode 100644 index 0000000..056270d --- /dev/null +++ b/bots-venv/lib/python3.7/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/bots-venv/lib/python3.7/site-packages/blinker/base.py b/bots-venv/lib/python3.7/site-packages/blinker/base.py new file mode 100644 index 0000000..cc5880e --- /dev/null +++ b/bots-venv/lib/python3.7/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/bots-venv/lib/python3.7/site-packages/dateutil/__init__.py b/bots-venv/lib/python3.7/site-packages/dateutil/__init__.py new file mode 100644 index 0000000..0defb82 --- /dev/null +++ b/bots-venv/lib/python3.7/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/bots-venv/lib/python3.7/site-packages/dateutil/_common.py b/bots-venv/lib/python3.7/site-packages/dateutil/_common.py new file mode 100644 index 0000000..4eb2659 --- /dev/null +++ b/bots-venv/lib/python3.7/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/bots-venv/lib/python3.7/site-packages/dateutil/_version.py b/bots-venv/lib/python3.7/site-packages/dateutil/_version.py new file mode 100644 index 0000000..eac1209 --- /dev/null +++ b/bots-venv/lib/python3.7/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.8.1' diff --git a/bots-venv/lib/python3.7/site-packages/dateutil/easter.py b/bots-venv/lib/python3.7/site-packages/dateutil/easter.py new file mode 100644 index 0000000..53b7c78 --- /dev/null +++ b/bots-venv/lib/python3.7/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/bots-venv/lib/python3.7/site-packages/dateutil/parser/__init__.py b/bots-venv/lib/python3.7/site-packages/dateutil/parser/__init__.py new file mode 100644 index 0000000..d174b0e --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/dateutil/parser/__init__.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +from ._parser import parse, parser, parserinfo, ParserError +from ._parser import DEFAULTPARSER, DEFAULTTZPARSER +from ._parser import UnknownTimezoneWarning + +from ._parser import __doc__ + +from .isoparser import isoparser, isoparse + +__all__ = ['parse', 'parser', 'parserinfo', + 'isoparse', 'isoparser', + 'ParserError', + 'UnknownTimezoneWarning'] + + +### +# Deprecate portions of the private interface so that downstream code that +# is improperly relying on it is given *some* notice. + + +def __deprecated_private_func(f): + from functools import wraps + import warnings + + msg = ('{name} is a private function and may break without warning, ' + 'it will be moved and or renamed in future versions.') + msg = msg.format(name=f.__name__) + + @wraps(f) + def deprecated_func(*args, **kwargs): + warnings.warn(msg, DeprecationWarning) + return f(*args, **kwargs) + + return deprecated_func + +def __deprecate_private_class(c): + import warnings + + msg = ('{name} is a private class and may break without warning, ' + 'it will be moved and or renamed in future versions.') + msg = msg.format(name=c.__name__) + + class private_class(c): + __doc__ = c.__doc__ + + def __init__(self, *args, **kwargs): + warnings.warn(msg, DeprecationWarning) + super(private_class, self).__init__(*args, **kwargs) + + private_class.__name__ = c.__name__ + + return private_class + + +from ._parser import _timelex, _resultbase +from ._parser import _tzparser, _parsetz + +_timelex = __deprecate_private_class(_timelex) +_tzparser = __deprecate_private_class(_tzparser) +_resultbase = __deprecate_private_class(_resultbase) +_parsetz = __deprecated_private_func(_parsetz) diff --git a/bots-venv/lib/python3.7/site-packages/dateutil/parser/_parser.py b/bots-venv/lib/python3.7/site-packages/dateutil/parser/_parser.py new file mode 100644 index 0000000..458aa6a --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/dateutil/parser/_parser.py @@ -0,0 +1,1609 @@ +# -*- 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 integer_types, text_type + +from decimal import Decimal + +from warnings import warn + +from .. import relativedelta +from .. import tz + +__all__ = ["parse", "parserinfo", "ParserError"] + + +# TODO: pandas.core.tools.datetimes imports this explicitly. Might be worth +# making public and/or figuring out if there is something we can +# take off their plate. +class _timelex(object): + # Fractional seconds are sometimes split by a comma + _split_decimal = re.compile("([.,])") + + def __init__(self, instream): + if six.PY2: + # In Python 2, we can't duck type properly because unicode has + # a 'decode' function, and we'd be double-decoding + if isinstance(instream, (bytes, bytearray)): + instream = instream.decode() + else: + if getattr(instream, 'decode', None) is not None: + instream = instream.decode() + + if isinstance(instream, text_type): + instream = StringIO(instream) + elif getattr(instream, 'read', None) is None: + raise TypeError('Parser must be a string or character stream, not ' + '{itype}'.format(itype=instream.__class__.__name__)) + + self.instream = instream + self.charstack = [] + self.tokenstack = [] + self.eof = False + + def get_token(self): + """ + This function breaks the time string into lexical units (tokens), which + can be parsed by the parser. Lexical units are demarcated by changes in + the character set, so any continuous string of letters is considered + one unit, any continuous string of numbers is considered one unit. + + The main complication arises from the fact that dots ('.') can be used + both as separators (e.g. "Sep.20.2009") or decimal points (e.g. + "4:30:21.447"). As such, it is necessary to read the full context of + any dot-separated strings before breaking it into tokens; as such, this + function maintains a "token stack", for when the ambiguous context + demands that multiple tokens be parsed at once. + """ + if self.tokenstack: + return self.tokenstack.pop(0) + + seenletters = False + token = None + state = None + + while not self.eof: + # We only realize that we've reached the end of a token when we + # find a character that's not part of the current token - since + # that character may be part of the next token, it's stored in the + # charstack. + if self.charstack: + nextchar = self.charstack.pop(0) + else: + nextchar = self.instream.read(1) + while nextchar == '\x00': + nextchar = self.instream.read(1) + + if not nextchar: + self.eof = True + break + elif not state: + # First character of the token - determines if we're starting + # to parse a word, a number or something else. + token = nextchar + if self.isword(nextchar): + state = 'a' + elif self.isnum(nextchar): + state = '0' + elif self.isspace(nextchar): + token = ' ' + break # emit token + else: + break # emit token + elif state == 'a': + # If we've already started reading a word, we keep reading + # letters until we find something that's not part of a word. + seenletters = True + if self.isword(nextchar): + token += nextchar + elif nextchar == '.': + token += nextchar + state = 'a.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == '0': + # If we've already started reading a number, we keep reading + # numbers until we find something that doesn't fit. + if self.isnum(nextchar): + token += nextchar + elif nextchar == '.' or (nextchar == ',' and len(token) >= 2): + token += nextchar + state = '0.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == 'a.': + # If we've seen some letters and a dot separator, continue + # parsing, and the tokens will be broken up later. + seenletters = True + if nextchar == '.' or self.isword(nextchar): + token += nextchar + elif self.isnum(nextchar) and token[-1] == '.': + token += nextchar + state = '0.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == '0.': + # If we've seen at least one dot separator, keep going, we'll + # break up the tokens later. + if nextchar == '.' or self.isnum(nextchar): + token += nextchar + elif self.isword(nextchar) and token[-1] == '.': + token += nextchar + state = 'a.' + else: + self.charstack.append(nextchar) + break # emit token + + if (state in ('a.', '0.') and (seenletters or token.count('.') > 1 or + token[-1] in '.,')): + l = self._split_decimal.split(token) + token = l[0] + for tok in l[1:]: + if tok: + self.tokenstack.append(tok) + + if state == '0.' and token.count('.') == 0: + token = token.replace(',', '.') + + return token + + def __iter__(self): + return self + + def __next__(self): + token = self.get_token() + if token is None: + raise StopIteration + + return token + + def next(self): + return self.__next__() # Python 2.x support + + @classmethod + def split(cls, s): + return list(cls(s)) + + @classmethod + def isword(cls, nextchar): + """ Whether or not the next character is part of a word """ + return nextchar.isalpha() + + @classmethod + def isnum(cls, nextchar): + """ Whether the next character is part of a number """ + return nextchar.isdigit() + + @classmethod + def isspace(cls, nextchar): + """ Whether the next character is whitespace """ + return nextchar.isspace() + + +class _resultbase(object): + + def __init__(self): + for attr in self.__slots__: + setattr(self, attr, None) + + def _repr(self, classname): + l = [] + for attr in self.__slots__: + value = getattr(self, attr) + if value is not None: + l.append("%s=%s" % (attr, repr(value))) + return "%s(%s)" % (classname, ", ".join(l)) + + def __len__(self): + return (sum(getattr(self, attr) is not None + for attr in self.__slots__)) + + def __repr__(self): + return self._repr(self.__class__.__name__) + + +class parserinfo(object): + """ + Class which handles what inputs are accepted. Subclass this to customize + the language and acceptable values for each parameter. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM + and YMD. Default is ``False``. + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken + to be the year, otherwise the last number is taken to be the year. + Default is ``False``. + """ + + # m from a.m/p.m, t from ISO T separator + JUMP = [" ", ".", ",", ";", "-", "/", "'", + "at", "on", "and", "ad", "m", "t", "of", + "st", "nd", "rd", "th"] + + WEEKDAYS = [("Mon", "Monday"), + ("Tue", "Tuesday"), # TODO: "Tues" + ("Wed", "Wednesday"), + ("Thu", "Thursday"), # TODO: "Thurs" + ("Fri", "Friday"), + ("Sat", "Saturday"), + ("Sun", "Sunday")] + MONTHS = [("Jan", "January"), + ("Feb", "February"), # TODO: "Febr" + ("Mar", "March"), + ("Apr", "April"), + ("May", "May"), + ("Jun", "June"), + ("Jul", "July"), + ("Aug", "August"), + ("Sep", "Sept", "September"), + ("Oct", "October"), + ("Nov", "November"), + ("Dec", "December")] + HMS = [("h", "hour", "hours"), + ("m", "minute", "minutes"), + ("s", "second", "seconds")] + AMPM = [("am", "a"), + ("pm", "p")] + UTCZONE = ["UTC", "GMT", "Z", "z"] + PERTAIN = ["of"] + TZOFFSET = {} + # TODO: ERA = ["AD", "BC", "CE", "BCE", "Stardate", + # "Anno Domini", "Year of Our Lord"] + + def __init__(self, dayfirst=False, yearfirst=False): + self._jump = self._convert(self.JUMP) + self._weekdays = self._convert(self.WEEKDAYS) + self._months = self._convert(self.MONTHS) + self._hms = self._convert(self.HMS) + self._ampm = self._convert(self.AMPM) + self._utczone = self._convert(self.UTCZONE) + self._pertain = self._convert(self.PERTAIN) + + self.dayfirst = dayfirst + self.yearfirst = yearfirst + + self._year = time.localtime().tm_year + self._century = self._year // 100 * 100 + + def _convert(self, lst): + dct = {} + for i, v in enumerate(lst): + if isinstance(v, tuple): + for v in v: + dct[v.lower()] = i + else: + dct[v.lower()] = i + return dct + + def jump(self, name): + return name.lower() in self._jump + + def weekday(self, name): + try: + return self._weekdays[name.lower()] + except KeyError: + pass + return None + + def month(self, name): + try: + return self._months[name.lower()] + 1 + except KeyError: + pass + return None + + def hms(self, name): + try: + return self._hms[name.lower()] + except KeyError: + return None + + def ampm(self, name): + try: + return self._ampm[name.lower()] + except KeyError: + return None + + def pertain(self, name): + return name.lower() in self._pertain + + def utczone(self, name): + return name.lower() in self._utczone + + def tzoffset(self, name): + if name in self._utczone: + return 0 + + return self.TZOFFSET.get(name) + + def convertyear(self, year, century_specified=False): + """ + Converts two-digit years to year within [-50, 49] + range of self._year (current local time) + """ + + # Function contract is that the year is always positive + assert year >= 0 + + if year < 100 and not century_specified: + # assume current century to start + year += self._century + + if year >= self._year + 50: # if too far in future + year -= 100 + elif year < self._year - 50: # if too far in past + year += 100 + + return year + + def validate(self, res): + # move to info + if res.year is not None: + res.year = self.convertyear(res.year, res.century_specified) + + if ((res.tzoffset == 0 and not res.tzname) or + (res.tzname == 'Z' or res.tzname == 'z')): + res.tzname = "UTC" + res.tzoffset = 0 + elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname): + res.tzoffset = 0 + return True + + +class _ymd(list): + def __init__(self, *args, **kwargs): + super(self.__class__, self).__init__(*args, **kwargs) + self.century_specified = False + self.dstridx = None + self.mstridx = None + self.ystridx = None + + @property + def has_year(self): + return self.ystridx is not None + + @property + def has_month(self): + return self.mstridx is not None + + @property + def has_day(self): + return self.dstridx is not None + + def could_be_day(self, value): + if self.has_day: + return False + elif not self.has_month: + return 1 <= value <= 31 + elif not self.has_year: + # Be permissive, assume leap year + month = self[self.mstridx] + return 1 <= value <= monthrange(2000, month)[1] + else: + month = self[self.mstridx] + year = self[self.ystridx] + return 1 <= value <= monthrange(year, month)[1] + + def append(self, val, label=None): + if hasattr(val, '__len__'): + if val.isdigit() and len(val) > 2: + self.century_specified = True + if label not in [None, 'Y']: # pragma: no cover + raise ValueError(label) + label = 'Y' + elif val > 100: + self.century_specified = True + if label not in [None, 'Y']: # pragma: no cover + raise ValueError(label) + label = 'Y' + + super(self.__class__, self).append(int(val)) + + if label == 'M': + if self.has_month: + raise ValueError('Month is already set') + self.mstridx = len(self) - 1 + elif label == 'D': + if self.has_day: + raise ValueError('Day is already set') + self.dstridx = len(self) - 1 + elif label == 'Y': + if self.has_year: + raise ValueError('Year is already set') + self.ystridx = len(self) - 1 + + def _resolve_from_stridxs(self, strids): + """ + Try to resolve the identities of year/month/day elements using + ystridx, mstridx, and dstridx, if enough of these are specified. + """ + if len(self) == 3 and len(strids) == 2: + # we can back out the remaining stridx value + missing = [x for x in range(3) if x not in strids.values()] + key = [x for x in ['y', 'm', 'd'] if x not in strids] + assert len(missing) == len(key) == 1 + key = key[0] + val = missing[0] + strids[key] = val + + assert len(self) == len(strids) # otherwise this should not be called + out = {key: self[strids[key]] for key in strids} + return (out.get('y'), out.get('m'), out.get('d')) + + def resolve_ymd(self, yearfirst, dayfirst): + len_ymd = len(self) + year, month, day = (None, None, None) + + strids = (('y', self.ystridx), + ('m', self.mstridx), + ('d', self.dstridx)) + + strids = {key: val for key, val in strids if val is not None} + if (len(self) == len(strids) > 0 or + (len(self) == 3 and len(strids) == 2)): + return self._resolve_from_stridxs(strids) + + mstridx = self.mstridx + + if len_ymd > 3: + raise ValueError("More than three YMD values") + elif len_ymd == 1 or (mstridx is not None and len_ymd == 2): + # One member, or two members with a month string + if mstridx is not None: + month = self[mstridx] + # since mstridx is 0 or 1, self[mstridx-1] always + # looks up the other element + other = self[mstridx - 1] + else: + other = self[0] + + if len_ymd > 1 or mstridx is None: + if other > 31: + year = other + else: + day = other + + elif len_ymd == 2: + # Two members with numbers + if self[0] > 31: + # 99-01 + year, month = self + elif self[1] > 31: + # 01-99 + month, year = self + elif dayfirst and self[1] <= 12: + # 13-01 + day, month = self + else: + # 01-13 + month, day = self + + elif len_ymd == 3: + # Three members + if mstridx == 0: + if self[1] > 31: + # Apr-2003-25 + month, year, day = self + else: + month, day, year = self + elif mstridx == 1: + if self[0] > 31 or (yearfirst and self[2] <= 31): + # 99-Jan-01 + year, month, day = self + else: + # 01-Jan-01 + # Give precedence to day-first, since + # two-digit years is usually hand-written. + day, month, year = self + + elif mstridx == 2: + # WTF!? + if self[1] > 31: + # 01-99-Jan + day, year, month = self + else: + # 99-01-Jan + year, day, month = self + + else: + if (self[0] > 31 or + self.ystridx == 0 or + (yearfirst and self[1] <= 12 and self[2] <= 31)): + # 99-01-01 + if dayfirst and self[2] <= 12: + year, day, month = self + else: + year, month, day = self + elif self[0] > 12 or (dayfirst and self[1] <= 12): + # 13-01-01 + day, month, year = self + else: + # 01-13-01 + month, day, year = self + + return year, month, day + + +class parser(object): + def __init__(self, info=None): + self.info = info or parserinfo() + + def parse(self, timestr, default=None, + ignoretz=False, tzinfos=None, **kwargs): + """ + Parse the date/time string into a :class:`datetime.datetime` object. + + :param timestr: + Any date/time string using the supported formats. + + :param default: + The default datetime object, if this is a datetime object and not + ``None``, elements specified in ``timestr`` replace elements in the + default object. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a + naive :class:`datetime.datetime` object is returned. + + :param tzinfos: + Additional time zone names / aliases which may be present in the + string. This argument maps time zone names (and optionally offsets + from those time zones) to time zones. This parameter can be a + dictionary with timezone aliases mapping time zone names to time + zones or a function taking two parameters (``tzname`` and + ``tzoffset``) and returning a time zone. + + The timezones to which the names are mapped can be an integer + offset from UTC in seconds or a :class:`tzinfo` object. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> from dateutil.parser import parse + >>> from dateutil.tz import gettz + >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")} + >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200)) + >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, + tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) + + This parameter is ignored if ``ignoretz`` is set. + + :param \\*\\*kwargs: + Keyword arguments as passed to ``_parse()``. + + :return: + Returns a :class:`datetime.datetime` object or, if the + ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the + first element being a :class:`datetime.datetime` object, the second + a tuple containing the fuzzy tokens. + + :raises ParserError: + Raised for invalid or unknown string format, if the provided + :class:`tzinfo` is not in a valid format, or if an invalid date + would be created. + + :raises TypeError: + Raised for non-string or character stream input. + + :raises OverflowError: + Raised if the parsed date exceeds the largest valid C integer on + your system. + """ + + if default is None: + default = datetime.datetime.now().replace(hour=0, minute=0, + second=0, microsecond=0) + + res, skipped_tokens = self._parse(timestr, **kwargs) + + if res is None: + raise ParserError("Unknown string format: %s", timestr) + + if len(res) == 0: + raise ParserError("String does not contain a date: %s", timestr) + + try: + ret = self._build_naive(res, default) + except ValueError as e: + six.raise_from(ParserError(e.args[0] + ": %s", timestr), e) + + if not ignoretz: + ret = self._build_tzaware(ret, res, tzinfos) + + if kwargs.get('fuzzy_with_tokens', False): + return ret, skipped_tokens + else: + return ret + + class _result(_resultbase): + __slots__ = ["year", "month", "day", "weekday", + "hour", "minute", "second", "microsecond", + "tzname", "tzoffset", "ampm","any_unused_tokens"] + + def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False, + fuzzy_with_tokens=False): + """ + Private method which performs the heavy lifting of parsing, called from + ``parse()``, which passes on its ``kwargs`` to this function. + + :param timestr: + The string to parse. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM + and YMD. If set to ``None``, this value is retrieved from the + current :class:`parserinfo` object (which itself defaults to + ``False``). + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken + to be the year, otherwise the last number is taken to be the year. + If this is set to ``None``, the value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param fuzzy: + Whether to allow fuzzy parsing, allowing for string like "Today is + January 1, 2047 at 8:21:00AM". + + :param fuzzy_with_tokens: + If ``True``, ``fuzzy`` is automatically set to True, and the parser + will return a tuple where the first element is the parsed + :class:`datetime.datetime` datetimestamp and the second element is + a tuple containing the portions of the string which were ignored: + + .. doctest:: + + >>> from dateutil.parser import parse + >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) + (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) + + """ + if fuzzy_with_tokens: + fuzzy = True + + info = self.info + + if dayfirst is None: + dayfirst = info.dayfirst + + if yearfirst is None: + yearfirst = info.yearfirst + + res = self._result() + l = _timelex.split(timestr) # Splits the timestr into tokens + + skipped_idxs = [] + + # year/month/day list + ymd = _ymd() + + len_l = len(l) + i = 0 + try: + while i < len_l: + + # Check if it's a number + value_repr = l[i] + try: + value = float(value_repr) + except ValueError: + value = None + + if value is not None: + # Numeric token + i = self._parse_numeric_token(l, i, info, ymd, res, fuzzy) + + # Check weekday + elif info.weekday(l[i]) is not None: + value = info.weekday(l[i]) + res.weekday = value + + # Check month name + elif info.month(l[i]) is not None: + value = info.month(l[i]) + ymd.append(value, 'M') + + if i + 1 < len_l: + if l[i + 1] in ('-', '/'): + # Jan-01[-99] + sep = l[i + 1] + ymd.append(l[i + 2]) + + if i + 3 < len_l and l[i + 3] == sep: + # Jan-01-99 + ymd.append(l[i + 4]) + i += 2 + + i += 2 + + elif (i + 4 < len_l and l[i + 1] == l[i + 3] == ' ' and + info.pertain(l[i + 2])): + # Jan of 01 + # In this case, 01 is clearly year + if l[i + 4].isdigit(): + # Convert it here to become unambiguous + value = int(l[i + 4]) + year = str(info.convertyear(value)) + ymd.append(year, 'Y') + else: + # Wrong guess + pass + # TODO: not hit in tests + i += 4 + + # Check am/pm + elif info.ampm(l[i]) is not None: + value = info.ampm(l[i]) + val_is_ampm = self._ampm_valid(res.hour, res.ampm, fuzzy) + + if val_is_ampm: + res.hour = self._adjust_ampm(res.hour, value) + res.ampm = value + + elif fuzzy: + skipped_idxs.append(i) + + # Check for a timezone name + elif self._could_be_tzname(res.hour, res.tzname, res.tzoffset, l[i]): + res.tzname = l[i] + res.tzoffset = info.tzoffset(res.tzname) + + # Check for something like GMT+3, or BRST+3. Notice + # that it doesn't mean "I am 3 hours after GMT", but + # "my time +3 is GMT". If found, we reverse the + # logic so that timezone parsing code will get it + # right. + if i + 1 < len_l and l[i + 1] in ('+', '-'): + l[i + 1] = ('+', '-')[l[i + 1] == '+'] + res.tzoffset = None + if info.utczone(res.tzname): + # With something like GMT+3, the timezone + # is *not* GMT. + res.tzname = None + + # Check for a numbered timezone + elif res.hour is not None and l[i] in ('+', '-'): + signal = (-1, 1)[l[i] == '+'] + len_li = len(l[i + 1]) + + # TODO: check that l[i + 1] is integer? + if len_li == 4: + # -0300 + hour_offset = int(l[i + 1][:2]) + min_offset = int(l[i + 1][2:]) + elif i + 2 < len_l and l[i + 2] == ':': + # -03:00 + hour_offset = int(l[i + 1]) + min_offset = int(l[i + 3]) # TODO: Check that l[i+3] is minute-like? + i += 2 + elif len_li <= 2: + # -[0]3 + hour_offset = int(l[i + 1][:2]) + min_offset = 0 + else: + raise ValueError(timestr) + + res.tzoffset = signal * (hour_offset * 3600 + min_offset * 60) + + # Look for a timezone name between parenthesis + if (i + 5 < len_l and + info.jump(l[i + 2]) and l[i + 3] == '(' and + l[i + 5] == ')' and + 3 <= len(l[i + 4]) and + self._could_be_tzname(res.hour, res.tzname, + None, l[i + 4])): + # -0300 (BRST) + res.tzname = l[i + 4] + i += 4 + + i += 1 + + # Check jumps + elif not (info.jump(l[i]) or fuzzy): + raise ValueError(timestr) + + else: + skipped_idxs.append(i) + i += 1 + + # Process year/month/day + year, month, day = ymd.resolve_ymd(yearfirst, dayfirst) + + res.century_specified = ymd.century_specified + res.year = year + res.month = month + res.day = day + + except (IndexError, ValueError): + return None, None + + if not info.validate(res): + return None, None + + if fuzzy_with_tokens: + skipped_tokens = self._recombine_skipped(l, skipped_idxs) + return res, tuple(skipped_tokens) + else: + return res, None + + def _parse_numeric_token(self, tokens, idx, info, ymd, res, fuzzy): + # Token is a number + value_repr = tokens[idx] + try: + value = self._to_decimal(value_repr) + except Exception as e: + six.raise_from(ValueError('Unknown numeric token'), e) + + len_li = len(value_repr) + + len_l = len(tokens) + + if (len(ymd) == 3 and len_li in (2, 4) and + res.hour is None and + (idx + 1 >= len_l or + (tokens[idx + 1] != ':' and + info.hms(tokens[idx + 1]) is None))): + # 19990101T23[59] + s = tokens[idx] + res.hour = int(s[:2]) + + if len_li == 4: + res.minute = int(s[2:]) + + elif len_li == 6 or (len_li > 6 and tokens[idx].find('.') == 6): + # YYMMDD or HHMMSS[.ss] + s = tokens[idx] + + if not ymd and '.' not in tokens[idx]: + ymd.append(s[:2]) + ymd.append(s[2:4]) + ymd.append(s[4:]) + else: + # 19990101T235959[.59] + + # TODO: Check if res attributes already set. + res.hour = int(s[:2]) + res.minute = int(s[2:4]) + res.second, res.microsecond = self._parsems(s[4:]) + + elif len_li in (8, 12, 14): + # YYYYMMDD + s = tokens[idx] + ymd.append(s[:4], 'Y') + ymd.append(s[4:6]) + ymd.append(s[6:8]) + + if len_li > 8: + res.hour = int(s[8:10]) + res.minute = int(s[10:12]) + + if len_li > 12: + res.second = int(s[12:]) + + elif self._find_hms_idx(idx, tokens, info, allow_jump=True) is not None: + # HH[ ]h or MM[ ]m or SS[.ss][ ]s + hms_idx = self._find_hms_idx(idx, tokens, info, allow_jump=True) + (idx, hms) = self._parse_hms(idx, tokens, info, hms_idx) + if hms is not None: + # TODO: checking that hour/minute/second are not + # already set? + self._assign_hms(res, value_repr, hms) + + elif idx + 2 < len_l and tokens[idx + 1] == ':': + # HH:MM[:SS[.ss]] + res.hour = int(value) + value = self._to_decimal(tokens[idx + 2]) # TODO: try/except for this? + (res.minute, res.second) = self._parse_min_sec(value) + + if idx + 4 < len_l and tokens[idx + 3] == ':': + res.second, res.microsecond = self._parsems(tokens[idx + 4]) + + idx += 2 + + idx += 2 + + elif idx + 1 < len_l and tokens[idx + 1] in ('-', '/', '.'): + sep = tokens[idx + 1] + ymd.append(value_repr) + + if idx + 2 < len_l and not info.jump(tokens[idx + 2]): + if tokens[idx + 2].isdigit(): + # 01-01[-01] + ymd.append(tokens[idx + 2]) + else: + # 01-Jan[-01] + value = info.month(tokens[idx + 2]) + + if value is not None: + ymd.append(value, 'M') + else: + raise ValueError() + + if idx + 3 < len_l and tokens[idx + 3] == sep: + # We have three members + value = info.month(tokens[idx + 4]) + + if value is not None: + ymd.append(value, 'M') + else: + ymd.append(tokens[idx + 4]) + idx += 2 + + idx += 1 + idx += 1 + + elif idx + 1 >= len_l or info.jump(tokens[idx + 1]): + if idx + 2 < len_l and info.ampm(tokens[idx + 2]) is not None: + # 12 am + hour = int(value) + res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 2])) + idx += 1 + else: + # Year, month or day + ymd.append(value) + idx += 1 + + elif info.ampm(tokens[idx + 1]) is not None and (0 <= value < 24): + # 12am + hour = int(value) + res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 1])) + idx += 1 + + elif ymd.could_be_day(value): + ymd.append(value) + + elif not fuzzy: + raise ValueError() + + return idx + + def _find_hms_idx(self, idx, tokens, info, allow_jump): + len_l = len(tokens) + + if idx+1 < len_l and info.hms(tokens[idx+1]) is not None: + # There is an "h", "m", or "s" label following this token. We take + # assign the upcoming label to the current token. + # e.g. the "12" in 12h" + hms_idx = idx + 1 + + elif (allow_jump and idx+2 < len_l and tokens[idx+1] == ' ' and + info.hms(tokens[idx+2]) is not None): + # There is a space and then an "h", "m", or "s" label. + # e.g. the "12" in "12 h" + hms_idx = idx + 2 + + elif idx > 0 and info.hms(tokens[idx-1]) is not None: + # There is a "h", "m", or "s" preceding this token. Since neither + # of the previous cases was hit, there is no label following this + # token, so we use the previous label. + # e.g. the "04" in "12h04" + hms_idx = idx-1 + + elif (1 < idx == len_l-1 and tokens[idx-1] == ' ' and + info.hms(tokens[idx-2]) is not None): + # If we are looking at the final token, we allow for a + # backward-looking check to skip over a space. + # TODO: Are we sure this is the right condition here? + hms_idx = idx - 2 + + else: + hms_idx = None + + return hms_idx + + def _assign_hms(self, res, value_repr, hms): + # See GH issue #427, fixing float rounding + value = self._to_decimal(value_repr) + + if hms == 0: + # Hour + res.hour = int(value) + if value % 1: + res.minute = int(60*(value % 1)) + + elif hms == 1: + (res.minute, res.second) = self._parse_min_sec(value) + + elif hms == 2: + (res.second, res.microsecond) = self._parsems(value_repr) + + def _could_be_tzname(self, hour, tzname, tzoffset, token): + return (hour is not None and + tzname is None and + tzoffset is None and + len(token) <= 5 and + (all(x in string.ascii_uppercase for x in token) + or token in self.info.UTCZONE)) + + def _ampm_valid(self, hour, ampm, fuzzy): + """ + For fuzzy parsing, 'a' or 'am' (both valid English words) + may erroneously trigger the AM/PM flag. Deal with that + here. + """ + val_is_ampm = True + + # If there's already an AM/PM flag, this one isn't one. + if fuzzy and ampm is not None: + val_is_ampm = False + + # If AM/PM is found and hour is not, raise a ValueError + if hour is None: + if fuzzy: + val_is_ampm = False + else: + raise ValueError('No hour specified with AM or PM flag.') + elif not 0 <= hour <= 12: + # If AM/PM is found, it's a 12 hour clock, so raise + # an error for invalid range + if fuzzy: + val_is_ampm = False + else: + raise ValueError('Invalid hour specified for 12-hour clock.') + + return val_is_ampm + + def _adjust_ampm(self, hour, ampm): + if hour < 12 and ampm == 1: + hour += 12 + elif hour == 12 and ampm == 0: + hour = 0 + return hour + + def _parse_min_sec(self, value): + # TODO: Every usage of this function sets res.second to the return + # value. Are there any cases where second will be returned as None and + # we *don't* want to set res.second = None? + minute = int(value) + second = None + + sec_remainder = value % 1 + if sec_remainder: + second = int(60 * sec_remainder) + return (minute, second) + + def _parse_hms(self, idx, tokens, info, hms_idx): + # TODO: Is this going to admit a lot of false-positives for when we + # just happen to have digits and "h", "m" or "s" characters in non-date + # text? I guess hex hashes won't have that problem, but there's plenty + # of random junk out there. + if hms_idx is None: + hms = None + new_idx = idx + elif hms_idx > idx: + hms = info.hms(tokens[hms_idx]) + new_idx = hms_idx + else: + # Looking backwards, increment one. + hms = info.hms(tokens[hms_idx]) + 1 + new_idx = idx + + return (new_idx, hms) + + # ------------------------------------------------------------------ + # Handling for individual tokens. These are kept as methods instead + # of functions for the sake of customizability via subclassing. + + def _parsems(self, value): + """Parse a I[.F] seconds value into (seconds, microseconds).""" + if "." not in value: + return int(value), 0 + else: + i, f = value.split(".") + return int(i), int(f.ljust(6, "0")[:6]) + + def _to_decimal(self, val): + try: + decimal_value = Decimal(val) + # See GH 662, edge case, infinite value should not be converted + # via `_to_decimal` + if not decimal_value.is_finite(): + raise ValueError("Converted decimal value is infinite or NaN") + except Exception as e: + msg = "Could not convert %s to decimal" % val + six.raise_from(ValueError(msg), e) + else: + return decimal_value + + # ------------------------------------------------------------------ + # Post-Parsing construction of datetime output. These are kept as + # methods instead of functions for the sake of customizability via + # subclassing. + + def _build_tzinfo(self, tzinfos, tzname, tzoffset): + if callable(tzinfos): + tzdata = tzinfos(tzname, tzoffset) + else: + tzdata = tzinfos.get(tzname) + # handle case where tzinfo is paased an options that returns None + # eg tzinfos = {'BRST' : None} + if isinstance(tzdata, datetime.tzinfo) or tzdata is None: + tzinfo = tzdata + elif isinstance(tzdata, text_type): + tzinfo = tz.tzstr(tzdata) + elif isinstance(tzdata, integer_types): + tzinfo = tz.tzoffset(tzname, tzdata) + else: + raise TypeError("Offset must be tzinfo subclass, tz string, " + "or int offset.") + return tzinfo + + def _build_tzaware(self, naive, res, tzinfos): + if (callable(tzinfos) or (tzinfos and res.tzname in tzinfos)): + tzinfo = self._build_tzinfo(tzinfos, res.tzname, res.tzoffset) + aware = naive.replace(tzinfo=tzinfo) + aware = self._assign_tzname(aware, res.tzname) + + elif res.tzname and res.tzname in time.tzname: + aware = naive.replace(tzinfo=tz.tzlocal()) + + # Handle ambiguous local datetime + aware = self._assign_tzname(aware, res.tzname) + + # This is mostly relevant for winter GMT zones parsed in the UK + if (aware.tzname() != res.tzname and + res.tzname in self.info.UTCZONE): + aware = aware.replace(tzinfo=tz.UTC) + + elif res.tzoffset == 0: + aware = naive.replace(tzinfo=tz.UTC) + + elif res.tzoffset: + aware = naive.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) + + elif not res.tzname and not res.tzoffset: + # i.e. no timezone information was found. + aware = naive + + elif res.tzname: + # tz-like string was parsed but we don't know what to do + # with it + warnings.warn("tzname {tzname} identified but not understood. " + "Pass `tzinfos` argument in order to correctly " + "return a timezone-aware datetime. In a future " + "version, this will raise an " + "exception.".format(tzname=res.tzname), + category=UnknownTimezoneWarning) + aware = naive + + return aware + + def _build_naive(self, res, default): + repl = {} + for attr in ("year", "month", "day", "hour", + "minute", "second", "microsecond"): + value = getattr(res, attr) + if value is not None: + repl[attr] = value + + if 'day' not in repl: + # If the default day exceeds the last day of the month, fall back + # to the end of the month. + cyear = default.year if res.year is None else res.year + cmonth = default.month if res.month is None else res.month + cday = default.day if res.day is None else res.day + + if cday > monthrange(cyear, cmonth)[1]: + repl['day'] = monthrange(cyear, cmonth)[1] + + naive = default.replace(**repl) + + if res.weekday is not None and not res.day: + naive = naive + relativedelta.relativedelta(weekday=res.weekday) + + return naive + + def _assign_tzname(self, dt, tzname): + if dt.tzname() != tzname: + new_dt = tz.enfold(dt, fold=1) + if new_dt.tzname() == tzname: + return new_dt + + return dt + + def _recombine_skipped(self, tokens, skipped_idxs): + """ + >>> tokens = ["foo", " ", "bar", " ", "19June2000", "baz"] + >>> skipped_idxs = [0, 1, 2, 5] + >>> _recombine_skipped(tokens, skipped_idxs) + ["foo bar", "baz"] + """ + skipped_tokens = [] + for i, idx in enumerate(sorted(skipped_idxs)): + if i > 0 and idx - 1 == skipped_idxs[i - 1]: + skipped_tokens[-1] = skipped_tokens[-1] + tokens[idx] + else: + skipped_tokens.append(tokens[idx]) + + return skipped_tokens + + +DEFAULTPARSER = parser() + + +def parse(timestr, parserinfo=None, **kwargs): + """ + + Parse a string in one of the supported formats, using the + ``parserinfo`` parameters. + + :param timestr: + A string containing a date/time stamp. + + :param parserinfo: + A :class:`parserinfo` object containing parameters for the parser. + If ``None``, the default arguments to the :class:`parserinfo` + constructor are used. + + The ``**kwargs`` parameter takes the following keyword arguments: + + :param default: + The default datetime object, if this is a datetime object and not + ``None``, elements specified in ``timestr`` replace elements in the + default object. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a naive + :class:`datetime` object is returned. + + :param tzinfos: + Additional time zone names / aliases which may be present in the + string. This argument maps time zone names (and optionally offsets + from those time zones) to time zones. This parameter can be a + dictionary with timezone aliases mapping time zone names to time + zones or a function taking two parameters (``tzname`` and + ``tzoffset``) and returning a time zone. + + The timezones to which the names are mapped can be an integer + offset from UTC in seconds or a :class:`tzinfo` object. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> from dateutil.parser import parse + >>> from dateutil.tz import gettz + >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")} + >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200)) + >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, + tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) + + This parameter is ignored if ``ignoretz`` is set. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM and + YMD. If set to ``None``, this value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken to + be the year, otherwise the last number is taken to be the year. If + this is set to ``None``, the value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param fuzzy: + Whether to allow fuzzy parsing, allowing for string like "Today is + January 1, 2047 at 8:21:00AM". + + :param fuzzy_with_tokens: + If ``True``, ``fuzzy`` is automatically set to True, and the parser + will return a tuple where the first element is the parsed + :class:`datetime.datetime` datetimestamp and the second element is + a tuple containing the portions of the string which were ignored: + + .. doctest:: + + >>> from dateutil.parser import parse + >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) + (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) + + :return: + Returns a :class:`datetime.datetime` object or, if the + ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the + first element being a :class:`datetime.datetime` object, the second + a tuple containing the fuzzy tokens. + + :raises ValueError: + Raised for invalid or unknown string format, if the provided + :class:`tzinfo` is not in a valid format, or if an invalid date + would be created. + + :raises OverflowError: + Raised if the parsed date exceeds the largest valid C integer on + your system. + """ + if parserinfo: + return parser(parserinfo).parse(timestr, **kwargs) + else: + return DEFAULTPARSER.parse(timestr, **kwargs) + + +class _tzparser(object): + + class _result(_resultbase): + + __slots__ = ["stdabbr", "stdoffset", "dstabbr", "dstoffset", + "start", "end"] + + class _attr(_resultbase): + __slots__ = ["month", "week", "weekday", + "yday", "jyday", "day", "time"] + + def __repr__(self): + return self._repr("") + + def __init__(self): + _resultbase.__init__(self) + self.start = self._attr() + self.end = self._attr() + + def parse(self, tzstr): + res = self._result() + l = [x for x in re.split(r'([,:.]|[a-zA-Z]+|[0-9]+)',tzstr) if x] + used_idxs = list() + try: + + len_l = len(l) + + i = 0 + while i < len_l: + # BRST+3[BRDT[+2]] + j = i + while j < len_l and not [x for x in l[j] + if x in "0123456789:,-+"]: + j += 1 + if j != i: + if not res.stdabbr: + offattr = "stdoffset" + res.stdabbr = "".join(l[i:j]) + else: + offattr = "dstoffset" + res.dstabbr = "".join(l[i:j]) + + for ii in range(j): + used_idxs.append(ii) + i = j + if (i < len_l and (l[i] in ('+', '-') or l[i][0] in + "0123456789")): + if l[i] in ('+', '-'): + # Yes, that's right. See the TZ variable + # documentation. + signal = (1, -1)[l[i] == '+'] + used_idxs.append(i) + i += 1 + else: + signal = -1 + len_li = len(l[i]) + if len_li == 4: + # -0300 + setattr(res, offattr, (int(l[i][:2]) * 3600 + + int(l[i][2:]) * 60) * signal) + elif i + 1 < len_l and l[i + 1] == ':': + # -03:00 + setattr(res, offattr, + (int(l[i]) * 3600 + + int(l[i + 2]) * 60) * signal) + used_idxs.append(i) + i += 2 + elif len_li <= 2: + # -[0]3 + setattr(res, offattr, + int(l[i][:2]) * 3600 * signal) + else: + return None + used_idxs.append(i) + i += 1 + if res.dstabbr: + break + else: + break + + + if i < len_l: + for j in range(i, len_l): + if l[j] == ';': + l[j] = ',' + + assert l[i] == ',' + + i += 1 + + if i >= len_l: + pass + elif (8 <= l.count(',') <= 9 and + not [y for x in l[i:] if x != ',' + for y in x if y not in "0123456789+-"]): + # GMT0BST,3,0,30,3600,10,0,26,7200[,3600] + for x in (res.start, res.end): + x.month = int(l[i]) + used_idxs.append(i) + i += 2 + if l[i] == '-': + value = int(l[i + 1]) * -1 + used_idxs.append(i) + i += 1 + else: + value = int(l[i]) + used_idxs.append(i) + i += 2 + if value: + x.week = value + x.weekday = (int(l[i]) - 1) % 7 + else: + x.day = int(l[i]) + used_idxs.append(i) + i += 2 + x.time = int(l[i]) + used_idxs.append(i) + i += 2 + if i < len_l: + if l[i] in ('-', '+'): + signal = (-1, 1)[l[i] == "+"] + used_idxs.append(i) + i += 1 + else: + signal = 1 + used_idxs.append(i) + res.dstoffset = (res.stdoffset + int(l[i]) * signal) + + # This was a made-up format that is not in normal use + warn(('Parsed time zone "%s"' % tzstr) + + 'is in a non-standard dateutil-specific format, which ' + + 'is now deprecated; support for parsing this format ' + + 'will be removed in future versions. It is recommended ' + + 'that you switch to a standard format like the GNU ' + + 'TZ variable format.', tz.DeprecatedTzFormatWarning) + elif (l.count(',') == 2 and l[i:].count('/') <= 2 and + not [y for x in l[i:] if x not in (',', '/', 'J', 'M', + '.', '-', ':') + for y in x if y not in "0123456789"]): + for x in (res.start, res.end): + if l[i] == 'J': + # non-leap year day (1 based) + used_idxs.append(i) + i += 1 + x.jyday = int(l[i]) + elif l[i] == 'M': + # month[-.]week[-.]weekday + used_idxs.append(i) + i += 1 + x.month = int(l[i]) + used_idxs.append(i) + i += 1 + assert l[i] in ('-', '.') + used_idxs.append(i) + i += 1 + x.week = int(l[i]) + if x.week == 5: + x.week = -1 + used_idxs.append(i) + i += 1 + assert l[i] in ('-', '.') + used_idxs.append(i) + i += 1 + x.weekday = (int(l[i]) - 1) % 7 + else: + # year day (zero based) + x.yday = int(l[i]) + 1 + + used_idxs.append(i) + i += 1 + + if i < len_l and l[i] == '/': + used_idxs.append(i) + i += 1 + # start time + len_li = len(l[i]) + if len_li == 4: + # -0300 + x.time = (int(l[i][:2]) * 3600 + + int(l[i][2:]) * 60) + elif i + 1 < len_l and l[i + 1] == ':': + # -03:00 + x.time = int(l[i]) * 3600 + int(l[i + 2]) * 60 + used_idxs.append(i) + i += 2 + if i + 1 < len_l and l[i + 1] == ':': + used_idxs.append(i) + i += 2 + x.time += int(l[i]) + elif len_li <= 2: + # -[0]3 + x.time = (int(l[i][:2]) * 3600) + else: + return None + used_idxs.append(i) + i += 1 + + assert i == len_l or l[i] == ',' + + i += 1 + + assert i >= len_l + + except (IndexError, ValueError, AssertionError): + return None + + unused_idxs = set(range(len_l)).difference(used_idxs) + res.any_unused_tokens = not {l[n] for n in unused_idxs}.issubset({",",":"}) + return res + + +DEFAULTTZPARSER = _tzparser() + + +def _parsetz(tzstr): + return DEFAULTTZPARSER.parse(tzstr) + + +class ParserError(ValueError): + """Error class for representing failure to parse a datetime string.""" + def __str__(self): + try: + return self.args[0] % self.args[1:] + except (TypeError, IndexError): + return super(ParserError, self).__str__() + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, str(self)) + + +class UnknownTimezoneWarning(RuntimeWarning): + """Raised when the parser finds a timezone it cannot parse into a tzinfo""" +# vim:ts=4:sw=4:et diff --git a/bots-venv/lib/python3.7/site-packages/dateutil/parser/isoparser.py b/bots-venv/lib/python3.7/site-packages/dateutil/parser/isoparser.py new file mode 100644 index 0000000..48f86a3 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/dateutil/parser/isoparser.py @@ -0,0 +1,411 @@ +# -*- coding: utf-8 -*- +""" +This module offers a parser for ISO-8601 strings + +It is intended to support all valid date, time and datetime formats per the +ISO-8601 specification. + +..versionadded:: 2.7.0 +""" +from datetime import datetime, timedelta, time, date +import calendar +from dateutil import tz + +from functools import wraps + +import re +import six + +__all__ = ["isoparse", "isoparser"] + + +def _takes_ascii(f): + @wraps(f) + def func(self, str_in, *args, **kwargs): + # If it's a stream, read the whole thing + str_in = getattr(str_in, 'read', lambda: str_in)() + + # If it's unicode, turn it into bytes, since ISO-8601 only covers ASCII + if isinstance(str_in, six.text_type): + # ASCII is the same in UTF-8 + try: + str_in = str_in.encode('ascii') + except UnicodeEncodeError as e: + msg = 'ISO-8601 strings should contain only ASCII characters' + six.raise_from(ValueError(msg), e) + + return f(self, str_in, *args, **kwargs) + + return func + + +class isoparser(object): + def __init__(self, sep=None): + """ + :param sep: + A single character that separates date and time portions. If + ``None``, the parser will accept any single character. + For strict ISO-8601 adherence, pass ``'T'``. + """ + if sep is not None: + if (len(sep) != 1 or ord(sep) >= 128 or sep in '0123456789'): + raise ValueError('Separator must be a single, non-numeric ' + + 'ASCII character') + + sep = sep.encode('ascii') + + self._sep = sep + + @_takes_ascii + def isoparse(self, dt_str): + """ + Parse an ISO-8601 datetime string into a :class:`datetime.datetime`. + + An ISO-8601 datetime string consists of a date portion, followed + optionally by a time portion - the date and time portions are separated + by a single character separator, which is ``T`` in the official + standard. Incomplete date formats (such as ``YYYY-MM``) may *not* be + combined with a time portion. + + Supported date formats are: + + Common: + + - ``YYYY`` + - ``YYYY-MM`` or ``YYYYMM`` + - ``YYYY-MM-DD`` or ``YYYYMMDD`` + + Uncommon: + + - ``YYYY-Www`` or ``YYYYWww`` - ISO week (day defaults to 0) + - ``YYYY-Www-D`` or ``YYYYWwwD`` - ISO week and day + + The ISO week and day numbering follows the same logic as + :func:`datetime.date.isocalendar`. + + Supported time formats are: + + - ``hh`` + - ``hh:mm`` or ``hhmm`` + - ``hh:mm:ss`` or ``hhmmss`` + - ``hh:mm:ss.ssssss`` (Up to 6 sub-second digits) + + Midnight is a special case for `hh`, as the standard supports both + 00:00 and 24:00 as a representation. The decimal separator can be + either a dot or a comma. + + + .. caution:: + + Support for fractional components other than seconds is part of the + ISO-8601 standard, but is not currently implemented in this parser. + + Supported time zone offset formats are: + + - `Z` (UTC) + - `±HH:MM` + - `±HHMM` + - `±HH` + + Offsets will be represented as :class:`dateutil.tz.tzoffset` objects, + with the exception of UTC, which will be represented as + :class:`dateutil.tz.tzutc`. Time zone offsets equivalent to UTC (such + as `+00:00`) will also be represented as :class:`dateutil.tz.tzutc`. + + :param dt_str: + A string or stream containing only an ISO-8601 datetime string + + :return: + Returns a :class:`datetime.datetime` representing the string. + Unspecified components default to their lowest value. + + .. warning:: + + As of version 2.7.0, the strictness of the parser should not be + considered a stable part of the contract. Any valid ISO-8601 string + that parses correctly with the default settings will continue to + parse correctly in future versions, but invalid strings that + currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not + guaranteed to continue failing in future versions if they encode + a valid date. + + .. versionadded:: 2.7.0 + """ + components, pos = self._parse_isodate(dt_str) + + if len(dt_str) > pos: + if self._sep is None or dt_str[pos:pos + 1] == self._sep: + components += self._parse_isotime(dt_str[pos + 1:]) + else: + raise ValueError('String contains unknown ISO components') + + if len(components) > 3 and components[3] == 24: + components[3] = 0 + return datetime(*components) + timedelta(days=1) + + return datetime(*components) + + @_takes_ascii + def parse_isodate(self, datestr): + """ + Parse the date portion of an ISO string. + + :param datestr: + The string portion of an ISO string, without a separator + + :return: + Returns a :class:`datetime.date` object + """ + components, pos = self._parse_isodate(datestr) + if pos < len(datestr): + raise ValueError('String contains unknown ISO ' + + 'components: {}'.format(datestr)) + return date(*components) + + @_takes_ascii + def parse_isotime(self, timestr): + """ + Parse the time portion of an ISO string. + + :param timestr: + The time portion of an ISO string, without a separator + + :return: + Returns a :class:`datetime.time` object + """ + components = self._parse_isotime(timestr) + if components[0] == 24: + components[0] = 0 + return time(*components) + + @_takes_ascii + def parse_tzstr(self, tzstr, zero_as_utc=True): + """ + Parse a valid ISO time zone string. + + See :func:`isoparser.isoparse` for details on supported formats. + + :param tzstr: + A string representing an ISO time zone offset + + :param zero_as_utc: + Whether to return :class:`dateutil.tz.tzutc` for zero-offset zones + + :return: + Returns :class:`dateutil.tz.tzoffset` for offsets and + :class:`dateutil.tz.tzutc` for ``Z`` and (if ``zero_as_utc`` is + specified) offsets equivalent to UTC. + """ + return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc) + + # Constants + _DATE_SEP = b'-' + _TIME_SEP = b':' + _FRACTION_REGEX = re.compile(b'[\\.,]([0-9]+)') + + def _parse_isodate(self, dt_str): + try: + return self._parse_isodate_common(dt_str) + except ValueError: + return self._parse_isodate_uncommon(dt_str) + + def _parse_isodate_common(self, dt_str): + len_str = len(dt_str) + components = [1, 1, 1] + + if len_str < 4: + raise ValueError('ISO string too short') + + # Year + components[0] = int(dt_str[0:4]) + pos = 4 + if pos >= len_str: + return components, pos + + has_sep = dt_str[pos:pos + 1] == self._DATE_SEP + if has_sep: + pos += 1 + + # Month + if len_str - pos < 2: + raise ValueError('Invalid common month') + + components[1] = int(dt_str[pos:pos + 2]) + pos += 2 + + if pos >= len_str: + if has_sep: + return components, pos + else: + raise ValueError('Invalid ISO format') + + if has_sep: + if dt_str[pos:pos + 1] != self._DATE_SEP: + raise ValueError('Invalid separator in ISO string') + pos += 1 + + # Day + if len_str - pos < 2: + raise ValueError('Invalid common day') + components[2] = int(dt_str[pos:pos + 2]) + return components, pos + 2 + + def _parse_isodate_uncommon(self, dt_str): + if len(dt_str) < 4: + raise ValueError('ISO string too short') + + # All ISO formats start with the year + year = int(dt_str[0:4]) + + has_sep = dt_str[4:5] == self._DATE_SEP + + pos = 4 + has_sep # Skip '-' if it's there + if dt_str[pos:pos + 1] == b'W': + # YYYY-?Www-?D? + pos += 1 + weekno = int(dt_str[pos:pos + 2]) + pos += 2 + + dayno = 1 + if len(dt_str) > pos: + if (dt_str[pos:pos + 1] == self._DATE_SEP) != has_sep: + raise ValueError('Inconsistent use of dash separator') + + pos += has_sep + + dayno = int(dt_str[pos:pos + 1]) + pos += 1 + + base_date = self._calculate_weekdate(year, weekno, dayno) + else: + # YYYYDDD or YYYY-DDD + if len(dt_str) - pos < 3: + raise ValueError('Invalid ordinal day') + + ordinal_day = int(dt_str[pos:pos + 3]) + pos += 3 + + if ordinal_day < 1 or ordinal_day > (365 + calendar.isleap(year)): + raise ValueError('Invalid ordinal day' + + ' {} for year {}'.format(ordinal_day, year)) + + base_date = date(year, 1, 1) + timedelta(days=ordinal_day - 1) + + components = [base_date.year, base_date.month, base_date.day] + return components, pos + + def _calculate_weekdate(self, year, week, day): + """ + Calculate the day of corresponding to the ISO year-week-day calendar. + + This function is effectively the inverse of + :func:`datetime.date.isocalendar`. + + :param year: + The year in the ISO calendar + + :param week: + The week in the ISO calendar - range is [1, 53] + + :param day: + The day in the ISO calendar - range is [1 (MON), 7 (SUN)] + + :return: + Returns a :class:`datetime.date` + """ + if not 0 < week < 54: + raise ValueError('Invalid week: {}'.format(week)) + + if not 0 < day < 8: # Range is 1-7 + raise ValueError('Invalid weekday: {}'.format(day)) + + # Get week 1 for the specific year: + jan_4 = date(year, 1, 4) # Week 1 always has January 4th in it + week_1 = jan_4 - timedelta(days=jan_4.isocalendar()[2] - 1) + + # Now add the specific number of weeks and days to get what we want + week_offset = (week - 1) * 7 + (day - 1) + return week_1 + timedelta(days=week_offset) + + def _parse_isotime(self, timestr): + len_str = len(timestr) + components = [0, 0, 0, 0, None] + pos = 0 + comp = -1 + + if len(timestr) < 2: + raise ValueError('ISO time too short') + + has_sep = len_str >= 3 and timestr[2:3] == self._TIME_SEP + + while pos < len_str and comp < 5: + comp += 1 + + if timestr[pos:pos + 1] in b'-+Zz': + # Detect time zone boundary + components[-1] = self._parse_tzstr(timestr[pos:]) + pos = len_str + break + + if comp < 3: + # Hour, minute, second + components[comp] = int(timestr[pos:pos + 2]) + pos += 2 + if (has_sep and pos < len_str and + timestr[pos:pos + 1] == self._TIME_SEP): + pos += 1 + + if comp == 3: + # Fraction of a second + frac = self._FRACTION_REGEX.match(timestr[pos:]) + if not frac: + continue + + us_str = frac.group(1)[:6] # Truncate to microseconds + components[comp] = int(us_str) * 10**(6 - len(us_str)) + pos += len(frac.group()) + + if pos < len_str: + raise ValueError('Unused components in ISO string') + + if components[0] == 24: + # Standard supports 00:00 and 24:00 as representations of midnight + if any(component != 0 for component in components[1:4]): + raise ValueError('Hour may only be 24 at 24:00:00.000') + + return components + + def _parse_tzstr(self, tzstr, zero_as_utc=True): + if tzstr == b'Z' or tzstr == b'z': + return tz.UTC + + if len(tzstr) not in {3, 5, 6}: + raise ValueError('Time zone offset must be 1, 3, 5 or 6 characters') + + if tzstr[0:1] == b'-': + mult = -1 + elif tzstr[0:1] == b'+': + mult = 1 + else: + raise ValueError('Time zone offset requires sign') + + hours = int(tzstr[1:3]) + if len(tzstr) == 3: + minutes = 0 + else: + minutes = int(tzstr[(4 if tzstr[3:4] == self._TIME_SEP else 3):]) + + if zero_as_utc and hours == 0 and minutes == 0: + return tz.UTC + else: + if minutes > 59: + raise ValueError('Invalid minutes in time zone offset') + + if hours > 23: + raise ValueError('Invalid hours in time zone offset') + + return tz.tzoffset(None, mult * (hours * 60 + minutes) * 60) + + +DEFAULT_ISOPARSER = isoparser() +isoparse = DEFAULT_ISOPARSER.isoparse diff --git a/bots-venv/lib/python3.7/site-packages/dateutil/relativedelta.py b/bots-venv/lib/python3.7/site-packages/dateutil/relativedelta.py new file mode 100644 index 0000000..a9e85f7 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/dateutil/relativedelta.py @@ -0,0 +1,599 @@ +# -*- coding: utf-8 -*- +import datetime +import calendar + +import operator +from math import copysign + +from six import integer_types +from warnings import warn + +from ._common import weekday + +MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) + +__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] + + +class relativedelta(object): + """ + The relativedelta type is designed to be applied to an existing datetime and + can replace specific components of that datetime, or represents an interval + of time. + + It is based on the specification of the excellent work done by M.-A. Lemburg + in his + `mx.DateTime `_ extension. + However, notice that this type does *NOT* implement the same algorithm as + his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. + + There are two different ways to build a relativedelta instance. The + first one is passing it two date/datetime classes:: + + relativedelta(datetime1, datetime2) + + The second one is passing it any number of the following keyword arguments:: + + relativedelta(arg1=x,arg2=y,arg3=z...) + + year, month, day, hour, minute, second, microsecond: + Absolute information (argument is singular); adding or subtracting a + relativedelta with absolute information does not perform an arithmetic + operation, but rather REPLACES the corresponding value in the + original datetime with the value(s) in relativedelta. + + years, months, weeks, days, hours, minutes, seconds, microseconds: + Relative information, may be negative (argument is plural); adding + or subtracting a relativedelta with relative information performs + the corresponding arithmetic operation on the original datetime value + with the information in the relativedelta. + + weekday: + One of the weekday instances (MO, TU, etc) available in the + relativedelta module. These instances may receive a parameter N, + specifying the Nth weekday, which could be positive or negative + (like MO(+1) or MO(-2)). Not specifying it is the same as specifying + +1. You can also use an integer, where 0=MO. This argument is always + relative e.g. if the calculated date is already Monday, using MO(1) + or MO(-1) won't change the day. To effectively make it absolute, use + it in combination with the day argument (e.g. day=1, MO(1) for first + Monday of the month). + + leapdays: + Will add given days to the date found, if year is a leap + year, and the date found is post 28 of february. + + yearday, nlyearday: + Set the yearday or the non-leap year day (jump leap days). + These are converted to day/month/leapdays information. + + There are relative and absolute forms of the keyword + arguments. The plural is relative, and the singular is + absolute. For each argument in the order below, the absolute form + is applied first (by setting each attribute to that value) and + then the relative form (by adding the value to the attribute). + + The order of attributes considered when this relativedelta is + added to a datetime is: + + 1. Year + 2. Month + 3. Day + 4. Hours + 5. Minutes + 6. Seconds + 7. Microseconds + + Finally, weekday is applied, using the rule described above. + + For example + + >>> from datetime import datetime + >>> from dateutil.relativedelta import relativedelta, MO + >>> dt = datetime(2018, 4, 9, 13, 37, 0) + >>> delta = relativedelta(hours=25, day=1, weekday=MO(1)) + >>> dt + delta + datetime.datetime(2018, 4, 2, 14, 37) + + First, the day is set to 1 (the first of the month), then 25 hours + are added, to get to the 2nd day and 14th hour, finally the + weekday is applied, but since the 2nd is already a Monday there is + no effect. + + """ + + def __init__(self, dt1=None, dt2=None, + years=0, months=0, days=0, leapdays=0, weeks=0, + hours=0, minutes=0, seconds=0, microseconds=0, + year=None, month=None, day=None, weekday=None, + yearday=None, nlyearday=None, + hour=None, minute=None, second=None, microsecond=None): + + if dt1 and dt2: + # datetime is a subclass of date. So both must be date + if not (isinstance(dt1, datetime.date) and + isinstance(dt2, datetime.date)): + raise TypeError("relativedelta only diffs datetime/date") + + # We allow two dates, or two datetimes, so we coerce them to be + # of the same type + if (isinstance(dt1, datetime.datetime) != + isinstance(dt2, datetime.datetime)): + if not isinstance(dt1, datetime.datetime): + dt1 = datetime.datetime.fromordinal(dt1.toordinal()) + elif not isinstance(dt2, datetime.datetime): + dt2 = datetime.datetime.fromordinal(dt2.toordinal()) + + self.years = 0 + self.months = 0 + self.days = 0 + self.leapdays = 0 + self.hours = 0 + self.minutes = 0 + self.seconds = 0 + self.microseconds = 0 + self.year = None + self.month = None + self.day = None + self.weekday = None + self.hour = None + self.minute = None + self.second = None + self.microsecond = None + self._has_time = 0 + + # Get year / month delta between the two + months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month) + self._set_months(months) + + # Remove the year/month delta so the timedelta is just well-defined + # time units (seconds, days and microseconds) + dtm = self.__radd__(dt2) + + # If we've overshot our target, make an adjustment + if dt1 < dt2: + compare = operator.gt + increment = 1 + else: + compare = operator.lt + increment = -1 + + while compare(dt1, dtm): + months += increment + self._set_months(months) + dtm = self.__radd__(dt2) + + # Get the timedelta between the "months-adjusted" date and dt1 + delta = dt1 - dtm + self.seconds = delta.seconds + delta.days * 86400 + self.microseconds = delta.microseconds + else: + # Check for non-integer values in integer-only quantities + if any(x is not None and x != int(x) for x in (years, months)): + raise ValueError("Non-integer years and months are " + "ambiguous and not currently supported.") + + # Relative information + self.years = int(years) + self.months = int(months) + self.days = days + weeks * 7 + self.leapdays = leapdays + self.hours = hours + self.minutes = minutes + self.seconds = seconds + self.microseconds = microseconds + + # Absolute information + self.year = year + self.month = month + self.day = day + self.hour = hour + self.minute = minute + self.second = second + self.microsecond = microsecond + + if any(x is not None and int(x) != x + for x in (year, month, day, hour, + minute, second, microsecond)): + # For now we'll deprecate floats - later it'll be an error. + warn("Non-integer value passed as absolute information. " + + "This is not a well-defined condition and will raise " + + "errors in future versions.", DeprecationWarning) + + if isinstance(weekday, integer_types): + self.weekday = weekdays[weekday] + else: + self.weekday = weekday + + yday = 0 + if nlyearday: + yday = nlyearday + elif yearday: + yday = yearday + if yearday > 59: + self.leapdays = -1 + if yday: + ydayidx = [31, 59, 90, 120, 151, 181, 212, + 243, 273, 304, 334, 366] + for idx, ydays in enumerate(ydayidx): + if yday <= ydays: + self.month = idx+1 + if idx == 0: + self.day = yday + else: + self.day = yday-ydayidx[idx-1] + break + else: + raise ValueError("invalid year day (%d)" % yday) + + self._fix() + + def _fix(self): + if abs(self.microseconds) > 999999: + s = _sign(self.microseconds) + div, mod = divmod(self.microseconds * s, 1000000) + self.microseconds = mod * s + self.seconds += div * s + if abs(self.seconds) > 59: + s = _sign(self.seconds) + div, mod = divmod(self.seconds * s, 60) + self.seconds = mod * s + self.minutes += div * s + if abs(self.minutes) > 59: + s = _sign(self.minutes) + div, mod = divmod(self.minutes * s, 60) + self.minutes = mod * s + self.hours += div * s + if abs(self.hours) > 23: + s = _sign(self.hours) + div, mod = divmod(self.hours * s, 24) + self.hours = mod * s + self.days += div * s + if abs(self.months) > 11: + s = _sign(self.months) + div, mod = divmod(self.months * s, 12) + self.months = mod * s + self.years += div * s + if (self.hours or self.minutes or self.seconds or self.microseconds + or self.hour is not None or self.minute is not None or + self.second is not None or self.microsecond is not None): + self._has_time = 1 + else: + self._has_time = 0 + + @property + def weeks(self): + return int(self.days / 7.0) + + @weeks.setter + def weeks(self, value): + self.days = self.days - (self.weeks * 7) + value * 7 + + def _set_months(self, months): + self.months = months + if abs(self.months) > 11: + s = _sign(self.months) + div, mod = divmod(self.months * s, 12) + self.months = mod * s + self.years = div * s + else: + self.years = 0 + + def normalized(self): + """ + Return a version of this object represented entirely using integer + values for the relative attributes. + + >>> relativedelta(days=1.5, hours=2).normalized() + relativedelta(days=+1, hours=+14) + + :return: + Returns a :class:`dateutil.relativedelta.relativedelta` object. + """ + # Cascade remainders down (rounding each to roughly nearest microsecond) + days = int(self.days) + + hours_f = round(self.hours + 24 * (self.days - days), 11) + hours = int(hours_f) + + minutes_f = round(self.minutes + 60 * (hours_f - hours), 10) + minutes = int(minutes_f) + + seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8) + seconds = int(seconds_f) + + microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds)) + + # Constructor carries overflow back up with call to _fix() + return self.__class__(years=self.years, months=self.months, + days=days, hours=hours, minutes=minutes, + seconds=seconds, microseconds=microseconds, + leapdays=self.leapdays, year=self.year, + month=self.month, day=self.day, + weekday=self.weekday, hour=self.hour, + minute=self.minute, second=self.second, + microsecond=self.microsecond) + + def __add__(self, other): + if isinstance(other, relativedelta): + return self.__class__(years=other.years + self.years, + months=other.months + self.months, + days=other.days + self.days, + hours=other.hours + self.hours, + minutes=other.minutes + self.minutes, + seconds=other.seconds + self.seconds, + microseconds=(other.microseconds + + self.microseconds), + leapdays=other.leapdays or self.leapdays, + year=(other.year if other.year is not None + else self.year), + month=(other.month if other.month is not None + else self.month), + day=(other.day if other.day is not None + else self.day), + weekday=(other.weekday if other.weekday is not None + else self.weekday), + hour=(other.hour if other.hour is not None + else self.hour), + minute=(other.minute if other.minute is not None + else self.minute), + second=(other.second if other.second is not None + else self.second), + microsecond=(other.microsecond if other.microsecond + is not None else + self.microsecond)) + if isinstance(other, datetime.timedelta): + return self.__class__(years=self.years, + months=self.months, + days=self.days + other.days, + hours=self.hours, + minutes=self.minutes, + seconds=self.seconds + other.seconds, + microseconds=self.microseconds + other.microseconds, + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + if not isinstance(other, datetime.date): + return NotImplemented + elif self._has_time and not isinstance(other, datetime.datetime): + other = datetime.datetime.fromordinal(other.toordinal()) + year = (self.year or other.year)+self.years + month = self.month or other.month + if self.months: + assert 1 <= abs(self.months) <= 12 + month += self.months + if month > 12: + year += 1 + month -= 12 + elif month < 1: + year -= 1 + month += 12 + day = min(calendar.monthrange(year, month)[1], + self.day or other.day) + repl = {"year": year, "month": month, "day": day} + for attr in ["hour", "minute", "second", "microsecond"]: + value = getattr(self, attr) + if value is not None: + repl[attr] = value + days = self.days + if self.leapdays and month > 2 and calendar.isleap(year): + days += self.leapdays + ret = (other.replace(**repl) + + datetime.timedelta(days=days, + hours=self.hours, + minutes=self.minutes, + seconds=self.seconds, + microseconds=self.microseconds)) + if self.weekday: + weekday, nth = self.weekday.weekday, self.weekday.n or 1 + jumpdays = (abs(nth) - 1) * 7 + if nth > 0: + jumpdays += (7 - ret.weekday() + weekday) % 7 + else: + jumpdays += (ret.weekday() - weekday) % 7 + jumpdays *= -1 + ret += datetime.timedelta(days=jumpdays) + return ret + + def __radd__(self, other): + return self.__add__(other) + + def __rsub__(self, other): + return self.__neg__().__radd__(other) + + def __sub__(self, other): + if not isinstance(other, relativedelta): + return NotImplemented # In case the other object defines __rsub__ + return self.__class__(years=self.years - other.years, + months=self.months - other.months, + days=self.days - other.days, + hours=self.hours - other.hours, + minutes=self.minutes - other.minutes, + seconds=self.seconds - other.seconds, + microseconds=self.microseconds - other.microseconds, + leapdays=self.leapdays or other.leapdays, + year=(self.year if self.year is not None + else other.year), + month=(self.month if self.month is not None else + other.month), + day=(self.day if self.day is not None else + other.day), + weekday=(self.weekday if self.weekday is not None else + other.weekday), + hour=(self.hour if self.hour is not None else + other.hour), + minute=(self.minute if self.minute is not None else + other.minute), + second=(self.second if self.second is not None else + other.second), + microsecond=(self.microsecond if self.microsecond + is not None else + other.microsecond)) + + def __abs__(self): + return self.__class__(years=abs(self.years), + months=abs(self.months), + days=abs(self.days), + hours=abs(self.hours), + minutes=abs(self.minutes), + seconds=abs(self.seconds), + microseconds=abs(self.microseconds), + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + def __neg__(self): + return self.__class__(years=-self.years, + months=-self.months, + days=-self.days, + hours=-self.hours, + minutes=-self.minutes, + seconds=-self.seconds, + microseconds=-self.microseconds, + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + def __bool__(self): + return not (not self.years and + not self.months and + not self.days and + not self.hours and + not self.minutes and + not self.seconds and + not self.microseconds and + not self.leapdays and + self.year is None and + self.month is None and + self.day is None and + self.weekday is None and + self.hour is None and + self.minute is None and + self.second is None and + self.microsecond is None) + # Compatibility with Python 2.x + __nonzero__ = __bool__ + + def __mul__(self, other): + try: + f = float(other) + except TypeError: + return NotImplemented + + return self.__class__(years=int(self.years * f), + months=int(self.months * f), + days=int(self.days * f), + hours=int(self.hours * f), + minutes=int(self.minutes * f), + seconds=int(self.seconds * f), + microseconds=int(self.microseconds * f), + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + __rmul__ = __mul__ + + def __eq__(self, other): + if not isinstance(other, relativedelta): + return NotImplemented + if self.weekday or other.weekday: + if not self.weekday or not other.weekday: + return False + if self.weekday.weekday != other.weekday.weekday: + return False + n1, n2 = self.weekday.n, other.weekday.n + if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)): + return False + return (self.years == other.years and + self.months == other.months and + self.days == other.days and + self.hours == other.hours and + self.minutes == other.minutes and + self.seconds == other.seconds and + self.microseconds == other.microseconds and + self.leapdays == other.leapdays and + self.year == other.year and + self.month == other.month and + self.day == other.day and + self.hour == other.hour and + self.minute == other.minute and + self.second == other.second and + self.microsecond == other.microsecond) + + def __hash__(self): + return hash(( + self.weekday, + self.years, + self.months, + self.days, + self.hours, + self.minutes, + self.seconds, + self.microseconds, + self.leapdays, + self.year, + self.month, + self.day, + self.hour, + self.minute, + self.second, + self.microsecond, + )) + + def __ne__(self, other): + return not self.__eq__(other) + + def __div__(self, other): + try: + reciprocal = 1 / float(other) + except TypeError: + return NotImplemented + + return self.__mul__(reciprocal) + + __truediv__ = __div__ + + def __repr__(self): + l = [] + for attr in ["years", "months", "days", "leapdays", + "hours", "minutes", "seconds", "microseconds"]: + value = getattr(self, attr) + if value: + l.append("{attr}={value:+g}".format(attr=attr, value=value)) + for attr in ["year", "month", "day", "weekday", + "hour", "minute", "second", "microsecond"]: + value = getattr(self, attr) + if value is not None: + l.append("{attr}={value}".format(attr=attr, value=repr(value))) + return "{classname}({attrs})".format(classname=self.__class__.__name__, + attrs=", ".join(l)) + + +def _sign(x): + return int(copysign(1, x)) + +# vim:ts=4:sw=4:et diff --git a/bots-venv/lib/python3.7/site-packages/dateutil/rrule.py b/bots-venv/lib/python3.7/site-packages/dateutil/rrule.py new file mode 100644 index 0000000..6bf0ea9 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/dateutil/rrule.py @@ -0,0 +1,1735 @@ +# -*- coding: utf-8 -*- +""" +The rrule module offers a small, complete, and very fast, implementation of +the recurrence rules documented in the +`iCalendar RFC `_, +including support for caching of results. +""" +import itertools +import datetime +import calendar +import re +import sys + +try: + from math import gcd +except ImportError: + from fractions import gcd + +from six import advance_iterator, integer_types +from six.moves import _thread, range +import heapq + +from ._common import weekday as weekdaybase + +# For warning about deprecation of until and count +from warnings import warn + +__all__ = ["rrule", "rruleset", "rrulestr", + "YEARLY", "MONTHLY", "WEEKLY", "DAILY", + "HOURLY", "MINUTELY", "SECONDLY", + "MO", "TU", "WE", "TH", "FR", "SA", "SU"] + +# Every mask is 7 days longer to handle cross-year weekly periods. +M366MASK = tuple([1]*31+[2]*29+[3]*31+[4]*30+[5]*31+[6]*30 + + [7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7) +M365MASK = list(M366MASK) +M29, M30, M31 = list(range(1, 30)), list(range(1, 31)), list(range(1, 32)) +MDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) +MDAY365MASK = list(MDAY366MASK) +M29, M30, M31 = list(range(-29, 0)), list(range(-30, 0)), list(range(-31, 0)) +NMDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) +NMDAY365MASK = list(NMDAY366MASK) +M366RANGE = (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366) +M365RANGE = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365) +WDAYMASK = [0, 1, 2, 3, 4, 5, 6]*55 +del M29, M30, M31, M365MASK[59], MDAY365MASK[59], NMDAY365MASK[31] +MDAY365MASK = tuple(MDAY365MASK) +M365MASK = tuple(M365MASK) + +FREQNAMES = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', 'HOURLY', 'MINUTELY', 'SECONDLY'] + +(YEARLY, + MONTHLY, + WEEKLY, + DAILY, + HOURLY, + MINUTELY, + SECONDLY) = list(range(7)) + +# Imported on demand. +easter = None +parser = None + + +class weekday(weekdaybase): + """ + This version of weekday does not allow n = 0. + """ + def __init__(self, wkday, n=None): + if n == 0: + raise ValueError("Can't create weekday with n==0") + + super(weekday, self).__init__(wkday, n) + + +MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) + + +def _invalidates_cache(f): + """ + Decorator for rruleset methods which may invalidate the + cached length. + """ + def inner_func(self, *args, **kwargs): + rv = f(self, *args, **kwargs) + self._invalidate_cache() + return rv + + return inner_func + + +class rrulebase(object): + def __init__(self, cache=False): + if cache: + self._cache = [] + self._cache_lock = _thread.allocate_lock() + self._invalidate_cache() + else: + self._cache = None + self._cache_complete = False + self._len = None + + def __iter__(self): + if self._cache_complete: + return iter(self._cache) + elif self._cache is None: + return self._iter() + else: + return self._iter_cached() + + def _invalidate_cache(self): + if self._cache is not None: + self._cache = [] + self._cache_complete = False + self._cache_gen = self._iter() + + if self._cache_lock.locked(): + self._cache_lock.release() + + self._len = None + + def _iter_cached(self): + i = 0 + gen = self._cache_gen + cache = self._cache + acquire = self._cache_lock.acquire + release = self._cache_lock.release + while gen: + if i == len(cache): + acquire() + if self._cache_complete: + break + try: + for j in range(10): + cache.append(advance_iterator(gen)) + except StopIteration: + self._cache_gen = gen = None + self._cache_complete = True + break + release() + yield cache[i] + i += 1 + while i < self._len: + yield cache[i] + i += 1 + + def __getitem__(self, item): + if self._cache_complete: + return self._cache[item] + elif isinstance(item, slice): + if item.step and item.step < 0: + return list(iter(self))[item] + else: + return list(itertools.islice(self, + item.start or 0, + item.stop or sys.maxsize, + item.step or 1)) + elif item >= 0: + gen = iter(self) + try: + for i in range(item+1): + res = advance_iterator(gen) + except StopIteration: + raise IndexError + return res + else: + return list(iter(self))[item] + + def __contains__(self, item): + if self._cache_complete: + return item in self._cache + else: + for i in self: + if i == item: + return True + elif i > item: + return False + return False + + # __len__() introduces a large performance penalty. + def count(self): + """ Returns the number of recurrences in this set. It will have go + trough the whole recurrence, if this hasn't been done before. """ + if self._len is None: + for x in self: + pass + return self._len + + def before(self, dt, inc=False): + """ Returns the last recurrence before the given datetime instance. The + inc keyword defines what happens if dt is an occurrence. With + inc=True, if dt itself is an occurrence, it will be returned. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + last = None + if inc: + for i in gen: + if i > dt: + break + last = i + else: + for i in gen: + if i >= dt: + break + last = i + return last + + def after(self, dt, inc=False): + """ Returns the first recurrence after the given datetime instance. The + inc keyword defines what happens if dt is an occurrence. With + inc=True, if dt itself is an occurrence, it will be returned. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + if inc: + for i in gen: + if i >= dt: + return i + else: + for i in gen: + if i > dt: + return i + return None + + def xafter(self, dt, count=None, inc=False): + """ + Generator which yields up to `count` recurrences after the given + datetime instance, equivalent to `after`. + + :param dt: + The datetime at which to start generating recurrences. + + :param count: + The maximum number of recurrences to generate. If `None` (default), + dates are generated until the recurrence rule is exhausted. + + :param inc: + If `dt` is an instance of the rule and `inc` is `True`, it is + included in the output. + + :yields: Yields a sequence of `datetime` objects. + """ + + if self._cache_complete: + gen = self._cache + else: + gen = self + + # Select the comparison function + if inc: + comp = lambda dc, dtc: dc >= dtc + else: + comp = lambda dc, dtc: dc > dtc + + # Generate dates + n = 0 + for d in gen: + if comp(d, dt): + if count is not None: + n += 1 + if n > count: + break + + yield d + + def between(self, after, before, inc=False, count=1): + """ Returns all the occurrences of the rrule between after and before. + The inc keyword defines what happens if after and/or before are + themselves occurrences. With inc=True, they will be included in the + list, if they are found in the recurrence set. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + started = False + l = [] + if inc: + for i in gen: + if i > before: + break + elif not started: + if i >= after: + started = True + l.append(i) + else: + l.append(i) + else: + for i in gen: + if i >= before: + break + elif not started: + if i > after: + started = True + l.append(i) + else: + l.append(i) + return l + + +class rrule(rrulebase): + """ + That's the base of the rrule operation. It accepts all the keywords + defined in the RFC as its constructor parameters (except byday, + which was renamed to byweekday) and more. The constructor prototype is:: + + rrule(freq) + + Where freq must be one of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, + or SECONDLY. + + .. note:: + Per RFC section 3.3.10, recurrence instances falling on invalid dates + and times are ignored rather than coerced: + + Recurrence rules may generate recurrence instances with an invalid + date (e.g., February 30) or nonexistent local time (e.g., 1:30 AM + on a day where the local time is moved forward by an hour at 1:00 + AM). Such recurrence instances MUST be ignored and MUST NOT be + counted as part of the recurrence set. + + This can lead to possibly surprising behavior when, for example, the + start date occurs at the end of the month: + + >>> from dateutil.rrule import rrule, MONTHLY + >>> from datetime import datetime + >>> start_date = datetime(2014, 12, 31) + >>> list(rrule(freq=MONTHLY, count=4, dtstart=start_date)) + ... # doctest: +NORMALIZE_WHITESPACE + [datetime.datetime(2014, 12, 31, 0, 0), + datetime.datetime(2015, 1, 31, 0, 0), + datetime.datetime(2015, 3, 31, 0, 0), + datetime.datetime(2015, 5, 31, 0, 0)] + + Additionally, it supports the following keyword arguments: + + :param dtstart: + The recurrence start. Besides being the base for the recurrence, + missing parameters in the final recurrence instances will also be + extracted from this date. If not given, datetime.now() will be used + instead. + :param interval: + The interval between each freq iteration. For example, when using + YEARLY, an interval of 2 means once every two years, but with HOURLY, + it means once every two hours. The default interval is 1. + :param wkst: + The week start day. Must be one of the MO, TU, WE constants, or an + integer, specifying the first day of the week. This will affect + recurrences based on weekly periods. The default week start is got + from calendar.firstweekday(), and may be modified by + calendar.setfirstweekday(). + :param count: + If given, this determines how many occurrences will be generated. + + .. note:: + As of version 2.5.0, the use of the keyword ``until`` in conjunction + with ``count`` is deprecated, to make sure ``dateutil`` is fully + compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` + **must not** occur in the same call to ``rrule``. + :param until: + If given, this must be a datetime instance specifying the upper-bound + limit of the recurrence. The last recurrence in the rule is the greatest + datetime that is less than or equal to the value specified in the + ``until`` parameter. + + .. note:: + As of version 2.5.0, the use of the keyword ``until`` in conjunction + with ``count`` is deprecated, to make sure ``dateutil`` is fully + compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` + **must not** occur in the same call to ``rrule``. + :param bysetpos: + If given, it must be either an integer, or a sequence of integers, + positive or negative. Each given integer will specify an occurrence + number, corresponding to the nth occurrence of the rule inside the + frequency period. For example, a bysetpos of -1 if combined with a + MONTHLY frequency, and a byweekday of (MO, TU, WE, TH, FR), will + result in the last work day of every month. + :param bymonth: + If given, it must be either an integer, or a sequence of integers, + meaning the months to apply the recurrence to. + :param bymonthday: + If given, it must be either an integer, or a sequence of integers, + meaning the month days to apply the recurrence to. + :param byyearday: + If given, it must be either an integer, or a sequence of integers, + meaning the year days to apply the recurrence to. + :param byeaster: + If given, it must be either an integer, or a sequence of integers, + positive or negative. Each integer will define an offset from the + Easter Sunday. Passing the offset 0 to byeaster will yield the Easter + Sunday itself. This is an extension to the RFC specification. + :param byweekno: + If given, it must be either an integer, or a sequence of integers, + meaning the week numbers to apply the recurrence to. Week numbers + have the meaning described in ISO8601, that is, the first week of + the year is that containing at least four days of the new year. + :param byweekday: + If given, it must be either an integer (0 == MO), a sequence of + integers, one of the weekday constants (MO, TU, etc), or a sequence + of these constants. When given, these variables will define the + weekdays where the recurrence will be applied. It's also possible to + use an argument n for the weekday instances, which will mean the nth + occurrence of this weekday in the period. For example, with MONTHLY, + or with YEARLY and BYMONTH, using FR(+1) in byweekday will specify the + first friday of the month where the recurrence happens. Notice that in + the RFC documentation, this is specified as BYDAY, but was renamed to + avoid the ambiguity of that keyword. + :param byhour: + If given, it must be either an integer, or a sequence of integers, + meaning the hours to apply the recurrence to. + :param byminute: + If given, it must be either an integer, or a sequence of integers, + meaning the minutes to apply the recurrence to. + :param bysecond: + If given, it must be either an integer, or a sequence of integers, + meaning the seconds to apply the recurrence to. + :param cache: + If given, it must be a boolean value specifying to enable or disable + caching of results. If you will use the same rrule instance multiple + times, enabling caching will improve the performance considerably. + """ + def __init__(self, freq, dtstart=None, + interval=1, wkst=None, count=None, until=None, bysetpos=None, + bymonth=None, bymonthday=None, byyearday=None, byeaster=None, + byweekno=None, byweekday=None, + byhour=None, byminute=None, bysecond=None, + cache=False): + super(rrule, self).__init__(cache) + global easter + if not dtstart: + if until and until.tzinfo: + dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0) + else: + dtstart = datetime.datetime.now().replace(microsecond=0) + elif not isinstance(dtstart, datetime.datetime): + dtstart = datetime.datetime.fromordinal(dtstart.toordinal()) + else: + dtstart = dtstart.replace(microsecond=0) + self._dtstart = dtstart + self._tzinfo = dtstart.tzinfo + self._freq = freq + self._interval = interval + self._count = count + + # Cache the original byxxx rules, if they are provided, as the _byxxx + # attributes do not necessarily map to the inputs, and this can be + # a problem in generating the strings. Only store things if they've + # been supplied (the string retrieval will just use .get()) + self._original_rule = {} + + if until and not isinstance(until, datetime.datetime): + until = datetime.datetime.fromordinal(until.toordinal()) + self._until = until + + if self._dtstart and self._until: + if (self._dtstart.tzinfo is not None) != (self._until.tzinfo is not None): + # According to RFC5545 Section 3.3.10: + # https://tools.ietf.org/html/rfc5545#section-3.3.10 + # + # > If the "DTSTART" property is specified as a date with UTC + # > time or a date with local time and time zone reference, + # > then the UNTIL rule part MUST be specified as a date with + # > UTC time. + raise ValueError( + 'RRULE UNTIL values must be specified in UTC when DTSTART ' + 'is timezone-aware' + ) + + if count is not None and until: + warn("Using both 'count' and 'until' is inconsistent with RFC 5545" + " and has been deprecated in dateutil. Future versions will " + "raise an error.", DeprecationWarning) + + if wkst is None: + self._wkst = calendar.firstweekday() + elif isinstance(wkst, integer_types): + self._wkst = wkst + else: + self._wkst = wkst.weekday + + if bysetpos is None: + self._bysetpos = None + elif isinstance(bysetpos, integer_types): + if bysetpos == 0 or not (-366 <= bysetpos <= 366): + raise ValueError("bysetpos must be between 1 and 366, " + "or between -366 and -1") + self._bysetpos = (bysetpos,) + else: + self._bysetpos = tuple(bysetpos) + for pos in self._bysetpos: + if pos == 0 or not (-366 <= pos <= 366): + raise ValueError("bysetpos must be between 1 and 366, " + "or between -366 and -1") + + if self._bysetpos: + self._original_rule['bysetpos'] = self._bysetpos + + if (byweekno is None and byyearday is None and bymonthday is None and + byweekday is None and byeaster is None): + if freq == YEARLY: + if bymonth is None: + bymonth = dtstart.month + self._original_rule['bymonth'] = None + bymonthday = dtstart.day + self._original_rule['bymonthday'] = None + elif freq == MONTHLY: + bymonthday = dtstart.day + self._original_rule['bymonthday'] = None + elif freq == WEEKLY: + byweekday = dtstart.weekday() + self._original_rule['byweekday'] = None + + # bymonth + if bymonth is None: + self._bymonth = None + else: + if isinstance(bymonth, integer_types): + bymonth = (bymonth,) + + self._bymonth = tuple(sorted(set(bymonth))) + + if 'bymonth' not in self._original_rule: + self._original_rule['bymonth'] = self._bymonth + + # byyearday + if byyearday is None: + self._byyearday = None + else: + if isinstance(byyearday, integer_types): + byyearday = (byyearday,) + + self._byyearday = tuple(sorted(set(byyearday))) + self._original_rule['byyearday'] = self._byyearday + + # byeaster + if byeaster is not None: + if not easter: + from dateutil import easter + if isinstance(byeaster, integer_types): + self._byeaster = (byeaster,) + else: + self._byeaster = tuple(sorted(byeaster)) + + self._original_rule['byeaster'] = self._byeaster + else: + self._byeaster = None + + # bymonthday + if bymonthday is None: + self._bymonthday = () + self._bynmonthday = () + else: + if isinstance(bymonthday, integer_types): + bymonthday = (bymonthday,) + + bymonthday = set(bymonthday) # Ensure it's unique + + self._bymonthday = tuple(sorted(x for x in bymonthday if x > 0)) + self._bynmonthday = tuple(sorted(x for x in bymonthday if x < 0)) + + # Storing positive numbers first, then negative numbers + if 'bymonthday' not in self._original_rule: + self._original_rule['bymonthday'] = tuple( + itertools.chain(self._bymonthday, self._bynmonthday)) + + # byweekno + if byweekno is None: + self._byweekno = None + else: + if isinstance(byweekno, integer_types): + byweekno = (byweekno,) + + self._byweekno = tuple(sorted(set(byweekno))) + + self._original_rule['byweekno'] = self._byweekno + + # byweekday / bynweekday + if byweekday is None: + self._byweekday = None + self._bynweekday = None + else: + # If it's one of the valid non-sequence types, convert to a + # single-element sequence before the iterator that builds the + # byweekday set. + if isinstance(byweekday, integer_types) or hasattr(byweekday, "n"): + byweekday = (byweekday,) + + self._byweekday = set() + self._bynweekday = set() + for wday in byweekday: + if isinstance(wday, integer_types): + self._byweekday.add(wday) + elif not wday.n or freq > MONTHLY: + self._byweekday.add(wday.weekday) + else: + self._bynweekday.add((wday.weekday, wday.n)) + + if not self._byweekday: + self._byweekday = None + elif not self._bynweekday: + self._bynweekday = None + + if self._byweekday is not None: + self._byweekday = tuple(sorted(self._byweekday)) + orig_byweekday = [weekday(x) for x in self._byweekday] + else: + orig_byweekday = () + + if self._bynweekday is not None: + self._bynweekday = tuple(sorted(self._bynweekday)) + orig_bynweekday = [weekday(*x) for x in self._bynweekday] + else: + orig_bynweekday = () + + if 'byweekday' not in self._original_rule: + self._original_rule['byweekday'] = tuple(itertools.chain( + orig_byweekday, orig_bynweekday)) + + # byhour + if byhour is None: + if freq < HOURLY: + self._byhour = {dtstart.hour} + else: + self._byhour = None + else: + if isinstance(byhour, integer_types): + byhour = (byhour,) + + if freq == HOURLY: + self._byhour = self.__construct_byset(start=dtstart.hour, + byxxx=byhour, + base=24) + else: + self._byhour = set(byhour) + + self._byhour = tuple(sorted(self._byhour)) + self._original_rule['byhour'] = self._byhour + + # byminute + if byminute is None: + if freq < MINUTELY: + self._byminute = {dtstart.minute} + else: + self._byminute = None + else: + if isinstance(byminute, integer_types): + byminute = (byminute,) + + if freq == MINUTELY: + self._byminute = self.__construct_byset(start=dtstart.minute, + byxxx=byminute, + base=60) + else: + self._byminute = set(byminute) + + self._byminute = tuple(sorted(self._byminute)) + self._original_rule['byminute'] = self._byminute + + # bysecond + if bysecond is None: + if freq < SECONDLY: + self._bysecond = ((dtstart.second,)) + else: + self._bysecond = None + else: + if isinstance(bysecond, integer_types): + bysecond = (bysecond,) + + self._bysecond = set(bysecond) + + if freq == SECONDLY: + self._bysecond = self.__construct_byset(start=dtstart.second, + byxxx=bysecond, + base=60) + else: + self._bysecond = set(bysecond) + + self._bysecond = tuple(sorted(self._bysecond)) + self._original_rule['bysecond'] = self._bysecond + + if self._freq >= HOURLY: + self._timeset = None + else: + self._timeset = [] + for hour in self._byhour: + for minute in self._byminute: + for second in self._bysecond: + self._timeset.append( + datetime.time(hour, minute, second, + tzinfo=self._tzinfo)) + self._timeset.sort() + self._timeset = tuple(self._timeset) + + def __str__(self): + """ + Output a string that would generate this RRULE if passed to rrulestr. + This is mostly compatible with RFC5545, except for the + dateutil-specific extension BYEASTER. + """ + + output = [] + h, m, s = [None] * 3 + if self._dtstart: + output.append(self._dtstart.strftime('DTSTART:%Y%m%dT%H%M%S')) + h, m, s = self._dtstart.timetuple()[3:6] + + parts = ['FREQ=' + FREQNAMES[self._freq]] + if self._interval != 1: + parts.append('INTERVAL=' + str(self._interval)) + + if self._wkst: + parts.append('WKST=' + repr(weekday(self._wkst))[0:2]) + + if self._count is not None: + parts.append('COUNT=' + str(self._count)) + + if self._until: + parts.append(self._until.strftime('UNTIL=%Y%m%dT%H%M%S')) + + if self._original_rule.get('byweekday') is not None: + # The str() method on weekday objects doesn't generate + # RFC5545-compliant strings, so we should modify that. + original_rule = dict(self._original_rule) + wday_strings = [] + for wday in original_rule['byweekday']: + if wday.n: + wday_strings.append('{n:+d}{wday}'.format( + n=wday.n, + wday=repr(wday)[0:2])) + else: + wday_strings.append(repr(wday)) + + original_rule['byweekday'] = wday_strings + else: + original_rule = self._original_rule + + partfmt = '{name}={vals}' + for name, key in [('BYSETPOS', 'bysetpos'), + ('BYMONTH', 'bymonth'), + ('BYMONTHDAY', 'bymonthday'), + ('BYYEARDAY', 'byyearday'), + ('BYWEEKNO', 'byweekno'), + ('BYDAY', 'byweekday'), + ('BYHOUR', 'byhour'), + ('BYMINUTE', 'byminute'), + ('BYSECOND', 'bysecond'), + ('BYEASTER', 'byeaster')]: + value = original_rule.get(key) + if value: + parts.append(partfmt.format(name=name, vals=(','.join(str(v) + for v in value)))) + + output.append('RRULE:' + ';'.join(parts)) + return '\n'.join(output) + + def replace(self, **kwargs): + """Return new rrule with same attributes except for those attributes given new + values by whichever keyword arguments are specified.""" + new_kwargs = {"interval": self._interval, + "count": self._count, + "dtstart": self._dtstart, + "freq": self._freq, + "until": self._until, + "wkst": self._wkst, + "cache": False if self._cache is None else True } + new_kwargs.update(self._original_rule) + new_kwargs.update(kwargs) + return rrule(**new_kwargs) + + def _iter(self): + year, month, day, hour, minute, second, weekday, yearday, _ = \ + self._dtstart.timetuple() + + # Some local variables to speed things up a bit + freq = self._freq + interval = self._interval + wkst = self._wkst + until = self._until + bymonth = self._bymonth + byweekno = self._byweekno + byyearday = self._byyearday + byweekday = self._byweekday + byeaster = self._byeaster + bymonthday = self._bymonthday + bynmonthday = self._bynmonthday + bysetpos = self._bysetpos + byhour = self._byhour + byminute = self._byminute + bysecond = self._bysecond + + ii = _iterinfo(self) + ii.rebuild(year, month) + + getdayset = {YEARLY: ii.ydayset, + MONTHLY: ii.mdayset, + WEEKLY: ii.wdayset, + DAILY: ii.ddayset, + HOURLY: ii.ddayset, + MINUTELY: ii.ddayset, + SECONDLY: ii.ddayset}[freq] + + if freq < HOURLY: + timeset = self._timeset + else: + gettimeset = {HOURLY: ii.htimeset, + MINUTELY: ii.mtimeset, + SECONDLY: ii.stimeset}[freq] + if ((freq >= HOURLY and + self._byhour and hour not in self._byhour) or + (freq >= MINUTELY and + self._byminute and minute not in self._byminute) or + (freq >= SECONDLY and + self._bysecond and second not in self._bysecond)): + timeset = () + else: + timeset = gettimeset(hour, minute, second) + + total = 0 + count = self._count + while True: + # Get dayset with the right frequency + dayset, start, end = getdayset(year, month, day) + + # Do the "hard" work ;-) + filtered = False + for i in dayset[start:end]: + if ((bymonth and ii.mmask[i] not in bymonth) or + (byweekno and not ii.wnomask[i]) or + (byweekday and ii.wdaymask[i] not in byweekday) or + (ii.nwdaymask and not ii.nwdaymask[i]) or + (byeaster and not ii.eastermask[i]) or + ((bymonthday or bynmonthday) and + ii.mdaymask[i] not in bymonthday and + ii.nmdaymask[i] not in bynmonthday) or + (byyearday and + ((i < ii.yearlen and i+1 not in byyearday and + -ii.yearlen+i not in byyearday) or + (i >= ii.yearlen and i+1-ii.yearlen not in byyearday and + -ii.nextyearlen+i-ii.yearlen not in byyearday)))): + dayset[i] = None + filtered = True + + # Output results + if bysetpos and timeset: + poslist = [] + for pos in bysetpos: + if pos < 0: + daypos, timepos = divmod(pos, len(timeset)) + else: + daypos, timepos = divmod(pos-1, len(timeset)) + try: + i = [x for x in dayset[start:end] + if x is not None][daypos] + time = timeset[timepos] + except IndexError: + pass + else: + date = datetime.date.fromordinal(ii.yearordinal+i) + res = datetime.datetime.combine(date, time) + if res not in poslist: + poslist.append(res) + poslist.sort() + for res in poslist: + if until and res > until: + self._len = total + return + elif res >= self._dtstart: + if count is not None: + count -= 1 + if count < 0: + self._len = total + return + total += 1 + yield res + else: + for i in dayset[start:end]: + if i is not None: + date = datetime.date.fromordinal(ii.yearordinal + i) + for time in timeset: + res = datetime.datetime.combine(date, time) + if until and res > until: + self._len = total + return + elif res >= self._dtstart: + if count is not None: + count -= 1 + if count < 0: + self._len = total + return + + total += 1 + yield res + + # Handle frequency and interval + fixday = False + if freq == YEARLY: + year += interval + if year > datetime.MAXYEAR: + self._len = total + return + ii.rebuild(year, month) + elif freq == MONTHLY: + month += interval + if month > 12: + div, mod = divmod(month, 12) + month = mod + year += div + if month == 0: + month = 12 + year -= 1 + if year > datetime.MAXYEAR: + self._len = total + return + ii.rebuild(year, month) + elif freq == WEEKLY: + if wkst > weekday: + day += -(weekday+1+(6-wkst))+self._interval*7 + else: + day += -(weekday-wkst)+self._interval*7 + weekday = wkst + fixday = True + elif freq == DAILY: + day += interval + fixday = True + elif freq == HOURLY: + if filtered: + # Jump to one iteration before next day + hour += ((23-hour)//interval)*interval + + if byhour: + ndays, hour = self.__mod_distance(value=hour, + byxxx=self._byhour, + base=24) + else: + ndays, hour = divmod(hour+interval, 24) + + if ndays: + day += ndays + fixday = True + + timeset = gettimeset(hour, minute, second) + elif freq == MINUTELY: + if filtered: + # Jump to one iteration before next day + minute += ((1439-(hour*60+minute))//interval)*interval + + valid = False + rep_rate = (24*60) + for j in range(rep_rate // gcd(interval, rep_rate)): + if byminute: + nhours, minute = \ + self.__mod_distance(value=minute, + byxxx=self._byminute, + base=60) + else: + nhours, minute = divmod(minute+interval, 60) + + div, hour = divmod(hour+nhours, 24) + if div: + day += div + fixday = True + filtered = False + + if not byhour or hour in byhour: + valid = True + break + + if not valid: + raise ValueError('Invalid combination of interval and ' + + 'byhour resulting in empty rule.') + + timeset = gettimeset(hour, minute, second) + elif freq == SECONDLY: + if filtered: + # Jump to one iteration before next day + second += (((86399 - (hour * 3600 + minute * 60 + second)) + // interval) * interval) + + rep_rate = (24 * 3600) + valid = False + for j in range(0, rep_rate // gcd(interval, rep_rate)): + if bysecond: + nminutes, second = \ + self.__mod_distance(value=second, + byxxx=self._bysecond, + base=60) + else: + nminutes, second = divmod(second+interval, 60) + + div, minute = divmod(minute+nminutes, 60) + if div: + hour += div + div, hour = divmod(hour, 24) + if div: + day += div + fixday = True + + if ((not byhour or hour in byhour) and + (not byminute or minute in byminute) and + (not bysecond or second in bysecond)): + valid = True + break + + if not valid: + raise ValueError('Invalid combination of interval, ' + + 'byhour and byminute resulting in empty' + + ' rule.') + + timeset = gettimeset(hour, minute, second) + + if fixday and day > 28: + daysinmonth = calendar.monthrange(year, month)[1] + if day > daysinmonth: + while day > daysinmonth: + day -= daysinmonth + month += 1 + if month == 13: + month = 1 + year += 1 + if year > datetime.MAXYEAR: + self._len = total + return + daysinmonth = calendar.monthrange(year, month)[1] + ii.rebuild(year, month) + + def __construct_byset(self, start, byxxx, base): + """ + If a `BYXXX` sequence is passed to the constructor at the same level as + `FREQ` (e.g. `FREQ=HOURLY,BYHOUR={2,4,7},INTERVAL=3`), there are some + specifications which cannot be reached given some starting conditions. + + This occurs whenever the interval is not coprime with the base of a + given unit and the difference between the starting position and the + ending position is not coprime with the greatest common denominator + between the interval and the base. For example, with a FREQ of hourly + starting at 17:00 and an interval of 4, the only valid values for + BYHOUR would be {21, 1, 5, 9, 13, 17}, because 4 and 24 are not + coprime. + + :param start: + Specifies the starting position. + :param byxxx: + An iterable containing the list of allowed values. + :param base: + The largest allowable value for the specified frequency (e.g. + 24 hours, 60 minutes). + + This does not preserve the type of the iterable, returning a set, since + the values should be unique and the order is irrelevant, this will + speed up later lookups. + + In the event of an empty set, raises a :exception:`ValueError`, as this + results in an empty rrule. + """ + + cset = set() + + # Support a single byxxx value. + if isinstance(byxxx, integer_types): + byxxx = (byxxx, ) + + for num in byxxx: + i_gcd = gcd(self._interval, base) + # Use divmod rather than % because we need to wrap negative nums. + if i_gcd == 1 or divmod(num - start, i_gcd)[1] == 0: + cset.add(num) + + if len(cset) == 0: + raise ValueError("Invalid rrule byxxx generates an empty set.") + + return cset + + def __mod_distance(self, value, byxxx, base): + """ + Calculates the next value in a sequence where the `FREQ` parameter is + specified along with a `BYXXX` parameter at the same "level" + (e.g. `HOURLY` specified with `BYHOUR`). + + :param value: + The old value of the component. + :param byxxx: + The `BYXXX` set, which should have been generated by + `rrule._construct_byset`, or something else which checks that a + valid rule is present. + :param base: + The largest allowable value for the specified frequency (e.g. + 24 hours, 60 minutes). + + If a valid value is not found after `base` iterations (the maximum + number before the sequence would start to repeat), this raises a + :exception:`ValueError`, as no valid values were found. + + This returns a tuple of `divmod(n*interval, base)`, where `n` is the + smallest number of `interval` repetitions until the next specified + value in `byxxx` is found. + """ + accumulator = 0 + for ii in range(1, base + 1): + # Using divmod() over % to account for negative intervals + div, value = divmod(value + self._interval, base) + accumulator += div + if value in byxxx: + return (accumulator, value) + + +class _iterinfo(object): + __slots__ = ["rrule", "lastyear", "lastmonth", + "yearlen", "nextyearlen", "yearordinal", "yearweekday", + "mmask", "mrange", "mdaymask", "nmdaymask", + "wdaymask", "wnomask", "nwdaymask", "eastermask"] + + def __init__(self, rrule): + for attr in self.__slots__: + setattr(self, attr, None) + self.rrule = rrule + + def rebuild(self, year, month): + # Every mask is 7 days longer to handle cross-year weekly periods. + rr = self.rrule + if year != self.lastyear: + self.yearlen = 365 + calendar.isleap(year) + self.nextyearlen = 365 + calendar.isleap(year + 1) + firstyday = datetime.date(year, 1, 1) + self.yearordinal = firstyday.toordinal() + self.yearweekday = firstyday.weekday() + + wday = datetime.date(year, 1, 1).weekday() + if self.yearlen == 365: + self.mmask = M365MASK + self.mdaymask = MDAY365MASK + self.nmdaymask = NMDAY365MASK + self.wdaymask = WDAYMASK[wday:] + self.mrange = M365RANGE + else: + self.mmask = M366MASK + self.mdaymask = MDAY366MASK + self.nmdaymask = NMDAY366MASK + self.wdaymask = WDAYMASK[wday:] + self.mrange = M366RANGE + + if not rr._byweekno: + self.wnomask = None + else: + self.wnomask = [0]*(self.yearlen+7) + # no1wkst = firstwkst = self.wdaymask.index(rr._wkst) + no1wkst = firstwkst = (7-self.yearweekday+rr._wkst) % 7 + if no1wkst >= 4: + no1wkst = 0 + # Number of days in the year, plus the days we got + # from last year. + wyearlen = self.yearlen+(self.yearweekday-rr._wkst) % 7 + else: + # Number of days in the year, minus the days we + # left in last year. + wyearlen = self.yearlen-no1wkst + div, mod = divmod(wyearlen, 7) + numweeks = div+mod//4 + for n in rr._byweekno: + if n < 0: + n += numweeks+1 + if not (0 < n <= numweeks): + continue + if n > 1: + i = no1wkst+(n-1)*7 + if no1wkst != firstwkst: + i -= 7-firstwkst + else: + i = no1wkst + for j in range(7): + self.wnomask[i] = 1 + i += 1 + if self.wdaymask[i] == rr._wkst: + break + if 1 in rr._byweekno: + # Check week number 1 of next year as well + # TODO: Check -numweeks for next year. + i = no1wkst+numweeks*7 + if no1wkst != firstwkst: + i -= 7-firstwkst + if i < self.yearlen: + # If week starts in next year, we + # don't care about it. + for j in range(7): + self.wnomask[i] = 1 + i += 1 + if self.wdaymask[i] == rr._wkst: + break + if no1wkst: + # Check last week number of last year as + # well. If no1wkst is 0, either the year + # started on week start, or week number 1 + # got days from last year, so there are no + # days from last year's last week number in + # this year. + if -1 not in rr._byweekno: + lyearweekday = datetime.date(year-1, 1, 1).weekday() + lno1wkst = (7-lyearweekday+rr._wkst) % 7 + lyearlen = 365+calendar.isleap(year-1) + if lno1wkst >= 4: + lno1wkst = 0 + lnumweeks = 52+(lyearlen + + (lyearweekday-rr._wkst) % 7) % 7//4 + else: + lnumweeks = 52+(self.yearlen-no1wkst) % 7//4 + else: + lnumweeks = -1 + if lnumweeks in rr._byweekno: + for i in range(no1wkst): + self.wnomask[i] = 1 + + if (rr._bynweekday and (month != self.lastmonth or + year != self.lastyear)): + ranges = [] + if rr._freq == YEARLY: + if rr._bymonth: + for month in rr._bymonth: + ranges.append(self.mrange[month-1:month+1]) + else: + ranges = [(0, self.yearlen)] + elif rr._freq == MONTHLY: + ranges = [self.mrange[month-1:month+1]] + if ranges: + # Weekly frequency won't get here, so we may not + # care about cross-year weekly periods. + self.nwdaymask = [0]*self.yearlen + for first, last in ranges: + last -= 1 + for wday, n in rr._bynweekday: + if n < 0: + i = last+(n+1)*7 + i -= (self.wdaymask[i]-wday) % 7 + else: + i = first+(n-1)*7 + i += (7-self.wdaymask[i]+wday) % 7 + if first <= i <= last: + self.nwdaymask[i] = 1 + + if rr._byeaster: + self.eastermask = [0]*(self.yearlen+7) + eyday = easter.easter(year).toordinal()-self.yearordinal + for offset in rr._byeaster: + self.eastermask[eyday+offset] = 1 + + self.lastyear = year + self.lastmonth = month + + def ydayset(self, year, month, day): + return list(range(self.yearlen)), 0, self.yearlen + + def mdayset(self, year, month, day): + dset = [None]*self.yearlen + start, end = self.mrange[month-1:month+1] + for i in range(start, end): + dset[i] = i + return dset, start, end + + def wdayset(self, year, month, day): + # We need to handle cross-year weeks here. + dset = [None]*(self.yearlen+7) + i = datetime.date(year, month, day).toordinal()-self.yearordinal + start = i + for j in range(7): + dset[i] = i + i += 1 + # if (not (0 <= i < self.yearlen) or + # self.wdaymask[i] == self.rrule._wkst): + # This will cross the year boundary, if necessary. + if self.wdaymask[i] == self.rrule._wkst: + break + return dset, start, i + + def ddayset(self, year, month, day): + dset = [None] * self.yearlen + i = datetime.date(year, month, day).toordinal() - self.yearordinal + dset[i] = i + return dset, i, i + 1 + + def htimeset(self, hour, minute, second): + tset = [] + rr = self.rrule + for minute in rr._byminute: + for second in rr._bysecond: + tset.append(datetime.time(hour, minute, second, + tzinfo=rr._tzinfo)) + tset.sort() + return tset + + def mtimeset(self, hour, minute, second): + tset = [] + rr = self.rrule + for second in rr._bysecond: + tset.append(datetime.time(hour, minute, second, tzinfo=rr._tzinfo)) + tset.sort() + return tset + + def stimeset(self, hour, minute, second): + return (datetime.time(hour, minute, second, + tzinfo=self.rrule._tzinfo),) + + +class rruleset(rrulebase): + """ The rruleset type allows more complex recurrence setups, mixing + multiple rules, dates, exclusion rules, and exclusion dates. The type + constructor takes the following keyword arguments: + + :param cache: If True, caching of results will be enabled, improving + performance of multiple queries considerably. """ + + class _genitem(object): + def __init__(self, genlist, gen): + try: + self.dt = advance_iterator(gen) + genlist.append(self) + except StopIteration: + pass + self.genlist = genlist + self.gen = gen + + def __next__(self): + try: + self.dt = advance_iterator(self.gen) + except StopIteration: + if self.genlist[0] is self: + heapq.heappop(self.genlist) + else: + self.genlist.remove(self) + heapq.heapify(self.genlist) + + next = __next__ + + def __lt__(self, other): + return self.dt < other.dt + + def __gt__(self, other): + return self.dt > other.dt + + def __eq__(self, other): + return self.dt == other.dt + + def __ne__(self, other): + return self.dt != other.dt + + def __init__(self, cache=False): + super(rruleset, self).__init__(cache) + self._rrule = [] + self._rdate = [] + self._exrule = [] + self._exdate = [] + + @_invalidates_cache + def rrule(self, rrule): + """ Include the given :py:class:`rrule` instance in the recurrence set + generation. """ + self._rrule.append(rrule) + + @_invalidates_cache + def rdate(self, rdate): + """ Include the given :py:class:`datetime` instance in the recurrence + set generation. """ + self._rdate.append(rdate) + + @_invalidates_cache + def exrule(self, exrule): + """ Include the given rrule instance in the recurrence set exclusion + list. Dates which are part of the given recurrence rules will not + be generated, even if some inclusive rrule or rdate matches them. + """ + self._exrule.append(exrule) + + @_invalidates_cache + def exdate(self, exdate): + """ Include the given datetime instance in the recurrence set + exclusion list. Dates included that way will not be generated, + even if some inclusive rrule or rdate matches them. """ + self._exdate.append(exdate) + + def _iter(self): + rlist = [] + self._rdate.sort() + self._genitem(rlist, iter(self._rdate)) + for gen in [iter(x) for x in self._rrule]: + self._genitem(rlist, gen) + exlist = [] + self._exdate.sort() + self._genitem(exlist, iter(self._exdate)) + for gen in [iter(x) for x in self._exrule]: + self._genitem(exlist, gen) + lastdt = None + total = 0 + heapq.heapify(rlist) + heapq.heapify(exlist) + while rlist: + ritem = rlist[0] + if not lastdt or lastdt != ritem.dt: + while exlist and exlist[0] < ritem: + exitem = exlist[0] + advance_iterator(exitem) + if exlist and exlist[0] is exitem: + heapq.heapreplace(exlist, exitem) + if not exlist or ritem != exlist[0]: + total += 1 + yield ritem.dt + lastdt = ritem.dt + advance_iterator(ritem) + if rlist and rlist[0] is ritem: + heapq.heapreplace(rlist, ritem) + self._len = total + + + + +class _rrulestr(object): + """ Parses a string representation of a recurrence rule or set of + recurrence rules. + + :param s: + Required, a string defining one or more recurrence rules. + + :param dtstart: + If given, used as the default recurrence start if not specified in the + rule string. + + :param cache: + If set ``True`` caching of results will be enabled, improving + performance of multiple queries considerably. + + :param unfold: + If set ``True`` indicates that a rule string is split over more + than one line and should be joined before processing. + + :param forceset: + If set ``True`` forces a :class:`dateutil.rrule.rruleset` to + be returned. + + :param compatible: + If set ``True`` forces ``unfold`` and ``forceset`` to be ``True``. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a naive + :class:`datetime.datetime` object is returned. + + :param tzids: + If given, a callable or mapping used to retrieve a + :class:`datetime.tzinfo` from a string representation. + Defaults to :func:`dateutil.tz.gettz`. + + :param tzinfos: + Additional time zone names / aliases which may be present in a string + representation. See :func:`dateutil.parser.parse` for more + information. + + :return: + Returns a :class:`dateutil.rrule.rruleset` or + :class:`dateutil.rrule.rrule` + """ + + _freq_map = {"YEARLY": YEARLY, + "MONTHLY": MONTHLY, + "WEEKLY": WEEKLY, + "DAILY": DAILY, + "HOURLY": HOURLY, + "MINUTELY": MINUTELY, + "SECONDLY": SECONDLY} + + _weekday_map = {"MO": 0, "TU": 1, "WE": 2, "TH": 3, + "FR": 4, "SA": 5, "SU": 6} + + def _handle_int(self, rrkwargs, name, value, **kwargs): + rrkwargs[name.lower()] = int(value) + + def _handle_int_list(self, rrkwargs, name, value, **kwargs): + rrkwargs[name.lower()] = [int(x) for x in value.split(',')] + + _handle_INTERVAL = _handle_int + _handle_COUNT = _handle_int + _handle_BYSETPOS = _handle_int_list + _handle_BYMONTH = _handle_int_list + _handle_BYMONTHDAY = _handle_int_list + _handle_BYYEARDAY = _handle_int_list + _handle_BYEASTER = _handle_int_list + _handle_BYWEEKNO = _handle_int_list + _handle_BYHOUR = _handle_int_list + _handle_BYMINUTE = _handle_int_list + _handle_BYSECOND = _handle_int_list + + def _handle_FREQ(self, rrkwargs, name, value, **kwargs): + rrkwargs["freq"] = self._freq_map[value] + + def _handle_UNTIL(self, rrkwargs, name, value, **kwargs): + global parser + if not parser: + from dateutil import parser + try: + rrkwargs["until"] = parser.parse(value, + ignoretz=kwargs.get("ignoretz"), + tzinfos=kwargs.get("tzinfos")) + except ValueError: + raise ValueError("invalid until date") + + def _handle_WKST(self, rrkwargs, name, value, **kwargs): + rrkwargs["wkst"] = self._weekday_map[value] + + def _handle_BYWEEKDAY(self, rrkwargs, name, value, **kwargs): + """ + Two ways to specify this: +1MO or MO(+1) + """ + l = [] + for wday in value.split(','): + if '(' in wday: + # If it's of the form TH(+1), etc. + splt = wday.split('(') + w = splt[0] + n = int(splt[1][:-1]) + elif len(wday): + # If it's of the form +1MO + for i in range(len(wday)): + if wday[i] not in '+-0123456789': + break + n = wday[:i] or None + w = wday[i:] + if n: + n = int(n) + else: + raise ValueError("Invalid (empty) BYDAY specification.") + + l.append(weekdays[self._weekday_map[w]](n)) + rrkwargs["byweekday"] = l + + _handle_BYDAY = _handle_BYWEEKDAY + + def _parse_rfc_rrule(self, line, + dtstart=None, + cache=False, + ignoretz=False, + tzinfos=None): + if line.find(':') != -1: + name, value = line.split(':') + if name != "RRULE": + raise ValueError("unknown parameter name") + else: + value = line + rrkwargs = {} + for pair in value.split(';'): + name, value = pair.split('=') + name = name.upper() + value = value.upper() + try: + getattr(self, "_handle_"+name)(rrkwargs, name, value, + ignoretz=ignoretz, + tzinfos=tzinfos) + except AttributeError: + raise ValueError("unknown parameter '%s'" % name) + except (KeyError, ValueError): + raise ValueError("invalid '%s': %s" % (name, value)) + return rrule(dtstart=dtstart, cache=cache, **rrkwargs) + + def _parse_date_value(self, date_value, parms, rule_tzids, + ignoretz, tzids, tzinfos): + global parser + if not parser: + from dateutil import parser + + datevals = [] + value_found = False + TZID = None + + for parm in parms: + if parm.startswith("TZID="): + try: + tzkey = rule_tzids[parm.split('TZID=')[-1]] + except KeyError: + continue + if tzids is None: + from . import tz + tzlookup = tz.gettz + elif callable(tzids): + tzlookup = tzids + else: + tzlookup = getattr(tzids, 'get', None) + if tzlookup is None: + msg = ('tzids must be a callable, mapping, or None, ' + 'not %s' % tzids) + raise ValueError(msg) + + TZID = tzlookup(tzkey) + continue + + # RFC 5445 3.8.2.4: The VALUE parameter is optional, but may be found + # only once. + if parm not in {"VALUE=DATE-TIME", "VALUE=DATE"}: + raise ValueError("unsupported parm: " + parm) + else: + if value_found: + msg = ("Duplicate value parameter found in: " + parm) + raise ValueError(msg) + value_found = True + + for datestr in date_value.split(','): + date = parser.parse(datestr, ignoretz=ignoretz, tzinfos=tzinfos) + if TZID is not None: + if date.tzinfo is None: + date = date.replace(tzinfo=TZID) + else: + raise ValueError('DTSTART/EXDATE specifies multiple timezone') + datevals.append(date) + + return datevals + + def _parse_rfc(self, s, + dtstart=None, + cache=False, + unfold=False, + forceset=False, + compatible=False, + ignoretz=False, + tzids=None, + tzinfos=None): + global parser + if compatible: + forceset = True + unfold = True + + TZID_NAMES = dict(map( + lambda x: (x.upper(), x), + re.findall('TZID=(?P[^:]+):', s) + )) + s = s.upper() + if not s.strip(): + raise ValueError("empty string") + if unfold: + lines = s.splitlines() + i = 0 + while i < len(lines): + line = lines[i].rstrip() + if not line: + del lines[i] + elif i > 0 and line[0] == " ": + lines[i-1] += line[1:] + del lines[i] + else: + i += 1 + else: + lines = s.split() + if (not forceset and len(lines) == 1 and (s.find(':') == -1 or + s.startswith('RRULE:'))): + return self._parse_rfc_rrule(lines[0], cache=cache, + dtstart=dtstart, ignoretz=ignoretz, + tzinfos=tzinfos) + else: + rrulevals = [] + rdatevals = [] + exrulevals = [] + exdatevals = [] + for line in lines: + if not line: + continue + if line.find(':') == -1: + name = "RRULE" + value = line + else: + name, value = line.split(':', 1) + parms = name.split(';') + if not parms: + raise ValueError("empty property name") + name = parms[0] + parms = parms[1:] + if name == "RRULE": + for parm in parms: + raise ValueError("unsupported RRULE parm: "+parm) + rrulevals.append(value) + elif name == "RDATE": + for parm in parms: + if parm != "VALUE=DATE-TIME": + raise ValueError("unsupported RDATE parm: "+parm) + rdatevals.append(value) + elif name == "EXRULE": + for parm in parms: + raise ValueError("unsupported EXRULE parm: "+parm) + exrulevals.append(value) + elif name == "EXDATE": + exdatevals.extend( + self._parse_date_value(value, parms, + TZID_NAMES, ignoretz, + tzids, tzinfos) + ) + elif name == "DTSTART": + dtvals = self._parse_date_value(value, parms, TZID_NAMES, + ignoretz, tzids, tzinfos) + if len(dtvals) != 1: + raise ValueError("Multiple DTSTART values specified:" + + value) + dtstart = dtvals[0] + else: + raise ValueError("unsupported property: "+name) + if (forceset or len(rrulevals) > 1 or rdatevals + or exrulevals or exdatevals): + if not parser and (rdatevals or exdatevals): + from dateutil import parser + rset = rruleset(cache=cache) + for value in rrulevals: + rset.rrule(self._parse_rfc_rrule(value, dtstart=dtstart, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in rdatevals: + for datestr in value.split(','): + rset.rdate(parser.parse(datestr, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in exrulevals: + rset.exrule(self._parse_rfc_rrule(value, dtstart=dtstart, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in exdatevals: + rset.exdate(value) + if compatible and dtstart: + rset.rdate(dtstart) + return rset + else: + return self._parse_rfc_rrule(rrulevals[0], + dtstart=dtstart, + cache=cache, + ignoretz=ignoretz, + tzinfos=tzinfos) + + def __call__(self, s, **kwargs): + return self._parse_rfc(s, **kwargs) + + +rrulestr = _rrulestr() + +# vim:ts=4:sw=4:et diff --git a/bots-venv/lib/python3.7/site-packages/dateutil/tz/__init__.py b/bots-venv/lib/python3.7/site-packages/dateutil/tz/__init__.py new file mode 100644 index 0000000..af1352c --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/dateutil/tz/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +from .tz import * +from .tz import __doc__ + +__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", + "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz", + "enfold", "datetime_ambiguous", "datetime_exists", + "resolve_imaginary", "UTC", "DeprecatedTzFormatWarning"] + + +class DeprecatedTzFormatWarning(Warning): + """Warning raised when time zones are parsed from deprecated formats.""" diff --git a/bots-venv/lib/python3.7/site-packages/dateutil/tz/_common.py b/bots-venv/lib/python3.7/site-packages/dateutil/tz/_common.py new file mode 100644 index 0000000..e6ac118 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/dateutil/tz/_common.py @@ -0,0 +1,419 @@ +from six import PY2 + +from functools import wraps + +from datetime import datetime, timedelta, tzinfo + + +ZERO = timedelta(0) + +__all__ = ['tzname_in_python2', 'enfold'] + + +def tzname_in_python2(namefunc): + """Change unicode output into bytestrings in Python 2 + + tzname() API changed in Python 3. It used to return bytes, but was changed + to unicode strings + """ + if PY2: + @wraps(namefunc) + def adjust_encoding(*args, **kwargs): + name = namefunc(*args, **kwargs) + if name is not None: + name = name.encode() + + return name + + return adjust_encoding + else: + return namefunc + + +# The following is adapted from Alexander Belopolsky's tz library +# https://github.com/abalkin/tz +if hasattr(datetime, 'fold'): + # This is the pre-python 3.6 fold situation + def enfold(dt, fold=1): + """ + Provides a unified interface for assigning the ``fold`` attribute to + datetimes both before and after the implementation of PEP-495. + + :param fold: + The value for the ``fold`` attribute in the returned datetime. This + should be either 0 or 1. + + :return: + Returns an object for which ``getattr(dt, 'fold', 0)`` returns + ``fold`` for all versions of Python. In versions prior to + Python 3.6, this is a ``_DatetimeWithFold`` object, which is a + subclass of :py:class:`datetime.datetime` with the ``fold`` + attribute added, if ``fold`` is 1. + + .. versionadded:: 2.6.0 + """ + return dt.replace(fold=fold) + +else: + class _DatetimeWithFold(datetime): + """ + This is a class designed to provide a PEP 495-compliant interface for + Python versions before 3.6. It is used only for dates in a fold, so + the ``fold`` attribute is fixed at ``1``. + + .. versionadded:: 2.6.0 + """ + __slots__ = () + + def replace(self, *args, **kwargs): + """ + Return a datetime with the same attributes, except for those + attributes given new values by whichever keyword arguments are + specified. Note that tzinfo=None can be specified to create a naive + datetime from an aware datetime with no conversion of date and time + data. + + This is reimplemented in ``_DatetimeWithFold`` because pypy3 will + return a ``datetime.datetime`` even if ``fold`` is unchanged. + """ + argnames = ( + 'year', 'month', 'day', 'hour', 'minute', 'second', + 'microsecond', 'tzinfo' + ) + + for arg, argname in zip(args, argnames): + if argname in kwargs: + raise TypeError('Duplicate argument: {}'.format(argname)) + + kwargs[argname] = arg + + for argname in argnames: + if argname not in kwargs: + kwargs[argname] = getattr(self, argname) + + dt_class = self.__class__ if kwargs.get('fold', 1) else datetime + + return dt_class(**kwargs) + + @property + def fold(self): + return 1 + + def enfold(dt, fold=1): + """ + Provides a unified interface for assigning the ``fold`` attribute to + datetimes both before and after the implementation of PEP-495. + + :param fold: + The value for the ``fold`` attribute in the returned datetime. This + should be either 0 or 1. + + :return: + Returns an object for which ``getattr(dt, 'fold', 0)`` returns + ``fold`` for all versions of Python. In versions prior to + Python 3.6, this is a ``_DatetimeWithFold`` object, which is a + subclass of :py:class:`datetime.datetime` with the ``fold`` + attribute added, if ``fold`` is 1. + + .. versionadded:: 2.6.0 + """ + if getattr(dt, 'fold', 0) == fold: + return dt + + args = dt.timetuple()[:6] + args += (dt.microsecond, dt.tzinfo) + + if fold: + return _DatetimeWithFold(*args) + else: + return datetime(*args) + + +def _validate_fromutc_inputs(f): + """ + The CPython version of ``fromutc`` checks that the input is a ``datetime`` + object and that ``self`` is attached as its ``tzinfo``. + """ + @wraps(f) + def fromutc(self, dt): + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + return f(self, dt) + + return fromutc + + +class _tzinfo(tzinfo): + """ + Base class for all ``dateutil`` ``tzinfo`` objects. + """ + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + + dt = dt.replace(tzinfo=self) + + wall_0 = enfold(dt, fold=0) + wall_1 = enfold(dt, fold=1) + + same_offset = wall_0.utcoffset() == wall_1.utcoffset() + same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None) + + return same_dt and not same_offset + + def _fold_status(self, dt_utc, dt_wall): + """ + Determine the fold status of a "wall" datetime, given a representation + of the same datetime as a (naive) UTC datetime. This is calculated based + on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all + datetimes, and that this offset is the actual number of hours separating + ``dt_utc`` and ``dt_wall``. + + :param dt_utc: + Representation of the datetime as UTC + + :param dt_wall: + Representation of the datetime as "wall time". This parameter must + either have a `fold` attribute or have a fold-naive + :class:`datetime.tzinfo` attached, otherwise the calculation may + fail. + """ + if self.is_ambiguous(dt_wall): + delta_wall = dt_wall - dt_utc + _fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst())) + else: + _fold = 0 + + return _fold + + def _fold(self, dt): + return getattr(dt, 'fold', 0) + + def _fromutc(self, dt): + """ + Given a timezone-aware datetime in a given timezone, calculates a + timezone-aware datetime in a new timezone. + + Since this is the one time that we *know* we have an unambiguous + datetime object, we take this opportunity to determine whether the + datetime is ambiguous and in a "fold" state (e.g. if it's the first + occurrence, chronologically, of the ambiguous datetime). + + :param dt: + A timezone-aware :class:`datetime.datetime` object. + """ + + # Re-implement the algorithm from Python's datetime.py + dtoff = dt.utcoffset() + if dtoff is None: + raise ValueError("fromutc() requires a non-None utcoffset() " + "result") + + # The original datetime.py code assumes that `dst()` defaults to + # zero during ambiguous times. PEP 495 inverts this presumption, so + # for pre-PEP 495 versions of python, we need to tweak the algorithm. + dtdst = dt.dst() + if dtdst is None: + raise ValueError("fromutc() requires a non-None dst() result") + delta = dtoff - dtdst + + dt += delta + # Set fold=1 so we can default to being in the fold for + # ambiguous dates. + dtdst = enfold(dt, fold=1).dst() + if dtdst is None: + raise ValueError("fromutc(): dt.dst gave inconsistent " + "results; cannot convert") + return dt + dtdst + + @_validate_fromutc_inputs + def fromutc(self, dt): + """ + Given a timezone-aware datetime in a given timezone, calculates a + timezone-aware datetime in a new timezone. + + Since this is the one time that we *know* we have an unambiguous + datetime object, we take this opportunity to determine whether the + datetime is ambiguous and in a "fold" state (e.g. if it's the first + occurrence, chronologically, of the ambiguous datetime). + + :param dt: + A timezone-aware :class:`datetime.datetime` object. + """ + dt_wall = self._fromutc(dt) + + # Calculate the fold status given the two datetimes. + _fold = self._fold_status(dt, dt_wall) + + # Set the default fold value for ambiguous dates + return enfold(dt_wall, fold=_fold) + + +class tzrangebase(_tzinfo): + """ + This is an abstract base class for time zones represented by an annual + transition into and out of DST. Child classes should implement the following + methods: + + * ``__init__(self, *args, **kwargs)`` + * ``transitions(self, year)`` - this is expected to return a tuple of + datetimes representing the DST on and off transitions in standard + time. + + A fully initialized ``tzrangebase`` subclass should also provide the + following attributes: + * ``hasdst``: Boolean whether or not the zone uses DST. + * ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects + representing the respective UTC offsets. + * ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short + abbreviations in DST and STD, respectively. + * ``_hasdst``: Whether or not the zone has DST. + + .. versionadded:: 2.6.0 + """ + def __init__(self): + raise NotImplementedError('tzrangebase is an abstract base class') + + def utcoffset(self, dt): + isdst = self._isdst(dt) + + if isdst is None: + return None + elif isdst: + return self._dst_offset + else: + return self._std_offset + + def dst(self, dt): + isdst = self._isdst(dt) + + if isdst is None: + return None + elif isdst: + return self._dst_base_offset + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + if self._isdst(dt): + return self._dst_abbr + else: + return self._std_abbr + + def fromutc(self, dt): + """ Given a datetime in UTC, return local time """ + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + # Get transitions - if there are none, fixed offset + transitions = self.transitions(dt.year) + if transitions is None: + return dt + self.utcoffset(dt) + + # Get the transition times in UTC + dston, dstoff = transitions + + dston -= self._std_offset + dstoff -= self._std_offset + + utc_transitions = (dston, dstoff) + dt_utc = dt.replace(tzinfo=None) + + isdst = self._naive_isdst(dt_utc, utc_transitions) + + if isdst: + dt_wall = dt + self._dst_offset + else: + dt_wall = dt + self._std_offset + + _fold = int(not isdst and self.is_ambiguous(dt_wall)) + + return enfold(dt_wall, fold=_fold) + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + if not self.hasdst: + return False + + start, end = self.transitions(dt.year) + + dt = dt.replace(tzinfo=None) + return (end <= dt < end + self._dst_base_offset) + + def _isdst(self, dt): + if not self.hasdst: + return False + elif dt is None: + return None + + transitions = self.transitions(dt.year) + + if transitions is None: + return False + + dt = dt.replace(tzinfo=None) + + isdst = self._naive_isdst(dt, transitions) + + # Handle ambiguous dates + if not isdst and self.is_ambiguous(dt): + return not self._fold(dt) + else: + return isdst + + def _naive_isdst(self, dt, transitions): + dston, dstoff = transitions + + dt = dt.replace(tzinfo=None) + + if dston < dstoff: + isdst = dston <= dt < dstoff + else: + isdst = not dstoff <= dt < dston + + return isdst + + @property + def _dst_base_offset(self): + return self._dst_offset - self._std_offset + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(...)" % self.__class__.__name__ + + __reduce__ = object.__reduce__ diff --git a/bots-venv/lib/python3.7/site-packages/dateutil/tz/_factories.py b/bots-venv/lib/python3.7/site-packages/dateutil/tz/_factories.py new file mode 100644 index 0000000..f8a6589 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/dateutil/tz/_factories.py @@ -0,0 +1,80 @@ +from datetime import timedelta +import weakref +from collections import OrderedDict + +from six.moves import _thread + + +class _TzSingleton(type): + def __init__(cls, *args, **kwargs): + cls.__instance = None + super(_TzSingleton, cls).__init__(*args, **kwargs) + + def __call__(cls): + if cls.__instance is None: + cls.__instance = super(_TzSingleton, cls).__call__() + return cls.__instance + + +class _TzFactory(type): + def instance(cls, *args, **kwargs): + """Alternate constructor that returns a fresh instance""" + return type.__call__(cls, *args, **kwargs) + + +class _TzOffsetFactory(_TzFactory): + def __init__(cls, *args, **kwargs): + cls.__instances = weakref.WeakValueDictionary() + cls.__strong_cache = OrderedDict() + cls.__strong_cache_size = 8 + + cls._cache_lock = _thread.allocate_lock() + + def __call__(cls, name, offset): + if isinstance(offset, timedelta): + key = (name, offset.total_seconds()) + else: + key = (name, offset) + + instance = cls.__instances.get(key, None) + if instance is None: + instance = cls.__instances.setdefault(key, + cls.instance(name, offset)) + + # This lock may not be necessary in Python 3. See GH issue #901 + with cls._cache_lock: + cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) + + # Remove an item if the strong cache is overpopulated + if len(cls.__strong_cache) > cls.__strong_cache_size: + cls.__strong_cache.popitem(last=False) + + return instance + + +class _TzStrFactory(_TzFactory): + def __init__(cls, *args, **kwargs): + cls.__instances = weakref.WeakValueDictionary() + cls.__strong_cache = OrderedDict() + cls.__strong_cache_size = 8 + + cls.__cache_lock = _thread.allocate_lock() + + def __call__(cls, s, posix_offset=False): + key = (s, posix_offset) + instance = cls.__instances.get(key, None) + + if instance is None: + instance = cls.__instances.setdefault(key, + cls.instance(s, posix_offset)) + + # This lock may not be necessary in Python 3. See GH issue #901 + with cls.__cache_lock: + cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) + + # Remove an item if the strong cache is overpopulated + if len(cls.__strong_cache) > cls.__strong_cache_size: + cls.__strong_cache.popitem(last=False) + + return instance + diff --git a/bots-venv/lib/python3.7/site-packages/dateutil/tz/tz.py b/bots-venv/lib/python3.7/site-packages/dateutil/tz/tz.py new file mode 100644 index 0000000..af81e88 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/dateutil/tz/tz.py @@ -0,0 +1,1849 @@ +# -*- coding: utf-8 -*- +""" +This module offers timezone implementations subclassing the abstract +:py:class:`datetime.tzinfo` type. There are classes to handle tzfile format +files (usually are in :file:`/etc/localtime`, :file:`/usr/share/zoneinfo`, +etc), TZ environment string (in all known formats), given ranges (with help +from relative deltas), local machine timezone, fixed offset timezone, and UTC +timezone. +""" +import datetime +import struct +import time +import sys +import os +import bisect +import weakref +from collections import OrderedDict + +import six +from six import string_types +from six.moves import _thread +from ._common import tzname_in_python2, _tzinfo +from ._common import tzrangebase, enfold +from ._common import _validate_fromutc_inputs + +from ._factories import _TzSingleton, _TzOffsetFactory +from ._factories import _TzStrFactory +try: + from .win import tzwin, tzwinlocal +except ImportError: + tzwin = tzwinlocal = None + +# For warning about rounding tzinfo +from warnings import warn + +ZERO = datetime.timedelta(0) +EPOCH = datetime.datetime.utcfromtimestamp(0) +EPOCHORDINAL = EPOCH.toordinal() + + +@six.add_metaclass(_TzSingleton) +class tzutc(datetime.tzinfo): + """ + This is a tzinfo object that represents the UTC time zone. + + **Examples:** + + .. doctest:: + + >>> from datetime import * + >>> from dateutil.tz import * + + >>> datetime.now() + datetime.datetime(2003, 9, 27, 9, 40, 1, 521290) + + >>> datetime.now(tzutc()) + datetime.datetime(2003, 9, 27, 12, 40, 12, 156379, tzinfo=tzutc()) + + >>> datetime.now(tzutc()).tzname() + 'UTC' + + .. versionchanged:: 2.7.0 + ``tzutc()`` is now a singleton, so the result of ``tzutc()`` will + always return the same object. + + .. doctest:: + + >>> from dateutil.tz import tzutc, UTC + >>> tzutc() is tzutc() + True + >>> tzutc() is UTC + True + """ + def utcoffset(self, dt): + return ZERO + + def dst(self, dt): + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return "UTC" + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + return False + + @_validate_fromutc_inputs + def fromutc(self, dt): + """ + Fast track version of fromutc() returns the original ``dt`` object for + any valid :py:class:`datetime.datetime` object. + """ + return dt + + def __eq__(self, other): + if not isinstance(other, (tzutc, tzoffset)): + return NotImplemented + + return (isinstance(other, tzutc) or + (isinstance(other, tzoffset) and other._offset == ZERO)) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s()" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + + +#: Convenience constant providing a :class:`tzutc()` instance +#: +#: .. versionadded:: 2.7.0 +UTC = tzutc() + + +@six.add_metaclass(_TzOffsetFactory) +class tzoffset(datetime.tzinfo): + """ + A simple class for representing a fixed offset from UTC. + + :param name: + The timezone name, to be returned when ``tzname()`` is called. + :param offset: + The time zone offset in seconds, or (since version 2.6.0, represented + as a :py:class:`datetime.timedelta` object). + """ + def __init__(self, name, offset): + self._name = name + + try: + # Allow a timedelta + offset = offset.total_seconds() + except (TypeError, AttributeError): + pass + + self._offset = datetime.timedelta(seconds=_get_supported_offset(offset)) + + def utcoffset(self, dt): + return self._offset + + def dst(self, dt): + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._name + + @_validate_fromutc_inputs + def fromutc(self, dt): + return dt + self._offset + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + return False + + def __eq__(self, other): + if not isinstance(other, tzoffset): + return NotImplemented + + return self._offset == other._offset + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(%s, %s)" % (self.__class__.__name__, + repr(self._name), + int(self._offset.total_seconds())) + + __reduce__ = object.__reduce__ + + +class tzlocal(_tzinfo): + """ + A :class:`tzinfo` subclass built around the ``time`` timezone functions. + """ + def __init__(self): + super(tzlocal, self).__init__() + + self._std_offset = datetime.timedelta(seconds=-time.timezone) + if time.daylight: + self._dst_offset = datetime.timedelta(seconds=-time.altzone) + else: + self._dst_offset = self._std_offset + + self._dst_saved = self._dst_offset - self._std_offset + self._hasdst = bool(self._dst_saved) + self._tznames = tuple(time.tzname) + + def utcoffset(self, dt): + if dt is None and self._hasdst: + return None + + if self._isdst(dt): + return self._dst_offset + else: + return self._std_offset + + def dst(self, dt): + if dt is None and self._hasdst: + return None + + if self._isdst(dt): + return self._dst_offset - self._std_offset + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._tznames[self._isdst(dt)] + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + naive_dst = self._naive_is_dst(dt) + return (not naive_dst and + (naive_dst != self._naive_is_dst(dt - self._dst_saved))) + + def _naive_is_dst(self, dt): + timestamp = _datetime_to_timestamp(dt) + return time.localtime(timestamp + time.timezone).tm_isdst + + def _isdst(self, dt, fold_naive=True): + # We can't use mktime here. It is unstable when deciding if + # the hour near to a change is DST or not. + # + # timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, + # dt.minute, dt.second, dt.weekday(), 0, -1)) + # return time.localtime(timestamp).tm_isdst + # + # The code above yields the following result: + # + # >>> import tz, datetime + # >>> t = tz.tzlocal() + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRDT' + # >>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname() + # 'BRST' + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRST' + # >>> datetime.datetime(2003,2,15,22,tzinfo=t).tzname() + # 'BRDT' + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRDT' + # + # Here is a more stable implementation: + # + if not self._hasdst: + return False + + # Check for ambiguous times: + dstval = self._naive_is_dst(dt) + fold = getattr(dt, 'fold', None) + + if self.is_ambiguous(dt): + if fold is not None: + return not self._fold(dt) + else: + return True + + return dstval + + def __eq__(self, other): + if isinstance(other, tzlocal): + return (self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset) + elif isinstance(other, tzutc): + return (not self._hasdst and + self._tznames[0] in {'UTC', 'GMT'} and + self._std_offset == ZERO) + elif isinstance(other, tzoffset): + return (not self._hasdst and + self._tznames[0] == other._name and + self._std_offset == other._offset) + else: + return NotImplemented + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s()" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + + +class _ttinfo(object): + __slots__ = ["offset", "delta", "isdst", "abbr", + "isstd", "isgmt", "dstoffset"] + + def __init__(self): + for attr in self.__slots__: + setattr(self, attr, None) + + def __repr__(self): + l = [] + for attr in self.__slots__: + value = getattr(self, attr) + if value is not None: + l.append("%s=%s" % (attr, repr(value))) + return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) + + def __eq__(self, other): + if not isinstance(other, _ttinfo): + return NotImplemented + + return (self.offset == other.offset and + self.delta == other.delta and + self.isdst == other.isdst and + self.abbr == other.abbr and + self.isstd == other.isstd and + self.isgmt == other.isgmt and + self.dstoffset == other.dstoffset) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __getstate__(self): + state = {} + for name in self.__slots__: + state[name] = getattr(self, name, None) + return state + + def __setstate__(self, state): + for name in self.__slots__: + if name in state: + setattr(self, name, state[name]) + + +class _tzfile(object): + """ + Lightweight class for holding the relevant transition and time zone + information read from binary tzfiles. + """ + attrs = ['trans_list', 'trans_list_utc', 'trans_idx', 'ttinfo_list', + 'ttinfo_std', 'ttinfo_dst', 'ttinfo_before', 'ttinfo_first'] + + def __init__(self, **kwargs): + for attr in self.attrs: + setattr(self, attr, kwargs.get(attr, None)) + + +class tzfile(_tzinfo): + """ + This is a ``tzinfo`` subclass that allows one to use the ``tzfile(5)`` + format timezone files to extract current and historical zone information. + + :param fileobj: + This can be an opened file stream or a file name that the time zone + information can be read from. + + :param filename: + This is an optional parameter specifying the source of the time zone + information in the event that ``fileobj`` is a file object. If omitted + and ``fileobj`` is a file stream, this parameter will be set either to + ``fileobj``'s ``name`` attribute or to ``repr(fileobj)``. + + See `Sources for Time Zone and Daylight Saving Time Data + `_ for more information. + Time zone files can be compiled from the `IANA Time Zone database files + `_ with the `zic time zone compiler + `_ + + .. note:: + + Only construct a ``tzfile`` directly if you have a specific timezone + file on disk that you want to read into a Python ``tzinfo`` object. + If you want to get a ``tzfile`` representing a specific IANA zone, + (e.g. ``'America/New_York'``), you should call + :func:`dateutil.tz.gettz` with the zone identifier. + + + **Examples:** + + Using the US Eastern time zone as an example, we can see that a ``tzfile`` + provides time zone information for the standard Daylight Saving offsets: + + .. testsetup:: tzfile + + from dateutil.tz import gettz + from datetime import datetime + + .. doctest:: tzfile + + >>> NYC = gettz('America/New_York') + >>> NYC + tzfile('/usr/share/zoneinfo/America/New_York') + + >>> print(datetime(2016, 1, 3, tzinfo=NYC)) # EST + 2016-01-03 00:00:00-05:00 + + >>> print(datetime(2016, 7, 7, tzinfo=NYC)) # EDT + 2016-07-07 00:00:00-04:00 + + + The ``tzfile`` structure contains a fully history of the time zone, + so historical dates will also have the right offsets. For example, before + the adoption of the UTC standards, New York used local solar mean time: + + .. doctest:: tzfile + + >>> print(datetime(1901, 4, 12, tzinfo=NYC)) # LMT + 1901-04-12 00:00:00-04:56 + + And during World War II, New York was on "Eastern War Time", which was a + state of permanent daylight saving time: + + .. doctest:: tzfile + + >>> print(datetime(1944, 2, 7, tzinfo=NYC)) # EWT + 1944-02-07 00:00:00-04:00 + + """ + + def __init__(self, fileobj, filename=None): + super(tzfile, self).__init__() + + file_opened_here = False + if isinstance(fileobj, string_types): + self._filename = fileobj + fileobj = open(fileobj, 'rb') + file_opened_here = True + elif filename is not None: + self._filename = filename + elif hasattr(fileobj, "name"): + self._filename = fileobj.name + else: + self._filename = repr(fileobj) + + if fileobj is not None: + if not file_opened_here: + fileobj = _nullcontext(fileobj) + + with fileobj as file_stream: + tzobj = self._read_tzfile(file_stream) + + self._set_tzdata(tzobj) + + def _set_tzdata(self, tzobj): + """ Set the time zone data of this object from a _tzfile object """ + # Copy the relevant attributes over as private attributes + for attr in _tzfile.attrs: + setattr(self, '_' + attr, getattr(tzobj, attr)) + + def _read_tzfile(self, fileobj): + out = _tzfile() + + # From tzfile(5): + # + # The time zone information files used by tzset(3) + # begin with the magic characters "TZif" to identify + # them as time zone information files, followed by + # sixteen bytes reserved for future use, followed by + # six four-byte values of type long, written in a + # ``standard'' byte order (the high-order byte + # of the value is written first). + if fileobj.read(4).decode() != "TZif": + raise ValueError("magic not found") + + fileobj.read(16) + + ( + # The number of UTC/local indicators stored in the file. + ttisgmtcnt, + + # The number of standard/wall indicators stored in the file. + ttisstdcnt, + + # The number of leap seconds for which data is + # stored in the file. + leapcnt, + + # The number of "transition times" for which data + # is stored in the file. + timecnt, + + # The number of "local time types" for which data + # is stored in the file (must not be zero). + typecnt, + + # The number of characters of "time zone + # abbreviation strings" stored in the file. + charcnt, + + ) = struct.unpack(">6l", fileobj.read(24)) + + # The above header is followed by tzh_timecnt four-byte + # values of type long, sorted in ascending order. + # These values are written in ``standard'' byte order. + # Each is used as a transition time (as returned by + # time(2)) at which the rules for computing local time + # change. + + if timecnt: + out.trans_list_utc = list(struct.unpack(">%dl" % timecnt, + fileobj.read(timecnt*4))) + else: + out.trans_list_utc = [] + + # Next come tzh_timecnt one-byte values of type unsigned + # char; each one tells which of the different types of + # ``local time'' types described in the file is associated + # with the same-indexed transition time. These values + # serve as indices into an array of ttinfo structures that + # appears next in the file. + + if timecnt: + out.trans_idx = struct.unpack(">%dB" % timecnt, + fileobj.read(timecnt)) + else: + out.trans_idx = [] + + # Each ttinfo structure is written as a four-byte value + # for tt_gmtoff of type long, in a standard byte + # order, followed by a one-byte value for tt_isdst + # and a one-byte value for tt_abbrind. In each + # structure, tt_gmtoff gives the number of + # seconds to be added to UTC, tt_isdst tells whether + # tm_isdst should be set by localtime(3), and + # tt_abbrind serves as an index into the array of + # time zone abbreviation characters that follow the + # ttinfo structure(s) in the file. + + ttinfo = [] + + for i in range(typecnt): + ttinfo.append(struct.unpack(">lbb", fileobj.read(6))) + + abbr = fileobj.read(charcnt).decode() + + # Then there are tzh_leapcnt pairs of four-byte + # values, written in standard byte order; the + # first value of each pair gives the time (as + # returned by time(2)) at which a leap second + # occurs; the second gives the total number of + # leap seconds to be applied after the given time. + # The pairs of values are sorted in ascending order + # by time. + + # Not used, for now (but seek for correct file position) + if leapcnt: + fileobj.seek(leapcnt * 8, os.SEEK_CUR) + + # Then there are tzh_ttisstdcnt standard/wall + # indicators, each stored as a one-byte value; + # they tell whether the transition times associated + # with local time types were specified as standard + # time or wall clock time, and are used when + # a time zone file is used in handling POSIX-style + # time zone environment variables. + + if ttisstdcnt: + isstd = struct.unpack(">%db" % ttisstdcnt, + fileobj.read(ttisstdcnt)) + + # Finally, there are tzh_ttisgmtcnt UTC/local + # indicators, each stored as a one-byte value; + # they tell whether the transition times associated + # with local time types were specified as UTC or + # local time, and are used when a time zone file + # is used in handling POSIX-style time zone envi- + # ronment variables. + + if ttisgmtcnt: + isgmt = struct.unpack(">%db" % ttisgmtcnt, + fileobj.read(ttisgmtcnt)) + + # Build ttinfo list + out.ttinfo_list = [] + for i in range(typecnt): + gmtoff, isdst, abbrind = ttinfo[i] + gmtoff = _get_supported_offset(gmtoff) + tti = _ttinfo() + tti.offset = gmtoff + tti.dstoffset = datetime.timedelta(0) + tti.delta = datetime.timedelta(seconds=gmtoff) + tti.isdst = isdst + tti.abbr = abbr[abbrind:abbr.find('\x00', abbrind)] + tti.isstd = (ttisstdcnt > i and isstd[i] != 0) + tti.isgmt = (ttisgmtcnt > i and isgmt[i] != 0) + out.ttinfo_list.append(tti) + + # Replace ttinfo indexes for ttinfo objects. + out.trans_idx = [out.ttinfo_list[idx] for idx in out.trans_idx] + + # Set standard, dst, and before ttinfos. before will be + # used when a given time is before any transitions, + # and will be set to the first non-dst ttinfo, or to + # the first dst, if all of them are dst. + out.ttinfo_std = None + out.ttinfo_dst = None + out.ttinfo_before = None + if out.ttinfo_list: + if not out.trans_list_utc: + out.ttinfo_std = out.ttinfo_first = out.ttinfo_list[0] + else: + for i in range(timecnt-1, -1, -1): + tti = out.trans_idx[i] + if not out.ttinfo_std and not tti.isdst: + out.ttinfo_std = tti + elif not out.ttinfo_dst and tti.isdst: + out.ttinfo_dst = tti + + if out.ttinfo_std and out.ttinfo_dst: + break + else: + if out.ttinfo_dst and not out.ttinfo_std: + out.ttinfo_std = out.ttinfo_dst + + for tti in out.ttinfo_list: + if not tti.isdst: + out.ttinfo_before = tti + break + else: + out.ttinfo_before = out.ttinfo_list[0] + + # Now fix transition times to become relative to wall time. + # + # I'm not sure about this. In my tests, the tz source file + # is setup to wall time, and in the binary file isstd and + # isgmt are off, so it should be in wall time. OTOH, it's + # always in gmt time. Let me know if you have comments + # about this. + lastdst = None + lastoffset = None + lastdstoffset = None + lastbaseoffset = None + out.trans_list = [] + + for i, tti in enumerate(out.trans_idx): + offset = tti.offset + dstoffset = 0 + + if lastdst is not None: + if tti.isdst: + if not lastdst: + dstoffset = offset - lastoffset + + if not dstoffset and lastdstoffset: + dstoffset = lastdstoffset + + tti.dstoffset = datetime.timedelta(seconds=dstoffset) + lastdstoffset = dstoffset + + # If a time zone changes its base offset during a DST transition, + # then you need to adjust by the previous base offset to get the + # transition time in local time. Otherwise you use the current + # base offset. Ideally, I would have some mathematical proof of + # why this is true, but I haven't really thought about it enough. + baseoffset = offset - dstoffset + adjustment = baseoffset + if (lastbaseoffset is not None and baseoffset != lastbaseoffset + and tti.isdst != lastdst): + # The base DST has changed + adjustment = lastbaseoffset + + lastdst = tti.isdst + lastoffset = offset + lastbaseoffset = baseoffset + + out.trans_list.append(out.trans_list_utc[i] + adjustment) + + out.trans_idx = tuple(out.trans_idx) + out.trans_list = tuple(out.trans_list) + out.trans_list_utc = tuple(out.trans_list_utc) + + return out + + def _find_last_transition(self, dt, in_utc=False): + # If there's no list, there are no transitions to find + if not self._trans_list: + return None + + timestamp = _datetime_to_timestamp(dt) + + # Find where the timestamp fits in the transition list - if the + # timestamp is a transition time, it's part of the "after" period. + trans_list = self._trans_list_utc if in_utc else self._trans_list + idx = bisect.bisect_right(trans_list, timestamp) + + # We want to know when the previous transition was, so subtract off 1 + return idx - 1 + + def _get_ttinfo(self, idx): + # For no list or after the last transition, default to _ttinfo_std + if idx is None or (idx + 1) >= len(self._trans_list): + return self._ttinfo_std + + # If there is a list and the time is before it, return _ttinfo_before + if idx < 0: + return self._ttinfo_before + + return self._trans_idx[idx] + + def _find_ttinfo(self, dt): + idx = self._resolve_ambiguous_time(dt) + + return self._get_ttinfo(idx) + + def fromutc(self, dt): + """ + The ``tzfile`` implementation of :py:func:`datetime.tzinfo.fromutc`. + + :param dt: + A :py:class:`datetime.datetime` object. + + :raises TypeError: + Raised if ``dt`` is not a :py:class:`datetime.datetime` object. + + :raises ValueError: + Raised if this is called with a ``dt`` which does not have this + ``tzinfo`` attached. + + :return: + Returns a :py:class:`datetime.datetime` object representing the + wall time in ``self``'s time zone. + """ + # These isinstance checks are in datetime.tzinfo, so we'll preserve + # them, even if we don't care about duck typing. + if not isinstance(dt, datetime.datetime): + raise TypeError("fromutc() requires a datetime argument") + + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + # First treat UTC as wall time and get the transition we're in. + idx = self._find_last_transition(dt, in_utc=True) + tti = self._get_ttinfo(idx) + + dt_out = dt + datetime.timedelta(seconds=tti.offset) + + fold = self.is_ambiguous(dt_out, idx=idx) + + return enfold(dt_out, fold=int(fold)) + + def is_ambiguous(self, dt, idx=None): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + if idx is None: + idx = self._find_last_transition(dt) + + # Calculate the difference in offsets from current to previous + timestamp = _datetime_to_timestamp(dt) + tti = self._get_ttinfo(idx) + + if idx is None or idx <= 0: + return False + + od = self._get_ttinfo(idx - 1).offset - tti.offset + tt = self._trans_list[idx] # Transition time + + return timestamp < tt + od + + def _resolve_ambiguous_time(self, dt): + idx = self._find_last_transition(dt) + + # If we have no transitions, return the index + _fold = self._fold(dt) + if idx is None or idx == 0: + return idx + + # If it's ambiguous and we're in a fold, shift to a different index. + idx_offset = int(not _fold and self.is_ambiguous(dt, idx)) + + return idx - idx_offset + + def utcoffset(self, dt): + if dt is None: + return None + + if not self._ttinfo_std: + return ZERO + + return self._find_ttinfo(dt).delta + + def dst(self, dt): + if dt is None: + return None + + if not self._ttinfo_dst: + return ZERO + + tti = self._find_ttinfo(dt) + + if not tti.isdst: + return ZERO + + # The documentation says that utcoffset()-dst() must + # be constant for every dt. + return tti.dstoffset + + @tzname_in_python2 + def tzname(self, dt): + if not self._ttinfo_std or dt is None: + return None + return self._find_ttinfo(dt).abbr + + def __eq__(self, other): + if not isinstance(other, tzfile): + return NotImplemented + return (self._trans_list == other._trans_list and + self._trans_idx == other._trans_idx and + self._ttinfo_list == other._ttinfo_list) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._filename)) + + def __reduce__(self): + return self.__reduce_ex__(None) + + def __reduce_ex__(self, protocol): + return (self.__class__, (None, self._filename), self.__dict__) + + +class tzrange(tzrangebase): + """ + The ``tzrange`` object is a time zone specified by a set of offsets and + abbreviations, equivalent to the way the ``TZ`` variable can be specified + in POSIX-like systems, but using Python delta objects to specify DST + start, end and offsets. + + :param stdabbr: + The abbreviation for standard time (e.g. ``'EST'``). + + :param stdoffset: + An integer or :class:`datetime.timedelta` object or equivalent + specifying the base offset from UTC. + + If unspecified, +00:00 is used. + + :param dstabbr: + The abbreviation for DST / "Summer" time (e.g. ``'EDT'``). + + If specified, with no other DST information, DST is assumed to occur + and the default behavior or ``dstoffset``, ``start`` and ``end`` is + used. If unspecified and no other DST information is specified, it + is assumed that this zone has no DST. + + If this is unspecified and other DST information is *is* specified, + DST occurs in the zone but the time zone abbreviation is left + unchanged. + + :param dstoffset: + A an integer or :class:`datetime.timedelta` object or equivalent + specifying the UTC offset during DST. If unspecified and any other DST + information is specified, it is assumed to be the STD offset +1 hour. + + :param start: + A :class:`relativedelta.relativedelta` object or equivalent specifying + the time and time of year that daylight savings time starts. To + specify, for example, that DST starts at 2AM on the 2nd Sunday in + March, pass: + + ``relativedelta(hours=2, month=3, day=1, weekday=SU(+2))`` + + If unspecified and any other DST information is specified, the default + value is 2 AM on the first Sunday in April. + + :param end: + A :class:`relativedelta.relativedelta` object or equivalent + representing the time and time of year that daylight savings time + ends, with the same specification method as in ``start``. One note is + that this should point to the first time in the *standard* zone, so if + a transition occurs at 2AM in the DST zone and the clocks are set back + 1 hour to 1AM, set the ``hours`` parameter to +1. + + + **Examples:** + + .. testsetup:: tzrange + + from dateutil.tz import tzrange, tzstr + + .. doctest:: tzrange + + >>> tzstr('EST5EDT') == tzrange("EST", -18000, "EDT") + True + + >>> from dateutil.relativedelta import * + >>> range1 = tzrange("EST", -18000, "EDT") + >>> range2 = tzrange("EST", -18000, "EDT", -14400, + ... relativedelta(hours=+2, month=4, day=1, + ... weekday=SU(+1)), + ... relativedelta(hours=+1, month=10, day=31, + ... weekday=SU(-1))) + >>> tzstr('EST5EDT') == range1 == range2 + True + + """ + def __init__(self, stdabbr, stdoffset=None, + dstabbr=None, dstoffset=None, + start=None, end=None): + + global relativedelta + from dateutil import relativedelta + + self._std_abbr = stdabbr + self._dst_abbr = dstabbr + + try: + stdoffset = stdoffset.total_seconds() + except (TypeError, AttributeError): + pass + + try: + dstoffset = dstoffset.total_seconds() + except (TypeError, AttributeError): + pass + + if stdoffset is not None: + self._std_offset = datetime.timedelta(seconds=stdoffset) + else: + self._std_offset = ZERO + + if dstoffset is not None: + self._dst_offset = datetime.timedelta(seconds=dstoffset) + elif dstabbr and stdoffset is not None: + self._dst_offset = self._std_offset + datetime.timedelta(hours=+1) + else: + self._dst_offset = ZERO + + if dstabbr and start is None: + self._start_delta = relativedelta.relativedelta( + hours=+2, month=4, day=1, weekday=relativedelta.SU(+1)) + else: + self._start_delta = start + + if dstabbr and end is None: + self._end_delta = relativedelta.relativedelta( + hours=+1, month=10, day=31, weekday=relativedelta.SU(-1)) + else: + self._end_delta = end + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = bool(self._start_delta) + + def transitions(self, year): + """ + For a given year, get the DST on and off transition times, expressed + always on the standard time side. For zones with no transitions, this + function returns ``None``. + + :param year: + The year whose transitions you would like to query. + + :return: + Returns a :class:`tuple` of :class:`datetime.datetime` objects, + ``(dston, dstoff)`` for zones with an annual DST transition, or + ``None`` for fixed offset zones. + """ + if not self.hasdst: + return None + + base_year = datetime.datetime(year, 1, 1) + + start = base_year + self._start_delta + end = base_year + self._end_delta + + return (start, end) + + def __eq__(self, other): + if not isinstance(other, tzrange): + return NotImplemented + + return (self._std_abbr == other._std_abbr and + self._dst_abbr == other._dst_abbr and + self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset and + self._start_delta == other._start_delta and + self._end_delta == other._end_delta) + + @property + def _dst_base_offset(self): + return self._dst_base_offset_ + + +@six.add_metaclass(_TzStrFactory) +class tzstr(tzrange): + """ + ``tzstr`` objects are time zone objects specified by a time-zone string as + it would be passed to a ``TZ`` variable on POSIX-style systems (see + the `GNU C Library: TZ Variable`_ for more details). + + There is one notable exception, which is that POSIX-style time zones use an + inverted offset format, so normally ``GMT+3`` would be parsed as an offset + 3 hours *behind* GMT. The ``tzstr`` time zone object will parse this as an + offset 3 hours *ahead* of GMT. If you would like to maintain the POSIX + behavior, pass a ``True`` value to ``posix_offset``. + + The :class:`tzrange` object provides the same functionality, but is + specified using :class:`relativedelta.relativedelta` objects. rather than + strings. + + :param s: + A time zone string in ``TZ`` variable format. This can be a + :class:`bytes` (2.x: :class:`str`), :class:`str` (2.x: + :class:`unicode`) or a stream emitting unicode characters + (e.g. :class:`StringIO`). + + :param posix_offset: + Optional. If set to ``True``, interpret strings such as ``GMT+3`` or + ``UTC+3`` as being 3 hours *behind* UTC rather than ahead, per the + POSIX standard. + + .. caution:: + + Prior to version 2.7.0, this function also supported time zones + in the format: + + * ``EST5EDT,4,0,6,7200,10,0,26,7200,3600`` + * ``EST5EDT,4,1,0,7200,10,-1,0,7200,3600`` + + This format is non-standard and has been deprecated; this function + will raise a :class:`DeprecatedTZFormatWarning` until + support is removed in a future version. + + .. _`GNU C Library: TZ Variable`: + https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + """ + def __init__(self, s, posix_offset=False): + global parser + from dateutil.parser import _parser as parser + + self._s = s + + res = parser._parsetz(s) + if res is None or res.any_unused_tokens: + raise ValueError("unknown string format") + + # Here we break the compatibility with the TZ variable handling. + # GMT-3 actually *means* the timezone -3. + if res.stdabbr in ("GMT", "UTC") and not posix_offset: + res.stdoffset *= -1 + + # We must initialize it first, since _delta() needs + # _std_offset and _dst_offset set. Use False in start/end + # to avoid building it two times. + tzrange.__init__(self, res.stdabbr, res.stdoffset, + res.dstabbr, res.dstoffset, + start=False, end=False) + + if not res.dstabbr: + self._start_delta = None + self._end_delta = None + else: + self._start_delta = self._delta(res.start) + if self._start_delta: + self._end_delta = self._delta(res.end, isend=1) + + self.hasdst = bool(self._start_delta) + + def _delta(self, x, isend=0): + from dateutil import relativedelta + kwargs = {} + if x.month is not None: + kwargs["month"] = x.month + if x.weekday is not None: + kwargs["weekday"] = relativedelta.weekday(x.weekday, x.week) + if x.week > 0: + kwargs["day"] = 1 + else: + kwargs["day"] = 31 + elif x.day: + kwargs["day"] = x.day + elif x.yday is not None: + kwargs["yearday"] = x.yday + elif x.jyday is not None: + kwargs["nlyearday"] = x.jyday + if not kwargs: + # Default is to start on first sunday of april, and end + # on last sunday of october. + if not isend: + kwargs["month"] = 4 + kwargs["day"] = 1 + kwargs["weekday"] = relativedelta.SU(+1) + else: + kwargs["month"] = 10 + kwargs["day"] = 31 + kwargs["weekday"] = relativedelta.SU(-1) + if x.time is not None: + kwargs["seconds"] = x.time + else: + # Default is 2AM. + kwargs["seconds"] = 7200 + if isend: + # Convert to standard time, to follow the documented way + # of working with the extra hour. See the documentation + # of the tzinfo class. + delta = self._dst_offset - self._std_offset + kwargs["seconds"] -= delta.seconds + delta.days * 86400 + return relativedelta.relativedelta(**kwargs) + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._s)) + + +class _tzicalvtzcomp(object): + def __init__(self, tzoffsetfrom, tzoffsetto, isdst, + tzname=None, rrule=None): + self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom) + self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto) + self.tzoffsetdiff = self.tzoffsetto - self.tzoffsetfrom + self.isdst = isdst + self.tzname = tzname + self.rrule = rrule + + +class _tzicalvtz(_tzinfo): + def __init__(self, tzid, comps=[]): + super(_tzicalvtz, self).__init__() + + self._tzid = tzid + self._comps = comps + self._cachedate = [] + self._cachecomp = [] + self._cache_lock = _thread.allocate_lock() + + def _find_comp(self, dt): + if len(self._comps) == 1: + return self._comps[0] + + dt = dt.replace(tzinfo=None) + + try: + with self._cache_lock: + return self._cachecomp[self._cachedate.index( + (dt, self._fold(dt)))] + except ValueError: + pass + + lastcompdt = None + lastcomp = None + + for comp in self._comps: + compdt = self._find_compdt(comp, dt) + + if compdt and (not lastcompdt or lastcompdt < compdt): + lastcompdt = compdt + lastcomp = comp + + if not lastcomp: + # RFC says nothing about what to do when a given + # time is before the first onset date. We'll look for the + # first standard component, or the first component, if + # none is found. + for comp in self._comps: + if not comp.isdst: + lastcomp = comp + break + else: + lastcomp = comp[0] + + with self._cache_lock: + self._cachedate.insert(0, (dt, self._fold(dt))) + self._cachecomp.insert(0, lastcomp) + + if len(self._cachedate) > 10: + self._cachedate.pop() + self._cachecomp.pop() + + return lastcomp + + def _find_compdt(self, comp, dt): + if comp.tzoffsetdiff < ZERO and self._fold(dt): + dt -= comp.tzoffsetdiff + + compdt = comp.rrule.before(dt, inc=True) + + return compdt + + def utcoffset(self, dt): + if dt is None: + return None + + return self._find_comp(dt).tzoffsetto + + def dst(self, dt): + comp = self._find_comp(dt) + if comp.isdst: + return comp.tzoffsetdiff + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._find_comp(dt).tzname + + def __repr__(self): + return "" % repr(self._tzid) + + __reduce__ = object.__reduce__ + + +class tzical(object): + """ + This object is designed to parse an iCalendar-style ``VTIMEZONE`` structure + as set out in `RFC 5545`_ Section 4.6.5 into one or more `tzinfo` objects. + + :param `fileobj`: + A file or stream in iCalendar format, which should be UTF-8 encoded + with CRLF endings. + + .. _`RFC 5545`: https://tools.ietf.org/html/rfc5545 + """ + def __init__(self, fileobj): + global rrule + from dateutil import rrule + + if isinstance(fileobj, string_types): + self._s = fileobj + # ical should be encoded in UTF-8 with CRLF + fileobj = open(fileobj, 'r') + else: + self._s = getattr(fileobj, 'name', repr(fileobj)) + fileobj = _nullcontext(fileobj) + + self._vtz = {} + + with fileobj as fobj: + self._parse_rfc(fobj.read()) + + def keys(self): + """ + Retrieves the available time zones as a list. + """ + return list(self._vtz.keys()) + + def get(self, tzid=None): + """ + Retrieve a :py:class:`datetime.tzinfo` object by its ``tzid``. + + :param tzid: + If there is exactly one time zone available, omitting ``tzid`` + or passing :py:const:`None` value returns it. Otherwise a valid + key (which can be retrieved from :func:`keys`) is required. + + :raises ValueError: + Raised if ``tzid`` is not specified but there are either more + or fewer than 1 zone defined. + + :returns: + Returns either a :py:class:`datetime.tzinfo` object representing + the relevant time zone or :py:const:`None` if the ``tzid`` was + not found. + """ + if tzid is None: + if len(self._vtz) == 0: + raise ValueError("no timezones defined") + elif len(self._vtz) > 1: + raise ValueError("more than one timezone available") + tzid = next(iter(self._vtz)) + + return self._vtz.get(tzid) + + def _parse_offset(self, s): + s = s.strip() + if not s: + raise ValueError("empty offset") + if s[0] in ('+', '-'): + signal = (-1, +1)[s[0] == '+'] + s = s[1:] + else: + signal = +1 + if len(s) == 4: + return (int(s[:2]) * 3600 + int(s[2:]) * 60) * signal + elif len(s) == 6: + return (int(s[:2]) * 3600 + int(s[2:4]) * 60 + int(s[4:])) * signal + else: + raise ValueError("invalid offset: " + s) + + def _parse_rfc(self, s): + lines = s.splitlines() + if not lines: + raise ValueError("empty string") + + # Unfold + i = 0 + while i < len(lines): + line = lines[i].rstrip() + if not line: + del lines[i] + elif i > 0 and line[0] == " ": + lines[i-1] += line[1:] + del lines[i] + else: + i += 1 + + tzid = None + comps = [] + invtz = False + comptype = None + for line in lines: + if not line: + continue + name, value = line.split(':', 1) + parms = name.split(';') + if not parms: + raise ValueError("empty property name") + name = parms[0].upper() + parms = parms[1:] + if invtz: + if name == "BEGIN": + if value in ("STANDARD", "DAYLIGHT"): + # Process component + pass + else: + raise ValueError("unknown component: "+value) + comptype = value + founddtstart = False + tzoffsetfrom = None + tzoffsetto = None + rrulelines = [] + tzname = None + elif name == "END": + if value == "VTIMEZONE": + if comptype: + raise ValueError("component not closed: "+comptype) + if not tzid: + raise ValueError("mandatory TZID not found") + if not comps: + raise ValueError( + "at least one component is needed") + # Process vtimezone + self._vtz[tzid] = _tzicalvtz(tzid, comps) + invtz = False + elif value == comptype: + if not founddtstart: + raise ValueError("mandatory DTSTART not found") + if tzoffsetfrom is None: + raise ValueError( + "mandatory TZOFFSETFROM not found") + if tzoffsetto is None: + raise ValueError( + "mandatory TZOFFSETFROM not found") + # Process component + rr = None + if rrulelines: + rr = rrule.rrulestr("\n".join(rrulelines), + compatible=True, + ignoretz=True, + cache=True) + comp = _tzicalvtzcomp(tzoffsetfrom, tzoffsetto, + (comptype == "DAYLIGHT"), + tzname, rr) + comps.append(comp) + comptype = None + else: + raise ValueError("invalid component end: "+value) + elif comptype: + if name == "DTSTART": + # DTSTART in VTIMEZONE takes a subset of valid RRULE + # values under RFC 5545. + for parm in parms: + if parm != 'VALUE=DATE-TIME': + msg = ('Unsupported DTSTART param in ' + + 'VTIMEZONE: ' + parm) + raise ValueError(msg) + rrulelines.append(line) + founddtstart = True + elif name in ("RRULE", "RDATE", "EXRULE", "EXDATE"): + rrulelines.append(line) + elif name == "TZOFFSETFROM": + if parms: + raise ValueError( + "unsupported %s parm: %s " % (name, parms[0])) + tzoffsetfrom = self._parse_offset(value) + elif name == "TZOFFSETTO": + if parms: + raise ValueError( + "unsupported TZOFFSETTO parm: "+parms[0]) + tzoffsetto = self._parse_offset(value) + elif name == "TZNAME": + if parms: + raise ValueError( + "unsupported TZNAME parm: "+parms[0]) + tzname = value + elif name == "COMMENT": + pass + else: + raise ValueError("unsupported property: "+name) + else: + if name == "TZID": + if parms: + raise ValueError( + "unsupported TZID parm: "+parms[0]) + tzid = value + elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"): + pass + else: + raise ValueError("unsupported property: "+name) + elif name == "BEGIN" and value == "VTIMEZONE": + tzid = None + comps = [] + invtz = True + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._s)) + + +if sys.platform != "win32": + TZFILES = ["/etc/localtime", "localtime"] + TZPATHS = ["/usr/share/zoneinfo", + "/usr/lib/zoneinfo", + "/usr/share/lib/zoneinfo", + "/etc/zoneinfo"] +else: + TZFILES = [] + TZPATHS = [] + + +def __get_gettz(): + tzlocal_classes = (tzlocal,) + if tzwinlocal is not None: + tzlocal_classes += (tzwinlocal,) + + class GettzFunc(object): + """ + Retrieve a time zone object from a string representation + + This function is intended to retrieve the :py:class:`tzinfo` subclass + that best represents the time zone that would be used if a POSIX + `TZ variable`_ were set to the same value. + + If no argument or an empty string is passed to ``gettz``, local time + is returned: + + .. code-block:: python3 + + >>> gettz() + tzfile('/etc/localtime') + + This function is also the preferred way to map IANA tz database keys + to :class:`tzfile` objects: + + .. code-block:: python3 + + >>> gettz('Pacific/Kiritimati') + tzfile('/usr/share/zoneinfo/Pacific/Kiritimati') + + On Windows, the standard is extended to include the Windows-specific + zone names provided by the operating system: + + .. code-block:: python3 + + >>> gettz('Egypt Standard Time') + tzwin('Egypt Standard Time') + + Passing a GNU ``TZ`` style string time zone specification returns a + :class:`tzstr` object: + + .. code-block:: python3 + + >>> gettz('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') + tzstr('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') + + :param name: + A time zone name (IANA, or, on Windows, Windows keys), location of + a ``tzfile(5)`` zoneinfo file or ``TZ`` variable style time zone + specifier. An empty string, no argument or ``None`` is interpreted + as local time. + + :return: + Returns an instance of one of ``dateutil``'s :py:class:`tzinfo` + subclasses. + + .. versionchanged:: 2.7.0 + + After version 2.7.0, any two calls to ``gettz`` using the same + input strings will return the same object: + + .. code-block:: python3 + + >>> tz.gettz('America/Chicago') is tz.gettz('America/Chicago') + True + + In addition to improving performance, this ensures that + `"same zone" semantics`_ are used for datetimes in the same zone. + + + .. _`TZ variable`: + https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + + .. _`"same zone" semantics`: + https://blog.ganssle.io/articles/2018/02/aware-datetime-arithmetic.html + """ + def __init__(self): + + self.__instances = weakref.WeakValueDictionary() + self.__strong_cache_size = 8 + self.__strong_cache = OrderedDict() + self._cache_lock = _thread.allocate_lock() + + def __call__(self, name=None): + with self._cache_lock: + rv = self.__instances.get(name, None) + + if rv is None: + rv = self.nocache(name=name) + if not (name is None + or isinstance(rv, tzlocal_classes) + or rv is None): + # tzlocal is slightly more complicated than the other + # time zone providers because it depends on environment + # at construction time, so don't cache that. + # + # We also cannot store weak references to None, so we + # will also not store that. + self.__instances[name] = rv + else: + # No need for strong caching, return immediately + return rv + + self.__strong_cache[name] = self.__strong_cache.pop(name, rv) + + if len(self.__strong_cache) > self.__strong_cache_size: + self.__strong_cache.popitem(last=False) + + return rv + + def set_cache_size(self, size): + with self._cache_lock: + self.__strong_cache_size = size + while len(self.__strong_cache) > size: + self.__strong_cache.popitem(last=False) + + def cache_clear(self): + with self._cache_lock: + self.__instances = weakref.WeakValueDictionary() + self.__strong_cache.clear() + + @staticmethod + def nocache(name=None): + """A non-cached version of gettz""" + tz = None + if not name: + try: + name = os.environ["TZ"] + except KeyError: + pass + if name is None or name == ":": + for filepath in TZFILES: + if not os.path.isabs(filepath): + filename = filepath + for path in TZPATHS: + filepath = os.path.join(path, filename) + if os.path.isfile(filepath): + break + else: + continue + if os.path.isfile(filepath): + try: + tz = tzfile(filepath) + break + except (IOError, OSError, ValueError): + pass + else: + tz = tzlocal() + else: + try: + if name.startswith(":"): + name = name[1:] + except TypeError as e: + if isinstance(name, bytes): + new_msg = "gettz argument should be str, not bytes" + six.raise_from(TypeError(new_msg), e) + else: + raise + if os.path.isabs(name): + if os.path.isfile(name): + tz = tzfile(name) + else: + tz = None + else: + for path in TZPATHS: + filepath = os.path.join(path, name) + if not os.path.isfile(filepath): + filepath = filepath.replace(' ', '_') + if not os.path.isfile(filepath): + continue + try: + tz = tzfile(filepath) + break + except (IOError, OSError, ValueError): + pass + else: + tz = None + if tzwin is not None: + try: + tz = tzwin(name) + except (WindowsError, UnicodeEncodeError): + # UnicodeEncodeError is for Python 2.7 compat + tz = None + + if not tz: + from dateutil.zoneinfo import get_zonefile_instance + tz = get_zonefile_instance().get(name) + + if not tz: + for c in name: + # name is not a tzstr unless it has at least + # one offset. For short values of "name", an + # explicit for loop seems to be the fastest way + # To determine if a string contains a digit + if c in "0123456789": + try: + tz = tzstr(name) + except ValueError: + pass + break + else: + if name in ("GMT", "UTC"): + tz = UTC + elif name in time.tzname: + tz = tzlocal() + return tz + + return GettzFunc() + + +gettz = __get_gettz() +del __get_gettz + + +def datetime_exists(dt, tz=None): + """ + Given a datetime and a time zone, determine whether or not a given datetime + would fall in a gap. + + :param dt: + A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` + is provided.) + + :param tz: + A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If + ``None`` or not provided, the datetime's own time zone will be used. + + :return: + Returns a boolean value whether or not the "wall time" exists in + ``tz``. + + .. versionadded:: 2.7.0 + """ + if tz is None: + if dt.tzinfo is None: + raise ValueError('Datetime is naive and no time zone provided.') + tz = dt.tzinfo + + dt = dt.replace(tzinfo=None) + + # This is essentially a test of whether or not the datetime can survive + # a round trip to UTC. + dt_rt = dt.replace(tzinfo=tz).astimezone(UTC).astimezone(tz) + dt_rt = dt_rt.replace(tzinfo=None) + + return dt == dt_rt + + +def datetime_ambiguous(dt, tz=None): + """ + Given a datetime and a time zone, determine whether or not a given datetime + is ambiguous (i.e if there are two times differentiated only by their DST + status). + + :param dt: + A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` + is provided.) + + :param tz: + A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If + ``None`` or not provided, the datetime's own time zone will be used. + + :return: + Returns a boolean value whether or not the "wall time" is ambiguous in + ``tz``. + + .. versionadded:: 2.6.0 + """ + if tz is None: + if dt.tzinfo is None: + raise ValueError('Datetime is naive and no time zone provided.') + + tz = dt.tzinfo + + # If a time zone defines its own "is_ambiguous" function, we'll use that. + is_ambiguous_fn = getattr(tz, 'is_ambiguous', None) + if is_ambiguous_fn is not None: + try: + return tz.is_ambiguous(dt) + except Exception: + pass + + # If it doesn't come out and tell us it's ambiguous, we'll just check if + # the fold attribute has any effect on this particular date and time. + dt = dt.replace(tzinfo=tz) + wall_0 = enfold(dt, fold=0) + wall_1 = enfold(dt, fold=1) + + same_offset = wall_0.utcoffset() == wall_1.utcoffset() + same_dst = wall_0.dst() == wall_1.dst() + + return not (same_offset and same_dst) + + +def resolve_imaginary(dt): + """ + Given a datetime that may be imaginary, return an existing datetime. + + This function assumes that an imaginary datetime represents what the + wall time would be in a zone had the offset transition not occurred, so + it will always fall forward by the transition's change in offset. + + .. doctest:: + + >>> from dateutil import tz + >>> from datetime import datetime + >>> NYC = tz.gettz('America/New_York') + >>> print(tz.resolve_imaginary(datetime(2017, 3, 12, 2, 30, tzinfo=NYC))) + 2017-03-12 03:30:00-04:00 + + >>> KIR = tz.gettz('Pacific/Kiritimati') + >>> print(tz.resolve_imaginary(datetime(1995, 1, 1, 12, 30, tzinfo=KIR))) + 1995-01-02 12:30:00+14:00 + + As a note, :func:`datetime.astimezone` is guaranteed to produce a valid, + existing datetime, so a round-trip to and from UTC is sufficient to get + an extant datetime, however, this generally "falls back" to an earlier time + rather than falling forward to the STD side (though no guarantees are made + about this behavior). + + :param dt: + A :class:`datetime.datetime` which may or may not exist. + + :return: + Returns an existing :class:`datetime.datetime`. If ``dt`` was not + imaginary, the datetime returned is guaranteed to be the same object + passed to the function. + + .. versionadded:: 2.7.0 + """ + if dt.tzinfo is not None and not datetime_exists(dt): + + curr_offset = (dt + datetime.timedelta(hours=24)).utcoffset() + old_offset = (dt - datetime.timedelta(hours=24)).utcoffset() + + dt += curr_offset - old_offset + + return dt + + +def _datetime_to_timestamp(dt): + """ + Convert a :class:`datetime.datetime` object to an epoch timestamp in + seconds since January 1, 1970, ignoring the time zone. + """ + return (dt.replace(tzinfo=None) - EPOCH).total_seconds() + + +if sys.version_info >= (3, 6): + def _get_supported_offset(second_offset): + return second_offset +else: + def _get_supported_offset(second_offset): + # For python pre-3.6, round to full-minutes if that's not the case. + # Python's datetime doesn't accept sub-minute timezones. Check + # http://python.org/sf/1447945 or https://bugs.python.org/issue5288 + # for some information. + old_offset = second_offset + calculated_offset = 60 * ((second_offset + 30) // 60) + return calculated_offset + + +try: + # Python 3.7 feature + from contextlib import nullcontext as _nullcontext +except ImportError: + class _nullcontext(object): + """ + Class for wrapping contexts so that they are passed through in a + with statement. + """ + def __init__(self, context): + self.context = context + + def __enter__(self): + return self.context + + def __exit__(*args, **kwargs): + pass + +# vim:ts=4:sw=4:et diff --git a/bots-venv/lib/python3.7/site-packages/dateutil/tz/win.py b/bots-venv/lib/python3.7/site-packages/dateutil/tz/win.py new file mode 100644 index 0000000..cde07ba --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/dateutil/tz/win.py @@ -0,0 +1,370 @@ +# -*- coding: utf-8 -*- +""" +This module provides an interface to the native time zone data on Windows, +including :py:class:`datetime.tzinfo` implementations. + +Attempting to import this module on a non-Windows platform will raise an +:py:obj:`ImportError`. +""" +# This code was originally contributed by Jeffrey Harris. +import datetime +import struct + +from six.moves import winreg +from six import text_type + +try: + import ctypes + from ctypes import wintypes +except ValueError: + # ValueError is raised on non-Windows systems for some horrible reason. + raise ImportError("Running tzwin on non-Windows system") + +from ._common import tzrangebase + +__all__ = ["tzwin", "tzwinlocal", "tzres"] + +ONEWEEK = datetime.timedelta(7) + +TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" +TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones" +TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" + + +def _settzkeyname(): + handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + try: + winreg.OpenKey(handle, TZKEYNAMENT).Close() + TZKEYNAME = TZKEYNAMENT + except WindowsError: + TZKEYNAME = TZKEYNAME9X + handle.Close() + return TZKEYNAME + + +TZKEYNAME = _settzkeyname() + + +class tzres(object): + """ + Class for accessing ``tzres.dll``, which contains timezone name related + resources. + + .. versionadded:: 2.5.0 + """ + p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char + + def __init__(self, tzres_loc='tzres.dll'): + # Load the user32 DLL so we can load strings from tzres + user32 = ctypes.WinDLL('user32') + + # Specify the LoadStringW function + user32.LoadStringW.argtypes = (wintypes.HINSTANCE, + wintypes.UINT, + wintypes.LPWSTR, + ctypes.c_int) + + self.LoadStringW = user32.LoadStringW + self._tzres = ctypes.WinDLL(tzres_loc) + self.tzres_loc = tzres_loc + + def load_name(self, offset): + """ + Load a timezone name from a DLL offset (integer). + + >>> from dateutil.tzwin import tzres + >>> tzr = tzres() + >>> print(tzr.load_name(112)) + 'Eastern Standard Time' + + :param offset: + A positive integer value referring to a string from the tzres dll. + + .. note:: + + Offsets found in the registry are generally of the form + ``@tzres.dll,-114``. The offset in this case is 114, not -114. + + """ + resource = self.p_wchar() + lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR) + nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0) + return resource[:nchar] + + def name_from_string(self, tzname_str): + """ + Parse strings as returned from the Windows registry into the time zone + name as defined in the registry. + + >>> from dateutil.tzwin import tzres + >>> tzr = tzres() + >>> print(tzr.name_from_string('@tzres.dll,-251')) + 'Dateline Daylight Time' + >>> print(tzr.name_from_string('Eastern Standard Time')) + 'Eastern Standard Time' + + :param tzname_str: + A timezone name string as returned from a Windows registry key. + + :return: + Returns the localized timezone string from tzres.dll if the string + is of the form `@tzres.dll,-offset`, else returns the input string. + """ + if not tzname_str.startswith('@'): + return tzname_str + + name_splt = tzname_str.split(',-') + try: + offset = int(name_splt[1]) + except: + raise ValueError("Malformed timezone string.") + + return self.load_name(offset) + + +class tzwinbase(tzrangebase): + """tzinfo class based on win32's timezones available in the registry.""" + def __init__(self): + raise NotImplementedError('tzwinbase is an abstract base class') + + def __eq__(self, other): + # Compare on all relevant dimensions, including name. + if not isinstance(other, tzwinbase): + return NotImplemented + + return (self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset and + self._stddayofweek == other._stddayofweek and + self._dstdayofweek == other._dstdayofweek and + self._stdweeknumber == other._stdweeknumber and + self._dstweeknumber == other._dstweeknumber and + self._stdhour == other._stdhour and + self._dsthour == other._dsthour and + self._stdminute == other._stdminute and + self._dstminute == other._dstminute and + self._std_abbr == other._std_abbr and + self._dst_abbr == other._dst_abbr) + + @staticmethod + def list(): + """Return a list of all time zones known to the system.""" + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + with winreg.OpenKey(handle, TZKEYNAME) as tzkey: + result = [winreg.EnumKey(tzkey, i) + for i in range(winreg.QueryInfoKey(tzkey)[0])] + return result + + def display(self): + """ + Return the display name of the time zone. + """ + return self._display + + def transitions(self, year): + """ + For a given year, get the DST on and off transition times, expressed + always on the standard time side. For zones with no transitions, this + function returns ``None``. + + :param year: + The year whose transitions you would like to query. + + :return: + Returns a :class:`tuple` of :class:`datetime.datetime` objects, + ``(dston, dstoff)`` for zones with an annual DST transition, or + ``None`` for fixed offset zones. + """ + + if not self.hasdst: + return None + + dston = picknthweekday(year, self._dstmonth, self._dstdayofweek, + self._dsthour, self._dstminute, + self._dstweeknumber) + + dstoff = picknthweekday(year, self._stdmonth, self._stddayofweek, + self._stdhour, self._stdminute, + self._stdweeknumber) + + # Ambiguous dates default to the STD side + dstoff -= self._dst_base_offset + + return dston, dstoff + + def _get_hasdst(self): + return self._dstmonth != 0 + + @property + def _dst_base_offset(self): + return self._dst_base_offset_ + + +class tzwin(tzwinbase): + """ + Time zone object created from the zone info in the Windows registry + + These are similar to :py:class:`dateutil.tz.tzrange` objects in that + the time zone data is provided in the format of a single offset rule + for either 0 or 2 time zone transitions per year. + + :param: name + The name of a Windows time zone key, e.g. "Eastern Standard Time". + The full list of keys can be retrieved with :func:`tzwin.list`. + """ + + def __init__(self, name): + self._name = name + + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + tzkeyname = text_type("{kn}\\{name}").format(kn=TZKEYNAME, name=name) + with winreg.OpenKey(handle, tzkeyname) as tzkey: + keydict = valuestodict(tzkey) + + self._std_abbr = keydict["Std"] + self._dst_abbr = keydict["Dlt"] + + self._display = keydict["Display"] + + # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm + tup = struct.unpack("=3l16h", keydict["TZI"]) + stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 + dstoffset = stdoffset-tup[2] # + DaylightBias * -1 + self._std_offset = datetime.timedelta(minutes=stdoffset) + self._dst_offset = datetime.timedelta(minutes=dstoffset) + + # for the meaning see the win32 TIME_ZONE_INFORMATION structure docs + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx + (self._stdmonth, + self._stddayofweek, # Sunday = 0 + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[4:9] + + (self._dstmonth, + self._dstdayofweek, # Sunday = 0 + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[12:17] + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = self._get_hasdst() + + def __repr__(self): + return "tzwin(%s)" % repr(self._name) + + def __reduce__(self): + return (self.__class__, (self._name,)) + + +class tzwinlocal(tzwinbase): + """ + Class representing the local time zone information in the Windows registry + + While :class:`dateutil.tz.tzlocal` makes system calls (via the :mod:`time` + module) to retrieve time zone information, ``tzwinlocal`` retrieves the + rules directly from the Windows registry and creates an object like + :class:`dateutil.tz.tzwin`. + + Because Windows does not have an equivalent of :func:`time.tzset`, on + Windows, :class:`dateutil.tz.tzlocal` instances will always reflect the + time zone settings *at the time that the process was started*, meaning + changes to the machine's time zone settings during the run of a program + on Windows will **not** be reflected by :class:`dateutil.tz.tzlocal`. + Because ``tzwinlocal`` reads the registry directly, it is unaffected by + this issue. + """ + def __init__(self): + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: + keydict = valuestodict(tzlocalkey) + + self._std_abbr = keydict["StandardName"] + self._dst_abbr = keydict["DaylightName"] + + try: + tzkeyname = text_type('{kn}\\{sn}').format(kn=TZKEYNAME, + sn=self._std_abbr) + with winreg.OpenKey(handle, tzkeyname) as tzkey: + _keydict = valuestodict(tzkey) + self._display = _keydict["Display"] + except OSError: + self._display = None + + stdoffset = -keydict["Bias"]-keydict["StandardBias"] + dstoffset = stdoffset-keydict["DaylightBias"] + + self._std_offset = datetime.timedelta(minutes=stdoffset) + self._dst_offset = datetime.timedelta(minutes=dstoffset) + + # For reasons unclear, in this particular key, the day of week has been + # moved to the END of the SYSTEMTIME structure. + tup = struct.unpack("=8h", keydict["StandardStart"]) + + (self._stdmonth, + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[1:5] + + self._stddayofweek = tup[7] + + tup = struct.unpack("=8h", keydict["DaylightStart"]) + + (self._dstmonth, + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[1:5] + + self._dstdayofweek = tup[7] + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = self._get_hasdst() + + def __repr__(self): + return "tzwinlocal()" + + def __str__(self): + # str will return the standard name, not the daylight name. + return "tzwinlocal(%s)" % repr(self._std_abbr) + + def __reduce__(self): + return (self.__class__, ()) + + +def picknthweekday(year, month, dayofweek, hour, minute, whichweek): + """ dayofweek == 0 means Sunday, whichweek 5 means last instance """ + first = datetime.datetime(year, month, 1, hour, minute) + + # This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6), + # Because 7 % 7 = 0 + weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1) + wd = weekdayone + ((whichweek - 1) * ONEWEEK) + if (wd.month != month): + wd -= ONEWEEK + + return wd + + +def valuestodict(key): + """Convert a registry key's values to a dictionary.""" + dout = {} + size = winreg.QueryInfoKey(key)[1] + tz_res = None + + for i in range(size): + key_name, value, dtype = winreg.EnumValue(key, i) + if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN: + # If it's a DWORD (32-bit integer), it's stored as unsigned - convert + # that to a proper signed integer + if value & (1 << 31): + value = value - (1 << 32) + elif dtype == winreg.REG_SZ: + # If it's a reference to the tzres DLL, load the actual string + if value.startswith('@tzres'): + tz_res = tz_res or tzres() + value = tz_res.name_from_string(value) + + value = value.rstrip('\x00') # Remove trailing nulls + + dout[key_name] = value + + return dout diff --git a/bots-venv/lib/python3.7/site-packages/dateutil/tzwin.py b/bots-venv/lib/python3.7/site-packages/dateutil/tzwin.py new file mode 100644 index 0000000..cebc673 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/dateutil/tzwin.py @@ -0,0 +1,2 @@ +# tzwin has moved to dateutil.tz.win +from .tz.win import * diff --git a/bots-venv/lib/python3.7/site-packages/dateutil/utils.py b/bots-venv/lib/python3.7/site-packages/dateutil/utils.py new file mode 100644 index 0000000..44d9c99 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/dateutil/utils.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +""" +This module offers general convenience and utility functions for dealing with +datetimes. + +.. versionadded:: 2.7.0 +""" +from __future__ import unicode_literals + +from datetime import datetime, time + + +def today(tzinfo=None): + """ + Returns a :py:class:`datetime` representing the current day at midnight + + :param tzinfo: + The time zone to attach (also used to determine the current day). + + :return: + A :py:class:`datetime.datetime` object representing the current day + at midnight. + """ + + dt = datetime.now(tzinfo) + return datetime.combine(dt.date(), time(0, tzinfo=tzinfo)) + + +def default_tzinfo(dt, tzinfo): + """ + Sets the ``tzinfo`` parameter on naive datetimes only + + This is useful for example when you are provided a datetime that may have + either an implicit or explicit time zone, such as when parsing a time zone + string. + + .. doctest:: + + >>> from dateutil.tz import tzoffset + >>> from dateutil.parser import parse + >>> from dateutil.utils import default_tzinfo + >>> dflt_tz = tzoffset("EST", -18000) + >>> print(default_tzinfo(parse('2014-01-01 12:30 UTC'), dflt_tz)) + 2014-01-01 12:30:00+00:00 + >>> print(default_tzinfo(parse('2014-01-01 12:30'), dflt_tz)) + 2014-01-01 12:30:00-05:00 + + :param dt: + The datetime on which to replace the time zone + + :param tzinfo: + The :py:class:`datetime.tzinfo` subclass instance to assign to + ``dt`` if (and only if) it is naive. + + :return: + Returns an aware :py:class:`datetime.datetime`. + """ + if dt.tzinfo is not None: + return dt + else: + return dt.replace(tzinfo=tzinfo) + + +def within_delta(dt1, dt2, delta): + """ + Useful for comparing two datetimes that may a negilible difference + to be considered equal. + """ + delta = abs(delta) + difference = dt1 - dt2 + return -delta <= difference <= delta diff --git a/bots-venv/lib/python3.7/site-packages/dateutil/zoneinfo/__init__.py b/bots-venv/lib/python3.7/site-packages/dateutil/zoneinfo/__init__.py new file mode 100644 index 0000000..34f11ad --- /dev/null +++ b/bots-venv/lib/python3.7/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/bots-venv/lib/python3.7/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz b/bots-venv/lib/python3.7/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz new file mode 100644 index 0000000..89e8351 Binary files /dev/null and b/bots-venv/lib/python3.7/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz differ diff --git a/bots-venv/lib/python3.7/site-packages/dateutil/zoneinfo/rebuild.py b/bots-venv/lib/python3.7/site-packages/dateutil/zoneinfo/rebuild.py new file mode 100644 index 0000000..78f0d1a --- /dev/null +++ b/bots-venv/lib/python3.7/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/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/COPYING.txt b/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/COPYING.txt new file mode 100644 index 0000000..bfc8937 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/COPYING.txt @@ -0,0 +1,137 @@ +================== + Copying Docutils +================== + +:Author: David Goodger +:Contact: goodger@python.org +:Date: $Date: 2015-05-08 17:56:32 +0200 (Fr, 08. Mai 2015) $ +:Web site: http://docutils.sourceforge.net/ +:Copyright: This document has been placed in the public domain. + +Most of the files included in this project have been placed in the +public domain, and therefore have no license requirements and no +restrictions on copying or usage; see the `Public Domain Dedication`_ +below. There are a few exceptions_, listed below. +Files in the Sandbox_ are not distributed with Docutils releases and +may have different license terms. + + +Public Domain Dedication +======================== + +The persons who have associated their work with this project (the +"Dedicator": David Goodger and the many contributors to the Docutils +project) hereby dedicate the entire copyright, less the exceptions_ +listed below, in the work of authorship known as "Docutils" identified +below (the "Work") to the public domain. + +The primary repository for the Work is the Internet World Wide Web +site . The Work consists of the +files within the "docutils" module of the Docutils project Subversion +repository (Internet host docutils.svn.sourceforge.net, filesystem path +/svnroot/docutils), whose Internet web interface is located at +. Files dedicated to the +public domain may be identified by the inclusion, near the beginning +of each file, of a declaration of the form:: + + Copyright: This document/module/DTD/stylesheet/file/etc. has been + placed in the public domain. + +Dedicator makes this dedication for the benefit of the public at large +and to the detriment of Dedicator's heirs and successors. Dedicator +intends this dedication to be an overt act of relinquishment in +perpetuity of all present and future rights under copyright law, +whether vested or contingent, in the Work. Dedicator understands that +such relinquishment of all rights includes the relinquishment of all +rights to enforce (by lawsuit or otherwise) those copyrights in the +Work. + +Dedicator recognizes that, once placed in the public domain, the Work +may be freely reproduced, distributed, transmitted, used, modified, +built upon, or otherwise exploited by anyone for any purpose, +commercial or non-commercial, and in any way, including by methods +that have not yet been invented or conceived. + +(This dedication is derived from the text of the `Creative Commons +Public Domain Dedication`. [#]_) + +.. [#] Creative Commons has `retired this legal tool`__ and does not + recommend that it be applied to works: This tool is based on United + States law and may not be applicable outside the US. For dedicating new + works to the public domain, Creative Commons recommend the replacement + Public Domain Dedication CC0_ (CC zero, "No Rights Reserved"). So does + the Free Software Foundation in its license-list_. + + __ http://creativecommons.org/retiredlicenses + .. _CC0: http://creativecommons.org/about/cc0 + +Exceptions +========== + +The exceptions to the `Public Domain Dedication`_ above are: + +* docutils/writers/s5_html/themes/default/iepngfix.htc: + + IE5.5+ PNG Alpha Fix v1.0 by Angus Turnbull + . Free usage permitted as long as + this notice remains intact. + +* docutils/utils/math/__init__.py, + docutils/utils/math/latex2mathml.py, + docutils/writers/xetex/__init__.py, + docutils/writers/latex2e/docutils-05-compat.sty, + docs/user/docutils-05-compat.sty.txt, + docutils/utils/error_reporting.py, + docutils/test/transforms/test_smartquotes.py: + + Copyright © Günter Milde. + Released under the terms of the `2-Clause BSD license`_ + (`local copy `__). + +* docutils/utils/smartquotes.py + + Copyright © 2011 Günter Milde, + based on `SmartyPants`_ © 2003 John Gruber + (released under a 3-Clause BSD license included in the file) + and smartypants.py © 2004, 2007 Chad Miller. + Released under the terms of the `2-Clause BSD license`_ + (`local copy `__). + + .. _SmartyPants: http://daringfireball.net/projects/smartypants/ + +* docutils/utils/math/math2html.py, + docutils/writers/html4css1/math.css + + Copyright © Alex Fernández + These files are part of eLyXer_, released under the `GNU + General Public License`_ version 3 or later. The author relicensed + them for Docutils under the terms of the `2-Clause BSD license`_ + (`local copy `__). + + .. _eLyXer: http://www.nongnu.org/elyxer/ + +* docutils/utils/roman.py, copyright by Mark Pilgrim, released under the + `Python 2.1.1 license`_ (`local copy`__). + + __ licenses/python-2-1-1.txt + +* tools/editors/emacs/rst.el, copyright by Free Software Foundation, + Inc., released under the `GNU General Public License`_ version 3 or + later (`local copy`__). + + __ licenses/gpl-3-0.txt + +The `2-Clause BSD license`_ and the Python licenses are OSI-approved_ +and GPL-compatible_. + +Plaintext versions of all the linked-to licenses are provided in the +licenses_ directory. + +.. _sandbox: http://docutils.sourceforge.net/sandbox/README.html +.. _licenses: licenses/ +.. _Python 2.1.1 license: http://www.python.org/2.1.1/license.html +.. _GNU General Public License: http://www.gnu.org/copyleft/gpl.html +.. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause +.. _OSI-approved: http://opensource.org/licenses/ +.. _license-list: +.. _GPL-compatible: http://www.gnu.org/licenses/license-list.html diff --git a/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/INSTALLER b/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/METADATA b/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/METADATA new file mode 100644 index 0000000..bac2ed6 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/METADATA @@ -0,0 +1,64 @@ +Metadata-Version: 2.1 +Name: docutils +Version: 0.16 +Summary: Docutils -- Python Documentation Utilities +Home-page: http://docutils.sourceforge.net/ +Author: David Goodger +Author-email: goodger@python.org +Maintainer: docutils-develop list +Maintainer-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 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: 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 :: Danish +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 :: Hebrew +Classifier: Natural Language :: Italian +Classifier: Natural Language :: Japanese +Classifier: Natural Language :: Korean +Classifier: Natural Language :: Latvian +Classifier: Natural Language :: Lithuanian +Classifier: Natural Language :: Persian +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 +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* + +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/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/RECORD b/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/RECORD new file mode 100644 index 0000000..0b0d7bb --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/RECORD @@ -0,0 +1,332 @@ +../../../bin/__pycache__/rst2html.cpython-37.pyc,, +../../../bin/__pycache__/rst2html4.cpython-37.pyc,, +../../../bin/__pycache__/rst2html5.cpython-37.pyc,, +../../../bin/__pycache__/rst2latex.cpython-37.pyc,, +../../../bin/__pycache__/rst2man.cpython-37.pyc,, +../../../bin/__pycache__/rst2odt.cpython-37.pyc,, +../../../bin/__pycache__/rst2odt_prepstyles.cpython-37.pyc,, +../../../bin/__pycache__/rst2pseudoxml.cpython-37.pyc,, +../../../bin/__pycache__/rst2s5.cpython-37.pyc,, +../../../bin/__pycache__/rst2xetex.cpython-37.pyc,, +../../../bin/__pycache__/rst2xml.cpython-37.pyc,, +../../../bin/__pycache__/rstpep2html.cpython-37.pyc,, +../../../bin/rst2html.py,sha256=_ZSHNn9dmPKAx6L7qZ8XgfPAy3KZv5MNky6n_8V3xiI,584 +../../../bin/rst2html4.py,sha256=u5YWYGbe9xyegHW30S6gzVpAg8nTI9Xu_ZOeve5CbOo,704 +../../../bin/rst2html5.py,sha256=_r_cnR_zNKLHiLjSTXDkMBLZskLkxmrPt93InJbhkbA,1127 +../../../bin/rst2latex.py,sha256=nLGST5HE554VD4EbuhUn2_6kijGgDFcQgOMa4RrFngI,781 +../../../bin/rst2man.py,sha256=nPtkEv9P7LIoK8GJOeiA66yi1ApVM1gNOFdbOB0CIXM,589 +../../../bin/rst2odt.py,sha256=iH9HaLPbzEfJU599E2DvAW2n2py69TXfJDOIZEHRsyk,754 +../../../bin/rst2odt_prepstyles.py,sha256=B90_1J9aRI6eJlKTYYDiHGyFio8Dfmgli9vJmg9qXHs,1716 +../../../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.16.dist-info/COPYING.txt,sha256=76DIWqmepbR4EkADGw0Cr1TXtVVMh60rK-qbpJD2Zj8,5538 +docutils-0.16.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +docutils-0.16.dist-info/METADATA,sha256=4IVnPgSLMJRQ5ZfIjKFlv0poher_Si1vM7QowzUC1dI,2749 +docutils-0.16.dist-info/RECORD,, +docutils-0.16.dist-info/WHEEL,sha256=h_aVn5OB2IERUjMbi2pucmR_zzWJtk303YXvhh60NJ8,110 +docutils-0.16.dist-info/top_level.txt,sha256=dPq3jQoxpMOEhrZ1tQh3_-9hqhdvOjUplSdSVDAc95A,9 +docutils/__init__.py,sha256=YIX4_xeGvPh3ZINoAqP1zx76lI2RCCEw39wbeigwYWU,7759 +docutils/__pycache__/__init__.cpython-37.pyc,, +docutils/__pycache__/core.cpython-37.pyc,, +docutils/__pycache__/examples.cpython-37.pyc,, +docutils/__pycache__/frontend.cpython-37.pyc,, +docutils/__pycache__/io.cpython-37.pyc,, +docutils/__pycache__/nodes.cpython-37.pyc,, +docutils/__pycache__/statemachine.cpython-37.pyc,, +docutils/core.py,sha256=PTD5CeE4Z42n5f9UJJUxXGBK27fRGQ5kanx5aMlZ5qA,29575 +docutils/examples.py,sha256=nlPe2PYXNvz5ZAVRu7xHHiV7qML7s-eyUu4Gun7j3SY,3959 +docutils/frontend.py,sha256=dxG9PqW_-snfcbR5S2vunCsUUwH1OTauqx0RJxnMPdQ,37086 +docutils/io.py,sha256=th8Zq_UUIWY27bDIB1sLMtYhdzBWE4qo4Gf9VEwL_gk,16765 +docutils/languages/__init__.py,sha256=oPqoleNr7_rp64XNjbg4gdL8owO_gLEZc1HWHKOA26k,1555 +docutils/languages/__pycache__/__init__.cpython-37.pyc,, +docutils/languages/__pycache__/af.cpython-37.pyc,, +docutils/languages/__pycache__/ca.cpython-37.pyc,, +docutils/languages/__pycache__/cs.cpython-37.pyc,, +docutils/languages/__pycache__/da.cpython-37.pyc,, +docutils/languages/__pycache__/de.cpython-37.pyc,, +docutils/languages/__pycache__/en.cpython-37.pyc,, +docutils/languages/__pycache__/eo.cpython-37.pyc,, +docutils/languages/__pycache__/es.cpython-37.pyc,, +docutils/languages/__pycache__/fa.cpython-37.pyc,, +docutils/languages/__pycache__/fi.cpython-37.pyc,, +docutils/languages/__pycache__/fr.cpython-37.pyc,, +docutils/languages/__pycache__/gl.cpython-37.pyc,, +docutils/languages/__pycache__/he.cpython-37.pyc,, +docutils/languages/__pycache__/it.cpython-37.pyc,, +docutils/languages/__pycache__/ja.cpython-37.pyc,, +docutils/languages/__pycache__/ko.cpython-37.pyc,, +docutils/languages/__pycache__/lt.cpython-37.pyc,, +docutils/languages/__pycache__/lv.cpython-37.pyc,, +docutils/languages/__pycache__/nl.cpython-37.pyc,, +docutils/languages/__pycache__/pl.cpython-37.pyc,, +docutils/languages/__pycache__/pt_br.cpython-37.pyc,, +docutils/languages/__pycache__/ru.cpython-37.pyc,, +docutils/languages/__pycache__/sk.cpython-37.pyc,, +docutils/languages/__pycache__/sv.cpython-37.pyc,, +docutils/languages/__pycache__/zh_cn.cpython-37.pyc,, +docutils/languages/__pycache__/zh_tw.cpython-37.pyc,, +docutils/languages/af.py,sha256=6wl-XbsF8wbSiBs5IORna4Lz-WPqnnye_6qr8Cbh_1c,1824 +docutils/languages/ca.py,sha256=7_XR5q5GEXaeI4O5ianMmdnjWY_sRQD1ynuRB_HxUok,1940 +docutils/languages/cs.py,sha256=GARzldc3tDZByhqf_R-cOh1QJDOrFvSJzqewRdWRNXw,1928 +docutils/languages/da.py,sha256=88ZPB6ppJPWqNsaFQKKAC1aEZVLYw13BFR44vecp4MU,1907 +docutils/languages/de.py,sha256=AQTUIl4j3x43kQi5800-lobZHsh06VOsrxfAjS5FHGM,1722 +docutils/languages/en.py,sha256=tWVrPr6593mIzFokX_4Hw1fC6ZWC8GPdgpc72jmQq1g,1848 +docutils/languages/eo.py,sha256=uGSGcjfeHFliqm-f9amMfsPIl3trHONYPXdGR_ybm8U,1948 +docutils/languages/es.py,sha256=nk0PUeVyjFEu_8cNJA9Llsdv-W3kKx-wgOeaYGllqgw,1962 +docutils/languages/fa.py,sha256=vsf5xJxJuzcPXyz8ad3Cuf9ntBoOND0zdhsE4dwJEjI,2044 +docutils/languages/fi.py,sha256=yrE6Zhs-LdknXCpf-WX15QACQ75DroMpET95r3JD_OM,2026 +docutils/languages/fr.py,sha256=FTaOYQQHvqFzkzuhbdFQPS7dPkcpGLUPMeStu0cjFGs,1893 +docutils/languages/gl.py,sha256=6Uoq9rYigieFN0-LeSgoRmB9_ROX60Oqul7faPROMYs,2040 +docutils/languages/he.py,sha256=BfiRLxQ5Y8pivYi7GbGQ5NJ-rEgUId9mstJR6YYkeAQ,2683 +docutils/languages/it.py,sha256=Z3eh86YrmB8aT_Dl56X7W2gqpJgYK0v0QnJ07hekXlM,1808 +docutils/languages/ja.py,sha256=VJc9fRk4H_OhD8wyx3-uGUay3qdqeAIHCZEF67EsMTQ,1942 +docutils/languages/ko.py,sha256=PsZlCPQ5uQsUC2Puk2POTDmTc83cJWOxHmLrnq3-s2U,1880 +docutils/languages/lt.py,sha256=Y1204UnrqJlvM1vyteckkrqkbU02dSvrnpHCB5b23oM,1940 +docutils/languages/lv.py,sha256=tEqF4ya9h4FznB-uiUKQnNLY2XDFRatxKt2evJPhsDM,1867 +docutils/languages/nl.py,sha256=3CjsGFeJgLZEw2xhHxAc4sYDKRW6ODUZsVyibjl7DmM,1865 +docutils/languages/pl.py,sha256=ImdSmS6l4SgZ7uEaWQR5_aLNmv4K5onOInEtinPqWJk,1900 +docutils/languages/pt_br.py,sha256=cW8OceiFMPzPICT6buhnjm3tvEEw4W1PG-GFC4MxhyI,1982 +docutils/languages/ru.py,sha256=AkDQVhcW4cbvP0je-z8UJafQ-s2Lwc-eAIqXGCFZzco,2155 +docutils/languages/sk.py,sha256=v4XMBxIx5d6zQY7ehjZdtgfUy9Hz999XdH9G9d7SxEc,1860 +docutils/languages/sv.py,sha256=yIVWV9He5dbCuMpegSDiuI75tD4GXm9VkN7ra0-xgMI,2103 +docutils/languages/zh_cn.py,sha256=kF2sokHa0Qr-3X45ZeWEb0mImZRlZpiX2TM6d9_GjpY,2026 +docutils/languages/zh_tw.py,sha256=Xwrbf8wSylyMwwaNH6xYeAjMrldiBbDB_LElv67hGPQ,2771 +docutils/nodes.py,sha256=RRwVFRtRtRUzbuoYIWkMoSBXBAfQ4Dc2Xtzvht7XwAY,81241 +docutils/parsers/__init__.py,sha256=aDrATsCPGo92zucFl_n2ludoFSfxOrmdF7U8oQV0uEQ,1584 +docutils/parsers/__pycache__/__init__.cpython-37.pyc,, +docutils/parsers/__pycache__/null.cpython-37.pyc,, +docutils/parsers/null.py,sha256=LtO7n-E6lNOs4mLXZ2SiShB3C630SoJ80ugd9fh1vXI,445 +docutils/parsers/rst/__init__.py,sha256=F6PQ5IyGtYcpxOi0YHekivCrob2iLQIn5JpaqTIME50,16208 +docutils/parsers/rst/__pycache__/__init__.cpython-37.pyc,, +docutils/parsers/rst/__pycache__/roles.cpython-37.pyc,, +docutils/parsers/rst/__pycache__/states.cpython-37.pyc,, +docutils/parsers/rst/__pycache__/tableparser.cpython-37.pyc,, +docutils/parsers/rst/directives/__init__.py,sha256=dk9-IEfhp-xsEcuEkW1PthgJYdkva87rMlAVMh8eRYs,14053 +docutils/parsers/rst/directives/__pycache__/__init__.cpython-37.pyc,, +docutils/parsers/rst/directives/__pycache__/admonitions.cpython-37.pyc,, +docutils/parsers/rst/directives/__pycache__/body.cpython-37.pyc,, +docutils/parsers/rst/directives/__pycache__/html.cpython-37.pyc,, +docutils/parsers/rst/directives/__pycache__/images.cpython-37.pyc,, +docutils/parsers/rst/directives/__pycache__/misc.cpython-37.pyc,, +docutils/parsers/rst/directives/__pycache__/parts.cpython-37.pyc,, +docutils/parsers/rst/directives/__pycache__/references.cpython-37.pyc,, +docutils/parsers/rst/directives/__pycache__/tables.cpython-37.pyc,, +docutils/parsers/rst/directives/admonitions.py,sha256=w38T7YfRc0isuF5qmaw-6wOQgHx9xGHJBqu_gDqQCdI,2413 +docutils/parsers/rst/directives/body.py,sha256=qkeqiZrbSbfd8bMxfSl1M0HJ_vCfZYg63HhUmdldpE0,9191 +docutils/parsers/rst/directives/html.py,sha256=CKHg2_lAFayH2DGoVD_t9--nNKVMh7aNuWmNTef7ffI,3235 +docutils/parsers/rst/directives/images.py,sha256=PthLb5pJJvltqvTLkQSOemWEzO_ZEQbjlbKBilZ03x0,6982 +docutils/parsers/rst/directives/misc.py,sha256=MVqSu3TutboCbNFLIXgJ8EXF2R8Z8K_k2nDJ73JY1_w,23872 +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=QjxFrNMHdH6rgyIiqBcbVqWvy8ousjy5k6ZeDKeZh8s,22815 +docutils/parsers/rst/include/README.txt,sha256=_C6pSfeGShQ0evvKKKZUtx4bP4RFG9UoUlgHoMn06DU,666 +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/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=sOTYRqWClRSCBOO1I46Z8-WYDJPgWtqwwqt9iIKXpls,1014 +docutils/parsers/rst/languages/__pycache__/__init__.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/af.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/ca.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/cs.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/da.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/de.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/en.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/eo.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/es.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/fa.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/fi.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/fr.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/gl.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/he.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/it.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/ja.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/ko.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/lt.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/lv.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/nl.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/pl.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/pt_br.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/ru.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/sk.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/sv.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/zh_cn.cpython-37.pyc,, +docutils/parsers/rst/languages/__pycache__/zh_tw.cpython-37.pyc,, +docutils/parsers/rst/languages/af.py,sha256=CKdMjiS3nx4yNRrzeuwCoT0XeeQmHm2_O8U34BZns7s,3677 +docutils/parsers/rst/languages/ca.py,sha256=7eAACvO9Dpnqujsc3BJZQktHk1RDjmwf4LeEvkV_66o,4467 +docutils/parsers/rst/languages/cs.py,sha256=7g4NgLXvV5RP7oOqQT_TM033xW48mDQVoGGHhKSwZ8I,4857 +docutils/parsers/rst/languages/da.py,sha256=u9zvTSVuqJBQ-KKlcZmi4SMAp_kQXNS6ZsHBEiVmXjY,3765 +docutils/parsers/rst/languages/de.py,sha256=wiBjwHBDmiDIRs9gTtpI63VTyp7zqds5DWy9Kdw9Ntg,3464 +docutils/parsers/rst/languages/en.py,sha256=z_J8xJ1gBTk5MX6u_bonFGoop_e8VF6jWgq9Tzl5gNY,3317 +docutils/parsers/rst/languages/eo.py,sha256=q0025TiEuSZ_8VGZz2n86ZK4o6GVTdus1HBgdxAgz0A,3898 +docutils/parsers/rst/languages/es.py,sha256=vranS55noGU_pSC0f7uyJ12szSoxMKgRJuEK-DQfhpk,4261 +docutils/parsers/rst/languages/fa.py,sha256=XWeO9UdNGKbHWpcsAhEOgBvuLMGxjQ76I5ed7i2szPw,3430 +docutils/parsers/rst/languages/fi.py,sha256=CiWQxGOSnLA05yONUB0tqsnv4IPCbvfPYU7obKRo-kU,3661 +docutils/parsers/rst/languages/fr.py,sha256=42D2mXBLYl1Vc7KnVGi-CFQbhe_DTEIpT26_f3gWknI,3709 +docutils/parsers/rst/languages/gl.py,sha256=48NwM1CfNZOfmSYkqfhUsAww2292kZLPlt_Ty5sGzxE,3711 +docutils/parsers/rst/languages/he.py,sha256=go2_5QONLkGd4KS7ZIBur2vP04GUlN95XnOTizfwZOc,3640 +docutils/parsers/rst/languages/it.py,sha256=ihEL_NivtrckGnU6dT_kTmOrafmPUIC7-e6KMUJ_y2Q,3270 +docutils/parsers/rst/languages/ja.py,sha256=jaRnNN5sTXciz9O9_-kl7rlrScCJC12S3msQWPg5yH0,3863 +docutils/parsers/rst/languages/ko.py,sha256=4cZFij5pbqx_RgKvwg_10TMMARQ9LR_edvLqlLmZbwE,3449 +docutils/parsers/rst/languages/lt.py,sha256=RWSEyHddq14wvtuIbDxRT-JnPuRh8Gk33hOHh5BEXFI,3584 +docutils/parsers/rst/languages/lv.py,sha256=OfnTo1Si1V1E_7yNAwkyQMe6y07FsOA_F-1H2Fb6vIU,3381 +docutils/parsers/rst/languages/nl.py,sha256=2yKY36FtTEkRl-YjBOm9KtLTQR8nTIGT5ORt1qhcWQk,3708 +docutils/parsers/rst/languages/pl.py,sha256=RsEunaFFi6fiko9CZrVTyWTS7MbEuJLd0eAC5EOWWWo,3427 +docutils/parsers/rst/languages/pt_br.py,sha256=wDfQWhP_ZikrL3o9T4SjyfhRmCg8Y9uIS6FnUrTASkw,3992 +docutils/parsers/rst/languages/ru.py,sha256=abw64X77S-WFpIhbbn2v_YzzjphGyjqbMhaQjnLL1N0,3306 +docutils/parsers/rst/languages/sk.py,sha256=dhy_kVHdIVSFW9aBnNBU3YTXstouhmeOMHzZsBQ7vfU,3979 +docutils/parsers/rst/languages/sv.py,sha256=S8vtO81rSQTvAV4rzrzP7XQ1Hg894gtjjlqJ8FcPRf8,3331 +docutils/parsers/rst/languages/zh_cn.py,sha256=6FyYegjPnIFguqQhR9cBu5xxlrkRszNQHWGXEByF05M,4007 +docutils/parsers/rst/languages/zh_tw.py,sha256=PswlAgKcAMvgQB2jbp1hDCpzG63ZfxgBwlJviK8KRHM,5172 +docutils/parsers/rst/roles.py,sha256=o7thTCx0_1-pMkdYseZt5UaYf6RK646ZzhkuRFAePhQ,14944 +docutils/parsers/rst/states.py,sha256=S80k_XG5kA9PNwQh9dlI-JLVDpuRMwObIvPhNxMaQ04,132111 +docutils/parsers/rst/tableparser.py,sha256=Zbvt0mR6LJYeiMR495xe0dgqlK43HK9JjIw20PvcqdM,21001 +docutils/readers/__init__.py,sha256=5alRbkyRVbNkq8njp1egI_OnGyXkxQBwMGa9MbxAoD4,3456 +docutils/readers/__pycache__/__init__.cpython-37.pyc,, +docutils/readers/__pycache__/doctree.cpython-37.pyc,, +docutils/readers/__pycache__/pep.cpython-37.pyc,, +docutils/readers/__pycache__/standalone.cpython-37.pyc,, +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/statemachine.py,sha256=zneRqVNxZaSKK7RGag5QrlbCSAkuphG4qlZXu7CeEXw,57663 +docutils/transforms/__init__.py,sha256=KZ2cNaA87oQNs6Yi_UK2A3V3mUZz4AbZlzaCD-2fFnY,6504 +docutils/transforms/__pycache__/__init__.cpython-37.pyc,, +docutils/transforms/__pycache__/components.cpython-37.pyc,, +docutils/transforms/__pycache__/frontmatter.cpython-37.pyc,, +docutils/transforms/__pycache__/misc.cpython-37.pyc,, +docutils/transforms/__pycache__/parts.cpython-37.pyc,, +docutils/transforms/__pycache__/peps.cpython-37.pyc,, +docutils/transforms/__pycache__/references.cpython-37.pyc,, +docutils/transforms/__pycache__/universal.cpython-37.pyc,, +docutils/transforms/__pycache__/writer_aux.cpython-37.pyc,, +docutils/transforms/components.py,sha256=F0fXyOZsf_OA0QsX-jIAsk3RLCrkEW9GRR1-l0Nx13o,1993 +docutils/transforms/frontmatter.py,sha256=ooKzVho4qdgnkDqbjLql6AauIbJ8vHglQPnv3W4BEFU,20167 +docutils/transforms/misc.py,sha256=0jDAIndz8a7C5TVWLkC44N1pVfhwZu4nu-lnk1GmzGg,4882 +docutils/transforms/parts.py,sha256=jxs_CsaBH7uyvtCrxZqHQynLj4ktKax-_4d2xVDNNFs,6997 +docutils/transforms/peps.py,sha256=NdXwjk5gvzuzWDXred5OEbpPIDn2bWxECrBkYC_gF10,11056 +docutils/transforms/references.py,sha256=3094yQhcQa84BzcX1zjKTK7UGMjhOYejvwcv_8k7WZw,36478 +docutils/transforms/universal.py,sha256=tRcngG0usTWzQESOKqJeO4VCi3KlHzoUxHBw5ZJ_ljM,11380 +docutils/transforms/writer_aux.py,sha256=4Zd8z8s4jTG2J0xSnm5hj1kiZrKN6FqvGvpJ1cb09oU,2609 +docutils/utils/__init__.py,sha256=5VaDEvT45RFBaJvB_g5zbMilxQopc3hOMUa0SI0CRoY,28767 +docutils/utils/__pycache__/__init__.cpython-37.pyc,, +docutils/utils/__pycache__/code_analyzer.cpython-37.pyc,, +docutils/utils/__pycache__/error_reporting.cpython-37.pyc,, +docutils/utils/__pycache__/punctuation_chars.cpython-37.pyc,, +docutils/utils/__pycache__/roman.cpython-37.pyc,, +docutils/utils/__pycache__/smartquotes.cpython-37.pyc,, +docutils/utils/__pycache__/urischemes.cpython-37.pyc,, +docutils/utils/code_analyzer.py,sha256=wdT6BK7wcyYs13lu24KamEmXqRYi0fZgQBJCwE39ff0,5166 +docutils/utils/error_reporting.py,sha256=R0ViAzmz8GWr3Eayu51l6m9kCivbaJLCIf4NOiSPvqk,8460 +docutils/utils/math/__init__.py,sha256=TUGMP6ytwBzbQCHrFSxSl8OcDdbLGfCCkGteBdWm6l0,1755 +docutils/utils/math/__pycache__/__init__.cpython-37.pyc,, +docutils/utils/math/__pycache__/latex2mathml.cpython-37.pyc,, +docutils/utils/math/__pycache__/math2html.cpython-37.pyc,, +docutils/utils/math/__pycache__/tex2mathml_extern.cpython-37.pyc,, +docutils/utils/math/__pycache__/tex2unichar.cpython-37.pyc,, +docutils/utils/math/__pycache__/unichar2tex.cpython-37.pyc,, +docutils/utils/math/latex2mathml.py,sha256=JO-79izteHloIWN33QIhg3v-8cbI_kWGwvfQ32tbarc,17456 +docutils/utils/math/math2html.py,sha256=ibVwl7RbG2F--iwVVDhGoEqxnKj552Y-HYm7TybzREQ,184696 +docutils/utils/math/tex2mathml_extern.py,sha256=Zx-47MMnzGnbP9YLu_Yc6KM7dcuSgzJPfKZy_i0gj5I,5656 +docutils/utils/math/tex2unichar.py,sha256=0tXV29tatGECc6zDV78yvx-6vc5npfO1GukZrzbI5ps,35109 +docutils/utils/math/unichar2tex.py,sha256=pQsCi50-vC7hbl0DsfHEK5Xn9jhHOAOtG6D0tTWGAAY,17590 +docutils/utils/punctuation_chars.py,sha256=UPAtrr1Om1cJ0l3JpNLvnuNryfR8ybXNvOJNriO7PW0,6426 +docutils/utils/roman.py,sha256=nJRHm0rTPqrKFrwJNwOM8ywKIy81DKvAlXPdnOsefIU,2688 +docutils/utils/smartquotes.py,sha256=DuVPLCPUnn1Q_TPn0PzNLRTonSPPNOTgWbo1cc1kQY4,40050 +docutils/utils/urischemes.py,sha256=_wNIoSw8D_Ch4dLGMZmCTceF4mT6jnodFK_H2HDSRLA,6272 +docutils/writers/__init__.py,sha256=ybRAHTZrV29u5zCFTbAcQvh6ZZDRgQqliRn4cPMiVzw,4593 +docutils/writers/__pycache__/__init__.cpython-37.pyc,, +docutils/writers/__pycache__/_html_base.cpython-37.pyc,, +docutils/writers/__pycache__/docutils_xml.cpython-37.pyc,, +docutils/writers/__pycache__/manpage.cpython-37.pyc,, +docutils/writers/__pycache__/null.cpython-37.pyc,, +docutils/writers/__pycache__/pseudoxml.cpython-37.pyc,, +docutils/writers/_html_base.py,sha256=h30bM3-CdDeK-lF1DM3-M1b_-4n7rAjl1N0VP_CRXpE,63552 +docutils/writers/docutils_xml.py,sha256=P8A8o8YnEZQXmHQrXF-D3vXCm29RZE185Fvd_sJWIXg,7134 +docutils/writers/html4css1/__init__.py,sha256=fFw4DkEEIZmPM8B90Gw15raeupFCQZ2dw61gMS4LV9k,33839 +docutils/writers/html4css1/__pycache__/__init__.cpython-37.pyc,, +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=c_rbVRHUFlPFl6tBKv95WoC1DPeDbqL-2vazhCCVJ3k,9612 +docutils/writers/html5_polyglot/__pycache__/__init__.cpython-37.pyc,, +docutils/writers/html5_polyglot/math.css,sha256=hGjowMetSh3oAlkm7_EA2pTtkv_Lg_TgwrBkyNs85Co,4783 +docutils/writers/html5_polyglot/minimal.css,sha256=pdv3dDbMH9X4VQS0aQO3Ufqg1h-waHnNJ0lFeBsy2Bs,7468 +docutils/writers/html5_polyglot/plain.css,sha256=pHd4urXyhNsneZq1MHvnScLIsRhEdyoU9X52L7fF3gM,6939 +docutils/writers/html5_polyglot/template.txt,sha256=HDzUUyAv7gT4ewGQTqfOE2_9HOVyGu9-wCRgsmoCmjQ,114 +docutils/writers/latex2e/__init__.py,sha256=aBoY6-pndI5_6o1Afw-RF2jHIxhKe36ECDEbO4WY3RA,130375 +docutils/writers/latex2e/__pycache__/__init__.cpython-37.pyc,, +docutils/writers/latex2e/default.tex,sha256=KQFojDUG6AFGSZFGWVsQCslQrWT5-JVB29idVWiRtUA,422 +docutils/writers/latex2e/titlepage.tex,sha256=wxPKr4rylqUamK2U0EJXQTaVQ1392ua5DtSVGDqrNsI,534 +docutils/writers/latex2e/xelatex.tex,sha256=oUEz2Ze3wpoMp0X0iVELcUGNtKVre5Ug5Ujge7OiMgU,672 +docutils/writers/manpage.py,sha256=FEM_KtyyY8PgAScTa5R7dXLKlGCJPLbsaapNoI1SORg,36148 +docutils/writers/null.py,sha256=zyIuah_o8SlqvgbOWLRG9chpeNKky0D13lOTtR1E73U,450 +docutils/writers/odf_odt/__init__.py,sha256=8mr-TC7zljTp_mUUL-tZNhCCS5lezfojN1yadSDRqjQ,133436 +docutils/writers/odf_odt/__pycache__/__init__.cpython-37.pyc,, +docutils/writers/odf_odt/__pycache__/pygmentsformatter.cpython-37.pyc,, +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=gOJnTxuKLtM-81wdjIFbH5GyTFLV9m1_ZOu7Lys_MZc,3558 +docutils/writers/pep_html/__pycache__/__init__.cpython-37.pyc,, +docutils/writers/pep_html/pep.css,sha256=UbFfiHHFSWusk2rEv7AUaTCS0r6TxfMzgLtW0VURwGw,6367 +docutils/writers/pep_html/template.txt,sha256=CfyVCCoqqB3Y8hOyt7Uxf0XeyTwtFf926L_hJeaS1wU,1294 +docutils/writers/pseudoxml.py,sha256=2bRL9zMleW8jDuGaINvM52siZiJWH1-zQfEELJE2oPE,772 +docutils/writers/s5_html/__init__.py,sha256=MUczVy19slU_C6s1akdizatEvt5KPi72Bmj2f46r-IU,14688 +docutils/writers/s5_html/__pycache__/__init__.cpython-37.pyc,, +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=ROox1G2bCAOJoht_UttnqAVgFckavEXjlkpBpQj8LFc,5797 +docutils/writers/xetex/__pycache__/__init__.cpython-37.pyc,, diff --git a/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/WHEEL b/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/WHEEL new file mode 100644 index 0000000..78e6f69 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.4) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/top_level.txt b/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/top_level.txt new file mode 100644 index 0000000..5492d76 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/docutils-0.16.dist-info/top_level.txt @@ -0,0 +1 @@ +docutils diff --git a/bots-venv/lib/python3.7/site-packages/docutils/__init__.py b/bots-venv/lib/python3.7/site-packages/docutils/__init__.py new file mode 100644 index 0000000..5bb6504 --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/docutils/__init__.py @@ -0,0 +1,233 @@ +# $Id: __init__.py 8453 2020-01-12 13:28:32Z 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 +from collections import namedtuple + + +__docformat__ = 'reStructuredText' + +__version__ = '0.16' +"""Docutils version identifier (complies with PEP 440):: + + major.minor[.micro][releaselevel[serial]][.dev] + +For version comparison operations, use `__version_info__` (see, below) +rather than parsing the text of `__version__`. + +See 'Version Numbering' in docs/dev/policies.txt. +""" + +VersionInfo = namedtuple( + 'VersionInfo', 'major minor micro releaselevel serial release') + +__version_info__ = VersionInfo( + major=0, + minor=16, + micro=0, + releaselevel='final', # one of 'alpha', 'beta', 'candidate', 'final' + # pre-release serial number (0 for final releases and active development): + serial=0, + release=True # True for official releases and pre-releases + ) +"""Comprehensive version information tuple. See 'Version Numbering' in +docs/dev/policies.txt.""" + +__version_details__ = 'release' +"""Optional extra version details (e.g. 'snapshot 2005-05-29, r3410'). +(For development and release status see `__version_info__`.) +""" + + +class ApplicationError(Exception): pass + +class DataError(ApplicationError): pass + + +class SettingsSpec(object): + + """ + 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/bots-venv/lib/python3.7/site-packages/docutils/core.py b/bots-venv/lib/python3.7/site-packages/docutils/core.py new file mode 100644 index 0000000..940391b --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/docutils/core.py @@ -0,0 +1,666 @@ +# $Id: core.py 8367 2019-08-27 12:09:56Z 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 +""" +from __future__ import print_function + +__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(object): + + """ + 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(u'Unable to open source file for reading:\n' + u' %s\n' % ErrorString(error)) + elif isinstance(error, io.OutputError): + self._stderr.write( + u'Unable to open destination file for writing:\n' + u' %s\n' % ErrorString(error)) + else: + print(u'%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/bots-venv/lib/python3.7/site-packages/docutils/examples.py b/bots-venv/lib/python3.7/site-packages/docutils/examples.py new file mode 100644 index 0000000..395dbbf --- /dev/null +++ b/bots-venv/lib/python3.7/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/bots-venv/lib/python3.7/site-packages/docutils/frontend.py b/bots-venv/lib/python3.7/site-packages/docutils/frontend.py new file mode 100644 index 0000000..ff571cc --- /dev/null +++ b/bots-venv/lib/python3.7/site-packages/docutils/frontend.py @@ -0,0 +1,863 @@ +# $Id: frontend.py 8439 2019-12-13 17:02:41Z 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 codecs +import optparse +from optparse import SUPPRESS_HELP +if sys.version_info >= (3, 0): + from configparser import RawConfigParser + from os import getcwd +else: + from ConfigParser import RawConfigParser + from os import getcwdu as getcwd + +import docutils +import docutils.utils +import docutils.nodes +from docutils.utils.error_reporting import (locale_encoding, SafeString, + ErrorOutput, ErrorString) + +if sys.version_info >= (3, 0): + unicode = str # noqa + + +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 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(u' \t\n') for i in last.split(u',') if i.strip(u' \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(u'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 = 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 ("", [

\n' + self.in_document_title = len(self.body) + else: + assert isinstance(node.parent, nodes.section) + h_level = self.section_level + self.initial_header_level - 1 + atts = {} + if (len(node.parent) >= 2 and + isinstance(node.parent[1], nodes.subtitle)): + atts['CLASS'] = 'with-subtitle' + self.body.append( + self.starttag(node, 'h%s' % h_level, '', **atts)) + atts = {} + if node.hasattr('refid'): + atts['class'] = 'toc-backref' + atts['href'] = '#' + node['refid'] + if atts: + self.body.append(self.starttag({}, 'a', '', **atts)) + close_tag = '\n' % (h_level) + else: + close_tag = '\n' % (h_level) + self.context.append(close_tag) + + def depart_title(self, node): + self.body.append(self.context.pop()) + if self.in_document_title: + self.title = self.body[self.in_document_title:-1] + self.in_document_title = 0 + self.body_pre_docinfo.extend(self.body) + self.html_title.extend(self.body) + del self.body[:] + + def visit_title_reference(self, node): + self.body.append(self.starttag(node, 'cite', '')) + + def depart_title_reference(self, node): + self.body.append('') + + # TODO: use the new HTML5 element