diff --git a/app.py b/app.py index ac58000..7d7a621 100644 --- a/app.py +++ b/app.py @@ -1,9 +1,9 @@ from flask import Flask, Blueprint, jsonify -from flask_restplus import Api +from flask_restx import Api from ma import ma from db import db -from resources.book import Book, BookList, book_ns +from resources.competicao import Competicao, CompeticaoList, competicao_ns from marshmallow import ValidationError from server.instance import server @@ -22,8 +22,8 @@ def handle_validation_error(error): return jsonify(error.messages), 400 -api.add_resource(Book, '/books/') -api.add_resource(BookList, '/books') +api.add_resource(Competicao, '/competicao/') +api.add_resource(CompeticaoList, '/competicao') if __name__ == '__main__': db.init_app(app) diff --git a/models/book.py b/models/book.py deleted file mode 100644 index 897ddc2..0000000 --- a/models/book.py +++ /dev/null @@ -1,40 +0,0 @@ -from db import db -from typing import List - - -class BookModel(db.Model): - __tablename__ = "books" - - id = db.Column(db.Integer, primary_key=True) - title = db.Column(db.String(80), nullable=False, unique=True) - pages = db.Column(db.Float(precision=2), nullable=False) - - def __init__(self, title, pages): - self.title = title - self.pages = pages - - def __repr__(self): - return f'BookModel(title={self.title}, pages={self.pages})' - - def json(self): - return {'title': self.title, 'pages': self.pages} - - @classmethod - def find_by_title(cls, title) -> "BookModel": - return cls.query.filter_by(title=title).first() - - @classmethod - def find_by_id(cls, _id) -> "BookModel": - return cls.query.filter_by(id=_id).first() - - @classmethod - def find_all(cls) -> List["BookModel"]: - return cls.query.all() - - def save_to_db(self) -> None: - db.session.add(self) - db.session.commit() - - def delete_from_db(self) -> None: - db.session.delete(self) - db.session.commit() diff --git a/models/competicao.py b/models/competicao.py new file mode 100644 index 0000000..c4bfad4 --- /dev/null +++ b/models/competicao.py @@ -0,0 +1,51 @@ +from db import db +from typing import List +from sqlalchemy import desc + + +class CompeticaoModel(db.Model): + __tablename__ = "competicoes" + + id = db.Column(db.Integer, primary_key=True) + competicao = db.Column(db.String(80), nullable=False) + atleta = db.Column(db.String(80), nullable=False) + value = db.Column(db.String(80), nullable=False) + unidade = db.Column(db.String(80), nullable=False) + enc = db.Column(db.String(80), nullable=False) + + def __init__(self, competicao, atleta,value,unidade,enc): + self.competicao = competicao + self.atleta = atleta + self.value = value + self.unidade = unidade + self.enc = enc + + def __repr__(self): + return f'CompeticaoModel(competicao={self.competicao}, atleta={self.atleta},value={self.value}, unidade={self.unidade}, enc={self.enc})' + + def json(self): + return {'competicao': self.competicao, 'atleta': self.atleta ,'value': self.value, 'unidade': self.unidade, 'enc': self.enc} + + @classmethod + def filter_by_ranking(cls, value) -> "CompeticaoModel": + return cls.query.order_by(desc(value)).all() + + @classmethod + def find_by_competicao(cls, competicao) -> "CompeticaoModel": + return cls.query.filter_by(competicao=competicao).first() + + @classmethod + def find_by_id(cls, _id) -> "CompeticaoModel": + return cls.query.filter_by(id=_id).first() + + @classmethod + def find_all(cls) -> List["CompeticaoModel"]: + return cls.query.all() + + def save_to_db(self) -> None: + db.session.add(self) + db.session.commit() + + def delete_from_db(self) -> None: + db.session.delete(self) + db.session.commit() diff --git a/radar/Include/site/python3.9/greenlet/greenlet.h b/radar/Include/site/python3.9/greenlet/greenlet.h new file mode 100644 index 0000000..830bef8 --- /dev/null +++ b/radar/Include/site/python3.9/greenlet/greenlet.h @@ -0,0 +1,146 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ + +/* Greenlet object interface */ + +#ifndef Py_GREENLETOBJECT_H +#define Py_GREENLETOBJECT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is deprecated and undocumented. It does not change. */ +#define GREENLET_VERSION "1.0.0" + +typedef struct _greenlet { + PyObject_HEAD + char* stack_start; + char* stack_stop; + char* stack_copy; + intptr_t stack_saved; + struct _greenlet* stack_prev; + struct _greenlet* parent; + PyObject* run_info; + struct _frame* top_frame; + int recursion_depth; + PyObject* weakreflist; +#if PY_VERSION_HEX >= 0x030700A3 + _PyErr_StackItem* exc_info; + _PyErr_StackItem exc_state; +#else + PyObject* exc_type; + PyObject* exc_value; + PyObject* exc_traceback; +#endif + PyObject* dict; +#if PY_VERSION_HEX >= 0x030700A3 + PyObject* context; +#endif +#if PY_VERSION_HEX >= 0x30A00B1 + CFrame* cframe; +#endif +} PyGreenlet; + +#define PyGreenlet_Check(op) PyObject_TypeCheck(op, &PyGreenlet_Type) +#define PyGreenlet_MAIN(op) (((PyGreenlet*)(op))->stack_stop == (char*)-1) +#define PyGreenlet_STARTED(op) (((PyGreenlet*)(op))->stack_stop != NULL) +#define PyGreenlet_ACTIVE(op) (((PyGreenlet*)(op))->stack_start != NULL) +#define PyGreenlet_GET_PARENT(op) (((PyGreenlet*)(op))->parent) + +/* C API functions */ + +/* Total number of symbols that are exported */ +#define PyGreenlet_API_pointers 8 + +#define PyGreenlet_Type_NUM 0 +#define PyExc_GreenletError_NUM 1 +#define PyExc_GreenletExit_NUM 2 + +#define PyGreenlet_New_NUM 3 +#define PyGreenlet_GetCurrent_NUM 4 +#define PyGreenlet_Throw_NUM 5 +#define PyGreenlet_Switch_NUM 6 +#define PyGreenlet_SetParent_NUM 7 + +#ifndef GREENLET_MODULE +/* This section is used by modules that uses the greenlet C API */ +static void** _PyGreenlet_API = NULL; + +# define PyGreenlet_Type \ + (*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM]) + +# define PyExc_GreenletError \ + ((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM]) + +# define PyExc_GreenletExit \ + ((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM]) + +/* + * PyGreenlet_New(PyObject *args) + * + * greenlet.greenlet(run, parent=None) + */ +# define PyGreenlet_New \ + (*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \ + _PyGreenlet_API[PyGreenlet_New_NUM]) + +/* + * PyGreenlet_GetCurrent(void) + * + * greenlet.getcurrent() + */ +# define PyGreenlet_GetCurrent \ + (*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) + +/* + * PyGreenlet_Throw( + * PyGreenlet *greenlet, + * PyObject *typ, + * PyObject *val, + * PyObject *tb) + * + * g.throw(...) + */ +# define PyGreenlet_Throw \ + (*(PyObject * (*)(PyGreenlet * self, \ + PyObject * typ, \ + PyObject * val, \ + PyObject * tb)) \ + _PyGreenlet_API[PyGreenlet_Throw_NUM]) + +/* + * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) + * + * g.switch(*args, **kwargs) + */ +# define PyGreenlet_Switch \ + (*(PyObject * \ + (*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \ + _PyGreenlet_API[PyGreenlet_Switch_NUM]) + +/* + * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) + * + * g.parent = new_parent + */ +# define PyGreenlet_SetParent \ + (*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \ + _PyGreenlet_API[PyGreenlet_SetParent_NUM]) + +/* Macro that imports greenlet and initializes C API */ +/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we + keep the older definition to be sure older code that might have a copy of + the header still works. */ +# define PyGreenlet_Import() \ + { \ + _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \ + } + +#endif /* GREENLET_MODULE */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_GREENLETOBJECT_H */ diff --git a/radar/Lib/site-packages/Flask-2.0.2.dist-info/INSTALLER b/radar/Lib/site-packages/Flask-2.0.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/radar/Lib/site-packages/Flask-2.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/radar/Lib/site-packages/Flask-2.0.2.dist-info/LICENSE.rst b/radar/Lib/site-packages/Flask-2.0.2.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/radar/Lib/site-packages/Flask-2.0.2.dist-info/LICENSE.rst @@ -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/radar/Lib/site-packages/Flask-2.0.2.dist-info/METADATA b/radar/Lib/site-packages/Flask-2.0.2.dist-info/METADATA new file mode 100644 index 0000000..aaf27ca --- /dev/null +++ b/radar/Lib/site-packages/Flask-2.0.2.dist-info/METADATA @@ -0,0 +1,125 @@ +Metadata-Version: 2.1 +Name: Flask +Version: 2.0.2 +Summary: A simple framework for building complex web applications. +Home-page: https://palletsprojects.com/p/flask +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://flask.palletsprojects.com/ +Project-URL: Changes, https://flask.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/flask/ +Project-URL: Issue Tracker, https://github.com/pallets/flask/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: Flask +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: Werkzeug (>=2.0) +Requires-Dist: Jinja2 (>=3.0) +Requires-Dist: itsdangerous (>=2.0) +Requires-Dist: click (>=7.1.2) +Provides-Extra: async +Requires-Dist: asgiref (>=3.2) ; extra == 'async' +Provides-Extra: dotenv +Requires-Dist: python-dotenv ; extra == 'dotenv' + +Flask +===== + +Flask is a lightweight `WSGI`_ web application framework. It is designed +to make getting started quick and easy, with the ability to scale up to +complex applications. It began as a simple wrapper around `Werkzeug`_ +and `Jinja`_ and has become one of the most popular Python web +application frameworks. + +Flask offers suggestions, but doesn't enforce any dependencies or +project layout. It is up to the developer to choose the tools and +libraries they want to use. There are many extensions provided by the +community that make adding new functionality easy. + +.. _WSGI: https://wsgi.readthedocs.io/ +.. _Werkzeug: https://werkzeug.palletsprojects.com/ +.. _Jinja: https://jinja.palletsprojects.com/ + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Flask + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +.. code-block:: python + + # save this as app.py + from flask import Flask + + app = Flask(__name__) + + @app.route("/") + def hello(): + return "Hello, World!" + +.. code-block:: text + + $ flask run + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) + + +Contributing +------------ + +For guidance on setting up a development environment and how to make a +contribution to Flask, see the `contributing guidelines`_. + +.. _contributing guidelines: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst + + +Donate +------ + +The Pallets organization develops and supports Flask and the libraries +it uses. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://flask.palletsprojects.com/ +- Changes: https://flask.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Flask/ +- Source Code: https://github.com/pallets/flask/ +- Issue Tracker: https://github.com/pallets/flask/issues/ +- Website: https://palletsprojects.com/p/flask/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/radar/Lib/site-packages/Flask-2.0.2.dist-info/RECORD b/radar/Lib/site-packages/Flask-2.0.2.dist-info/RECORD new file mode 100644 index 0000000..6144fe1 --- /dev/null +++ b/radar/Lib/site-packages/Flask-2.0.2.dist-info/RECORD @@ -0,0 +1,52 @@ +../../Scripts/flask.exe,sha256=RHc0TdKNC-DqNXdMZuBnxDtjnA3io0YwzHtEN-W2YMU,106354 +Flask-2.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Flask-2.0.2.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +Flask-2.0.2.dist-info/METADATA,sha256=aKsvjFA_ZjZN1jLh1Ac3aQk-ZUZDPrrwo_TGYW1kdAQ,3839 +Flask-2.0.2.dist-info/RECORD,, +Flask-2.0.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +Flask-2.0.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +Flask-2.0.2.dist-info/entry_points.txt,sha256=gBLA1aKg0OYR8AhbAfg8lnburHtKcgJLDU52BBctN0k,42 +Flask-2.0.2.dist-info/top_level.txt,sha256=dvi65F6AeGWVU0TBpYiC04yM60-FX1gJFkK31IKQr5c,6 +flask/__init__.py,sha256=9ZCelLoNCpr6eSuLmYlzvbp12B3lrLgoN5U2UWk1vdo,2251 +flask/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30 +flask/__pycache__/__init__.cpython-39.pyc,, +flask/__pycache__/__main__.cpython-39.pyc,, +flask/__pycache__/app.cpython-39.pyc,, +flask/__pycache__/blueprints.cpython-39.pyc,, +flask/__pycache__/cli.cpython-39.pyc,, +flask/__pycache__/config.cpython-39.pyc,, +flask/__pycache__/ctx.cpython-39.pyc,, +flask/__pycache__/debughelpers.cpython-39.pyc,, +flask/__pycache__/globals.cpython-39.pyc,, +flask/__pycache__/helpers.cpython-39.pyc,, +flask/__pycache__/logging.cpython-39.pyc,, +flask/__pycache__/scaffold.cpython-39.pyc,, +flask/__pycache__/sessions.cpython-39.pyc,, +flask/__pycache__/signals.cpython-39.pyc,, +flask/__pycache__/templating.cpython-39.pyc,, +flask/__pycache__/testing.cpython-39.pyc,, +flask/__pycache__/typing.cpython-39.pyc,, +flask/__pycache__/views.cpython-39.pyc,, +flask/__pycache__/wrappers.cpython-39.pyc,, +flask/app.py,sha256=ectBbi9hGmVHAse5TNcFQZIDRkDAxYUAnLgfuKD0Xws,81975 +flask/blueprints.py,sha256=AkAVXZ_MMkjwjklzCAMdBNowTiM0wVQPynnUnXjTL2M,23781 +flask/cli.py,sha256=wn2Un9RO32ZfRmCMem5KJ5h62-5lnmy1H9uxgyV-eBs,32238 +flask/config.py,sha256=70Uyjh1Jzb9MfTCT7NDhuZWAzyIEu-TIyk6-22MP3zQ,11285 +flask/ctx.py,sha256=EM3W0v1ctuFQAGk_HWtQdoJEg_r2f5Le4xcmElxFwwk,17428 +flask/debughelpers.py,sha256=W82-xrRmodjopBngI9roYH-q08EbQwN2HEGfDAi6SA0,6184 +flask/globals.py,sha256=cWd-R2hUH3VqPhnmQNww892tQS6Yjqg_wg8UvW1M7NM,1723 +flask/helpers.py,sha256=00WqA3wYeyjMrnAOPZTUyrnUf7H8ik3CVT0kqGl_qjk,30589 +flask/json/__init__.py,sha256=unAKdZBlxMI5OMiTU0-Z2Hl4CF1CMJmqTUzpStiExNw,11822 +flask/json/__pycache__/__init__.cpython-39.pyc,, +flask/json/__pycache__/tag.cpython-39.pyc,, +flask/json/tag.py,sha256=fys3HBLssWHuMAIJuTcf2K0bCtosePBKXIWASZEEjnU,8857 +flask/logging.py,sha256=1o_hirVGqdj7SBdETnhX7IAjklG89RXlrwz_2CjzQQE,2273 +flask/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask/scaffold.py,sha256=fM9mRy7QBh9fhJ0VTogVx900dDa5oxz8FOw6OK5F-TU,32796 +flask/sessions.py,sha256=Kb7zY4qBIOU2cw1xM5mQ_KmgYUBDFbUYWjlkq0EFYis,15189 +flask/signals.py,sha256=H7QwDciK-dtBxinjKpexpglP0E6k0MJILiFWTItfmqU,2136 +flask/templating.py,sha256=l96VD39JQ0nue4Bcj7wZ4-FWWs-ppLxvgBCpwDQ4KAk,5626 +flask/testing.py,sha256=OsHT-2B70abWH3ulY9IbhLchXIeyj3L-cfcDa88wv5E,10281 +flask/typing.py,sha256=hXEVcXoH-QEabmy1F11pYaQ2SonlkMAwfjBAnqj2x18,1982 +flask/views.py,sha256=nhq31TRB5Z-z2mjFGZACaaB2Et5XPCmWhWxJxOvLWww,5948 +flask/wrappers.py,sha256=VndbHPRBSUUOejmd2Y3ydkoCVUtsS2OJIdJEVIkBVD8,5604 diff --git a/radar/Lib/site-packages/Flask-2.0.2.dist-info/REQUESTED b/radar/Lib/site-packages/Flask-2.0.2.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/radar/Lib/site-packages/Flask-2.0.2.dist-info/WHEEL b/radar/Lib/site-packages/Flask-2.0.2.dist-info/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/radar/Lib/site-packages/Flask-2.0.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/radar/Lib/site-packages/Flask-2.0.2.dist-info/entry_points.txt b/radar/Lib/site-packages/Flask-2.0.2.dist-info/entry_points.txt new file mode 100644 index 0000000..1eb0252 --- /dev/null +++ b/radar/Lib/site-packages/Flask-2.0.2.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +flask = flask.cli:main + diff --git a/radar/Lib/site-packages/Flask-2.0.2.dist-info/top_level.txt b/radar/Lib/site-packages/Flask-2.0.2.dist-info/top_level.txt new file mode 100644 index 0000000..7e10602 --- /dev/null +++ b/radar/Lib/site-packages/Flask-2.0.2.dist-info/top_level.txt @@ -0,0 +1 @@ +flask diff --git a/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/INSTALLER b/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/LICENSE.rst b/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/LICENSE.rst @@ -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/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/METADATA b/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/METADATA new file mode 100644 index 0000000..a1971dc --- /dev/null +++ b/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/METADATA @@ -0,0 +1,94 @@ +Metadata-Version: 2.1 +Name: Flask-SQLAlchemy +Version: 2.5.1 +Summary: Adds SQLAlchemy support to your Flask application. +Home-page: https://github.com/pallets/flask-sqlalchemy +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://flask-sqlalchemy.palletsprojects.com/ +Project-URL: Code, https://github.com/pallets/flask-sqlalchemy +Project-URL: Issue tracker, https://github.com/pallets/flask-sqlalchemy/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: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.* +Requires-Dist: Flask (>=0.10) +Requires-Dist: SQLAlchemy (>=0.8.0) + +Flask-SQLAlchemy +================ + +Flask-SQLAlchemy is an extension for `Flask`_ that adds support for +`SQLAlchemy`_ to your application. It aims to simplify using SQLAlchemy +with Flask by providing useful defaults and extra helpers that make it +easier to accomplish common tasks. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Flask-SQLAlchemy + + +A Simple Example +---------------- + +.. code-block:: python + + from flask import Flask + from flask_sqlalchemy import SQLAlchemy + + app = Flask(__name__) + app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.sqlite" + db = SQLAlchemy(app) + + + class User(db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String, unique=True, nullable=False) + email = db.Column(db.String, unique=True, nullable=False) + + + db.session.add(User(name="Flask", email="example@example.com")) + db.session.commit() + + users = User.query.all() + + +Links +----- + +- Documentation: https://flask-sqlalchemy.palletsprojects.com/ +- Releases: https://pypi.org/project/Flask-SQLAlchemy/ +- Code: https://github.com/pallets/flask-sqlalchemy +- Issue tracker: https://github.com/pallets/flask-sqlalchemy/issues +- Test status: https://travis-ci.org/pallets/flask-sqlalchemy +- Test coverage: https://codecov.io/gh/pallets/flask-sqlalchemy + +.. _Flask: https://palletsprojects.com/p/flask/ +.. _SQLAlchemy: https://www.sqlalchemy.org +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + diff --git a/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/RECORD b/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/RECORD new file mode 100644 index 0000000..df5d60f --- /dev/null +++ b/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/RECORD @@ -0,0 +1,15 @@ +Flask_SQLAlchemy-2.5.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Flask_SQLAlchemy-2.5.1.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +Flask_SQLAlchemy-2.5.1.dist-info/METADATA,sha256=vVCeMtTM_xOrUVoVyemeNaTUI5L9iXa16NsiMDDOgFU,3128 +Flask_SQLAlchemy-2.5.1.dist-info/RECORD,, +Flask_SQLAlchemy-2.5.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +Flask_SQLAlchemy-2.5.1.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 +Flask_SQLAlchemy-2.5.1.dist-info/top_level.txt,sha256=w2K4fNNoTh4HItoFfz2FRQShSeLcvHYrzU_sZov21QU,17 +flask_sqlalchemy/__init__.py,sha256=IaupgTRkQnY05KPLYvfiNnJdrmwoyfsxaiyGtrEYfO4,40738 +flask_sqlalchemy/__pycache__/__init__.cpython-39.pyc,, +flask_sqlalchemy/__pycache__/_compat.cpython-39.pyc,, +flask_sqlalchemy/__pycache__/model.cpython-39.pyc,, +flask_sqlalchemy/__pycache__/utils.cpython-39.pyc,, +flask_sqlalchemy/_compat.py,sha256=yua0ZSgVWwi56QpEgwaPInzkNQ9PFb7YQdvEk3dImXo,821 +flask_sqlalchemy/model.py,sha256=bd2mIv9LA1A2MZkQObgnMUCSrxNvyqplaSkCxyxKNxY,4988 +flask_sqlalchemy/utils.py,sha256=4eHqAbYElnJ3NbSAHhuINckoAHDABoxjleMJD0iKgyg,1390 diff --git a/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/REQUESTED b/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/WHEEL b/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/WHEEL new file mode 100644 index 0000000..01b8fc7 --- /dev/null +++ b/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/top_level.txt b/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/top_level.txt new file mode 100644 index 0000000..8a5538e --- /dev/null +++ b/radar/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/top_level.txt @@ -0,0 +1 @@ +flask_sqlalchemy diff --git a/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/INSTALLER b/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/LICENSE.rst b/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/LICENSE.rst new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/radar/Lib/site-packages/Jinja2-3.0.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/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/METADATA b/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/METADATA new file mode 100644 index 0000000..274e91c --- /dev/null +++ b/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/METADATA @@ -0,0 +1,113 @@ +Metadata-Version: 2.1 +Name: Jinja2 +Version: 3.0.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: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Changes, https://jinja.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/jinja/ +Project-URL: Issue Tracker, https://github.com/pallets/jinja/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +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: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: MarkupSafe (>=2.0) +Provides-Extra: i18n +Requires-Dist: Babel (>=2.7) ; 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/getting-started/ + + +In A Nutshell +------------- + +.. code-block:: jinja + + {% extends "base.html" %} + {% block title %}Members{% endblock %} + {% block content %} + + {% endblock %} + + +Donate +------ + +The Pallets organization develops and supports Jinja and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://jinja.palletsprojects.com/ +- Changes: https://jinja.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Jinja2/ +- Source Code: https://github.com/pallets/jinja/ +- Issue Tracker: https://github.com/pallets/jinja/issues/ +- Website: https://palletsprojects.com/p/jinja/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/RECORD b/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/RECORD new file mode 100644 index 0000000..ea72b5d --- /dev/null +++ b/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/RECORD @@ -0,0 +1,58 @@ +Jinja2-3.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Jinja2-3.0.2.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Jinja2-3.0.2.dist-info/METADATA,sha256=NxKriGP-LwQjFBZ4hjV696NkZMe3GPy3tE00McDQZZ0,3539 +Jinja2-3.0.2.dist-info/RECORD,, +Jinja2-3.0.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +Jinja2-3.0.2.dist-info/entry_points.txt,sha256=Qy_DkVo6Xj_zzOtmErrATe8lHZhOqdjpt3e4JJAGyi8,61 +Jinja2-3.0.2.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7 +jinja2/__init__.py,sha256=w4sanG5_dogKC4J_2thsPN9tXzmily3fmXK8Pd46Q1Y,2205 +jinja2/__pycache__/__init__.cpython-39.pyc,, +jinja2/__pycache__/_identifier.cpython-39.pyc,, +jinja2/__pycache__/async_utils.cpython-39.pyc,, +jinja2/__pycache__/bccache.cpython-39.pyc,, +jinja2/__pycache__/compiler.cpython-39.pyc,, +jinja2/__pycache__/constants.cpython-39.pyc,, +jinja2/__pycache__/debug.cpython-39.pyc,, +jinja2/__pycache__/defaults.cpython-39.pyc,, +jinja2/__pycache__/environment.cpython-39.pyc,, +jinja2/__pycache__/exceptions.cpython-39.pyc,, +jinja2/__pycache__/ext.cpython-39.pyc,, +jinja2/__pycache__/filters.cpython-39.pyc,, +jinja2/__pycache__/idtracking.cpython-39.pyc,, +jinja2/__pycache__/lexer.cpython-39.pyc,, +jinja2/__pycache__/loaders.cpython-39.pyc,, +jinja2/__pycache__/meta.cpython-39.pyc,, +jinja2/__pycache__/nativetypes.cpython-39.pyc,, +jinja2/__pycache__/nodes.cpython-39.pyc,, +jinja2/__pycache__/optimizer.cpython-39.pyc,, +jinja2/__pycache__/parser.cpython-39.pyc,, +jinja2/__pycache__/runtime.cpython-39.pyc,, +jinja2/__pycache__/sandbox.cpython-39.pyc,, +jinja2/__pycache__/tests.cpython-39.pyc,, +jinja2/__pycache__/utils.cpython-39.pyc,, +jinja2/__pycache__/visitor.cpython-39.pyc,, +jinja2/_identifier.py,sha256=EdgGJKi7O1yvr4yFlvqPNEqV6M1qHyQr8Gt8GmVTKVM,1775 +jinja2/async_utils.py,sha256=bY2nCUfBA_4FSnNUsIsJgljBq3hACr6fzLi7LiyMTn8,1751 +jinja2/bccache.py,sha256=v5rKAlYxIvfJEa0uGzAC6yCYSS3KuXT5Eqi-n9qvNi8,12670 +jinja2/compiler.py,sha256=v7zKz-mgSYXmfXD9mRmi2BU0B6Z-1RGZmOXCrsPKzc0,72209 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=uBmrsiwjYH5l14R9STn5mydOOyriBYol5lDGvEqAb3A,9238 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=T6U4be9mY1CUXXin_EQFwpvpFqCiryweGqzXGRYIoSA,61573 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=44SjDjeYkkxQTpmC2BetOTxEFMgQ42p2dfSwXmPFcSo,32122 +jinja2/filters.py,sha256=jusKTZbd0ddZMaibZkxMUVKNsOsaYtOq_Il8Imtx4BE,52609 +jinja2/idtracking.py,sha256=WekexMql3u5n3vDxFsQ_i8HW0j24AtjWTjrPBLWrHww,10721 +jinja2/lexer.py,sha256=qNEQqDQw_zO5EaH6rFQsER7Qwn2du0o22prB-TR11HE,29930 +jinja2/loaders.py,sha256=WGJNP5RQEGtuTABIK2tp1sWEy2kfe5ZFOY0ON0gi0LY,22477 +jinja2/meta.py,sha256=GNPEvifmSaU3CMxlbheBOZjeZ277HThOPUTf1RkppKQ,4396 +jinja2/nativetypes.py,sha256=62hvvsAxAj0YaxylOHoREYVogJ5JqOlJISgGY3OKd_o,3675 +jinja2/nodes.py,sha256=8zvzM5mYaylI-kbT6oI1DChxGH8dZkghWt1fVizy4Us,34605 +jinja2/optimizer.py,sha256=tHkMwXxfZkbfA1KmLcqmBMSaz7RLIvvItrJcPoXTyD8,1650 +jinja2/parser.py,sha256=kHnU8v92GwMYkfr0MVakWv8UlSf_kJPx8LUsgQMof70,39767 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=wVRlkEmAgNU67AIQDqLvI6UkNLkzDqpLA-z4Mi3vl3g,35054 +jinja2/sandbox.py,sha256=-8zxR6TO9kUkciAVFsIKu8Oq-C7PTeYEdZ5TtA55-gw,14600 +jinja2/tests.py,sha256=Am5Z6Lmfr2XaH_npIfJJ8MdXtWsbLjMULZJulTAj30E,5905 +jinja2/utils.py,sha256=udQxWIKaq4QDCZiXN31ngKOaGGdaMA5fl0JMaM-F6fg,26971 +jinja2/visitor.py,sha256=ZmeLuTj66ic35-uFH-1m0EKXiw4ObDDb_WuE6h5vPFg,3572 diff --git a/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/WHEEL b/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/entry_points.txt b/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/entry_points.txt new file mode 100644 index 0000000..3619483 --- /dev/null +++ b/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[babel.extractors] +jinja2 = jinja2.ext:babel_extract [i18n] + diff --git a/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/top_level.txt b/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/top_level.txt new file mode 100644 index 0000000..7f7afbf --- /dev/null +++ b/radar/Lib/site-packages/Jinja2-3.0.2.dist-info/top_level.txt @@ -0,0 +1 @@ +jinja2 diff --git a/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER b/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/LICENSE.rst b/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/LICENSE.rst @@ -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/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/METADATA b/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/METADATA new file mode 100644 index 0000000..ef44e2b --- /dev/null +++ b/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/METADATA @@ -0,0 +1,100 @@ +Metadata-Version: 2.1 +Name: MarkupSafe +Version: 2.0.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: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://markupsafe.palletsprojects.com/ +Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/markupsafe/ +Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +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: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst + +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('<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 str subclass + >>> # methods and operators escape their arguments + >>> template = Markup("Hello {name}") + >>> template.format(name='"World"') + Markup('Hello "World"') + + +Donate +------ + +The Pallets organization develops and supports MarkupSafe and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +`please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://markupsafe.palletsprojects.com/ +- Changes: https://markupsafe.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/MarkupSafe/ +- Source Code: https://github.com/pallets/markupsafe/ +- Issue Tracker: https://github.com/pallets/markupsafe/issues/ +- Website: https://palletsprojects.com/p/markupsafe/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/RECORD b/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/RECORD new file mode 100644 index 0000000..c5acda2 --- /dev/null +++ b/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/RECORD @@ -0,0 +1,13 @@ +MarkupSafe-2.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +MarkupSafe-2.0.1.dist-info/LICENSE.rst,sha256=RjHsDbX9kKVH4zaBcmTGeYIUM4FG-KyUtKV_lu6MnsQ,1503 +MarkupSafe-2.0.1.dist-info/METADATA,sha256=FmPpxBdaqCCjF-XKqoxeEzqAzhetQnrkkSsd3V3X-Jc,3211 +MarkupSafe-2.0.1.dist-info/RECORD,, +MarkupSafe-2.0.1.dist-info/WHEEL,sha256=jr7ubY0Lkz_yXH9FfFe9PTtLhGOsf62dZkNvTYrJINE,100 +MarkupSafe-2.0.1.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +markupsafe/__init__.py,sha256=s08KbuFRV3zh4Wh7xjsIphXgp1xf0EUB79wlPj-4scc,9211 +markupsafe/__pycache__/__init__.cpython-39.pyc,, +markupsafe/__pycache__/_native.cpython-39.pyc,, +markupsafe/_native.py,sha256=JMXegJtk1ZcnRKrgyCA-CEXmRnOpaIXLyDAM98GbshY,2061 +markupsafe/_speedups.cp39-win_amd64.pyd,sha256=hPTsANj9bt7hLYxWLuZcC7E3-EnJQ4GlvIf9Vx0p1p0,16384 +markupsafe/_speedups.pyi,sha256=f5QtwIOP0eLrxh2v5p6SmaYmlcHIGIfmz0DovaqL0OU,238 +markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/WHEEL b/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/WHEEL new file mode 100644 index 0000000..d1267fc --- /dev/null +++ b/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: false +Tag: cp39-cp39-win_amd64 + diff --git a/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/top_level.txt b/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/top_level.txt new file mode 100644 index 0000000..75bf729 --- /dev/null +++ b/radar/Lib/site-packages/MarkupSafe-2.0.1.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/INSTALLER b/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/LICENSE b/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/LICENSE new file mode 100644 index 0000000..0d9fb6d --- /dev/null +++ b/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/LICENSE @@ -0,0 +1,19 @@ +Copyright 2005-2021 SQLAlchemy authors and contributors . + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/METADATA b/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/METADATA new file mode 100644 index 0000000..a45c258 --- /dev/null +++ b/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/METADATA @@ -0,0 +1,240 @@ +Metadata-Version: 2.1 +Name: SQLAlchemy +Version: 1.4.26 +Summary: Database Abstraction Library +Home-page: https://www.sqlalchemy.org +Author: Mike Bayer +Author-email: mike_mp@zzzcomputing.com +License: MIT +Project-URL: Documentation, https://docs.sqlalchemy.org +Project-URL: Issue Tracker, https://github.com/sqlalchemy/sqlalchemy/ +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.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Database :: Front-Ends +Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: importlib-metadata ; python_version < "3.8" +Requires-Dist: greenlet (!=0.4.17) ; python_version >= "3" and (platform_machine == "aarch64" or (platform_machine == "ppc64le" or (platform_machine == "x86_64" or (platform_machine == "amd64" or (platform_machine == "AMD64" or (platform_machine == "win32" or platform_machine == "WIN32")))))) +Provides-Extra: aiomysql +Requires-Dist: greenlet (!=0.4.17) ; (python_version >= "3") and extra == 'aiomysql' +Requires-Dist: aiomysql ; (python_version >= "3") and extra == 'aiomysql' +Provides-Extra: aiosqlite +Requires-Dist: typing-extensions (!=3.10.0.1) ; extra == 'aiosqlite' +Requires-Dist: greenlet (!=0.4.17) ; (python_version >= "3") and extra == 'aiosqlite' +Requires-Dist: aiosqlite ; (python_version >= "3") and extra == 'aiosqlite' +Provides-Extra: asyncio +Requires-Dist: greenlet (!=0.4.17) ; (python_version >= "3") and extra == 'asyncio' +Provides-Extra: asyncmy +Requires-Dist: greenlet (!=0.4.17) ; (python_version >= "3") and extra == 'asyncmy' +Requires-Dist: asyncmy (>=0.2.3) ; (python_version >= "3") and extra == 'asyncmy' +Provides-Extra: mariadb_connector +Requires-Dist: mariadb (>=1.0.1) ; (python_version >= "3") and extra == 'mariadb_connector' +Provides-Extra: mssql +Requires-Dist: pyodbc ; extra == 'mssql' +Provides-Extra: mssql_pymssql +Requires-Dist: pymssql ; extra == 'mssql_pymssql' +Provides-Extra: mssql_pyodbc +Requires-Dist: pyodbc ; extra == 'mssql_pyodbc' +Provides-Extra: mypy +Requires-Dist: sqlalchemy2-stubs ; extra == 'mypy' +Requires-Dist: mypy (>=0.910) ; (python_version >= "3") and extra == 'mypy' +Provides-Extra: mysql +Requires-Dist: mysqlclient (<2,>=1.4.0) ; (python_version < "3") and extra == 'mysql' +Requires-Dist: mysqlclient (>=1.4.0) ; (python_version >= "3") and extra == 'mysql' +Provides-Extra: mysql_connector +Requires-Dist: mysql-connector-python ; extra == 'mysql_connector' +Provides-Extra: oracle +Requires-Dist: cx-oracle (<8,>=7) ; (python_version < "3") and extra == 'oracle' +Requires-Dist: cx-oracle (>=7) ; (python_version >= "3") and extra == 'oracle' +Provides-Extra: postgresql +Requires-Dist: psycopg2 (>=2.7) ; extra == 'postgresql' +Provides-Extra: postgresql_asyncpg +Requires-Dist: greenlet (!=0.4.17) ; (python_version >= "3") and extra == 'postgresql_asyncpg' +Requires-Dist: asyncpg ; (python_version >= "3") and extra == 'postgresql_asyncpg' +Provides-Extra: postgresql_pg8000 +Requires-Dist: pg8000 (>=1.16.6) ; extra == 'postgresql_pg8000' +Provides-Extra: postgresql_psycopg2binary +Requires-Dist: psycopg2-binary ; extra == 'postgresql_psycopg2binary' +Provides-Extra: postgresql_psycopg2cffi +Requires-Dist: psycopg2cffi ; extra == 'postgresql_psycopg2cffi' +Provides-Extra: pymysql +Requires-Dist: pymysql (<1) ; (python_version < "3") and extra == 'pymysql' +Requires-Dist: pymysql ; (python_version >= "3") and extra == 'pymysql' +Provides-Extra: sqlcipher +Requires-Dist: sqlcipher3-binary ; (python_version >= "3") and extra == 'sqlcipher' + +SQLAlchemy +========== + +|PyPI| |Python| |Downloads| + +.. |PyPI| image:: https://img.shields.io/pypi/v/sqlalchemy + :target: https://pypi.org/project/sqlalchemy + :alt: PyPI + +.. |Python| image:: https://img.shields.io/pypi/pyversions/sqlalchemy + :target: https://pypi.org/project/sqlalchemy + :alt: PyPI - Python Version + +.. |Downloads| image:: https://img.shields.io/pypi/dm/sqlalchemy + :target: https://pypi.org/project/sqlalchemy + :alt: PyPI - Downloads + + +The Python SQL Toolkit and Object Relational Mapper + +Introduction +------------- + +SQLAlchemy is the Python SQL toolkit and Object Relational Mapper +that gives application developers the full power and +flexibility of SQL. SQLAlchemy provides a full suite +of well known enterprise-level persistence patterns, +designed for efficient and high-performing database +access, adapted into a simple and Pythonic domain +language. + +Major SQLAlchemy features include: + +* An industrial strength ORM, built + from the core on the identity map, unit of work, + and data mapper patterns. These patterns + allow transparent persistence of objects + using a declarative configuration system. + Domain models + can be constructed and manipulated naturally, + and changes are synchronized with the + current transaction automatically. +* A relationally-oriented query system, exposing + the full range of SQL's capabilities + explicitly, including joins, subqueries, + correlation, and most everything else, + in terms of the object model. + Writing queries with the ORM uses the same + techniques of relational composition you use + when writing SQL. While you can drop into + literal SQL at any time, it's virtually never + needed. +* A comprehensive and flexible system + of eager loading for related collections and objects. + Collections are cached within a session, + and can be loaded on individual access, all + at once using joins, or by query per collection + across the full result set. +* A Core SQL construction system and DBAPI + interaction layer. The SQLAlchemy Core is + separate from the ORM and is a full database + abstraction layer in its own right, and includes + an extensible Python-based SQL expression + language, schema metadata, connection pooling, + type coercion, and custom types. +* All primary and foreign key constraints are + assumed to be composite and natural. Surrogate + integer primary keys are of course still the + norm, but SQLAlchemy never assumes or hardcodes + to this model. +* Database introspection and generation. Database + schemas can be "reflected" in one step into + Python structures representing database metadata; + those same structures can then generate + CREATE statements right back out - all within + the Core, independent of the ORM. + +SQLAlchemy's philosophy: + +* SQL databases behave less and less like object + collections the more size and performance start to + matter; object collections behave less and less like + tables and rows the more abstraction starts to matter. + SQLAlchemy aims to accommodate both of these + principles. +* An ORM doesn't need to hide the "R". A relational + database provides rich, set-based functionality + that should be fully exposed. SQLAlchemy's + ORM provides an open-ended set of patterns + that allow a developer to construct a custom + mediation layer between a domain model and + a relational schema, turning the so-called + "object relational impedance" issue into + a distant memory. +* The developer, in all cases, makes all decisions + regarding the design, structure, and naming conventions + of both the object model as well as the relational + schema. SQLAlchemy only provides the means + to automate the execution of these decisions. +* With SQLAlchemy, there's no such thing as + "the ORM generated a bad query" - you + retain full control over the structure of + queries, including how joins are organized, + how subqueries and correlation is used, what + columns are requested. Everything SQLAlchemy + does is ultimately the result of a developer- + initiated decision. +* Don't use an ORM if the problem doesn't need one. + SQLAlchemy consists of a Core and separate ORM + component. The Core offers a full SQL expression + language that allows Pythonic construction + of SQL constructs that render directly to SQL + strings for a target database, returning + result sets that are essentially enhanced DBAPI + cursors. +* Transactions should be the norm. With SQLAlchemy's + ORM, nothing goes to permanent storage until + commit() is called. SQLAlchemy encourages applications + to create a consistent means of delineating + the start and end of a series of operations. +* Never render a literal value in a SQL statement. + Bound parameters are used to the greatest degree + possible, allowing query optimizers to cache + query plans effectively and making SQL injection + attacks a non-issue. + +Documentation +------------- + +Latest documentation is at: + +https://www.sqlalchemy.org/docs/ + +Installation / Requirements +--------------------------- + +Full documentation for installation is at +`Installation `_. + +Getting Help / Development / Bug reporting +------------------------------------------ + +Please refer to the `SQLAlchemy Community Guide `_. + +Code of Conduct +--------------- + +Above all, SQLAlchemy places great emphasis on polite, thoughtful, and +constructive communication between users and developers. +Please see our current Code of Conduct at +`Code of Conduct `_. + +License +------- + +SQLAlchemy is distributed under the `MIT license +`_. + + + diff --git a/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/RECORD b/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/RECORD new file mode 100644 index 0000000..083ac79 --- /dev/null +++ b/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/RECORD @@ -0,0 +1,484 @@ +SQLAlchemy-1.4.26.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +SQLAlchemy-1.4.26.dist-info/LICENSE,sha256=YBpAuebmf1_VblyrYHwFdqeATqzBxJ-T6h8-e4s2zW4,1119 +SQLAlchemy-1.4.26.dist-info/METADATA,sha256=116cQtDkYRlEFVQ8oZ-2Uzuqzuj5jyH8p1vrSMUqAmc,9967 +SQLAlchemy-1.4.26.dist-info/RECORD,, +SQLAlchemy-1.4.26.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +SQLAlchemy-1.4.26.dist-info/WHEEL,sha256=MRZ_p7RU4olp1XL4U2EYLkuYikriaVRqXBoKVLH_OSE,100 +SQLAlchemy-1.4.26.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11 +sqlalchemy/__init__.py,sha256=WdZOUC_pnJe6OcRXV7YfpBlUv2Vl6qYEpteBOq3yEAA,4242 +sqlalchemy/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/__pycache__/events.cpython-39.pyc,, +sqlalchemy/__pycache__/exc.cpython-39.pyc,, +sqlalchemy/__pycache__/inspection.cpython-39.pyc,, +sqlalchemy/__pycache__/log.cpython-39.pyc,, +sqlalchemy/__pycache__/processors.cpython-39.pyc,, +sqlalchemy/__pycache__/schema.cpython-39.pyc,, +sqlalchemy/__pycache__/types.cpython-39.pyc,, +sqlalchemy/cimmutabledict.cp39-win_amd64.pyd,sha256=1W0s3-RcIwc4Hu1RoeeCP6AeKc6wGUexjhXnB5I3CKo,14848 +sqlalchemy/connectors/__init__.py,sha256=9R7M0wh_MdUj-WAPV--FHY45VIzVukkrIcAs-AVXiQE,289 +sqlalchemy/connectors/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/connectors/__pycache__/mxodbc.cpython-39.pyc,, +sqlalchemy/connectors/__pycache__/pyodbc.cpython-39.pyc,, +sqlalchemy/connectors/mxodbc.py,sha256=xzgqHegww7PtHhRe7bCgVgTkMsqKGegbTh5vDOjE5AU,5950 +sqlalchemy/connectors/pyodbc.py,sha256=opPnx1gds_5nZkuAKOIZ8GjoVJjv65fXo_-7f5rxbH0,7018 +sqlalchemy/cprocessors.cp39-win_amd64.pyd,sha256=IM91vKKYMpK7gw2_q1EdHJTkqG1JVJZpMFFnf4C6C50,17408 +sqlalchemy/cresultproxy.cp39-win_amd64.pyd,sha256=lEZd1BPvAGcKptr8BB2OxAvX_mwNqNO6qk6DpxSYagU,20992 +sqlalchemy/databases/__init__.py,sha256=mxKoy-gIMRoVgtg8yV4FhkGZlV80H7T_WthG9N1uOKI,1048 +sqlalchemy/databases/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/__init__.py,sha256=8i5fZ2FBQ1UzW0AyKnB2DyJAvBUWD3fAkh12tUVKqcQ,2157 +sqlalchemy/dialects/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/firebird/__init__.py,sha256=qQjx1eAxja3K5XU69G0aY6Lx0iRe3plkHiirfvJII3s,1194 +sqlalchemy/dialects/firebird/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/firebird/__pycache__/base.cpython-39.pyc,, +sqlalchemy/dialects/firebird/__pycache__/fdb.cpython-39.pyc,, +sqlalchemy/dialects/firebird/__pycache__/kinterbasdb.cpython-39.pyc,, +sqlalchemy/dialects/firebird/base.py,sha256=IphYp7SK7caEPFdZl3MIp22h4vmE7knBBPxDdn2CKoY,32160 +sqlalchemy/dialects/firebird/fdb.py,sha256=NIEyFOxCwlpX4J7o25N6u-Jn0mR-tnRTz67eB5Gld9A,4228 +sqlalchemy/dialects/firebird/kinterbasdb.py,sha256=xiglTtJHqIfCGtKbrh958JZQpuHvX4G303sg3mfgVBU,6681 +sqlalchemy/dialects/mssql/__init__.py,sha256=vxGVb_gvKXYpTP00Wf2RQrXXUmqcVJ4J_h7_h2SJ6Qg,1873 +sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/mssql/__pycache__/base.cpython-39.pyc,, +sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-39.pyc,, +sqlalchemy/dialects/mssql/__pycache__/json.cpython-39.pyc,, +sqlalchemy/dialects/mssql/__pycache__/mxodbc.cpython-39.pyc,, +sqlalchemy/dialects/mssql/__pycache__/provision.cpython-39.pyc,, +sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-39.pyc,, +sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-39.pyc,, +sqlalchemy/dialects/mssql/base.py,sha256=pco4yqQTHU2Lf_UDJXdstw7fMPycWFi2YUpB2Ypz0UU,115336 +sqlalchemy/dialects/mssql/information_schema.py,sha256=JkMcdYPT-OZgVajNzV1V7RbOWHkhXksXufql6hByBbI,7816 +sqlalchemy/dialects/mssql/json.py,sha256=bAi9z-htrC6adJUiBRetVj3RwtJ2GNN5I_KGeHFL8_s,4683 +sqlalchemy/dialects/mssql/mxodbc.py,sha256=q38_ooZVi2GZ805McU04H-G2umdexSLSt1wpHSBMIHA,4958 +sqlalchemy/dialects/mssql/provision.py,sha256=oTGw0l5RNcXIWkA0s2erVgk2XKiNw26IRzZE84tN2uE,4371 +sqlalchemy/dialects/mssql/pymssql.py,sha256=vPro0Kq3vstyRi44UIoUTG28mQZjIzN0FmyGisWBhSc,4981 +sqlalchemy/dialects/mssql/pyodbc.py,sha256=BczVN7pCfgGTE6JJGVWOwM5h_cI7ZDYH4OdTPsfeta4,22583 +sqlalchemy/dialects/mysql/__init__.py,sha256=QgRh5VsET8rGAwl0F6JdQeIOqU6gqmz_BX75u6lU3jk,2293 +sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/aiomysql.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/asyncmy.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/base.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/dml.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/enumerated.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/expression.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/json.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mariadb.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mariadbconnector.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/oursql.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/provision.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/reflection.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/types.cpython-39.pyc,, +sqlalchemy/dialects/mysql/aiomysql.py,sha256=xk5OB9_ClaoRrklEVx60XX-nsHJnrUsgcZ2jYTUEFHI,9926 +sqlalchemy/dialects/mysql/asyncmy.py,sha256=zApLzLPi44o4GYwe20gL5c9ZOv-Proe_4JbeHY2M0n4,10235 +sqlalchemy/dialects/mysql/base.py,sha256=A1umMZuZ8yeSC6c5-nDGHMR1Z4SmFRtV84ixqZVTk84,122909 +sqlalchemy/dialects/mysql/cymysql.py,sha256=bLHR8upJw3fsDJK3YrLMmwp03CYxZBRC0ueS2avreA8,2353 +sqlalchemy/dialects/mysql/dml.py,sha256=rRmYkeT-R8ZgfqwSG2uuCPwVMN60AMCJ3IMhVdQNjbw,6355 +sqlalchemy/dialects/mysql/enumerated.py,sha256=igWgJtq1g-gWnM12e8xRgalfSPv1LJ3TbNr0S5_kAG0,9397 +sqlalchemy/dialects/mysql/expression.py,sha256=opg17QfNs805tLjcxdTLs_nGthpL-vSEiXl25iAPOO4,3871 +sqlalchemy/dialects/mysql/json.py,sha256=5XN4UZ7MSbVDpQWiiDeyL64yUEIBy4s5aWtoj580NuY,2397 +sqlalchemy/dialects/mysql/mariadb.py,sha256=jE1Hpmka_nKJjWsFHcCVRKWvjg7l1mNCjCzvVADMD-k,523 +sqlalchemy/dialects/mysql/mariadbconnector.py,sha256=H6sNtcbz221Q3SafzLHasb9KHLdrutIaRj5JfnDL56k,7756 +sqlalchemy/dialects/mysql/mysqlconnector.py,sha256=NYFlULU8QRDkIzuAHMx71QboFaJPRP8nDAsni9ECR24,7930 +sqlalchemy/dialects/mysql/mysqldb.py,sha256=kwZlL1xEhU-zd7af8JgxHs3_NlAw6jG40qwi7FD63HA,10855 +sqlalchemy/dialects/mysql/oursql.py,sha256=cW---MOJr1JUFDsDUh5tZoUHVfpiZOqWAOgio_JrRmw,8796 +sqlalchemy/dialects/mysql/provision.py,sha256=-gCG7MwEMtztSmNU107Q563t-nN-oqTVkCiay-HRRQI,2727 +sqlalchemy/dialects/mysql/pymysql.py,sha256=oCiRwX6ui_06q_WdUHYurOBSiTsYa3lLJWO4D13aGr4,2868 +sqlalchemy/dialects/mysql/pyodbc.py,sha256=ztvUEUyPx1N5FCH-eInayfk1WWmPE2Zrx0E7mHWjxFg,4635 +sqlalchemy/dialects/mysql/reflection.py,sha256=lSVmht9BeGTQZiEswbgrh954Sw6C08xXH0DuiHpIy9U,19111 +sqlalchemy/dialects/mysql/types.py,sha256=y8tBh47YE-iIlMy34sQxge62j9jVI_8gmI8AfvUA-9E,25362 +sqlalchemy/dialects/oracle/__init__.py,sha256=I0LO6_N2t9cuxB8V8z4D7qCd8STy00Pt3YN9mMyxpFk,1287 +sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/oracle/__pycache__/base.cpython-39.pyc,, +sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-39.pyc,, +sqlalchemy/dialects/oracle/__pycache__/provision.cpython-39.pyc,, +sqlalchemy/dialects/oracle/base.py,sha256=Vjm5TLZSHQgn9eiCdZ5F0rRhHgI94jacmwBCWnXH1Ls,89376 +sqlalchemy/dialects/oracle/cx_oracle.py,sha256=H9kMkziVNXKH9sJDmimmGK7uLobIZEzwb-RAG7CLg1c,54142 +sqlalchemy/dialects/oracle/provision.py,sha256=HCi5hs5fv6rXv-lVQir6gsKrhes9NoAWlChzR3ueq74,5965 +sqlalchemy/dialects/postgresql/__init__.py,sha256=qPxMM9lQd7YmgmUefA3j4oIcRfZZl3qwa1PGZ27vPgI,2626 +sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/array.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/asyncpg.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/base.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/dml.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/ext.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/json.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/provision.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/pygresql.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/pypostgresql.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/array.py,sha256=zgKiyebFd8XBoqPjWVe8mM3rFoouNzR5geO6V0eI7tM,12621 +sqlalchemy/dialects/postgresql/asyncpg.py,sha256=T3TJ0YXiVZ6oBjio_RM0pIgjSt3huqNFXm6rF_F26vw,34624 +sqlalchemy/dialects/postgresql/base.py,sha256=gSArsh5mAGn-I8ekfovAG12g2Q6whmGYiqdytPQQ9yk,158370 +sqlalchemy/dialects/postgresql/dml.py,sha256=4ReHHNxI72rDD61VXvDCoGaDqh6EqcuLlGEsAQWuSCw,9829 +sqlalchemy/dialects/postgresql/ext.py,sha256=3gEVNqMPtW0cY44RzNqbre9pbWFIkcjjqVf1afreUP0,8658 +sqlalchemy/dialects/postgresql/hstore.py,sha256=VwMzNIBvPH18zYDkiYevi2wUQVrdxQAN6ivh-Eegrlk,12943 +sqlalchemy/dialects/postgresql/json.py,sha256=M4S1ZhWJ-zchLK1biM-a65-LoB5rsk5reRI1U8dk0J0,10883 +sqlalchemy/dialects/postgresql/pg8000.py,sha256=a9V8GysB-vAjFTB7kWoqCaxlamwKVK1OJ5a3Fji9bp8,17415 +sqlalchemy/dialects/postgresql/provision.py,sha256=EgdZRObrTzwZ7jaBA_oPkWx3Ly1Cathw4pOfWZm6Pn0,4543 +sqlalchemy/dialects/postgresql/psycopg2.py,sha256=H1FTn5yb2y-m9x6pvKa7YMB_mp0XxHQPFZxfL3Z3Ulg,39358 +sqlalchemy/dialects/postgresql/psycopg2cffi.py,sha256=l0BcgY5wsG4uhmUtP9Ksn2fdxBe7T3y0j0PeYSksDpc,1751 +sqlalchemy/dialects/postgresql/pygresql.py,sha256=5U4yGfmL5yhiu3I4OxJ0lXzFn1kcZf8Rlj1B8RTdSC0,8863 +sqlalchemy/dialects/postgresql/pypostgresql.py,sha256=EZfy4yTFgWtXUkHbYe_48JnHM_VB6WScbwqcC5e39GQ,3819 +sqlalchemy/dialects/postgresql/ranges.py,sha256=Uf0XOeLhD1KNp-A3MJbsBx7Cxu-r1G2uHkTIMiraAT8,4869 +sqlalchemy/dialects/sqlite/__init__.py,sha256=eXKVjUjgbWCVsTrl4vLZInTJc3Pn_dC4EQAIaqb5WuU,1256 +sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/aiosqlite.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/base.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/dml.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/json.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/provision.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/aiosqlite.py,sha256=SlhNyx9huFYxmbbkVoqNoy0gp0Ke3YDrLWA2P162-p0,10331 +sqlalchemy/dialects/sqlite/base.py,sha256=hFfB3poHeqogjgstp7_wMmZvW4LNbikdi5Eb_GgP4qI,90883 +sqlalchemy/dialects/sqlite/dml.py,sha256=1MZ70pY0fgVWs_8-KZLWlvtzR4ZdTO4r_vqfBuMj42M,7038 +sqlalchemy/dialects/sqlite/json.py,sha256=bz_1axFG5YI9kLszE-oiCN3-z95zIPPcLgVwug_-AT4,2602 +sqlalchemy/dialects/sqlite/provision.py,sha256=3F5ZX2dYGMFAGra99UVavCihth1_XazJXX9XAet8gbw,4818 +sqlalchemy/dialects/sqlite/pysqlcipher.py,sha256=OhM80N9ZrcVXQhPvbQ0l70ZZkH8ItyiYLkIP4yesE50,5769 +sqlalchemy/dialects/sqlite/pysqlite.py,sha256=yzCqSrnoAmo-iWqY_pbsUw7gnsfBkSoNy7jZvjmhWzQ,24054 +sqlalchemy/dialects/sybase/__init__.py,sha256=iu3g_UdZebWi3vEb_dvl-o9LcHxLPSQxUDd11ZF6Lrc,1431 +sqlalchemy/dialects/sybase/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/sybase/__pycache__/base.cpython-39.pyc,, +sqlalchemy/dialects/sybase/__pycache__/mxodbc.cpython-39.pyc,, +sqlalchemy/dialects/sybase/__pycache__/pyodbc.cpython-39.pyc,, +sqlalchemy/dialects/sybase/__pycache__/pysybase.cpython-39.pyc,, +sqlalchemy/dialects/sybase/base.py,sha256=XaFBcW1BzX0W0R26MpdUfBEgdcTbcsKhsDDTwNYuMG0,33521 +sqlalchemy/dialects/sybase/mxodbc.py,sha256=1MJQ95Rt6tI7h8ihCN8q_TGuzhSgLocSCmGqumU1dbI,973 +sqlalchemy/dialects/sybase/pyodbc.py,sha256=Q5zyi3KBIiiJaxouthkDeeK0k725PqXkc2WA1UsgI4M,2319 +sqlalchemy/dialects/sybase/pysybase.py,sha256=doOLJtsapSIzXGxql7KRk4DdzpTXcwIvuk6BUeSZHzM,3476 +sqlalchemy/engine/__init__.py,sha256=vKYde0KcmcNLyp7_93chwUziu8Cn7i2haUpw39O-fhE,2136 +sqlalchemy/engine/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/base.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/characteristics.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/create.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/cursor.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/default.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/events.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/interfaces.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/mock.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/reflection.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/result.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/row.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/strategies.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/url.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/util.cpython-39.pyc,, +sqlalchemy/engine/base.py,sha256=pstrRJS5sMuasPS0P_9mGe3FWkY1BwUE-gG5hNZDHkA,122294 +sqlalchemy/engine/characteristics.py,sha256=DrhLcmpnkMpmgo_kgQpcujoD4nW81vTt-GkuWqW9QaY,1873 +sqlalchemy/engine/create.py,sha256=xn4YtedMQK-be0WNHvrqI-yf8MDKwGsR-Xt0l_mvMNA,32041 +sqlalchemy/engine/cursor.py,sha256=knJP41oy3UOwJ1E5lEYrZ9wpSd8q9TDsAVFpy5kaHqo,69826 +sqlalchemy/engine/default.py,sha256=cDDxirMHurv2QyxqGcSr36DUxudo8cXkBPxfHMV_zKQ,66452 +sqlalchemy/engine/events.py,sha256=fDectOfTs-oCfHwl2NrYIDb5FBoFYBmZ15IjalMXuWc,33995 +sqlalchemy/engine/interfaces.py,sha256=LKx7br-McCIJFh9Aic1kx8eQiVh8rW0PCIYeKr2ZZlI,61918 +sqlalchemy/engine/mock.py,sha256=w4zCNsRxe9d4RoiprXOPgekAsBcZhFUFFuahgSpbu7I,3744 +sqlalchemy/engine/reflection.py,sha256=0l3l4KUPEic8mLc7gBPQhGzD1Qb5VJf9v1WIVTAWDAk,39853 +sqlalchemy/engine/result.py,sha256=8kGDn8ZA2YZReNevP6aHpSE6all0NVgPcr8J8ZqCkbI,55970 +sqlalchemy/engine/row.py,sha256=cFjoUn1QYfgMqs2VkjRzNlTysF0ptE6k_RMBsqpSuGE,18800 +sqlalchemy/engine/strategies.py,sha256=t1Q0jIlyxe6z9ph4TOFaViLjMYODQk6akTKAc8smxVI,431 +sqlalchemy/engine/url.py,sha256=wPHXWmjkfyV2kL53mTP1khcgIa6WK9K8TpvO_689e7c,27094 +sqlalchemy/engine/util.py,sha256=TIS9VU_mcFHgz3N8rAB2z4JUwQh8asXCIMkBWBOyeKE,7876 +sqlalchemy/event/__init__.py,sha256=XreIYU5ZboKOqh0OhNwlD_BM6Q_CpFnB_RwBqB_3uPw,534 +sqlalchemy/event/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/event/__pycache__/api.cpython-39.pyc,, +sqlalchemy/event/__pycache__/attr.cpython-39.pyc,, +sqlalchemy/event/__pycache__/base.cpython-39.pyc,, +sqlalchemy/event/__pycache__/legacy.cpython-39.pyc,, +sqlalchemy/event/__pycache__/registry.cpython-39.pyc,, +sqlalchemy/event/api.py,sha256=GTs1gOKlcpvGgqOO-67fwlpfxgPL7XK-v5aqHBUSFW0,8262 +sqlalchemy/event/attr.py,sha256=z2gqp7Cpa1Q18Ng4dyzZRceDtbamMVGt0KiJ-S43F-k,15093 +sqlalchemy/event/base.py,sha256=IwlUCiQUM3RMtBnf7iqHw7wkTXYVabWdm9yyfV2IPzs,11281 +sqlalchemy/event/legacy.py,sha256=nF-QWFPHPpUQVPYR0nVGpOZ-0PSpvDLew4IK0jB9gZY,6455 +sqlalchemy/event/registry.py,sha256=U4_rjtE36ZAGXiH6D9seLNzfNdewRLy3f2B7HOPP1J8,8783 +sqlalchemy/events.py,sha256=miQEoutDD_jq5JPe71LV7a02CEGYZOryS6t12qLjSMk,481 +sqlalchemy/exc.py,sha256=qa_pm3pGfE-7RmsQ-2n7Z8Jchkk-G-Wv5x7ZNjvw6es,21555 +sqlalchemy/ext/__init__.py,sha256=_IOFj7IDQZZT8ysB2lpotQgBTo53EkqUnRe_vfTLuVU,333 +sqlalchemy/ext/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/associationproxy.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/automap.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/baked.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/compiler.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/horizontal_shard.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/hybrid.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/indexable.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/instrumentation.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/mutable.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/orderinglist.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/serializer.cpython-39.pyc,, +sqlalchemy/ext/associationproxy.py,sha256=Dba12TOxOTKkWciyuDmF34vpJ9jd9VXFzksRZt6a3xc,51570 +sqlalchemy/ext/asyncio/__init__.py,sha256=S4AkBA8do84Z00lagJIU1DIvErCsppEBI5quT7_PEFI,799 +sqlalchemy/ext/asyncio/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/ext/asyncio/__pycache__/base.cpython-39.pyc,, +sqlalchemy/ext/asyncio/__pycache__/engine.cpython-39.pyc,, +sqlalchemy/ext/asyncio/__pycache__/events.cpython-39.pyc,, +sqlalchemy/ext/asyncio/__pycache__/exc.cpython-39.pyc,, +sqlalchemy/ext/asyncio/__pycache__/result.cpython-39.pyc,, +sqlalchemy/ext/asyncio/__pycache__/scoping.cpython-39.pyc,, +sqlalchemy/ext/asyncio/__pycache__/session.cpython-39.pyc,, +sqlalchemy/ext/asyncio/base.py,sha256=SgGrnW1A817GFDsAbA1-juPIMTCx-LX-agyYaE-99Ck,2369 +sqlalchemy/ext/asyncio/engine.py,sha256=wNx04dpfIQW2HXlgz2dflnJlO1ezu24ufni0BO4x1b0,26967 +sqlalchemy/ext/asyncio/events.py,sha256=E_1w1IR_-I8WlCKK5OFUgNpzXCWvYPVWvcwsnkR_3uw,1271 +sqlalchemy/ext/asyncio/exc.py,sha256=JuPU1z3THX9eshfFtUMkE-Yk-jt1xXtKrbG5V_BeXqg,660 +sqlalchemy/ext/asyncio/result.py,sha256=0Yw_05OkfpluaSYvjwxJPY-NXs3BcrYSw2yAI6Oetek,21086 +sqlalchemy/ext/asyncio/scoping.py,sha256=AgO5G5rPfieHmggh2PG1OhOZw7c4R_sExvVlktw_ing,3043 +sqlalchemy/ext/asyncio/session.py,sha256=cEd5yElhp4vR6ONuruXJWZNRDvQKP5jIhU6kCF7FHa4,20518 +sqlalchemy/ext/automap.py,sha256=nZyOR8I4FaDiQGiKiccSAZm7D_nOZZarEYiMIEfxDRg,46419 +sqlalchemy/ext/baked.py,sha256=c4MZL4ZiC_JOr_FhRqVJQGm9n_kVGh_7vRtIynBbfYw,20617 +sqlalchemy/ext/compiler.py,sha256=7bWnrFeV_-56BYLNrEaKNkI5EKaiz9epc0TDUwa6Y_c,18497 +sqlalchemy/ext/declarative/__init__.py,sha256=djj3b6PIoMQj7P-W6GWwwb2BGL764-Yx9qEnDiGiaz4,1906 +sqlalchemy/ext/declarative/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/ext/declarative/__pycache__/extensions.cpython-39.pyc,, +sqlalchemy/ext/declarative/extensions.py,sha256=6CTE_iJ7pJe3KXuFk-IctUdqm1CLa9KvezVjNC44Ixw,16867 +sqlalchemy/ext/horizontal_shard.py,sha256=VAyBynCtGGiGKZvENNZaBNfKB4PJXo_cqvSNWl94Hjo,9178 +sqlalchemy/ext/hybrid.py,sha256=JvvlHmRpOReBz8zaj_GS-zLch3TNqH4B8tbrF_M3WSs,42987 +sqlalchemy/ext/indexable.py,sha256=Yqgp8J_dEUYsWUabGTOvyXasqY7qr_84ruSV0mqXKBU,11607 +sqlalchemy/ext/instrumentation.py,sha256=kx2zn8mN30F-CbWqiBxb7HgnMSJChLcvQhJ8Tb8VIXo,14802 +sqlalchemy/ext/mutable.py,sha256=AmkDIhuTF0z0FhrMTq97nDPYlDtPwdkpXwVKBh03MT8,32948 +sqlalchemy/ext/mypy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sqlalchemy/ext/mypy/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/ext/mypy/__pycache__/apply.cpython-39.pyc,, +sqlalchemy/ext/mypy/__pycache__/decl_class.cpython-39.pyc,, +sqlalchemy/ext/mypy/__pycache__/infer.cpython-39.pyc,, +sqlalchemy/ext/mypy/__pycache__/names.cpython-39.pyc,, +sqlalchemy/ext/mypy/__pycache__/plugin.cpython-39.pyc,, +sqlalchemy/ext/mypy/__pycache__/util.cpython-39.pyc,, +sqlalchemy/ext/mypy/apply.py,sha256=-D_QJpSpVE-K810gFHjZe004z7Ka4VQQwySxjeT1QSA,9800 +sqlalchemy/ext/mypy/decl_class.py,sha256=EIeEPkZrey9aD8Epj4Fhr0Y9aEmnmin49NwJIXdqZm4,17201 +sqlalchemy/ext/mypy/infer.py,sha256=HZhwi0zfGAh3I8i69Vrsiqu-AZX00Or8h82VV-ar9IA,18291 +sqlalchemy/ext/mypy/names.py,sha256=zu_DknT9fMh7MrWMy9ZzCe6UIV_NxGllpkcM_yw7yzI,7934 +sqlalchemy/ext/mypy/plugin.py,sha256=HSS7kZPaYl8_KXvyWwcapqV36fTzoTkhv3w7tnFG7UA,9507 +sqlalchemy/ext/mypy/util.py,sha256=A3YXYa4tKJC5O1KMtl0oNX30R6ts3Omo9FlaNll5pbo,8448 +sqlalchemy/ext/orderinglist.py,sha256=r-NMePBtFl37NXG_W08cYz7kQ3rTpWV51KYZ4PUJ7VY,14263 +sqlalchemy/ext/serializer.py,sha256=9fZbqOVD2Tw3umIhnf5mU_9P0We9aBogxv-HJxZ0d2o,6133 +sqlalchemy/future/__init__.py,sha256=1agu_FUR_f5j3XLUwfh1WoBWghZX2PpcvO0bvaZiSis,543 +sqlalchemy/future/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/future/__pycache__/engine.cpython-39.pyc,, +sqlalchemy/future/engine.py,sha256=2Qc97YYmaKSp0jyOF4X6S6efF4btcsuj3c0Wpu1PlNU,16993 +sqlalchemy/future/orm/__init__.py,sha256=PkzdGJzfKaxXrGL3_nCkSlVhxnVa3IPbOr96Yf1EbXg,299 +sqlalchemy/future/orm/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/inspection.py,sha256=GOUbF3OA24hZeGxzOZBeTjb23QuGtMPwPp_e0x9xGJI,3144 +sqlalchemy/log.py,sha256=ZYPQpq9Jh7Ab8TpWNWj7PPwt-D3h-3wxqwuosxzhfXU,6995 +sqlalchemy/orm/__init__.py,sha256=JHuzhJkzPUusJEk0MiRpYk9o-7JyjDwiJNz9kZAMQck,10813 +sqlalchemy/orm/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/attributes.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/base.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/clsregistry.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/collections.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/context.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/decl_api.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/decl_base.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/dependency.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/descriptor_props.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/dynamic.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/evaluator.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/events.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/exc.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/identity.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/instrumentation.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/interfaces.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/loading.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/mapper.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/path_registry.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/persistence.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/properties.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/query.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/relationships.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/scoping.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/session.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/state.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/strategies.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/strategy_options.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/sync.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/unitofwork.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/util.cpython-39.pyc,, +sqlalchemy/orm/attributes.py,sha256=MfF4S6oz7zdfjQtq8zEBeeUydyfn4u2va1AoNg9MSGI,78905 +sqlalchemy/orm/base.py,sha256=VAfg8c7tr77wW6YNH4jGje9ZyDYOdiiOEQjfWmh9MjY,15640 +sqlalchemy/orm/clsregistry.py,sha256=ze1nonwKAW0KUwNihFlrAJv517Sd4FRdlXw454JugJo,13732 +sqlalchemy/orm/collections.py,sha256=_NmtExwLSgfgFczNUl1Jz0WPHJabRoL4P0FX08q5rv0,56417 +sqlalchemy/orm/context.py,sha256=Pm3dIMHg0uSfAWKhS_tnQIFHLgmQf7sLFOaUPBXyBIE,109071 +sqlalchemy/orm/decl_api.py,sha256=qwKszq1njeSBpn7pFnSVRelHRSMRisUg_A6fy2-vPIU,36021 +sqlalchemy/orm/decl_base.py,sha256=QPiYaHJg2T0Rlk6dH8z0Ri06ake4hmY5xj6DXoKCCqc,44489 +sqlalchemy/orm/dependency.py,sha256=ypxJPZdqW4O3CVFa_fBgqZOwP0Djo-lrdI_NnsdLwK8,48277 +sqlalchemy/orm/descriptor_props.py,sha256=Lpe6BZUV7rBB1Yzbq8X0-Em8bo-r9msJ7UFdncpP3Nc,26702 +sqlalchemy/orm/dynamic.py,sha256=AIKXgSmGXQ5fFv9HkoNj2_ChgbzdjmwZ3gSSEnar3LA,16307 +sqlalchemy/orm/evaluator.py,sha256=v_4PH0oOyiESW-8xVCQ5-jwmvVksf3kh7qCMExMcHzM,7093 +sqlalchemy/orm/events.py,sha256=52oYUhI3K8r-m0Qw-3W1H4br0s58AZegEZ7GQ6nmdok,113099 +sqlalchemy/orm/exc.py,sha256=HEIHiFBDPCdU-Vp5nUY0jnvUlIBXsgAGZOmNjB6HaJU,6736 +sqlalchemy/orm/identity.py,sha256=rSktG-3dPoR2pZ_SfwPYv4WXmN55Gy84swEBPVCY_kY,7487 +sqlalchemy/orm/instrumentation.py,sha256=Ck-84bGBBFpOmUghjdDZDoSgKXPPDUUQ9H2yBz0h1Ac,20999 +sqlalchemy/orm/interfaces.py,sha256=i4U_jkxGl39HBajCrKW83Slya2yTIYl6aCkvFRpZA_E,29383 +sqlalchemy/orm/loading.py,sha256=IOpPeq8l-0Z6z83-C6ekNUyMlsTYZCYe0Sfmiit-OoY,50590 +sqlalchemy/orm/mapper.py,sha256=6Oz7F8fzVod2AxbQROSo42RSXByPs__U6KSmvDHRDjk,142430 +sqlalchemy/orm/path_registry.py,sha256=y-HpHM0X8UL7HazKDnZsQrESgZuL4Qbn0gi9GGrFvCY,16927 +sqlalchemy/orm/persistence.py,sha256=fj5-1Eo-MXQ_CQ2S21-j2LH0RnujztQmyuXqz9uRnfw,82829 +sqlalchemy/orm/properties.py,sha256=RIcVZqQaOqx5hIFrnFqFCxo7ZK32758UxG-339PbsQM,15320 +sqlalchemy/orm/query.py,sha256=P5q8DblC5z8TgWCsqWZoCem3xRrZ3KUaHh_prSLyp1g,125309 +sqlalchemy/orm/relationships.py,sha256=7eNtdlHFDdYm_SlZq71KGN_07WMl9er8acQFC9ozVkU,146709 +sqlalchemy/orm/scoping.py,sha256=zClL6yMwuWlmfTL5ws5PJoky_gfLJjpVtuHebAWwnOU,7448 +sqlalchemy/orm/session.py,sha256=tnMdmDjI3kCeYum0pEs_kUyxxcRYxyLXqTuMKy09jsc,162887 +sqlalchemy/orm/state.py,sha256=GtdGI_Kk8zvUMKmRYBbdiX6GbAS1jjasP9sH31D9IEI,34432 +sqlalchemy/orm/strategies.py,sha256=oub2QnkStZLQPy8ETM1zz3AKJFlAV3bb6YGFQX3I0Bc,110442 +sqlalchemy/orm/strategy_options.py,sha256=odTzhQ35A2c6hZ2AEZKaH8BljBsWTz9ZeF1uiEV8gfY,68748 +sqlalchemy/orm/sync.py,sha256=i4_pckRzwuteE7Eoq2_tZ3YJHYLp34Ip5UvvjR9xUVw,5991 +sqlalchemy/orm/unitofwork.py,sha256=ih7G9uAet5DxkAA5-yXB3hJZqVjklie469uTX4mSvSI,27874 +sqlalchemy/orm/util.py,sha256=sz3QptQF4hbpYaCFz-o0c1mhioeaP8dfDetqb8NIk0M,74014 +sqlalchemy/pool/__init__.py,sha256=pxU58-DDcAI-iJnlNdTLMXKKarUIMO0IurslcpZtGjw,1659 +sqlalchemy/pool/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/pool/__pycache__/base.cpython-39.pyc,, +sqlalchemy/pool/__pycache__/dbapi_proxy.cpython-39.pyc,, +sqlalchemy/pool/__pycache__/events.cpython-39.pyc,, +sqlalchemy/pool/__pycache__/impl.cpython-39.pyc,, +sqlalchemy/pool/base.py,sha256=f1TXOgvxoIvHZmg0a3H3hh5DedcoDj47hX2bGOPDn90,39673 +sqlalchemy/pool/dbapi_proxy.py,sha256=t5tN6PjqbNPi8qJNbdKK-1aWIVZ1ieV0vmxXsoHAzp8,4376 +sqlalchemy/pool/events.py,sha256=OVHZmXVs-B9hbF2agZhI62BXgV5A0bj2BgNnWlFiYuQ,10324 +sqlalchemy/pool/impl.py,sha256=VnLgkhiTzWxIUNWVO_h-Rwx4bRJQp4CC0hN90xpEESU,16297 +sqlalchemy/processors.py,sha256=seo5tJyEcZI7nhjt_4TNovfLUMMjl2kerStSXOZwy8k,5921 +sqlalchemy/schema.py,sha256=hE9BHCyIyG7bD3iOEQJ1r9el-_dVsrRf4Ga3m7KGE9U,2472 +sqlalchemy/sql/__init__.py,sha256=LbTzY2myefIGr3fI8VfSxve-7Q8taYSCj9VP-Hu2rsA,4811 +sqlalchemy/sql/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/annotation.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/base.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/coercions.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/compiler.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/crud.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/ddl.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/default_comparator.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/dml.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/elements.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/events.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/expression.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/functions.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/lambdas.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/naming.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/operators.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/roles.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/schema.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/selectable.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/sqltypes.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/traversals.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/type_api.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/util.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/visitors.cpython-39.pyc,, +sqlalchemy/sql/annotation.py,sha256=jjX7XMd1X6hq_NJHpq97dymriDSca2W2-zEM1vmBDOo,11593 +sqlalchemy/sql/base.py,sha256=G9RwxvZ4R1_iSOMsXxCqhixLf4k1yN2VTegK3xBQVfo,57502 +sqlalchemy/sql/coercions.py,sha256=v_EPp3fs7jbjSzJvsK4v4vmAul4DAMnm7XR5k1dPjyo,35060 +sqlalchemy/sql/compiler.py,sha256=4hW2o8V9QCPKXKUl06CYCgRM_fEoIiAYJihqXbsvhdM,189551 +sqlalchemy/sql/crud.py,sha256=1e6VprKJPuXYScn6M3a15sk6qOIp38qfG3c6cGM53kA,36500 +sqlalchemy/sql/ddl.py,sha256=ws0MvmgiXrn8PI0BukrkqIy76NBwauZ4fwKJG85YGUo,45338 +sqlalchemy/sql/default_comparator.py,sha256=5A1DVrK7bTN14UOEH8Stl-wBMLptWjd-sl1ntc4vYhM,11402 +sqlalchemy/sql/dml.py,sha256=YmdvmbOAyZrUSgC0ZMI16SsJ6E8YN4eCoDco4kPO3qo,53086 +sqlalchemy/sql/elements.py,sha256=P28ZkqAzPGtYtkj5xTdaKgF0ORQcJr5RKuFNZo0qTAE,186220 +sqlalchemy/sql/events.py,sha256=ZF3llBNlMRjuJYY65uZaM2_l7BXe_F3klI8Z_D3VYqk,13700 +sqlalchemy/sql/expression.py,sha256=ts3AukLIz196uvknRSm-DYC00BzMveWJx6UNyissn2Q,9106 +sqlalchemy/sql/functions.py,sha256=he7cDITjy49TjJAxBeKlYldaTR2M5xDVhar3drY7ZMI,48891 +sqlalchemy/sql/lambdas.py,sha256=3Y0wS_p_AKq1K2A2r0K2Q1uvSjI8Zpl8JRQ1ahjuA8o,45532 +sqlalchemy/sql/naming.py,sha256=1aeTLfJRX6sOfkElOBbMhfw6je2k5j5_QZyjeluVtdQ,6996 +sqlalchemy/sql/operators.py,sha256=BboDrQlibNSe_ocGnco4w0A3xm91X8GDCYR9I8KAIA4,49100 +sqlalchemy/sql/roles.py,sha256=vdzY0kL_alQi-C1UDcAPLhKv6fbabLdbH1ZGQI8rAho,5760 +sqlalchemy/sql/schema.py,sha256=B3BLKPPu3diy_yPp_5mQ7kBIopFafnhWcGppFTQOd-A,193336 +sqlalchemy/sql/selectable.py,sha256=PuTzyL4yfcjytKtjxXPcnQvkAhUvVsjPDhtK-sjH0Hs,237125 +sqlalchemy/sql/sqltypes.py,sha256=gTu3JLvB-yjTJdZLPoJT8L95ZLHpLPZN82ERzzCibFY,115059 +sqlalchemy/sql/traversals.py,sha256=LtdeC4-O9XSyYShQN-NF5HEyICU47ZrXTIG2FzNYYGM,50908 +sqlalchemy/sql/type_api.py,sha256=wtEMjCAdNBYhgqNvDSa1R8qdJ7Nodl7FVq9t8pmYL20,59373 +sqlalchemy/sql/util.py,sha256=US3j0gvcS8SBkn516V3Tb_7SDq6DwZOf4nOxt7e1zmw,36976 +sqlalchemy/sql/visitors.py,sha256=J0Dq6s-cID4y51MNa1oYLgMi8fokvKoG4vhJoz2Rxlg,28168 +sqlalchemy/testing/__init__.py,sha256=wLsdwby_EtsJioHMETDXxt-a72_SaLGO_Kv-oquS6Tc,2852 +sqlalchemy/testing/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/assertions.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/assertsql.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/asyncio.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/config.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/engines.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/entities.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/exclusions.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/fixtures.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/mock.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/pickleable.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/profiling.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/provision.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/requirements.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/schema.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/util.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/warnings.cpython-39.pyc,, +sqlalchemy/testing/assertions.py,sha256=F92SvIhKLUCC9ER_5sIs74LnEAKR5aBt9FRIbXqosBo,25248 +sqlalchemy/testing/assertsql.py,sha256=ECCn-HmA62q7IwTa49aS2PE-jpsE83qUpfRcFTHpRVg,15421 +sqlalchemy/testing/asyncio.py,sha256=oFYKzKstOnYTDdt9y_nRTgRciyYm1IsQ32sxmCPir9k,3801 +sqlalchemy/testing/config.py,sha256=P2u5FCuHosOoaENchgTVOpRPG3JA2k5L-MBvyxGdz_g,6752 +sqlalchemy/testing/engines.py,sha256=pSPvuPpY_R2ca_XneXpz3y3l76Fubm7KRBTAAqlPq84,13105 +sqlalchemy/testing/entities.py,sha256=sL_-R9UXbk0jw-76yIT2WieD0c8vTgfKFZDg3J7XMIc,3364 +sqlalchemy/testing/exclusions.py,sha256=VW5TaRXawRdMSSIGxuC4N6Yyv1tNTpdkQE-W0m2frhw,13794 +sqlalchemy/testing/fixtures.py,sha256=Ot--ToFfTm4WpNdUkNVRVMWqxasngWbYkxlbcoUjLIs,26466 +sqlalchemy/testing/mock.py,sha256=zGkRclR12c-9DI5Hu_kmt3oxnbk9qws7j0UxB5YjdBQ,926 +sqlalchemy/testing/pickleable.py,sha256=RyI7y6HbY9_jAJNf-I2fceExCKihSm5JwbhLSEbGNys,2848 +sqlalchemy/testing/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sqlalchemy/testing/plugin/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-39.pyc,, +sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-39.pyc,, +sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-39.pyc,, +sqlalchemy/testing/plugin/__pycache__/reinvent_fixtures_py2k.cpython-39.pyc,, +sqlalchemy/testing/plugin/bootstrap.py,sha256=Hf3ikHp52oOUmsD-p3oOG3MzYHBWJMiCBFOVbcUiyPE,1736 +sqlalchemy/testing/plugin/plugin_base.py,sha256=ucimofy8qgv0vCdjerrCJsh7t4e341sfG98T8HcOoOk,22334 +sqlalchemy/testing/plugin/pytestplugin.py,sha256=mlvQ25QTwPx3dJM4jgrtzRElls0A2qtXVrpRnAIXhCA,26370 +sqlalchemy/testing/plugin/reinvent_fixtures_py2k.py,sha256=b9fWp5RXdePykrNviZPXaGDIjOEOfovchez2Ovr4IRQ,3400 +sqlalchemy/testing/profiling.py,sha256=4K-jOvPSRima5y6IQjnUwVwVAuwf2Ni8UaQMuFp8Q-Y,10901 +sqlalchemy/testing/provision.py,sha256=J9PWi7i7Xmr-sALBrmK4OTXH9ur4rpFXKHhnhrjcQcs,12461 +sqlalchemy/testing/requirements.py,sha256=PdHmQuQBmsudiFoN4rkVAkEg8xiK9c6-tJ3nza688Ho,43505 +sqlalchemy/testing/schema.py,sha256=xwGjcgGYJJH0fjbt3nw1FjfjJ4YU3NmLX3lfteW0oMk,6762 +sqlalchemy/testing/suite/__init__.py,sha256=u3lEc0j47s7Dad_2SVWOZ6EU2aOMRWqE_WrQ17HmBsA,489 +sqlalchemy/testing/suite/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_cte.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_insert.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_results.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_select.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_types.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-39.pyc,, +sqlalchemy/testing/suite/test_cte.py,sha256=shi2WJZpzAzDCdkmzx0IDEu-URsLLBsdyFdzDqsfpyw,6387 +sqlalchemy/testing/suite/test_ddl.py,sha256=BZuYkKaG_tNUkZcFLNd6o1qnfbzU0IYiedCw_NhOJBg,12143 +sqlalchemy/testing/suite/test_deprecations.py,sha256=0LUmXIiK8hHUr6tY8cJJC7VErOj9YNGNN-c324k08Dw,5204 +sqlalchemy/testing/suite/test_dialect.py,sha256=XpwZxIXlMVsZlP74khuPW2wZskinT0IOT9S6PM5PyE8,11256 +sqlalchemy/testing/suite/test_insert.py,sha256=QlMovNeDU-GB5UvTEumce3LLK94PAduKlWe2TqCcLF4,11501 +sqlalchemy/testing/suite/test_reflection.py,sha256=VBOoR-Gjj7ZMWFa2bQYS7ERHB5TMb9rSV7Hhid7AZ9U,58848 +sqlalchemy/testing/suite/test_results.py,sha256=wA4SjK46-1TCqqXcOw_a7_9ajMkF3aU4tuLOK9iVWyI,14409 +sqlalchemy/testing/suite/test_rowcount.py,sha256=0kswa4gunxRreCpRkFR7Yxff0k0o-2R8fugMmOGcANg,5042 +sqlalchemy/testing/suite/test_select.py,sha256=ZMrtmX5AfcVtGA09TSh1rY3syMczniLokCXFfwjkaOQ,54115 +sqlalchemy/testing/suite/test_sequence.py,sha256=2KBcs0FtoL3gk37IN2PnRZSnQwt7RKkShAbYQHFTBcw,8713 +sqlalchemy/testing/suite/test_types.py,sha256=Z2LudWdXz6bAu-r3X3LFCrDHbtt8jtEv7k9jbFc-CD4,47025 +sqlalchemy/testing/suite/test_unicode_ddl.py,sha256=HhAM4_38vpwJFX2d9xh0S_YAIQJQ-79GmfO3ich2Tu4,6943 +sqlalchemy/testing/suite/test_update_delete.py,sha256=q3AoCPMKhxYVUi4n7YBjf8v8nb9obXmUXqnFsVxpZLY,1646 +sqlalchemy/testing/util.py,sha256=QqaOSzP9UALgo17KTEb49cMfedShMg_wsIZNqyCkK6o,12961 +sqlalchemy/testing/warnings.py,sha256=0bxlFo8YEjc_Mj0WcwSqIUsLpt0pYcVPH7-B2UgtL8M,4183 +sqlalchemy/types.py,sha256=WSKKIhEFnXh7w7TfSYrCCZRmxol5y4GvRNNLZHn_YbU,2998 +sqlalchemy/util/__init__.py,sha256=svJbaz8iSkvD0WZqrHK4nqpt0dS9nrnlIq3ZGvGBE6Y,6487 +sqlalchemy/util/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/util/__pycache__/_collections.cpython-39.pyc,, +sqlalchemy/util/__pycache__/_compat_py3k.cpython-39.pyc,, +sqlalchemy/util/__pycache__/_concurrency_py3k.cpython-39.pyc,, +sqlalchemy/util/__pycache__/_preloaded.cpython-39.pyc,, +sqlalchemy/util/__pycache__/compat.cpython-39.pyc,, +sqlalchemy/util/__pycache__/concurrency.cpython-39.pyc,, +sqlalchemy/util/__pycache__/deprecations.cpython-39.pyc,, +sqlalchemy/util/__pycache__/langhelpers.cpython-39.pyc,, +sqlalchemy/util/__pycache__/queue.cpython-39.pyc,, +sqlalchemy/util/__pycache__/topological.cpython-39.pyc,, +sqlalchemy/util/_collections.py,sha256=wmPLB-GxCASprLRAAKlcsWplJA_hcCA_nzDJefpTD98,30228 +sqlalchemy/util/_compat_py3k.py,sha256=im50u82G2TBj766YtMAECFaFM4wUH4_kRFKw0Iw-sa8,2262 +sqlalchemy/util/_concurrency_py3k.py,sha256=N0gM0qAnVGEFlvnoEGsqZvD77rW9VmPpWkkPLAaxQLQ,6982 +sqlalchemy/util/_preloaded.py,sha256=g7_fB3j8aISY0wTtipzNJUy0S17jmNgHQtzSTL6QONQ,2464 +sqlalchemy/util/compat.py,sha256=GUuqvj6rYVG27XkE9EMlDytyusyCCnnEJnSJd-_ttEs,18876 +sqlalchemy/util/concurrency.py,sha256=MoTNh3lhTynyRAxt7mXcZJJ7QW_89KyS6GnucdUrXWU,2186 +sqlalchemy/util/deprecations.py,sha256=sTu_IGvp2vRoymZbMKT8wxoxL_wuZBXPs1Vd7uNISxw,11895 +sqlalchemy/util/langhelpers.py,sha256=SrpVi0jBvvybjsL4Te1bfL1n7ootrDg17RQK3V7TZbs,58191 +sqlalchemy/util/queue.py,sha256=nARh6EiML58Yie_tEE7KKvpEakV_aSRl16weYN8iRbs,9584 +sqlalchemy/util/topological.py,sha256=S1MHtwgtiTcsHTbsxIet0ALX0jqjMfG0PHG7jrlyJ8g,2959 diff --git a/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/REQUESTED b/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/WHEEL b/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/WHEEL new file mode 100644 index 0000000..2cc9fa7 --- /dev/null +++ b/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: false +Tag: cp39-cp39-win_amd64 + diff --git a/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/top_level.txt b/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/top_level.txt new file mode 100644 index 0000000..39fb2be --- /dev/null +++ b/radar/Lib/site-packages/SQLAlchemy-1.4.26.dist-info/top_level.txt @@ -0,0 +1 @@ +sqlalchemy diff --git a/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/INSTALLER b/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/LICENSE.rst b/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/LICENSE.rst new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/radar/Lib/site-packages/Werkzeug-2.0.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/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/METADATA b/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/METADATA new file mode 100644 index 0000000..b58b9bd --- /dev/null +++ b/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/METADATA @@ -0,0 +1,129 @@ +Metadata-Version: 2.1 +Name: Werkzeug +Version: 2.0.2 +Summary: The comprehensive WSGI web application library. +Home-page: https://palletsprojects.com/p/werkzeug/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://werkzeug.palletsprojects.com/ +Project-URL: Changes, https://werkzeug.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/werkzeug/ +Project-URL: Issue Tracker, https://github.com/pallets/werkzeug/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +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: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: dataclasses ; python_version < "3.7" +Provides-Extra: watchdog +Requires-Dist: watchdog ; extra == 'watchdog' + +Werkzeug +======== + +*werkzeug* German noun: "tool". Etymology: *werk* ("work"), *zeug* ("stuff") + +Werkzeug is a comprehensive `WSGI`_ web application library. It began as +a simple collection of various utilities for WSGI applications and has +become one of the most advanced WSGI utility libraries. + +It includes: + +- An interactive debugger that allows inspecting stack traces and + source code in the browser with an interactive interpreter for any + frame in the stack. +- A full-featured request object with objects to interact with + headers, query args, form data, files, and cookies. +- A response object that can wrap other WSGI applications and handle + streaming data. +- A routing system for matching URLs to endpoints and generating URLs + for endpoints, with an extensible system for capturing variables + from URLs. +- HTTP utilities to handle entity tags, cache control, dates, user + agents, cookies, files, and more. +- A threaded WSGI server for use while developing applications + locally. +- A test client for simulating HTTP requests during testing without + requiring running a server. + +Werkzeug doesn't enforce any dependencies. It is up to the developer to +choose a template engine, database adapter, and even how to handle +requests. It can be used to build all sorts of end user applications +such as blogs, wikis, or bulletin boards. + +`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while +providing more structure and patterns for defining powerful +applications. + +.. _WSGI: https://wsgi.readthedocs.io/en/latest/ +.. _Flask: https://www.palletsprojects.com/p/flask/ + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U Werkzeug + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +.. code-block:: python + + from werkzeug.wrappers import Request, Response + + @Request.application + def application(request): + return Response('Hello, World!') + + if __name__ == '__main__': + from werkzeug.serving import run_simple + run_simple('localhost', 4000, application) + + +Donate +------ + +The Pallets organization develops and supports Werkzeug and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +`please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://werkzeug.palletsprojects.com/ +- Changes: https://werkzeug.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Werkzeug/ +- Source Code: https://github.com/pallets/werkzeug/ +- Issue Tracker: https://github.com/pallets/werkzeug/issues/ +- Website: https://palletsprojects.com/p/werkzeug/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/RECORD b/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/RECORD new file mode 100644 index 0000000..5b22152 --- /dev/null +++ b/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/RECORD @@ -0,0 +1,111 @@ +Werkzeug-2.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Werkzeug-2.0.2.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Werkzeug-2.0.2.dist-info/METADATA,sha256=vh_xrARtpmkFYnWRAgfSiHgl66LH143rMfAfPZo-R_E,4452 +Werkzeug-2.0.2.dist-info/RECORD,, +Werkzeug-2.0.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +Werkzeug-2.0.2.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9 +werkzeug/__init__.py,sha256=Wx1PLCftJ7UAS0fBXEO4Prdr6kvEQ124Stwg-XwyhW4,188 +werkzeug/__pycache__/__init__.cpython-39.pyc,, +werkzeug/__pycache__/_internal.cpython-39.pyc,, +werkzeug/__pycache__/_reloader.cpython-39.pyc,, +werkzeug/__pycache__/datastructures.cpython-39.pyc,, +werkzeug/__pycache__/exceptions.cpython-39.pyc,, +werkzeug/__pycache__/filesystem.cpython-39.pyc,, +werkzeug/__pycache__/formparser.cpython-39.pyc,, +werkzeug/__pycache__/http.cpython-39.pyc,, +werkzeug/__pycache__/local.cpython-39.pyc,, +werkzeug/__pycache__/routing.cpython-39.pyc,, +werkzeug/__pycache__/security.cpython-39.pyc,, +werkzeug/__pycache__/serving.cpython-39.pyc,, +werkzeug/__pycache__/test.cpython-39.pyc,, +werkzeug/__pycache__/testapp.cpython-39.pyc,, +werkzeug/__pycache__/urls.cpython-39.pyc,, +werkzeug/__pycache__/user_agent.cpython-39.pyc,, +werkzeug/__pycache__/useragents.cpython-39.pyc,, +werkzeug/__pycache__/utils.cpython-39.pyc,, +werkzeug/__pycache__/wsgi.cpython-39.pyc,, +werkzeug/_internal.py,sha256=_QKkvdaG4pDFwK68c0EpPzYJGe9Y7toRAT1cBbC-CxU,18572 +werkzeug/_reloader.py,sha256=B1hEfgsUOz2IginBQM5Zak_eaIF7gr3GS5-0x2OHvAE,13950 +werkzeug/datastructures.py,sha256=m79A8rHQEt5B7qVqyrjARXzHL66Katn8S92urGscTw4,97929 +werkzeug/datastructures.pyi,sha256=CoVwrQ2Vr9JnbprNL9aE3vOz8mOejT9qysQ-BT53C8Y,34089 +werkzeug/debug/__init__.py,sha256=jYA1e1Gw_8EPOytr-BoMdmm0rzP-Z1H0Ih7wIObnKwQ,17968 +werkzeug/debug/__pycache__/__init__.cpython-39.pyc,, +werkzeug/debug/__pycache__/console.cpython-39.pyc,, +werkzeug/debug/__pycache__/repr.cpython-39.pyc,, +werkzeug/debug/__pycache__/tbtools.cpython-39.pyc,, +werkzeug/debug/console.py,sha256=E1nBMEvFkX673ShQjPtVY-byYatfX9MN-dBMjRI8a8E,5897 +werkzeug/debug/repr.py,sha256=QCSHENKsChEZDCIApkVi_UNjhJ77v8BMXK1OfxO189M,9483 +werkzeug/debug/shared/FONT_LICENSE,sha256=LwAVEI1oYnvXiNMT9SnCH_TaLCxCpeHziDrMg0gPkAI,4673 +werkzeug/debug/shared/ICON_LICENSE.md,sha256=DhA6Y1gUl5Jwfg0NFN9Rj4VWITt8tUx0IvdGf0ux9-s,222 +werkzeug/debug/shared/console.png,sha256=bxax6RXXlvOij_KeqvSNX0ojJf83YbnZ7my-3Gx9w2A,507 +werkzeug/debug/shared/debugger.js,sha256=tg42SZs1SVmYWZ-_Fj5ELK5-FLHnGNQrei0K2By8Bw8,10521 +werkzeug/debug/shared/less.png,sha256=-4-kNRaXJSONVLahrQKUxMwXGm9R4OnZ9SxDGpHlIR4,191 +werkzeug/debug/shared/more.png,sha256=GngN7CioHQoV58rH6ojnkYi8c_qED2Aka5FO5UXrReY,200 +werkzeug/debug/shared/source.png,sha256=RoGcBTE4CyCB85GBuDGTFlAnUqxwTBiIfDqW15EpnUQ,818 +werkzeug/debug/shared/style.css,sha256=h1ZSUVaKNpfbfcYzRb513WAhPySGDQom1uih3uEDxPw,6704 +werkzeug/debug/shared/ubuntu.ttf,sha256=1eaHFyepmy4FyDvjLVzpITrGEBu_CZYY94jE0nED1c0,70220 +werkzeug/debug/tbtools.py,sha256=AFRrjLDCAps7G5K2-RxNZpXXaEoeFHm68T00f4vlDYA,19362 +werkzeug/exceptions.py,sha256=CUwx0pBiNbk4f9cON17ekgKnmLi6HIVFjUmYZc2x0wM,28681 +werkzeug/filesystem.py,sha256=JS2Dv2QF98WILxY4_thHl-WMcUcwluF_4igkDPaP1l4,1956 +werkzeug/formparser.py,sha256=X-p3Ek4ji8XrKrbmaWxr8StLSc6iuksbpIeweaabs4s,17400 +werkzeug/http.py,sha256=oUCXFFMnkOQ-cHbUY_aiqitshcrSzNDq3fEMf1VI_yk,45141 +werkzeug/local.py,sha256=bwL-y3-qOZAspJ66W1P36SUApLXJy3UY8nLYbM9kfmY,23183 +werkzeug/middleware/__init__.py,sha256=qfqgdT5npwG9ses3-FXQJf3aB95JYP1zchetH_T3PUw,500 +werkzeug/middleware/__pycache__/__init__.cpython-39.pyc,, +werkzeug/middleware/__pycache__/dispatcher.cpython-39.pyc,, +werkzeug/middleware/__pycache__/http_proxy.cpython-39.pyc,, +werkzeug/middleware/__pycache__/lint.cpython-39.pyc,, +werkzeug/middleware/__pycache__/profiler.cpython-39.pyc,, +werkzeug/middleware/__pycache__/proxy_fix.cpython-39.pyc,, +werkzeug/middleware/__pycache__/shared_data.cpython-39.pyc,, +werkzeug/middleware/dispatcher.py,sha256=Fh_w-KyWnTSYF-Lfv5dimQ7THSS7afPAZMmvc4zF1gg,2580 +werkzeug/middleware/http_proxy.py,sha256=HE8VyhS7CR-E1O6_9b68huv8FLgGGR1DLYqkS3Xcp3Q,7558 +werkzeug/middleware/lint.py,sha256=sAg3GcOhICIkwYX5bJGG8n8iebX0Yipq_UH0HvrBvoU,13964 +werkzeug/middleware/profiler.py,sha256=QkXk7cqnaPnF8wQu-5SyPCIOT3_kdABUBorQOghVNOA,4899 +werkzeug/middleware/proxy_fix.py,sha256=uRgQ3dEvFV8JxUqajHYYYOPEeA_BFqaa51Yp8VW0uzA,6849 +werkzeug/middleware/shared_data.py,sha256=xydEqOhAGg0aQJEllPDVfz2-8jHwWvJpAxfPsfPCu7k,10960 +werkzeug/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +werkzeug/routing.py,sha256=oqJ32sWIZtIF6zbqfrnwB1Pbv2ShNwPDJd6FYqxdYVo,84527 +werkzeug/sansio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +werkzeug/sansio/__pycache__/__init__.cpython-39.pyc,, +werkzeug/sansio/__pycache__/multipart.cpython-39.pyc,, +werkzeug/sansio/__pycache__/request.cpython-39.pyc,, +werkzeug/sansio/__pycache__/response.cpython-39.pyc,, +werkzeug/sansio/__pycache__/utils.cpython-39.pyc,, +werkzeug/sansio/multipart.py,sha256=bJMCNC2f5xyAaylahNViJ0JqmV4ThLRbDVGVzKwcqrQ,8751 +werkzeug/sansio/request.py,sha256=aA9rABkWiG4MhYMByanst2NXkEclsq8SIxhb0LQf0e0,20228 +werkzeug/sansio/response.py,sha256=zvCq9HSBBZGBd5Gg412BY9RZIwnKsJl5Kzfd3Kl9sSo,26098 +werkzeug/sansio/utils.py,sha256=V5v-UUnX8pm4RehP9Tt_NiUSOJGJGUvKjlW0eOIQldM,4164 +werkzeug/security.py,sha256=gPDRuCjkjWrcqj99tBMq8_nHFZLFQjgoW5Ga5XIw9jo,8158 +werkzeug/serving.py,sha256=AfgLn0yKr9qXknmwO-0KXJ055oloS4h5DIFDHEu8iHA,38088 +werkzeug/test.py,sha256=8gE1l-Y9yAh2i3SI0kgpxIaI4oYZuehIkxxyDFcz6J0,48123 +werkzeug/testapp.py,sha256=f48prWSGJhbSrvYb8e1fnAah4BkrLb0enHSdChgsjBY,9471 +werkzeug/urls.py,sha256=Du2lreBHvgBh5c2_bcx72g3hzV2ZabXYZsp-picUIJs,41023 +werkzeug/user_agent.py,sha256=WclZhpvgLurMF45hsioSbS75H1Zb4iMQGKN3_yZ2oKo,1420 +werkzeug/useragents.py,sha256=G8tmv_6vxJaPrLQH3eODNgIYe0_V6KETROQlJI-WxDE,7264 +werkzeug/utils.py,sha256=D_dnCLUfodQ4k0GRSpnI6qDoVoaX7-Dza57bx7sabG0,37101 +werkzeug/wrappers/__init__.py,sha256=-s75nPbyXHzU_rwmLPDhoMuGbEUk0jZT_n0ZQAOFGf8,654 +werkzeug/wrappers/__pycache__/__init__.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/accept.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/auth.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/base_request.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/base_response.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/common_descriptors.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/cors.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/etag.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/json.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/request.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/response.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/user_agent.cpython-39.pyc,, +werkzeug/wrappers/accept.py,sha256=_oZtAQkahvsrPRkNj2fieg7_St9P0NFC3SgZbJKS6xU,429 +werkzeug/wrappers/auth.py,sha256=rZPCzGxHk9R55PRkmS90kRywUVjjuMWzCGtH68qCq8U,856 +werkzeug/wrappers/base_request.py,sha256=saz9RyNQkvI_XLPYVm29KijNHmD1YzgxDqa0qHTbgss,1174 +werkzeug/wrappers/base_response.py,sha256=q_-TaYywT5G4zA-DWDRDJhJSat2_4O7gOPob6ye4_9A,1186 +werkzeug/wrappers/common_descriptors.py,sha256=v_kWLH3mvCiSRVJ1FNw7nO3w2UJfzY57UKKB5J4zCvE,898 +werkzeug/wrappers/cors.py,sha256=c5UndlZsZvYkbPrp6Gj5iSXxw_VOJDJHskO6-jRmNyQ,846 +werkzeug/wrappers/etag.py,sha256=XHWQQs7Mdd1oWezgBIsl-bYe8ydKkRZVil2Qd01D0Mo,846 +werkzeug/wrappers/json.py,sha256=HM1btPseGeXca0vnwQN_MvZl6h-qNsFY5YBKXKXFwus,410 +werkzeug/wrappers/request.py,sha256=yZGplfC3UqNuykwLJmgywiMhmnoKEGHJOZn_A_ublcQ,24822 +werkzeug/wrappers/response.py,sha256=0n8OcQptiM2e550SALLeg7vC1uWsUbCeE1rPZFfXR78,35177 +werkzeug/wrappers/user_agent.py,sha256=Wl1-A0-1r8o7cHIZQTB55O4Ged6LpCKENaQDlOY5pXA,435 +werkzeug/wsgi.py,sha256=L7s5-Rlt7BRVEZ1m81MaenGfMDP7yL3p1Kxt9Yssqzg,33727 diff --git a/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/WHEEL b/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/top_level.txt b/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/top_level.txt new file mode 100644 index 0000000..6fe8da8 --- /dev/null +++ b/radar/Lib/site-packages/Werkzeug-2.0.2.dist-info/top_level.txt @@ -0,0 +1 @@ +werkzeug diff --git a/radar/Lib/site-packages/_pyrsistent_version.py b/radar/Lib/site-packages/_pyrsistent_version.py new file mode 100644 index 0000000..5ec52a9 --- /dev/null +++ b/radar/Lib/site-packages/_pyrsistent_version.py @@ -0,0 +1 @@ +__version__ = '0.18.0' diff --git a/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/INSTALLER b/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/LICENSE b/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/LICENSE new file mode 100644 index 0000000..40b0774 --- /dev/null +++ b/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2021, Brandon Nielsen +All rights reserved. + +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/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/METADATA b/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/METADATA new file mode 100644 index 0000000..cb5b25d --- /dev/null +++ b/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/METADATA @@ -0,0 +1,513 @@ +Metadata-Version: 2.1 +Name: aniso8601 +Version: 9.0.1 +Summary: A library for parsing ISO 8601 strings. +Home-page: https://bitbucket.org/nielsenb/aniso8601 +Author: Brandon Nielsen +Author-email: nielsenb@jetfuse.net +License: UNKNOWN +Project-URL: Documentation, https://aniso8601.readthedocs.io/ +Project-URL: Source, https://bitbucket.org/nielsenb/aniso8601 +Project-URL: Tracker, https://bitbucket.org/nielsenb/aniso8601/issues +Keywords: iso8601 parser +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Description-Content-Type: text/x-rst +Provides-Extra: dev +Requires-Dist: black ; extra == 'dev' +Requires-Dist: coverage ; extra == 'dev' +Requires-Dist: isort ; extra == 'dev' +Requires-Dist: pre-commit ; extra == 'dev' +Requires-Dist: pyenchant ; extra == 'dev' +Requires-Dist: pylint ; extra == 'dev' + +aniso8601 +========= + +Another ISO 8601 parser for Python +---------------------------------- + +Features +======== +* Pure Python implementation +* Logical behavior + + - Parse a time, get a `datetime.time `_ + - Parse a date, get a `datetime.date `_ + - Parse a datetime, get a `datetime.datetime `_ + - Parse a duration, get a `datetime.timedelta `_ + - Parse an interval, get a tuple of dates or datetimes + - Parse a repeating interval, get a date or datetime `generator `_ + +* UTC offset represented as fixed-offset tzinfo +* Parser separate from representation, allowing parsing to different datetime representations (see `Builders`_) +* No regular expressions + +Installation +============ + +The recommended installation method is to use pip:: + + $ pip install aniso8601 + +Alternatively, you can download the source (git repository hosted at `Bitbucket `_) and install directly:: + + $ python setup.py install + +Use +=== + +Parsing datetimes +----------------- + +*Consider* `datetime.datetime.fromisoformat `_ *for basic ISO 8601 datetime parsing* + +To parse a typical ISO 8601 datetime string:: + + >>> import aniso8601 + >>> aniso8601.parse_datetime('1977-06-10T12:00:00Z') + datetime.datetime(1977, 6, 10, 12, 0, tzinfo=+0:00:00 UTC) + +Alternative delimiters can be specified, for example, a space:: + + >>> aniso8601.parse_datetime('1977-06-10 12:00:00Z', delimiter=' ') + datetime.datetime(1977, 6, 10, 12, 0, tzinfo=+0:00:00 UTC) + +UTC offsets are supported:: + + >>> aniso8601.parse_datetime('1979-06-05T08:00:00-08:00') + datetime.datetime(1979, 6, 5, 8, 0, tzinfo=-8:00:00 UTC) + +If a UTC offset is not specified, the returned datetime will be naive:: + + >>> aniso8601.parse_datetime('1983-01-22T08:00:00') + datetime.datetime(1983, 1, 22, 8, 0) + +Leap seconds are currently not supported and attempting to parse one raises a :code:`LeapSecondError`:: + + >>> aniso8601.parse_datetime('2018-03-06T23:59:60') + Traceback (most recent call last): + File "", line 1, in + File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/time.py", line 196, in parse_datetime + return builder.build_datetime(datepart, timepart) + File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py", line 237, in build_datetime + cls._build_object(time)) + File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/__init__.py", line 336, in _build_object + return cls.build_time(hh=parsetuple.hh, mm=parsetuple.mm, + File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py", line 191, in build_time + hh, mm, ss, tz = cls.range_check_time(hh, mm, ss, tz) + File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/__init__.py", line 266, in range_check_time + raise LeapSecondError('Leap seconds are not supported.') + aniso8601.exceptions.LeapSecondError: Leap seconds are not supported. + +To get the resolution of an ISO 8601 datetime string:: + + >>> aniso8601.get_datetime_resolution('1977-06-10T12:00:00Z') == aniso8601.resolution.TimeResolution.Seconds + True + >>> aniso8601.get_datetime_resolution('1977-06-10T12:00') == aniso8601.resolution.TimeResolution.Minutes + True + >>> aniso8601.get_datetime_resolution('1977-06-10T12') == aniso8601.resolution.TimeResolution.Hours + True + +Note that datetime resolutions map to :code:`TimeResolution` as a valid datetime must have at least one time member so the resolution mapping is equivalent. + +Parsing dates +------------- + +*Consider* `datetime.date.fromisoformat `_ *for basic ISO 8601 date parsing* + +To parse a date represented in an ISO 8601 string:: + + >>> import aniso8601 + >>> aniso8601.parse_date('1984-04-23') + datetime.date(1984, 4, 23) + +Basic format is supported as well:: + + >>> aniso8601.parse_date('19840423') + datetime.date(1984, 4, 23) + +To parse a date using the ISO 8601 week date format:: + + >>> aniso8601.parse_date('1986-W38-1') + datetime.date(1986, 9, 15) + +To parse an ISO 8601 ordinal date:: + + >>> aniso8601.parse_date('1988-132') + datetime.date(1988, 5, 11) + +To get the resolution of an ISO 8601 date string:: + + >>> aniso8601.get_date_resolution('1981-04-05') == aniso8601.resolution.DateResolution.Day + True + >>> aniso8601.get_date_resolution('1981-04') == aniso8601.resolution.DateResolution.Month + True + >>> aniso8601.get_date_resolution('1981') == aniso8601.resolution.DateResolution.Year + True + +Parsing times +------------- + +*Consider* `datetime.time.fromisoformat `_ *for basic ISO 8601 time parsing* + +To parse a time formatted as an ISO 8601 string:: + + >>> import aniso8601 + >>> aniso8601.parse_time('11:31:14') + datetime.time(11, 31, 14) + +As with all of the above, basic format is supported:: + + >>> aniso8601.parse_time('113114') + datetime.time(11, 31, 14) + +A UTC offset can be specified for times:: + + >>> aniso8601.parse_time('17:18:19-02:30') + datetime.time(17, 18, 19, tzinfo=-2:30:00 UTC) + >>> aniso8601.parse_time('171819Z') + datetime.time(17, 18, 19, tzinfo=+0:00:00 UTC) + +Reduced accuracy is supported:: + + >>> aniso8601.parse_time('21:42') + datetime.time(21, 42) + >>> aniso8601.parse_time('22') + datetime.time(22, 0) + +A decimal fraction is always allowed on the lowest order element of an ISO 8601 formatted time:: + + >>> aniso8601.parse_time('22:33.5') + datetime.time(22, 33, 30) + >>> aniso8601.parse_time('23.75') + datetime.time(23, 45) + +The decimal fraction can be specified with a comma instead of a full-stop:: + + >>> aniso8601.parse_time('22:33,5') + datetime.time(22, 33, 30) + >>> aniso8601.parse_time('23,75') + datetime.time(23, 45) + +Leap seconds are currently not supported and attempting to parse one raises a :code:`LeapSecondError`:: + + >>> aniso8601.parse_time('23:59:60') + Traceback (most recent call last): + File "", line 1, in + File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/time.py", line 174, in parse_time + return builder.build_time(hh=hourstr, mm=minutestr, ss=secondstr, tz=tz) + File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py", line 191, in build_time + hh, mm, ss, tz = cls.range_check_time(hh, mm, ss, tz) + File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/__init__.py", line 266, in range_check_time + raise LeapSecondError('Leap seconds are not supported.') + aniso8601.exceptions.LeapSecondError: Leap seconds are not supported. + +To get the resolution of an ISO 8601 time string:: + + >>> aniso8601.get_time_resolution('11:31:14') == aniso8601.resolution.TimeResolution.Seconds + True + >>> aniso8601.get_time_resolution('11:31') == aniso8601.resolution.TimeResolution.Minutes + True + >>> aniso8601.get_time_resolution('11') == aniso8601.resolution.TimeResolution.Hours + True + +Parsing durations +----------------- + +To parse a duration formatted as an ISO 8601 string:: + + >>> import aniso8601 + >>> aniso8601.parse_duration('P1Y2M3DT4H54M6S') + datetime.timedelta(428, 17646) + +Reduced accuracy is supported:: + + >>> aniso8601.parse_duration('P1Y') + datetime.timedelta(365) + +A decimal fraction is allowed on the lowest order element:: + + >>> aniso8601.parse_duration('P1YT3.5M') + datetime.timedelta(365, 210) + +The decimal fraction can be specified with a comma instead of a full-stop:: + + >>> aniso8601.parse_duration('P1YT3,5M') + datetime.timedelta(365, 210) + +Parsing a duration from a combined date and time is supported as well:: + + >>> aniso8601.parse_duration('P0001-01-02T01:30:05') + datetime.timedelta(397, 5405) + +To get the resolution of an ISO 8601 duration string:: + + >>> aniso8601.get_duration_resolution('P1Y2M3DT4H54M6S') == aniso8601.resolution.DurationResolution.Seconds + True + >>> aniso8601.get_duration_resolution('P1Y2M3DT4H54M') == aniso8601.resolution.DurationResolution.Minutes + True + >>> aniso8601.get_duration_resolution('P1Y2M3DT4H') == aniso8601.resolution.DurationResolution.Hours + True + >>> aniso8601.get_duration_resolution('P1Y2M3D') == aniso8601.resolution.DurationResolution.Days + True + >>> aniso8601.get_duration_resolution('P1Y2M') == aniso8601.resolution.DurationResolution.Months + True + >>> aniso8601.get_duration_resolution('P1Y') == aniso8601.resolution.DurationResolution.Years + True + +The default :code:`PythonTimeBuilder` assumes years are 365 days, and months are 30 days. Where calendar level accuracy is required, a `RelativeTimeBuilder `_ can be used, see also `Builders`_. + +Parsing intervals +----------------- + +To parse an interval specified by a start and end:: + + >>> import aniso8601 + >>> aniso8601.parse_interval('2007-03-01T13:00:00/2008-05-11T15:30:00') + (datetime.datetime(2007, 3, 1, 13, 0), datetime.datetime(2008, 5, 11, 15, 30)) + +Intervals specified by a start time and a duration are supported:: + + >>> aniso8601.parse_interval('2007-03-01T13:00:00Z/P1Y2M10DT2H30M') + (datetime.datetime(2007, 3, 1, 13, 0, tzinfo=+0:00:00 UTC), datetime.datetime(2008, 5, 9, 15, 30, tzinfo=+0:00:00 UTC)) + +A duration can also be specified by a duration and end time:: + + >>> aniso8601.parse_interval('P1M/1981-04-05') + (datetime.date(1981, 4, 5), datetime.date(1981, 3, 6)) + +Notice that the result of the above parse is not in order from earliest to latest. If sorted intervals are required, simply use the :code:`sorted` keyword as shown below:: + + >>> sorted(aniso8601.parse_interval('P1M/1981-04-05')) + [datetime.date(1981, 3, 6), datetime.date(1981, 4, 5)] + +The end of an interval is returned as a datetime when required to maintain the resolution specified by a duration, even if the duration start is given as a date:: + + >>> aniso8601.parse_interval('2014-11-12/PT4H54M6.5S') + (datetime.date(2014, 11, 12), datetime.datetime(2014, 11, 12, 4, 54, 6, 500000)) + >>> aniso8601.parse_interval('2007-03-01/P1.5D') + (datetime.date(2007, 3, 1), datetime.datetime(2007, 3, 2, 12, 0)) + +Concise representations are supported:: + + >>> aniso8601.parse_interval('2020-01-01/02') + (datetime.date(2020, 1, 1), datetime.date(2020, 1, 2)) + >>> aniso8601.parse_interval('2007-12-14T13:30/15:30') + (datetime.datetime(2007, 12, 14, 13, 30), datetime.datetime(2007, 12, 14, 15, 30)) + >>> aniso8601.parse_interval('2008-02-15/03-14') + (datetime.date(2008, 2, 15), datetime.date(2008, 3, 14)) + >>> aniso8601.parse_interval('2007-11-13T09:00/15T17:00') + (datetime.datetime(2007, 11, 13, 9, 0), datetime.datetime(2007, 11, 15, 17, 0)) + +Repeating intervals are supported as well, and return a `generator `_:: + + >>> aniso8601.parse_repeating_interval('R3/1981-04-05/P1D') + + >>> list(aniso8601.parse_repeating_interval('R3/1981-04-05/P1D')) + [datetime.date(1981, 4, 5), datetime.date(1981, 4, 6), datetime.date(1981, 4, 7)] + +Repeating intervals are allowed to go in the reverse direction:: + + >>> list(aniso8601.parse_repeating_interval('R2/PT1H2M/1980-03-05T01:01:00')) + [datetime.datetime(1980, 3, 5, 1, 1), datetime.datetime(1980, 3, 4, 23, 59)] + +Unbounded intervals are also allowed (Python 2):: + + >>> result = aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00') + >>> result.next() + datetime.datetime(1980, 3, 5, 1, 1) + >>> result.next() + datetime.datetime(1980, 3, 4, 23, 59) + +or for Python 3:: + + >>> result = aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00') + >>> next(result) + datetime.datetime(1980, 3, 5, 1, 1) + >>> next(result) + datetime.datetime(1980, 3, 4, 23, 59) + +Note that you should never try to convert a generator produced by an unbounded interval to a list:: + + >>> list(aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00')) + Traceback (most recent call last): + File "", line 1, in + File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py", line 560, in _date_generator_unbounded + currentdate += timedelta + OverflowError: date value out of range + +To get the resolution of an ISO 8601 interval string:: + + >>> aniso8601.get_interval_resolution('2007-03-01T13:00:00/2008-05-11T15:30:00') == aniso8601.resolution.IntervalResolution.Seconds + True + >>> aniso8601.get_interval_resolution('2007-03-01T13:00/2008-05-11T15:30') == aniso8601.resolution.IntervalResolution.Minutes + True + >>> aniso8601.get_interval_resolution('2007-03-01T13/2008-05-11T15') == aniso8601.resolution.IntervalResolution.Hours + True + >>> aniso8601.get_interval_resolution('2007-03-01/2008-05-11') == aniso8601.resolution.IntervalResolution.Day + True + >>> aniso8601.get_interval_resolution('2007-03/P1Y') == aniso8601.resolution.IntervalResolution.Month + True + >>> aniso8601.get_interval_resolution('2007/P1Y') == aniso8601.resolution.IntervalResolution.Year + True + +And for repeating ISO 8601 interval strings:: + + >>> aniso8601.get_repeating_interval_resolution('R3/1981-04-05/P1D') == aniso8601.resolution.IntervalResolution.Day + True + >>> aniso8601.get_repeating_interval_resolution('R/PT1H2M/1980-03-05T01:01:00') == aniso8601.resolution.IntervalResolution.Seconds + True + +Builders +======== + +Builders can be used to change the output format of a parse operation. All parse functions have a :code:`builder` keyword argument which accepts a builder class. + +Two builders are included. The :code:`PythonTimeBuilder` (the default) in the :code:`aniso8601.builders.python` module, and the :code:`TupleBuilder` which returns the parse result as a corresponding named tuple and is located in the :code:`aniso8601.builders` module. + +Information on writing a builder can be found in `BUILDERS `_. + +The following builders are available as separate projects: + +* `RelativeTimeBuilder `_ supports parsing to `datetutil relativedelta types `_ for calendar level accuracy +* `AttoTimeBuilder `_ supports parsing directly to `attotime attodatetime and attotimedelta types `_ which support sub-nanosecond precision +* `NumPyTimeBuilder `_ supports parsing directly to `NumPy datetime64 and timedelta64 types `_ + +TupleBuilder +------------ + +The :code:`TupleBuilder` returns parse results as `named tuples `_. It is located in the :code:`aniso8601.builders` module. + +Datetimes +^^^^^^^^^ + +Parsing a datetime returns a :code:`DatetimeTuple` containing :code:`Date` and :code:`Time` tuples . The date tuple contains the following parse components: :code:`YYYY`, :code:`MM`, :code:`DD`, :code:`Www`, :code:`D`, :code:`DDD`. The time tuple contains the following parse components :code:`hh`, :code:`mm`, :code:`ss`, :code:`tz`, where :code:`tz` itself is a tuple with the following components :code:`negative`, :code:`Z`, :code:`hh`, :code:`mm`, :code:`name` with :code:`negative` and :code:`Z` being booleans:: + + >>> import aniso8601 + >>> from aniso8601.builders import TupleBuilder + >>> aniso8601.parse_datetime('1977-06-10T12:00:00', builder=TupleBuilder) + Datetime(date=Date(YYYY='1977', MM='06', DD='10', Www=None, D=None, DDD=None), time=Time(hh='12', mm='00', ss='00', tz=None)) + >>> aniso8601.parse_datetime('1979-06-05T08:00:00-08:00', builder=TupleBuilder) + Datetime(date=Date(YYYY='1979', MM='06', DD='05', Www=None, D=None, DDD=None), time=Time(hh='08', mm='00', ss='00', tz=Timezone(negative=True, Z=None, hh='08', mm='00', name='-08:00'))) + +Dates +^^^^^ + +Parsing a date returns a :code:`DateTuple` containing the following parse components: :code:`YYYY`, :code:`MM`, :code:`DD`, :code:`Www`, :code:`D`, :code:`DDD`:: + + >>> import aniso8601 + >>> from aniso8601.builders import TupleBuilder + >>> aniso8601.parse_date('1984-04-23', builder=TupleBuilder) + Date(YYYY='1984', MM='04', DD='23', Www=None, D=None, DDD=None) + >>> aniso8601.parse_date('1986-W38-1', builder=TupleBuilder) + Date(YYYY='1986', MM=None, DD=None, Www='38', D='1', DDD=None) + >>> aniso8601.parse_date('1988-132', builder=TupleBuilder) + Date(YYYY='1988', MM=None, DD=None, Www=None, D=None, DDD='132') + +Times +^^^^^ + +Parsing a time returns a :code:`TimeTuple` containing following parse components: :code:`hh`, :code:`mm`, :code:`ss`, :code:`tz`, where :code:`tz` is a :code:`TimezoneTuple` with the following components :code:`negative`, :code:`Z`, :code:`hh`, :code:`mm`, :code:`name`, with :code:`negative` and :code:`Z` being booleans:: + + >>> import aniso8601 + >>> from aniso8601.builders import TupleBuilder + >>> aniso8601.parse_time('11:31:14', builder=TupleBuilder) + Time(hh='11', mm='31', ss='14', tz=None) + >>> aniso8601.parse_time('171819Z', builder=TupleBuilder) + Time(hh='17', mm='18', ss='19', tz=Timezone(negative=False, Z=True, hh=None, mm=None, name='Z')) + >>> aniso8601.parse_time('17:18:19-02:30', builder=TupleBuilder) + Time(hh='17', mm='18', ss='19', tz=Timezone(negative=True, Z=None, hh='02', mm='30', name='-02:30')) + +Durations +^^^^^^^^^ + +Parsing a duration returns a :code:`DurationTuple` containing the following parse components: :code:`PnY`, :code:`PnM`, :code:`PnW`, :code:`PnD`, :code:`TnH`, :code:`TnM`, :code:`TnS`:: + + >>> import aniso8601 + >>> from aniso8601.builders import TupleBuilder + >>> aniso8601.parse_duration('P1Y2M3DT4H54M6S', builder=TupleBuilder) + Duration(PnY='1', PnM='2', PnW=None, PnD='3', TnH='4', TnM='54', TnS='6') + >>> aniso8601.parse_duration('P7W', builder=TupleBuilder) + Duration(PnY=None, PnM=None, PnW='7', PnD=None, TnH=None, TnM=None, TnS=None) + +Intervals +^^^^^^^^^ + +Parsing an interval returns an :code:`IntervalTuple` containing the following parse components: :code:`start`, :code:`end`, :code:`duration`, :code:`start` and :code:`end` may both be datetime or date tuples, :code:`duration` is a duration tuple:: + + >>> import aniso8601 + >>> from aniso8601.builders import TupleBuilder + >>> aniso8601.parse_interval('2007-03-01T13:00:00/2008-05-11T15:30:00', builder=TupleBuilder) + Interval(start=Datetime(date=Date(YYYY='2007', MM='03', DD='01', Www=None, D=None, DDD=None), time=Time(hh='13', mm='00', ss='00', tz=None)), end=Datetime(date=Date(YYYY='2008', MM='05', DD='11', Www=None, D=None, DDD=None), time=Time(hh='15', mm='30', ss='00', tz=None)), duration=None) + >>> aniso8601.parse_interval('2007-03-01T13:00:00Z/P1Y2M10DT2H30M', builder=TupleBuilder) + Interval(start=Datetime(date=Date(YYYY='2007', MM='03', DD='01', Www=None, D=None, DDD=None), time=Time(hh='13', mm='00', ss='00', tz=Timezone(negative=False, Z=True, hh=None, mm=None, name='Z'))), end=None, duration=Duration(PnY='1', PnM='2', PnW=None, PnD='10', TnH='2', TnM='30', TnS=None)) + >>> aniso8601.parse_interval('P1M/1981-04-05', builder=TupleBuilder) + Interval(start=None, end=Date(YYYY='1981', MM='04', DD='05', Www=None, D=None, DDD=None), duration=Duration(PnY=None, PnM='1', PnW=None, PnD=None, TnH=None, TnM=None, TnS=None)) + +A repeating interval returns a :code:`RepeatingIntervalTuple` containing the following parse components: :code:`R`, :code:`Rnn`, :code:`interval`, where :code:`R` is a boolean, :code:`True` for an unbounded interval, :code:`False` otherwise.:: + + >>> aniso8601.parse_repeating_interval('R3/1981-04-05/P1D', builder=TupleBuilder) + RepeatingInterval(R=False, Rnn='3', interval=Interval(start=Date(YYYY='1981', MM='04', DD='05', Www=None, D=None, DDD=None), end=None, duration=Duration(PnY=None, PnM=None, PnW=None, PnD='1', TnH=None, TnM=None, TnS=None))) + >>> aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00', builder=TupleBuilder) + RepeatingInterval(R=True, Rnn=None, interval=Interval(start=None, end=Datetime(date=Date(YYYY='1980', MM='03', DD='05', Www=None, D=None, DDD=None), time=Time(hh='01', mm='01', ss='00', tz=None)), duration=Duration(PnY=None, PnM=None, PnW=None, PnD=None, TnH='1', TnM='2', TnS=None))) + +Development +=========== + +Setup +----- + +It is recommended to develop using a `virtualenv `_. + +Inside a virtualenv, development dependencies can be installed automatically:: + + $ pip install -e .[dev] + +`pre-commit `_ is used for managing pre-commit hooks:: + + $ pre-commit install + +To run the pre-commit hooks manually:: + + $ pre-commit run --all-files + +Tests +----- + +Tests can be run using the `unittest testing framework `_:: + + $ python -m unittest discover aniso8601 + +Contributing +============ + +aniso8601 is an open source project hosted on `Bitbucket `_. + +Any and all bugs are welcome on our `issue tracker `_. +Of particular interest are valid ISO 8601 strings that don't parse, or invalid ones that do. At a minimum, +bug reports should include an example of the misbehaving string, as well as the expected result. Of course +patches containing unit tests (or fixed bugs) are welcome! + +References +========== + +* `ISO 8601:2004(E) `_ (Caution, PDF link) +* `Wikipedia article on ISO 8601 `_ +* `Discussion on alternative ISO 8601 parsers for Python `_ + + diff --git a/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/RECORD b/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/RECORD new file mode 100644 index 0000000..4e2cfda --- /dev/null +++ b/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/RECORD @@ -0,0 +1,60 @@ +aniso8601-9.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +aniso8601-9.0.1.dist-info/LICENSE,sha256=Z_-MGC_A4Nc1cViNi8B5tOSmJKknTE4mqSPeIxDTvSk,1501 +aniso8601-9.0.1.dist-info/METADATA,sha256=8x7vpReMZppobPRH8Q564bwHL9XFsgCFR3TKrHhfkjE,23431 +aniso8601-9.0.1.dist-info/RECORD,, +aniso8601-9.0.1.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 +aniso8601-9.0.1.dist-info/top_level.txt,sha256=MVQomyeED8nGIH7PUQdMzxgLppIB48oYHtcmL17ETB0,10 +aniso8601/__init__.py,sha256=tHN7Nq-3I79PLzKkBuWun_UKgolnDrn7ISO8s1HlMdo,704 +aniso8601/__pycache__/__init__.cpython-39.pyc,, +aniso8601/__pycache__/compat.cpython-39.pyc,, +aniso8601/__pycache__/date.cpython-39.pyc,, +aniso8601/__pycache__/decimalfraction.cpython-39.pyc,, +aniso8601/__pycache__/duration.cpython-39.pyc,, +aniso8601/__pycache__/exceptions.cpython-39.pyc,, +aniso8601/__pycache__/interval.cpython-39.pyc,, +aniso8601/__pycache__/resolution.cpython-39.pyc,, +aniso8601/__pycache__/time.cpython-39.pyc,, +aniso8601/__pycache__/timezone.cpython-39.pyc,, +aniso8601/__pycache__/utcoffset.cpython-39.pyc,, +aniso8601/builders/__init__.py,sha256=sJanTP5Lo0lRxpLa5VKVBS9u6ZP8R1VRgozx5uSUMUU,17958 +aniso8601/builders/__pycache__/__init__.cpython-39.pyc,, +aniso8601/builders/__pycache__/python.cpython-39.pyc,, +aniso8601/builders/python.py,sha256=I0RhPY2syncaMwYRVJxM6ct-O_5MHnNFY3dcF6wvy0Y,22122 +aniso8601/builders/tests/__init__.py,sha256=XWM00Wzg9EZkSKyy3IW18Z8TiXfCbJS-XJNFVuylvuU,209 +aniso8601/builders/tests/__pycache__/__init__.cpython-39.pyc,, +aniso8601/builders/tests/__pycache__/test_init.cpython-39.pyc,, +aniso8601/builders/tests/__pycache__/test_python.cpython-39.pyc,, +aniso8601/builders/tests/test_init.py,sha256=wnDhjyb5iBt9l_zTXT96uqXus-igSqn5Kn_rqX_NSHA,29997 +aniso8601/builders/tests/test_python.py,sha256=pNr3lwfBKVSUQKc5BPmwCiCTpP_063WpOM-canDz4J8,61593 +aniso8601/compat.py,sha256=2exJsHW2DAxt_D2_mGj5mv0HCSMFeAAkPyFAM-ZFrA0,571 +aniso8601/date.py,sha256=IDn_kqeZshllwr4pICUNZhjbqSVVlYTyHmBOgp2MlNE,4475 +aniso8601/decimalfraction.py,sha256=EtwqSZJTtsQlu05m2guolhii5N1yN4dVv0v1zCZhiyk,333 +aniso8601/duration.py,sha256=6AAl9A-WM2Io898peIz9xbwOvxcLc6WYGUdkYuQlTU8,9583 +aniso8601/exceptions.py,sha256=-zrdcKocZhzhl71HhgVKXWF481XDWO3UhinbcycCzPU,1313 +aniso8601/interval.py,sha256=7e5wICHdF2gTeFluPxBrzaA4-_5b78QzXC62DSnNzlM,10763 +aniso8601/resolution.py,sha256=ee7GxL865D0dJL70TsXScz4Kzo_dwMORNvfuyCXdsgI,684 +aniso8601/tests/__init__.py,sha256=XWM00Wzg9EZkSKyy3IW18Z8TiXfCbJS-XJNFVuylvuU,209 +aniso8601/tests/__pycache__/__init__.cpython-39.pyc,, +aniso8601/tests/__pycache__/compat.cpython-39.pyc,, +aniso8601/tests/__pycache__/test_compat.cpython-39.pyc,, +aniso8601/tests/__pycache__/test_date.cpython-39.pyc,, +aniso8601/tests/__pycache__/test_decimalfraction.cpython-39.pyc,, +aniso8601/tests/__pycache__/test_duration.cpython-39.pyc,, +aniso8601/tests/__pycache__/test_init.cpython-39.pyc,, +aniso8601/tests/__pycache__/test_interval.cpython-39.pyc,, +aniso8601/tests/__pycache__/test_time.cpython-39.pyc,, +aniso8601/tests/__pycache__/test_timezone.cpython-39.pyc,, +aniso8601/tests/__pycache__/test_utcoffset.cpython-39.pyc,, +aniso8601/tests/compat.py,sha256=9HJqKvl0PIFBjePUgT-1eMGkA9tlESX0wNDkPvV7GOk,346 +aniso8601/tests/test_compat.py,sha256=2oFOFLKTfOJIMbLjkeVhrkxSDMjE0wM-NB86SJ6st5g,763 +aniso8601/tests/test_date.py,sha256=3AWmIHTS2sxm9_ZUYcI2w9ALJOYnHkkYEwlD1VW90iQ,8960 +aniso8601/tests/test_decimalfraction.py,sha256=T4R_SY24DW30YuQkyofxvAmngTuXtsmwd77pF25QAlc,578 +aniso8601/tests/test_duration.py,sha256=ZqUxodLrDBZ1GZWutFXjktAFHYS1hidxLclIGZP7aSA,44952 +aniso8601/tests/test_init.py,sha256=GazCeGTv-OFocCx9Cck04b-c1cWiiRnqhGwoGgm4Y1Q,1689 +aniso8601/tests/test_interval.py,sha256=lTg-E1vW1xmgwiWfHHwJDJ25AogSR-1p-0L4O2gQKQw,60457 +aniso8601/tests/test_time.py,sha256=HLutGVdg2_HHU51U2eEEZ9UNwljrQBPU_PtX8JrdVV0,19147 +aniso8601/tests/test_timezone.py,sha256=Shw7-fcUJZAbH7diCx37iXZ4VZEH45lqIgMJvoQQhtQ,4649 +aniso8601/tests/test_utcoffset.py,sha256=fRNuiz3WPMrHtrdMGK3HOuZRYd68hR-VNldbwVG-cDA,1926 +aniso8601/time.py,sha256=9IRsCERfEl_SnBBUIOR8E43XFD7Y2EqhowjiCcfinb0,5688 +aniso8601/timezone.py,sha256=5_LRd_pYd08i2hmXsn_1tTUxKOI4caSvxci-VByHCWU,2134 +aniso8601/utcoffset.py,sha256=8Gh8WNk_q9ELLEFZLMPbMESH-yqcoNFjul7VcpHq_1Q,2423 diff --git a/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/WHEEL b/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/WHEEL new file mode 100644 index 0000000..01b8fc7 --- /dev/null +++ b/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/top_level.txt b/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/top_level.txt new file mode 100644 index 0000000..166ae78 --- /dev/null +++ b/radar/Lib/site-packages/aniso8601-9.0.1.dist-info/top_level.txt @@ -0,0 +1 @@ +aniso8601 diff --git a/radar/Lib/site-packages/aniso8601/__init__.py b/radar/Lib/site-packages/aniso8601/__init__.py new file mode 100644 index 0000000..033d30b --- /dev/null +++ b/radar/Lib/site-packages/aniso8601/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Brandon Nielsen +# All rights reserved. +# +# This software may be modified and distributed under the terms +# of the BSD license. See the LICENSE file for details. + +from aniso8601.date import get_date_resolution, parse_date +from aniso8601.duration import get_duration_resolution, parse_duration +from aniso8601.interval import ( + get_interval_resolution, + get_repeating_interval_resolution, + parse_interval, + parse_repeating_interval, +) + +# Import the main parsing functions so they are readily available +from aniso8601.time import ( + get_datetime_resolution, + get_time_resolution, + parse_datetime, + parse_time, +) + +__version__ = "9.0.1" diff --git a/radar/Lib/site-packages/aniso8601/builders/__init__.py b/radar/Lib/site-packages/aniso8601/builders/__init__.py new file mode 100644 index 0000000..834c72a --- /dev/null +++ b/radar/Lib/site-packages/aniso8601/builders/__init__.py @@ -0,0 +1,614 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Brandon Nielsen +# All rights reserved. +# +# This software may be modified and distributed under the terms +# of the BSD license. See the LICENSE file for details. + +import calendar +from collections import namedtuple + +from aniso8601.exceptions import ( + DayOutOfBoundsError, + HoursOutOfBoundsError, + ISOFormatError, + LeapSecondError, + MidnightBoundsError, + MinutesOutOfBoundsError, + MonthOutOfBoundsError, + SecondsOutOfBoundsError, + WeekOutOfBoundsError, + YearOutOfBoundsError, +) + +DateTuple = namedtuple("Date", ["YYYY", "MM", "DD", "Www", "D", "DDD"]) +TimeTuple = namedtuple("Time", ["hh", "mm", "ss", "tz"]) +DatetimeTuple = namedtuple("Datetime", ["date", "time"]) +DurationTuple = namedtuple( + "Duration", ["PnY", "PnM", "PnW", "PnD", "TnH", "TnM", "TnS"] +) +IntervalTuple = namedtuple("Interval", ["start", "end", "duration"]) +RepeatingIntervalTuple = namedtuple("RepeatingInterval", ["R", "Rnn", "interval"]) +TimezoneTuple = namedtuple("Timezone", ["negative", "Z", "hh", "mm", "name"]) + +Limit = namedtuple( + "Limit", + [ + "casterrorstring", + "min", + "max", + "rangeexception", + "rangeerrorstring", + "rangefunc", + ], +) + + +def cast( + value, + castfunction, + caughtexceptions=(ValueError,), + thrownexception=ISOFormatError, + thrownmessage=None, +): + try: + result = castfunction(value) + except caughtexceptions: + raise thrownexception(thrownmessage) + + return result + + +def range_check(valuestr, limit): + # Returns cast value if in range, raises defined exceptions on failure + if valuestr is None: + return None + + if "." in valuestr: + castfunc = float + else: + castfunc = int + + value = cast(valuestr, castfunc, thrownmessage=limit.casterrorstring) + + if limit.min is not None and value < limit.min: + raise limit.rangeexception(limit.rangeerrorstring) + + if limit.max is not None and value > limit.max: + raise limit.rangeexception(limit.rangeerrorstring) + + return value + + +class BaseTimeBuilder(object): + # Limit tuple format cast function, cast error string, + # lower limit, upper limit, limit error string + DATE_YYYY_LIMIT = Limit( + "Invalid year string.", + 0000, + 9999, + YearOutOfBoundsError, + "Year must be between 1..9999.", + range_check, + ) + DATE_MM_LIMIT = Limit( + "Invalid month string.", + 1, + 12, + MonthOutOfBoundsError, + "Month must be between 1..12.", + range_check, + ) + DATE_DD_LIMIT = Limit( + "Invalid day string.", + 1, + 31, + DayOutOfBoundsError, + "Day must be between 1..31.", + range_check, + ) + DATE_WWW_LIMIT = Limit( + "Invalid week string.", + 1, + 53, + WeekOutOfBoundsError, + "Week number must be between 1..53.", + range_check, + ) + DATE_D_LIMIT = Limit( + "Invalid weekday string.", + 1, + 7, + DayOutOfBoundsError, + "Weekday number must be between 1..7.", + range_check, + ) + DATE_DDD_LIMIT = Limit( + "Invalid ordinal day string.", + 1, + 366, + DayOutOfBoundsError, + "Ordinal day must be between 1..366.", + range_check, + ) + TIME_HH_LIMIT = Limit( + "Invalid hour string.", + 0, + 24, + HoursOutOfBoundsError, + "Hour must be between 0..24 with " "24 representing midnight.", + range_check, + ) + TIME_MM_LIMIT = Limit( + "Invalid minute string.", + 0, + 59, + MinutesOutOfBoundsError, + "Minute must be between 0..59.", + range_check, + ) + TIME_SS_LIMIT = Limit( + "Invalid second string.", + 0, + 60, + SecondsOutOfBoundsError, + "Second must be between 0..60 with " "60 representing a leap second.", + range_check, + ) + TZ_HH_LIMIT = Limit( + "Invalid timezone hour string.", + 0, + 23, + HoursOutOfBoundsError, + "Hour must be between 0..23.", + range_check, + ) + TZ_MM_LIMIT = Limit( + "Invalid timezone minute string.", + 0, + 59, + MinutesOutOfBoundsError, + "Minute must be between 0..59.", + range_check, + ) + DURATION_PNY_LIMIT = Limit( + "Invalid year duration string.", + 0, + None, + ISOFormatError, + "Duration years component must be positive.", + range_check, + ) + DURATION_PNM_LIMIT = Limit( + "Invalid month duration string.", + 0, + None, + ISOFormatError, + "Duration months component must be positive.", + range_check, + ) + DURATION_PNW_LIMIT = Limit( + "Invalid week duration string.", + 0, + None, + ISOFormatError, + "Duration weeks component must be positive.", + range_check, + ) + DURATION_PND_LIMIT = Limit( + "Invalid day duration string.", + 0, + None, + ISOFormatError, + "Duration days component must be positive.", + range_check, + ) + DURATION_TNH_LIMIT = Limit( + "Invalid hour duration string.", + 0, + None, + ISOFormatError, + "Duration hours component must be positive.", + range_check, + ) + DURATION_TNM_LIMIT = Limit( + "Invalid minute duration string.", + 0, + None, + ISOFormatError, + "Duration minutes component must be positive.", + range_check, + ) + DURATION_TNS_LIMIT = Limit( + "Invalid second duration string.", + 0, + None, + ISOFormatError, + "Duration seconds component must be positive.", + range_check, + ) + INTERVAL_RNN_LIMIT = Limit( + "Invalid duration repetition string.", + 0, + None, + ISOFormatError, + "Duration repetition count must be positive.", + range_check, + ) + + DATE_RANGE_DICT = { + "YYYY": DATE_YYYY_LIMIT, + "MM": DATE_MM_LIMIT, + "DD": DATE_DD_LIMIT, + "Www": DATE_WWW_LIMIT, + "D": DATE_D_LIMIT, + "DDD": DATE_DDD_LIMIT, + } + + TIME_RANGE_DICT = {"hh": TIME_HH_LIMIT, "mm": TIME_MM_LIMIT, "ss": TIME_SS_LIMIT} + + DURATION_RANGE_DICT = { + "PnY": DURATION_PNY_LIMIT, + "PnM": DURATION_PNM_LIMIT, + "PnW": DURATION_PNW_LIMIT, + "PnD": DURATION_PND_LIMIT, + "TnH": DURATION_TNH_LIMIT, + "TnM": DURATION_TNM_LIMIT, + "TnS": DURATION_TNS_LIMIT, + } + + REPEATING_INTERVAL_RANGE_DICT = {"Rnn": INTERVAL_RNN_LIMIT} + + TIMEZONE_RANGE_DICT = {"hh": TZ_HH_LIMIT, "mm": TZ_MM_LIMIT} + + LEAP_SECONDS_SUPPORTED = False + + @classmethod + def build_date(cls, YYYY=None, MM=None, DD=None, Www=None, D=None, DDD=None): + raise NotImplementedError + + @classmethod + def build_time(cls, hh=None, mm=None, ss=None, tz=None): + raise NotImplementedError + + @classmethod + def build_datetime(cls, date, time): + raise NotImplementedError + + @classmethod + def build_duration( + cls, PnY=None, PnM=None, PnW=None, PnD=None, TnH=None, TnM=None, TnS=None + ): + raise NotImplementedError + + @classmethod + def build_interval(cls, start=None, end=None, duration=None): + # start, end, and duration are all tuples + raise NotImplementedError + + @classmethod + def build_repeating_interval(cls, R=None, Rnn=None, interval=None): + # interval is a tuple + raise NotImplementedError + + @classmethod + def build_timezone(cls, negative=None, Z=None, hh=None, mm=None, name=""): + raise NotImplementedError + + @classmethod + def range_check_date( + cls, YYYY=None, MM=None, DD=None, Www=None, D=None, DDD=None, rangedict=None + ): + if rangedict is None: + rangedict = cls.DATE_RANGE_DICT + + if "YYYY" in rangedict: + YYYY = rangedict["YYYY"].rangefunc(YYYY, rangedict["YYYY"]) + + if "MM" in rangedict: + MM = rangedict["MM"].rangefunc(MM, rangedict["MM"]) + + if "DD" in rangedict: + DD = rangedict["DD"].rangefunc(DD, rangedict["DD"]) + + if "Www" in rangedict: + Www = rangedict["Www"].rangefunc(Www, rangedict["Www"]) + + if "D" in rangedict: + D = rangedict["D"].rangefunc(D, rangedict["D"]) + + if "DDD" in rangedict: + DDD = rangedict["DDD"].rangefunc(DDD, rangedict["DDD"]) + + if DD is not None: + # Check calendar + if DD > calendar.monthrange(YYYY, MM)[1]: + raise DayOutOfBoundsError( + "{0} is out of range for {1}-{2}".format(DD, YYYY, MM) + ) + + if DDD is not None: + if calendar.isleap(YYYY) is False and DDD == 366: + raise DayOutOfBoundsError( + "{0} is only valid for leap year.".format(DDD) + ) + + return (YYYY, MM, DD, Www, D, DDD) + + @classmethod + def range_check_time(cls, hh=None, mm=None, ss=None, tz=None, rangedict=None): + # Used for midnight and leap second handling + midnight = False # Handle hh = '24' specially + + if rangedict is None: + rangedict = cls.TIME_RANGE_DICT + + if "hh" in rangedict: + try: + hh = rangedict["hh"].rangefunc(hh, rangedict["hh"]) + except HoursOutOfBoundsError as e: + if float(hh) > 24 and float(hh) < 25: + raise MidnightBoundsError("Hour 24 may only represent midnight.") + + raise e + + if "mm" in rangedict: + mm = rangedict["mm"].rangefunc(mm, rangedict["mm"]) + + if "ss" in rangedict: + ss = rangedict["ss"].rangefunc(ss, rangedict["ss"]) + + if hh is not None and hh == 24: + midnight = True + + # Handle midnight range + if midnight is True and ( + (mm is not None and mm != 0) or (ss is not None and ss != 0) + ): + raise MidnightBoundsError("Hour 24 may only represent midnight.") + + if cls.LEAP_SECONDS_SUPPORTED is True: + if hh != 23 and mm != 59 and ss == 60: + raise cls.TIME_SS_LIMIT.rangeexception( + cls.TIME_SS_LIMIT.rangeerrorstring + ) + else: + if hh == 23 and mm == 59 and ss == 60: + # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is + raise LeapSecondError("Leap seconds are not supported.") + + if ss == 60: + raise cls.TIME_SS_LIMIT.rangeexception( + cls.TIME_SS_LIMIT.rangeerrorstring + ) + + return (hh, mm, ss, tz) + + @classmethod + def range_check_duration( + cls, + PnY=None, + PnM=None, + PnW=None, + PnD=None, + TnH=None, + TnM=None, + TnS=None, + rangedict=None, + ): + if rangedict is None: + rangedict = cls.DURATION_RANGE_DICT + + if "PnY" in rangedict: + PnY = rangedict["PnY"].rangefunc(PnY, rangedict["PnY"]) + + if "PnM" in rangedict: + PnM = rangedict["PnM"].rangefunc(PnM, rangedict["PnM"]) + + if "PnW" in rangedict: + PnW = rangedict["PnW"].rangefunc(PnW, rangedict["PnW"]) + + if "PnD" in rangedict: + PnD = rangedict["PnD"].rangefunc(PnD, rangedict["PnD"]) + + if "TnH" in rangedict: + TnH = rangedict["TnH"].rangefunc(TnH, rangedict["TnH"]) + + if "TnM" in rangedict: + TnM = rangedict["TnM"].rangefunc(TnM, rangedict["TnM"]) + + if "TnS" in rangedict: + TnS = rangedict["TnS"].rangefunc(TnS, rangedict["TnS"]) + + return (PnY, PnM, PnW, PnD, TnH, TnM, TnS) + + @classmethod + def range_check_repeating_interval( + cls, R=None, Rnn=None, interval=None, rangedict=None + ): + if rangedict is None: + rangedict = cls.REPEATING_INTERVAL_RANGE_DICT + + if "Rnn" in rangedict: + Rnn = rangedict["Rnn"].rangefunc(Rnn, rangedict["Rnn"]) + + return (R, Rnn, interval) + + @classmethod + def range_check_timezone( + cls, negative=None, Z=None, hh=None, mm=None, name="", rangedict=None + ): + if rangedict is None: + rangedict = cls.TIMEZONE_RANGE_DICT + + if "hh" in rangedict: + hh = rangedict["hh"].rangefunc(hh, rangedict["hh"]) + + if "mm" in rangedict: + mm = rangedict["mm"].rangefunc(mm, rangedict["mm"]) + + return (negative, Z, hh, mm, name) + + @classmethod + def _build_object(cls, parsetuple): + # Given a TupleBuilder tuple, build the correct object + if type(parsetuple) is DateTuple: + return cls.build_date( + YYYY=parsetuple.YYYY, + MM=parsetuple.MM, + DD=parsetuple.DD, + Www=parsetuple.Www, + D=parsetuple.D, + DDD=parsetuple.DDD, + ) + + if type(parsetuple) is TimeTuple: + return cls.build_time( + hh=parsetuple.hh, mm=parsetuple.mm, ss=parsetuple.ss, tz=parsetuple.tz + ) + + if type(parsetuple) is DatetimeTuple: + return cls.build_datetime(parsetuple.date, parsetuple.time) + + if type(parsetuple) is DurationTuple: + return cls.build_duration( + PnY=parsetuple.PnY, + PnM=parsetuple.PnM, + PnW=parsetuple.PnW, + PnD=parsetuple.PnD, + TnH=parsetuple.TnH, + TnM=parsetuple.TnM, + TnS=parsetuple.TnS, + ) + + if type(parsetuple) is IntervalTuple: + return cls.build_interval( + start=parsetuple.start, end=parsetuple.end, duration=parsetuple.duration + ) + + if type(parsetuple) is RepeatingIntervalTuple: + return cls.build_repeating_interval( + R=parsetuple.R, Rnn=parsetuple.Rnn, interval=parsetuple.interval + ) + + return cls.build_timezone( + negative=parsetuple.negative, + Z=parsetuple.Z, + hh=parsetuple.hh, + mm=parsetuple.mm, + name=parsetuple.name, + ) + + @classmethod + def _is_interval_end_concise(cls, endtuple): + if type(endtuple) is TimeTuple: + return True + + if type(endtuple) is DatetimeTuple: + enddatetuple = endtuple.date + else: + enddatetuple = endtuple + + if enddatetuple.YYYY is None: + return True + + return False + + @classmethod + def _combine_concise_interval_tuples(cls, starttuple, conciseendtuple): + starttimetuple = None + startdatetuple = None + + endtimetuple = None + enddatetuple = None + + if type(starttuple) is DateTuple: + startdatetuple = starttuple + else: + # Start is a datetime + starttimetuple = starttuple.time + startdatetuple = starttuple.date + + if type(conciseendtuple) is DateTuple: + enddatetuple = conciseendtuple + elif type(conciseendtuple) is DatetimeTuple: + enddatetuple = conciseendtuple.date + endtimetuple = conciseendtuple.time + else: + # Time + endtimetuple = conciseendtuple + + if enddatetuple is not None: + if enddatetuple.YYYY is None and enddatetuple.MM is None: + newenddatetuple = DateTuple( + YYYY=startdatetuple.YYYY, + MM=startdatetuple.MM, + DD=enddatetuple.DD, + Www=enddatetuple.Www, + D=enddatetuple.D, + DDD=enddatetuple.DDD, + ) + else: + newenddatetuple = DateTuple( + YYYY=startdatetuple.YYYY, + MM=enddatetuple.MM, + DD=enddatetuple.DD, + Www=enddatetuple.Www, + D=enddatetuple.D, + DDD=enddatetuple.DDD, + ) + + if (starttimetuple is not None and starttimetuple.tz is not None) and ( + endtimetuple is not None and endtimetuple.tz != starttimetuple.tz + ): + # Copy the timezone across + endtimetuple = TimeTuple( + hh=endtimetuple.hh, + mm=endtimetuple.mm, + ss=endtimetuple.ss, + tz=starttimetuple.tz, + ) + + if enddatetuple is not None and endtimetuple is None: + return newenddatetuple + + if enddatetuple is not None and endtimetuple is not None: + return TupleBuilder.build_datetime(newenddatetuple, endtimetuple) + + return TupleBuilder.build_datetime(startdatetuple, endtimetuple) + + +class TupleBuilder(BaseTimeBuilder): + # Builder used to return the arguments as a tuple, cleans up some parse methods + @classmethod + def build_date(cls, YYYY=None, MM=None, DD=None, Www=None, D=None, DDD=None): + + return DateTuple(YYYY, MM, DD, Www, D, DDD) + + @classmethod + def build_time(cls, hh=None, mm=None, ss=None, tz=None): + return TimeTuple(hh, mm, ss, tz) + + @classmethod + def build_datetime(cls, date, time): + return DatetimeTuple(date, time) + + @classmethod + def build_duration( + cls, PnY=None, PnM=None, PnW=None, PnD=None, TnH=None, TnM=None, TnS=None + ): + + return DurationTuple(PnY, PnM, PnW, PnD, TnH, TnM, TnS) + + @classmethod + def build_interval(cls, start=None, end=None, duration=None): + return IntervalTuple(start, end, duration) + + @classmethod + def build_repeating_interval(cls, R=None, Rnn=None, interval=None): + return RepeatingIntervalTuple(R, Rnn, interval) + + @classmethod + def build_timezone(cls, negative=None, Z=None, hh=None, mm=None, name=""): + return TimezoneTuple(negative, Z, hh, mm, name) diff --git a/radar/Lib/site-packages/aniso8601/builders/python.py b/radar/Lib/site-packages/aniso8601/builders/python.py new file mode 100644 index 0000000..8956740 --- /dev/null +++ b/radar/Lib/site-packages/aniso8601/builders/python.py @@ -0,0 +1,705 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Brandon Nielsen +# All rights reserved. +# +# This software may be modified and distributed under the terms +# of the BSD license. See the LICENSE file for details. + +import datetime +from collections import namedtuple +from functools import partial + +from aniso8601.builders import ( + BaseTimeBuilder, + DatetimeTuple, + DateTuple, + Limit, + TimeTuple, + TupleBuilder, + cast, + range_check, +) +from aniso8601.exceptions import ( + DayOutOfBoundsError, + HoursOutOfBoundsError, + ISOFormatError, + LeapSecondError, + MidnightBoundsError, + MinutesOutOfBoundsError, + MonthOutOfBoundsError, + SecondsOutOfBoundsError, + WeekOutOfBoundsError, + YearOutOfBoundsError, +) +from aniso8601.utcoffset import UTCOffset + +DAYS_PER_YEAR = 365 +DAYS_PER_MONTH = 30 +DAYS_PER_WEEK = 7 + +HOURS_PER_DAY = 24 + +MINUTES_PER_HOUR = 60 +MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY + +SECONDS_PER_MINUTE = 60 +SECONDS_PER_DAY = MINUTES_PER_DAY * SECONDS_PER_MINUTE + +MICROSECONDS_PER_SECOND = int(1e6) + +MICROSECONDS_PER_MINUTE = 60 * MICROSECONDS_PER_SECOND +MICROSECONDS_PER_HOUR = 60 * MICROSECONDS_PER_MINUTE +MICROSECONDS_PER_DAY = 24 * MICROSECONDS_PER_HOUR +MICROSECONDS_PER_WEEK = 7 * MICROSECONDS_PER_DAY +MICROSECONDS_PER_MONTH = DAYS_PER_MONTH * MICROSECONDS_PER_DAY +MICROSECONDS_PER_YEAR = DAYS_PER_YEAR * MICROSECONDS_PER_DAY + +TIMEDELTA_MAX_DAYS = datetime.timedelta.max.days + +FractionalComponent = namedtuple( + "FractionalComponent", ["principal", "microsecondremainder"] +) + + +def year_range_check(valuestr, limit): + YYYYstr = valuestr + + # Truncated dates, like '19', refer to 1900-1999 inclusive, + # we simply parse to 1900 + if len(valuestr) < 4: + # Shift 0s in from the left to form complete year + YYYYstr = valuestr.ljust(4, "0") + + return range_check(YYYYstr, limit) + + +def fractional_range_check(conversion, valuestr, limit): + if valuestr is None: + return None + + if "." in valuestr: + castfunc = partial(_cast_to_fractional_component, conversion) + else: + castfunc = int + + value = cast(valuestr, castfunc, thrownmessage=limit.casterrorstring) + + if type(value) is FractionalComponent: + tocheck = float(valuestr) + else: + tocheck = int(valuestr) + + if limit.min is not None and tocheck < limit.min: + raise limit.rangeexception(limit.rangeerrorstring) + + if limit.max is not None and tocheck > limit.max: + raise limit.rangeexception(limit.rangeerrorstring) + + return value + + +def _cast_to_fractional_component(conversion, floatstr): + # Splits a string with a decimal point into an int, and + # int representing the floating point remainder as a number + # of microseconds, determined by multiplying by conversion + intpart, floatpart = floatstr.split(".") + + intvalue = int(intpart) + preconvertedvalue = int(floatpart) + + convertedvalue = (preconvertedvalue * conversion) // (10 ** len(floatpart)) + + return FractionalComponent(intvalue, convertedvalue) + + +class PythonTimeBuilder(BaseTimeBuilder): + # 0000 (1 BC) is not representable as a Python date + DATE_YYYY_LIMIT = Limit( + "Invalid year string.", + datetime.MINYEAR, + datetime.MAXYEAR, + YearOutOfBoundsError, + "Year must be between {0}..{1}.".format(datetime.MINYEAR, datetime.MAXYEAR), + year_range_check, + ) + TIME_HH_LIMIT = Limit( + "Invalid hour string.", + 0, + 24, + HoursOutOfBoundsError, + "Hour must be between 0..24 with " "24 representing midnight.", + partial(fractional_range_check, MICROSECONDS_PER_HOUR), + ) + TIME_MM_LIMIT = Limit( + "Invalid minute string.", + 0, + 59, + MinutesOutOfBoundsError, + "Minute must be between 0..59.", + partial(fractional_range_check, MICROSECONDS_PER_MINUTE), + ) + TIME_SS_LIMIT = Limit( + "Invalid second string.", + 0, + 60, + SecondsOutOfBoundsError, + "Second must be between 0..60 with " "60 representing a leap second.", + partial(fractional_range_check, MICROSECONDS_PER_SECOND), + ) + DURATION_PNY_LIMIT = Limit( + "Invalid year duration string.", + None, + None, + YearOutOfBoundsError, + None, + partial(fractional_range_check, MICROSECONDS_PER_YEAR), + ) + DURATION_PNM_LIMIT = Limit( + "Invalid month duration string.", + None, + None, + MonthOutOfBoundsError, + None, + partial(fractional_range_check, MICROSECONDS_PER_MONTH), + ) + DURATION_PNW_LIMIT = Limit( + "Invalid week duration string.", + None, + None, + WeekOutOfBoundsError, + None, + partial(fractional_range_check, MICROSECONDS_PER_WEEK), + ) + DURATION_PND_LIMIT = Limit( + "Invalid day duration string.", + None, + None, + DayOutOfBoundsError, + None, + partial(fractional_range_check, MICROSECONDS_PER_DAY), + ) + DURATION_TNH_LIMIT = Limit( + "Invalid hour duration string.", + None, + None, + HoursOutOfBoundsError, + None, + partial(fractional_range_check, MICROSECONDS_PER_HOUR), + ) + DURATION_TNM_LIMIT = Limit( + "Invalid minute duration string.", + None, + None, + MinutesOutOfBoundsError, + None, + partial(fractional_range_check, MICROSECONDS_PER_MINUTE), + ) + DURATION_TNS_LIMIT = Limit( + "Invalid second duration string.", + None, + None, + SecondsOutOfBoundsError, + None, + partial(fractional_range_check, MICROSECONDS_PER_SECOND), + ) + + DATE_RANGE_DICT = BaseTimeBuilder.DATE_RANGE_DICT + DATE_RANGE_DICT["YYYY"] = DATE_YYYY_LIMIT + + TIME_RANGE_DICT = {"hh": TIME_HH_LIMIT, "mm": TIME_MM_LIMIT, "ss": TIME_SS_LIMIT} + + DURATION_RANGE_DICT = { + "PnY": DURATION_PNY_LIMIT, + "PnM": DURATION_PNM_LIMIT, + "PnW": DURATION_PNW_LIMIT, + "PnD": DURATION_PND_LIMIT, + "TnH": DURATION_TNH_LIMIT, + "TnM": DURATION_TNM_LIMIT, + "TnS": DURATION_TNS_LIMIT, + } + + @classmethod + def build_date(cls, YYYY=None, MM=None, DD=None, Www=None, D=None, DDD=None): + YYYY, MM, DD, Www, D, DDD = cls.range_check_date(YYYY, MM, DD, Www, D, DDD) + + if MM is None: + MM = 1 + + if DD is None: + DD = 1 + + if DDD is not None: + return PythonTimeBuilder._build_ordinal_date(YYYY, DDD) + + if Www is not None: + return PythonTimeBuilder._build_week_date(YYYY, Www, isoday=D) + + return datetime.date(YYYY, MM, DD) + + @classmethod + def build_time(cls, hh=None, mm=None, ss=None, tz=None): + # Builds a time from the given parts, handling fractional arguments + # where necessary + hours = 0 + minutes = 0 + seconds = 0 + microseconds = 0 + + hh, mm, ss, tz = cls.range_check_time(hh, mm, ss, tz) + + if type(hh) is FractionalComponent: + hours = hh.principal + microseconds = hh.microsecondremainder + elif hh is not None: + hours = hh + + if type(mm) is FractionalComponent: + minutes = mm.principal + microseconds = mm.microsecondremainder + elif mm is not None: + minutes = mm + + if type(ss) is FractionalComponent: + seconds = ss.principal + microseconds = ss.microsecondremainder + elif ss is not None: + seconds = ss + + ( + hours, + minutes, + seconds, + microseconds, + ) = PythonTimeBuilder._distribute_microseconds( + microseconds, + (hours, minutes, seconds), + (MICROSECONDS_PER_HOUR, MICROSECONDS_PER_MINUTE, MICROSECONDS_PER_SECOND), + ) + + # Move midnight into range + if hours == 24: + hours = 0 + + # Datetimes don't handle fractional components, so we use a timedelta + if tz is not None: + return ( + datetime.datetime( + 1, 1, 1, hour=hours, minute=minutes, tzinfo=cls._build_object(tz) + ) + + datetime.timedelta(seconds=seconds, microseconds=microseconds) + ).timetz() + + return ( + datetime.datetime(1, 1, 1, hour=hours, minute=minutes) + + datetime.timedelta(seconds=seconds, microseconds=microseconds) + ).time() + + @classmethod + def build_datetime(cls, date, time): + return datetime.datetime.combine( + cls._build_object(date), cls._build_object(time) + ) + + @classmethod + def build_duration( + cls, PnY=None, PnM=None, PnW=None, PnD=None, TnH=None, TnM=None, TnS=None + ): + # PnY and PnM will be distributed to PnD, microsecond remainder to TnS + PnY, PnM, PnW, PnD, TnH, TnM, TnS = cls.range_check_duration( + PnY, PnM, PnW, PnD, TnH, TnM, TnS + ) + + seconds = TnS.principal + microseconds = TnS.microsecondremainder + + return datetime.timedelta( + days=PnD, + seconds=seconds, + microseconds=microseconds, + minutes=TnM, + hours=TnH, + weeks=PnW, + ) + + @classmethod + def build_interval(cls, start=None, end=None, duration=None): + start, end, duration = cls.range_check_interval(start, end, duration) + + if start is not None and end is not None: + # / + startobject = cls._build_object(start) + endobject = cls._build_object(end) + + return (startobject, endobject) + + durationobject = cls._build_object(duration) + + # Determine if datetime promotion is required + datetimerequired = ( + duration.TnH is not None + or duration.TnM is not None + or duration.TnS is not None + or durationobject.seconds != 0 + or durationobject.microseconds != 0 + ) + + if end is not None: + # / + endobject = cls._build_object(end) + + # Range check + if type(end) is DateTuple and datetimerequired is True: + # is a date, and requires datetime resolution + return ( + endobject, + cls.build_datetime(end, TupleBuilder.build_time()) - durationobject, + ) + + return (endobject, endobject - durationobject) + + # / + startobject = cls._build_object(start) + + # Range check + if type(start) is DateTuple and datetimerequired is True: + # is a date, and requires datetime resolution + return ( + startobject, + cls.build_datetime(start, TupleBuilder.build_time()) + durationobject, + ) + + return (startobject, startobject + durationobject) + + @classmethod + def build_repeating_interval(cls, R=None, Rnn=None, interval=None): + startobject = None + endobject = None + + R, Rnn, interval = cls.range_check_repeating_interval(R, Rnn, interval) + + if interval.start is not None: + startobject = cls._build_object(interval.start) + + if interval.end is not None: + endobject = cls._build_object(interval.end) + + if interval.duration is not None: + durationobject = cls._build_object(interval.duration) + else: + durationobject = endobject - startobject + + if R is True: + if startobject is not None: + return cls._date_generator_unbounded(startobject, durationobject) + + return cls._date_generator_unbounded(endobject, -durationobject) + + iterations = int(Rnn) + + if startobject is not None: + return cls._date_generator(startobject, durationobject, iterations) + + return cls._date_generator(endobject, -durationobject, iterations) + + @classmethod + def build_timezone(cls, negative=None, Z=None, hh=None, mm=None, name=""): + negative, Z, hh, mm, name = cls.range_check_timezone(negative, Z, hh, mm, name) + + if Z is True: + # Z -> UTC + return UTCOffset(name="UTC", minutes=0) + + tzhour = int(hh) + + if mm is not None: + tzminute = int(mm) + else: + tzminute = 0 + + if negative is True: + return UTCOffset(name=name, minutes=-(tzhour * 60 + tzminute)) + + return UTCOffset(name=name, minutes=tzhour * 60 + tzminute) + + @classmethod + def range_check_duration( + cls, + PnY=None, + PnM=None, + PnW=None, + PnD=None, + TnH=None, + TnM=None, + TnS=None, + rangedict=None, + ): + years = 0 + months = 0 + days = 0 + weeks = 0 + hours = 0 + minutes = 0 + seconds = 0 + microseconds = 0 + + PnY, PnM, PnW, PnD, TnH, TnM, TnS = BaseTimeBuilder.range_check_duration( + PnY, PnM, PnW, PnD, TnH, TnM, TnS, rangedict=cls.DURATION_RANGE_DICT + ) + + if PnY is not None: + if type(PnY) is FractionalComponent: + years = PnY.principal + microseconds = PnY.microsecondremainder + else: + years = PnY + + if years * DAYS_PER_YEAR > TIMEDELTA_MAX_DAYS: + raise YearOutOfBoundsError("Duration exceeds maximum timedelta size.") + + if PnM is not None: + if type(PnM) is FractionalComponent: + months = PnM.principal + microseconds = PnM.microsecondremainder + else: + months = PnM + + if months * DAYS_PER_MONTH > TIMEDELTA_MAX_DAYS: + raise MonthOutOfBoundsError("Duration exceeds maximum timedelta size.") + + if PnW is not None: + if type(PnW) is FractionalComponent: + weeks = PnW.principal + microseconds = PnW.microsecondremainder + else: + weeks = PnW + + if weeks * DAYS_PER_WEEK > TIMEDELTA_MAX_DAYS: + raise WeekOutOfBoundsError("Duration exceeds maximum timedelta size.") + + if PnD is not None: + if type(PnD) is FractionalComponent: + days = PnD.principal + microseconds = PnD.microsecondremainder + else: + days = PnD + + if days > TIMEDELTA_MAX_DAYS: + raise DayOutOfBoundsError("Duration exceeds maximum timedelta size.") + + if TnH is not None: + if type(TnH) is FractionalComponent: + hours = TnH.principal + microseconds = TnH.microsecondremainder + else: + hours = TnH + + if hours // HOURS_PER_DAY > TIMEDELTA_MAX_DAYS: + raise HoursOutOfBoundsError("Duration exceeds maximum timedelta size.") + + if TnM is not None: + if type(TnM) is FractionalComponent: + minutes = TnM.principal + microseconds = TnM.microsecondremainder + else: + minutes = TnM + + if minutes // MINUTES_PER_DAY > TIMEDELTA_MAX_DAYS: + raise MinutesOutOfBoundsError( + "Duration exceeds maximum timedelta size." + ) + + if TnS is not None: + if type(TnS) is FractionalComponent: + seconds = TnS.principal + microseconds = TnS.microsecondremainder + else: + seconds = TnS + + if seconds // SECONDS_PER_DAY > TIMEDELTA_MAX_DAYS: + raise SecondsOutOfBoundsError( + "Duration exceeds maximum timedelta size." + ) + + ( + years, + months, + weeks, + days, + hours, + minutes, + seconds, + microseconds, + ) = PythonTimeBuilder._distribute_microseconds( + microseconds, + (years, months, weeks, days, hours, minutes, seconds), + ( + MICROSECONDS_PER_YEAR, + MICROSECONDS_PER_MONTH, + MICROSECONDS_PER_WEEK, + MICROSECONDS_PER_DAY, + MICROSECONDS_PER_HOUR, + MICROSECONDS_PER_MINUTE, + MICROSECONDS_PER_SECOND, + ), + ) + + # Note that weeks can be handled without conversion to days + totaldays = years * DAYS_PER_YEAR + months * DAYS_PER_MONTH + days + + # Check against timedelta limits + if ( + totaldays + + weeks * DAYS_PER_WEEK + + hours // HOURS_PER_DAY + + minutes // MINUTES_PER_DAY + + seconds // SECONDS_PER_DAY + > TIMEDELTA_MAX_DAYS + ): + raise DayOutOfBoundsError("Duration exceeds maximum timedelta size.") + + return ( + None, + None, + weeks, + totaldays, + hours, + minutes, + FractionalComponent(seconds, microseconds), + ) + + @classmethod + def range_check_interval(cls, start=None, end=None, duration=None): + # Handles concise format, range checks any potential durations + if start is not None and end is not None: + # / + # Handle concise format + if cls._is_interval_end_concise(end) is True: + end = cls._combine_concise_interval_tuples(start, end) + + return (start, end, duration) + + durationobject = cls._build_object(duration) + + if end is not None: + # / + endobject = cls._build_object(end) + + # Range check + if type(end) is DateTuple: + enddatetime = cls.build_datetime(end, TupleBuilder.build_time()) + + if enddatetime - datetime.datetime.min < durationobject: + raise YearOutOfBoundsError("Interval end less than minimium date.") + else: + mindatetime = datetime.datetime.min + + if end.time.tz is not None: + mindatetime = mindatetime.replace(tzinfo=endobject.tzinfo) + + if endobject - mindatetime < durationobject: + raise YearOutOfBoundsError("Interval end less than minimium date.") + else: + # / + startobject = cls._build_object(start) + + # Range check + if type(start) is DateTuple: + startdatetime = cls.build_datetime(start, TupleBuilder.build_time()) + + if datetime.datetime.max - startdatetime < durationobject: + raise YearOutOfBoundsError( + "Interval end greater than maximum date." + ) + else: + maxdatetime = datetime.datetime.max + + if start.time.tz is not None: + maxdatetime = maxdatetime.replace(tzinfo=startobject.tzinfo) + + if maxdatetime - startobject < durationobject: + raise YearOutOfBoundsError( + "Interval end greater than maximum date." + ) + + return (start, end, duration) + + @staticmethod + def _build_week_date(isoyear, isoweek, isoday=None): + if isoday is None: + return PythonTimeBuilder._iso_year_start(isoyear) + datetime.timedelta( + weeks=isoweek - 1 + ) + + return PythonTimeBuilder._iso_year_start(isoyear) + datetime.timedelta( + weeks=isoweek - 1, days=isoday - 1 + ) + + @staticmethod + def _build_ordinal_date(isoyear, isoday): + # Day of year to a date + # https://stackoverflow.com/questions/2427555/python-question-year-and-day-of-year-to-date + builtdate = datetime.date(isoyear, 1, 1) + datetime.timedelta(days=isoday - 1) + + return builtdate + + @staticmethod + def _iso_year_start(isoyear): + # Given an ISO year, returns the equivalent of the start of the year + # on the Gregorian calendar (which is used by Python) + # Stolen from: + # http://stackoverflow.com/questions/304256/whats-the-best-way-to-find-the-inverse-of-datetime-isocalendar + + # Determine the location of the 4th of January, the first week of + # the ISO year is the week containing the 4th of January + # http://en.wikipedia.org/wiki/ISO_week_date + fourth_jan = datetime.date(isoyear, 1, 4) + + # Note the conversion from ISO day (1 - 7) and Python day (0 - 6) + delta = datetime.timedelta(days=fourth_jan.isoweekday() - 1) + + # Return the start of the year + return fourth_jan - delta + + @staticmethod + def _date_generator(startdate, timedelta, iterations): + currentdate = startdate + currentiteration = 0 + + while currentiteration < iterations: + yield currentdate + + # Update the values + currentdate += timedelta + currentiteration += 1 + + @staticmethod + def _date_generator_unbounded(startdate, timedelta): + currentdate = startdate + + while True: + yield currentdate + + # Update the value + currentdate += timedelta + + @staticmethod + def _distribute_microseconds(todistribute, recipients, reductions): + # Given a number of microseconds as int, a tuple of ints length n + # to distribute to, and a tuple of ints length n to divide todistribute + # by (from largest to smallest), returns a tuple of length n + 1, with + # todistribute divided across recipients using the reductions, with + # the final remainder returned as the final tuple member + results = [] + + remainder = todistribute + + for index, reduction in enumerate(reductions): + additional, remainder = divmod(remainder, reduction) + + results.append(recipients[index] + additional) + + # Always return the remaining microseconds + results.append(remainder) + + return tuple(results) diff --git a/radar/Lib/site-packages/aniso8601/builders/tests/__init__.py b/radar/Lib/site-packages/aniso8601/builders/tests/__init__.py new file mode 100644 index 0000000..1a94e01 --- /dev/null +++ b/radar/Lib/site-packages/aniso8601/builders/tests/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Brandon Nielsen +# All rights reserved. +# +# This software may be modified and distributed under the terms +# of the BSD license. See the LICENSE file for details. diff --git a/radar/Lib/site-packages/aniso8601/builders/tests/test_init.py b/radar/Lib/site-packages/aniso8601/builders/tests/test_init.py new file mode 100644 index 0000000..7c9f092 --- /dev/null +++ b/radar/Lib/site-packages/aniso8601/builders/tests/test_init.py @@ -0,0 +1,838 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Brandon Nielsen +# All rights reserved. +# +# This software may be modified and distributed under the terms +# of the BSD license. See the LICENSE file for details. + +import unittest + +import aniso8601 +from aniso8601.builders import ( + BaseTimeBuilder, + DatetimeTuple, + DateTuple, + DurationTuple, + IntervalTuple, + RepeatingIntervalTuple, + TimeTuple, + TimezoneTuple, + TupleBuilder, + cast, +) +from aniso8601.exceptions import ( + DayOutOfBoundsError, + HoursOutOfBoundsError, + ISOFormatError, + LeapSecondError, + MidnightBoundsError, + MinutesOutOfBoundsError, + MonthOutOfBoundsError, + SecondsOutOfBoundsError, + WeekOutOfBoundsError, +) +from aniso8601.tests.compat import mock + + +class LeapSecondSupportingTestBuilder(BaseTimeBuilder): + LEAP_SECONDS_SUPPORTED = True + + +class TestBuilderFunctions(unittest.TestCase): + def test_cast(self): + self.assertEqual(cast("1", int), 1) + self.assertEqual(cast("-2", int), -2) + self.assertEqual(cast("3", float), float(3)) + self.assertEqual(cast("-4", float), float(-4)) + self.assertEqual(cast("5.6", float), 5.6) + self.assertEqual(cast("-7.8", float), -7.8) + + def test_cast_exception(self): + with self.assertRaises(ISOFormatError): + cast("asdf", int) + + with self.assertRaises(ISOFormatError): + cast("asdf", float) + + def test_cast_caughtexception(self): + def tester(value): + raise RuntimeError + + with self.assertRaises(ISOFormatError): + cast("asdf", tester, caughtexceptions=(RuntimeError,)) + + def test_cast_thrownexception(self): + with self.assertRaises(RuntimeError): + cast("asdf", int, thrownexception=RuntimeError) + + +class TestBaseTimeBuilder(unittest.TestCase): + def test_build_date(self): + with self.assertRaises(NotImplementedError): + BaseTimeBuilder.build_date() + + def test_build_time(self): + with self.assertRaises(NotImplementedError): + BaseTimeBuilder.build_time() + + def test_build_datetime(self): + with self.assertRaises(NotImplementedError): + BaseTimeBuilder.build_datetime(None, None) + + def test_build_duration(self): + with self.assertRaises(NotImplementedError): + BaseTimeBuilder.build_duration() + + def test_build_interval(self): + with self.assertRaises(NotImplementedError): + BaseTimeBuilder.build_interval() + + def test_build_repeating_interval(self): + with self.assertRaises(NotImplementedError): + BaseTimeBuilder.build_repeating_interval() + + def test_build_timezone(self): + with self.assertRaises(NotImplementedError): + BaseTimeBuilder.build_timezone() + + def test_range_check_date(self): + # Check the calendar for day ranges + with self.assertRaises(DayOutOfBoundsError): + BaseTimeBuilder.range_check_date(YYYY="0007", MM="02", DD="30") + + with self.assertRaises(DayOutOfBoundsError): + BaseTimeBuilder.range_check_date(YYYY="0007", DDD="366") + + with self.assertRaises(MonthOutOfBoundsError): + BaseTimeBuilder.range_check_date(YYYY="4333", MM="30", DD="30") + + # 0 isn't a valid week number + with self.assertRaises(WeekOutOfBoundsError): + BaseTimeBuilder.range_check_date(YYYY="2003", Www="00") + + # Week must not be larger than 53 + with self.assertRaises(WeekOutOfBoundsError): + BaseTimeBuilder.range_check_date(YYYY="2004", Www="54") + + # 0 isn't a valid day number + with self.assertRaises(DayOutOfBoundsError): + BaseTimeBuilder.range_check_date(YYYY="2001", Www="02", D="0") + + # Day must not be larger than 7 + with self.assertRaises(DayOutOfBoundsError): + BaseTimeBuilder.range_check_date(YYYY="2001", Www="02", D="8") + + with self.assertRaises(DayOutOfBoundsError): + BaseTimeBuilder.range_check_date(YYYY="1981", DDD="000") + + # Day must be 365, or 366, not larger + with self.assertRaises(DayOutOfBoundsError): + BaseTimeBuilder.range_check_date(YYYY="1234", DDD="000") + + with self.assertRaises(DayOutOfBoundsError): + BaseTimeBuilder.range_check_date(YYYY="1234", DDD="367") + + # https://bitbucket.org/nielsenb/aniso8601/issues/14/parsing-ordinal-dates-should-only-allow + with self.assertRaises(DayOutOfBoundsError): + BaseTimeBuilder.range_check_date(YYYY="1981", DDD="366") + + # Make sure Nones pass through unmodified + self.assertEqual( + BaseTimeBuilder.range_check_date(rangedict={}), + (None, None, None, None, None, None), + ) + + def test_range_check_time(self): + # Leap seconds not supported + # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is + # https://bitbucket.org/nielsenb/aniso8601/issues/13/parsing-of-leap-second-gives-wildly + with self.assertRaises(LeapSecondError): + BaseTimeBuilder.range_check_time(hh="23", mm="59", ss="60") + + with self.assertRaises(SecondsOutOfBoundsError): + BaseTimeBuilder.range_check_time(hh="00", mm="00", ss="60") + + with self.assertRaises(SecondsOutOfBoundsError): + BaseTimeBuilder.range_check_time(hh="00", mm="00", ss="61") + + with self.assertRaises(MinutesOutOfBoundsError): + BaseTimeBuilder.range_check_time(hh="00", mm="61") + + with self.assertRaises(MinutesOutOfBoundsError): + BaseTimeBuilder.range_check_time(hh="00", mm="60") + + with self.assertRaises(MinutesOutOfBoundsError): + BaseTimeBuilder.range_check_time(hh="00", mm="60.1") + + with self.assertRaises(HoursOutOfBoundsError): + BaseTimeBuilder.range_check_time(hh="25") + + # Hour 24 can only represent midnight + with self.assertRaises(MidnightBoundsError): + BaseTimeBuilder.range_check_time(hh="24", mm="00", ss="01") + + with self.assertRaises(MidnightBoundsError): + BaseTimeBuilder.range_check_time(hh="24", mm="00.1") + + with self.assertRaises(MidnightBoundsError): + BaseTimeBuilder.range_check_time(hh="24", mm="01") + + with self.assertRaises(MidnightBoundsError): + BaseTimeBuilder.range_check_time(hh="24.1") + + # Leap seconds not supported + # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is + # https://bitbucket.org/nielsenb/aniso8601/issues/13/parsing-of-leap-second-gives-wildly + with self.assertRaises(LeapSecondError): + BaseTimeBuilder.range_check_time(hh="23", mm="59", ss="60") + + # Make sure Nones pass through unmodified + self.assertEqual( + BaseTimeBuilder.range_check_time(rangedict={}), (None, None, None, None) + ) + + def test_range_check_time_leap_seconds_supported(self): + self.assertEqual( + LeapSecondSupportingTestBuilder.range_check_time(hh="23", mm="59", ss="60"), + (23, 59, 60, None), + ) + + with self.assertRaises(SecondsOutOfBoundsError): + LeapSecondSupportingTestBuilder.range_check_time(hh="01", mm="02", ss="60") + + def test_range_check_duration(self): + self.assertEqual( + BaseTimeBuilder.range_check_duration(), + (None, None, None, None, None, None, None), + ) + + self.assertEqual( + BaseTimeBuilder.range_check_duration(rangedict={}), + (None, None, None, None, None, None, None), + ) + + def test_range_check_repeating_interval(self): + self.assertEqual( + BaseTimeBuilder.range_check_repeating_interval(), (None, None, None) + ) + + self.assertEqual( + BaseTimeBuilder.range_check_repeating_interval(rangedict={}), + (None, None, None), + ) + + def test_range_check_timezone(self): + self.assertEqual( + BaseTimeBuilder.range_check_timezone(), (None, None, None, None, "") + ) + + self.assertEqual( + BaseTimeBuilder.range_check_timezone(rangedict={}), + (None, None, None, None, ""), + ) + + def test_build_object(self): + datetest = ( + DateTuple("1", "2", "3", "4", "5", "6"), + {"YYYY": "1", "MM": "2", "DD": "3", "Www": "4", "D": "5", "DDD": "6"}, + ) + + timetest = ( + TimeTuple("1", "2", "3", TimezoneTuple(False, False, "4", "5", "tz name")), + { + "hh": "1", + "mm": "2", + "ss": "3", + "tz": TimezoneTuple(False, False, "4", "5", "tz name"), + }, + ) + + datetimetest = ( + DatetimeTuple( + DateTuple("1", "2", "3", "4", "5", "6"), + TimeTuple( + "7", "8", "9", TimezoneTuple(True, False, "10", "11", "tz name") + ), + ), + ( + DateTuple("1", "2", "3", "4", "5", "6"), + TimeTuple( + "7", "8", "9", TimezoneTuple(True, False, "10", "11", "tz name") + ), + ), + ) + + durationtest = ( + DurationTuple("1", "2", "3", "4", "5", "6", "7"), + { + "PnY": "1", + "PnM": "2", + "PnW": "3", + "PnD": "4", + "TnH": "5", + "TnM": "6", + "TnS": "7", + }, + ) + + intervaltests = ( + ( + IntervalTuple( + DateTuple("1", "2", "3", "4", "5", "6"), + DateTuple("7", "8", "9", "10", "11", "12"), + None, + ), + { + "start": DateTuple("1", "2", "3", "4", "5", "6"), + "end": DateTuple("7", "8", "9", "10", "11", "12"), + "duration": None, + }, + ), + ( + IntervalTuple( + DateTuple("1", "2", "3", "4", "5", "6"), + None, + DurationTuple("7", "8", "9", "10", "11", "12", "13"), + ), + { + "start": DateTuple("1", "2", "3", "4", "5", "6"), + "end": None, + "duration": DurationTuple("7", "8", "9", "10", "11", "12", "13"), + }, + ), + ( + IntervalTuple( + None, + TimeTuple( + "1", "2", "3", TimezoneTuple(True, False, "4", "5", "tz name") + ), + DurationTuple("6", "7", "8", "9", "10", "11", "12"), + ), + { + "start": None, + "end": TimeTuple( + "1", "2", "3", TimezoneTuple(True, False, "4", "5", "tz name") + ), + "duration": DurationTuple("6", "7", "8", "9", "10", "11", "12"), + }, + ), + ) + + repeatingintervaltests = ( + ( + RepeatingIntervalTuple( + True, + None, + IntervalTuple( + DateTuple("1", "2", "3", "4", "5", "6"), + DateTuple("7", "8", "9", "10", "11", "12"), + None, + ), + ), + { + "R": True, + "Rnn": None, + "interval": IntervalTuple( + DateTuple("1", "2", "3", "4", "5", "6"), + DateTuple("7", "8", "9", "10", "11", "12"), + None, + ), + }, + ), + ( + RepeatingIntervalTuple( + False, + "1", + IntervalTuple( + DatetimeTuple( + DateTuple("2", "3", "4", "5", "6", "7"), + TimeTuple("8", "9", "10", None), + ), + DatetimeTuple( + DateTuple("11", "12", "13", "14", "15", "16"), + TimeTuple("17", "18", "19", None), + ), + None, + ), + ), + { + "R": False, + "Rnn": "1", + "interval": IntervalTuple( + DatetimeTuple( + DateTuple("2", "3", "4", "5", "6", "7"), + TimeTuple("8", "9", "10", None), + ), + DatetimeTuple( + DateTuple("11", "12", "13", "14", "15", "16"), + TimeTuple("17", "18", "19", None), + ), + None, + ), + }, + ), + ) + + timezonetest = ( + TimezoneTuple(False, False, "1", "2", "+01:02"), + {"negative": False, "Z": False, "hh": "1", "mm": "2", "name": "+01:02"}, + ) + + with mock.patch.object( + aniso8601.builders.BaseTimeBuilder, "build_date" + ) as mock_build: + mock_build.return_value = datetest[0] + + result = BaseTimeBuilder._build_object(datetest[0]) + + self.assertEqual(result, datetest[0]) + mock_build.assert_called_once_with(**datetest[1]) + + with mock.patch.object( + aniso8601.builders.BaseTimeBuilder, "build_time" + ) as mock_build: + mock_build.return_value = timetest[0] + + result = BaseTimeBuilder._build_object(timetest[0]) + + self.assertEqual(result, timetest[0]) + mock_build.assert_called_once_with(**timetest[1]) + + with mock.patch.object( + aniso8601.builders.BaseTimeBuilder, "build_datetime" + ) as mock_build: + mock_build.return_value = datetimetest[0] + + result = BaseTimeBuilder._build_object(datetimetest[0]) + + self.assertEqual(result, datetimetest[0]) + mock_build.assert_called_once_with(*datetimetest[1]) + + with mock.patch.object( + aniso8601.builders.BaseTimeBuilder, "build_duration" + ) as mock_build: + mock_build.return_value = durationtest[0] + + result = BaseTimeBuilder._build_object(durationtest[0]) + + self.assertEqual(result, durationtest[0]) + mock_build.assert_called_once_with(**durationtest[1]) + + for intervaltest in intervaltests: + with mock.patch.object( + aniso8601.builders.BaseTimeBuilder, "build_interval" + ) as mock_build: + mock_build.return_value = intervaltest[0] + + result = BaseTimeBuilder._build_object(intervaltest[0]) + + self.assertEqual(result, intervaltest[0]) + mock_build.assert_called_once_with(**intervaltest[1]) + + for repeatingintervaltest in repeatingintervaltests: + with mock.patch.object( + aniso8601.builders.BaseTimeBuilder, "build_repeating_interval" + ) as mock_build: + mock_build.return_value = repeatingintervaltest[0] + + result = BaseTimeBuilder._build_object(repeatingintervaltest[0]) + + self.assertEqual(result, repeatingintervaltest[0]) + mock_build.assert_called_once_with(**repeatingintervaltest[1]) + + with mock.patch.object( + aniso8601.builders.BaseTimeBuilder, "build_timezone" + ) as mock_build: + mock_build.return_value = timezonetest[0] + + result = BaseTimeBuilder._build_object(timezonetest[0]) + + self.assertEqual(result, timezonetest[0]) + mock_build.assert_called_once_with(**timezonetest[1]) + + def test_is_interval_end_concise(self): + self.assertTrue( + BaseTimeBuilder._is_interval_end_concise(TimeTuple("1", "2", "3", None)) + ) + self.assertTrue( + BaseTimeBuilder._is_interval_end_concise( + DateTuple(None, "2", "3", "4", "5", "6") + ) + ) + self.assertTrue( + BaseTimeBuilder._is_interval_end_concise( + DatetimeTuple( + DateTuple(None, "2", "3", "4", "5", "6"), + TimeTuple("7", "8", "9", None), + ) + ) + ) + + self.assertFalse( + BaseTimeBuilder._is_interval_end_concise( + DateTuple("1", "2", "3", "4", "5", "6") + ) + ) + self.assertFalse( + BaseTimeBuilder._is_interval_end_concise( + DatetimeTuple( + DateTuple("1", "2", "3", "4", "5", "6"), + TimeTuple("7", "8", "9", None), + ) + ) + ) + + def test_combine_concise_interval_tuples(self): + testtuples = ( + ( + DateTuple("2020", "01", "01", None, None, None), + DateTuple(None, None, "02", None, None, None), + DateTuple("2020", "01", "02", None, None, None), + ), + ( + DateTuple("2008", "02", "15", None, None, None), + DateTuple(None, "03", "14", None, None, None), + DateTuple("2008", "03", "14", None, None, None), + ), + ( + DatetimeTuple( + DateTuple("2007", "12", "14", None, None, None), + TimeTuple("13", "30", None, None), + ), + TimeTuple("15", "30", None, None), + DatetimeTuple( + DateTuple("2007", "12", "14", None, None, None), + TimeTuple("15", "30", None, None), + ), + ), + ( + DatetimeTuple( + DateTuple("2007", "11", "13", None, None, None), + TimeTuple("09", "00", None, None), + ), + DatetimeTuple( + DateTuple(None, None, "15", None, None, None), + TimeTuple("17", "00", None, None), + ), + DatetimeTuple( + DateTuple("2007", "11", "15", None, None, None), + TimeTuple("17", "00", None, None), + ), + ), + ( + DatetimeTuple( + DateTuple("2007", "11", "13", None, None, None), + TimeTuple("00", "00", None, None), + ), + DatetimeTuple( + DateTuple(None, None, "16", None, None, None), + TimeTuple("00", "00", None, None), + ), + DatetimeTuple( + DateTuple("2007", "11", "16", None, None, None), + TimeTuple("00", "00", None, None), + ), + ), + ( + DatetimeTuple( + DateTuple("2007", "11", "13", None, None, None), + TimeTuple( + "09", "00", None, TimezoneTuple(False, True, None, None, "Z") + ), + ), + DatetimeTuple( + DateTuple(None, None, "15", None, None, None), + TimeTuple("17", "00", None, None), + ), + DatetimeTuple( + DateTuple("2007", "11", "15", None, None, None), + TimeTuple( + "17", "00", None, TimezoneTuple(False, True, None, None, "Z") + ), + ), + ), + ) + + for testtuple in testtuples: + result = BaseTimeBuilder._combine_concise_interval_tuples( + testtuple[0], testtuple[1] + ) + self.assertEqual(result, testtuple[2]) + + +class TestTupleBuilder(unittest.TestCase): + def test_build_date(self): + datetuple = TupleBuilder.build_date() + + self.assertEqual(datetuple, DateTuple(None, None, None, None, None, None)) + + datetuple = TupleBuilder.build_date( + YYYY="1", MM="2", DD="3", Www="4", D="5", DDD="6" + ) + + self.assertEqual(datetuple, DateTuple("1", "2", "3", "4", "5", "6")) + + def test_build_time(self): + testtuples = ( + ({}, TimeTuple(None, None, None, None)), + ( + {"hh": "1", "mm": "2", "ss": "3", "tz": None}, + TimeTuple("1", "2", "3", None), + ), + ( + { + "hh": "1", + "mm": "2", + "ss": "3", + "tz": TimezoneTuple(False, False, "4", "5", "tz name"), + }, + TimeTuple( + "1", "2", "3", TimezoneTuple(False, False, "4", "5", "tz name") + ), + ), + ) + + for testtuple in testtuples: + self.assertEqual(TupleBuilder.build_time(**testtuple[0]), testtuple[1]) + + def test_build_datetime(self): + testtuples = ( + ( + { + "date": DateTuple("1", "2", "3", "4", "5", "6"), + "time": TimeTuple("7", "8", "9", None), + }, + DatetimeTuple( + DateTuple("1", "2", "3", "4", "5", "6"), + TimeTuple("7", "8", "9", None), + ), + ), + ( + { + "date": DateTuple("1", "2", "3", "4", "5", "6"), + "time": TimeTuple( + "7", "8", "9", TimezoneTuple(True, False, "10", "11", "tz name") + ), + }, + DatetimeTuple( + DateTuple("1", "2", "3", "4", "5", "6"), + TimeTuple( + "7", "8", "9", TimezoneTuple(True, False, "10", "11", "tz name") + ), + ), + ), + ) + + for testtuple in testtuples: + self.assertEqual(TupleBuilder.build_datetime(**testtuple[0]), testtuple[1]) + + def test_build_duration(self): + testtuples = ( + ({}, DurationTuple(None, None, None, None, None, None, None)), + ( + { + "PnY": "1", + "PnM": "2", + "PnW": "3", + "PnD": "4", + "TnH": "5", + "TnM": "6", + "TnS": "7", + }, + DurationTuple("1", "2", "3", "4", "5", "6", "7"), + ), + ) + + for testtuple in testtuples: + self.assertEqual(TupleBuilder.build_duration(**testtuple[0]), testtuple[1]) + + def test_build_interval(self): + testtuples = ( + ({}, IntervalTuple(None, None, None)), + ( + { + "start": DateTuple("1", "2", "3", "4", "5", "6"), + "end": DateTuple("7", "8", "9", "10", "11", "12"), + }, + IntervalTuple( + DateTuple("1", "2", "3", "4", "5", "6"), + DateTuple("7", "8", "9", "10", "11", "12"), + None, + ), + ), + ( + { + "start": TimeTuple( + "1", "2", "3", TimezoneTuple(True, False, "7", "8", "tz name") + ), + "end": TimeTuple( + "4", "5", "6", TimezoneTuple(False, False, "9", "10", "tz name") + ), + }, + IntervalTuple( + TimeTuple( + "1", "2", "3", TimezoneTuple(True, False, "7", "8", "tz name") + ), + TimeTuple( + "4", "5", "6", TimezoneTuple(False, False, "9", "10", "tz name") + ), + None, + ), + ), + ( + { + "start": DatetimeTuple( + DateTuple("1", "2", "3", "4", "5", "6"), + TimeTuple( + "7", + "8", + "9", + TimezoneTuple(True, False, "10", "11", "tz name"), + ), + ), + "end": DatetimeTuple( + DateTuple("12", "13", "14", "15", "16", "17"), + TimeTuple( + "18", + "19", + "20", + TimezoneTuple(False, False, "21", "22", "tz name"), + ), + ), + }, + IntervalTuple( + DatetimeTuple( + DateTuple("1", "2", "3", "4", "5", "6"), + TimeTuple( + "7", + "8", + "9", + TimezoneTuple(True, False, "10", "11", "tz name"), + ), + ), + DatetimeTuple( + DateTuple("12", "13", "14", "15", "16", "17"), + TimeTuple( + "18", + "19", + "20", + TimezoneTuple(False, False, "21", "22", "tz name"), + ), + ), + None, + ), + ), + ( + { + "start": DateTuple("1", "2", "3", "4", "5", "6"), + "end": None, + "duration": DurationTuple("7", "8", "9", "10", "11", "12", "13"), + }, + IntervalTuple( + DateTuple("1", "2", "3", "4", "5", "6"), + None, + DurationTuple("7", "8", "9", "10", "11", "12", "13"), + ), + ), + ( + { + "start": None, + "end": TimeTuple( + "1", "2", "3", TimezoneTuple(True, False, "4", "5", "tz name") + ), + "duration": DurationTuple("6", "7", "8", "9", "10", "11", "12"), + }, + IntervalTuple( + None, + TimeTuple( + "1", "2", "3", TimezoneTuple(True, False, "4", "5", "tz name") + ), + DurationTuple("6", "7", "8", "9", "10", "11", "12"), + ), + ), + ) + + for testtuple in testtuples: + self.assertEqual(TupleBuilder.build_interval(**testtuple[0]), testtuple[1]) + + def test_build_repeating_interval(self): + testtuples = ( + ({}, RepeatingIntervalTuple(None, None, None)), + ( + { + "R": True, + "interval": IntervalTuple( + DateTuple("1", "2", "3", "4", "5", "6"), + DateTuple("7", "8", "9", "10", "11", "12"), + None, + ), + }, + RepeatingIntervalTuple( + True, + None, + IntervalTuple( + DateTuple("1", "2", "3", "4", "5", "6"), + DateTuple("7", "8", "9", "10", "11", "12"), + None, + ), + ), + ), + ( + { + "R": False, + "Rnn": "1", + "interval": IntervalTuple( + DatetimeTuple( + DateTuple("2", "3", "4", "5", "6", "7"), + TimeTuple("8", "9", "10", None), + ), + DatetimeTuple( + DateTuple("11", "12", "13", "14", "15", "16"), + TimeTuple("17", "18", "19", None), + ), + None, + ), + }, + RepeatingIntervalTuple( + False, + "1", + IntervalTuple( + DatetimeTuple( + DateTuple("2", "3", "4", "5", "6", "7"), + TimeTuple("8", "9", "10", None), + ), + DatetimeTuple( + DateTuple("11", "12", "13", "14", "15", "16"), + TimeTuple("17", "18", "19", None), + ), + None, + ), + ), + ), + ) + + for testtuple in testtuples: + result = TupleBuilder.build_repeating_interval(**testtuple[0]) + self.assertEqual(result, testtuple[1]) + + def test_build_timezone(self): + testtuples = ( + ({}, TimezoneTuple(None, None, None, None, "")), + ( + {"negative": False, "Z": True, "name": "UTC"}, + TimezoneTuple(False, True, None, None, "UTC"), + ), + ( + {"negative": False, "Z": False, "hh": "1", "mm": "2", "name": "+01:02"}, + TimezoneTuple(False, False, "1", "2", "+01:02"), + ), + ( + {"negative": True, "Z": False, "hh": "1", "mm": "2", "name": "-01:02"}, + TimezoneTuple(True, False, "1", "2", "-01:02"), + ), + ) + + for testtuple in testtuples: + result = TupleBuilder.build_timezone(**testtuple[0]) + self.assertEqual(result, testtuple[1]) diff --git a/radar/Lib/site-packages/aniso8601/builders/tests/test_python.py b/radar/Lib/site-packages/aniso8601/builders/tests/test_python.py new file mode 100644 index 0000000..11111a1 --- /dev/null +++ b/radar/Lib/site-packages/aniso8601/builders/tests/test_python.py @@ -0,0 +1,1710 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Brandon Nielsen +# All rights reserved. +# +# This software may be modified and distributed under the terms +# of the BSD license. See the LICENSE file for details. + +import datetime +import unittest + +from aniso8601 import compat +from aniso8601.builders import ( + DatetimeTuple, + DateTuple, + DurationTuple, + IntervalTuple, + Limit, + TimeTuple, + TimezoneTuple, +) +from aniso8601.builders.python import ( + FractionalComponent, + PythonTimeBuilder, + _cast_to_fractional_component, + fractional_range_check, + year_range_check, +) +from aniso8601.exceptions import ( + DayOutOfBoundsError, + HoursOutOfBoundsError, + ISOFormatError, + LeapSecondError, + MidnightBoundsError, + MinutesOutOfBoundsError, + MonthOutOfBoundsError, + SecondsOutOfBoundsError, + WeekOutOfBoundsError, + YearOutOfBoundsError, +) +from aniso8601.utcoffset import UTCOffset + + +class TestPythonTimeBuilder_UtiltyFunctions(unittest.TestCase): + def test_year_range_check(self): + yearlimit = Limit( + "Invalid year string.", + 0000, + 9999, + YearOutOfBoundsError, + "Year must be between 1..9999.", + None, + ) + + self.assertEqual(year_range_check("1", yearlimit), 1000) + + def test_fractional_range_check(self): + limit = Limit( + "Invalid string.", -1, 1, ValueError, "Value must be between -1..1.", None + ) + + self.assertEqual(fractional_range_check(10, "1", limit), 1) + self.assertEqual(fractional_range_check(10, "-1", limit), -1) + self.assertEqual( + fractional_range_check(10, "0.1", limit), FractionalComponent(0, 1) + ) + self.assertEqual( + fractional_range_check(10, "-0.1", limit), FractionalComponent(-0, 1) + ) + + with self.assertRaises(ValueError): + fractional_range_check(10, "1.1", limit) + + with self.assertRaises(ValueError): + fractional_range_check(10, "-1.1", limit) + + def test_cast_to_fractional_component(self): + self.assertEqual( + _cast_to_fractional_component(10, "1.1"), FractionalComponent(1, 1) + ) + self.assertEqual( + _cast_to_fractional_component(10, "-1.1"), FractionalComponent(-1, 1) + ) + + self.assertEqual( + _cast_to_fractional_component(100, "1.1"), FractionalComponent(1, 10) + ) + self.assertEqual( + _cast_to_fractional_component(100, "-1.1"), FractionalComponent(-1, 10) + ) + + +class TestPythonTimeBuilder(unittest.TestCase): + def test_build_date(self): + testtuples = ( + ( + { + "YYYY": "2013", + "MM": None, + "DD": None, + "Www": None, + "D": None, + "DDD": None, + }, + datetime.date(2013, 1, 1), + ), + ( + { + "YYYY": "0001", + "MM": None, + "DD": None, + "Www": None, + "D": None, + "DDD": None, + }, + datetime.date(1, 1, 1), + ), + ( + { + "YYYY": "1900", + "MM": None, + "DD": None, + "Www": None, + "D": None, + "DDD": None, + }, + datetime.date(1900, 1, 1), + ), + ( + { + "YYYY": "1981", + "MM": "04", + "DD": "05", + "Www": None, + "D": None, + "DDD": None, + }, + datetime.date(1981, 4, 5), + ), + ( + { + "YYYY": "1981", + "MM": "04", + "DD": None, + "Www": None, + "D": None, + "DDD": None, + }, + datetime.date(1981, 4, 1), + ), + ( + { + "YYYY": "1981", + "MM": None, + "DD": None, + "Www": None, + "D": None, + "DDD": "095", + }, + datetime.date(1981, 4, 5), + ), + ( + { + "YYYY": "1981", + "MM": None, + "DD": None, + "Www": None, + "D": None, + "DDD": "365", + }, + datetime.date(1981, 12, 31), + ), + ( + { + "YYYY": "1980", + "MM": None, + "DD": None, + "Www": None, + "D": None, + "DDD": "366", + }, + datetime.date(1980, 12, 31), + ), + # Make sure we shift in zeros + ( + { + "YYYY": "1", + "MM": None, + "DD": None, + "Www": None, + "D": None, + "DDD": None, + }, + datetime.date(1000, 1, 1), + ), + ( + { + "YYYY": "12", + "MM": None, + "DD": None, + "Www": None, + "D": None, + "DDD": None, + }, + datetime.date(1200, 1, 1), + ), + ( + { + "YYYY": "123", + "MM": None, + "DD": None, + "Www": None, + "D": None, + "DDD": None, + }, + datetime.date(1230, 1, 1), + ), + ) + + for testtuple in testtuples: + result = PythonTimeBuilder.build_date(**testtuple[0]) + self.assertEqual(result, testtuple[1]) + + # Test weekday + testtuples = ( + ( + { + "YYYY": "2004", + "MM": None, + "DD": None, + "Www": "53", + "D": None, + "DDD": None, + }, + datetime.date(2004, 12, 27), + 0, + ), + ( + { + "YYYY": "2009", + "MM": None, + "DD": None, + "Www": "01", + "D": None, + "DDD": None, + }, + datetime.date(2008, 12, 29), + 0, + ), + ( + { + "YYYY": "2010", + "MM": None, + "DD": None, + "Www": "01", + "D": None, + "DDD": None, + }, + datetime.date(2010, 1, 4), + 0, + ), + ( + { + "YYYY": "2009", + "MM": None, + "DD": None, + "Www": "53", + "D": None, + "DDD": None, + }, + datetime.date(2009, 12, 28), + 0, + ), + ( + { + "YYYY": "2009", + "MM": None, + "DD": None, + "Www": "01", + "D": "1", + "DDD": None, + }, + datetime.date(2008, 12, 29), + 0, + ), + ( + { + "YYYY": "2009", + "MM": None, + "DD": None, + "Www": "53", + "D": "7", + "DDD": None, + }, + datetime.date(2010, 1, 3), + 6, + ), + ( + { + "YYYY": "2010", + "MM": None, + "DD": None, + "Www": "01", + "D": "1", + "DDD": None, + }, + datetime.date(2010, 1, 4), + 0, + ), + ( + { + "YYYY": "2004", + "MM": None, + "DD": None, + "Www": "53", + "D": "6", + "DDD": None, + }, + datetime.date(2005, 1, 1), + 5, + ), + ) + + for testtuple in testtuples: + result = PythonTimeBuilder.build_date(**testtuple[0]) + self.assertEqual(result, testtuple[1]) + self.assertEqual(result.weekday(), testtuple[2]) + + def test_build_time(self): + testtuples = ( + ({}, datetime.time()), + ({"hh": "12.5"}, datetime.time(hour=12, minute=30)), + ( + {"hh": "23.99999999997"}, + datetime.time(hour=23, minute=59, second=59, microsecond=999999), + ), + ({"hh": "1", "mm": "23"}, datetime.time(hour=1, minute=23)), + ( + {"hh": "1", "mm": "23.4567"}, + datetime.time(hour=1, minute=23, second=27, microsecond=402000), + ), + ( + {"hh": "14", "mm": "43.999999997"}, + datetime.time(hour=14, minute=43, second=59, microsecond=999999), + ), + ( + {"hh": "1", "mm": "23", "ss": "45"}, + datetime.time(hour=1, minute=23, second=45), + ), + ( + {"hh": "23", "mm": "21", "ss": "28.512400"}, + datetime.time(hour=23, minute=21, second=28, microsecond=512400), + ), + ( + {"hh": "01", "mm": "03", "ss": "11.858714"}, + datetime.time(hour=1, minute=3, second=11, microsecond=858714), + ), + ( + {"hh": "14", "mm": "43", "ss": "59.9999997"}, + datetime.time(hour=14, minute=43, second=59, microsecond=999999), + ), + ({"hh": "24"}, datetime.time(hour=0)), + ({"hh": "24", "mm": "00"}, datetime.time(hour=0)), + ({"hh": "24", "mm": "00", "ss": "00"}, datetime.time(hour=0)), + ( + {"tz": TimezoneTuple(False, None, "00", "00", "UTC")}, + datetime.time(tzinfo=UTCOffset(name="UTC", minutes=0)), + ), + ( + { + "hh": "23", + "mm": "21", + "ss": "28.512400", + "tz": TimezoneTuple(False, None, "00", "00", "+00:00"), + }, + datetime.time( + hour=23, + minute=21, + second=28, + microsecond=512400, + tzinfo=UTCOffset(name="+00:00", minutes=0), + ), + ), + ( + { + "hh": "1", + "mm": "23", + "tz": TimezoneTuple(False, None, "01", "00", "+1"), + }, + datetime.time( + hour=1, minute=23, tzinfo=UTCOffset(name="+1", minutes=60) + ), + ), + ( + { + "hh": "1", + "mm": "23.4567", + "tz": TimezoneTuple(True, None, "01", "00", "-1"), + }, + datetime.time( + hour=1, + minute=23, + second=27, + microsecond=402000, + tzinfo=UTCOffset(name="-1", minutes=-60), + ), + ), + ( + { + "hh": "23", + "mm": "21", + "ss": "28.512400", + "tz": TimezoneTuple(False, None, "01", "30", "+1:30"), + }, + datetime.time( + hour=23, + minute=21, + second=28, + microsecond=512400, + tzinfo=UTCOffset(name="+1:30", minutes=90), + ), + ), + ( + { + "hh": "23", + "mm": "21", + "ss": "28.512400", + "tz": TimezoneTuple(False, None, "11", "15", "+11:15"), + }, + datetime.time( + hour=23, + minute=21, + second=28, + microsecond=512400, + tzinfo=UTCOffset(name="+11:15", minutes=675), + ), + ), + ( + { + "hh": "23", + "mm": "21", + "ss": "28.512400", + "tz": TimezoneTuple(False, None, "12", "34", "+12:34"), + }, + datetime.time( + hour=23, + minute=21, + second=28, + microsecond=512400, + tzinfo=UTCOffset(name="+12:34", minutes=754), + ), + ), + ( + { + "hh": "23", + "mm": "21", + "ss": "28.512400", + "tz": TimezoneTuple(False, None, "00", "00", "UTC"), + }, + datetime.time( + hour=23, + minute=21, + second=28, + microsecond=512400, + tzinfo=UTCOffset(name="UTC", minutes=0), + ), + ), + # Make sure we truncate, not round + # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is + # https://bitbucket.org/nielsenb/aniso8601/issues/21/sub-microsecond-precision-is-lost-when + ( + {"hh": "14.9999999999999999"}, + datetime.time(hour=14, minute=59, second=59, microsecond=999999), + ), + ({"mm": "0.00000000999"}, datetime.time()), + ({"mm": "0.0000000999"}, datetime.time(microsecond=5)), + ({"ss": "0.0000001"}, datetime.time()), + ({"ss": "2.0000048"}, datetime.time(second=2, microsecond=4)), + ) + + for testtuple in testtuples: + result = PythonTimeBuilder.build_time(**testtuple[0]) + self.assertEqual(result, testtuple[1]) + + def test_build_datetime(self): + testtuples = ( + ( + ( + DateTuple("2019", "06", "05", None, None, None), + TimeTuple("01", "03", "11.858714", None), + ), + datetime.datetime( + 2019, 6, 5, hour=1, minute=3, second=11, microsecond=858714 + ), + ), + ( + ( + DateTuple("1234", "02", "03", None, None, None), + TimeTuple("23", "21", "28.512400", None), + ), + datetime.datetime( + 1234, 2, 3, hour=23, minute=21, second=28, microsecond=512400 + ), + ), + ( + ( + DateTuple("1981", "04", "05", None, None, None), + TimeTuple( + "23", + "21", + "28.512400", + TimezoneTuple(False, None, "11", "15", "+11:15"), + ), + ), + datetime.datetime( + 1981, + 4, + 5, + hour=23, + minute=21, + second=28, + microsecond=512400, + tzinfo=UTCOffset(name="+11:15", minutes=675), + ), + ), + ) + + for testtuple in testtuples: + result = PythonTimeBuilder.build_datetime(*testtuple[0]) + self.assertEqual(result, testtuple[1]) + + def test_build_duration(self): + testtuples = ( + ( + { + "PnY": "1", + "PnM": "2", + "PnD": "3", + "TnH": "4", + "TnM": "54", + "TnS": "6", + }, + datetime.timedelta(days=428, hours=4, minutes=54, seconds=6), + ), + ( + { + "PnY": "1", + "PnM": "2", + "PnD": "3", + "TnH": "4", + "TnM": "54", + "TnS": "6.5", + }, + datetime.timedelta(days=428, hours=4, minutes=54, seconds=6.5), + ), + ({"PnY": "1", "PnM": "2", "PnD": "3"}, datetime.timedelta(days=428)), + ({"PnY": "1", "PnM": "2", "PnD": "3.5"}, datetime.timedelta(days=428.5)), + ( + {"TnH": "4", "TnM": "54", "TnS": "6.5"}, + datetime.timedelta(hours=4, minutes=54, seconds=6.5), + ), + ( + {"TnH": "1", "TnM": "3", "TnS": "11.858714"}, + datetime.timedelta(hours=1, minutes=3, seconds=11, microseconds=858714), + ), + ( + {"TnH": "4", "TnM": "54", "TnS": "28.512400"}, + datetime.timedelta( + hours=4, minutes=54, seconds=28, microseconds=512400 + ), + ), + # Make sure we truncate, not round + # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is + # https://bitbucket.org/nielsenb/aniso8601/issues/21/sub-microsecond-precision-is-lost-when + ( + {"PnY": "1999.9999999999999999"}, + datetime.timedelta(days=729999, seconds=86399, microseconds=999999), + ), + ( + {"PnM": "1.9999999999999999"}, + datetime.timedelta( + days=59, hours=23, minutes=59, seconds=59, microseconds=999999 + ), + ), + ( + {"PnW": "1.9999999999999999"}, + datetime.timedelta( + days=13, hours=23, minutes=59, seconds=59, microseconds=999999 + ), + ), + ( + {"PnD": "1.9999999999999999"}, + datetime.timedelta( + days=1, hours=23, minutes=59, seconds=59, microseconds=999999 + ), + ), + ( + {"TnH": "14.9999999999999999"}, + datetime.timedelta( + hours=14, minutes=59, seconds=59, microseconds=999999 + ), + ), + ({"TnM": "0.00000000999"}, datetime.timedelta(0)), + ({"TnM": "0.0000000999"}, datetime.timedelta(microseconds=5)), + ({"TnS": "0.0000001"}, datetime.timedelta(0)), + ({"TnS": "2.0000048"}, datetime.timedelta(seconds=2, microseconds=4)), + ({"PnY": "1"}, datetime.timedelta(days=365)), + ({"PnY": "1.5"}, datetime.timedelta(days=547.5)), + ({"PnM": "1"}, datetime.timedelta(days=30)), + ({"PnM": "1.5"}, datetime.timedelta(days=45)), + ({"PnW": "1"}, datetime.timedelta(days=7)), + ({"PnW": "1.5"}, datetime.timedelta(days=10.5)), + ({"PnD": "1"}, datetime.timedelta(days=1)), + ({"PnD": "1.5"}, datetime.timedelta(days=1.5)), + ( + { + "PnY": "0003", + "PnM": "06", + "PnD": "04", + "TnH": "12", + "TnM": "30", + "TnS": "05", + }, + datetime.timedelta(days=1279, hours=12, minutes=30, seconds=5), + ), + ( + { + "PnY": "0003", + "PnM": "06", + "PnD": "04", + "TnH": "12", + "TnM": "30", + "TnS": "05.5", + }, + datetime.timedelta(days=1279, hours=12, minutes=30, seconds=5.5), + ), + # Test timedelta limit + ( + {"PnD": "999999999", "TnH": "23", "TnM": "59", "TnS": "59.999999"}, + datetime.timedelta.max, + ), + # Make sure we truncate, not round + # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is + ( + { + "PnY": "0001", + "PnM": "02", + "PnD": "03", + "TnH": "14", + "TnM": "43", + "TnS": "59.9999997", + }, + datetime.timedelta( + days=428, hours=14, minutes=43, seconds=59, microseconds=999999 + ), + ), + # Verify overflows + ({"TnH": "36"}, datetime.timedelta(days=1, hours=12)), + ) + + for testtuple in testtuples: + result = PythonTimeBuilder.build_duration(**testtuple[0]) + self.assertEqual(result, testtuple[1]) + + def test_build_interval(self): + testtuples = ( + ( + { + "end": DatetimeTuple( + DateTuple("1981", "04", "05", None, None, None), + TimeTuple("01", "01", "00", None), + ), + "duration": DurationTuple(None, "1", None, None, None, None, None), + }, + datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1), + datetime.datetime(year=1981, month=3, day=6, hour=1, minute=1), + ), + ( + { + "end": DateTuple("1981", "04", "05", None, None, None), + "duration": DurationTuple(None, "1", None, None, None, None, None), + }, + datetime.date(year=1981, month=4, day=5), + datetime.date(year=1981, month=3, day=6), + ), + ( + { + "end": DateTuple("2018", "03", "06", None, None, None), + "duration": DurationTuple( + "1.5", None, None, None, None, None, None + ), + }, + datetime.date(year=2018, month=3, day=6), + datetime.datetime(year=2016, month=9, day=4, hour=12), + ), + ( + { + "end": DateTuple("2014", "11", "12", None, None, None), + "duration": DurationTuple(None, None, None, None, "1", None, None), + }, + datetime.date(year=2014, month=11, day=12), + datetime.datetime(year=2014, month=11, day=11, hour=23), + ), + ( + { + "end": DateTuple("2014", "11", "12", None, None, None), + "duration": DurationTuple(None, None, None, None, "4", "54", "6.5"), + }, + datetime.date(year=2014, month=11, day=12), + datetime.datetime( + year=2014, + month=11, + day=11, + hour=19, + minute=5, + second=53, + microsecond=500000, + ), + ), + ( + { + "end": DatetimeTuple( + DateTuple("2050", "03", "01", None, None, None), + TimeTuple( + "13", + "00", + "00", + TimezoneTuple(False, True, None, None, "Z"), + ), + ), + "duration": DurationTuple(None, None, None, None, "10", None, None), + }, + datetime.datetime( + year=2050, + month=3, + day=1, + hour=13, + tzinfo=UTCOffset(name="UTC", minutes=0), + ), + datetime.datetime( + year=2050, + month=3, + day=1, + hour=3, + tzinfo=UTCOffset(name="UTC", minutes=0), + ), + ), + # Make sure we truncate, not round + # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is + # https://bitbucket.org/nielsenb/aniso8601/issues/21/sub-microsecond-precision-is-lost-when + ( + { + "end": DateTuple("2000", "01", "01", None, None, None), + "duration": DurationTuple( + "1999.9999999999999999", None, None, None, None, None, None + ), + }, + datetime.date(year=2000, month=1, day=1), + datetime.datetime( + year=1, month=4, day=30, hour=0, minute=0, second=0, microsecond=1 + ), + ), + ( + { + "end": DateTuple("1989", "03", "01", None, None, None), + "duration": DurationTuple( + None, "1.9999999999999999", None, None, None, None, None + ), + }, + datetime.date(year=1989, month=3, day=1), + datetime.datetime( + year=1988, + month=12, + day=31, + hour=0, + minute=0, + second=0, + microsecond=1, + ), + ), + ( + { + "end": DateTuple("1989", "03", "01", None, None, None), + "duration": DurationTuple( + None, None, "1.9999999999999999", None, None, None, None + ), + }, + datetime.date(year=1989, month=3, day=1), + datetime.datetime( + year=1989, + month=2, + day=15, + hour=0, + minute=0, + second=0, + microsecond=1, + ), + ), + ( + { + "end": DateTuple("1989", "03", "01", None, None, None), + "duration": DurationTuple( + None, None, None, "1.9999999999999999", None, None, None + ), + }, + datetime.date(year=1989, month=3, day=1), + datetime.datetime( + year=1989, + month=2, + day=27, + hour=0, + minute=0, + second=0, + microsecond=1, + ), + ), + ( + { + "end": DateTuple("2001", "01", "01", None, None, None), + "duration": DurationTuple( + None, None, None, None, "14.9999999999999999", None, None + ), + }, + datetime.date(year=2001, month=1, day=1), + datetime.datetime( + year=2000, + month=12, + day=31, + hour=9, + minute=0, + second=0, + microsecond=1, + ), + ), + ( + { + "end": DateTuple("2001", "01", "01", None, None, None), + "duration": DurationTuple( + None, None, None, None, None, "0.00000000999", None + ), + }, + datetime.date(year=2001, month=1, day=1), + datetime.datetime(year=2001, month=1, day=1), + ), + ( + { + "end": DateTuple("2001", "01", "01", None, None, None), + "duration": DurationTuple( + None, None, None, None, None, "0.0000000999", None + ), + }, + datetime.date(year=2001, month=1, day=1), + datetime.datetime( + year=2000, + month=12, + day=31, + hour=23, + minute=59, + second=59, + microsecond=999995, + ), + ), + ( + { + "end": DateTuple("2018", "03", "06", None, None, None), + "duration": DurationTuple( + None, None, None, None, None, None, "0.0000001" + ), + }, + datetime.date(year=2018, month=3, day=6), + datetime.datetime(year=2018, month=3, day=6), + ), + ( + { + "end": DateTuple("2018", "03", "06", None, None, None), + "duration": DurationTuple( + None, None, None, None, None, None, "2.0000048" + ), + }, + datetime.date(year=2018, month=3, day=6), + datetime.datetime( + year=2018, + month=3, + day=5, + hour=23, + minute=59, + second=57, + microsecond=999996, + ), + ), + ( + { + "start": DatetimeTuple( + DateTuple("1981", "04", "05", None, None, None), + TimeTuple("01", "01", "00", None), + ), + "duration": DurationTuple(None, "1", None, "1", None, "1", None), + }, + datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1), + datetime.datetime(year=1981, month=5, day=6, hour=1, minute=2), + ), + ( + { + "start": DateTuple("1981", "04", "05", None, None, None), + "duration": DurationTuple(None, "1", None, "1", None, None, None), + }, + datetime.date(year=1981, month=4, day=5), + datetime.date(year=1981, month=5, day=6), + ), + ( + { + "start": DateTuple("2018", "03", "06", None, None, None), + "duration": DurationTuple( + None, "2.5", None, None, None, None, None + ), + }, + datetime.date(year=2018, month=3, day=6), + datetime.date(year=2018, month=5, day=20), + ), + ( + { + "start": DateTuple("2014", "11", "12", None, None, None), + "duration": DurationTuple(None, None, None, None, "1", None, None), + }, + datetime.date(year=2014, month=11, day=12), + datetime.datetime(year=2014, month=11, day=12, hour=1, minute=0), + ), + ( + { + "start": DateTuple("2014", "11", "12", None, None, None), + "duration": DurationTuple(None, None, None, None, "4", "54", "6.5"), + }, + datetime.date(year=2014, month=11, day=12), + datetime.datetime( + year=2014, + month=11, + day=12, + hour=4, + minute=54, + second=6, + microsecond=500000, + ), + ), + ( + { + "start": DatetimeTuple( + DateTuple("2050", "03", "01", None, None, None), + TimeTuple( + "13", + "00", + "00", + TimezoneTuple(False, True, None, None, "Z"), + ), + ), + "duration": DurationTuple(None, None, None, None, "10", None, None), + }, + datetime.datetime( + year=2050, + month=3, + day=1, + hour=13, + tzinfo=UTCOffset(name="UTC", minutes=0), + ), + datetime.datetime( + year=2050, + month=3, + day=1, + hour=23, + tzinfo=UTCOffset(name="UTC", minutes=0), + ), + ), + # Make sure we truncate, not round + # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is + ( + { + "start": DateTuple("0001", "01", "01", None, None, None), + "duration": DurationTuple( + "1999.9999999999999999", None, None, None, None, None, None + ), + }, + datetime.date(year=1, month=1, day=1), + datetime.datetime( + year=1999, + month=9, + day=3, + hour=23, + minute=59, + second=59, + microsecond=999999, + ), + ), + ( + { + "start": DateTuple("1989", "03", "01", None, None, None), + "duration": DurationTuple( + None, "1.9999999999999999", None, None, None, None, None + ), + }, + datetime.date(year=1989, month=3, day=1), + datetime.datetime( + year=1989, + month=4, + day=29, + hour=23, + minute=59, + second=59, + microsecond=999999, + ), + ), + ( + { + "start": DateTuple("1989", "03", "01", None, None, None), + "duration": DurationTuple( + None, None, "1.9999999999999999", None, None, None, None + ), + }, + datetime.date(year=1989, month=3, day=1), + datetime.datetime( + year=1989, + month=3, + day=14, + hour=23, + minute=59, + second=59, + microsecond=999999, + ), + ), + ( + { + "start": DateTuple("1989", "03", "01", None, None, None), + "duration": DurationTuple( + None, None, None, "1.9999999999999999", None, None, None + ), + }, + datetime.date(year=1989, month=3, day=1), + datetime.datetime( + year=1989, + month=3, + day=2, + hour=23, + minute=59, + second=59, + microsecond=999999, + ), + ), + ( + { + "start": DateTuple("2001", "01", "01", None, None, None), + "duration": DurationTuple( + None, None, None, None, "14.9999999999999999", None, None + ), + }, + datetime.date(year=2001, month=1, day=1), + datetime.datetime( + year=2001, + month=1, + day=1, + hour=14, + minute=59, + second=59, + microsecond=999999, + ), + ), + ( + { + "start": DateTuple("2001", "01", "01", None, None, None), + "duration": DurationTuple( + None, None, None, None, None, "0.00000000999", None + ), + }, + datetime.date(year=2001, month=1, day=1), + datetime.datetime(year=2001, month=1, day=1), + ), + ( + { + "start": DateTuple("2001", "01", "01", None, None, None), + "duration": DurationTuple( + None, None, None, None, None, "0.0000000999", None + ), + }, + datetime.date(year=2001, month=1, day=1), + datetime.datetime( + year=2001, month=1, day=1, hour=0, minute=0, second=0, microsecond=5 + ), + ), + ( + { + "start": DateTuple("2018", "03", "06", None, None, None), + "duration": DurationTuple( + None, None, None, None, None, None, "0.0000001" + ), + }, + datetime.date(year=2018, month=3, day=6), + datetime.datetime(year=2018, month=3, day=6), + ), + ( + { + "start": DateTuple("2018", "03", "06", None, None, None), + "duration": DurationTuple( + None, None, None, None, None, None, "2.0000048" + ), + }, + datetime.date(year=2018, month=3, day=6), + datetime.datetime( + year=2018, month=3, day=6, hour=0, minute=0, second=2, microsecond=4 + ), + ), + ( + { + "start": DatetimeTuple( + DateTuple("1980", "03", "05", None, None, None), + TimeTuple("01", "01", "00", None), + ), + "end": DatetimeTuple( + DateTuple("1981", "04", "05", None, None, None), + TimeTuple("01", "01", "00", None), + ), + }, + datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1), + datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1), + ), + ( + { + "start": DatetimeTuple( + DateTuple("1980", "03", "05", None, None, None), + TimeTuple("01", "01", "00", None), + ), + "end": DateTuple("1981", "04", "05", None, None, None), + }, + datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1), + datetime.date(year=1981, month=4, day=5), + ), + ( + { + "start": DateTuple("1980", "03", "05", None, None, None), + "end": DatetimeTuple( + DateTuple("1981", "04", "05", None, None, None), + TimeTuple("01", "01", "00", None), + ), + }, + datetime.date(year=1980, month=3, day=5), + datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1), + ), + ( + { + "start": DateTuple("1980", "03", "05", None, None, None), + "end": DateTuple("1981", "04", "05", None, None, None), + }, + datetime.date(year=1980, month=3, day=5), + datetime.date(year=1981, month=4, day=5), + ), + ( + { + "start": DateTuple("1981", "04", "05", None, None, None), + "end": DateTuple("1980", "03", "05", None, None, None), + }, + datetime.date(year=1981, month=4, day=5), + datetime.date(year=1980, month=3, day=5), + ), + ( + { + "start": DatetimeTuple( + DateTuple("2050", "03", "01", None, None, None), + TimeTuple( + "13", + "00", + "00", + TimezoneTuple(False, True, None, None, "Z"), + ), + ), + "end": DatetimeTuple( + DateTuple("2050", "05", "11", None, None, None), + TimeTuple( + "15", + "30", + "00", + TimezoneTuple(False, True, None, None, "Z"), + ), + ), + }, + datetime.datetime( + year=2050, + month=3, + day=1, + hour=13, + tzinfo=UTCOffset(name="UTC", minutes=0), + ), + datetime.datetime( + year=2050, + month=5, + day=11, + hour=15, + minute=30, + tzinfo=UTCOffset(name="UTC", minutes=0), + ), + ), + # Test concise representation + ( + { + "start": DateTuple("2020", "01", "01", None, None, None), + "end": DateTuple(None, None, "02", None, None, None), + }, + datetime.date(year=2020, month=1, day=1), + datetime.date(year=2020, month=1, day=2), + ), + ( + { + "start": DateTuple("2008", "02", "15", None, None, None), + "end": DateTuple(None, "03", "14", None, None, None), + }, + datetime.date(year=2008, month=2, day=15), + datetime.date(year=2008, month=3, day=14), + ), + ( + { + "start": DatetimeTuple( + DateTuple("2007", "12", "14", None, None, None), + TimeTuple("13", "30", None, None), + ), + "end": TimeTuple("15", "30", None, None), + }, + datetime.datetime(year=2007, month=12, day=14, hour=13, minute=30), + datetime.datetime(year=2007, month=12, day=14, hour=15, minute=30), + ), + ( + { + "start": DatetimeTuple( + DateTuple("2007", "11", "13", None, None, None), + TimeTuple("09", "00", None, None), + ), + "end": DatetimeTuple( + DateTuple(None, None, "15", None, None, None), + TimeTuple("17", "00", None, None), + ), + }, + datetime.datetime(year=2007, month=11, day=13, hour=9), + datetime.datetime(year=2007, month=11, day=15, hour=17), + ), + ( + { + "start": DatetimeTuple( + DateTuple("2007", "11", "13", None, None, None), + TimeTuple("00", "00", None, None), + ), + "end": DatetimeTuple( + DateTuple(None, None, "16", None, None, None), + TimeTuple("00", "00", None, None), + ), + }, + datetime.datetime(year=2007, month=11, day=13), + datetime.datetime(year=2007, month=11, day=16), + ), + ( + { + "start": DatetimeTuple( + DateTuple("2007", "11", "13", None, None, None), + TimeTuple( + "09", + "00", + None, + TimezoneTuple(False, True, None, None, "Z"), + ), + ), + "end": DatetimeTuple( + DateTuple(None, None, "15", None, None, None), + TimeTuple("17", "00", None, None), + ), + }, + datetime.datetime( + year=2007, + month=11, + day=13, + hour=9, + tzinfo=UTCOffset(name="UTC", minutes=0), + ), + datetime.datetime( + year=2007, + month=11, + day=15, + hour=17, + tzinfo=UTCOffset(name="UTC", minutes=0), + ), + ), + ( + { + "start": DatetimeTuple( + DateTuple("2007", "11", "13", None, None, None), + TimeTuple("09", "00", None, None), + ), + "end": TimeTuple("12", "34.567", None, None), + }, + datetime.datetime(year=2007, month=11, day=13, hour=9), + datetime.datetime( + year=2007, + month=11, + day=13, + hour=12, + minute=34, + second=34, + microsecond=20000, + ), + ), + ( + { + "start": DateTuple("2007", "11", "13", None, None, None), + "end": TimeTuple("12", "34", None, None), + }, + datetime.date(year=2007, month=11, day=13), + datetime.datetime(year=2007, month=11, day=13, hour=12, minute=34), + ), + # Make sure we truncate, not round + # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is + ( + { + "start": DatetimeTuple( + DateTuple("1980", "03", "05", None, None, None), + TimeTuple("01", "01", "00.0000001", None), + ), + "end": DatetimeTuple( + DateTuple("1981", "04", "05", None, None, None), + TimeTuple("14", "43", "59.9999997", None), + ), + }, + datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1), + datetime.datetime( + year=1981, + month=4, + day=5, + hour=14, + minute=43, + second=59, + microsecond=999999, + ), + ), + ) + + for testtuple in testtuples: + result = PythonTimeBuilder.build_interval(**testtuple[0]) + self.assertEqual(result[0], testtuple[1]) + self.assertEqual(result[1], testtuple[2]) + + def test_build_repeating_interval(self): + args = { + "Rnn": "3", + "interval": IntervalTuple( + DateTuple("1981", "04", "05", None, None, None), + None, + DurationTuple(None, None, None, "1", None, None, None), + ), + } + results = list(PythonTimeBuilder.build_repeating_interval(**args)) + + self.assertEqual(results[0], datetime.date(year=1981, month=4, day=5)) + self.assertEqual(results[1], datetime.date(year=1981, month=4, day=6)) + self.assertEqual(results[2], datetime.date(year=1981, month=4, day=7)) + + args = { + "Rnn": "11", + "interval": IntervalTuple( + None, + DatetimeTuple( + DateTuple("1980", "03", "05", None, None, None), + TimeTuple("01", "01", "00", None), + ), + DurationTuple(None, None, None, None, "1", "2", None), + ), + } + results = list(PythonTimeBuilder.build_repeating_interval(**args)) + + for dateindex in compat.range(0, 11): + self.assertEqual( + results[dateindex], + datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1) + - dateindex * datetime.timedelta(hours=1, minutes=2), + ) + + args = { + "Rnn": "2", + "interval": IntervalTuple( + DatetimeTuple( + DateTuple("1980", "03", "05", None, None, None), + TimeTuple("01", "01", "00", None), + ), + DatetimeTuple( + DateTuple("1981", "04", "05", None, None, None), + TimeTuple("01", "01", "00", None), + ), + None, + ), + } + results = list(PythonTimeBuilder.build_repeating_interval(**args)) + + self.assertEqual( + results[0], datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1) + ) + self.assertEqual( + results[1], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1) + ) + + args = { + "Rnn": "2", + "interval": IntervalTuple( + DatetimeTuple( + DateTuple("1980", "03", "05", None, None, None), + TimeTuple("01", "01", "00", None), + ), + DatetimeTuple( + DateTuple("1981", "04", "05", None, None, None), + TimeTuple("01", "01", "00", None), + ), + None, + ), + } + results = list(PythonTimeBuilder.build_repeating_interval(**args)) + + self.assertEqual( + results[0], datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1) + ) + self.assertEqual( + results[1], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1) + ) + + args = { + "R": True, + "interval": IntervalTuple( + None, + DatetimeTuple( + DateTuple("1980", "03", "05", None, None, None), + TimeTuple("01", "01", "00", None), + ), + DurationTuple(None, None, None, None, "1", "2", None), + ), + } + resultgenerator = PythonTimeBuilder.build_repeating_interval(**args) + + # Test the first 11 generated + for dateindex in compat.range(0, 11): + self.assertEqual( + next(resultgenerator), + datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1) + - dateindex * datetime.timedelta(hours=1, minutes=2), + ) + + args = { + "R": True, + "interval": IntervalTuple( + DateTuple("1981", "04", "05", None, None, None), + None, + DurationTuple(None, None, None, "1", None, None, None), + ), + } + resultgenerator = PythonTimeBuilder.build_repeating_interval(**args) + + # Test the first 11 generated + for dateindex in compat.range(0, 11): + self.assertEqual( + next(resultgenerator), + ( + datetime.datetime(year=1981, month=4, day=5, hour=0, minute=0) + + dateindex * datetime.timedelta(days=1) + ).date(), + ) + + def test_build_timezone(self): + testtuples = ( + ({"Z": True, "name": "Z"}, datetime.timedelta(hours=0), "UTC"), + ( + {"negative": False, "hh": "00", "mm": "00", "name": "+00:00"}, + datetime.timedelta(hours=0), + "+00:00", + ), + ( + {"negative": False, "hh": "01", "mm": "00", "name": "+01:00"}, + datetime.timedelta(hours=1), + "+01:00", + ), + ( + {"negative": True, "hh": "01", "mm": "00", "name": "-01:00"}, + -datetime.timedelta(hours=1), + "-01:00", + ), + ( + {"negative": False, "hh": "00", "mm": "12", "name": "+00:12"}, + datetime.timedelta(minutes=12), + "+00:12", + ), + ( + {"negative": False, "hh": "01", "mm": "23", "name": "+01:23"}, + datetime.timedelta(hours=1, minutes=23), + "+01:23", + ), + ( + {"negative": True, "hh": "01", "mm": "23", "name": "-01:23"}, + -datetime.timedelta(hours=1, minutes=23), + "-01:23", + ), + ( + {"negative": False, "hh": "00", "name": "+00"}, + datetime.timedelta(hours=0), + "+00", + ), + ( + {"negative": False, "hh": "01", "name": "+01"}, + datetime.timedelta(hours=1), + "+01", + ), + ( + {"negative": True, "hh": "01", "name": "-01"}, + -datetime.timedelta(hours=1), + "-01", + ), + ( + {"negative": False, "hh": "12", "name": "+12"}, + datetime.timedelta(hours=12), + "+12", + ), + ( + {"negative": True, "hh": "12", "name": "-12"}, + -datetime.timedelta(hours=12), + "-12", + ), + ) + + for testtuple in testtuples: + result = PythonTimeBuilder.build_timezone(**testtuple[0]) + self.assertEqual(result.utcoffset(None), testtuple[1]) + self.assertEqual(result.tzname(None), testtuple[2]) + + def test_range_check_date(self): + # 0 isn't a valid year for a Python builder + with self.assertRaises(YearOutOfBoundsError): + PythonTimeBuilder.build_date(YYYY="0000") + + # Leap year + # https://bitbucket.org/nielsenb/aniso8601/issues/14/parsing-ordinal-dates-should-only-allow + with self.assertRaises(DayOutOfBoundsError): + PythonTimeBuilder.build_date(YYYY="1981", DDD="366") + + def test_range_check_time(self): + # Hour 24 can only represent midnight + with self.assertRaises(MidnightBoundsError): + PythonTimeBuilder.build_time(hh="24", mm="00", ss="01") + + with self.assertRaises(MidnightBoundsError): + PythonTimeBuilder.build_time(hh="24", mm="00.1") + + with self.assertRaises(MidnightBoundsError): + PythonTimeBuilder.build_time(hh="24", mm="01") + + with self.assertRaises(MidnightBoundsError): + PythonTimeBuilder.build_time(hh="24.1") + + def test_range_check_duration(self): + with self.assertRaises(YearOutOfBoundsError): + PythonTimeBuilder.build_duration( + PnY=str((datetime.timedelta.max.days // 365) + 1) + ) + + with self.assertRaises(MonthOutOfBoundsError): + PythonTimeBuilder.build_duration( + PnM=str((datetime.timedelta.max.days // 30) + 1) + ) + + with self.assertRaises(DayOutOfBoundsError): + PythonTimeBuilder.build_duration(PnD=str(datetime.timedelta.max.days + 1)) + + with self.assertRaises(WeekOutOfBoundsError): + PythonTimeBuilder.build_duration( + PnW=str((datetime.timedelta.max.days // 7) + 1) + ) + + with self.assertRaises(HoursOutOfBoundsError): + PythonTimeBuilder.build_duration( + TnH=str((datetime.timedelta.max.days * 24) + 24) + ) + + with self.assertRaises(MinutesOutOfBoundsError): + PythonTimeBuilder.build_duration( + TnM=str((datetime.timedelta.max.days * 24 * 60) + 24 * 60) + ) + + with self.assertRaises(SecondsOutOfBoundsError): + PythonTimeBuilder.build_duration( + TnS=str((datetime.timedelta.max.days * 24 * 60 * 60) + 24 * 60 * 60) + ) + + # Split max range across all parts + maxpart = datetime.timedelta.max.days // 7 + + with self.assertRaises(DayOutOfBoundsError): + PythonTimeBuilder.build_duration( + PnY=str((maxpart // 365) + 1), + PnM=str((maxpart // 30) + 1), + PnD=str((maxpart + 1)), + PnW=str((maxpart // 7) + 1), + TnH=str((maxpart * 24) + 1), + TnM=str((maxpart * 24 * 60) + 1), + TnS=str((maxpart * 24 * 60 * 60) + 1), + ) + + def test_range_check_interval(self): + with self.assertRaises(YearOutOfBoundsError): + PythonTimeBuilder.build_interval( + start=DateTuple("0007", None, None, None, None, None), + duration=DurationTuple( + None, None, None, str(datetime.timedelta.max.days), None, None, None + ), + ) + + with self.assertRaises(YearOutOfBoundsError): + PythonTimeBuilder.build_interval( + start=DatetimeTuple( + DateTuple("0007", None, None, None, None, None), + TimeTuple("1", None, None, None), + ), + duration=DurationTuple( + str(datetime.timedelta.max.days // 365), + None, + None, + None, + None, + None, + None, + ), + ) + + with self.assertRaises(YearOutOfBoundsError): + PythonTimeBuilder.build_interval( + end=DateTuple("0001", None, None, None, None, None), + duration=DurationTuple("3", None, None, None, None, None, None), + ) + + with self.assertRaises(YearOutOfBoundsError): + PythonTimeBuilder.build_interval( + end=DatetimeTuple( + DateTuple("0001", None, None, None, None, None), + TimeTuple("1", None, None, None), + ), + duration=DurationTuple("2", None, None, None, None, None, None), + ) + + def test_build_week_date(self): + weekdate = PythonTimeBuilder._build_week_date(2009, 1) + self.assertEqual(weekdate, datetime.date(year=2008, month=12, day=29)) + + weekdate = PythonTimeBuilder._build_week_date(2009, 53, isoday=7) + self.assertEqual(weekdate, datetime.date(year=2010, month=1, day=3)) + + def test_build_ordinal_date(self): + ordinaldate = PythonTimeBuilder._build_ordinal_date(1981, 95) + self.assertEqual(ordinaldate, datetime.date(year=1981, month=4, day=5)) + + def test_iso_year_start(self): + yearstart = PythonTimeBuilder._iso_year_start(2004) + self.assertEqual(yearstart, datetime.date(year=2003, month=12, day=29)) + + yearstart = PythonTimeBuilder._iso_year_start(2010) + self.assertEqual(yearstart, datetime.date(year=2010, month=1, day=4)) + + yearstart = PythonTimeBuilder._iso_year_start(2009) + self.assertEqual(yearstart, datetime.date(year=2008, month=12, day=29)) + + def test_date_generator(self): + startdate = datetime.date(year=2018, month=8, day=29) + timedelta = datetime.timedelta(days=1) + iterations = 10 + + generator = PythonTimeBuilder._date_generator(startdate, timedelta, iterations) + + results = list(generator) + + for dateindex in compat.range(0, 10): + self.assertEqual( + results[dateindex], + datetime.date(year=2018, month=8, day=29) + + dateindex * datetime.timedelta(days=1), + ) + + def test_date_generator_unbounded(self): + startdate = datetime.date(year=2018, month=8, day=29) + timedelta = datetime.timedelta(days=5) + + generator = PythonTimeBuilder._date_generator_unbounded(startdate, timedelta) + + # Check the first 10 results + for dateindex in compat.range(0, 10): + self.assertEqual( + next(generator), + datetime.date(year=2018, month=8, day=29) + + dateindex * datetime.timedelta(days=5), + ) + + def test_distribute_microseconds(self): + self.assertEqual(PythonTimeBuilder._distribute_microseconds(1, (), ()), (1,)) + self.assertEqual( + PythonTimeBuilder._distribute_microseconds(11, (0,), (10,)), (1, 1) + ) + self.assertEqual( + PythonTimeBuilder._distribute_microseconds(211, (0, 0), (100, 10)), + (2, 1, 1), + ) + + self.assertEqual(PythonTimeBuilder._distribute_microseconds(1, (), ()), (1,)) + self.assertEqual( + PythonTimeBuilder._distribute_microseconds(11, (5,), (10,)), (6, 1) + ) + self.assertEqual( + PythonTimeBuilder._distribute_microseconds(211, (10, 5), (100, 10)), + (12, 6, 1), + ) diff --git a/radar/Lib/site-packages/aniso8601/compat.py b/radar/Lib/site-packages/aniso8601/compat.py new file mode 100644 index 0000000..25af579 --- /dev/null +++ b/radar/Lib/site-packages/aniso8601/compat.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Brandon Nielsen +# All rights reserved. +# +# This software may be modified and distributed under the terms +# of the BSD license. See the LICENSE file for details. + +import sys + +PY2 = sys.version_info[0] == 2 + +if PY2: # pragma: no cover + range = xrange # pylint: disable=undefined-variable +else: + range = range + + +def is_string(tocheck): + # pylint: disable=undefined-variable + if PY2: # pragma: no cover + return isinstance(tocheck, str) or isinstance(tocheck, unicode) + + return isinstance(tocheck, str) diff --git a/radar/Lib/site-packages/aniso8601/date.py b/radar/Lib/site-packages/aniso8601/date.py new file mode 100644 index 0000000..ea0cf9c --- /dev/null +++ b/radar/Lib/site-packages/aniso8601/date.py @@ -0,0 +1,161 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Brandon Nielsen +# All rights reserved. +# +# This software may be modified and distributed under the terms +# of the BSD license. See the LICENSE file for details. + +from aniso8601.builders import TupleBuilder +from aniso8601.builders.python import PythonTimeBuilder +from aniso8601.compat import is_string +from aniso8601.exceptions import ISOFormatError +from aniso8601.resolution import DateResolution + + +def get_date_resolution(isodatestr): + # Valid string formats are: + # + # Y[YYY] + # YYYY-MM-DD + # YYYYMMDD + # YYYY-MM + # YYYY-Www + # YYYYWww + # YYYY-Www-D + # YYYYWwwD + # YYYY-DDD + # YYYYDDD + isodatetuple = parse_date(isodatestr, builder=TupleBuilder) + + if isodatetuple.DDD is not None: + # YYYY-DDD + # YYYYDDD + return DateResolution.Ordinal + + if isodatetuple.D is not None: + # YYYY-Www-D + # YYYYWwwD + return DateResolution.Weekday + + if isodatetuple.Www is not None: + # YYYY-Www + # YYYYWww + return DateResolution.Week + + if isodatetuple.DD is not None: + # YYYY-MM-DD + # YYYYMMDD + return DateResolution.Day + + if isodatetuple.MM is not None: + # YYYY-MM + return DateResolution.Month + + # Y[YYY] + return DateResolution.Year + + +def parse_date(isodatestr, builder=PythonTimeBuilder): + # Given a string in any ISO 8601 date format, return a datetime.date + # object that corresponds to the given date. Valid string formats are: + # + # Y[YYY] + # YYYY-MM-DD + # YYYYMMDD + # YYYY-MM + # YYYY-Www + # YYYYWww + # YYYY-Www-D + # YYYYWwwD + # YYYY-DDD + # YYYYDDD + if is_string(isodatestr) is False: + raise ValueError("Date must be string.") + + if isodatestr.startswith("+") or isodatestr.startswith("-"): + raise NotImplementedError( + "ISO 8601 extended year representation " "not supported." + ) + + if len(isodatestr) == 0 or isodatestr.count("-") > 2: + raise ISOFormatError('"{0}" is not a valid ISO 8601 date.'.format(isodatestr)) + yearstr = None + monthstr = None + daystr = None + weekstr = None + weekdaystr = None + ordinaldaystr = None + + if len(isodatestr) <= 4: + # Y[YYY] + yearstr = isodatestr + elif "W" in isodatestr: + if len(isodatestr) == 10: + # YYYY-Www-D + yearstr = isodatestr[0:4] + weekstr = isodatestr[6:8] + weekdaystr = isodatestr[9] + elif len(isodatestr) == 8: + if "-" in isodatestr: + # YYYY-Www + yearstr = isodatestr[0:4] + weekstr = isodatestr[6:] + else: + # YYYYWwwD + yearstr = isodatestr[0:4] + weekstr = isodatestr[5:7] + weekdaystr = isodatestr[7] + elif len(isodatestr) == 7: + # YYYYWww + yearstr = isodatestr[0:4] + weekstr = isodatestr[5:] + elif len(isodatestr) == 7: + if "-" in isodatestr: + # YYYY-MM + yearstr = isodatestr[0:4] + monthstr = isodatestr[5:] + else: + # YYYYDDD + yearstr = isodatestr[0:4] + ordinaldaystr = isodatestr[4:] + elif len(isodatestr) == 8: + if "-" in isodatestr: + # YYYY-DDD + yearstr = isodatestr[0:4] + ordinaldaystr = isodatestr[5:] + else: + # YYYYMMDD + yearstr = isodatestr[0:4] + monthstr = isodatestr[4:6] + daystr = isodatestr[6:] + elif len(isodatestr) == 10: + # YYYY-MM-DD + yearstr = isodatestr[0:4] + monthstr = isodatestr[5:7] + daystr = isodatestr[8:] + else: + raise ISOFormatError('"{0}" is not a valid ISO 8601 date.'.format(isodatestr)) + + hascomponent = False + + for componentstr in [yearstr, monthstr, daystr, weekstr, weekdaystr, ordinaldaystr]: + if componentstr is not None: + hascomponent = True + + if componentstr.isdigit() is False: + raise ISOFormatError( + '"{0}" is not a valid ISO 8601 date.'.format(isodatestr) + ) + + if hascomponent is False: + raise ISOFormatError('"{0}" is not a valid ISO 8601 date.'.format(isodatestr)) + + return builder.build_date( + YYYY=yearstr, + MM=monthstr, + DD=daystr, + Www=weekstr, + D=weekdaystr, + DDD=ordinaldaystr, + ) diff --git a/radar/Lib/site-packages/aniso8601/decimalfraction.py b/radar/Lib/site-packages/aniso8601/decimalfraction.py new file mode 100644 index 0000000..3086ee7 --- /dev/null +++ b/radar/Lib/site-packages/aniso8601/decimalfraction.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Brandon Nielsen +# All rights reserved. +# +# This software may be modified and distributed under the terms +# of the BSD license. See the LICENSE file for details. + + +def normalize(value): + """Returns the string with decimal separators normalized.""" + return value.replace(",", ".") diff --git a/radar/Lib/site-packages/aniso8601/duration.py b/radar/Lib/site-packages/aniso8601/duration.py new file mode 100644 index 0000000..cdc0f8f --- /dev/null +++ b/radar/Lib/site-packages/aniso8601/duration.py @@ -0,0 +1,291 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Brandon Nielsen +# All rights reserved. +# +# This software may be modified and distributed under the terms +# of the BSD license. See the LICENSE file for details. + +from aniso8601 import compat +from aniso8601.builders import TupleBuilder +from aniso8601.builders.python import PythonTimeBuilder +from aniso8601.date import parse_date +from aniso8601.decimalfraction import normalize +from aniso8601.exceptions import ISOFormatError +from aniso8601.resolution import DurationResolution +from aniso8601.time import parse_time + + +def get_duration_resolution(isodurationstr): + # Valid string formats are: + # + # PnYnMnDTnHnMnS (or any reduced precision equivalent) + # PnW + # PT