commit bd2cc20110efef6315c7842f09d79bad256c4a64 Author: Josh Washburne Date: Wed Apr 3 20:52:01 2024 -0400 Initial commit. Needs more test coverage. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..92c86be --- /dev/null +++ b/.gitignore @@ -0,0 +1,163 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Dev settings +.vscode/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/contrib/example.py b/contrib/example.py new file mode 100644 index 0000000..af48753 --- /dev/null +++ b/contrib/example.py @@ -0,0 +1,27 @@ +""" +An example showing off the current D'Ni time and it's conversion back to an +Earth datetime. +""" + +from datetime import datetime +import sys +from time import sleep + +from dnidatetime import DniDatetime + + +if __name__ == "__main__": + while True: + try: + earth_now = datetime.now().astimezone() + dni_now = DniDatetime.from_earth(earth_now) + back = dni_now.to_earth().astimezone() + print( + f"{earth_now} --> {dni_now} --> {back}", + end=" \r", + flush=True, + ) + sleep(0.01) + except KeyboardInterrupt: + print("\n") + sys.exit() diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..e4f7658 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +mypy_path = $MYPY_CONFIG_FILE_DIR/src diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..97e9c43 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1044 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "astroid" +version = "3.1.0" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, +] + +[[package]] +name = "babel" +version = "2.14.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, + {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, +] + +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + +[[package]] +name = "black" +version = "24.3.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"}, + {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"}, + {file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"}, + {file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"}, + {file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"}, + {file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"}, + {file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"}, + {file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"}, + {file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"}, + {file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"}, + {file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"}, + {file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"}, + {file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"}, + {file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"}, + {file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"}, + {file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"}, + {file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"}, + {file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"}, + {file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"}, + {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"}, + {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"}, + {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "ghp-import" +version = "2.1.0" +description = "Copy your docs directly to the gh-pages branch." +optional = false +python-versions = "*" +files = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["flake8", "markdown", "twine", "wheel"] + +[[package]] +name = "griffe" +version = "0.42.1" +description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." +optional = false +python-versions = ">=3.8" +files = [ + {file = "griffe-0.42.1-py3-none-any.whl", hash = "sha256:7e805e35617601355edcac0d3511cedc1ed0cb1f7645e2d336ae4b05bbae7b3b"}, + {file = "griffe-0.42.1.tar.gz", hash = "sha256:57046131384043ed078692b85d86b76568a686266cc036b9b56b704466f803ce"}, +] + +[package.dependencies] +colorama = ">=0.4" + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markdown" +version = "3.6" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Markdown-3.6-py3-none-any.whl", hash = "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f"}, + {file = "Markdown-3.6.tar.gz", hash = "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224"}, +] + +[package.extras] +docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +optional = false +python-versions = ">=3.6" +files = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] + +[[package]] +name = "mkdocs" +version = "1.5.3" +description = "Project documentation with Markdown." +optional = false +python-versions = ">=3.7" +files = [ + {file = "mkdocs-1.5.3-py3-none-any.whl", hash = "sha256:3b3a78e736b31158d64dbb2f8ba29bd46a379d0c6e324c2246c3bc3d2189cfc1"}, + {file = "mkdocs-1.5.3.tar.gz", hash = "sha256:eb7c99214dcb945313ba30426c2451b735992c73c2e10838f76d09e39ff4d0e2"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} +ghp-import = ">=1.0" +jinja2 = ">=2.11.1" +markdown = ">=3.2.1" +markupsafe = ">=2.0.1" +mergedeep = ">=1.3.4" +packaging = ">=20.5" +pathspec = ">=0.11.1" +platformdirs = ">=2.2.0" +pyyaml = ">=5.1" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pathspec (==0.11.1)", "platformdirs (==2.2.0)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"] + +[[package]] +name = "mkdocs-autorefs" +version = "1.0.1" +description = "Automatically link across pages in MkDocs." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_autorefs-1.0.1-py3-none-any.whl", hash = "sha256:aacdfae1ab197780fb7a2dac92ad8a3d8f7ca8049a9cbe56a4218cd52e8da570"}, + {file = "mkdocs_autorefs-1.0.1.tar.gz", hash = "sha256:f684edf847eced40b570b57846b15f0bf57fb93ac2c510450775dcf16accb971"}, +] + +[package.dependencies] +Markdown = ">=3.3" +markupsafe = ">=2.0.1" +mkdocs = ">=1.1" + +[[package]] +name = "mkdocs-material" +version = "9.5.14" +description = "Documentation that simply works" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_material-9.5.14-py3-none-any.whl", hash = "sha256:a45244ac221fda46ecf8337f00ec0e5cb5348ab9ffb203ca2a0c313b0d4dbc27"}, + {file = "mkdocs_material-9.5.14.tar.gz", hash = "sha256:2a1f8e67cda2587ab93ecea9ba42d0ca61d1d7b5fad8cf690eeaeb39dcd4b9af"}, +] + +[package.dependencies] +babel = ">=2.10,<3.0" +colorama = ">=0.4,<1.0" +jinja2 = ">=3.0,<4.0" +markdown = ">=3.2,<4.0" +mkdocs = ">=1.5.3,<1.6.0" +mkdocs-material-extensions = ">=1.3,<2.0" +paginate = ">=0.5,<1.0" +pygments = ">=2.16,<3.0" +pymdown-extensions = ">=10.2,<11.0" +regex = ">=2022.4" +requests = ">=2.26,<3.0" + +[package.extras] +git = ["mkdocs-git-committers-plugin-2 (>=1.1,<2.0)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"] +imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"] +recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +description = "Extension pack for Python Markdown and MkDocs Material." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, + {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, +] + +[[package]] +name = "mkdocstrings" +version = "0.24.1" +description = "Automatic documentation from sources, for MkDocs." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocstrings-0.24.1-py3-none-any.whl", hash = "sha256:b4206f9a2ca8a648e222d5a0ca1d36ba7dee53c88732818de183b536f9042b5d"}, + {file = "mkdocstrings-0.24.1.tar.gz", hash = "sha256:cc83f9a1c8724fc1be3c2fa071dd73d91ce902ef6a79710249ec8d0ee1064401"}, +] + +[package.dependencies] +click = ">=7.0" +Jinja2 = ">=2.11.1" +Markdown = ">=3.3" +MarkupSafe = ">=1.1" +mkdocs = ">=1.4" +mkdocs-autorefs = ">=0.3.1" +mkdocstrings-python = {version = ">=0.5.2", optional = true, markers = "extra == \"python\""} +platformdirs = ">=2.2.0" +pymdown-extensions = ">=6.3" + +[package.extras] +crystal = ["mkdocstrings-crystal (>=0.3.4)"] +python = ["mkdocstrings-python (>=0.5.2)"] +python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] + +[[package]] +name = "mkdocstrings-python" +version = "1.8.0" +description = "A Python handler for mkdocstrings." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocstrings_python-1.8.0-py3-none-any.whl", hash = "sha256:4209970cc90bec194568682a535848a8d8489516c6ed4adbe58bbc67b699ca9d"}, + {file = "mkdocstrings_python-1.8.0.tar.gz", hash = "sha256:1488bddf50ee42c07d9a488dddc197f8e8999c2899687043ec5dd1643d057192"}, +] + +[package.dependencies] +griffe = ">=0.37" +mkdocstrings = ">=0.20" + +[[package]] +name = "mypy" +version = "1.9.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, + {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, + {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, + {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, + {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, + {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, + {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, + {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, + {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, + {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, + {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, + {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, + {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, + {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, + {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, + {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, + {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, + {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, + {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "paginate" +version = "0.5.6" +description = "Divides large result sets into pages for easier browsing" +optional = false +python-versions = "*" +files = [ + {file = "paginate-0.5.6.tar.gz", hash = "sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pylint" +version = "3.1.0" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, +] + +[package.dependencies] +astroid = ">=3.1.0,<=3.2.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, +] +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pymdown-extensions" +version = "10.7.1" +description = "Extension pack for Python Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pymdown_extensions-10.7.1-py3-none-any.whl", hash = "sha256:f5cc7000d7ff0d1ce9395d216017fa4df3dde800afb1fb72d1c7d3fd35e710f4"}, + {file = "pymdown_extensions-10.7.1.tar.gz", hash = "sha256:c70e146bdd83c744ffc766b4671999796aba18842b268510a329f7f64700d584"}, +] + +[package.dependencies] +markdown = ">=3.5" +pyyaml = "*" + +[package.extras] +extra = ["pygments (>=2.12)"] + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +description = "A custom YAML tag for referencing environment variables in YAML files. " +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] + +[package.dependencies] +pyyaml = "*" + +[[package]] +name = "regex" +version = "2023.12.25" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.7" +files = [ + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, + {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, + {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, + {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, + {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, + {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, + {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, + {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, + {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, + {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, + {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, + {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, + {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, + {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, + {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "watchdog" +version = "4.0.0" +description = "Filesystem events monitoring" +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b"}, + {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b"}, + {file = "watchdog-4.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b"}, + {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:45cc09cc4c3b43fb10b59ef4d07318d9a3ecdbff03abd2e36e77b6dd9f9a5c85"}, + {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eed82cdf79cd7f0232e2fdc1ad05b06a5e102a43e331f7d041e5f0e0a34a51c4"}, + {file = "watchdog-4.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba30a896166f0fee83183cec913298151b73164160d965af2e93a20bbd2ab605"}, + {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101"}, + {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca"}, + {file = "watchdog-4.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8"}, + {file = "watchdog-4.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b"}, + {file = "watchdog-4.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92"}, + {file = "watchdog-4.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269"}, + {file = "watchdog-4.0.0-py3-none-win32.whl", hash = "sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c"}, + {file = "watchdog-4.0.0-py3-none-win_amd64.whl", hash = "sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245"}, + {file = "watchdog-4.0.0-py3-none-win_ia64.whl", hash = "sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7"}, + {file = "watchdog-4.0.0.tar.gz", hash = "sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec"}, +] + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.11" +content-hash = "c78f673c91252f3784533b16eef005b2d4e8cd1de61e45f2e85153928e7448f4" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a21d4cd --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,28 @@ +[tool.poetry] +name = "dnidatetime" +version = "0.1.0" +description = "" +authors = ["Josh Washburne "] +readme = "README.md" +packages = [{include = "dnidatetime", from = "src"}] + +[tool.poetry.dependencies] +python = "^3.11" + +[tool.poetry.group.dev.dependencies] +pylint = "^3.1.0" +mypy = "^1.9.0" +black = "^24.3.0" +pytest = "^8.1.1" + +[tool.poetry.group.docs.dependencies] +mkdocs = "^1.5.3" +mkdocstrings = {extras = ["python"], version = "^0.24.1"} +mkdocs-material = "^9.5.14" + +[tool.pytest.ini_options] +pythonpath = "src" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/src/dnidatetime/__init__.py b/src/dnidatetime/__init__.py new file mode 100644 index 0000000..756e955 --- /dev/null +++ b/src/dnidatetime/__init__.py @@ -0,0 +1,7 @@ +from .dnitimedelta import DniTimedelta +from .dnidate import DniDate +from .dnitime import DniTime +from .dnidatetime import DniDatetime + + +__all__ = ["DniDate", "DniDatetime", "DniTime", "DniTimedelta"] diff --git a/src/dnidatetime/constants.py b/src/dnidatetime/constants.py new file mode 100644 index 0000000..60354fc --- /dev/null +++ b/src/dnidatetime/constants.py @@ -0,0 +1,120 @@ +"""Constants for all classes and functions with D'Ni datetimes.""" + +from collections import OrderedDict +from datetime import datetime, timezone +from typing import Tuple, TypedDict + + +DniDateTimeTuple = Tuple[int, int, int, int, int, int, int] + + +class DniUnitType(TypedDict): + """Stores D'Ni units of time. Used for type-checking purposes.""" + + min: int + max: int + total_pro: int + + +# All the units of time in the D'Ni culture from largest to smallest. +# +# Hahr - The D'Ni eqivalent of an earth year. +# Vailee - The D'Ni eqivalent of an earth month, 10 total in each hahr. +# Yahr - The D'Ni equivalent of an earth day, 29 total in each vailee. +# Gahrtahvo - 5 total in each yahr. +# Pahrtahvo - The D'Ni equivalent of an earth hour, 5 total in each gahrtahvo +# and 25 total in each yahr. Not used in calculations, but can +# be used to show a different method of D'Ni time. +# Tahvo - 5 total in each pahrtahvo and 25 total in each gahrtahvo. +# Gorahn - 25 total in each tahvo. +# Prorahn - The D'Ni equivalent of an earth second, 25 total in each gorahn. +# +# Limit: Maximum amount before rollover (ie. 60 seconds in 1 minute) +# Total Prorahntee: A "prorahn" is the smallest unit of time and is +# used in the calcuation of the other units. +DNI_UNITS: OrderedDict[str, DniUnitType] = OrderedDict( + [ + ("hahr", {"min": 7654, "max": 17655, "total_pro": 22656250}), + ("vailee", {"min": 1, "max": 10, "total_pro": 2265625}), + ("yahr", {"min": 1, "max": 29, "total_pro": 78125}), + ("gahrtahvo", {"min": 0, "max": 5, "total_pro": 15625}), + ("tahvo", {"min": 0, "max": 25, "total_pro": 625}), + ("gorahn", {"min": 0, "max": 25, "total_pro": 25}), + ("prorahn", {"min": 0, "max": 25, "total_pro": 1}), + ] +) + +# A hahr's length is equal to the Mean Solar Tropical Year for 1995 in +# milliseconds. +MS_PER_HAHR = 31556925216 + +# A prorahn's length in milliseconds. +# Some previous [wrong] calculations: +# JS = 1392.8573857142859 +# Python = 1392.8573888441379 +MS_PER_PRORAHN = 1392.85737275 + +# Just as the UNIX Epoch is based on midnight on 1-1-1970, the D'Ni Epoch +# is based on the timestamp of the original MYST Hypercard file: +# April 21, 1991 9:54AM PST (16:54 UTC) --> Leefo 1, 9647 00:00:00:00 +EARTH_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) +DNI_EPOCH_EDT = datetime(1991, 4, 21, 16, 54, 0, tzinfo=timezone.utc) # Earth +DNI_EPOCH_HAHR = 9647 + +# Converted leap seconds to ms, but adjusted for the UNIX epoch instead. +# https://data.iana.org/time-zones/data/leap-seconds.list +# delta_ms_1900_to_1970 = -2208988800000 +# leap_ms = leap_sec_from_ietf * 1000 + delta_ms_1900_to_1970 +LEAP_MS_FROM_EPOCH = [ + 63072000000, # 1 Jan 1972 + 78796800000, # 1 Jul 1972 + 94694400000, # 1 Jan 1973 + 126230400000, # 1 Jan 1974 + 157766400000, # 1 Jan 1975 + 189302400000, # 1 Jan 1976 + 220924800000, # 1 Jan 1977 + 252460800000, # 1 Jan 1978 + 283996800000, # 1 Jan 1979 + 315532800000, # 1 Jan 1980 + 362793600000, # 1 Jul 1981 + 394329600000, # 1 Jul 1982 + 425865600000, # 1 Jul 1983 + 489024000000, # 1 Jul 1985 + 567993600000, # 1 Jan 1988 + 631152000000, # 1 Jan 1990 + 662688000000, # 1 Jan 1991 + 709948800000, # 1 Jul 1992 + 741484800000, # 1 Jul 1993 + 773020800000, # 1 Jul 1994 + 820454400000, # 1 Jan 1996 + 867715200000, # 1 Jul 1997 + 915148800000, # 1 Jan 1999 + 1136073600000, # 1 Jan 2006 + 1230768000000, # 1 Jan 2009 + 1341100800000, # 1 Jul 2012 + 1435708800000, # 1 Jul 2015 + 1483228800000, # 1 Jan 2017 +] + +# DniDate.max.to_ordinal() +# (For whatever reason, 5119863 was the former value and I can't figure why. +# Noting it here in case I need it for something.) +MIN_DNI_ORDINAL = 1 +MAX_DNI_ORDINAL = 2900494 + +# The names of the 10 vailee in a hahr. +VAILEE_NAMES = [ + "Leefo", + "Leebro", + "Leesahn", + "Leetar", + "Leevot", + "Leevofo", + "Leevobro", + "Leevosahn", + "Leevotar", + "Leenovoo", +] + +# The number of yahrtee (days) before each consectutive vailee (month). +YAHRTEE_BEFORE_VAILEE = [-1, 0, 29, 58, 87, 116, 145, 174, 203, 232, 261] diff --git a/src/dnidatetime/conversions.py b/src/dnidatetime/conversions.py new file mode 100644 index 0000000..b4050a5 --- /dev/null +++ b/src/dnidatetime/conversions.py @@ -0,0 +1,152 @@ +"""Conversion functions for Earth/D'Ni datetimes & Ordinal/Julian dates.""" + +from datetime import datetime, timezone +from math import floor +from typing import Dict, Optional, Tuple + +from .constants import ( + DniDateTimeTuple, + DNI_EPOCH_EDT, + DNI_EPOCH_HAHR, + DNI_UNITS, + MAX_DNI_ORDINAL, + MIN_DNI_ORDINAL, + MS_PER_HAHR, + MS_PER_PRORAHN, + YAHRTEE_BEFORE_VAILEE +) + +from .utils import ( + check_dni_date_fields, + get_adj_ms_from_epoch, + get_ms_from_epoch, + is_timezone_aware +) + + +# Number of yahrtee (days) in a hahr (year) == 290. +_YAHR_IN_HAHR = DNI_UNITS["yahr"]["max"] * DNI_UNITS["vailee"]["max"] + + +def hvy2ord(hahr: int, vailee: int, yahr: int) -> int: + """The amount of yahrtee since Leefo 1, 7654 (DniDate.min).""" + ok_hahr, ok_vailee, ok_yahr = check_dni_date_fields(hahr, vailee, yahr) + yahr_before_hahr = (ok_hahr - DNI_UNITS["hahr"]["min"]) * _YAHR_IN_HAHR + return yahr_before_hahr + YAHRTEE_BEFORE_VAILEE[ok_vailee] + ok_yahr + + +def ord2hvy(ordinal: int) -> Tuple[int, int, int]: + """Find the exact hahr, vailee, and yahr from an ordinal value of + yahrtee. + """ + if not MIN_DNI_ORDINAL <= ordinal <= MAX_DNI_ORDINAL: + raise ValueError( + f"Ordinal must be in {MIN_DNI_ORDINAL}..{MAX_DNI_ORDINAL}", + ordinal, + ) + # TODO: I hate this with a firey passion, but it works. Needs a revamp at + # some point. + hahr, rem = divmod(ordinal, _YAHR_IN_HAHR) + hahr += DNI_UNITS["hahr"]["min"] + if rem == 0: + hahr -= 1 + vailee = DNI_UNITS["vailee"]["max"] + yahr = DNI_UNITS["yahr"]["max"] + else: + vailee, yahr = divmod( + rem + DNI_UNITS["yahr"]["max"], + DNI_UNITS["yahr"]["max"] + ) + if yahr == 0: + vailee -= 1 + yahr = DNI_UNITS["yahr"]["max"] + return hahr, vailee, yahr + + +def earth_to_dni( + date_time: Optional[datetime] = None +) -> DniDateTimeTuple: + """Converts an Earth gregorian datetime to a D'Ni datetime.""" + if date_time is None: + moment = datetime.now(timezone.utc) + else: + if is_timezone_aware(date_time): + moment = date_time.astimezone(timezone.utc) + else: + raise ValueError("Supplied datetime must be timezone aware.") + + dni_dt: Dict[str, int] = {} + + stamp = get_adj_ms_from_epoch(moment) + dni_epoch = get_adj_ms_from_epoch(DNI_EPOCH_EDT) + delta = float(stamp - dni_epoch) + + for name, details in DNI_UNITS.items(): + if name == "hahr": + dni_dt[name] = floor(delta / MS_PER_HAHR) + delta -= dni_dt[name] * MS_PER_HAHR + delta *= details["total_pro"] / MS_PER_HAHR + else: + dni_dt[name] = floor(delta / details["total_pro"]) + delta -= dni_dt[name] * details["total_pro"] + + # Correct potential value underflow (dates before the reference date) + temp_units = list(DNI_UNITS.items()) + rev_index = len(temp_units) - 1 + for index, unit in enumerate(reversed(temp_units)): + if dni_dt[unit[0]] < 0: + dni_dt[unit[0]] += unit[1]["max"] + dni_dt[temp_units[rev_index - index][0]] -= 1 + + # Add reference D'ni hahr + dni_dt["hahr"] += DNI_EPOCH_HAHR + + # Vailee and Yahr are not 0-based + dni_dt["vailee"] += 1 + dni_dt["yahr"] += 1 + + return ( + dni_dt["hahr"], + dni_dt["vailee"], + dni_dt["yahr"], + dni_dt["gahrtahvo"], + dni_dt["tahvo"], + dni_dt["gorahn"], + dni_dt["prorahn"], + ) + + +def dni_to_earth(dni_date_time: DniDateTimeTuple) -> datetime: + """Converts a D'Ni datetime to an Earth gregorian datetime.""" + + if not isinstance(dni_date_time, tuple): + raise ValueError( + ( + "D'Ni datetime must be in the form of a timetuple. " + "See DniDateTime.timetuple()." + ), + dni_date_time + ) + + if len(dni_date_time) != 7: + raise ValueError( + "The D'Ni timetuple is malformed. See DniDateTime.timetuple().", + dni_date_time + ) + + temp_units = list(dni_date_time) + + temp_units[0] -= DNI_EPOCH_HAHR + temp_units[1] -= 1 + temp_units[2] -= 1 + + total_prorahn = 0 + + for index, unit in enumerate(DNI_UNITS.items()): + total_prorahn += temp_units[index] * unit[1]["total_pro"] + + timestamp = total_prorahn * MS_PER_PRORAHN + epoch = get_ms_from_epoch(DNI_EPOCH_EDT) + date = timestamp + epoch + + return datetime.fromtimestamp(date / 1000.0, timezone.utc) diff --git a/src/dnidatetime/dnidate.py b/src/dnidatetime/dnidate.py new file mode 100644 index 0000000..6c98d36 --- /dev/null +++ b/src/dnidatetime/dnidate.py @@ -0,0 +1,183 @@ +"""The DniDate class.""" + +from typing import Any, Optional, TYPE_CHECKING, Union + +from .constants import DniDateTimeTuple, DNI_UNITS, MAX_DNI_ORDINAL +from .conversions import hvy2ord, ord2hvy +from .dnitimedelta import DniTimedelta +from .utils import check_dni_date_fields, cmp + + +class DniDate: + """A D'Ni date.""" + + __slots__ = "_hahr", "_vailee", "_yahr" + + if TYPE_CHECKING: + _hahr: int + _vailee: int + _yahr: int + + # Class properties + min: "DniDate" + max: "DniDate" + resolution: DniTimedelta + + def __new__( + cls, + hahr: int = DNI_UNITS["hahr"]["min"], + vailee: int = DNI_UNITS["vailee"]["min"], + yahr: int = DNI_UNITS["yahr"]["min"], + ) -> "DniDate": + if isinstance(hahr, int): + hahr, vailee, yahr = check_dni_date_fields(hahr, vailee, yahr) + self = object.__new__(cls) + self._hahr = hahr + self._vailee = vailee + self._yahr = yahr + return self + return NotImplemented + + @classmethod + def from_dnitimestamp(cls, prorahntee: int) -> "DniDate": + """Construct a D'Ni date from a D'Ni timestamp.""" + yahrtee = prorahntee // DNI_UNITS["yahr"]["total_pro"] + return cls.from_ordinal(yahrtee) + + @classmethod + def today(cls) -> "DniDate": + # TODO + return NotImplemented + + @classmethod + def from_isoformat(cls, date_str: str) -> "DniDate": + if not isinstance(date_str, str): + raise TypeError("from_isoformat: argument must be str.") + + try: + assert 8 <= len(date_str) <= 11 + parts = date_str.split("-") + if len(parts) != 3: + raise ValueError(f"Invalid isoformat string: {date_str!r}") + return cls(int(parts[0]), int(parts[1]), int(parts[2])) + except Exception as exc: + raise ValueError(f"Invalid isoformat string: {date_str!r}") from exc + + @classmethod + def from_ordinal(cls, yahrtee: int) -> "DniDate": + """Construct a D'ni date from a D'ni ordinal.""" + hahr, vailee, yahr = ord2hvy(yahrtee) + return cls(hahr, vailee, yahr) + + def to_ordinal(self) -> int: + """Return a D'ni ordinal for the hahr, vailee, and yahr.""" + return hvy2ord(self._hahr, self._vailee, self._yahr) + + def __repr__(self) -> str: + return ( + f"{self.__class__.__qualname__}" + f"({self._hahr}, " + f"{self._vailee}, " + f"{self._yahr})" + ) + + def isoformat(self) -> str: + """Return the date formatted according to ISO standard.""" + return f"{self._hahr}-{self._vailee:02}-{self._yahr:02}" + + __str__ = isoformat + + # Read-only field accessors + @property + def hahr(self) -> int: + """hahr""" + return self._hahr + + @property + def vailee(self) -> int: + """vailee""" + return self._vailee + + @property + def yahr(self) -> int: + """yahr""" + return self._yahr + + def replace( + self, + hahr: Optional[int] = None, + vailee: Optional[int] = None, + yahr: Optional[int] = None, + ) -> "DniDate": + """Return a new D'ni date with new values for the specified fields.""" + if hahr is None: + hahr = self._hahr + if vailee is None: + vailee = self._vailee + if yahr is None: + yahr = self._yahr + return type(self)(hahr, vailee, yahr) + + # Arithmetic + def __add__(self, other: Any) -> "DniDate": + if isinstance(other, DniTimedelta): + ordinal = self.to_ordinal() + other._yahrtee + if 0 < ordinal <= MAX_DNI_ORDINAL: + return type(self).from_ordinal(ordinal) + raise OverflowError("Result out of range.") + return NotImplemented + + __radd__ = __add__ + + def __sub__(self, other: Any) -> Union["DniDate", DniTimedelta]: + if isinstance(other, DniTimedelta): + return self + DniTimedelta(-other._yahrtee) + if isinstance(other, DniDate): + yahrtee1 = self.to_ordinal() + yahrtee2 = other.to_ordinal() + return DniTimedelta(yahrtee1 - yahrtee2) + return NotImplemented + + # Rich comparisons + def _cmp(self, other: Any) -> int: + """Helper function for rich comparison of DniDate objects.""" + # pylint: disable=protected-access + assert isinstance(other, DniDate) + hahr1, vailee1, yahr1 = self._hahr, self._vailee, self._yahr + hahr2, vailee2, yahr2 = other._hahr, other._vailee, other._yahr + return cmp((hahr1, vailee1, yahr1), (hahr2, vailee2, yahr2)) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, DniDate): + return self._cmp(other) == 0 + return NotImplemented + + def __le__(self, other: Any) -> bool: + if isinstance(other, DniDate): + return self._cmp(other) <= 0 + return NotImplemented + + def __lt__(self, other: Any) -> bool: + if isinstance(other, DniDate): + return self._cmp(other) < 0 + return NotImplemented + + def __ge__(self, other: Any) -> bool: + if isinstance(other, DniDate): + return self._cmp(other) >= 0 + return NotImplemented + + def __gt__(self, other: Any) -> bool: + if isinstance(other, DniDate): + return self._cmp(other) > 0 + return NotImplemented + + def dnitimetuple(self) -> DniDateTimeTuple: + """Local time tuple compatible with dnitime.localtime().""" + # TODO + return NotImplemented + + +DniDate.min = DniDate(7654, 1, 1) +DniDate.max = DniDate(17655, 8, 1) +DniDate.resolution = DniTimedelta(yahrtee=1) diff --git a/src/dnidatetime/dnidatetime.py b/src/dnidatetime/dnidatetime.py new file mode 100644 index 0000000..367e218 --- /dev/null +++ b/src/dnidatetime/dnidatetime.py @@ -0,0 +1,319 @@ +"""The DniDatetime class.""" + +from datetime import datetime, timedelta, timezone +from typing import Any, Optional, TYPE_CHECKING, Union + +from .constants import ( + DniDateTimeTuple, + DNI_EPOCH_HAHR, + DNI_UNITS, + MAX_DNI_ORDINAL +) +from .conversions import dni_to_earth, earth_to_dni, hvy2ord +from .dnidate import DniDate +from .dnitime import DniTime +from .dnitimedelta import DniTimedelta +from .utils import check_dni_date_fields, check_dni_time_fields, cmp + + +class DniDatetime: + """A D'ni timestamp.""" + + __slots__ = DniDate.__slots__ + DniTime.__slots__ + + if TYPE_CHECKING: + _hahr: int + _vailee: int + _yahr: int + _gahrtahvo: int + _tahvo: int + _gorahn: int + _prorahn: int + + # Class properties + min: "DniDatetime" + max: "DniDatetime" + resolution: DniTimedelta + + def __new__( + cls, + hahr: int = DNI_UNITS["hahr"]["min"], + vailee: int = DNI_UNITS["vailee"]["min"], + yahr: int = DNI_UNITS["yahr"]["min"], + gahrtahvo: int = DNI_UNITS["gahrtahvo"]["min"], + tahvo: int = DNI_UNITS["tahvo"]["min"], + gorahn: int = DNI_UNITS["gorahn"]["min"], + prorahn: int = DNI_UNITS["prorahn"]["min"], + ) -> "DniDatetime": + if isinstance(hahr, int): + hahr, vailee, yahr = check_dni_date_fields(hahr, vailee, yahr) + gahrtahvo, tahvo, gorahn, prorahn = check_dni_time_fields( + gahrtahvo, tahvo, gorahn, prorahn + ) + self = object.__new__(cls) + self._hahr = hahr + self._vailee = vailee + self._yahr = yahr + self._gahrtahvo = gahrtahvo + self._tahvo = tahvo + self._gorahn = gorahn + self._prorahn = prorahn + return self + return NotImplemented + + def __repr__(self) -> str: + return ( + f"{self.__class__.__qualname__}" + f"({self._hahr}, " + f"{self._vailee}, " + f"{self._yahr}, " + f"{self._gahrtahvo}, " + f"{self._tahvo}, " + f"{self._gorahn}, " + f"{self._prorahn})" + ) + + def isoformat(self) -> str: + """Return the datetime formatted according to ISO standard.""" + return ( + f"{self._hahr}-{self._vailee:02}-{self._yahr:02} " + f"{self._gahrtahvo}:{self._tahvo:02}:" + f"{self._gorahn:02}:{self._prorahn:02}" + ) + + __str__ = isoformat + + # Read-only field accessors + @property + def hahr(self) -> int: + """hahr""" + return self._hahr + + @property + def vailee(self) -> int: + """vailee""" + return self._vailee + + @property + def yahr(self) -> int: + """yahr""" + return self._yahr + + @property + def gahrtahvo(self) -> int: + """gahrtahvo""" + return self._gahrtahvo + + @property + def tahvo(self) -> int: + """tahvo""" + return self._tahvo + + @property + def gorahn(self) -> int: + """gorahn""" + return self._gorahn + + @property + def prorahn(self) -> int: + """prorahn""" + return self._prorahn + + def replace( + self, + hahr: Optional[int] = None, + vailee: Optional[int] = None, + yahr: Optional[int] = None, + gahrtahvo: Optional[int] = None, + tahvo: Optional[int] = None, + gorahn: Optional[int] = None, + prorahn: Optional[int] = None, + ) -> "DniDatetime": + """Return a new D'ni datetime with new values for the specified fields.""" + if hahr is None: + hahr = self._hahr + if vailee is None: + vailee = self._vailee + if yahr is None: + yahr = self._yahr + if gahrtahvo is None: + gahrtahvo = self._gahrtahvo + if tahvo is None: + tahvo = self._tahvo + if gorahn is None: + gorahn = self._gorahn + if prorahn is None: + prorahn = self._prorahn + return type(self)(hahr, vailee, yahr, gahrtahvo, tahvo, gorahn, prorahn) + + # Arithmetic + def __add__(self, other: Any) -> "DniDatetime": + if not isinstance(other, (timedelta, DniTimedelta)): + return NotImplemented + if isinstance(other, timedelta): + other = DniTimedelta.from_timedelta(other) + delta = DniTimedelta( + self.to_ordinal(), + gahrtahvotee=self._gahrtahvo, + tahvotee=self._tahvo, + gorahntee=self._gorahn, + prorahntee=self._prorahn, + ) + delta += other + total_prorahn = delta.pahrtahvotee * 3125 + delta.prorahntee + gahrtahvo, rem = divmod(total_prorahn, 15625) + tahvo, rem = divmod(rem, 625) + gorahn, prorahn = divmod(rem, 25) + if 0 < delta.yahrtee <= MAX_DNI_ORDINAL: + return type(self).combine( + DniDate.from_ordinal(delta.yahrtee), + DniTime(gahrtahvo, tahvo, gorahn, prorahn), + ) + raise OverflowError("result out of range") + + __radd__ = __add__ + + def __sub__(self, other: Any) -> Union["DniDatetime", DniTimedelta]: + if not isinstance(other, DniDatetime): + if isinstance(other, timedelta): + return self + -other + else: + yahrtee1 = self.to_ordinal() + yahrtee2 = other.to_ordinal() + prorahntee1 = ( + self._gahrtahvo * 15625 + + self._tahvo * 625 + + self._gorahn * 25 + + self._prorahn + ) + prorahntee2 = ( + other._gahrtahvo * 15625 + + other._tahvo * 625 + + other._gorahn * 25 + + other._prorahn + ) + return DniTimedelta(yahrtee1 - yahrtee2, prorahntee1 - prorahntee2) + return NotImplemented + + # Rich comparisons + def _cmp(self, other: Any) -> int: + """Helper function for rich comparison of DniDatetime objects.""" + # pylint: disable=protected-access + assert isinstance(other, DniDatetime) + return cmp( + ( + self._hahr, + self._vailee, + self._yahr, + self._gahrtahvo, + self._tahvo, + self._gorahn, + self._prorahn, + ), + ( + other._hahr, + other._vailee, + other._yahr, + other._gahrtahvo, + other._tahvo, + other._gorahn, + other._prorahn, + ), + ) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, DniDatetime): + return self._cmp(other) == 0 + if not isinstance(other, DniDate): + return NotImplemented + return False + + def __le__(self, other: Any) -> bool: + if isinstance(other, DniDatetime): + return self._cmp(other) <= 0 + if not isinstance(other, DniDate): + return NotImplemented + return NotImplemented + + def __lt__(self, other: Any) -> bool: + if isinstance(other, DniDatetime): + return self._cmp(other) < 0 + if not isinstance(other, DniDate): + return NotImplemented + return NotImplemented + + def __ge__(self, other: Any) -> bool: + if isinstance(other, DniDatetime): + return self._cmp(other) >= 0 + if not isinstance(other, DniDate): + return NotImplemented + return NotImplemented + + def __gt__(self, other: Any) -> bool: + if isinstance(other, DniDatetime): + return self._cmp(other) > 0 + if not isinstance(other, DniDate): + return NotImplemented + return NotImplemented + + def date(self) -> DniDate: + """Return the date part of the DniDatetime.""" + return DniDate(self._hahr, self._vailee, self._yahr) + + def time(self) -> DniTime: + """Return the time part of the DniDatetime.""" + return DniTime(self._gahrtahvo, self._tahvo, self._gorahn, self._prorahn) + + def timestamp(self) -> int: + """Return total number of prorahntee since the D'ni epoch.""" + return (self - DNI_EPOCH_DDT).total_prorahntee() #type: ignore + + def timetuple(self) -> DniDateTimeTuple: + """Return the DniDateTime in the form of a timetuple.""" + return ( + self._hahr, + self._vailee, + self._yahr, + self._gahrtahvo, + self._tahvo, + self._gorahn, + self._prorahn + ) + + def to_ordinal(self) -> int: + """Return a D'ni ordinal for the hahr, vailee, and yahr.""" + return hvy2ord(self._hahr, self._vailee, self._yahr) + + @classmethod + def combine(cls, date_obj: DniDate, time_obj: DniTime) -> "DniDatetime": + """Construct a D'ni datetime from a given DniDate and DniTime.""" + # pylint: disable=protected-access + if not isinstance(date_obj, DniDate): + raise TypeError("date_obj argument must be a DniDate instance.") + if not isinstance(time_obj, DniTime): + raise TypeError("time_obj argument must be a DniTime instance.") + return cls( + date_obj._hahr, + date_obj._vailee, + date_obj._yahr, + time_obj._gahrtahvo, + time_obj._tahvo, + time_obj._gorahn, + time_obj._prorahn, + ) + + def to_earth(self, tz: Optional[timezone] = timezone.utc) -> datetime: + """Create an earth datetime from a DniDatetime.""" + return dni_to_earth(self.timetuple()).astimezone(tz) + + @classmethod + def from_earth(cls, date_time: datetime) -> "DniDatetime": + """Create a DniDatetime from an earth datetime.""" + ddt = earth_to_dni(date_time) + return cls(*ddt) + + +DniDatetime.min = DniDatetime(7654, 1, 1) +DniDatetime.max = DniDatetime(17655, 8, 1, 4, 24, 24, 24) +DniDatetime.resolution = DniTimedelta(prorahntee=1) +DNI_EPOCH_DDT = DniDatetime(DNI_EPOCH_HAHR, 1, 1, 0, 0, 0, 0) diff --git a/src/dnidatetime/dnitime.py b/src/dnidatetime/dnitime.py new file mode 100644 index 0000000..da0eb7d --- /dev/null +++ b/src/dnidatetime/dnitime.py @@ -0,0 +1,154 @@ +"""The DniTime class.""" + +from typing import Any, Optional, TYPE_CHECKING + +from .constants import DNI_UNITS +from .dnitimedelta import DniTimedelta +from .utils import check_dni_time_fields, cmp + + +class DniTime: + """A D'ni representation of a timestamp.""" + + __slots__ = "_gahrtahvo", "_tahvo", "_gorahn", "_prorahn" + + if TYPE_CHECKING: + _gahrtahvo: int + _tahvo: int + _gorahn: int + _prorahn: int + + # Class properties + min: "DniTime" + max: "DniTime" + resolution: DniTimedelta + + def __new__( + cls, + gahrtahvo: int = DNI_UNITS["gahrtahvo"]["min"], + tahvo: int = DNI_UNITS["tahvo"]["min"], + gorahn: int = DNI_UNITS["gorahn"]["min"], + prorahn: int = DNI_UNITS["prorahn"]["min"], + ) -> "DniTime": + gahrtahvo, tahvo, gorahn, prorahn = check_dni_time_fields( + gahrtahvo, tahvo, gorahn, prorahn + ) + self = object.__new__(cls) + self._gahrtahvo = gahrtahvo + self._tahvo = tahvo + self._gorahn = gorahn + self._prorahn = prorahn + return self + + @classmethod + def from_isoformat(cls, time_str: str) -> "DniTime": + if not isinstance(time_str, str): + raise TypeError("from_isoformat: argument must be str.") + + try: + assert 7 <= len(time_str) <= 11 + parts = time_str.split(":") + if len(parts) != 4: + raise ValueError(f"Invalid isoformat string: {time_str!r}") + return cls(int(parts[0]), int(parts[1]), int(parts[2]), int(parts[3])) + except Exception as exc: + raise ValueError(f"Invalid isoformat string: {time_str!r}") from exc + + def __repr__(self) -> str: + return ( + f"{self.__class__.__qualname__}" + f"({self._gahrtahvo}, " + f"{self._tahvo}, " + f"{self._gorahn}, " + f"{self._prorahn})" + ) + + def isoformat(self) -> str: + """Return the date formatted according to ISO standard.""" + return ( + f"{self._gahrtahvo}:{self._tahvo:02}:" + f"{self._gorahn:02}:{self._prorahn:02}" + ) + + __str__ = isoformat + + # Read-only field accessors + @property + def gahrtahvo(self) -> int: + """gahrtahvo""" + return self._gahrtahvo + + @property + def tahvo(self) -> int: + """tahvo""" + return self._tahvo + + @property + def gorahn(self) -> int: + """gorahn""" + return self._gorahn + + @property + def prorahn(self) -> int: + """prorahn""" + return self._prorahn + + def replace( + self, + gahrtahvo: Optional[int] = None, + tahvo: Optional[int] = None, + gorahn: Optional[int] = None, + prorahn: Optional[int] = None, + ) -> "DniTime": + """Return a new D'ni time with new values for the specified fields.""" + if gahrtahvo is None: + gahrtahvo = self._gahrtahvo + if tahvo is None: + tahvo = self._tahvo + if gorahn is None: + gorahn = self._gorahn + if prorahn is None: + prorahn = self._prorahn + return type(self)(gahrtahvo, tahvo, gorahn, prorahn) + + # Rich comparisons + def _cmp(self, other: Any) -> int: + """Helper function for rich comparison of dnidate objects.""" + # pylint: disable=protected-access + assert isinstance(other, DniTime) + mygopro = self._gorahn * 25 + self._prorahn + othergopro = self._gorahn * 25 + self._prorahn + return cmp( + (self._gahrtahvo, self._tahvo, mygopro), + (other._gahrtahvo, other._tahvo, othergopro), + ) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, DniTime): + return self._cmp(other) == 0 + return NotImplemented + + def __le__(self, other: Any) -> bool: + if isinstance(other, DniTime): + return self._cmp(other) <= 0 + return NotImplemented + + def __lt__(self, other: Any) -> bool: + if isinstance(other, DniTime): + return self._cmp(other) < 0 + return NotImplemented + + def __ge__(self, other: Any) -> bool: + if isinstance(other, DniTime): + return self._cmp(other) >= 0 + return NotImplemented + + def __gt__(self, other: Any) -> bool: + if isinstance(other, DniTime): + return self._cmp(other) > 0 + return NotImplemented + + +DniTime.min = DniTime(0, 0, 0, 0) +DniTime.max = DniTime(4, 24, 24, 24) +DniTime.resolution = DniTimedelta(prorahntee=1) diff --git a/src/dnidatetime/dnitimedelta.py b/src/dnidatetime/dnitimedelta.py new file mode 100644 index 0000000..8b38d77 --- /dev/null +++ b/src/dnidatetime/dnitimedelta.py @@ -0,0 +1,320 @@ +"""The DniTimedelta class.""" + +from datetime import timedelta +from math import modf +from typing import Any, Tuple, TYPE_CHECKING, Union + +from .constants import DNI_UNITS, MS_PER_PRORAHN +from .utils import cmp, divide_and_round + + +class DniTimedelta: + """A D'ni timedelta, modelled after the Python datetime.timedelta class.""" + + __slots__ = "_yahrtee", "_pahrtahvotee", "_prorahntee" + + if TYPE_CHECKING: + _yahrtee: int + _pahrtahvotee: int + _prorahntee: int + + # Class properties + min: "DniTimedelta" + max: "DniTimedelta" + resolution: "DniTimedelta" + + def __new__( + cls, + yahrtee: Union[float, int] = 0, + pahrtahvotee: Union[float, int] = 0, + prorahntee: Union[float, int] = 0, + gorahntee: Union[float, int] = 0, + tahvotee: Union[float, int] = 0, + gahrtahvotee: Union[float, int] = 0, + vaileetee: Union[float, int] = 0, + ) -> "DniTimedelta": + # Final values are integer. + yahr: int = 0 + pahr: int = 0 + pro: int = 0 + + # Normalize + yahrtee += vaileetee * DNI_UNITS["yahr"]["max"] + pahrtahvotee += gahrtahvotee * 5 + prorahntee += (gorahntee * DNI_UNITS["prorahn"]["max"]) + ( + tahvotee * DNI_UNITS["gorahn"]["max"] * DNI_UNITS["prorahn"]["max"] + ) + + # Remove fractions: yahrtee + if isinstance(yahrtee, float): + yahr_frac, yahrtee = modf(yahrtee) + yahr_pahr_frac, yahr_pahr_whole = modf(yahr_frac * 25.0) + assert yahr_pahr_whole == int(yahr_pahr_whole) + pahr = int(yahr_pahr_whole) + assert yahrtee == int(yahrtee) + yahr = int(yahrtee) + else: + yahr_pahr_frac = 0.0 + yahr = yahrtee + assert isinstance(yahr_pahr_frac, float) + assert abs(yahr_pahr_frac) <= 1.0 + assert isinstance(yahr, int) + assert abs(pahr) <= 25 + + # Remove fractions: pahrtahvotee + if isinstance(pahrtahvotee, float): + pahr_frac, pahrtahvotee = modf(pahrtahvotee) + assert pahrtahvotee == int(pahrtahvotee) + pahrtahvotee = int(pahrtahvotee) + pahr_frac += yahr_pahr_frac + assert abs(pahr_frac) <= 2.0 + else: + pahr_frac = yahr_pahr_frac + assert isinstance(pahr_frac, float) + assert abs(pahr_frac) <= 2.0 + + assert isinstance(pahrtahvotee, int) + yahrtee, pahrtahvotee = divmod(pahrtahvotee, 25) + yahr += yahrtee + pahr += int(pahrtahvotee) + assert isinstance(pahr, int) + assert abs(pahr) <= 2 * 25 + + # Remove fractions: prorahntee + pro_double = pahr_frac * 3125.0 + assert abs(pro_double) < 2 * 3125.0 + + if isinstance(prorahntee, float): + prorahntee = round(prorahntee + pro_double) + pahrtahvotee, prorahntee = divmod(prorahntee, 3125) + yahrtee, pahrtahvotee = divmod(pahrtahvotee, 25) + yahr += yahrtee + pahr += pahrtahvotee + else: + prorahntee = int(prorahntee) + pahrtahvotee, prorahntee = divmod(prorahntee, 3125) + yahrtee, pahrtahvotee = divmod(pahrtahvotee, 25) + yahr += yahrtee + pahr += pahrtahvotee + prorahntee = round(prorahntee + pro_double) + assert isinstance(pahr, int) + assert isinstance(prorahntee, int) + assert abs(pahr) <= 3 * 25 + assert abs(prorahntee) < 3 * 3125.0 + + # Last bit of normalization + pahrtahvotee, pro = divmod(prorahntee, 3125) + pahr += pahrtahvotee + yahrtee, pahr = divmod(pahr, 25) + yahr += yahrtee + + assert isinstance(yahr, int) + assert isinstance(pahr, int) and 0 <= pahr < 25 + assert isinstance(pro, int) and 0 <= pro < 3125 + + if abs(yahr) > 999999999: + raise OverflowError(f"DniTimedelta # of yahrtee is too large: {yahr}") + + self = object.__new__(cls) + self._yahrtee = yahr + self._pahrtahvotee = pahr + self._prorahntee = pro + return self + + def __repr__(self) -> str: + args = [] + if self._yahrtee: + args.append(f"yahrtee={self._yahrtee}") + if self._pahrtahvotee: + args.append(f"pahrtahvotee={self._pahrtahvotee}") + if self._prorahntee: + args.append(f"prorahntee={self._prorahntee}") + if not args: + args.append("0") + return f"{self.__class__.__qualname__}" f"({', '.join(args)})" + + def __str__(self) -> str: + prorahn = self._pahrtahvotee * 3125 + self._prorahntee + go, pro = divmod(prorahn, 25) + tahvo, go = divmod(go, 25) + pahr, tahvo = divmod(tahvo, 5) + output = f"{pahr}:{tahvo:02}:{go:02}:{pro:02}" + if self._yahrtee: + plural = "tee" if self._yahrtee != 1 else "" + output = f"{self._yahrtee} yahr{plural}, " + output + return output + + def total_prorahntee(self) -> int: + """Total number of prorahntee in the duration.""" + return (self._yahrtee * 78125) + (self._pahrtahvotee * 3125) + self._prorahntee + + # Read-only field accessors + @property + def yahrtee(self) -> int: + """yahrtee""" + return self._yahrtee + + @property + def pahrtahvotee(self) -> int: + """pahrtahvotee""" + return self._pahrtahvotee + + @property + def prorahntee(self) -> int: + """prorahntee""" + return self._prorahntee + + # Arithmetic functions + def __add__(self, other: Any) -> "DniTimedelta": + if isinstance(other, (timedelta, DniTimedelta)): + if isinstance(other, timedelta): + other = self.from_timedelta(other) + return DniTimedelta( + self._yahrtee + other._yahrtee, + self._pahrtahvotee + other._pahrtahvotee, + self._prorahntee + other._prorahntee, + ) + return NotImplemented + + __radd__ = __add__ + + def __sub__(self, other: Any) -> "DniTimedelta": + if isinstance(other, (timedelta, DniTimedelta)): + if isinstance(other, timedelta): + other = self.from_timedelta(other) + return DniTimedelta( + self._yahrtee - other._yahrtee, + self._pahrtahvotee - other._pahrtahvotee, + self._prorahntee - other._prorahntee, + ) + return NotImplemented + + def __rsub__(self, other: Any) -> "DniTimedelta": + if isinstance(other, (timedelta, DniTimedelta)): + if isinstance(other, timedelta): + other = self.from_timedelta(other) + return -self + other + return NotImplemented + + def __neg__(self) -> "DniTimedelta": + return DniTimedelta(-self._yahrtee, -self._pahrtahvotee, -self._prorahntee) + + def __pos__(self) -> "DniTimedelta": + return self + + def __abs__(self) -> "DniTimedelta": + if self._yahrtee < 0: + return -self + return self + + def __mul__(self, other: Any) -> "DniTimedelta": + if isinstance(other, int): + return DniTimedelta( + self._yahrtee * other, + self._pahrtahvotee * other, + self._prorahntee * other, + ) + if isinstance(other, float): + pro = self.total_prorahntee() + numer, denom = other.as_integer_ratio() + return DniTimedelta(0, 0, divide_and_round(pro * numer, denom)) + return NotImplemented + + __rmul__ = __mul__ + + def __floordiv__(self, other: Any) -> Union["DniTimedelta", int]: + if not isinstance(other, (int, DniTimedelta)): + return NotImplemented + pro = self.total_prorahntee() + if isinstance(other, DniTimedelta): + return pro // other.total_prorahntee() + if isinstance(other, int): + return DniTimedelta(0, 0, pro // other) + + def __truediv__(self, other: Any) -> Union["DniTimedelta", float]: + if not isinstance(other, (float, int, DniTimedelta)): + return NotImplemented + pro = self.total_prorahntee() + if isinstance(other, DniTimedelta): + return pro / other.total_prorahntee() + if isinstance(other, float): + numer, denom = other.as_integer_ratio() + return DniTimedelta(0, 0, divide_and_round(denom * pro, numer)) + if isinstance(other, int): + return DniTimedelta(0, 0, divide_and_round(pro, other)) + + def __mod__(self, other: Any) -> "DniTimedelta": + if isinstance(other, DniTimedelta): + rem = self.total_prorahntee() % other.total_prorahntee() + return DniTimedelta(0, 0, rem) + return NotImplemented + + def __divmod__(self, other: Any) -> Tuple[int, "DniTimedelta"]: + if isinstance(other, DniTimedelta): + quot, rem = divmod(self.total_prorahntee(), other.total_prorahntee()) + return quot, DniTimedelta(0, 0, rem) + return NotImplemented + + # Rich comparisons + def _cmp(self, other: Any) -> int: + """Helper function for rich comparison of DniTimedelta objects.""" + assert isinstance(other, DniTimedelta) + return cmp(self.total_prorahntee(), other.total_prorahntee()) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, DniTimedelta): + return self._cmp(other) == 0 + return NotImplemented + + def __le__(self, other: Any) -> bool: + if isinstance(other, DniTimedelta): + return self._cmp(other) <= 0 + return NotImplemented + + def __lt__(self, other: Any) -> bool: + if isinstance(other, DniTimedelta): + return self._cmp(other) < 0 + return NotImplemented + + def __ge__(self, other: Any) -> bool: + if isinstance(other, DniTimedelta): + return self._cmp(other) >= 0 + return NotImplemented + + def __gt__(self, other: Any) -> bool: + if isinstance(other, DniTimedelta): + return self._cmp(other) > 0 + return NotImplemented + + def __bool__(self) -> bool: + return self._yahrtee != 0 or self._pahrtahvotee != 0 or self._prorahntee != 0 + + @classmethod + def from_timedelta(cls, delta: timedelta) -> "DniTimedelta": + """Converts a normal timedelta to a D'Ni timedelta.""" + prorahntee = delta.total_seconds() * 1000 / MS_PER_PRORAHN + return cls(prorahntee=prorahntee) + + def to_timedelta(self) -> timedelta: + """Converts the D'Ni timedelta to a normal timedelta.""" + milliseconds = self.total_prorahntee() * MS_PER_PRORAHN + try: + delta = timedelta(milliseconds=milliseconds) + return delta + except OverflowError as exc: + # This is needed for precision issues in conversion. + if self >= self.max: + return timedelta.max + if self <= self.min: + return timedelta.min + raise exc + + +# Converted from timedelta.min +DniTimedelta.min = DniTimedelta(yahrtee=-793993715, pahrtahvotee=17, prorahntee=768) + +# Converted from timedelta.max +DniTimedelta.max = DniTimedelta(yahrtee=793993715, pahrtahvotee=2, prorahntee=1887) + +# Smallest measurement of change. +DniTimedelta.resolution = DniTimedelta(prorahntee=1) diff --git a/src/dnidatetime/utils.py b/src/dnidatetime/utils.py new file mode 100644 index 0000000..8134bfc --- /dev/null +++ b/src/dnidatetime/utils.py @@ -0,0 +1,137 @@ +"""Helper functions for all classes/functions of the module.""" + +# pylint: disable=line-too-long + +from datetime import datetime, timedelta, timezone +from typing import Any, Optional, Tuple, Union + +from .constants import DNI_UNITS, EARTH_EPOCH, LEAP_MS_FROM_EPOCH + + +def cmp(x: Any, y: Any) -> int: + """Compare objects and return: + + x < y -> -1 + x == y -> 0 + x > y -> 1 + """ + return 0 if x == y else 1 if x > y else -1 + + +def divide_and_round(a: Union[float, int], b: Union[float, int]) -> int: + """Divide a by b and round result to the nearest integer. When the ratio + is exactly half-way between two integers, the even integer is returned. + """ + quot, rem = divmod(a, b) + rem *= 2 + greater_than_half = rem > b if b > 0 else rem < b + if greater_than_half or rem == b and quot % 2 == 1: + quot += 1 + + return int(quot) + + +def check_dni_date_fields( + hahr: int = DNI_UNITS["hahr"]["min"], + vailee: int = DNI_UNITS["vailee"]["min"], + yahr: int = DNI_UNITS["yahr"]["min"], +) -> Tuple[int, int, int]: + """Verifies the D'Ni date fields are within proper boundaries.""" + if not DNI_UNITS["hahr"]["min"] <= hahr <= DNI_UNITS["hahr"]["max"]: + raise ValueError( + f"Hahr must be in {DNI_UNITS['hahr']['min']}..{DNI_UNITS['hahr']['max']}", + hahr, + ) + if not DNI_UNITS["vailee"]["min"] <= vailee <= DNI_UNITS["vailee"]["max"]: + raise ValueError( + f"Vailee must be in {DNI_UNITS['vailee']['min']}..{DNI_UNITS['vailee']['max']}", + vailee, + ) + if not DNI_UNITS["yahr"]["min"] <= yahr <= DNI_UNITS["yahr"]["max"]: + raise ValueError( + f"Yahr must be in {DNI_UNITS['yahr']['min']}..{DNI_UNITS['yahr']['max']}", + yahr, + ) + return hahr, vailee, yahr + + +def check_dni_time_fields( + gahrtahvo: int = DNI_UNITS["gahrtahvo"]["min"], + tahvo: int = DNI_UNITS["tahvo"]["min"], + gorahn: int = DNI_UNITS["gorahn"]["min"], + prorahn: int = DNI_UNITS["prorahn"]["min"], +) -> Tuple[int, int, int, int]: + """Verifies the D'Ni time fields are within proper boundaries.""" + if ( + not DNI_UNITS["gahrtahvo"]["min"] + <= gahrtahvo + <= DNI_UNITS["gahrtahvo"]["max"] - 1 + ): + raise ValueError( + f"Gahrtahvo must be in {DNI_UNITS['gahrtahvo']['min']}..{DNI_UNITS['gahrtahvo']['max'] - 1}", + gahrtahvo, + ) + if not DNI_UNITS["tahvo"]["min"] <= tahvo <= DNI_UNITS["tahvo"]["max"] - 1: + raise ValueError( + f"Tahvo must be in {DNI_UNITS['tahvo']['min']}..{DNI_UNITS['tahvo']['max'] - 1}", + tahvo, + ) + if ( + not DNI_UNITS["gorahn"]["min"] + <= gorahn + <= DNI_UNITS["gorahn"]["max"] - 1 + ): + raise ValueError( + f"Gorahn must be in {DNI_UNITS['gorahn']['min']}..{DNI_UNITS['gorahn']['max'] - 1}", + gorahn, + ) + if ( + not DNI_UNITS["prorahn"]["min"] + <= prorahn + <= DNI_UNITS["prorahn"]["max"] - 1 + ): + raise ValueError( + f"Prorahn must be in {DNI_UNITS['prorahn']['min']}..{DNI_UNITS['prorahn']['max'] - 1}", + prorahn, + ) + return gahrtahvo, tahvo, gorahn, prorahn + + +def add_leap_seconds(timestamp: int) -> int: + """Adds leap seconds to a timestamp.""" + leap_seconds = 0 + for leap in LEAP_MS_FROM_EPOCH: + if timestamp >= leap: + leap_seconds += 1 + + # Adjust for the 10 leap seconds that started in 1972. + if leap_seconds > 0: + leap_seconds += 9 + + return timestamp + (leap_seconds * 1000) + + +def get_ms_from_epoch(date_time: Optional[datetime] = None) -> int: + """ + Returns the amount of milliseconds since the UNIX epoch (1-1-1970). + """ + # Thanks to the examples from here: + # https://stackoverflow.com/questions/5395872/ + if not date_time: + date = datetime.now(timezone.utc) + else: + date = date_time.replace(tzinfo=timezone.utc) + return ((date - EARTH_EPOCH) // timedelta(microseconds=1)) // 1000 + + +def get_adj_ms_from_epoch(date_time: Optional[datetime] = None) -> int: + """ + Returns the amount of milliseconds since the UNIX epoch (1-1-1970), + but also accounting for leap seconds. + """ + return add_leap_seconds(get_ms_from_epoch(date_time)) + + +def is_timezone_aware(dt: datetime) -> bool: + """Returns whether a datetime object has timezone info or not.""" + return dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_conversions.py b/tests/test_conversions.py new file mode 100644 index 0000000..ae39bdc --- /dev/null +++ b/tests/test_conversions.py @@ -0,0 +1,93 @@ +"""Tests for the helper functions in conversions.py""" + +from datetime import datetime, timezone + +import pytest + +from dnidatetime.conversions import ( + hvy2ord, + ord2hvy, + earth_to_dni, + dni_to_earth +) + + +def test_hvy2ord_8765_4_29(): + assert hvy2ord(8765, 4, 29) == 322306 + + +def test_hvy2ord_8765_5_1(): + assert hvy2ord(8765, 5, 1) == hvy2ord(8765, 4, 29) + 1 + + +def test_hvy2ord_8765_10_29(): + assert hvy2ord(8765, 10, 29) == 322480 + + +def test_hvy2ord_8766_1_1(): + assert hvy2ord(8766, 1, 1) == hvy2ord(8765, 10, 29) + 1 + + +def test_ord2hvy_too_low(): + with pytest.raises(ValueError, match="Ordinal"): + ord2hvy(-1) + + +def test_ord2hvy_too_high(): + with pytest.raises(ValueError, match="Ordinal"): + ord2hvy(2900500) + + +def test_ord2hvy_322306(): + assert ord2hvy(322306) == (8765, 4, 29) + + +def test_ord2hvy_322307(): + assert ord2hvy(322307) == (8765, 5, 1) + + +def test_ord2hvy_322480(): + assert ord2hvy(322480) == (8765, 10, 29) + + +def test_ord2hvy_322481(): + assert ord2hvy(322481) == (8766, 1, 1) + + +def test_earth_to_dni_no_timezone(): + with pytest.raises(ValueError, match="timezone"): + earth_to_dni(datetime.now()) + + +def test_earth_to_dni_dniepoch(): + assert earth_to_dni( + datetime(1991, 4, 21, 16, 54, 0, tzinfo=timezone.utc) + ) == (9647, 1, 1, 0, 0, 0, 0) + + +def test_earth_to_dni_2020(): + assert earth_to_dni( + datetime(2020, 8, 27, 10, 51, 0, tzinfo=timezone.utc) + ) == (9676, 4, 16, 1, 0, 20, 5) + + +def test_dni_to_earth_not_tuple(): + with pytest.raises(ValueError, match="timetuple"): + dni_to_earth([9647, 1, 1, 0, 0, 0, 0]) + + +def test_dni_to_earth_bad_tuple(): + with pytest.raises(ValueError, match="malformed"): + dni_to_earth((9647, 1, 1)) + + +def test_dni_to_earth_dniepoch(): + assert dni_to_earth( + (9647, 1, 1, 0, 0, 0, 0) + ) == datetime(1991, 4, 21, 16, 54, tzinfo=timezone.utc) + + +def test_dni_to_earth_2020(): + assert dni_to_earth( + (9676, 4, 16, 1, 0, 20, 5) + ) == datetime(2020, 8, 27, 10, 50, 59, 668172, tzinfo=timezone.utc) diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..1625fa4 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,154 @@ +"""Tests for the helper functions in util.py""" + +from datetime import datetime + +import pytest + +from dnidatetime.utils import ( + cmp, + divide_and_round, + check_dni_date_fields, + check_dni_time_fields, + add_leap_seconds, + get_ms_from_epoch, + get_adj_ms_from_epoch, + is_timezone_aware +) + + +def test_cmp_lessthan(): + assert cmp(1, 10) == -1 + + +def test_cmp_equal(): + assert cmp(5, 5) == 0 + + +def test_cmp_greaterthan(): + assert cmp(10, 1) == 1 + + +def test_dar_down(): + assert divide_and_round(5, 2) == 2 + + +def test_dar_up(): + assert divide_and_round(7, 2) == 4 + + +def test_dar_no_rem(): + assert divide_and_round(6, 2) == 3 + + +def test_check_dnidate_hahr_low(): + with pytest.raises(ValueError, match="Hahr"): + check_dni_date_fields(7000, 1, 1) + + +def test_check_dnidate_hahr_high(): + with pytest.raises(ValueError, match="Hahr"): + check_dni_date_fields(20000, 1, 1) + + +def test_check_dnidate_vailee_low(): + with pytest.raises(ValueError, match="Vailee"): + check_dni_date_fields(7654, -1, 1) + + +def test_check_dnidate_vailee_high(): + with pytest.raises(ValueError, match="Vailee"): + check_dni_date_fields(7654, 12, 1) + + +def test_check_dnidate_yahr_low(): + with pytest.raises(ValueError, match="Yahr"): + check_dni_date_fields(7654, 1, -1) + + +def test_check_dnidate_yahr_high(): + with pytest.raises(ValueError, match="Yahr"): + check_dni_date_fields(7654, 1, 31) + + +def test_check_dnitime_gahrtahvo_low(): + with pytest.raises(ValueError, match="Gahrtahvo"): + check_dni_time_fields(-1, 0, 0, 0) + + +def test_check_dnitime_gahrtahvo_high(): + with pytest.raises(ValueError, match="Gahrtahvo"): + check_dni_time_fields(10, 0, 0, 0) + + +def test_check_dnitime_tahvo_low(): + with pytest.raises(ValueError, match="Tahvo"): + check_dni_time_fields(0, -1, 0, 0) + + +def test_check_dnitime_tahvo_high(): + with pytest.raises(ValueError, match="Tahvo"): + check_dni_time_fields(0, 30, 0, 0) + + +def test_check_dnitime_gorahn_low(): + with pytest.raises(ValueError, match="Gorahn"): + check_dni_time_fields(0, 0, -1, 0) + + +def test_check_dnitime_gorahn_high(): + with pytest.raises(ValueError, match="Gorahn"): + check_dni_time_fields(0, 0, 30, 0) + + +def test_check_dnitime_prorahn_low(): + with pytest.raises(ValueError, match="Prorahn"): + check_dni_time_fields(0, 0, 0, -1) + + +def test_check_dnitime_prorahn_high(): + with pytest.raises(ValueError, match="Prorahn"): + check_dni_time_fields(0, 0, 0, 30) + + +def test_add_leap_seconds_epoch(): + assert add_leap_seconds(0) == 0 + + +def test_add_leap_seconds_1984(): + assert add_leap_seconds(455328000000) == 455328022000 + + +def test_add_leap_seconds_2020(): + assert add_leap_seconds(1598486400000) == 1598486437000 + + +def test_get_ms_from_epoch_base(): + assert get_ms_from_epoch(datetime(1970, 1, 1)) == 0 + + +def test_get_ms_from_epoch_1984(): + assert get_ms_from_epoch(datetime(1984, 6, 6)) == 455328000000 + + +def test_get_ms_from_epoch_2020(): + assert get_ms_from_epoch(datetime(2020, 8, 27)) == 1598486400000 + + +def test_get_adj_ms_from_epoch_1959(): + assert get_adj_ms_from_epoch(datetime(1959, 1, 17)) == -345772800000 + + +def test_get_adj_ms_from_epoch_1981(): + assert get_adj_ms_from_epoch(datetime(1981, 9, 14)) == 369273620000 + + +def test_get_adj_ms_from_epoch_2023(): + assert get_adj_ms_from_epoch(datetime(2023, 12, 8)) == 1701993637000 + + +def test_is_timezone_aware_true(): + assert is_timezone_aware(datetime.now().astimezone()) is True + + +def test_is_timezone_aware_false(): + assert is_timezone_aware(datetime.now()) is False