mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-04-20 00:10:03 +02:00
Initial commit
This commit is contained in:
parent
cda7955934
commit
b67c0f9da3
17
.flake8
Normal file
17
.flake8
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[flake8]
|
||||||
|
ignore = E501,W503,FS003,F403,F405,E203
|
||||||
|
include = src
|
||||||
|
max-line-length = 88
|
||||||
|
max-doc-length = 88
|
||||||
|
# mccabe cyclomatic complexity
|
||||||
|
max-complexity = 7
|
||||||
|
# flake8-expression-complexity
|
||||||
|
max-expression-complexity = 5.5
|
||||||
|
# flake8-class-attributes-order
|
||||||
|
use_class_attributes_order_strict_mode=True
|
||||||
|
# flake8-eradicate
|
||||||
|
# this
|
||||||
|
eradicate_whitelist_extend = "#openapi.yaml#"
|
||||||
|
per-file-ignores =
|
||||||
|
services/*/generated/*: F401,W505
|
||||||
|
services/*/on_service_ready.py: F401,E800
|
140
.gitignore
vendored
Normal file
140
.gitignore
vendored
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
settings.json
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__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/
|
22
.vscode/launch.json
vendored
Normal file
22
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "foo",
|
||||||
|
"justMyCode": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bar",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "bar",
|
||||||
|
"justMyCode": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
674
poetry.lock
generated
Normal file
674
poetry.lock
generated
Normal file
@ -0,0 +1,674 @@
|
|||||||
|
# This file is automatically @generated by Poetry and should not be changed by hand.
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "attrs"
|
||||||
|
version = "23.1.0"
|
||||||
|
description = "Classes Without Boilerplate"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"},
|
||||||
|
{file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
cov = ["attrs[tests]", "coverage[toml] (>=5.3)"]
|
||||||
|
dev = ["attrs[docs,tests]", "pre-commit"]
|
||||||
|
docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"]
|
||||||
|
tests = ["attrs[tests-no-zope]", "zope-interface"]
|
||||||
|
tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "black"
|
||||||
|
version = "23.3.0"
|
||||||
|
description = "The uncompromising code formatter."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"},
|
||||||
|
{file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"},
|
||||||
|
{file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"},
|
||||||
|
{file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"},
|
||||||
|
{file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"},
|
||||||
|
{file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"},
|
||||||
|
{file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"},
|
||||||
|
{file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"},
|
||||||
|
{file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"},
|
||||||
|
{file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"},
|
||||||
|
{file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"},
|
||||||
|
{file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"},
|
||||||
|
{file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"},
|
||||||
|
{file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"},
|
||||||
|
{file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"},
|
||||||
|
{file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"},
|
||||||
|
{file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"},
|
||||||
|
{file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"},
|
||||||
|
{file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"},
|
||||||
|
{file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"},
|
||||||
|
{file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"},
|
||||||
|
{file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"},
|
||||||
|
{file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"},
|
||||||
|
{file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"},
|
||||||
|
{file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[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)"]
|
||||||
|
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||||
|
uvloop = ["uvloop (>=0.15.2)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "click"
|
||||||
|
version = "8.1.3"
|
||||||
|
description = "Composable command line interface toolkit"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
|
||||||
|
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorama"
|
||||||
|
version = "0.4.6"
|
||||||
|
description = "Cross-platform colored terminal text."
|
||||||
|
category = "main"
|
||||||
|
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 = "coverage"
|
||||||
|
version = "7.2.7"
|
||||||
|
description = "Code coverage measurement for Python"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"},
|
||||||
|
{file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"},
|
||||||
|
{file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
toml = ["tomli"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "eradicate"
|
||||||
|
version = "2.3.0"
|
||||||
|
description = "Removes commented-out code."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "eradicate-2.3.0-py3-none-any.whl", hash = "sha256:2b29b3dd27171f209e4ddd8204b70c02f0682ae95eecb353f10e8d72b149c63e"},
|
||||||
|
{file = "eradicate-2.3.0.tar.gz", hash = "sha256:06df115be3b87d0fc1c483db22a2ebb12bcf40585722810d809cc770f5031c37"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flake8"
|
||||||
|
version = "5.0.4"
|
||||||
|
description = "the modular source code checker: pep8 pyflakes and co"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6.1"
|
||||||
|
files = [
|
||||||
|
{file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"},
|
||||||
|
{file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
mccabe = ">=0.7.0,<0.8.0"
|
||||||
|
pycodestyle = ">=2.9.0,<2.10.0"
|
||||||
|
pyflakes = ">=2.5.0,<2.6.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flake8-comprehensions"
|
||||||
|
version = "3.13.0"
|
||||||
|
description = "A flake8 plugin to help you write better list/set/dict comprehensions."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "flake8_comprehensions-3.13.0-py3-none-any.whl", hash = "sha256:cc0d6dbb336ff4e9cdf4eb605a3f719ea59261f2d6ba52034871a173c40e1f60"},
|
||||||
|
{file = "flake8_comprehensions-3.13.0.tar.gz", hash = "sha256:83cf98e816c9e23360f36aaf47de59a5b21437fdff8a056c46e2ad49f81861bf"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
flake8 = ">=3.0,<3.2.0 || >3.2.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flake8-eradicate"
|
||||||
|
version = "1.5.0"
|
||||||
|
description = "Flake8 plugin to find commented out code"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8,<4.0"
|
||||||
|
files = [
|
||||||
|
{file = "flake8_eradicate-1.5.0-py3-none-any.whl", hash = "sha256:18acc922ad7de623f5247c7d5595da068525ec5437dd53b22ec2259b96ce9d22"},
|
||||||
|
{file = "flake8_eradicate-1.5.0.tar.gz", hash = "sha256:aee636cb9ecb5594a7cd92d67ad73eb69909e5cc7bd81710cf9d00970f3983a6"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
attrs = "*"
|
||||||
|
eradicate = ">=2.0,<3.0"
|
||||||
|
flake8 = ">5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flake8-functions"
|
||||||
|
version = "0.0.7"
|
||||||
|
description = "A flake8 extension that checks functions"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "flake8_functions-0.0.7-py3-none-any.whl", hash = "sha256:f2f75545c2b0df9eeba0ad316e2ac38c101676970b4441300fc07af3226a44f6"},
|
||||||
|
{file = "flake8_functions-0.0.7.tar.gz", hash = "sha256:40584b05d57e5ab185545bcfa08aa0edca52b04646d0df266e2b1667d6437184"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
mr-proper = "*"
|
||||||
|
setuptools = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flake8-pep585"
|
||||||
|
version = "0.1.7"
|
||||||
|
description = "flake8 plugin to enforce new-style type hints (PEP 585)"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7,<4.0"
|
||||||
|
files = [
|
||||||
|
{file = "flake8-pep585-0.1.7.tar.gz", hash = "sha256:363f9413aa12849ee9bfdc437c4e79cc4e0fb3af4abbb61cfed79860e349e0e0"},
|
||||||
|
{file = "flake8_pep585-0.1.7-py3-none-any.whl", hash = "sha256:d5c7a5858382d6ca8c56554bd8bed090e12c378b98f6d7c6502abed9a40a658e"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flake8-pep604"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "flake8 plugin to enforce use of `|` over `typing.Union`"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "flake8_pep604-0.1.0-py2.py3-none-any.whl", hash = "sha256:87449930c00c50dfac4b2d3dcb847b49507a05fb8b888dc973dd338df350ca81"},
|
||||||
|
{file = "flake8_pep604-0.1.0.tar.gz", hash = "sha256:38d8852ac6a7c33a0d863841154fdc055128713f2f78d13e9664ac277374c4f2"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
flake8 = ">=3.8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flake8-use-fstring"
|
||||||
|
version = "1.4"
|
||||||
|
description = "Flake8 plugin for string formatting style."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "flake8-use-fstring-1.4.tar.gz", hash = "sha256:6550bf722585eb97dffa8343b0f1c372101f5c4ab5b07ebf0edd1c79880cdd39"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
flake8 = ">=3"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
ci = ["coverage (>=4.0.0,<5.0.0)", "coveralls", "flake8-builtins", "flake8-commas", "flake8-fixme", "flake8-print", "flake8-quotes", "flake8-todo", "pytest (>=4)", "pytest-cov (>=2)"]
|
||||||
|
dev = ["coverage (>=4.0.0,<5.0.0)", "flake8-builtins", "flake8-commas", "flake8-fixme", "flake8-print", "flake8-quotes", "flake8-todo", "pytest (>=4)", "pytest-cov (>=2)"]
|
||||||
|
test = ["coverage (>=4.0.0,<5.0.0)", "flake8-builtins", "flake8-commas", "flake8-fixme", "flake8-print", "flake8-quotes", "flake8-todo", "pytest (>=4)", "pytest-cov (>=2)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iniconfig"
|
||||||
|
version = "2.0.0"
|
||||||
|
description = "brain-dead simple config-ini parsing"
|
||||||
|
category = "dev"
|
||||||
|
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.12.0"
|
||||||
|
description = "A Python utility / library to sort Python imports."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8.0"
|
||||||
|
files = [
|
||||||
|
{file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"},
|
||||||
|
{file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
colors = ["colorama (>=0.4.3)"]
|
||||||
|
pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"]
|
||||||
|
plugins = ["setuptools"]
|
||||||
|
requirements-deprecated-finder = ["pip-api", "pipreqs"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "loguru"
|
||||||
|
version = "0.7.0"
|
||||||
|
description = "Python logging made (stupidly) simple"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.5"
|
||||||
|
files = [
|
||||||
|
{file = "loguru-0.7.0-py3-none-any.whl", hash = "sha256:b93aa30099fa6860d4727f1b81f8718e965bb96253fa190fab2077aaad6d15d3"},
|
||||||
|
{file = "loguru-0.7.0.tar.gz", hash = "sha256:1612053ced6ae84d7959dd7d5e431a0532642237ec21f7fd83ac73fe539e03e1"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
|
||||||
|
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["Sphinx (==5.3.0)", "colorama (==0.4.5)", "colorama (==0.4.6)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v0.990)", "pre-commit (==3.2.1)", "pytest (==6.1.2)", "pytest (==7.2.1)", "pytest-cov (==2.12.1)", "pytest-cov (==4.0.0)", "pytest-mypy-plugins (==1.10.1)", "pytest-mypy-plugins (==1.9.3)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.2.0)", "tox (==3.27.1)", "tox (==4.4.6)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mccabe"
|
||||||
|
version = "0.7.0"
|
||||||
|
description = "McCabe checker, plugin for flake8"
|
||||||
|
category = "dev"
|
||||||
|
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 = "mr-proper"
|
||||||
|
version = "0.0.7"
|
||||||
|
description = "Static Python code analyzer, that tries to check if functions in code are pure or not and why."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "mr_proper-0.0.7-py3-none-any.whl", hash = "sha256:74a1b60240c46f10ba518707ef72811a01e5c270da0a78b5dd2dd923d99fdb14"},
|
||||||
|
{file = "mr_proper-0.0.7.tar.gz", hash = "sha256:03b517b19e617537f711ce418b125e5f2efd82ec881539cdee83195c78c14a02"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
click = ">=7.1.2"
|
||||||
|
setuptools = "*"
|
||||||
|
stdlib-list = ">=0.5.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mypy"
|
||||||
|
version = "1.4.1"
|
||||||
|
description = "Optional static typing for Python"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "mypy-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:566e72b0cd6598503e48ea610e0052d1b8168e60a46e0bfd34b3acf2d57f96a8"},
|
||||||
|
{file = "mypy-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca637024ca67ab24a7fd6f65d280572c3794665eaf5edcc7e90a866544076878"},
|
||||||
|
{file = "mypy-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dde1d180cd84f0624c5dcaaa89c89775550a675aff96b5848de78fb11adabcd"},
|
||||||
|
{file = "mypy-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8c4d8e89aa7de683e2056a581ce63c46a0c41e31bd2b6d34144e2c80f5ea53dc"},
|
||||||
|
{file = "mypy-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:bfdca17c36ae01a21274a3c387a63aa1aafe72bff976522886869ef131b937f1"},
|
||||||
|
{file = "mypy-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7549fbf655e5825d787bbc9ecf6028731973f78088fbca3a1f4145c39ef09462"},
|
||||||
|
{file = "mypy-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98324ec3ecf12296e6422939e54763faedbfcc502ea4a4c38502082711867258"},
|
||||||
|
{file = "mypy-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141dedfdbfe8a04142881ff30ce6e6653c9685b354876b12e4fe6c78598b45e2"},
|
||||||
|
{file = "mypy-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8207b7105829eca6f3d774f64a904190bb2231de91b8b186d21ffd98005f14a7"},
|
||||||
|
{file = "mypy-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:16f0db5b641ba159eff72cff08edc3875f2b62b2fa2bc24f68c1e7a4e8232d01"},
|
||||||
|
{file = "mypy-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:470c969bb3f9a9efcedbadcd19a74ffb34a25f8e6b0e02dae7c0e71f8372f97b"},
|
||||||
|
{file = "mypy-1.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5952d2d18b79f7dc25e62e014fe5a23eb1a3d2bc66318df8988a01b1a037c5b"},
|
||||||
|
{file = "mypy-1.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:190b6bab0302cec4e9e6767d3eb66085aef2a1cc98fe04936d8a42ed2ba77bb7"},
|
||||||
|
{file = "mypy-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9d40652cc4fe33871ad3338581dca3297ff5f2213d0df345bcfbde5162abf0c9"},
|
||||||
|
{file = "mypy-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01fd2e9f85622d981fd9063bfaef1aed6e336eaacca00892cd2d82801ab7c042"},
|
||||||
|
{file = "mypy-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2460a58faeea905aeb1b9b36f5065f2dc9a9c6e4c992a6499a2360c6c74ceca3"},
|
||||||
|
{file = "mypy-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2746d69a8196698146a3dbe29104f9eb6a2a4d8a27878d92169a6c0b74435b6"},
|
||||||
|
{file = "mypy-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae704dcfaa180ff7c4cfbad23e74321a2b774f92ca77fd94ce1049175a21c97f"},
|
||||||
|
{file = "mypy-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:43d24f6437925ce50139a310a64b2ab048cb2d3694c84c71c3f2a1626d8101dc"},
|
||||||
|
{file = "mypy-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c482e1246726616088532b5e964e39765b6d1520791348e6c9dc3af25b233828"},
|
||||||
|
{file = "mypy-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43b592511672017f5b1a483527fd2684347fdffc041c9ef53428c8dc530f79a3"},
|
||||||
|
{file = "mypy-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34a9239d5b3502c17f07fd7c0b2ae6b7dd7d7f6af35fbb5072c6208e76295816"},
|
||||||
|
{file = "mypy-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5703097c4936bbb9e9bce41478c8d08edd2865e177dc4c52be759f81ee4dd26c"},
|
||||||
|
{file = "mypy-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e02d700ec8d9b1859790c0475df4e4092c7bf3272a4fd2c9f33d87fac4427b8f"},
|
||||||
|
{file = "mypy-1.4.1-py3-none-any.whl", hash = "sha256:45d32cec14e7b97af848bddd97d85ea4f0db4d5a149ed9676caa4eb2f7402bb4"},
|
||||||
|
{file = "mypy-1.4.1.tar.gz", hash = "sha256:9bbcd9ab8ea1f2e1c8031c21445b511442cc45c89951e49bbf852cbb70755b1b"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
mypy-extensions = ">=1.0.0"
|
||||||
|
typing-extensions = ">=4.1.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dmypy = ["psutil (>=4.0)"]
|
||||||
|
install-types = ["pip"]
|
||||||
|
python2 = ["typed-ast (>=1.4.0,<2)"]
|
||||||
|
reports = ["lxml"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mypy-extensions"
|
||||||
|
version = "1.0.0"
|
||||||
|
description = "Type system extensions for programs checked with the mypy type checker."
|
||||||
|
category = "dev"
|
||||||
|
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 = "23.1"
|
||||||
|
description = "Core utilities for Python packages"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
|
||||||
|
{file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathspec"
|
||||||
|
version = "0.11.1"
|
||||||
|
description = "Utility library for gitignore style pattern matching of file paths."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"},
|
||||||
|
{file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "platformdirs"
|
||||||
|
version = "3.8.0"
|
||||||
|
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "platformdirs-3.8.0-py3-none-any.whl", hash = "sha256:ca9ed98ce73076ba72e092b23d3c93ea6c4e186b3f1c3dad6edd98ff6ffcca2e"},
|
||||||
|
{file = "platformdirs-3.8.0.tar.gz", hash = "sha256:b0cabcb11063d21a0b261d557acb0a9d2126350e63b70cdf7db6347baea456dc"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
|
||||||
|
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pluggy"
|
||||||
|
version = "1.2.0"
|
||||||
|
description = "plugin and hook calling mechanisms for python"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"},
|
||||||
|
{file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["pre-commit", "tox"]
|
||||||
|
testing = ["pytest", "pytest-benchmark"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plumbum"
|
||||||
|
version = "1.8.2"
|
||||||
|
description = "Plumbum: shell combinators library"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "plumbum-1.8.2-py3-none-any.whl", hash = "sha256:3ad9e5f56c6ec98f6f7988f7ea8b52159662ea9e915868d369dbccbfca0e367e"},
|
||||||
|
{file = "plumbum-1.8.2.tar.gz", hash = "sha256:9e6dc032f4af952665f32f3206567bc23b7858b1413611afe603a3f8ad9bfd75"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
pywin32 = {version = "*", markers = "platform_system == \"Windows\" and platform_python_implementation != \"PyPy\""}
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["paramiko", "psutil", "pytest (>=6.0)", "pytest-cov", "pytest-mock", "pytest-timeout"]
|
||||||
|
docs = ["sphinx (>=4.0.0)", "sphinx-rtd-theme (>=1.0.0)"]
|
||||||
|
ssh = ["paramiko"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pycodestyle"
|
||||||
|
version = "2.9.1"
|
||||||
|
description = "Python style guide checker"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"},
|
||||||
|
{file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyflakes"
|
||||||
|
version = "2.5.0"
|
||||||
|
description = "passive checker of Python programs"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"},
|
||||||
|
{file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest"
|
||||||
|
version = "7.4.0"
|
||||||
|
description = "pytest: simple powerful testing with Python"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"},
|
||||||
|
{file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||||
|
iniconfig = "*"
|
||||||
|
packaging = "*"
|
||||||
|
pluggy = ">=0.12,<2.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest-cov"
|
||||||
|
version = "4.1.0"
|
||||||
|
description = "Pytest plugin for measuring coverage."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"},
|
||||||
|
{file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
coverage = {version = ">=5.2.1", extras = ["toml"]}
|
||||||
|
pytest = ">=4.6"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pywin32"
|
||||||
|
version = "306"
|
||||||
|
description = "Python for Window Extensions"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"},
|
||||||
|
{file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"},
|
||||||
|
{file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"},
|
||||||
|
{file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"},
|
||||||
|
{file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"},
|
||||||
|
{file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"},
|
||||||
|
{file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"},
|
||||||
|
{file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"},
|
||||||
|
{file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"},
|
||||||
|
{file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"},
|
||||||
|
{file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"},
|
||||||
|
{file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"},
|
||||||
|
{file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"},
|
||||||
|
{file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rpyc"
|
||||||
|
version = "5.3.1"
|
||||||
|
description = "Remote Python Call (RPyC) is a transparent and symmetric distributed computing library"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "rpyc-5.3.1-py3-none-any.whl", hash = "sha256:6e8153792ac221a80f420d2e0b241a10c6e43b105d325998b18a4e7af329f9ec"},
|
||||||
|
{file = "rpyc-5.3.1.tar.gz", hash = "sha256:f2233174879faf18ae266437d5a65511ce46c817cec4edc1344f036758cfbf52"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
plumbum = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "setuptools"
|
||||||
|
version = "68.0.0"
|
||||||
|
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"},
|
||||||
|
{file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
|
||||||
|
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||||
|
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stdlib-list"
|
||||||
|
version = "0.9.0"
|
||||||
|
description = "A list of Python Standard Libraries (2.7 through 3.9)."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "stdlib_list-0.9.0-py3-none-any.whl", hash = "sha256:f79957d59e41930d44afcd81e465f740b9a7a9828707a40e24ab1092b12bd423"},
|
||||||
|
{file = "stdlib_list-0.9.0.tar.gz", hash = "sha256:98eb66135976c96b4ee3f4c0ef0552ebb5a9977ce3028433db79f4738b02af26"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["build", "stdlib-list[doc,lint,test]"]
|
||||||
|
doc = ["furo", "sphinx"]
|
||||||
|
lint = ["black", "mypy", "ruff"]
|
||||||
|
support = ["sphobjinv"]
|
||||||
|
test = ["coverage[toml]", "pytest", "pytest-cov"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typing-extensions"
|
||||||
|
version = "4.7.1"
|
||||||
|
description = "Backported and Experimental Type Hints for Python 3.7+"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"},
|
||||||
|
{file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "win32-setctime"
|
||||||
|
version = "1.1.0"
|
||||||
|
description = "A small Python utility to set file creation time on Windows"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.5"
|
||||||
|
files = [
|
||||||
|
{file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
|
||||||
|
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
lock-version = "2.0"
|
||||||
|
python-versions = "^3.11"
|
||||||
|
content-hash = "bf97d78cee39148ec209f0c2056783ba41c0b6aa3937e1ef15f45cc6b10e5dd1"
|
2
poetry.toml
Normal file
2
poetry.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[virtualenvs]
|
||||||
|
in-project = true
|
72
pyproject.toml
Normal file
72
pyproject.toml
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
[tool.poetry]
|
||||||
|
name = "pyDataInterface"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = ""
|
||||||
|
authors = ["Mose Mueller <mosmuell@ethz.ch>"]
|
||||||
|
readme = "README.md"
|
||||||
|
packages = [{include = "pyDataInterface", from = "src"}]
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.11"
|
||||||
|
rpyc = "^5.3.1"
|
||||||
|
loguru = "^0.7.0"
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
pytest = "^7.4.0"
|
||||||
|
pytest-cov = "^4.1.0"
|
||||||
|
mypy = "^1.4.1"
|
||||||
|
black = "^23.1.0"
|
||||||
|
isort = "^5.12.0"
|
||||||
|
flake8 = "^5.0.4"
|
||||||
|
flake8-use-fstring = "^1.4"
|
||||||
|
flake8-functions = "^0.0.7"
|
||||||
|
flake8-comprehensions = "^3.11.1"
|
||||||
|
flake8-pep585 = "^0.1.7"
|
||||||
|
flake8-pep604 = "^0.1.0"
|
||||||
|
flake8-eradicate = "^1.4.0"
|
||||||
|
|
||||||
|
[tool.setuptools.package-data]
|
||||||
|
pyDataInterface = ['frontend/*', 'examples/*', 'version.json']
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
|
[tool.pyright]
|
||||||
|
include = ["pyDataInterface", "tests"]
|
||||||
|
exclude = ["**/node_modules",
|
||||||
|
"**/__pycache__",
|
||||||
|
"docs",
|
||||||
|
"frontend",
|
||||||
|
]
|
||||||
|
venvPath = "."
|
||||||
|
venv = ".venv"
|
||||||
|
typeCheckingMode = "basic"
|
||||||
|
reportUnknownMemberType = true
|
||||||
|
|
||||||
|
[tool.black]
|
||||||
|
line-length = 88
|
||||||
|
exclude = '''
|
||||||
|
/(
|
||||||
|
\.git
|
||||||
|
| \.mypy_cache
|
||||||
|
| \.tox
|
||||||
|
| venv
|
||||||
|
| \.venv
|
||||||
|
| _build
|
||||||
|
| buck-out
|
||||||
|
| build
|
||||||
|
| dist
|
||||||
|
)/
|
||||||
|
'''
|
||||||
|
|
||||||
|
[tool.isort]
|
||||||
|
profile = "black"
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
show_error_codes = true
|
||||||
|
disallow_untyped_defs = true
|
||||||
|
disallow_untyped_calls = true
|
||||||
|
disallow_incomplete_defs = true
|
||||||
|
check_untyped_defs = true
|
||||||
|
ignore_missing_imports = false
|
3
src/pyDataInterface/__init__.py
Normal file
3
src/pyDataInterface/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from .data_service import DataService
|
||||||
|
|
||||||
|
__all__ = ["DataService"]
|
0
src/pyDataInterface/client.py
Normal file
0
src/pyDataInterface/client.py
Normal file
7
src/pyDataInterface/data_service/__init__.py
Normal file
7
src/pyDataInterface/data_service/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from .data_service import DataService
|
||||||
|
from .data_service_list import DataServiceList
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"DataService",
|
||||||
|
"DataServiceList",
|
||||||
|
]
|
337
src/pyDataInterface/data_service/data_service.py
Normal file
337
src/pyDataInterface/data_service/data_service.py
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
import asyncio
|
||||||
|
import inspect
|
||||||
|
import threading
|
||||||
|
from collections.abc import Callable
|
||||||
|
from concurrent.futures import Future
|
||||||
|
from typing import Any, cast
|
||||||
|
|
||||||
|
import rpyc
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from .data_service_list import DataServiceList
|
||||||
|
|
||||||
|
|
||||||
|
class DataService(rpyc.Service):
|
||||||
|
_full_access_path: set[str]
|
||||||
|
""" TODO: improve this docstring
|
||||||
|
A set of strings, each representing a unique path to access the attribute from an
|
||||||
|
exposed class instance. Each path starts with the name of the exposed class. It's
|
||||||
|
dynamically updated to accurately represent the current attribute structure.
|
||||||
|
This attribute is used to emit notifications to a web server whenever the attribute
|
||||||
|
changes, allowing for real-time tracking and updates of class instance
|
||||||
|
modifications.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
--------
|
||||||
|
>>> class SubClass(DataService):
|
||||||
|
>>> pass
|
||||||
|
|
||||||
|
>>> class ExposedClass(DataService):
|
||||||
|
>>> attr = SubClass()
|
||||||
|
|
||||||
|
>>> service = ExposedClass()
|
||||||
|
>>> # ... expose class
|
||||||
|
|
||||||
|
>>> print(service.attr._full_access_path) # {"ServiceClass.attr"}
|
||||||
|
|
||||||
|
Have a look at tests/test_full_access_path.py to see more examples.
|
||||||
|
"""
|
||||||
|
_list_mapping: dict[int, DataServiceList] = {}
|
||||||
|
"""
|
||||||
|
A dictionary mapping the id of the original lists to the corresponding
|
||||||
|
DataServiceList instances.
|
||||||
|
This is used to ensure that all references to the same list within the DataService
|
||||||
|
object point to the same DataServiceList, so that any modifications to that list can
|
||||||
|
be tracked consistently. The keys of the dictionary are the ids of the original
|
||||||
|
lists, and the values are the DataServiceList instances that wrap these lists.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
# dictionary to keep track of running tasks
|
||||||
|
self.__tasks: dict[str, Future[None]] = {}
|
||||||
|
self._autostart_tasks: dict[str, tuple[Any]]
|
||||||
|
if "_autostart_tasks" not in self.__dict__:
|
||||||
|
self._autostart_tasks = {}
|
||||||
|
|
||||||
|
self._set_start_and_stop_for_async_methods()
|
||||||
|
|
||||||
|
self._start_async_loop_in_thread()
|
||||||
|
self._start_autostart_tasks()
|
||||||
|
|
||||||
|
self._update_full_access_path(self, f"{self.__class__.__name__}")
|
||||||
|
self._turn_lists_into_notify_lists()
|
||||||
|
self._do_something_with_properties()
|
||||||
|
self._initialised = True
|
||||||
|
|
||||||
|
def _do_something_with_properties(self) -> None:
|
||||||
|
for attr_name in dir(self.__class__):
|
||||||
|
attr_value = getattr(self.__class__, attr_name)
|
||||||
|
if isinstance(attr_value, property): # If attribute is a property
|
||||||
|
logger.debug(attr_value.fget.__code__.co_names)
|
||||||
|
|
||||||
|
def _turn_lists_into_notify_lists(self) -> None:
|
||||||
|
def create_callback(attr_name: str) -> Callable:
|
||||||
|
"""TODO: explain what this is used for...
|
||||||
|
Create a callback with current attr_name captured in the default argument.
|
||||||
|
|
||||||
|
Default arguments solve the late binding problem by capturing the value at
|
||||||
|
the time the lambda is defined, not when it is called, thus preventing
|
||||||
|
attr_name from being overwritten in another loop iteratianother
|
||||||
|
"""
|
||||||
|
|
||||||
|
return lambda index, value, attr_name=attr_name: self._emit(
|
||||||
|
access_path=self._full_access_path,
|
||||||
|
name=f"{attr_name}[{index}]",
|
||||||
|
value=value,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Convert all list attributes (both class and instance) to DataServiceList
|
||||||
|
for attr_name in set(dir(self)) - set(dir(object)):
|
||||||
|
attr_value = getattr(self, attr_name)
|
||||||
|
|
||||||
|
if isinstance(attr_value, list):
|
||||||
|
# Create callback for current attr_name
|
||||||
|
callback = create_callback(attr_name)
|
||||||
|
|
||||||
|
# Check if attr_value is already a DataServiceList or in the mapping
|
||||||
|
if isinstance(attr_value, DataServiceList):
|
||||||
|
attr_value.add_callback(callback)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if id(attr_value) in self._list_mapping:
|
||||||
|
notifying_list = self._list_mapping[id(attr_value)]
|
||||||
|
notifying_list.add_callback(callback)
|
||||||
|
else:
|
||||||
|
notifying_list = DataServiceList(attr_value, callback=[callback])
|
||||||
|
self._list_mapping[id(attr_value)] = notifying_list
|
||||||
|
|
||||||
|
setattr(self, attr_name, notifying_list)
|
||||||
|
|
||||||
|
def _start_autostart_tasks(self) -> None:
|
||||||
|
if self._autostart_tasks is not None:
|
||||||
|
for service_name, args in self._autostart_tasks.items():
|
||||||
|
start_method = getattr(self, f"start_{service_name}", None)
|
||||||
|
if start_method is not None and callable(start_method):
|
||||||
|
start_method(*args)
|
||||||
|
else:
|
||||||
|
logger.warning(
|
||||||
|
f"No start method found for service '{service_name}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _start_async_loop_in_thread(self) -> None:
|
||||||
|
# create a new event loop and run it in a separate thread
|
||||||
|
self.__loop = asyncio.new_event_loop()
|
||||||
|
self.__thread = threading.Thread(target=self._start_loop)
|
||||||
|
self.__thread.start()
|
||||||
|
|
||||||
|
def _set_start_and_stop_for_async_methods(self) -> None:
|
||||||
|
# inspect the methods of the class
|
||||||
|
for name, method in inspect.getmembers(
|
||||||
|
self, predicate=inspect.iscoroutinefunction
|
||||||
|
):
|
||||||
|
|
||||||
|
def start_task(*args: Any, **kwargs: Any) -> None:
|
||||||
|
async def task(*args: Any, **kwargs: Any) -> None:
|
||||||
|
try:
|
||||||
|
await getattr(self, name)(*args, **kwargs)
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
print(f"Task {name} was cancelled")
|
||||||
|
|
||||||
|
self.__tasks[name] = asyncio.run_coroutine_threadsafe(
|
||||||
|
task(*args, **kwargs), self.__loop
|
||||||
|
)
|
||||||
|
|
||||||
|
def stop_task() -> None:
|
||||||
|
# cancel the task
|
||||||
|
task = self.__tasks.get(name)
|
||||||
|
if task is not None:
|
||||||
|
self.__loop.call_soon_threadsafe(task.cancel)
|
||||||
|
|
||||||
|
# create start and stop methods for each coroutine
|
||||||
|
setattr(self, f"start_{name}", start_task)
|
||||||
|
setattr(self, f"stop_{name}", stop_task)
|
||||||
|
|
||||||
|
def _update_full_access_path(self, obj: "DataService", parent_path: str) -> None:
|
||||||
|
"""
|
||||||
|
Recursive helper function to update '_full_access_path' for the object and all
|
||||||
|
its nested attributes
|
||||||
|
"""
|
||||||
|
|
||||||
|
parent_class_name = parent_path.split(".")[0] if parent_path else None
|
||||||
|
|
||||||
|
# Remove all access paths that don't start with the parent class name. As the
|
||||||
|
# exposed class is instantiated last, this ensures that all access paths start
|
||||||
|
# with the root class
|
||||||
|
access_path: set[str] = {
|
||||||
|
p
|
||||||
|
for p in cast(list[str], getattr(obj, "_full_access_path", set()))
|
||||||
|
if not parent_class_name or p.startswith(parent_class_name)
|
||||||
|
}
|
||||||
|
# add the new access path
|
||||||
|
access_path.add(parent_path)
|
||||||
|
setattr(obj, "_full_access_path", access_path)
|
||||||
|
|
||||||
|
# Recursively update access paths for all nested attributes of the object
|
||||||
|
for nested_attr_name in set(dir(obj)) - set(dir(object)):
|
||||||
|
nested_attr = getattr(obj, nested_attr_name)
|
||||||
|
if isinstance(nested_attr, list):
|
||||||
|
for i, list_item in enumerate(nested_attr):
|
||||||
|
if isinstance(list_item, DataService):
|
||||||
|
new_path = f"{parent_path}.{nested_attr_name}[{i}]"
|
||||||
|
self._update_full_access_path(list_item, new_path)
|
||||||
|
elif isinstance(nested_attr, DataService):
|
||||||
|
new_path = f"{parent_path}.{nested_attr_name}"
|
||||||
|
self._update_full_access_path(nested_attr, new_path)
|
||||||
|
|
||||||
|
def _start_loop(self) -> None:
|
||||||
|
asyncio.set_event_loop(self.__loop)
|
||||||
|
try:
|
||||||
|
self.__loop.run_forever()
|
||||||
|
finally:
|
||||||
|
# cancel all running tasks
|
||||||
|
for task in self.__tasks.values():
|
||||||
|
self.__loop.call_soon_threadsafe(task.cancel)
|
||||||
|
self.__loop.call_soon_threadsafe(self.__loop.stop)
|
||||||
|
self.__thread.join()
|
||||||
|
|
||||||
|
def __setattr__(self, __name: str, __value: Any) -> None:
|
||||||
|
if self.__dict__.get("_initialised"):
|
||||||
|
access_path: set[str] = getattr(self, "_full_access_path", set())
|
||||||
|
if access_path:
|
||||||
|
self._emit(access_path, __name, __value)
|
||||||
|
# TODO: add emits for properties -> can use co_names, which is a tuple
|
||||||
|
# containing the names used by the bytecode
|
||||||
|
super().__setattr__(__name, __value)
|
||||||
|
|
||||||
|
def _emit(self, access_path: set[str], name: str, value: Any) -> None:
|
||||||
|
for path in access_path:
|
||||||
|
logger.debug(f"{path}.{name} changed to {value}!")
|
||||||
|
|
||||||
|
def _rpyc_getattr(self, name: str) -> Any:
|
||||||
|
if name.startswith("_"):
|
||||||
|
# disallow special and private attributes
|
||||||
|
raise AttributeError("cannot access private/special names")
|
||||||
|
# allow all other attributes
|
||||||
|
return getattr(self, name)
|
||||||
|
|
||||||
|
def _rpyc_setattr(self, name: str, value: Any):
|
||||||
|
if name.startswith("_"):
|
||||||
|
# disallow special and private attributes
|
||||||
|
raise AttributeError("cannot access private/special names")
|
||||||
|
|
||||||
|
# check if the attribute has a setter method
|
||||||
|
attr = getattr(self, name, None)
|
||||||
|
if isinstance(attr, property) and attr.fset is None:
|
||||||
|
raise AttributeError(f"{name} attribute does not have a setter method")
|
||||||
|
|
||||||
|
# allow all other attributes
|
||||||
|
setattr(self, name, value)
|
||||||
|
|
||||||
|
def serialize(self, prefix: str = "") -> dict[str, dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Serializes the instance into a dictionary, preserving the structure of the
|
||||||
|
instance.
|
||||||
|
|
||||||
|
For each attribute, method, and property, the method includes its name, type,
|
||||||
|
value, readonly status, and documentation if any in the resulting dictionary.
|
||||||
|
Attributes and methods starting with an underscore are ignored.
|
||||||
|
|
||||||
|
For attributes, methods, and properties unique to the class (not inherited from
|
||||||
|
the base class), the method uses the format "<prefix>.<key>" for keys in the
|
||||||
|
dictionary. If no prefix is provided, the key format is simply "<key>".
|
||||||
|
|
||||||
|
For nested DataService instances, the method serializes recursively and appends
|
||||||
|
the key of the nested instance to the prefix in the format "<prefix>.<key>".
|
||||||
|
|
||||||
|
For attributes of type list, each item in the list is serialized individually.
|
||||||
|
If an item in the list is an instance of DataService, it is serialized
|
||||||
|
recursively with its key in the format "<prefix>.<key>.<item_id>", where
|
||||||
|
"item_id" is the id of the item itself.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prefix (str, optional): The prefix for each key in the serialized
|
||||||
|
dictionary. This is mainly used when this method is called recursively to
|
||||||
|
maintain the structure of nested instances.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: The serialized instance.
|
||||||
|
"""
|
||||||
|
result: dict[str, dict[str, Any]] = {}
|
||||||
|
|
||||||
|
# Get the dictionary of the base class
|
||||||
|
base_dict = set(super().__class__.__dict__)
|
||||||
|
# Get the dictionary of the derived class
|
||||||
|
derived_dict = set(self.__class__.__dict__)
|
||||||
|
# Get the difference between the two dictionaries
|
||||||
|
derived_only_dict = derived_dict - base_dict
|
||||||
|
|
||||||
|
instance_dict = set(self.__dict__)
|
||||||
|
# Merge the class and instance dictionaries
|
||||||
|
merged_dict = derived_only_dict | instance_dict
|
||||||
|
|
||||||
|
# Iterate over attributes, properties, class attributes, and methods
|
||||||
|
for key in merged_dict:
|
||||||
|
if key.startswith("_"):
|
||||||
|
continue # Skip attributes that start with underscore
|
||||||
|
|
||||||
|
# Get the value of the current attribute or method
|
||||||
|
value = getattr(self, key)
|
||||||
|
|
||||||
|
# Prepare the key by appending prefix and the key
|
||||||
|
key = f"{prefix}.{key}" if prefix else key
|
||||||
|
|
||||||
|
if isinstance(value, DataService):
|
||||||
|
result[key] = {
|
||||||
|
"type": type(value).__name__,
|
||||||
|
"value": value.serialize(prefix=key),
|
||||||
|
"readonly": False,
|
||||||
|
"id": id(value),
|
||||||
|
"doc": inspect.getdoc(value),
|
||||||
|
}
|
||||||
|
elif isinstance(value, list):
|
||||||
|
result[key] = {
|
||||||
|
"type": "list",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": type(item).__name__,
|
||||||
|
"value": item.serialize(prefix=key)
|
||||||
|
if isinstance(item, DataService)
|
||||||
|
else item,
|
||||||
|
"readonly": False,
|
||||||
|
"id": id(item),
|
||||||
|
}
|
||||||
|
for item in value
|
||||||
|
],
|
||||||
|
"readonly": False,
|
||||||
|
}
|
||||||
|
elif inspect.isfunction(value) or inspect.ismethod(value):
|
||||||
|
sig = inspect.signature(value)
|
||||||
|
parameters = {
|
||||||
|
k: v.annotation.__name__
|
||||||
|
if v.annotation is not inspect._empty
|
||||||
|
else None
|
||||||
|
for k, v in sig.parameters.items()
|
||||||
|
}
|
||||||
|
result[key] = {
|
||||||
|
"type": "method",
|
||||||
|
"async": asyncio.iscoroutinefunction(value),
|
||||||
|
"parameters": parameters,
|
||||||
|
"readonly": False,
|
||||||
|
"doc": inspect.getdoc(value),
|
||||||
|
}
|
||||||
|
elif isinstance(getattr(self.__class__, key, None), property):
|
||||||
|
prop: property = getattr(self.__class__, key)
|
||||||
|
result[key] = {
|
||||||
|
"type": type(value).__name__,
|
||||||
|
"value": value,
|
||||||
|
"readonly": prop.fset is None,
|
||||||
|
"doc": inspect.getdoc(prop),
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
result[key] = {
|
||||||
|
"type": type(value).__name__,
|
||||||
|
"value": value,
|
||||||
|
"readonly": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
58
src/pyDataInterface/data_service/data_service_list.py
Normal file
58
src/pyDataInterface/data_service/data_service_list.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
from collections.abc import Callable
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
class DataServiceList(list):
|
||||||
|
"""
|
||||||
|
DataServiceList is a list with additional functionality to trigger callbacks
|
||||||
|
whenever an item is set. This can be used to track changes in the list items.
|
||||||
|
|
||||||
|
The class takes the same arguments as the list superclass during initialization,
|
||||||
|
with an additional optional 'callback' argument that is a list of functions.
|
||||||
|
These callbacks are stored and executed whenever an item in the DataServiceList
|
||||||
|
is set via the __setitem__ method. The callbacks receive the index of the changed
|
||||||
|
item and its new value as arguments.
|
||||||
|
|
||||||
|
The original list that is passed during initialization is kept as a private
|
||||||
|
attribute to prevent it from being garbage collected.
|
||||||
|
|
||||||
|
Additional callbacks can be added after initialization using the `add_callback`
|
||||||
|
method.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
_original_list (list):
|
||||||
|
Reference to the original list, to prevent it from being garbage collected.
|
||||||
|
callbacks (list):
|
||||||
|
List of callback functions to be executed on item set.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*args: list[Any],
|
||||||
|
callback: list[Callable[[int, Any], None]] | None = None,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> None:
|
||||||
|
self.callbacks: list[Callable[[int, Any], None]] = []
|
||||||
|
if isinstance(callback, list):
|
||||||
|
self.callbacks = callback
|
||||||
|
|
||||||
|
# prevent gc to delete the passed list by keeping a reference
|
||||||
|
self._original_list = args[0]
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs) # type: ignore
|
||||||
|
|
||||||
|
def __setitem__(self, key: int, value: Any) -> None: # type: ignore
|
||||||
|
super().__setitem__(key, value) # type: ignore
|
||||||
|
|
||||||
|
for callback in self.callbacks:
|
||||||
|
callback(key, value)
|
||||||
|
|
||||||
|
def add_callback(self, callback: Callable[[int, Any], None]) -> None:
|
||||||
|
"""
|
||||||
|
Add a new callback function to be executed on item set.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
callback (Callable[[int, Any], None]): Callback function that takes two
|
||||||
|
arguments - index of the changed item and its new value.
|
||||||
|
"""
|
||||||
|
self.callbacks.append(callback)
|
0
src/pyDataInterface/server/__init__.py
Normal file
0
src/pyDataInterface/server/__init__.py
Normal file
0
src/pyDataInterface/server/server.py
Normal file
0
src/pyDataInterface/server/server.py
Normal file
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
0
tests/test_autostart_task.py
Normal file
0
tests/test_autostart_task.py
Normal file
138
tests/test_emit_on_change.py
Normal file
138
tests/test_emit_on_change.py
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from pytest import CaptureFixture
|
||||||
|
|
||||||
|
from pyDataInterface import DataService
|
||||||
|
|
||||||
|
|
||||||
|
def emit(self: Any, access_path: set[str], name: str, value: Any) -> None:
|
||||||
|
if isinstance(value, DataService):
|
||||||
|
value = value.serialize()
|
||||||
|
|
||||||
|
for path in access_path:
|
||||||
|
print(f"{path}.{name} = {value}")
|
||||||
|
|
||||||
|
|
||||||
|
DataService._emit = emit # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def test_class_attribute(capsys: CaptureFixture) -> None:
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
attr = 0
|
||||||
|
|
||||||
|
service_instance = ServiceClass()
|
||||||
|
|
||||||
|
service_instance.attr = 1
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert captured.out == "ServiceClass.attr = 1\n"
|
||||||
|
|
||||||
|
|
||||||
|
def test_instance_attribute(capsys: CaptureFixture) -> None:
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.attr = "Hello World"
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
service_instance = ServiceClass()
|
||||||
|
|
||||||
|
service_instance.attr = "Hello"
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert captured.out == "ServiceClass.attr = Hello\n"
|
||||||
|
|
||||||
|
|
||||||
|
def test_class_list_attribute(capsys: CaptureFixture) -> None:
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
attr = [0, 1]
|
||||||
|
|
||||||
|
service_instance = ServiceClass()
|
||||||
|
|
||||||
|
service_instance.attr[0] = 1337
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert captured.out == "ServiceClass.attr[0] = 1337\n"
|
||||||
|
|
||||||
|
|
||||||
|
def test_instance_list_attribute(capsys: CaptureFixture) -> None:
|
||||||
|
class SubClass(DataService):
|
||||||
|
name = "SubClass"
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.attr = [0, SubClass()]
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
service_instance = ServiceClass()
|
||||||
|
|
||||||
|
_ = capsys.readouterr()
|
||||||
|
|
||||||
|
service_instance.attr[0] = "Hello"
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert captured.out == "ServiceClass.attr[0] = Hello\n"
|
||||||
|
|
||||||
|
service_instance.attr[1] = SubClass()
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert (
|
||||||
|
captured.out.strip()
|
||||||
|
== "ServiceClass.attr[1] = {'name': {'type': 'str', 'value': 'SubClass',"
|
||||||
|
" 'readonly': False}}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_reused_instance_list_attribute(capsys: CaptureFixture) -> None:
|
||||||
|
some_list = [0, 1, 2]
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.attr = some_list
|
||||||
|
self.attr_2 = some_list
|
||||||
|
self.attr_3 = [0, 1, 2]
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
service_instance = ServiceClass()
|
||||||
|
|
||||||
|
service_instance.attr[0] = "Hello"
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
|
assert service_instance.attr == service_instance.attr_2
|
||||||
|
assert service_instance.attr != service_instance.attr_3
|
||||||
|
expected_output = sorted(
|
||||||
|
[
|
||||||
|
"ServiceClass.attr[0] = Hello",
|
||||||
|
"ServiceClass.attr_2[0] = Hello",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
actual_output = sorted(captured.out.strip().split("\n"))
|
||||||
|
assert actual_output == expected_output
|
||||||
|
|
||||||
|
|
||||||
|
def test_nested_reused_instance_list_attribute(capsys: CaptureFixture) -> None:
|
||||||
|
some_list = [0, 1, 2]
|
||||||
|
|
||||||
|
class SubClass(DataService):
|
||||||
|
attr_list = some_list
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.attr_list_2 = some_list
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.attr = some_list
|
||||||
|
self.subclass = SubClass()
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
service_instance = ServiceClass()
|
||||||
|
|
||||||
|
_ = capsys.readouterr()
|
||||||
|
service_instance.attr[0] = "Hello"
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
|
assert service_instance.attr == service_instance.subclass.attr_list
|
||||||
|
expected_output = sorted(
|
||||||
|
[
|
||||||
|
"ServiceClass.subclass.attr_list_2[0] = Hello",
|
||||||
|
"ServiceClass.subclass.attr_list[0] = Hello",
|
||||||
|
"ServiceClass.attr[0] = Hello",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
actual_output = sorted(captured.out.strip().split("\n"))
|
||||||
|
assert actual_output == expected_output
|
364
tests/test_full_access_path.py
Normal file
364
tests/test_full_access_path.py
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
from pyDataInterface import DataService
|
||||||
|
|
||||||
|
|
||||||
|
def test_class_attributes() -> None:
|
||||||
|
class SubClass(DataService):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
attr_1 = SubClass()
|
||||||
|
|
||||||
|
test_service = ServiceClass()
|
||||||
|
assert test_service.attr_1._full_access_path == {"ServiceClass.attr_1"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_instance_attributes() -> None:
|
||||||
|
class SubClass(DataService):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
def __init__(self):
|
||||||
|
self.attr_1 = SubClass()
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
test_service = ServiceClass()
|
||||||
|
assert test_service.attr_1._full_access_path == {"ServiceClass.attr_1"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_reused_instance_attributes() -> None:
|
||||||
|
class SubClass(DataService):
|
||||||
|
pass
|
||||||
|
|
||||||
|
subclass_instance = SubClass()
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
def __init__(self):
|
||||||
|
self.attr_1 = subclass_instance
|
||||||
|
self.attr_2 = subclass_instance
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
test_service = ServiceClass()
|
||||||
|
assert test_service.attr_1._full_access_path == {
|
||||||
|
"ServiceClass.attr_1",
|
||||||
|
"ServiceClass.attr_2",
|
||||||
|
}
|
||||||
|
assert test_service.attr_2._full_access_path == {
|
||||||
|
"ServiceClass.attr_1",
|
||||||
|
"ServiceClass.attr_2",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert test_service.attr_1._full_access_path == {
|
||||||
|
"ServiceClass.attr_1",
|
||||||
|
"ServiceClass.attr_2",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_reused_attributes_mixed() -> None:
|
||||||
|
class SubClass(DataService):
|
||||||
|
pass
|
||||||
|
|
||||||
|
subclass_instance = SubClass()
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
attr_1 = subclass_instance
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.attr_2 = subclass_instance
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
test_service = ServiceClass()
|
||||||
|
assert test_service.attr_1._full_access_path == {
|
||||||
|
"ServiceClass.attr_1",
|
||||||
|
"ServiceClass.attr_2",
|
||||||
|
}
|
||||||
|
assert test_service.attr_2._full_access_path == {
|
||||||
|
"ServiceClass.attr_1",
|
||||||
|
"ServiceClass.attr_2",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_nested_class_attributes() -> None:
|
||||||
|
class SubSubSubClass(DataService):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SubSubClass(DataService):
|
||||||
|
attr = SubSubSubClass()
|
||||||
|
|
||||||
|
class SubClass(DataService):
|
||||||
|
attr = SubSubClass()
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
attr = SubClass()
|
||||||
|
|
||||||
|
test_service = ServiceClass()
|
||||||
|
assert test_service.attr._full_access_path == {
|
||||||
|
"ServiceClass.attr",
|
||||||
|
}
|
||||||
|
assert test_service.attr.attr._full_access_path == {
|
||||||
|
"ServiceClass.attr.attr",
|
||||||
|
}
|
||||||
|
assert test_service.attr.attr.attr._full_access_path == {
|
||||||
|
"ServiceClass.attr.attr.attr",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_nested_instance_attributes() -> None:
|
||||||
|
class SubSubSubClass(DataService):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SubSubClass(DataService):
|
||||||
|
def __init__(self):
|
||||||
|
self.attr = SubSubSubClass()
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
class SubClass(DataService):
|
||||||
|
def __init__(self):
|
||||||
|
self.attr = SubSubClass()
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
def __init__(self):
|
||||||
|
self.attr = SubClass()
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
test_service = ServiceClass()
|
||||||
|
assert test_service.attr._full_access_path == {
|
||||||
|
"ServiceClass.attr",
|
||||||
|
}
|
||||||
|
assert test_service.attr.attr._full_access_path == {
|
||||||
|
"ServiceClass.attr.attr",
|
||||||
|
}
|
||||||
|
assert test_service.attr.attr.attr._full_access_path == {
|
||||||
|
"ServiceClass.attr.attr.attr",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_advanced_nested_instance_attributes() -> None:
|
||||||
|
class SubSubSubClass(DataService):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SubSubClass(DataService):
|
||||||
|
def __init__(self):
|
||||||
|
self.attr = SubSubSubClass()
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
subsubclass_instance = SubSubClass()
|
||||||
|
|
||||||
|
class SubClass(DataService):
|
||||||
|
def __init__(self):
|
||||||
|
self.attr = subsubclass_instance
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
def __init__(self):
|
||||||
|
self.attr = SubClass()
|
||||||
|
self.subattr = subsubclass_instance
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
test_service = ServiceClass()
|
||||||
|
assert test_service.attr._full_access_path == {
|
||||||
|
"ServiceClass.attr",
|
||||||
|
}
|
||||||
|
assert test_service.attr.attr._full_access_path == {
|
||||||
|
"ServiceClass.attr.attr",
|
||||||
|
"ServiceClass.subattr",
|
||||||
|
}
|
||||||
|
assert test_service.attr.attr.attr._full_access_path == {
|
||||||
|
"ServiceClass.attr.attr.attr",
|
||||||
|
"ServiceClass.subattr.attr", # as the SubSubSubClass does not implement anything, both subattr.attr and attr.attr.attr refer to the same instance
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_advanced_nested_class_attributes() -> None:
|
||||||
|
class SubSubSubClass(DataService):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SubSubClass(DataService):
|
||||||
|
attr = SubSubSubClass()
|
||||||
|
|
||||||
|
class SubClass(DataService):
|
||||||
|
attr = SubSubClass()
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
attr = SubClass()
|
||||||
|
subattr = SubSubClass()
|
||||||
|
|
||||||
|
test_service = ServiceClass()
|
||||||
|
assert test_service.attr._full_access_path == {
|
||||||
|
"ServiceClass.attr",
|
||||||
|
}
|
||||||
|
assert test_service.subattr._full_access_path == {
|
||||||
|
"ServiceClass.subattr",
|
||||||
|
}
|
||||||
|
assert test_service.attr.attr._full_access_path == {
|
||||||
|
"ServiceClass.attr.attr",
|
||||||
|
}
|
||||||
|
assert test_service.attr.attr.attr._full_access_path == {
|
||||||
|
"ServiceClass.attr.attr.attr",
|
||||||
|
"ServiceClass.subattr.attr", # as the SubSubSubClass does not implement anything, both subattr.attr and attr.attr.attr refer to the same instance
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_advanced_nested_attributes_mixed() -> None:
|
||||||
|
class SubSubClass(DataService):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SubClass(DataService):
|
||||||
|
attr = SubSubClass()
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.attr_1 = SubSubClass()
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
subattr = SubClass()
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.attr = SubClass()
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
test_service = ServiceClass()
|
||||||
|
assert test_service.attr._full_access_path == {
|
||||||
|
"ServiceClass.attr",
|
||||||
|
}
|
||||||
|
assert test_service.subattr._full_access_path == {
|
||||||
|
"ServiceClass.subattr",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Subclass.attr is the same for all instances
|
||||||
|
assert test_service.attr.attr == test_service.subattr.attr
|
||||||
|
assert test_service.attr.attr._full_access_path == {
|
||||||
|
"ServiceClass.attr.attr",
|
||||||
|
"ServiceClass.subattr.attr",
|
||||||
|
}
|
||||||
|
assert test_service.subattr.attr._full_access_path == {
|
||||||
|
"ServiceClass.subattr.attr",
|
||||||
|
"ServiceClass.attr.attr",
|
||||||
|
}
|
||||||
|
|
||||||
|
# attr_1 is different for all instances of SubClass
|
||||||
|
assert test_service.attr.attr_1 != test_service.subattr.attr
|
||||||
|
assert test_service.attr.attr_1 != test_service.subattr.attr_1
|
||||||
|
assert test_service.subattr.attr_1._full_access_path == {
|
||||||
|
"ServiceClass.subattr.attr_1",
|
||||||
|
}
|
||||||
|
assert test_service.attr.attr_1._full_access_path == {
|
||||||
|
"ServiceClass.attr.attr_1",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_class_list_attributes() -> None:
|
||||||
|
class SubClass(DataService):
|
||||||
|
pass
|
||||||
|
|
||||||
|
subclass_instance = SubClass()
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
attr_list = [SubClass() for _ in range(2)]
|
||||||
|
attr_list_2 = [subclass_instance, subclass_instance]
|
||||||
|
attr = subclass_instance
|
||||||
|
|
||||||
|
test_service = ServiceClass()
|
||||||
|
assert test_service.attr_list[0] != test_service.attr_list[1]
|
||||||
|
assert test_service.attr_list[0]._full_access_path == {
|
||||||
|
"ServiceClass.attr_list[0]",
|
||||||
|
}
|
||||||
|
assert test_service.attr_list[1]._full_access_path == {
|
||||||
|
"ServiceClass.attr_list[1]",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert test_service.attr_list_2[0] == test_service.attr
|
||||||
|
assert test_service.attr_list_2[0] == test_service.attr_list_2[1]
|
||||||
|
assert test_service.attr_list_2[0]._full_access_path == {
|
||||||
|
"ServiceClass.attr",
|
||||||
|
"ServiceClass.attr_list_2[0]",
|
||||||
|
"ServiceClass.attr_list_2[1]",
|
||||||
|
}
|
||||||
|
assert test_service.attr_list_2[1]._full_access_path == {
|
||||||
|
"ServiceClass.attr",
|
||||||
|
"ServiceClass.attr_list_2[0]",
|
||||||
|
"ServiceClass.attr_list_2[1]",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_nested_class_list_attributes() -> None:
|
||||||
|
class SubSubClass(DataService):
|
||||||
|
pass
|
||||||
|
|
||||||
|
subsubclass_instance = SubSubClass()
|
||||||
|
|
||||||
|
class SubClass(DataService):
|
||||||
|
attr_list = [subsubclass_instance]
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
attr = [SubClass()]
|
||||||
|
subattr = subsubclass_instance
|
||||||
|
|
||||||
|
test_service = ServiceClass()
|
||||||
|
assert test_service.attr[0].attr_list[0] == test_service.subattr
|
||||||
|
assert test_service.attr[0].attr_list[0]._full_access_path == {
|
||||||
|
"ServiceClass.attr[0].attr_list[0]",
|
||||||
|
"ServiceClass.subattr",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_instance_list_attributes() -> None:
|
||||||
|
class SubClass(DataService):
|
||||||
|
pass
|
||||||
|
|
||||||
|
subclass_instance = SubClass()
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
def __init__(self):
|
||||||
|
self.attr_list = [SubClass() for _ in range(2)]
|
||||||
|
self.attr_list_2 = [subclass_instance, subclass_instance]
|
||||||
|
self.attr = subclass_instance
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
test_service = ServiceClass()
|
||||||
|
assert test_service.attr_list[0] != test_service.attr_list[1]
|
||||||
|
assert test_service.attr_list[0]._full_access_path == {
|
||||||
|
"ServiceClass.attr_list[0]",
|
||||||
|
}
|
||||||
|
assert test_service.attr_list[1]._full_access_path == {
|
||||||
|
"ServiceClass.attr_list[1]",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert test_service.attr_list_2[0] == test_service.attr
|
||||||
|
assert test_service.attr_list_2[0] == test_service.attr_list_2[1]
|
||||||
|
assert test_service.attr_list_2[0]._full_access_path == {
|
||||||
|
"ServiceClass.attr",
|
||||||
|
"ServiceClass.attr_list_2[0]",
|
||||||
|
"ServiceClass.attr_list_2[1]",
|
||||||
|
}
|
||||||
|
assert test_service.attr_list_2[1]._full_access_path == {
|
||||||
|
"ServiceClass.attr",
|
||||||
|
"ServiceClass.attr_list_2[0]",
|
||||||
|
"ServiceClass.attr_list_2[1]",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_nested_instance_list_attributes() -> None:
|
||||||
|
class SubSubClass(DataService):
|
||||||
|
pass
|
||||||
|
|
||||||
|
subsubclass_instance = SubSubClass()
|
||||||
|
|
||||||
|
class SubClass(DataService):
|
||||||
|
def __init__(self):
|
||||||
|
self.attr_list = [subsubclass_instance]
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
subattr = subsubclass_instance
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.attr = [SubClass()]
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
test_service = ServiceClass()
|
||||||
|
assert test_service.attr[0].attr_list[0] == test_service.subattr
|
||||||
|
assert test_service.attr[0].attr_list[0]._full_access_path == {
|
||||||
|
"ServiceClass.attr[0].attr_list[0]",
|
||||||
|
"ServiceClass.subattr",
|
||||||
|
}
|
34
tests/test_properties.py
Normal file
34
tests/test_properties.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
from pytest import CaptureFixture
|
||||||
|
|
||||||
|
from pyDataInterface import DataService
|
||||||
|
|
||||||
|
|
||||||
|
def test_properties(capsys: CaptureFixture) -> None:
|
||||||
|
class ServiceClass(DataService):
|
||||||
|
_power = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def power(self) -> bool:
|
||||||
|
return self._power
|
||||||
|
|
||||||
|
@power.setter
|
||||||
|
def power(self, value: bool) -> None:
|
||||||
|
self._power = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def power_two(self) -> bool:
|
||||||
|
return self._power
|
||||||
|
|
||||||
|
test_service = ServiceClass()
|
||||||
|
test_service.power = False
|
||||||
|
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
expected_output = sorted(
|
||||||
|
[
|
||||||
|
"ServiceClass.power = False",
|
||||||
|
"ServiceClass.power_two = False",
|
||||||
|
"ServiceClass._power = False",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
actual_output = sorted(captured.out.strip().split("\n"))
|
||||||
|
assert actual_output == expected_output
|
Loading…
x
Reference in New Issue
Block a user