Repository: equinor/tagreader-python (language: python)

Issue:
Convert to Poetry for packaging and dependency management
Convert to Poetry in order to align with other projects.


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 58e6683e..5713b56a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -13,34 +13,36 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        python: [3.7, 3.8, 3.9, '3.10']
+        python-version: ['3.8', '3.9', '3.10', '3.11']
         os: [windows-latest, ubuntu-latest, macos-latest]
     runs-on: ${{ matrix.os }}
     steps:
-    - uses: actions/checkout@v3
-    - name: Set up Python ${{ matrix.python }}
-      uses: actions/setup-python@v4
-      with:
-        python-version: ${{ matrix.python }}
-    - name: Install required Linux library for pykerberos
-      if: matrix.os == 'ubuntu-latest'
-      run: |
-        sudo apt-get install libkrb5-dev
-    - name: Install dependencies
-      run: |
-        python -m pip install --upgrade pip
-        pip install -r requirements.in
-    - name: Lint with flake8
-      run: |
-        pip install flake8
-        # stop the build if there are Python syntax errors or undefined names
-        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
-        # exit-zero treats all errors as warnings.
-        flake8 . --count --exit-zero --max-complexity=10 --statistics
-    - name: Test with pytest
-      run: |
-        pip install pytest
-        pytest
+      - name: Checkout code
+        uses: actions/checkout@v3
+
+      - name: Install required Linux library for pykerberos
+        if: matrix.os == 'ubuntu-latest'
+        run: |
+          sudo apt-get install libkrb5-dev
+
+      - name: Install Poetry
+        run: |
+          pipx install poetry
+
+      - name: Setup Python
+        uses: actions/setup-python@v4
+        with:
+          python-version: ${{matrix.python-version}}
+          cache: 'poetry'
+
+      - name: Check pyproject.toml validity
+        run: poetry check --no-interaction
+
+      - name: Install deps
+        run: poetry install --no-interaction
+
+      - name: Run tests
+        run: poetry run pytest
 
   testdeploy-if-tagged:
     needs: test
@@ -51,16 +53,28 @@ jobs:
       - name: Set up Python
         uses: actions/setup-python@v4
         with:
-          python-version: '3.10'
-      - name: Install dependencies
+          python-version: '3.11'
+
+      - name: Install Poetry
         run: |
-          python -m pip install --upgrade pip
-          pip install -r requirements.in
-          pip install setuptools wheel twine
+          pipx install poetry
+
+      - name: Setup Python
+        uses: actions/setup-python@v4
+        with:
+          python-version: ${{matrix.python-version}}
+          cache: 'poetry'
+
+      - name: Check pyproject.toml validity
+        run: poetry check --no-interaction
+
+      - name: Install deps
+        run: poetry install --no-interaction
+
       - name: Build and publish
         env:
           TWINE_USERNAME: __token__
           TWINE_PASSWORD: ${{ secrets.TEST_PYPI_PASSWORD }}
         run: |
-          python setup.py sdist bdist_wheel
-          twine upload --repository-url https://test.pypi.org/legacy/ dist/*
+          poetry config pypi-token.pypi $TEST_PYPI_PASSWORD
+          poetry publish --build
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 2af279d4..d80520a1 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -10,32 +10,16 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - name: Checkout source code
+      - name: Checkout code
         uses: actions/checkout@v3
 
-      - name: Install required Linux library for pykerberos
-        run: |
-          sudo apt-get install libkrb5-dev
-
-      - name: Set up Python
+      - name: Set up python
         uses: actions/setup-python@v4
         with:
-          python-version: 3.11
-
-      - name: Install dependencies
-        run: |
-          python -m pip install --upgrade pip
-          pip install -r requirements.in
+          python-version: "3.11"
 
-      - name: Lint with flake8
-        run: |
-          pip install flake8
-          # stop the build if there are Python syntax errors or undefined names
-          flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
-          # exit-zero treats all errors as warnings.
-          flake8 . --count --exit-zero --max-complexity=10 --statistics
+      - name: Install pre-commit
+        run: pip install pre-commit
 
-      - name: Check file formatting with Black
-        uses: psf/black@stable
-        with:
-          options: "--check --verbose"
+      - name: Run pre-commit
+        run: pre-commit run --all-files
diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml
index 59320350..37ec8cf8 100644
--- a/.github/workflows/pypi-publish.yml
+++ b/.github/workflows/pypi-publish.yml
@@ -15,16 +15,28 @@ jobs:
       - name: Set up Python
         uses: actions/setup-python@v4
         with:
-          python-version: '3.10'
-      - name: Install dependencies
+          python-version: '3.11'
+
+      - name: Install Poetry
         run: |
-          python -m pip install --upgrade pip
-          pip install -r requirements.in
-          pip install setuptools wheel twine
+          pipx install poetry
+
+      - name: Setup Python
+        uses: actions/setup-python@v4
+        with:
+          python-version: ${{matrix.python-version}}
+          cache: 'poetry'
+
+      - name: Check pyproject.toml validity
+        run: poetry check --no-interaction
+
+      - name: Install deps
+        run: poetry install --no-interaction
+
       - name: Build and publish
         env:
           TWINE_USERNAME: __token__
           TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
         run: |
-          python setup.py sdist bdist_wheel
-          twine upload dist/*
+          poetry config pypi-token.pypi $PYPI_PASSWORD
+          poetry publish --build
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..c54d74ea
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,34 @@
+repos:
+  - repo: https://github.com/psf/black
+    rev: 23.1.0
+    hooks:
+      - id: black
+        language_version: python3
+        exclude: tests
+
+  - repo: https://github.com/PyCQA/flake8
+    rev: '6.0.0'
+    hooks:
+      - id: flake8
+        args: ['--ignore=E501,W503,E231,E503,E203,F401']
+        exclude: tests
+
+  - repo: https://github.com/timothycrosley/isort
+    rev: '5.12.0'
+    hooks:
+      - id: isort
+        args:
+          [
+            '--line-length=88',
+            '--use-parentheses',
+            '--trailing-comma',
+            '--multi-line=3',
+          ]
+        exclude: tests
+
+  - repo: https://github.com/pre-commit/mirrors-mypy
+    rev: v1.0.1
+    hooks:
+      - id: mypy
+        args: [--ignore-missing-imports]
+        exclude: tests
diff --git a/README.md b/README.md
index 24f7fe49..0c72376e 100644
--- a/README.md
+++ b/README.md
@@ -53,6 +53,15 @@ to install some proprietary drivers. There is more information in the
 [manual](docs/manual.md#odbc-drivers). Please note that the web APIs should
 normally be preferred.
 
+## Usage example
+
+```
+import tagreader
+c = tagreader.IMSClient("mysource", "aspenone")
+print(c.search("tag*"))
+df = c.read_tags(["tag1", "tag2"], "18.06.2020 08:00:00", "18.06.2020 09:00:00", 60)
+```
+
 ## Documentation
 
 There is a [quickstart](docs/quickstart.ipynb) example file that should get
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index b43d1fa1..3cb15cac 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -6,14 +6,14 @@ pool:
 
 strategy:
   matrix:
-    Python37:
-      python.version: '3.7'
     Python38:
       python.version: '3.8'
     Python39:
       python.version: '3.9'
     Python310:
       python.version: '3.10'
+    Python311:
+      python.version: '3.11'
 
 steps:
 - task: UsePythonVersion@0
@@ -22,8 +22,10 @@ steps:
   displayName: 'Use Python $(python.version)'
 
 - script: |
-    python -m pip install --upgrade pip
-    pip install -r requirements.in
+    python -m pip install --upgrade pip pipx
+    pipx install poetry
+    poetry export -f requirements.txt --output requirements.txt
+    pip install -r requirements.txt
     pip install pytest pytest-azurepipelines pytest-cov
   displayName: 'Install dependencies'
   env: 
diff --git a/dev-requirements.in b/dev-requirements.in
deleted file mode 100644
index eff0d909..00000000
--- a/dev-requirements.in
+++ /dev/null
@@ -1,9 +0,0 @@
--c requirements.txt 
-black
-flake8
-isort
-jupyter
-mypy
-pip-tools
-pytest
-setuptools-scm
diff --git a/dev-requirements.txt b/dev-requirements.txt
deleted file mode 100644
index dc6b6672..00000000
--- a/dev-requirements.txt
+++ /dev/null
@@ -1,267 +0,0 @@
-#
-# This file is autogenerated by pip-compile with python 3.10
-# To update, run:
-#
-#    pip-compile '.\dev-requirements.in'
-#
-argon2-cffi==21.3.0
-    # via notebook
-argon2-cffi-bindings==21.2.0
-    # via argon2-cffi
-asttokens==2.0.5
-    # via stack-data
-attrs==21.4.0
-    # via
-    #   jsonschema
-    #   pytest
-backcall==0.2.0
-    # via ipython
-black==22.1.0
-    # via -r dev-requirements.in
-bleach==4.1.0
-    # via nbconvert
-cffi==1.15.0
-    # via
-    #   -c requirements.txt
-    #   argon2-cffi-bindings
-click==8.0.4
-    # via
-    #   black
-    #   pip-tools
-debugpy==1.5.1
-    # via ipykernel
-decorator==5.1.1
-    # via ipython
-defusedxml==0.7.1
-    # via nbconvert
-entrypoints==0.4
-    # via
-    #   jupyter-client
-    #   nbconvert
-executing==0.8.3
-    # via stack-data
-flake8==4.0.1
-    # via -r dev-requirements.in
-iniconfig==1.1.1
-    # via pytest
-ipykernel==6.9.1
-    # via
-    #   ipywidgets
-    #   jupyter
-    #   jupyter-console
-    #   notebook
-    #   qtconsole
-ipython==8.10.0
-    # via
-    #   ipykernel
-    #   ipywidgets
-    #   jupyter-console
-ipython-genutils==0.2.0
-    # via
-    #   ipywidgets
-    #   nbformat
-    #   notebook
-    #   qtconsole
-ipywidgets==8.0.4
-    # via jupyter
-isort==5.10.1
-    # via -r dev-requirements.in
-jedi==0.18.1
-    # via ipython
-jinja2==3.0.3
-    # via
-    #   nbconvert
-    #   notebook
-jsonschema==4.4.0
-    # via nbformat
-jupyter==1.0.0
-    # via -r dev-requirements.in
-jupyter-client==7.1.2
-    # via
-    #   ipykernel
-    #   jupyter-console
-    #   nbclient
-    #   notebook
-    #   qtconsole
-jupyter-console==6.4.3
-    # via jupyter
-jupyter-core==4.9.2
-    # via
-    #   jupyter-client
-    #   nbconvert
-    #   nbformat
-    #   notebook
-    #   qtconsole
-jupyterlab-pygments==0.1.2
-    # via nbconvert
-jupyterlab-widgets==1.0.2
-    # via ipywidgets
-markupsafe==2.1.0
-    # via jinja2
-matplotlib-inline==0.1.3
-    # via
-    #   ipykernel
-    #   ipython
-mccabe==0.6.1
-    # via flake8
-mistune==0.8.4
-    # via nbconvert
-mypy==0.931
-    # via -r dev-requirements.in
-mypy-extensions==0.4.3
-    # via
-    #   black
-    #   mypy
-nbclient==0.5.12
-    # via nbconvert
-nbconvert==7.2.9
-    # via
-    #   jupyter
-    #   notebook
-nbformat==5.1.3
-    # via
-    #   ipywidgets
-    #   nbclient
-    #   nbconvert
-    #   notebook
-nest-asyncio==1.5.4
-    # via
-    #   ipykernel
-    #   jupyter-client
-    #   nbclient
-    #   notebook
-notebook==6.4.12
-    # via
-    #   jupyter
-    #   widgetsnbextension
-packaging==21.3
-    # via
-    #   -c requirements.txt
-    #   bleach
-    #   pytest
-    #   qtpy
-    #   setuptools-scm
-pandocfilters==1.5.0
-    # via nbconvert
-parso==0.8.3
-    # via jedi
-pathspec==0.9.0
-    # via black
-pep517==0.12.0
-    # via pip-tools
-pexpect==4.8.0
-    # via ipython
-pickleshare==0.7.5
-    # via ipython
-pip-tools==6.5.1
-    # via -r dev-requirements.in
-platformdirs==2.5.1
-    # via black
-pluggy==1.0.0
-    # via pytest
-prometheus-client==0.13.1
-    # via notebook
-prompt-toolkit==3.0.28
-    # via
-    #   ipython
-    #   jupyter-console
-ptyprocess==0.7.0
-    # via
-    #   pexpect
-    #   terminado
-pure-eval==0.2.2
-    # via stack-data
-py==1.11.0
-    # via pytest
-pycodestyle==2.8.0
-    # via flake8
-pycparser==2.21
-    # via
-    #   -c requirements.txt
-    #   cffi
-pyflakes==2.4.0
-    # via flake8
-pygments==2.11.2
-    # via
-    #   ipython
-    #   jupyter-console
-    #   jupyterlab-pygments
-    #   nbconvert
-    #   qtconsole
-pyparsing==3.0.7
-    # via
-    #   -c requirements.txt
-    #   packaging
-pyrsistent==0.18.1
-    # via jsonschema
-pytest==7.0.1
-    # via -r dev-requirements.in
-python-dateutil==2.8.2
-    # via
-    #   -c requirements.txt
-    #   jupyter-client
-pyzmq==22.3.0
-    # via
-    #   jupyter-client
-    #   notebook
-    #   qtconsole
-qtconsole==5.2.2
-    # via jupyter
-qtpy==2.0.1
-    # via qtconsole
-send2trash==1.8.0
-    # via notebook
-setuptools-scm==6.4.2
-    # via -r dev-requirements.in
-six==1.16.0
-    # via
-    #   -c requirements.txt
-    #   asttokens
-    #   bleach
-    #   python-dateutil
-stack-data==0.2.0
-    # via ipython
-terminado==0.13.3
-    # via notebook
-testpath==0.6.0
-    # via nbconvert
-tomli==2.0.1
-    # via
-    #   black
-    #   mypy
-    #   pep517
-    #   pytest
-    #   setuptools-scm
-tornado==6.1
-    # via
-    #   ipykernel
-    #   jupyter-client
-    #   notebook
-    #   terminado
-traitlets==5.1.1
-    # via
-    #   ipykernel
-    #   ipython
-    #   ipywidgets
-    #   jupyter-client
-    #   jupyter-core
-    #   matplotlib-inline
-    #   nbclient
-    #   nbconvert
-    #   nbformat
-    #   notebook
-    #   qtconsole
-typing-extensions==4.1.1
-    # via mypy
-wcwidth==0.2.5
-    # via prompt-toolkit
-webencodings==0.5.1
-    # via bleach
-wheel==0.38.4
-    # via pip-tools
-widgetsnbextension==3.5.2
-    # via ipywidgets
-
-# The following packages are considered to be unsafe in a requirements file:
-# pip
-# setuptools
diff --git a/setup.cfg b/mypy.ini
similarity index 71%
rename from setup.cfg
rename to mypy.ini
index 545c3be1..8f564af5 100644
--- a/setup.cfg
+++ b/mypy.ini
@@ -1,12 +1,3 @@
-[flake8]
-max-line-length = 88
-select = B,C,E,F,W
-ignore =
-  # https://black.readthedocs.io/en/stable/the_black_code_style.html#slices
-  E203
-  # https://black.readthedocs.io/en/stable/the_black_code_style.html#line-breaks-binary-operators
-  W503
-
 [mypy]
 files = tagreader
 check_untyped_defs = True
@@ -36,4 +27,4 @@ ignore_errors = True
 ignore_errors = True
 
 [mypy-tagreader.web_handlers]
-ignore_errors = True
+ignore_errors = True
\ No newline at end of file
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 00000000..f3bd4368
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,2818 @@
+# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand.
+
+[[package]]
+name = "anyio"
+version = "3.6.2"
+description = "High level compatibility layer for multiple asynchronous event loop implementations"
+category = "dev"
+optional = false
+python-versions = ">=3.6.2"
+files = [
+    {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"},
+    {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"},
+]
+
+[package.dependencies]
+idna = ">=2.8"
+sniffio = ">=1.1"
+
+[package.extras]
+doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
+test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"]
+trio = ["trio (>=0.16,<0.22)"]
+
+[[package]]
+name = "appnope"
+version = "0.1.3"
+description = "Disable App Nap on macOS >= 10.9"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"},
+    {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"},
+]
+
+[[package]]
+name = "argon2-cffi"
+version = "21.3.0"
+description = "The secure Argon2 password hashing algorithm."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "argon2-cffi-21.3.0.tar.gz", hash = "sha256:d384164d944190a7dd7ef22c6aa3ff197da12962bd04b17f64d4e93d934dba5b"},
+    {file = "argon2_cffi-21.3.0-py3-none-any.whl", hash = "sha256:8c976986f2c5c0e5000919e6de187906cfd81fb1c72bf9d88c01177e77da7f80"},
+]
+
+[package.dependencies]
+argon2-cffi-bindings = "*"
+
+[package.extras]
+dev = ["cogapp", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "pre-commit", "pytest", "sphinx", "sphinx-notfound-page", "tomli"]
+docs = ["furo", "sphinx", "sphinx-notfound-page"]
+tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"]
+
+[[package]]
+name = "argon2-cffi-bindings"
+version = "21.2.0"
+description = "Low-level CFFI bindings for Argon2"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"},
+    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"},
+    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"},
+    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"},
+    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"},
+    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"},
+    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"},
+    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"},
+    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"},
+    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"},
+    {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"},
+    {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"},
+    {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"},
+    {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"},
+    {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"},
+    {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"},
+    {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"},
+    {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"},
+    {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"},
+    {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"},
+    {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"},
+]
+
+[package.dependencies]
+cffi = ">=1.0.1"
+
+[package.extras]
+dev = ["cogapp", "pre-commit", "pytest", "wheel"]
+tests = ["pytest"]
+
+[[package]]
+name = "arrow"
+version = "1.2.3"
+description = "Better dates & times for Python"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "arrow-1.2.3-py3-none-any.whl", hash = "sha256:5a49ab92e3b7b71d96cd6bfcc4df14efefc9dfa96ea19045815914a6ab6b1fe2"},
+    {file = "arrow-1.2.3.tar.gz", hash = "sha256:3934b30ca1b9f292376d9db15b19446088d12ec58629bc3f0da28fd55fb633a1"},
+]
+
+[package.dependencies]
+python-dateutil = ">=2.7.0"
+
+[[package]]
+name = "asttokens"
+version = "2.2.1"
+description = "Annotate AST trees with source code positions"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "asttokens-2.2.1-py2.py3-none-any.whl", hash = "sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c"},
+    {file = "asttokens-2.2.1.tar.gz", hash = "sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3"},
+]
+
+[package.dependencies]
+six = "*"
+
+[package.extras]
+test = ["astroid", "pytest"]
+
+[[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 = "backcall"
+version = "0.2.0"
+description = "Specifications for callback functions passed in to an API"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
+    {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
+]
+
+[[package]]
+name = "beautifulsoup4"
+version = "4.12.2"
+description = "Screen-scraping library"
+category = "dev"
+optional = false
+python-versions = ">=3.6.0"
+files = [
+    {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"},
+    {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"},
+]
+
+[package.dependencies]
+soupsieve = ">1.2"
+
+[package.extras]
+html5lib = ["html5lib"]
+lxml = ["lxml"]
+
+[[package]]
+name = "bleach"
+version = "6.0.0"
+description = "An easy safelist-based HTML-sanitizing tool."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "bleach-6.0.0-py3-none-any.whl", hash = "sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4"},
+    {file = "bleach-6.0.0.tar.gz", hash = "sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414"},
+]
+
+[package.dependencies]
+six = ">=1.9.0"
+webencodings = "*"
+
+[package.extras]
+css = ["tinycss2 (>=1.1.0,<1.2)"]
+
+[[package]]
+name = "blosc2"
+version = "2.0.0"
+description = "Python wrapper for the C-Blosc2 library."
+category = "main"
+optional = false
+python-versions = ">=3.8, <4"
+files = [
+    {file = "blosc2-2.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4085e5c1df186e1747d8a8578b0cc1c8b7668391d635e9f89e17156912fba85a"},
+    {file = "blosc2-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cec4c0570543921ce6b8c9ffdbf9f2170de37ecaf8e2b213e867e3130b81f205"},
+    {file = "blosc2-2.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fbecd7ef4876811719aa58d84e4b414f430f329162c18578b870f5e77b59864"},
+    {file = "blosc2-2.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96dd63eb7641594208e6a865fd60a0bdde24568a180180beb8af4d6608796f3a"},
+    {file = "blosc2-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:659e18d5e606a0ca4d80766f86d87e640818051911d01679eed11022243a7e4f"},
+    {file = "blosc2-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c58990ab2bcd0f412496acf1d05c65d955d963197bbaa57b10b2ace31c29181a"},
+    {file = "blosc2-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f206e6a01a8167b441bf886ff022eb20e0f085b09300f49f3018f566c14d918a"},
+    {file = "blosc2-2.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ec63269428aa3fb45f7d4881b2d11b428c4cb62e854caf54a767a64da4df83e"},
+    {file = "blosc2-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98ab1cd57f9d7422f1636a6b290f2940113ee8be26bfe3823e8c011826972b9c"},
+    {file = "blosc2-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:54c5614b18f9f01473758fa64e3bc699adbe31b307a45eca0e07fa2204e4d4a1"},
+    {file = "blosc2-2.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f465b8ab54ecde6b8654672a50a4c3699aafd8e2de0dfcd84ed53f8a34c1734a"},
+    {file = "blosc2-2.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0497793f55db0b75de08eb4c047a0bc5b96dbe5e405b53803dd3368e36336188"},
+    {file = "blosc2-2.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5c52649837d514669107c77e8f172e9e5ecfa030eef0d378bb47ce1689921c9"},
+    {file = "blosc2-2.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8eb02f67d4ed8ac8f0ce5f3c8cafc0059255bb6899fd35127e4076925640f239"},
+    {file = "blosc2-2.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:32c365bf744353103ed91dc1f03889de03b986588181601594aa7ee773818cb4"},
+    {file = "blosc2-2.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d98e850f0de55e15c402c6e27105ba850f8954e784e30a7f8bde89eb70a08574"},
+    {file = "blosc2-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6545d6d7e7365a2a3533d4bdf7095856443aed7d5ddc577ecd0e78083790bff1"},
+    {file = "blosc2-2.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c193959a984e7814833c8be6b9026d7744d2cff4d450476561583a87152e13e"},
+    {file = "blosc2-2.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea3396de7757092d502fb297a44a8d019d92e750e5aebcd9d39a157fde8785b3"},
+    {file = "blosc2-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:ef018926b55799cf23345127dde8f29356b4451b3e067e1b07f0d186213bd821"},
+    {file = "blosc2-2.0.0.tar.gz", hash = "sha256:f19b0b3674f6c825b490f00d8264b0c540c2cdc11ec7e81178d38b83c57790a1"},
+]
+
+[package.dependencies]
+msgpack = "*"
+
+[[package]]
+name = "certifi"
+version = "2023.5.7"
+description = "Python package for providing Mozilla's CA Bundle."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"},
+    {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"},
+]
+
+[[package]]
+name = "cffi"
+version = "1.15.1"
+description = "Foreign Function Interface for Python calling C code."
+category = "main"
+optional = false
+python-versions = "*"
+files = [
+    {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
+    {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
+    {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"},
+    {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"},
+    {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"},
+    {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"},
+    {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"},
+    {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"},
+    {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"},
+    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"},
+    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"},
+    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"},
+    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"},
+    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"},
+    {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"},
+    {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"},
+    {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"},
+    {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"},
+    {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"},
+    {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
+    {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"},
+    {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"},
+    {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"},
+    {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"},
+    {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"},
+    {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"},
+    {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"},
+    {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"},
+    {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"},
+    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"},
+    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"},
+    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"},
+    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"},
+    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"},
+    {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"},
+    {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"},
+    {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"},
+    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"},
+    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"},
+    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"},
+    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"},
+    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"},
+    {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"},
+    {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"},
+    {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"},
+    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"},
+    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"},
+    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"},
+    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"},
+    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"},
+    {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"},
+    {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"},
+    {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"},
+    {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"},
+    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"},
+    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"},
+    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"},
+    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"},
+    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"},
+    {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"},
+    {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"},
+    {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"},
+    {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
+    {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
+]
+
+[package.dependencies]
+pycparser = "*"
+
+[[package]]
+name = "cfgv"
+version = "3.3.1"
+description = "Validate configuration and produce human readable error messages."
+category = "dev"
+optional = false
+python-versions = ">=3.6.1"
+files = [
+    {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
+    {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.1.0"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+category = "main"
+optional = false
+python-versions = ">=3.7.0"
+files = [
+    {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"},
+    {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"},
+    {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"},
+    {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"},
+    {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"},
+    {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"},
+    {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"},
+    {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"},
+    {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"},
+    {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"},
+    {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"},
+    {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"},
+    {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"},
+    {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"},
+    {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"},
+    {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"},
+    {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"},
+    {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"},
+    {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"},
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+description = "Cross-platform colored terminal text."
+category = "dev"
+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 = "comm"
+version = "0.1.3"
+description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "comm-0.1.3-py3-none-any.whl", hash = "sha256:16613c6211e20223f215fc6d3b266a247b6e2641bf4e0a3ad34cb1aff2aa3f37"},
+    {file = "comm-0.1.3.tar.gz", hash = "sha256:a61efa9daffcfbe66fd643ba966f846a624e4e6d6767eda9cf6e993aadaab93e"},
+]
+
+[package.dependencies]
+traitlets = ">=5.3"
+
+[package.extras]
+lint = ["black (>=22.6.0)", "mdformat (>0.7)", "mdformat-gfm (>=0.3.5)", "ruff (>=0.0.156)"]
+test = ["pytest"]
+typing = ["mypy (>=0.990)"]
+
+[[package]]
+name = "cryptography"
+version = "40.0.2"
+description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "cryptography-40.0.2-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b"},
+    {file = "cryptography-40.0.2-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440"},
+    {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d"},
+    {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288"},
+    {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2"},
+    {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b"},
+    {file = "cryptography-40.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9"},
+    {file = "cryptography-40.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c"},
+    {file = "cryptography-40.0.2-cp36-abi3-win32.whl", hash = "sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9"},
+    {file = "cryptography-40.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b"},
+    {file = "cryptography-40.0.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b"},
+    {file = "cryptography-40.0.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e"},
+    {file = "cryptography-40.0.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a"},
+    {file = "cryptography-40.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958"},
+    {file = "cryptography-40.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b"},
+    {file = "cryptography-40.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636"},
+    {file = "cryptography-40.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e"},
+    {file = "cryptography-40.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404"},
+    {file = "cryptography-40.0.2.tar.gz", hash = "sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99"},
+]
+
+[package.dependencies]
+cffi = ">=1.12"
+
+[package.extras]
+docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
+docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
+pep8test = ["black", "check-manifest", "mypy", "ruff"]
+sdist = ["setuptools-rust (>=0.11.4)"]
+ssh = ["bcrypt (>=3.1.5)"]
+test = ["iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-shard (>=0.1.2)", "pytest-subtests", "pytest-xdist"]
+test-randomorder = ["pytest-randomly"]
+tox = ["tox"]
+
+[[package]]
+name = "cython"
+version = "0.29.34"
+description = "The Cython compiler for writing C extensions for the Python language."
+category = "main"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+files = [
+    {file = "Cython-0.29.34-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:742544024ddb74314e2d597accdb747ed76bd126e61fcf49940a5b5be0a8f381"},
+    {file = "Cython-0.29.34-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:03daae07f8cbf797506446adae512c3dd86e7f27a62a541fa1ee254baf43e32c"},
+    {file = "Cython-0.29.34-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5a8de3e793a576e40ca9b4f5518610cd416273c7dc5e254115656b6e4ec70663"},
+    {file = "Cython-0.29.34-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:60969d38e6a456a67e7ef8ae20668eff54e32ba439d4068ccf2854a44275a30f"},
+    {file = "Cython-0.29.34-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:21b88200620d80cfe193d199b259cdad2b9af56f916f0f7f474b5a3631ca0caa"},
+    {file = "Cython-0.29.34-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:308c8f1e58bf5e6e8a1c4dcf8abbd2d13d0f9b1e582f4d9ae8b89857342d8bb5"},
+    {file = "Cython-0.29.34-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:d8f822fb6ecd5d88c42136561f82960612421154fc5bf23c57103a367bb91356"},
+    {file = "Cython-0.29.34-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56866323f1660cecb4d5ff3a1fba92a56b91b7cfae0a8253777aa4bdb3bdf9a8"},
+    {file = "Cython-0.29.34-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:e971db8aeb12e7c0697cefafe65eefcc33ff1224ae3d8c7f83346cbc42c6c270"},
+    {file = "Cython-0.29.34-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4401270b0dc464c23671e2e9d52a60985f988318febaf51b047190e855bbe7d"},
+    {file = "Cython-0.29.34-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:dce0a36d163c05ae8b21200059511217d79b47baf2b7b0f926e8367bd7a3cc24"},
+    {file = "Cython-0.29.34-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dbd79221869ee9a6ccc4953b2c8838bb6ae08ab4d50ea4b60d7894f03739417b"},
+    {file = "Cython-0.29.34-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a0f4229df10bc4545ebbeaaf96ebb706011d8b333e54ed202beb03f2bee0a50e"},
+    {file = "Cython-0.29.34-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fd1ea21f1cebf33ae288caa0f3e9b5563a709f4df8925d53bad99be693fc0d9b"},
+    {file = "Cython-0.29.34-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:d7ef5f68f4c5baa93349ea54a352f8716d18bee9a37f3e93eff38a5d4e9b7262"},
+    {file = "Cython-0.29.34-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:459994d1de0f99bb18fad9f2325f760c4b392b1324aef37bcc1cd94922dfce41"},
+    {file = "Cython-0.29.34-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:1d6c809e2f9ce5950bbc52a1d2352ef3d4fc56186b64cb0d50c8c5a3c1d17661"},
+    {file = "Cython-0.29.34-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f674ceb5f722d364395f180fbac273072fc1a266aab924acc9cfd5afc645aae1"},
+    {file = "Cython-0.29.34-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9489de5b2044dcdfd9d6ca8242a02d560137b3c41b1f5ae1c4f6707d66d6e44d"},
+    {file = "Cython-0.29.34-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:5c121dc185040f4333bfded68963b4529698e1b6d994da56be32c97a90c896b6"},
+    {file = "Cython-0.29.34-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:b6149f7cc5b31bccb158c5b968e5a8d374fdc629792e7b928a9b66e08b03fca5"},
+    {file = "Cython-0.29.34-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0ab3cbf3d62b0354631a45dc93cfcdf79098663b1c65a6033af4a452b52217a7"},
+    {file = "Cython-0.29.34-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:4a2723447d1334484681d5aede34184f2da66317891f94b80e693a2f96a8f1a7"},
+    {file = "Cython-0.29.34-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e40cf86aadc29ecd1cb6de67b0d9488705865deea4fc185c7ad56d7a6fc78703"},
+    {file = "Cython-0.29.34-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8c3cd8bb8e880a3346f5685601004d96e0a2221e73edcaeea57ea848618b4ac6"},
+    {file = "Cython-0.29.34-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0e9032cd650b0cb1d2c2ef2623f5714c14d14c28d7647d589c3eeed0baf7428e"},
+    {file = "Cython-0.29.34-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:bdb3285660e3068438791ace7dd7b1efd6b442a10b5c8d7a4f0c9d184d08c8ed"},
+    {file = "Cython-0.29.34-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a8ad755f9364e720f10a36734a1c7a5ced5c679446718b589259261438a517c9"},
+    {file = "Cython-0.29.34-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:7595d29eaee95633dd8060f50f0e54b27472d01587659557ebcfe39da3ea946b"},
+    {file = "Cython-0.29.34-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e6ef7879668214d80ea3914c17e7d4e1ebf4242e0dd4dabe95ca5ccbe75589a5"},
+    {file = "Cython-0.29.34-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ccb223b5f0fd95d8d27561efc0c14502c0945f1a32274835831efa5d5baddfc1"},
+    {file = "Cython-0.29.34-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:11b1b278b8edef215caaa5250ad65a10023bfa0b5a93c776552248fc6f60098d"},
+    {file = "Cython-0.29.34-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:5718319a01489688fdd22ddebb8e2fcbbd60be5f30de4336ea7063c3ae29fbe5"},
+    {file = "Cython-0.29.34-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:cfb2302ef617d647ee590a4c0a00ba3c2da05f301dcefe7721125565d2e51351"},
+    {file = "Cython-0.29.34-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:67b850cf46b861bc27226d31e1d87c0e69869a02f8d3cc5d5bef549764029879"},
+    {file = "Cython-0.29.34-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0963266dad685812c1dbb758fcd4de78290e3adc7db271c8664dcde27380b13e"},
+    {file = "Cython-0.29.34-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7879992487d9060a61393eeefe00d299210256928dce44d887b6be313d342bac"},
+    {file = "Cython-0.29.34-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:44733366f1604b0c327613b6918469284878d2f5084297d10d26072fc6948d51"},
+    {file = "Cython-0.29.34-py2.py3-none-any.whl", hash = "sha256:be4f6b7be75a201c290c8611c0978549c60353890204573078e865423dbe3c83"},
+    {file = "Cython-0.29.34.tar.gz", hash = "sha256:1909688f5d7b521a60c396d20bba9e47a1b2d2784bfb085401e1e1e7d29a29a8"},
+]
+
+[[package]]
+name = "debugpy"
+version = "1.6.7"
+description = "An implementation of the Debug Adapter Protocol for Python"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "debugpy-1.6.7-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b3e7ac809b991006ad7f857f016fa92014445085711ef111fdc3f74f66144096"},
+    {file = "debugpy-1.6.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3876611d114a18aafef6383695dfc3f1217c98a9168c1aaf1a02b01ec7d8d1e"},
+    {file = "debugpy-1.6.7-cp310-cp310-win32.whl", hash = "sha256:33edb4afa85c098c24cc361d72ba7c21bb92f501104514d4ffec1fb36e09c01a"},
+    {file = "debugpy-1.6.7-cp310-cp310-win_amd64.whl", hash = "sha256:ed6d5413474e209ba50b1a75b2d9eecf64d41e6e4501977991cdc755dc83ab0f"},
+    {file = "debugpy-1.6.7-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:38ed626353e7c63f4b11efad659be04c23de2b0d15efff77b60e4740ea685d07"},
+    {file = "debugpy-1.6.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279d64c408c60431c8ee832dfd9ace7c396984fd7341fa3116aee414e7dcd88d"},
+    {file = "debugpy-1.6.7-cp37-cp37m-win32.whl", hash = "sha256:dbe04e7568aa69361a5b4c47b4493d5680bfa3a911d1e105fbea1b1f23f3eb45"},
+    {file = "debugpy-1.6.7-cp37-cp37m-win_amd64.whl", hash = "sha256:f90a2d4ad9a035cee7331c06a4cf2245e38bd7c89554fe3b616d90ab8aab89cc"},
+    {file = "debugpy-1.6.7-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:5224eabbbeddcf1943d4e2821876f3e5d7d383f27390b82da5d9558fd4eb30a9"},
+    {file = "debugpy-1.6.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae1123dff5bfe548ba1683eb972329ba6d646c3a80e6b4c06cd1b1dd0205e9b"},
+    {file = "debugpy-1.6.7-cp38-cp38-win32.whl", hash = "sha256:9cd10cf338e0907fdcf9eac9087faa30f150ef5445af5a545d307055141dd7a4"},
+    {file = "debugpy-1.6.7-cp38-cp38-win_amd64.whl", hash = "sha256:aaf6da50377ff4056c8ed470da24632b42e4087bc826845daad7af211e00faad"},
+    {file = "debugpy-1.6.7-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:0679b7e1e3523bd7d7869447ec67b59728675aadfc038550a63a362b63029d2c"},
+    {file = "debugpy-1.6.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de86029696e1b3b4d0d49076b9eba606c226e33ae312a57a46dca14ff370894d"},
+    {file = "debugpy-1.6.7-cp39-cp39-win32.whl", hash = "sha256:d71b31117779d9a90b745720c0eab54ae1da76d5b38c8026c654f4a066b0130a"},
+    {file = "debugpy-1.6.7-cp39-cp39-win_amd64.whl", hash = "sha256:c0ff93ae90a03b06d85b2c529eca51ab15457868a377c4cc40a23ab0e4e552a3"},
+    {file = "debugpy-1.6.7-py2.py3-none-any.whl", hash = "sha256:53f7a456bc50706a0eaabecf2d3ce44c4d5010e46dfc65b6b81a518b42866267"},
+    {file = "debugpy-1.6.7.zip", hash = "sha256:c4c2f0810fa25323abfdfa36cbbbb24e5c3b1a42cb762782de64439c575d67f2"},
+]
+
+[[package]]
+name = "decorator"
+version = "5.1.1"
+description = "Decorators for Humans"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+files = [
+    {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
+    {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
+]
+
+[[package]]
+name = "defusedxml"
+version = "0.7.1"
+description = "XML bomb protection for Python stdlib modules"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+files = [
+    {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
+    {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
+]
+
+[[package]]
+name = "distlib"
+version = "0.3.6"
+description = "Distribution utilities"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"},
+    {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"},
+]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.1.1"
+description = "Backport of PEP 654 (exception groups)"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"},
+    {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"},
+]
+
+[package.extras]
+test = ["pytest (>=6)"]
+
+[[package]]
+name = "executing"
+version = "1.2.0"
+description = "Get the currently executing AST node of a frame, and other information"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "executing-1.2.0-py2.py3-none-any.whl", hash = "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc"},
+    {file = "executing-1.2.0.tar.gz", hash = "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107"},
+]
+
+[package.extras]
+tests = ["asttokens", "littleutils", "pytest", "rich"]
+
+[[package]]
+name = "fastjsonschema"
+version = "2.17.1"
+description = "Fastest Python implementation of JSON schema"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "fastjsonschema-2.17.1-py3-none-any.whl", hash = "sha256:4b90b252628ca695280924d863fe37234eebadc29c5360d322571233dc9746e0"},
+    {file = "fastjsonschema-2.17.1.tar.gz", hash = "sha256:f4eeb8a77cef54861dbf7424ac8ce71306f12cbb086c45131bcba2c6a4f726e3"},
+]
+
+[package.extras]
+devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"]
+
+[[package]]
+name = "filelock"
+version = "3.12.0"
+description = "A platform independent file lock."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"},
+    {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"},
+]
+
+[package.extras]
+docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
+testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"]
+
+[[package]]
+name = "fqdn"
+version = "1.5.1"
+description = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4"
+files = [
+    {file = "fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014"},
+    {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"},
+]
+
+[[package]]
+name = "gssapi"
+version = "1.8.2"
+description = "Python GSSAPI Wrapper"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "gssapi-1.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c14cf97d61073d8211abfded77066af791356868022acda10224a8212639cf5"},
+    {file = "gssapi-1.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a36bc089036196aa03c3c312ea1639677065b3d7e8f37edbba731f61f5e56dd6"},
+    {file = "gssapi-1.8.2-cp310-cp310-win32.whl", hash = "sha256:cdd6bff2cf7f3a5a5a24cddde137b6e117fe53aad81f240dee2e8e383c0b289b"},
+    {file = "gssapi-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:f75b094913a1757e5e634b70b03e808cab0eb02c802ec50b760636b23b0aa50c"},
+    {file = "gssapi-1.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:47d69b1fd3bd0764d7a14500c6cbd8fd08198ee6e1737048a01ad7024d57f67d"},
+    {file = "gssapi-1.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67c85758a6b40042509c96bb00d0873a501491453ad5b0da88dbfa9343c8ffea"},
+    {file = "gssapi-1.8.2-cp311-cp311-win32.whl", hash = "sha256:4aaed04439aed8eba525ea44dd7219ecd97d520254320e66ac53d9ae4acb8fe5"},
+    {file = "gssapi-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8b4c1c664635153b0055a08bf30ec1552c8aa064a76f565d7905057500f334b3"},
+    {file = "gssapi-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:02e0a8f35e1f5b1c0eada646e3da1af3022c25e8df26ded3fd18ee78abb155ea"},
+    {file = "gssapi-1.8.2-cp37-cp37m-win32.whl", hash = "sha256:84bfd1b3954a5a608302884a36586eed6f623f6f38fcd331ad55937eca1ea89f"},
+    {file = "gssapi-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:85e7e623dfe51fef3f613acb965c4fa1866e7a7ee2c2558cbe2f203798171510"},
+    {file = "gssapi-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:512a2bd98e8003f48d98908bf54d1ed94c5388764e0a09fdc24cc691422a4162"},
+    {file = "gssapi-1.8.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:13aba9a947994f5f5f1fb6f425b397a359b191cee2983fa33911cf5e212d6cfb"},
+    {file = "gssapi-1.8.2-cp38-cp38-win32.whl", hash = "sha256:43c8b1acb8efa1f881ff7f7e912ccdc611689484190b7cc83f538b44f8992cbd"},
+    {file = "gssapi-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:787262e0db124d302c972d30e6f4b7c81423e696d08ee6e07047194a9bce2664"},
+    {file = "gssapi-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d6be1d8e07cc151e217f8a0ccf7c28ca5a1bb0ae00c8512586642c9f876a7298"},
+    {file = "gssapi-1.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:75763c49b3e18da60e160d76e8ac9a27b0bbd637c6e88513150fbb7158ae15d7"},
+    {file = "gssapi-1.8.2-cp39-cp39-win32.whl", hash = "sha256:dcd2be270bf490b6007eb3734795aaeddf92848f79b69d6a23f6204ccaa9b6c7"},
+    {file = "gssapi-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:fac7d1f4b14383bd29d3996cf5f6f23d0dc50ffd7965d2bc35bcc0849da24152"},
+    {file = "gssapi-1.8.2.tar.gz", hash = "sha256:b78e0a021cc91158660e4c5cc9263e07c719346c35a9c0f66725e914b235c89a"},
+]
+
+[package.dependencies]
+decorator = "*"
+
+[[package]]
+name = "identify"
+version = "2.5.24"
+description = "File identification library for Python"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"},
+    {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"},
+]
+
+[package.extras]
+license = ["ukkonen"]
+
+[[package]]
+name = "idna"
+version = "3.4"
+description = "Internationalized Domain Names in Applications (IDNA)"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+files = [
+    {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
+    {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
+]
+
+[[package]]
+name = "importlib-metadata"
+version = "6.6.0"
+description = "Read metadata from Python packages"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"},
+    {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"},
+]
+
+[package.dependencies]
+zipp = ">=0.5"
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+perf = ["ipython"]
+testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]
+
+[[package]]
+name = "importlib-resources"
+version = "5.12.0"
+description = "Read resources from Python packages"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"},
+    {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"},
+]
+
+[package.dependencies]
+zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
+
+[[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 = "ipykernel"
+version = "6.23.1"
+description = "IPython Kernel for Jupyter"
+category = "dev"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "ipykernel-6.23.1-py3-none-any.whl", hash = "sha256:77aeffab056c21d16f1edccdc9e5ccbf7d96eb401bd6703610a21be8b068aadc"},
+    {file = "ipykernel-6.23.1.tar.gz", hash = "sha256:1aba0ae8453e15e9bc6b24e497ef6840114afcdb832ae597f32137fa19d42a6f"},
+]
+
+[package.dependencies]
+appnope = {version = "*", markers = "platform_system == \"Darwin\""}
+comm = ">=0.1.1"
+debugpy = ">=1.6.5"
+ipython = ">=7.23.1"
+jupyter-client = ">=6.1.12"
+jupyter-core = ">=4.12,<5.0.0 || >=5.1.0"
+matplotlib-inline = ">=0.1"
+nest-asyncio = "*"
+packaging = "*"
+psutil = "*"
+pyzmq = ">=20"
+tornado = ">=6.1"
+traitlets = ">=5.4.0"
+
+[package.extras]
+cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"]
+docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"]
+pyqt5 = ["pyqt5"]
+pyside6 = ["pyside6"]
+test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio", "pytest-cov", "pytest-timeout"]
+
+[[package]]
+name = "ipython"
+version = "8.12.2"
+description = "IPython: Productive Interactive Computing"
+category = "dev"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "ipython-8.12.2-py3-none-any.whl", hash = "sha256:ea8801f15dfe4ffb76dea1b09b847430ffd70d827b41735c64a0638a04103bfc"},
+    {file = "ipython-8.12.2.tar.gz", hash = "sha256:c7b80eb7f5a855a88efc971fda506ff7a91c280b42cdae26643e0f601ea281ea"},
+]
+
+[package.dependencies]
+appnope = {version = "*", markers = "sys_platform == \"darwin\""}
+backcall = "*"
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+decorator = "*"
+jedi = ">=0.16"
+matplotlib-inline = "*"
+pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
+pickleshare = "*"
+prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0"
+pygments = ">=2.4.0"
+stack-data = "*"
+traitlets = ">=5"
+typing-extensions = {version = "*", markers = "python_version < \"3.10\""}
+
+[package.extras]
+all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"]
+black = ["black"]
+doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"]
+kernel = ["ipykernel"]
+nbconvert = ["nbconvert"]
+nbformat = ["nbformat"]
+notebook = ["ipywidgets", "notebook"]
+parallel = ["ipyparallel"]
+qtconsole = ["qtconsole"]
+test = ["pytest (<7.1)", "pytest-asyncio", "testpath"]
+test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"]
+
+[[package]]
+name = "ipython-genutils"
+version = "0.2.0"
+description = "Vestigial utilities from IPython"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"},
+    {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"},
+]
+
+[[package]]
+name = "ipywidgets"
+version = "8.0.6"
+description = "Jupyter interactive widgets"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "ipywidgets-8.0.6-py3-none-any.whl", hash = "sha256:a60bf8d2528997e05ac83fd19ea2fbe65f2e79fbe1b2b35779bdfc46c2941dcc"},
+    {file = "ipywidgets-8.0.6.tar.gz", hash = "sha256:de7d779f2045d60de9f6c25f653fdae2dba57898e6a1284494b3ba20b6893bb8"},
+]
+
+[package.dependencies]
+ipykernel = ">=4.5.1"
+ipython = ">=6.1.0"
+jupyterlab-widgets = ">=3.0.7,<3.1.0"
+traitlets = ">=4.3.1"
+widgetsnbextension = ">=4.0.7,<4.1.0"
+
+[package.extras]
+test = ["ipykernel", "jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"]
+
+[[package]]
+name = "isoduration"
+version = "20.11.0"
+description = "Operations with ISO 8601 durations"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042"},
+    {file = "isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9"},
+]
+
+[package.dependencies]
+arrow = ">=0.15.0"
+
+[[package]]
+name = "jedi"
+version = "0.18.2"
+description = "An autocompletion tool for Python that can be used for text editors."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "jedi-0.18.2-py2.py3-none-any.whl", hash = "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e"},
+    {file = "jedi-0.18.2.tar.gz", hash = "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612"},
+]
+
+[package.dependencies]
+parso = ">=0.8.0,<0.9.0"
+
+[package.extras]
+docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"]
+qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
+testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"]
+
+[[package]]
+name = "jinja2"
+version = "3.1.2"
+description = "A very fast and expressive template engine."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
+    {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
+]
+
+[package.dependencies]
+MarkupSafe = ">=2.0"
+
+[package.extras]
+i18n = ["Babel (>=2.7)"]
+
+[[package]]
+name = "jsonpointer"
+version = "2.3"
+description = "Identify specific nodes in a JSON document (RFC 6901)"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+    {file = "jsonpointer-2.3-py2.py3-none-any.whl", hash = "sha256:51801e558539b4e9cd268638c078c6c5746c9ac96bc38152d443400e4f3793e9"},
+    {file = "jsonpointer-2.3.tar.gz", hash = "sha256:97cba51526c829282218feb99dab1b1e6bdf8efd1c43dc9d57be093c0d69c99a"},
+]
+
+[[package]]
+name = "jsonschema"
+version = "4.17.3"
+description = "An implementation of JSON Schema validation for Python"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"},
+    {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"},
+]
+
+[package.dependencies]
+attrs = ">=17.4.0"
+fqdn = {version = "*", optional = true, markers = "extra == \"format-nongpl\""}
+idna = {version = "*", optional = true, markers = "extra == \"format-nongpl\""}
+importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""}
+isoduration = {version = "*", optional = true, markers = "extra == \"format-nongpl\""}
+jsonpointer = {version = ">1.13", optional = true, markers = "extra == \"format-nongpl\""}
+pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""}
+pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2"
+rfc3339-validator = {version = "*", optional = true, markers = "extra == \"format-nongpl\""}
+rfc3986-validator = {version = ">0.1.0", optional = true, markers = "extra == \"format-nongpl\""}
+uri-template = {version = "*", optional = true, markers = "extra == \"format-nongpl\""}
+webcolors = {version = ">=1.11", optional = true, markers = "extra == \"format-nongpl\""}
+
+[package.extras]
+format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"]
+format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"]
+
+[[package]]
+name = "jupyter"
+version = "1.0.0"
+description = "Jupyter metapackage. Install all the Jupyter components in one go."
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "jupyter-1.0.0-py2.py3-none-any.whl", hash = "sha256:5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78"},
+    {file = "jupyter-1.0.0.tar.gz", hash = "sha256:d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"},
+    {file = "jupyter-1.0.0.zip", hash = "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7"},
+]
+
+[package.dependencies]
+ipykernel = "*"
+ipywidgets = "*"
+jupyter-console = "*"
+nbconvert = "*"
+notebook = "*"
+qtconsole = "*"
+
+[[package]]
+name = "jupyter-client"
+version = "8.2.0"
+description = "Jupyter protocol implementation and client libraries"
+category = "dev"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "jupyter_client-8.2.0-py3-none-any.whl", hash = "sha256:b18219aa695d39e2ad570533e0d71fb7881d35a873051054a84ee2a17c4b7389"},
+    {file = "jupyter_client-8.2.0.tar.gz", hash = "sha256:9fe233834edd0e6c0aa5f05ca2ab4bdea1842bfd2d8a932878212fc5301ddaf0"},
+]
+
+[package.dependencies]
+importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""}
+jupyter-core = ">=4.12,<5.0.0 || >=5.1.0"
+python-dateutil = ">=2.8.2"
+pyzmq = ">=23.0"
+tornado = ">=6.2"
+traitlets = ">=5.3"
+
+[package.extras]
+docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"]
+test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"]
+
+[[package]]
+name = "jupyter-console"
+version = "6.6.3"
+description = "Jupyter terminal console"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485"},
+    {file = "jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539"},
+]
+
+[package.dependencies]
+ipykernel = ">=6.14"
+ipython = "*"
+jupyter-client = ">=7.0.0"
+jupyter-core = ">=4.12,<5.0.0 || >=5.1.0"
+prompt-toolkit = ">=3.0.30"
+pygments = "*"
+pyzmq = ">=17"
+traitlets = ">=5.4"
+
+[package.extras]
+test = ["flaky", "pexpect", "pytest"]
+
+[[package]]
+name = "jupyter-core"
+version = "5.3.0"
+description = "Jupyter core package. A base package on which Jupyter projects rely."
+category = "dev"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "jupyter_core-5.3.0-py3-none-any.whl", hash = "sha256:d4201af84559bc8c70cead287e1ab94aeef3c512848dde077b7684b54d67730d"},
+    {file = "jupyter_core-5.3.0.tar.gz", hash = "sha256:6db75be0c83edbf1b7c9f91ec266a9a24ef945da630f3120e1a0046dc13713fc"},
+]
+
+[package.dependencies]
+platformdirs = ">=2.5"
+pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""}
+traitlets = ">=5.3"
+
+[package.extras]
+docs = ["myst-parser", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"]
+test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"]
+
+[[package]]
+name = "jupyter-events"
+version = "0.6.3"
+description = "Jupyter Event System library"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "jupyter_events-0.6.3-py3-none-any.whl", hash = "sha256:57a2749f87ba387cd1bfd9b22a0875b889237dbf2edc2121ebb22bde47036c17"},
+    {file = "jupyter_events-0.6.3.tar.gz", hash = "sha256:9a6e9995f75d1b7146b436ea24d696ce3a35bfa8bfe45e0c33c334c79464d0b3"},
+]
+
+[package.dependencies]
+jsonschema = {version = ">=3.2.0", extras = ["format-nongpl"]}
+python-json-logger = ">=2.0.4"
+pyyaml = ">=5.3"
+rfc3339-validator = "*"
+rfc3986-validator = ">=0.1.1"
+traitlets = ">=5.3"
+
+[package.extras]
+cli = ["click", "rich"]
+docs = ["jupyterlite-sphinx", "myst-parser", "pydata-sphinx-theme", "sphinxcontrib-spelling"]
+test = ["click", "coverage", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.19.0)", "pytest-console-scripts", "pytest-cov", "rich"]
+
+[[package]]
+name = "jupyter-server"
+version = "2.5.0"
+description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications."
+category = "dev"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "jupyter_server-2.5.0-py3-none-any.whl", hash = "sha256:e6bc1e9e96d7c55b9ce9699ff6cb9a910581fe7349e27c40389acb67632e24c0"},
+    {file = "jupyter_server-2.5.0.tar.gz", hash = "sha256:9fde612791f716fd34d610cd939704a9639643744751ba66e7ee8fdc9cead07e"},
+]
+
+[package.dependencies]
+anyio = ">=3.1.0"
+argon2-cffi = "*"
+jinja2 = "*"
+jupyter-client = ">=7.4.4"
+jupyter-core = ">=4.12,<5.0.0 || >=5.1.0"
+jupyter-events = ">=0.4.0"
+jupyter-server-terminals = "*"
+nbconvert = ">=6.4.4"
+nbformat = ">=5.3.0"
+packaging = "*"
+prometheus-client = "*"
+pywinpty = {version = "*", markers = "os_name == \"nt\""}
+pyzmq = ">=24"
+send2trash = "*"
+terminado = ">=0.8.3"
+tornado = ">=6.2.0"
+traitlets = ">=5.6.0"
+websocket-client = "*"
+
+[package.extras]
+docs = ["docutils (<0.20)", "ipykernel", "jinja2", "jupyter-client", "jupyter-server", "mistune (<1.0.0)", "myst-parser", "nbformat", "prometheus-client", "pydata-sphinx-theme", "send2trash", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-openapi", "sphinxcontrib-spelling", "sphinxemoji", "tornado", "typing-extensions"]
+test = ["ipykernel", "pre-commit", "pytest (>=7.0)", "pytest-console-scripts", "pytest-jupyter[server] (>=0.4)", "pytest-timeout", "requests"]
+
+[[package]]
+name = "jupyter-server-terminals"
+version = "0.4.4"
+description = "A Jupyter Server Extension Providing Terminals."
+category = "dev"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "jupyter_server_terminals-0.4.4-py3-none-any.whl", hash = "sha256:75779164661cec02a8758a5311e18bb8eb70c4e86c6b699403100f1585a12a36"},
+    {file = "jupyter_server_terminals-0.4.4.tar.gz", hash = "sha256:57ab779797c25a7ba68e97bcfb5d7740f2b5e8a83b5e8102b10438041a7eac5d"},
+]
+
+[package.dependencies]
+pywinpty = {version = ">=2.0.3", markers = "os_name == \"nt\""}
+terminado = ">=0.8.3"
+
+[package.extras]
+docs = ["jinja2", "jupyter-server", "mistune (<3.0)", "myst-parser", "nbformat", "packaging", "pydata-sphinx-theme", "sphinxcontrib-github-alt", "sphinxcontrib-openapi", "sphinxcontrib-spelling", "sphinxemoji", "tornado"]
+test = ["coverage", "jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-cov", "pytest-jupyter[server] (>=0.5.3)", "pytest-timeout"]
+
+[[package]]
+name = "jupyterlab-pygments"
+version = "0.2.2"
+description = "Pygments theme using JupyterLab CSS variables"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "jupyterlab_pygments-0.2.2-py2.py3-none-any.whl", hash = "sha256:2405800db07c9f770863bcf8049a529c3dd4d3e28536638bd7c1c01d2748309f"},
+    {file = "jupyterlab_pygments-0.2.2.tar.gz", hash = "sha256:7405d7fde60819d905a9fa8ce89e4cd830e318cdad22a0030f7a901da705585d"},
+]
+
+[[package]]
+name = "jupyterlab-widgets"
+version = "3.0.7"
+description = "Jupyter interactive widgets for JupyterLab"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "jupyterlab_widgets-3.0.7-py3-none-any.whl", hash = "sha256:c73f8370338ec19f1bec47254752d6505b03601cbd5a67e6a0b184532f73a459"},
+    {file = "jupyterlab_widgets-3.0.7.tar.gz", hash = "sha256:c3a50ed5bf528a0c7a869096503af54702f86dda1db469aee1c92dc0c01b43ca"},
+]
+
+[[package]]
+name = "krb5"
+version = "0.5.0"
+description = "Kerberos API bindings for Python"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "krb5-0.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db6d5dada48a2e14793e3e61213fae5de3787c4f3d74dcb744ef8bf3a87afa96"},
+    {file = "krb5-0.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:939ba0fe5dd56eefb666485adc115216c034711c2ecf7a76f7a42864c2831a8c"},
+    {file = "krb5-0.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a9609198ba4aa35586c1ceb55171c113434eedcc97187a9f743be32c4831115f"},
+    {file = "krb5-0.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:31603913a068c148639d62c9aada9f51ddeb4ba77594c2d9857fbdfe6824376e"},
+    {file = "krb5-0.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8221f288dc993013097257f0cc612132a07b414e0c5dc69396387c13e07e369e"},
+    {file = "krb5-0.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e0a9debe7402ddb06f8b15d10da5894ae536317b80b95bb2393e916fd7e807b1"},
+    {file = "krb5-0.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bfd76ea4e4aee06a23da7cbec1c50452cf0388f05fd63fac71b14b4a9474c250"},
+    {file = "krb5-0.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3112879e5f77cd7f404c61552fceb9c8745cb330086db822289e10fd68911f33"},
+    {file = "krb5-0.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e1ae1c1b114817a7fd12c6427688a00b9bc8fc3b9896efd455f513d8d3e1111"},
+    {file = "krb5-0.5.0.tar.gz", hash = "sha256:33d6cf20ff9710cffb883136387fabc906360de1bce58bcd5673c4bb420e1657"},
+]
+
+[[package]]
+name = "markupsafe"
+version = "2.1.2"
+description = "Safely add untrusted strings to HTML/XML markup."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"},
+    {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"},
+    {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"},
+    {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"},
+    {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"},
+    {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"},
+    {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"},
+    {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"},
+    {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"},
+    {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"},
+    {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"},
+    {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"},
+    {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"},
+    {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"},
+    {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"},
+    {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"},
+    {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"},
+    {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"},
+    {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"},
+    {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"},
+    {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"},
+    {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"},
+    {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"},
+    {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"},
+    {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"},
+    {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"},
+    {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"},
+    {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"},
+    {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"},
+    {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"},
+    {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"},
+    {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"},
+    {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"},
+    {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"},
+    {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"},
+    {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"},
+    {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"},
+    {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"},
+    {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"},
+    {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"},
+    {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"},
+    {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"},
+    {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"},
+    {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"},
+    {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"},
+    {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"},
+    {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"},
+    {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"},
+    {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"},
+    {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"},
+]
+
+[[package]]
+name = "matplotlib-inline"
+version = "0.1.6"
+description = "Inline Matplotlib backend for Jupyter"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+files = [
+    {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"},
+    {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"},
+]
+
+[package.dependencies]
+traitlets = "*"
+
+[[package]]
+name = "mistune"
+version = "2.0.5"
+description = "A sane Markdown parser with useful plugins and renderers"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "mistune-2.0.5-py2.py3-none-any.whl", hash = "sha256:bad7f5d431886fcbaf5f758118ecff70d31f75231b34024a1341120340a65ce8"},
+    {file = "mistune-2.0.5.tar.gz", hash = "sha256:0246113cb2492db875c6be56974a7c893333bf26cd92891c85f63151cee09d34"},
+]
+
+[[package]]
+name = "msgpack"
+version = "1.0.5"
+description = "MessagePack serializer"
+category = "main"
+optional = false
+python-versions = "*"
+files = [
+    {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:525228efd79bb831cf6830a732e2e80bc1b05436b086d4264814b4b2955b2fa9"},
+    {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f8d8b3bf1ff2672567d6b5c725a1b347fe838b912772aa8ae2bf70338d5a198"},
+    {file = "msgpack-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdc793c50be3f01106245a61b739328f7dccc2c648b501e237f0699fe1395b81"},
+    {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cb47c21a8a65b165ce29f2bec852790cbc04936f502966768e4aae9fa763cb7"},
+    {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e42b9594cc3bf4d838d67d6ed62b9e59e201862a25e9a157019e171fbe672dd3"},
+    {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55b56a24893105dc52c1253649b60f475f36b3aa0fc66115bffafb624d7cb30b"},
+    {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1967f6129fc50a43bfe0951c35acbb729be89a55d849fab7686004da85103f1c"},
+    {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20a97bf595a232c3ee6d57ddaadd5453d174a52594bf9c21d10407e2a2d9b3bd"},
+    {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d25dd59bbbbb996eacf7be6b4ad082ed7eacc4e8f3d2df1ba43822da9bfa122a"},
+    {file = "msgpack-1.0.5-cp310-cp310-win32.whl", hash = "sha256:382b2c77589331f2cb80b67cc058c00f225e19827dbc818d700f61513ab47bea"},
+    {file = "msgpack-1.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:4867aa2df9e2a5fa5f76d7d5565d25ec76e84c106b55509e78c1ede0f152659a"},
+    {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f5ae84c5c8a857ec44dc180a8b0cc08238e021f57abdf51a8182e915e6299f0"},
+    {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e6ca5d5699bcd89ae605c150aee83b5321f2115695e741b99618f4856c50898"},
+    {file = "msgpack-1.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5494ea30d517a3576749cad32fa27f7585c65f5f38309c88c6d137877fa28a5a"},
+    {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab2f3331cb1b54165976a9d976cb251a83183631c88076613c6c780f0d6e45a"},
+    {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28592e20bbb1620848256ebc105fc420436af59515793ed27d5c77a217477705"},
+    {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe5c63197c55bce6385d9aee16c4d0641684628f63ace85f73571e65ad1c1e8d"},
+    {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed40e926fa2f297e8a653c954b732f125ef97bdd4c889f243182299de27e2aa9"},
+    {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b2de4c1c0538dcb7010902a2b97f4e00fc4ddf2c8cda9749af0e594d3b7fa3d7"},
+    {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf22a83f973b50f9d38e55c6aade04c41ddda19b00c4ebc558930d78eecc64ed"},
+    {file = "msgpack-1.0.5-cp311-cp311-win32.whl", hash = "sha256:c396e2cc213d12ce017b686e0f53497f94f8ba2b24799c25d913d46c08ec422c"},
+    {file = "msgpack-1.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c4c68d87497f66f96d50142a2b73b97972130d93677ce930718f68828b382e2"},
+    {file = "msgpack-1.0.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a2b031c2e9b9af485d5e3c4520f4220d74f4d222a5b8dc8c1a3ab9448ca79c57"},
+    {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f837b93669ce4336e24d08286c38761132bc7ab29782727f8557e1eb21b2080"},
+    {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1d46dfe3832660f53b13b925d4e0fa1432b00f5f7210eb3ad3bb9a13c6204a6"},
+    {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:366c9a7b9057e1547f4ad51d8facad8b406bab69c7d72c0eb6f529cf76d4b85f"},
+    {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4c075728a1095efd0634a7dccb06204919a2f67d1893b6aa8e00497258bf926c"},
+    {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:f933bbda5a3ee63b8834179096923b094b76f0c7a73c1cfe8f07ad608c58844b"},
+    {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:36961b0568c36027c76e2ae3ca1132e35123dcec0706c4b7992683cc26c1320c"},
+    {file = "msgpack-1.0.5-cp36-cp36m-win32.whl", hash = "sha256:b5ef2f015b95f912c2fcab19c36814963b5463f1fb9049846994b007962743e9"},
+    {file = "msgpack-1.0.5-cp36-cp36m-win_amd64.whl", hash = "sha256:288e32b47e67f7b171f86b030e527e302c91bd3f40fd9033483f2cacc37f327a"},
+    {file = "msgpack-1.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:137850656634abddfb88236008339fdaba3178f4751b28f270d2ebe77a563b6c"},
+    {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c05a4a96585525916b109bb85f8cb6511db1c6f5b9d9cbcbc940dc6b4be944b"},
+    {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56a62ec00b636583e5cb6ad313bbed36bb7ead5fa3a3e38938503142c72cba4f"},
+    {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef8108f8dedf204bb7b42994abf93882da1159728a2d4c5e82012edd92c9da9f"},
+    {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1835c84d65f46900920b3708f5ba829fb19b1096c1800ad60bae8418652a951d"},
+    {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e57916ef1bd0fee4f21c4600e9d1da352d8816b52a599c46460e93a6e9f17086"},
+    {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:17358523b85973e5f242ad74aa4712b7ee560715562554aa2134d96e7aa4cbbf"},
+    {file = "msgpack-1.0.5-cp37-cp37m-win32.whl", hash = "sha256:cb5aaa8c17760909ec6cb15e744c3ebc2ca8918e727216e79607b7bbce9c8f77"},
+    {file = "msgpack-1.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:ab31e908d8424d55601ad7075e471b7d0140d4d3dd3272daf39c5c19d936bd82"},
+    {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b72d0698f86e8d9ddf9442bdedec15b71df3598199ba33322d9711a19f08145c"},
+    {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:379026812e49258016dd84ad79ac8446922234d498058ae1d415f04b522d5b2d"},
+    {file = "msgpack-1.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:332360ff25469c346a1c5e47cbe2a725517919892eda5cfaffe6046656f0b7bb"},
+    {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:476a8fe8fae289fdf273d6d2a6cb6e35b5a58541693e8f9f019bfe990a51e4ba"},
+    {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9985b214f33311df47e274eb788a5893a761d025e2b92c723ba4c63936b69b1"},
+    {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48296af57cdb1d885843afd73c4656be5c76c0c6328db3440c9601a98f303d87"},
+    {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:addab7e2e1fcc04bd08e4eb631c2a90960c340e40dfc4a5e24d2ff0d5a3b3edb"},
+    {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:916723458c25dfb77ff07f4c66aed34e47503b2eb3188b3adbec8d8aa6e00f48"},
+    {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:821c7e677cc6acf0fd3f7ac664c98803827ae6de594a9f99563e48c5a2f27eb0"},
+    {file = "msgpack-1.0.5-cp38-cp38-win32.whl", hash = "sha256:1c0f7c47f0087ffda62961d425e4407961a7ffd2aa004c81b9c07d9269512f6e"},
+    {file = "msgpack-1.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:bae7de2026cbfe3782c8b78b0db9cbfc5455e079f1937cb0ab8d133496ac55e1"},
+    {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:20c784e66b613c7f16f632e7b5e8a1651aa5702463d61394671ba07b2fc9e025"},
+    {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:266fa4202c0eb94d26822d9bfd7af25d1e2c088927fe8de9033d929dd5ba24c5"},
+    {file = "msgpack-1.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18334484eafc2b1aa47a6d42427da7fa8f2ab3d60b674120bce7a895a0a85bdd"},
+    {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57e1f3528bd95cc44684beda696f74d3aaa8a5e58c816214b9046512240ef437"},
+    {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586d0d636f9a628ddc6a17bfd45aa5b5efaf1606d2b60fa5d87b8986326e933f"},
+    {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a740fa0e4087a734455f0fc3abf5e746004c9da72fbd541e9b113013c8dc3282"},
+    {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3055b0455e45810820db1f29d900bf39466df96ddca11dfa6d074fa47054376d"},
+    {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a61215eac016f391129a013c9e46f3ab308db5f5ec9f25811e811f96962599a8"},
+    {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:362d9655cd369b08fda06b6657a303eb7172d5279997abe094512e919cf74b11"},
+    {file = "msgpack-1.0.5-cp39-cp39-win32.whl", hash = "sha256:ac9dd47af78cae935901a9a500104e2dea2e253207c924cc95de149606dc43cc"},
+    {file = "msgpack-1.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:06f5174b5f8ed0ed919da0e62cbd4ffde676a374aba4020034da05fab67b9164"},
+    {file = "msgpack-1.0.5.tar.gz", hash = "sha256:c075544284eadc5cddc70f4757331d99dcbc16b2bbd4849d15f8aae4cf36d31c"},
+]
+
+[[package]]
+name = "nbclassic"
+version = "1.0.0"
+description = "Jupyter Notebook as a Jupyter Server extension."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "nbclassic-1.0.0-py3-none-any.whl", hash = "sha256:f99e4769b4750076cd4235c044b61232110733322384a94a63791d2e7beacc66"},
+    {file = "nbclassic-1.0.0.tar.gz", hash = "sha256:0ae11eb2319455d805596bf320336cda9554b41d99ab9a3c31bf8180bffa30e3"},
+]
+
+[package.dependencies]
+argon2-cffi = "*"
+ipykernel = "*"
+ipython-genutils = "*"
+jinja2 = "*"
+jupyter-client = ">=6.1.1"
+jupyter-core = ">=4.6.1"
+jupyter-server = ">=1.8"
+nbconvert = ">=5"
+nbformat = "*"
+nest-asyncio = ">=1.5"
+notebook-shim = ">=0.2.3"
+prometheus-client = "*"
+pyzmq = ">=17"
+Send2Trash = ">=1.8.0"
+terminado = ">=0.8.3"
+tornado = ">=6.1"
+traitlets = ">=4.2.1"
+
+[package.extras]
+docs = ["myst-parser", "nbsphinx", "sphinx", "sphinx-rtd-theme", "sphinxcontrib-github-alt"]
+json-logging = ["json-logging"]
+test = ["coverage", "nbval", "pytest", "pytest-cov", "pytest-jupyter", "pytest-playwright", "pytest-tornasync", "requests", "requests-unixsocket", "testpath"]
+
+[[package]]
+name = "nbclient"
+version = "0.8.0"
+description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor."
+category = "dev"
+optional = false
+python-versions = ">=3.8.0"
+files = [
+    {file = "nbclient-0.8.0-py3-none-any.whl", hash = "sha256:25e861299e5303a0477568557c4045eccc7a34c17fc08e7959558707b9ebe548"},
+    {file = "nbclient-0.8.0.tar.gz", hash = "sha256:f9b179cd4b2d7bca965f900a2ebf0db4a12ebff2f36a711cb66861e4ae158e55"},
+]
+
+[package.dependencies]
+jupyter-client = ">=6.1.12"
+jupyter-core = ">=4.12,<5.0.0 || >=5.1.0"
+nbformat = ">=5.1"
+traitlets = ">=5.4"
+
+[package.extras]
+dev = ["pre-commit"]
+docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"]
+test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"]
+
+[[package]]
+name = "nbconvert"
+version = "7.4.0"
+description = "Converting Jupyter Notebooks"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "nbconvert-7.4.0-py3-none-any.whl", hash = "sha256:af5064a9db524f9f12f4e8be7f0799524bd5b14c1adea37e34e83c95127cc818"},
+    {file = "nbconvert-7.4.0.tar.gz", hash = "sha256:51b6c77b507b177b73f6729dba15676e42c4e92bcb00edc8cc982ee72e7d89d7"},
+]
+
+[package.dependencies]
+beautifulsoup4 = "*"
+bleach = "*"
+defusedxml = "*"
+importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""}
+jinja2 = ">=3.0"
+jupyter-core = ">=4.7"
+jupyterlab-pygments = "*"
+markupsafe = ">=2.0"
+mistune = ">=2.0.3,<3"
+nbclient = ">=0.5.0"
+nbformat = ">=5.1"
+packaging = "*"
+pandocfilters = ">=1.4.1"
+pygments = ">=2.4.1"
+tinycss2 = "*"
+traitlets = ">=5.0"
+
+[package.extras]
+all = ["nbconvert[docs,qtpdf,serve,test,webpdf]"]
+docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (==5.0.2)", "sphinxcontrib-spelling"]
+qtpdf = ["nbconvert[qtpng]"]
+qtpng = ["pyqtwebengine (>=5.15)"]
+serve = ["tornado (>=6.1)"]
+test = ["ipykernel", "ipywidgets (>=7)", "pre-commit", "pytest", "pytest-dependency"]
+webpdf = ["pyppeteer (>=1,<1.1)"]
+
+[[package]]
+name = "nbformat"
+version = "5.8.0"
+description = "The Jupyter Notebook format"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "nbformat-5.8.0-py3-none-any.whl", hash = "sha256:d910082bd3e0bffcf07eabf3683ed7dda0727a326c446eeb2922abe102e65162"},
+    {file = "nbformat-5.8.0.tar.gz", hash = "sha256:46dac64c781f1c34dfd8acba16547024110348f9fc7eab0f31981c2a3dc48d1f"},
+]
+
+[package.dependencies]
+fastjsonschema = "*"
+jsonschema = ">=2.6"
+jupyter-core = "*"
+traitlets = ">=5.1"
+
+[package.extras]
+docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"]
+test = ["pep440", "pre-commit", "pytest", "testpath"]
+
+[[package]]
+name = "nest-asyncio"
+version = "1.5.6"
+description = "Patch asyncio to allow nested event loops"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+files = [
+    {file = "nest_asyncio-1.5.6-py3-none-any.whl", hash = "sha256:b9a953fb40dceaa587d109609098db21900182b16440652454a146cffb06e8b8"},
+    {file = "nest_asyncio-1.5.6.tar.gz", hash = "sha256:d267cc1ff794403f7df692964d1d2a3fa9418ffea2a3f6859a439ff482fef290"},
+]
+
+[[package]]
+name = "nodeenv"
+version = "1.8.0"
+description = "Node.js virtual environment builder"
+category = "dev"
+optional = false
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
+files = [
+    {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"},
+    {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"},
+]
+
+[package.dependencies]
+setuptools = "*"
+
+[[package]]
+name = "notebook"
+version = "6.5.4"
+description = "A web-based notebook environment for interactive computing"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "notebook-6.5.4-py3-none-any.whl", hash = "sha256:dd17e78aefe64c768737b32bf171c1c766666a21cc79a44d37a1700771cab56f"},
+    {file = "notebook-6.5.4.tar.gz", hash = "sha256:517209568bd47261e2def27a140e97d49070602eea0d226a696f42a7f16c9a4e"},
+]
+
+[package.dependencies]
+argon2-cffi = "*"
+ipykernel = "*"
+ipython-genutils = "*"
+jinja2 = "*"
+jupyter-client = ">=5.3.4"
+jupyter-core = ">=4.6.1"
+nbclassic = ">=0.4.7"
+nbconvert = ">=5"
+nbformat = "*"
+nest-asyncio = ">=1.5"
+prometheus-client = "*"
+pyzmq = ">=17"
+Send2Trash = ">=1.8.0"
+terminado = ">=0.8.3"
+tornado = ">=6.1"
+traitlets = ">=4.2.1"
+
+[package.extras]
+docs = ["myst-parser", "nbsphinx", "sphinx", "sphinx-rtd-theme", "sphinxcontrib-github-alt"]
+json-logging = ["json-logging"]
+test = ["coverage", "nbval", "pytest", "pytest-cov", "requests", "requests-unixsocket", "selenium (==4.1.5)", "testpath"]
+
+[[package]]
+name = "notebook-shim"
+version = "0.2.3"
+description = "A shim layer for notebook traits and config"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "notebook_shim-0.2.3-py3-none-any.whl", hash = "sha256:a83496a43341c1674b093bfcebf0fe8e74cbe7eda5fd2bbc56f8e39e1486c0c7"},
+    {file = "notebook_shim-0.2.3.tar.gz", hash = "sha256:f69388ac283ae008cd506dda10d0288b09a017d822d5e8c7129a152cbd3ce7e9"},
+]
+
+[package.dependencies]
+jupyter-server = ">=1.8,<3"
+
+[package.extras]
+test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync"]
+
+[[package]]
+name = "numexpr"
+version = "2.8.4"
+description = "Fast numerical expression evaluator for NumPy"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "numexpr-2.8.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a75967d46b6bd56455dd32da6285e5ffabe155d0ee61eef685bbfb8dafb2e484"},
+    {file = "numexpr-2.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db93cf1842f068247de631bfc8af20118bf1f9447cd929b531595a5e0efc9346"},
+    {file = "numexpr-2.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bca95f4473b444428061d4cda8e59ac564dc7dc6a1dea3015af9805c6bc2946"},
+    {file = "numexpr-2.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e34931089a6bafc77aaae21f37ad6594b98aa1085bb8b45d5b3cd038c3c17d9"},
+    {file = "numexpr-2.8.4-cp310-cp310-win32.whl", hash = "sha256:f3a920bfac2645017110b87ddbe364c9c7a742870a4d2f6120b8786c25dc6db3"},
+    {file = "numexpr-2.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:6931b1e9d4f629f43c14b21d44f3f77997298bea43790cfcdb4dd98804f90783"},
+    {file = "numexpr-2.8.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9400781553541f414f82eac056f2b4c965373650df9694286b9bd7e8d413f8d8"},
+    {file = "numexpr-2.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ee9db7598dd4001138b482342b96d78110dd77cefc051ec75af3295604dde6a"},
+    {file = "numexpr-2.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff5835e8af9a212e8480003d731aad1727aaea909926fd009e8ae6a1cba7f141"},
+    {file = "numexpr-2.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:655d84eb09adfee3c09ecf4a89a512225da153fdb7de13c447404b7d0523a9a7"},
+    {file = "numexpr-2.8.4-cp311-cp311-win32.whl", hash = "sha256:5538b30199bfc68886d2be18fcef3abd11d9271767a7a69ff3688defe782800a"},
+    {file = "numexpr-2.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:3f039321d1c17962c33079987b675fb251b273dbec0f51aac0934e932446ccc3"},
+    {file = "numexpr-2.8.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c867cc36cf815a3ec9122029874e00d8fbcef65035c4a5901e9b120dd5d626a2"},
+    {file = "numexpr-2.8.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:059546e8f6283ccdb47c683101a890844f667fa6d56258d48ae2ecf1b3875957"},
+    {file = "numexpr-2.8.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:845a6aa0ed3e2a53239b89c1ebfa8cf052d3cc6e053c72805e8153300078c0b1"},
+    {file = "numexpr-2.8.4-cp37-cp37m-win32.whl", hash = "sha256:a38664e699526cb1687aefd9069e2b5b9387da7feac4545de446141f1ef86f46"},
+    {file = "numexpr-2.8.4-cp37-cp37m-win_amd64.whl", hash = "sha256:eaec59e9bf70ff05615c34a8b8d6c7bd042bd9f55465d7b495ea5436f45319d0"},
+    {file = "numexpr-2.8.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b318541bf3d8326682ebada087ba0050549a16d8b3fa260dd2585d73a83d20a7"},
+    {file = "numexpr-2.8.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b076db98ca65eeaf9bd224576e3ac84c05e451c0bd85b13664b7e5f7b62e2c70"},
+    {file = "numexpr-2.8.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90f12cc851240f7911a47c91aaf223dba753e98e46dff3017282e633602e76a7"},
+    {file = "numexpr-2.8.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c368aa35ae9b18840e78b05f929d3a7b3abccdba9630a878c7db74ca2368339"},
+    {file = "numexpr-2.8.4-cp38-cp38-win32.whl", hash = "sha256:b96334fc1748e9ec4f93d5fadb1044089d73fb08208fdb8382ed77c893f0be01"},
+    {file = "numexpr-2.8.4-cp38-cp38-win_amd64.whl", hash = "sha256:a6d2d7740ae83ba5f3531e83afc4b626daa71df1ef903970947903345c37bd03"},
+    {file = "numexpr-2.8.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:77898fdf3da6bb96aa8a4759a8231d763a75d848b2f2e5c5279dad0b243c8dfe"},
+    {file = "numexpr-2.8.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df35324666b693f13a016bc7957de7cc4d8801b746b81060b671bf78a52b9037"},
+    {file = "numexpr-2.8.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17ac9cfe6d0078c5fc06ba1c1bbd20b8783f28c6f475bbabd3cad53683075cab"},
+    {file = "numexpr-2.8.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df3a1f6b24214a1ab826e9c1c99edf1686c8e307547a9aef33910d586f626d01"},
+    {file = "numexpr-2.8.4-cp39-cp39-win32.whl", hash = "sha256:7d71add384adc9119568d7e9ffa8a35b195decae81e0abf54a2b7779852f0637"},
+    {file = "numexpr-2.8.4-cp39-cp39-win_amd64.whl", hash = "sha256:9f096d707290a6a00b6ffdaf581ee37331109fb7b6c8744e9ded7c779a48e517"},
+    {file = "numexpr-2.8.4.tar.gz", hash = "sha256:d5432537418d18691b9115d615d6daa17ee8275baef3edf1afbbf8bc69806147"},
+]
+
+[package.dependencies]
+numpy = ">=1.13.3"
+
+[[package]]
+name = "numpy"
+version = "1.24.3"
+description = "Fundamental package for array computing in Python"
+category = "main"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "numpy-1.24.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c1104d3c036fb81ab923f507536daedc718d0ad5a8707c6061cdfd6d184e570"},
+    {file = "numpy-1.24.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:202de8f38fc4a45a3eea4b63e2f376e5f2dc64ef0fa692838e31a808520efaf7"},
+    {file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8535303847b89aa6b0f00aa1dc62867b5a32923e4d1681a35b5eef2d9591a463"},
+    {file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d926b52ba1367f9acb76b0df6ed21f0b16a1ad87c6720a1121674e5cf63e2b6"},
+    {file = "numpy-1.24.3-cp310-cp310-win32.whl", hash = "sha256:f21c442fdd2805e91799fbe044a7b999b8571bb0ab0f7850d0cb9641a687092b"},
+    {file = "numpy-1.24.3-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f23af8c16022663a652d3b25dcdc272ac3f83c3af4c02eb8b824e6b3ab9d7"},
+    {file = "numpy-1.24.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9a7721ec204d3a237225db3e194c25268faf92e19338a35f3a224469cb6039a3"},
+    {file = "numpy-1.24.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d6cc757de514c00b24ae8cf5c876af2a7c3df189028d68c0cb4eaa9cd5afc2bf"},
+    {file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76e3f4e85fc5d4fd311f6e9b794d0c00e7002ec122be271f2019d63376f1d385"},
+    {file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1d3c026f57ceaad42f8231305d4653d5f05dc6332a730ae5c0bea3513de0950"},
+    {file = "numpy-1.24.3-cp311-cp311-win32.whl", hash = "sha256:c91c4afd8abc3908e00a44b2672718905b8611503f7ff87390cc0ac3423fb096"},
+    {file = "numpy-1.24.3-cp311-cp311-win_amd64.whl", hash = "sha256:5342cf6aad47943286afa6f1609cad9b4266a05e7f2ec408e2cf7aea7ff69d80"},
+    {file = "numpy-1.24.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7776ea65423ca6a15255ba1872d82d207bd1e09f6d0894ee4a64678dd2204078"},
+    {file = "numpy-1.24.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ae8d0be48d1b6ed82588934aaaa179875e7dc4f3d84da18d7eae6eb3f06c242c"},
+    {file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecde0f8adef7dfdec993fd54b0f78183051b6580f606111a6d789cd14c61ea0c"},
+    {file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4749e053a29364d3452c034827102ee100986903263e89884922ef01a0a6fd2f"},
+    {file = "numpy-1.24.3-cp38-cp38-win32.whl", hash = "sha256:d933fabd8f6a319e8530d0de4fcc2e6a61917e0b0c271fded460032db42a0fe4"},
+    {file = "numpy-1.24.3-cp38-cp38-win_amd64.whl", hash = "sha256:56e48aec79ae238f6e4395886b5eaed058abb7231fb3361ddd7bfdf4eed54289"},
+    {file = "numpy-1.24.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4719d5aefb5189f50887773699eaf94e7d1e02bf36c1a9d353d9f46703758ca4"},
+    {file = "numpy-1.24.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ec87a7084caa559c36e0a2309e4ecb1baa03b687201d0a847c8b0ed476a7187"},
+    {file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea8282b9bcfe2b5e7d491d0bf7f3e2da29700cec05b49e64d6246923329f2b02"},
+    {file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210461d87fb02a84ef243cac5e814aad2b7f4be953b32cb53327bb49fd77fbb4"},
+    {file = "numpy-1.24.3-cp39-cp39-win32.whl", hash = "sha256:784c6da1a07818491b0ffd63c6bbe5a33deaa0e25a20e1b3ea20cf0e43f8046c"},
+    {file = "numpy-1.24.3-cp39-cp39-win_amd64.whl", hash = "sha256:d5036197ecae68d7f491fcdb4df90082b0d4960ca6599ba2659957aafced7c17"},
+    {file = "numpy-1.24.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:352ee00c7f8387b44d19f4cada524586f07379c0d49270f87233983bc5087ca0"},
+    {file = "numpy-1.24.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7d6acc2e7524c9955e5c903160aa4ea083736fde7e91276b0e5d98e6332812"},
+    {file = "numpy-1.24.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:35400e6a8d102fd07c71ed7dcadd9eb62ee9a6e84ec159bd48c28235bbb0f8e4"},
+    {file = "numpy-1.24.3.tar.gz", hash = "sha256:ab344f1bf21f140adab8e47fdbc7c35a477dc01408791f8ba00d018dd0bc5155"},
+]
+
+[[package]]
+name = "packaging"
+version = "23.1"
+description = "Core utilities for Python packages"
+category = "main"
+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 = "pandas"
+version = "2.0.1"
+description = "Powerful data structures for data analysis, time series, and statistics"
+category = "main"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pandas-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70a996a1d2432dadedbb638fe7d921c88b0cc4dd90374eab51bb33dc6c0c2a12"},
+    {file = "pandas-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:909a72b52175590debbf1d0c9e3e6bce2f1833c80c76d80bd1aa09188be768e5"},
+    {file = "pandas-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe7914d8ddb2d54b900cec264c090b88d141a1eed605c9539a187dbc2547f022"},
+    {file = "pandas-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a514ae436b23a92366fbad8365807fc0eed15ca219690b3445dcfa33597a5cc"},
+    {file = "pandas-2.0.1-cp310-cp310-win32.whl", hash = "sha256:12bd6618e3cc737c5200ecabbbb5eaba8ab645a4b0db508ceeb4004bb10b060e"},
+    {file = "pandas-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:2b6fe5f7ce1cba0e74188c8473c9091ead9b293ef0a6794939f8cc7947057abd"},
+    {file = "pandas-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:00959a04a1d7bbc63d75a768540fb20ecc9e65fd80744c930e23768345a362a7"},
+    {file = "pandas-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af2449e9e984dfad39276b885271ba31c5e0204ffd9f21f287a245980b0e4091"},
+    {file = "pandas-2.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:910df06feaf9935d05247db6de452f6d59820e432c18a2919a92ffcd98f8f79b"},
+    {file = "pandas-2.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fa0067f2419f933101bdc6001bcea1d50812afbd367b30943417d67fbb99678"},
+    {file = "pandas-2.0.1-cp311-cp311-win32.whl", hash = "sha256:7b8395d335b08bc8b050590da264f94a439b4770ff16bb51798527f1dd840388"},
+    {file = "pandas-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:8db5a644d184a38e6ed40feeb12d410d7fcc36648443defe4707022da127fc35"},
+    {file = "pandas-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7bbf173d364130334e0159a9a034f573e8b44a05320995127cf676b85fd8ce86"},
+    {file = "pandas-2.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c0853d487b6c868bf107a4b270a823746175b1932093b537b9b76c639fc6f7e"},
+    {file = "pandas-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25e23a03f7ad7211ffa30cb181c3e5f6d96a8e4cb22898af462a7333f8a74eb"},
+    {file = "pandas-2.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e09a53a4fe8d6ae2149959a2d02e1ef2f4d2ceb285ac48f74b79798507e468b4"},
+    {file = "pandas-2.0.1-cp38-cp38-win32.whl", hash = "sha256:a2564629b3a47b6aa303e024e3d84e850d36746f7e804347f64229f8c87416ea"},
+    {file = "pandas-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:03e677c6bc9cfb7f93a8b617d44f6091613a5671ef2944818469be7b42114a00"},
+    {file = "pandas-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3d099ecaa5b9e977b55cd43cf842ec13b14afa1cfa51b7e1179d90b38c53ce6a"},
+    {file = "pandas-2.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a37ee35a3eb6ce523b2c064af6286c45ea1c7ff882d46e10d0945dbda7572753"},
+    {file = "pandas-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:320b180d125c3842c5da5889183b9a43da4ebba375ab2ef938f57bf267a3c684"},
+    {file = "pandas-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18d22cb9043b6c6804529810f492ab09d638ddf625c5dea8529239607295cb59"},
+    {file = "pandas-2.0.1-cp39-cp39-win32.whl", hash = "sha256:90d1d365d77d287063c5e339f49b27bd99ef06d10a8843cf00b1a49326d492c1"},
+    {file = "pandas-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:99f7192d8b0e6daf8e0d0fd93baa40056684e4b4aaaef9ea78dff34168e1f2f0"},
+    {file = "pandas-2.0.1.tar.gz", hash = "sha256:19b8e5270da32b41ebf12f0e7165efa7024492e9513fb46fb631c5022ae5709d"},
+]
+
+[package.dependencies]
+numpy = [
+    {version = ">=1.20.3", markers = "python_version < \"3.10\""},
+    {version = ">=1.21.0", markers = "python_version >= \"3.10\""},
+    {version = ">=1.23.2", markers = "python_version >= \"3.11\""},
+]
+python-dateutil = ">=2.8.2"
+pytz = ">=2020.1"
+tzdata = ">=2022.1"
+
+[package.extras]
+all = ["PyQt5 (>=5.15.1)", "SQLAlchemy (>=1.4.16)", "beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.2)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "s3fs (>=2021.08.0)", "scipy (>=1.7.1)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"]
+aws = ["s3fs (>=2021.08.0)"]
+clipboard = ["PyQt5 (>=5.15.1)", "qtpy (>=2.2.0)"]
+compression = ["brotlipy (>=0.7.0)", "python-snappy (>=0.6.0)", "zstandard (>=0.15.2)"]
+computation = ["scipy (>=1.7.1)", "xarray (>=0.21.0)"]
+excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pyxlsb (>=1.0.8)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)"]
+feather = ["pyarrow (>=7.0.0)"]
+fss = ["fsspec (>=2021.07.0)"]
+gcp = ["gcsfs (>=2021.07.0)", "pandas-gbq (>=0.15.0)"]
+hdf5 = ["tables (>=3.6.1)"]
+html = ["beautifulsoup4 (>=4.9.3)", "html5lib (>=1.1)", "lxml (>=4.6.3)"]
+mysql = ["SQLAlchemy (>=1.4.16)", "pymysql (>=1.0.2)"]
+output-formatting = ["jinja2 (>=3.0.0)", "tabulate (>=0.8.9)"]
+parquet = ["pyarrow (>=7.0.0)"]
+performance = ["bottleneck (>=1.3.2)", "numba (>=0.53.1)", "numexpr (>=2.7.1)"]
+plot = ["matplotlib (>=3.6.1)"]
+postgresql = ["SQLAlchemy (>=1.4.16)", "psycopg2 (>=2.8.6)"]
+spss = ["pyreadstat (>=1.1.2)"]
+sql-other = ["SQLAlchemy (>=1.4.16)"]
+test = ["hypothesis (>=6.34.2)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"]
+xml = ["lxml (>=4.6.3)"]
+
+[[package]]
+name = "pandocfilters"
+version = "1.5.0"
+description = "Utilities for writing pandoc filters in python"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+    {file = "pandocfilters-1.5.0-py2.py3-none-any.whl", hash = "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f"},
+    {file = "pandocfilters-1.5.0.tar.gz", hash = "sha256:0b679503337d233b4339a817bfc8c50064e2eff681314376a47cb582305a7a38"},
+]
+
+[[package]]
+name = "parso"
+version = "0.8.3"
+description = "A Python Parser"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
+    {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
+]
+
+[package.extras]
+qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
+testing = ["docopt", "pytest (<6.0.0)"]
+
+[[package]]
+name = "pexpect"
+version = "4.8.0"
+description = "Pexpect allows easy control of interactive console applications."
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
+    {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
+]
+
+[package.dependencies]
+ptyprocess = ">=0.5"
+
+[[package]]
+name = "pickleshare"
+version = "0.7.5"
+description = "Tiny 'shelve'-like database with concurrency support"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
+    {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
+]
+
+[[package]]
+name = "pkgutil-resolve-name"
+version = "1.3.10"
+description = "Resolve a name to an object."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"},
+    {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"},
+]
+
+[[package]]
+name = "platformdirs"
+version = "3.5.1"
+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.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"},
+    {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"},
+]
+
+[package.extras]
+docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
+
+[[package]]
+name = "pluggy"
+version = "1.0.0"
+description = "plugin and hook calling mechanisms for python"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
+    {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
+]
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+testing = ["pytest", "pytest-benchmark"]
+
+[[package]]
+name = "pre-commit"
+version = "3.3.2"
+description = "A framework for managing and maintaining multi-language pre-commit hooks."
+category = "dev"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pre_commit-3.3.2-py2.py3-none-any.whl", hash = "sha256:8056bc52181efadf4aac792b1f4f255dfd2fb5a350ded7335d251a68561e8cb6"},
+    {file = "pre_commit-3.3.2.tar.gz", hash = "sha256:66e37bec2d882de1f17f88075047ef8962581f83c234ac08da21a0c58953d1f0"},
+]
+
+[package.dependencies]
+cfgv = ">=2.0.0"
+identify = ">=1.0.0"
+nodeenv = ">=0.11.1"
+pyyaml = ">=5.1"
+virtualenv = ">=20.10.0"
+
+[[package]]
+name = "prometheus-client"
+version = "0.16.0"
+description = "Python client for the Prometheus monitoring system."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "prometheus_client-0.16.0-py3-none-any.whl", hash = "sha256:0836af6eb2c8f4fed712b2f279f6c0a8bbab29f9f4aa15276b91c7cb0d1616ab"},
+    {file = "prometheus_client-0.16.0.tar.gz", hash = "sha256:a03e35b359f14dd1630898543e2120addfdeacd1a6069c1367ae90fd93ad3f48"},
+]
+
+[package.extras]
+twisted = ["twisted"]
+
+[[package]]
+name = "prompt-toolkit"
+version = "3.0.38"
+description = "Library for building powerful interactive command lines in Python"
+category = "dev"
+optional = false
+python-versions = ">=3.7.0"
+files = [
+    {file = "prompt_toolkit-3.0.38-py3-none-any.whl", hash = "sha256:45ea77a2f7c60418850331366c81cf6b5b9cf4c7fd34616f733c5427e6abbb1f"},
+    {file = "prompt_toolkit-3.0.38.tar.gz", hash = "sha256:23ac5d50538a9a38c8bde05fecb47d0b403ecd0662857a86f886f798563d5b9b"},
+]
+
+[package.dependencies]
+wcwidth = "*"
+
+[[package]]
+name = "psutil"
+version = "5.9.5"
+description = "Cross-platform lib for process and system monitoring in Python."
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+    {file = "psutil-5.9.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:be8929ce4313f9f8146caad4272f6abb8bf99fc6cf59344a3167ecd74f4f203f"},
+    {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ab8ed1a1d77c95453db1ae00a3f9c50227ebd955437bcf2a574ba8adbf6a74d5"},
+    {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4aef137f3345082a3d3232187aeb4ac4ef959ba3d7c10c33dd73763fbc063da4"},
+    {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ea8518d152174e1249c4f2a1c89e3e6065941df2fa13a1ab45327716a23c2b48"},
+    {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:acf2aef9391710afded549ff602b5887d7a2349831ae4c26be7c807c0a39fac4"},
+    {file = "psutil-5.9.5-cp27-none-win32.whl", hash = "sha256:5b9b8cb93f507e8dbaf22af6a2fd0ccbe8244bf30b1baad6b3954e935157ae3f"},
+    {file = "psutil-5.9.5-cp27-none-win_amd64.whl", hash = "sha256:8c5f7c5a052d1d567db4ddd231a9d27a74e8e4a9c3f44b1032762bd7b9fdcd42"},
+    {file = "psutil-5.9.5-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3c6f686f4225553615612f6d9bc21f1c0e305f75d7d8454f9b46e901778e7217"},
+    {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a7dd9997128a0d928ed4fb2c2d57e5102bb6089027939f3b722f3a210f9a8da"},
+    {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89518112647f1276b03ca97b65cc7f64ca587b1eb0278383017c2a0dcc26cbe4"},
+    {file = "psutil-5.9.5-cp36-abi3-win32.whl", hash = "sha256:104a5cc0e31baa2bcf67900be36acde157756b9c44017b86b2c049f11957887d"},
+    {file = "psutil-5.9.5-cp36-abi3-win_amd64.whl", hash = "sha256:b258c0c1c9d145a1d5ceffab1134441c4c5113b2417fafff7315a917a026c3c9"},
+    {file = "psutil-5.9.5-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:c607bb3b57dc779d55e1554846352b4e358c10fff3abf3514a7a6601beebdb30"},
+    {file = "psutil-5.9.5.tar.gz", hash = "sha256:5410638e4df39c54d957fc51ce03048acd8e6d60abc0f5107af51e5fb566eb3c"},
+]
+
+[package.extras]
+test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
+
+[[package]]
+name = "ptyprocess"
+version = "0.7.0"
+description = "Run a subprocess in a pseudo terminal"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
+    {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
+]
+
+[[package]]
+name = "pure-eval"
+version = "0.2.2"
+description = "Safely evaluate AST nodes without side effects"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"},
+    {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"},
+]
+
+[package.extras]
+tests = ["pytest"]
+
+[[package]]
+name = "py-cpuinfo"
+version = "9.0.0"
+description = "Get CPU info with pure Python"
+category = "main"
+optional = false
+python-versions = "*"
+files = [
+    {file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"},
+    {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"},
+]
+
+[[package]]
+name = "pycparser"
+version = "2.21"
+description = "C parser in Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+    {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
+    {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
+]
+
+[[package]]
+name = "pygments"
+version = "2.15.1"
+description = "Pygments is a syntax highlighting package written in Python."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"},
+    {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"},
+]
+
+[package.extras]
+plugins = ["importlib-metadata"]
+
+[[package]]
+name = "pyodbc"
+version = "4.0.39"
+description = "DB API Module for ODBC"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+files = [
+    {file = "pyodbc-4.0.39-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74af348dbaee4885998858daf50c8964e767629ecf6c195868b016367b0bb861"},
+    {file = "pyodbc-4.0.39-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0f5901b57eaef0761f4cf02bca8e7c63f589fd0fd723a79f6ccf1ea1275372e5"},
+    {file = "pyodbc-4.0.39-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0db69478d00fcd8d0b9bdde8aca0b0eada341fd6ed8c2da84b594b928c84106"},
+    {file = "pyodbc-4.0.39-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5faf2870e9d434c6a85c6adc1cdff55c0e376273baf480f06d9848025405688"},
+    {file = "pyodbc-4.0.39-cp310-cp310-win32.whl", hash = "sha256:62bb6d7d0d25dc75d1445e539f946461c9c5a3643ae14676b240f71794ea004f"},
+    {file = "pyodbc-4.0.39-cp310-cp310-win_amd64.whl", hash = "sha256:8eb5547282dc73a7784ce7b99584f68687dd85543538ca6f70cffaa6310676e7"},
+    {file = "pyodbc-4.0.39-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:530c1ac37ead782803b44fb1934ba4c68ed4a6969f7475cb8bc04ae1da14486e"},
+    {file = "pyodbc-4.0.39-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1f7fb65191926308f09ce75ae7ccecf89310232ee50cdea74edf17ee04a9b068"},
+    {file = "pyodbc-4.0.39-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ec009180fcd7c8197f45d083e6670623d8dfe198a457ca2a50ebb1bafe4107f"},
+    {file = "pyodbc-4.0.39-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:400e911d54980098c6badadecc82385fc0d6a9057db525d63d2652317df43efe"},
+    {file = "pyodbc-4.0.39-cp311-cp311-win32.whl", hash = "sha256:f792677b88e1dde12dab46de8647620fc8171742c02780d51744f7b1b2135dbc"},
+    {file = "pyodbc-4.0.39-cp311-cp311-win_amd64.whl", hash = "sha256:3d9d70e1635d35ba3aee3df216ec8e35f2824909f43331c0112b17f460a93923"},
+    {file = "pyodbc-4.0.39-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c1a59096f1784d0cda3d0b8f393849f05515c46a10016edb6da1b1960d039800"},
+    {file = "pyodbc-4.0.39-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b3467157661615d5c30893efa1069b55c9ffa434097fc3ae3739e740d83d2ec"},
+    {file = "pyodbc-4.0.39-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af027a60e84274ea08fad1c75991d37a5f1f6e8bcd30f6bda20db99f0cdfbc7d"},
+    {file = "pyodbc-4.0.39-cp36-cp36m-win32.whl", hash = "sha256:64c1de1263281de7b5ce585b0352746ab1a483453017a8589f838a79cbe3d6d9"},
+    {file = "pyodbc-4.0.39-cp36-cp36m-win_amd64.whl", hash = "sha256:27d1b3c3159673b44c97c878f9d8056901d45f747ce2e0b4d5d99f0fb6949dc7"},
+    {file = "pyodbc-4.0.39-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:efccc11dff6fba684a74ae1030c92ff8b82429d7f00e0a50aa2ac6f56621cd9f"},
+    {file = "pyodbc-4.0.39-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea08e9379c08663d7260e2b8a6c451f56d36c17291af735191089f8e29ad9578"},
+    {file = "pyodbc-4.0.39-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b36fe804d367d01ad81077fa524a36e667aabc3945e32564c7ef9595b28edfa9"},
+    {file = "pyodbc-4.0.39-cp37-cp37m-win32.whl", hash = "sha256:72d364e52f6ca2417881a23834b3a36733c09e0dcd4760f49a6b864218d98d92"},
+    {file = "pyodbc-4.0.39-cp37-cp37m-win_amd64.whl", hash = "sha256:39f6c56022c764309aa7552c0eb2c58fbb5902ab5d2010d42b021c0b205aa609"},
+    {file = "pyodbc-4.0.39-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ebcb900fcaf19ca2bc38632218c5d48c666fcc19fe38b08cde001917f4581456"},
+    {file = "pyodbc-4.0.39-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a3e133621ac2dad22d0870a8521c7e82d4270e24ce02451d64e7eb6a40ad0941"},
+    {file = "pyodbc-4.0.39-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05a0912e852ebddaffa8f235b0f3974475021dd8eb604eb46ea67af06efe1239"},
+    {file = "pyodbc-4.0.39-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6353044b99c763aeec7ca1760b4340298504d8ee544fdcab3c380a2abec15b78"},
+    {file = "pyodbc-4.0.39-cp38-cp38-win32.whl", hash = "sha256:a591a1cf3c251a9c7c1642cfb3774119bf3512f3be56151247238f8a7b22b336"},
+    {file = "pyodbc-4.0.39-cp38-cp38-win_amd64.whl", hash = "sha256:8553eaef9f8ec333bbddff6eadf0d322dda34b37f4bab19f0658eb532037840c"},
+    {file = "pyodbc-4.0.39-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9253e746c5c94bf61e3e9adb08fb7688d413cb68c06ebb287ec233387534760a"},
+    {file = "pyodbc-4.0.39-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a6f4067f46aaa78e77e8a15ade81eb21fb344563d245fb2d9a0aaa553c367cbd"},
+    {file = "pyodbc-4.0.39-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdf5a27e6587d1762f7f0e35d6f0309f09019bf3e19ca9177a4b765121f3f106"},
+    {file = "pyodbc-4.0.39-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe4ee87b88867867f582dd0c1236cd982508db359a6cbb5e91623ceb6c83e60a"},
+    {file = "pyodbc-4.0.39-cp39-cp39-win32.whl", hash = "sha256:42649ed57d09c04aa197bdd4fe0aa9ca319790b7aa86d0b0784cc70e78c426e5"},
+    {file = "pyodbc-4.0.39-cp39-cp39-win_amd64.whl", hash = "sha256:305c7d6337e2d4c8350677cc641b343fc0197b7b9bc167815c66b64545c67a53"},
+    {file = "pyodbc-4.0.39.tar.gz", hash = "sha256:e528bb70dd6d6299ee429868925df0866e3e919c772b9eff79c8e17920d8f116"},
+]
+
+[[package]]
+name = "pyrsistent"
+version = "0.19.3"
+description = "Persistent/Functional/Immutable data structures"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"},
+    {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"},
+    {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"},
+    {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"},
+    {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"},
+    {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"},
+    {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"},
+    {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"},
+    {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"},
+    {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"},
+    {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"},
+    {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"},
+    {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"},
+    {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"},
+    {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"},
+    {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"},
+    {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"},
+    {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"},
+    {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"},
+    {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"},
+    {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"},
+    {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"},
+    {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"},
+    {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"},
+    {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"},
+    {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"},
+    {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"},
+]
+
+[[package]]
+name = "pyspnego"
+version = "0.9.0"
+description = "Windows Negotiate Authentication Client and Server"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "pyspnego-0.9.0-cp310-cp310-win32.whl", hash = "sha256:f529b30a0ccfb0219b5b5b996c8a90cf5df0da6bcf19da33262573f9cca4d487"},
+    {file = "pyspnego-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:f656b22298a3582d210f8dfdd6541b0fb277b0020f9b649b329dea3688ff60da"},
+    {file = "pyspnego-0.9.0-cp311-cp311-win32.whl", hash = "sha256:2f2e51c7230f95febe9ff4e00eaa7c24ce64b197ae6c53dc85c7df88aa416d93"},
+    {file = "pyspnego-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:8d17f5157c572aa0fc5899211f8092917d1bee2ef84046684d939aa481a958f0"},
+    {file = "pyspnego-0.9.0-cp37-cp37m-win32.whl", hash = "sha256:c4111a7584aea4e30b1cd2e0c0b1f8355ed4fb413cce21e2cecef8bfdf0eabcf"},
+    {file = "pyspnego-0.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:44f39ec4c93c8cc51516a6b16365160e9dff622310afef9ee8456626bafda26b"},
+    {file = "pyspnego-0.9.0-cp38-cp38-win32.whl", hash = "sha256:9b9e5a645dcd4645537c7dd38e69124fa524eba501b65fb1ab2825e80038a5dc"},
+    {file = "pyspnego-0.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:d498ff81da792ace2eea7f55a103a49a363882b820723f99dc5ccf03ea7f3b3e"},
+    {file = "pyspnego-0.9.0-cp39-cp39-win32.whl", hash = "sha256:187aa308fb40be4292753e86f9ee3df1c48500accdb50bcfecb7246b618cd7d8"},
+    {file = "pyspnego-0.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:77b965afaed4214b63dddabefdef762f3f9c9796349408d200c64a5921de809b"},
+    {file = "pyspnego-0.9.0-py3-none-any.whl", hash = "sha256:18da3710c383167e54ae2b01f640f269de2cdc0992a8f72928e349441361c21d"},
+    {file = "pyspnego-0.9.0.tar.gz", hash = "sha256:3f10ca7a2f0d5170c2032bb5d55183384208314032aa44c1684d6920d50e97de"},
+]
+
+[package.dependencies]
+cryptography = "*"
+gssapi = {version = ">=1.6.0", optional = true, markers = "sys_platform != \"win32\" and extra == \"kerberos\""}
+krb5 = {version = ">=0.3.0", optional = true, markers = "sys_platform != \"win32\" and extra == \"kerberos\""}
+
+[package.extras]
+kerberos = ["gssapi (>=1.6.0)", "krb5 (>=0.3.0)"]
+yaml = ["ruamel.yaml"]
+
+[[package]]
+name = "pytest"
+version = "7.3.1"
+description = "pytest: simple powerful testing with Python"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"},
+    {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
+iniconfig = "*"
+packaging = "*"
+pluggy = ">=0.12,<2.0"
+tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
+
+[package.extras]
+testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
+
+[[package]]
+name = "python-dateutil"
+version = "2.8.2"
+description = "Extensions to the standard Python datetime module"
+category = "main"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+files = [
+    {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
+    {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
+]
+
+[package.dependencies]
+six = ">=1.5"
+
+[[package]]
+name = "python-json-logger"
+version = "2.0.7"
+description = "A python library adding a json log formatter"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "python-json-logger-2.0.7.tar.gz", hash = "sha256:23e7ec02d34237c5aa1e29a070193a4ea87583bb4e7f8fd06d3de8264c4b2e1c"},
+    {file = "python_json_logger-2.0.7-py3-none-any.whl", hash = "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd"},
+]
+
+[[package]]
+name = "pytz"
+version = "2023.3"
+description = "World timezone definitions, modern and historical"
+category = "main"
+optional = false
+python-versions = "*"
+files = [
+    {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"},
+    {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"},
+]
+
+[[package]]
+name = "pywin32"
+version = "306"
+description = "Python for Window Extensions"
+category = "dev"
+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 = "pywinpty"
+version = "2.0.10"
+description = "Pseudo terminal support for Windows from Python."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "pywinpty-2.0.10-cp310-none-win_amd64.whl", hash = "sha256:4c7d06ad10f6e92bc850a467f26d98f4f30e73d2fe5926536308c6ae0566bc16"},
+    {file = "pywinpty-2.0.10-cp311-none-win_amd64.whl", hash = "sha256:7ffbd66310b83e42028fc9df7746118978d94fba8c1ebf15a7c1275fdd80b28a"},
+    {file = "pywinpty-2.0.10-cp37-none-win_amd64.whl", hash = "sha256:38cb924f2778b5751ef91a75febd114776b3af0ae411bc667be45dd84fc881d3"},
+    {file = "pywinpty-2.0.10-cp38-none-win_amd64.whl", hash = "sha256:902d79444b29ad1833b8d5c3c9aabdfd428f4f068504430df18074007c8c0de8"},
+    {file = "pywinpty-2.0.10-cp39-none-win_amd64.whl", hash = "sha256:3c46aef80dd50979aff93de199e4a00a8ee033ba7a03cadf0a91fed45f0c39d7"},
+    {file = "pywinpty-2.0.10.tar.gz", hash = "sha256:cdbb5694cf8c7242c2ecfaca35c545d31fa5d5814c3d67a4e628f803f680ebea"},
+]
+
+[[package]]
+name = "pyyaml"
+version = "6.0"
+description = "YAML parser and emitter for Python"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
+    {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
+    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
+    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
+    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
+    {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
+    {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
+    {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"},
+    {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"},
+    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"},
+    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"},
+    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"},
+    {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"},
+    {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"},
+    {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
+    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
+    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
+    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
+    {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
+    {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
+    {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
+    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
+    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
+    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
+    {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
+    {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
+    {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
+    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
+    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
+    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
+    {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
+    {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
+    {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
+    {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
+    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
+    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
+    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
+    {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
+    {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
+    {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
+]
+
+[[package]]
+name = "pyzmq"
+version = "25.0.2"
+description = "Python bindings for 0MQ"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "pyzmq-25.0.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ac178e666c097c8d3deb5097b58cd1316092fc43e8ef5b5fdb259b51da7e7315"},
+    {file = "pyzmq-25.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:659e62e1cbb063151c52f5b01a38e1df6b54feccfa3e2509d44c35ca6d7962ee"},
+    {file = "pyzmq-25.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8280ada89010735a12b968ec3ea9a468ac2e04fddcc1cede59cb7f5178783b9c"},
+    {file = "pyzmq-25.0.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b5eeb5278a8a636bb0abdd9ff5076bcbb836cd2302565df53ff1fa7d106d54"},
+    {file = "pyzmq-25.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a2e5fe42dfe6b73ca120b97ac9f34bfa8414feb15e00e37415dbd51cf227ef6"},
+    {file = "pyzmq-25.0.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:827bf60e749e78acb408a6c5af6688efbc9993e44ecc792b036ec2f4b4acf485"},
+    {file = "pyzmq-25.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7b504ae43d37e282301da586529e2ded8b36d4ee2cd5e6db4386724ddeaa6bbc"},
+    {file = "pyzmq-25.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb1f69a0a2a2b1aae8412979dd6293cc6bcddd4439bf07e4758d864ddb112354"},
+    {file = "pyzmq-25.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b9c9cc965cdf28381e36da525dcb89fc1571d9c54800fdcd73e3f73a2fc29bd"},
+    {file = "pyzmq-25.0.2-cp310-cp310-win32.whl", hash = "sha256:24abbfdbb75ac5039205e72d6c75f10fc39d925f2df8ff21ebc74179488ebfca"},
+    {file = "pyzmq-25.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6a821a506822fac55d2df2085a52530f68ab15ceed12d63539adc32bd4410f6e"},
+    {file = "pyzmq-25.0.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:9af0bb0277e92f41af35e991c242c9c71920169d6aa53ade7e444f338f4c8128"},
+    {file = "pyzmq-25.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54a96cf77684a3a537b76acfa7237b1e79a8f8d14e7f00e0171a94b346c5293e"},
+    {file = "pyzmq-25.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88649b19ede1cab03b96b66c364cbbf17c953615cdbc844f7f6e5f14c5e5261c"},
+    {file = "pyzmq-25.0.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:715cff7644a80a7795953c11b067a75f16eb9fc695a5a53316891ebee7f3c9d5"},
+    {file = "pyzmq-25.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:312b3f0f066b4f1d17383aae509bacf833ccaf591184a1f3c7a1661c085063ae"},
+    {file = "pyzmq-25.0.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d488c5c8630f7e782e800869f82744c3aca4aca62c63232e5d8c490d3d66956a"},
+    {file = "pyzmq-25.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:38d9f78d69bcdeec0c11e0feb3bc70f36f9b8c44fc06e5d06d91dc0a21b453c7"},
+    {file = "pyzmq-25.0.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3059a6a534c910e1d5d068df42f60d434f79e6cc6285aa469b384fa921f78cf8"},
+    {file = "pyzmq-25.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6526d097b75192f228c09d48420854d53dfbc7abbb41b0e26f363ccb26fbc177"},
+    {file = "pyzmq-25.0.2-cp311-cp311-win32.whl", hash = "sha256:5c5fbb229e40a89a2fe73d0c1181916f31e30f253cb2d6d91bea7927c2e18413"},
+    {file = "pyzmq-25.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:ed15e3a2c3c2398e6ae5ce86d6a31b452dfd6ad4cd5d312596b30929c4b6e182"},
+    {file = "pyzmq-25.0.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:032f5c8483c85bf9c9ca0593a11c7c749d734ce68d435e38c3f72e759b98b3c9"},
+    {file = "pyzmq-25.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:374b55516393bfd4d7a7daa6c3b36d6dd6a31ff9d2adad0838cd6a203125e714"},
+    {file = "pyzmq-25.0.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:08bfcc21b5997a9be4fefa405341320d8e7f19b4d684fb9c0580255c5bd6d695"},
+    {file = "pyzmq-25.0.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1a843d26a8da1b752c74bc019c7b20e6791ee813cd6877449e6a1415589d22ff"},
+    {file = "pyzmq-25.0.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:b48616a09d7df9dbae2f45a0256eee7b794b903ddc6d8657a9948669b345f220"},
+    {file = "pyzmq-25.0.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d4427b4a136e3b7f85516c76dd2e0756c22eec4026afb76ca1397152b0ca8145"},
+    {file = "pyzmq-25.0.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:26b0358e8933990502f4513c991c9935b6c06af01787a36d133b7c39b1df37fa"},
+    {file = "pyzmq-25.0.2-cp36-cp36m-win32.whl", hash = "sha256:c8fedc3ccd62c6b77dfe6f43802057a803a411ee96f14e946f4a76ec4ed0e117"},
+    {file = "pyzmq-25.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:2da6813b7995b6b1d1307329c73d3e3be2fd2d78e19acfc4eff2e27262732388"},
+    {file = "pyzmq-25.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a35960c8b2f63e4ef67fd6731851030df68e4b617a6715dd11b4b10312d19fef"},
+    {file = "pyzmq-25.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eef2a0b880ab40aca5a878933376cb6c1ec483fba72f7f34e015c0f675c90b20"},
+    {file = "pyzmq-25.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:85762712b74c7bd18e340c3639d1bf2f23735a998d63f46bb6584d904b5e401d"},
+    {file = "pyzmq-25.0.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:64812f29d6eee565e129ca14b0c785744bfff679a4727137484101b34602d1a7"},
+    {file = "pyzmq-25.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:510d8e55b3a7cd13f8d3e9121edf0a8730b87d925d25298bace29a7e7bc82810"},
+    {file = "pyzmq-25.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b164cc3c8acb3d102e311f2eb6f3c305865ecb377e56adc015cb51f721f1dda6"},
+    {file = "pyzmq-25.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:28fdb9224a258134784a9cf009b59265a9dde79582fb750d4e88a6bcbc6fa3dc"},
+    {file = "pyzmq-25.0.2-cp37-cp37m-win32.whl", hash = "sha256:dd771a440effa1c36d3523bc6ba4e54ff5d2e54b4adcc1e060d8f3ca3721d228"},
+    {file = "pyzmq-25.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:9bdc40efb679b9dcc39c06d25629e55581e4c4f7870a5e88db4f1c51ce25e20d"},
+    {file = "pyzmq-25.0.2-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:1f82906a2d8e4ee310f30487b165e7cc8ed09c009e4502da67178b03083c4ce0"},
+    {file = "pyzmq-25.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:21ec0bf4831988af43c8d66ba3ccd81af2c5e793e1bf6790eb2d50e27b3c570a"},
+    {file = "pyzmq-25.0.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abbce982a17c88d2312ec2cf7673985d444f1beaac6e8189424e0a0e0448dbb3"},
+    {file = "pyzmq-25.0.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9e1d2f2d86fc75ed7f8845a992c5f6f1ab5db99747fb0d78b5e4046d041164d2"},
+    {file = "pyzmq-25.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2e92ff20ad5d13266bc999a29ed29a3b5b101c21fdf4b2cf420c09db9fb690e"},
+    {file = "pyzmq-25.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edbbf06cc2719889470a8d2bf5072bb00f423e12de0eb9ffec946c2c9748e149"},
+    {file = "pyzmq-25.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:77942243ff4d14d90c11b2afd8ee6c039b45a0be4e53fb6fa7f5e4fd0b59da39"},
+    {file = "pyzmq-25.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ab046e9cb902d1f62c9cc0eca055b1d11108bdc271caf7c2171487298f229b56"},
+    {file = "pyzmq-25.0.2-cp38-cp38-win32.whl", hash = "sha256:ad761cfbe477236802a7ab2c080d268c95e784fe30cafa7e055aacd1ca877eb0"},
+    {file = "pyzmq-25.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:8560756318ec7c4c49d2c341012167e704b5a46d9034905853c3d1ade4f55bee"},
+    {file = "pyzmq-25.0.2-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:ab2c056ac503f25a63f6c8c6771373e2a711b98b304614151dfb552d3d6c81f6"},
+    {file = "pyzmq-25.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cca8524b61c0eaaa3505382dc9b9a3bc8165f1d6c010fdd1452c224225a26689"},
+    {file = "pyzmq-25.0.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb9f7eae02d3ac42fbedad30006b7407c984a0eb4189a1322241a20944d61e5"},
+    {file = "pyzmq-25.0.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5eaeae038c68748082137d6896d5c4db7927e9349237ded08ee1bbd94f7361c9"},
+    {file = "pyzmq-25.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a31992a8f8d51663ebf79df0df6a04ffb905063083d682d4380ab8d2c67257c"},
+    {file = "pyzmq-25.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6a979e59d2184a0c8f2ede4b0810cbdd86b64d99d9cc8a023929e40dce7c86cc"},
+    {file = "pyzmq-25.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1f124cb73f1aa6654d31b183810febc8505fd0c597afa127c4f40076be4574e0"},
+    {file = "pyzmq-25.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:65c19a63b4a83ae45d62178b70223adeee5f12f3032726b897431b6553aa25af"},
+    {file = "pyzmq-25.0.2-cp39-cp39-win32.whl", hash = "sha256:83d822e8687621bed87404afc1c03d83fa2ce39733d54c2fd52d8829edb8a7ff"},
+    {file = "pyzmq-25.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:24683285cc6b7bf18ad37d75b9db0e0fefe58404e7001f1d82bf9e721806daa7"},
+    {file = "pyzmq-25.0.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a4b4261eb8f9ed71f63b9eb0198dd7c934aa3b3972dac586d0ef502ba9ab08b"},
+    {file = "pyzmq-25.0.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:62ec8d979f56c0053a92b2b6a10ff54b9ec8a4f187db2b6ec31ee3dd6d3ca6e2"},
+    {file = "pyzmq-25.0.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:affec1470351178e892121b3414c8ef7803269f207bf9bef85f9a6dd11cde264"},
+    {file = "pyzmq-25.0.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffc71111433bd6ec8607a37b9211f4ef42e3d3b271c6d76c813669834764b248"},
+    {file = "pyzmq-25.0.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:6fadc60970714d86eff27821f8fb01f8328dd36bebd496b0564a500fe4a9e354"},
+    {file = "pyzmq-25.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:269968f2a76c0513490aeb3ba0dc3c77b7c7a11daa894f9d1da88d4a0db09835"},
+    {file = "pyzmq-25.0.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f7c8b8368e84381ae7c57f1f5283b029c888504aaf4949c32e6e6fb256ec9bf0"},
+    {file = "pyzmq-25.0.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25e6873a70ad5aa31e4a7c41e5e8c709296edef4a92313e1cd5fc87bbd1874e2"},
+    {file = "pyzmq-25.0.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b733076ff46e7db5504c5e7284f04a9852c63214c74688bdb6135808531755a3"},
+    {file = "pyzmq-25.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a6f6ae12478fdc26a6d5fdb21f806b08fa5403cd02fd312e4cb5f72df078f96f"},
+    {file = "pyzmq-25.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:67da1c213fbd208906ab3470cfff1ee0048838365135a9bddc7b40b11e6d6c89"},
+    {file = "pyzmq-25.0.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:531e36d9fcd66f18de27434a25b51d137eb546931033f392e85674c7a7cea853"},
+    {file = "pyzmq-25.0.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34a6fddd159ff38aa9497b2e342a559f142ab365576284bc8f77cb3ead1f79c5"},
+    {file = "pyzmq-25.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b491998ef886662c1f3d49ea2198055a9a536ddf7430b051b21054f2a5831800"},
+    {file = "pyzmq-25.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:5d496815074e3e3d183fe2c7fcea2109ad67b74084c254481f87b64e04e9a471"},
+    {file = "pyzmq-25.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:56a94ab1d12af982b55ca96c6853db6ac85505e820d9458ac76364c1998972f4"},
+    {file = "pyzmq-25.0.2.tar.gz", hash = "sha256:6b8c1bbb70e868dc88801aa532cae6bd4e3b5233784692b786f17ad2962e5149"},
+]
+
+[package.dependencies]
+cffi = {version = "*", markers = "implementation_name == \"pypy\""}
+
+[[package]]
+name = "qtconsole"
+version = "5.4.3"
+description = "Jupyter Qt console"
+category = "dev"
+optional = false
+python-versions = ">= 3.7"
+files = [
+    {file = "qtconsole-5.4.3-py3-none-any.whl", hash = "sha256:35fd6e87b1f6d1fd41801b07e69339f8982e76afd4fa8ef35595bc6036717189"},
+    {file = "qtconsole-5.4.3.tar.gz", hash = "sha256:5e4082a86a201796b2a5cfd4298352d22b158b51b57736531824715fc2a979dd"},
+]
+
+[package.dependencies]
+ipykernel = ">=4.1"
+ipython-genutils = "*"
+jupyter-client = ">=4.1"
+jupyter-core = "*"
+packaging = "*"
+pygments = "*"
+pyzmq = ">=17.1"
+qtpy = ">=2.0.1"
+traitlets = "<5.2.1 || >5.2.1,<5.2.2 || >5.2.2"
+
+[package.extras]
+doc = ["Sphinx (>=1.3)"]
+test = ["flaky", "pytest", "pytest-qt"]
+
+[[package]]
+name = "qtpy"
+version = "2.3.1"
+description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6)."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "QtPy-2.3.1-py3-none-any.whl", hash = "sha256:5193d20e0b16e4d9d3bc2c642d04d9f4e2c892590bd1b9c92bfe38a95d5a2e12"},
+    {file = "QtPy-2.3.1.tar.gz", hash = "sha256:a8c74982d6d172ce124d80cafd39653df78989683f760f2281ba91a6e7b9de8b"},
+]
+
+[package.dependencies]
+packaging = "*"
+
+[package.extras]
+test = ["pytest (>=6,!=7.0.0,!=7.0.1)", "pytest-cov (>=3.0.0)", "pytest-qt"]
+
+[[package]]
+name = "requests"
+version = "2.31.0"
+description = "Python HTTP for Humans."
+category = "main"
+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 = "requests-kerberos"
+version = "0.14.0"
+description = "A Kerberos authentication handler for python-requests"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "requests-kerberos-0.14.0.tar.gz", hash = "sha256:cda9d1240ae5392e081869881c8742d0e171fd6a893a7ac0875db2748e966fd1"},
+    {file = "requests_kerberos-0.14.0-py2.py3-none-any.whl", hash = "sha256:da74ea478ccd8584de88092bdcd17a7c29d494374a340d1d8677189903c9ac6a"},
+]
+
+[package.dependencies]
+cryptography = ">=1.3"
+pyspnego = {version = "*", extras = ["kerberos"]}
+requests = ">=1.1.0"
+
+[[package]]
+name = "rfc3339-validator"
+version = "0.1.4"
+description = "A pure python RFC3339 validator"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+files = [
+    {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"},
+    {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"},
+]
+
+[package.dependencies]
+six = "*"
+
+[[package]]
+name = "rfc3986-validator"
+version = "0.1.1"
+description = "Pure python rfc3986 validator"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+files = [
+    {file = "rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9"},
+    {file = "rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055"},
+]
+
+[[package]]
+name = "send2trash"
+version = "1.8.2"
+description = "Send file to trash natively under Mac OS X, Windows and Linux"
+category = "dev"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+files = [
+    {file = "Send2Trash-1.8.2-py3-none-any.whl", hash = "sha256:a384719d99c07ce1eefd6905d2decb6f8b7ed054025bb0e618919f945de4f679"},
+    {file = "Send2Trash-1.8.2.tar.gz", hash = "sha256:c132d59fa44b9ca2b1699af5c86f57ce9f4c5eb56629d5d55fbb7a35f84e2312"},
+]
+
+[package.extras]
+nativelib = ["pyobjc-framework-Cocoa", "pywin32"]
+objc = ["pyobjc-framework-Cocoa"]
+win32 = ["pywin32"]
+
+[[package]]
+name = "setuptools"
+version = "67.8.0"
+description = "Easily download, build, install, upgrade, and uninstall Python packages"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"},
+    {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"},
+]
+
+[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 = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+category = "main"
+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 = "sniffio"
+version = "1.3.0"
+description = "Sniff out which async library your code is running under"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
+    {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
+]
+
+[[package]]
+name = "soupsieve"
+version = "2.4.1"
+description = "A modern CSS selector implementation for Beautiful Soup."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"},
+    {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"},
+]
+
+[[package]]
+name = "stack-data"
+version = "0.6.2"
+description = "Extract data from python stack frames and tracebacks for informative displays"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "stack_data-0.6.2-py3-none-any.whl", hash = "sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8"},
+    {file = "stack_data-0.6.2.tar.gz", hash = "sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815"},
+]
+
+[package.dependencies]
+asttokens = ">=2.1.0"
+executing = ">=1.2.0"
+pure-eval = "*"
+
+[package.extras]
+tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
+
+[[package]]
+name = "tables"
+version = "3.8.0"
+description = "Hierarchical datasets for Python"
+category = "main"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "tables-3.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:01e82e40f9845f71de137b4472210909e35c440bbcd0858bdd2871715daef4c7"},
+    {file = "tables-3.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db185d855afd45a7259ddd0b53e5f2f8993bb134b370002c6c19532f27ce92ac"},
+    {file = "tables-3.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70a3585a268beee6d0e71bfc9abec98da84d168182f350a2ffa1ae5e42798c18"},
+    {file = "tables-3.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:117cf0f73ee2a5cba5c2b04e4aca375779aec66045aa63128e043dc608f2023b"},
+    {file = "tables-3.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2861cd3ef9eb95eead7530e4de49fd130954871e7e6d2e288012797cb9d7c2e8"},
+    {file = "tables-3.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e9bdbfbe025b6c751976382123c5f5cbd8fab6956aed776b0e8c889669e90d3"},
+    {file = "tables-3.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0821007048f2af8c1a21eb3d832072046c5df366e39587a7c7e4afad14e73fc"},
+    {file = "tables-3.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:b9370c2a4dc0051aad6b71de4f1f9b0b8b60d30b662df5c742434f2b5c6a005e"},
+    {file = "tables-3.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e19686fad4e8f5a91c3dc1eb4b7ea928838e86fefa474c63c5787a125ea79fc7"},
+    {file = "tables-3.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:239f15fa9881c257b5c0d9fb4cb8832778af1c5c8c1db6f6722466f8f26541e2"},
+    {file = "tables-3.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c83a74cac3c0629a0e83570d465f88843ef3609ef56a8ef9a49ee85ab3b8f02f"},
+    {file = "tables-3.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:a5ccb80651c5fad6ac744e2a756b28cfac78eab3b8503f4a2320ee6653b3bee9"},
+    {file = "tables-3.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3375bfafc6cf305d13617a572ab3fffc51fae2fbe0f6efce9407a41f79970b62"},
+    {file = "tables-3.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:254a4d5c2009c7ebe4293b02b8d91ea60837bff85a3c0a40cd075b8f12b1e6c3"},
+    {file = "tables-3.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da3c96456c473fb977cf6dbca9e889710ac020df1fa5b9ebb7f676e83996337d"},
+    {file = "tables-3.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:72da9404094ef8277bf62fce8873e8dc141cee9a8763ec8e7080b2d0de206094"},
+    {file = "tables-3.8.0.tar.gz", hash = "sha256:34f3fa2366ce20b18f1df573a77c1d27306ce1f2a41d9f9eff621b5192ea8788"},
+]
+
+[package.dependencies]
+blosc2 = ">=2.0.0,<2.1.0"
+cython = ">=0.29.21"
+numexpr = ">=2.6.2"
+numpy = ">=1.19.0"
+packaging = "*"
+py-cpuinfo = "*"
+
+[package.extras]
+doc = ["ipython", "numpydoc", "sphinx (>=1.1)", "sphinx-rtd-theme"]
+
+[[package]]
+name = "terminado"
+version = "0.17.1"
+description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "terminado-0.17.1-py3-none-any.whl", hash = "sha256:8650d44334eba354dd591129ca3124a6ba42c3d5b70df5051b6921d506fdaeae"},
+    {file = "terminado-0.17.1.tar.gz", hash = "sha256:6ccbbcd3a4f8a25a5ec04991f39a0b8db52dfcd487ea0e578d977e6752380333"},
+]
+
+[package.dependencies]
+ptyprocess = {version = "*", markers = "os_name != \"nt\""}
+pywinpty = {version = ">=1.1.0", markers = "os_name == \"nt\""}
+tornado = ">=6.1.0"
+
+[package.extras]
+docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
+test = ["pre-commit", "pytest (>=7.0)", "pytest-timeout"]
+
+[[package]]
+name = "tinycss2"
+version = "1.2.1"
+description = "A tiny CSS parser"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "tinycss2-1.2.1-py3-none-any.whl", hash = "sha256:2b80a96d41e7c3914b8cda8bc7f705a4d9c49275616e886103dd839dfc847847"},
+    {file = "tinycss2-1.2.1.tar.gz", hash = "sha256:8cff3a8f066c2ec677c06dbc7b45619804a6938478d9d73c284b29d14ecb0627"},
+]
+
+[package.dependencies]
+webencodings = ">=0.4"
+
+[package.extras]
+doc = ["sphinx", "sphinx_rtd_theme"]
+test = ["flake8", "isort", "pytest"]
+
+[[package]]
+name = "tomli"
+version = "2.0.1"
+description = "A lil' TOML parser"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
+    {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
+]
+
+[[package]]
+name = "tornado"
+version = "6.3.2"
+description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
+category = "dev"
+optional = false
+python-versions = ">= 3.8"
+files = [
+    {file = "tornado-6.3.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:c367ab6c0393d71171123ca5515c61ff62fe09024fa6bf299cd1339dc9456829"},
+    {file = "tornado-6.3.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b46a6ab20f5c7c1cb949c72c1994a4585d2eaa0be4853f50a03b5031e964fc7c"},
+    {file = "tornado-6.3.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2de14066c4a38b4ecbbcd55c5cc4b5340eb04f1c5e81da7451ef555859c833f"},
+    {file = "tornado-6.3.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05615096845cf50a895026f749195bf0b10b8909f9be672f50b0fe69cba368e4"},
+    {file = "tornado-6.3.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b17b1cf5f8354efa3d37c6e28fdfd9c1c1e5122f2cb56dac121ac61baa47cbe"},
+    {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:29e71c847a35f6e10ca3b5c2990a52ce38b233019d8e858b755ea6ce4dcdd19d"},
+    {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:834ae7540ad3a83199a8da8f9f2d383e3c3d5130a328889e4cc991acc81e87a0"},
+    {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6a0848f1aea0d196a7c4f6772197cbe2abc4266f836b0aac76947872cd29b411"},
+    {file = "tornado-6.3.2-cp38-abi3-win32.whl", hash = "sha256:7efcbcc30b7c654eb6a8c9c9da787a851c18f8ccd4a5a3a95b05c7accfa068d2"},
+    {file = "tornado-6.3.2-cp38-abi3-win_amd64.whl", hash = "sha256:0c325e66c8123c606eea33084976c832aa4e766b7dff8aedd7587ea44a604cdf"},
+    {file = "tornado-6.3.2.tar.gz", hash = "sha256:4b927c4f19b71e627b13f3db2324e4ae660527143f9e1f2e2fb404f3a187e2ba"},
+]
+
+[[package]]
+name = "traitlets"
+version = "5.9.0"
+description = "Traitlets Python configuration system"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "traitlets-5.9.0-py3-none-any.whl", hash = "sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8"},
+    {file = "traitlets-5.9.0.tar.gz", hash = "sha256:f6cde21a9c68cf756af02035f72d5a723bf607e862e7be33ece505abf4a3bad9"},
+]
+
+[package.extras]
+docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
+test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"]
+
+[[package]]
+name = "typing-extensions"
+version = "4.6.1"
+description = "Backported and Experimental Type Hints for Python 3.7+"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "typing_extensions-4.6.1-py3-none-any.whl", hash = "sha256:6bac751f4789b135c43228e72de18637e9a6c29d12777023a703fd1a6858469f"},
+    {file = "typing_extensions-4.6.1.tar.gz", hash = "sha256:558bc0c4145f01e6405f4a5fdbd82050bd221b119f4bf72a961a1cfd471349d6"},
+]
+
+[[package]]
+name = "tzdata"
+version = "2023.3"
+description = "Provider of IANA time zone data"
+category = "main"
+optional = false
+python-versions = ">=2"
+files = [
+    {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"},
+    {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"},
+]
+
+[[package]]
+name = "uri-template"
+version = "1.2.0"
+description = "RFC 6570 URI Template Processor"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "uri_template-1.2.0-py3-none-any.whl", hash = "sha256:f1699c77b73b925cf4937eae31ab282a86dc885c333f2e942513f08f691fc7db"},
+    {file = "uri_template-1.2.0.tar.gz", hash = "sha256:934e4d09d108b70eb8a24410af8615294d09d279ce0e7cbcdaef1bd21f932b06"},
+]
+
+[package.extras]
+dev = ["flake8 (<4.0.0)", "flake8-annotations", "flake8-bugbear", "flake8-commas", "flake8-comprehensions", "flake8-continuation", "flake8-datetimez", "flake8-docstrings", "flake8-import-order", "flake8-literal", "flake8-noqa", "flake8-requirements", "flake8-type-annotations", "flake8-use-fstring", "mypy", "pep8-naming"]
+
+[[package]]
+name = "urllib3"
+version = "2.0.2"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "urllib3-2.0.2-py3-none-any.whl", hash = "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"},
+    {file = "urllib3-2.0.2.tar.gz", hash = "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc"},
+]
+
+[package.extras]
+brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
+secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"]
+socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
+zstd = ["zstandard (>=0.18.0)"]
+
+[[package]]
+name = "virtualenv"
+version = "20.23.0"
+description = "Virtual Python Environment builder"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"},
+    {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"},
+]
+
+[package.dependencies]
+distlib = ">=0.3.6,<1"
+filelock = ">=3.11,<4"
+platformdirs = ">=3.2,<4"
+
+[package.extras]
+docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"]
+test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"]
+
+[[package]]
+name = "wcwidth"
+version = "0.2.6"
+description = "Measures the displayed width of unicode strings in a terminal"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"},
+    {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"},
+]
+
+[[package]]
+name = "webcolors"
+version = "1.13"
+description = "A library for working with the color formats defined by HTML and CSS."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "webcolors-1.13-py3-none-any.whl", hash = "sha256:29bc7e8752c0a1bd4a1f03c14d6e6a72e93d82193738fa860cbff59d0fcc11bf"},
+    {file = "webcolors-1.13.tar.gz", hash = "sha256:c225b674c83fa923be93d235330ce0300373d02885cef23238813b0d5668304a"},
+]
+
+[package.extras]
+docs = ["furo", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinxext-opengraph"]
+tests = ["pytest", "pytest-cov"]
+
+[[package]]
+name = "webencodings"
+version = "0.5.1"
+description = "Character encoding aliases for legacy web content"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+    {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
+    {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
+]
+
+[[package]]
+name = "websocket-client"
+version = "1.5.2"
+description = "WebSocket client for Python with low level API options"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "websocket-client-1.5.2.tar.gz", hash = "sha256:c7d67c13b928645f259d9b847ab5b57fd2d127213ca41ebd880de1f553b7c23b"},
+    {file = "websocket_client-1.5.2-py3-none-any.whl", hash = "sha256:f8c64e28cd700e7ba1f04350d66422b6833b82a796b525a51e740b8cc8dab4b1"},
+]
+
+[package.extras]
+docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"]
+optional = ["python-socks", "wsaccel"]
+test = ["websockets"]
+
+[[package]]
+name = "widgetsnbextension"
+version = "4.0.7"
+description = "Jupyter interactive widgets for Jupyter Notebook"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "widgetsnbextension-4.0.7-py3-none-any.whl", hash = "sha256:be3228a73bbab189a16be2d4a3cd89ecbd4e31948bfdc64edac17dcdee3cd99c"},
+    {file = "widgetsnbextension-4.0.7.tar.gz", hash = "sha256:ea67c17a7cd4ae358f8f46c3b304c40698bc0423732e3f273321ee141232c8be"},
+]
+
+[[package]]
+name = "zipp"
+version = "3.15.0"
+description = "Backport of pathlib-compatible object wrapper for zip files"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"},
+    {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"},
+]
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
+
+[metadata]
+lock-version = "2.0"
+python-versions = "^3.8"
+content-hash = "e65374fa06e93d11d95add76ada9d754ce0a3f2409b24df7922df7ca14add7e6"
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..c9036afa
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,45 @@
+[tool.poetry]
+name = "tagreader-python"
+version = "3.0.2"
+description = "Tagreader is a Python package for reading trend data from the OSIsoft PI and Aspen Infoplus.21 IMS systems."
+authors = ["Einar S. Idsø <eiids@equinor.com>"]
+license = "MIT"
+readme = "README.md"
+packages = [{include = "tagreader"}]
+keywords=["Aspen InfoPlus.21", "OSIsoft PI"]
+classifiers=[
+    "Development Status :: 5 - Production/Stable",
+    "Intended Audience :: Developers",
+    "Intended Audience :: Science/Research",
+    "License :: OSI Approved :: MIT License",
+    "Natural Language :: English",
+    "Operating System :: MacOS",
+    "Operating System :: Microsoft :: Windows",
+    "Operating System :: POSIX :: Linux",
+    "Programming Language :: Python :: 3.8",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+    "Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator",
+    "Topic :: Software Development :: Libraries :: Python Modules",
+    "Topic :: Utilities",
+]
+
+[tool.poetry.dependencies]
+python = "^3.8"
+pandas = "^2.0.1"
+pyodbc = "^4.0.39"
+certifi = "^2023.5.7"
+requests = "^2.31.0"
+requests-kerberos = "^0.14.0"
+tables = "^3.8.0"
+
+
+[tool.poetry.group.dev.dependencies]
+pre-commit = "^3.3.2"
+jupyter = "^1.0.0"
+pytest = "^7.3.1"
+
+[build-system]
+requires = ["poetry-core"]
+build-backend = "poetry.core.masonry.api"
diff --git a/requirements.in b/requirements.in
deleted file mode 100644
index c79f07c4..00000000
--- a/requirements.in
+++ /dev/null
@@ -1,6 +0,0 @@
-certifi>=2020.04.05
-pandas>=1.0.0
-pyodbc; platform_system == 'Windows'
-requests
-requests-kerberos
-tables; platform_machine != 'arm64'
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index bf551bc7..00000000
--- a/requirements.txt
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# This file is autogenerated by pip-compile with python 3.10
-# To update, run:
-#
-#    pip-compile
-#
-certifi==2022.12.7
-    # via
-    #   -r requirements.in
-    #   requests
-cffi==1.15.0
-    # via cryptography
-charset-normalizer==2.0.12
-    # via requests
-cryptography==39.0.1
-    # via
-    #   pyspnego
-    #   requests-kerberos
-idna==3.3
-    # via requests
-numexpr==2.8.1
-    # via tables
-numpy==1.22.3
-    # via
-    #   numexpr
-    #   pandas
-    #   tables
-packaging==21.3
-    # via
-    #   numexpr
-    #   tables
-pandas==1.4.1
-    # via -r requirements.in
-pycparser==2.21
-    # via cffi
-pyodbc==4.0.32 ; platform_system == "Windows"
-    # via -r requirements.in
-pyparsing==3.0.7
-    # via packaging
-pyspnego[kerberos]==0.5.0
-    # via requests-kerberos
-python-dateutil==2.8.2
-    # via pandas
-pytz==2021.3
-    # via pandas
-requests==2.27.1
-    # via
-    #   -r requirements.in
-    #   requests-kerberos
-requests-kerberos==0.14.0
-    # via -r requirements.in
-six==1.16.0
-    # via python-dateutil
-tables==3.7.0 ; platform_machine != "arm64"
-    # via -r requirements.in
-urllib3==1.26.8
-    # via requests
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 0e0ceeed..00000000
--- a/setup.py
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/usr/bin/env python
-
-from typing import List
-
-from setuptools import setup
-
-long_description = """
-# Tagreader #
-
-Tagreader is a Python package for reading trend data from the OSIsoft
-PI and Aspen Infoplus.21 IMS systems. It is intended to be easy to use,
-and present as similar interfaces as possible to the backend historians.
-
-While originally developed for Windows, Tagreader can since release 3.0.0
-also be used on Linux and Windows platforms.
-
-Queries can be performed using either REST API (preferred) or ODBC queries.
-The use of ODBC queries require installation of proprietary drivers from
-AspenTech and OSIsoft that are only available for Windows.
-
-Trend data is output as Pandas Dataframes. The HDF5 file format is used
-to cache results.
-
-## Requirements
-
-Python >=3.7 with the following packages:
-
-  + pandas >= 1.0.0
-  + tables (*)
-  + requests
-  + requests-kerberos
-  + certifi >= 2020.04.05
-  + pyodbc (**)
-
-*) If tables is not installed, caching of fetched data will be disabled.
-Tables will be installed alongside Tagreader unless the installation is
-known to fail, which is the case for Macs with M1 chip.
-
-**) If using ODBC connections, you must also install proprietary drivers for
-PI ODBC and/or Aspen IP.21 SQLPlus. These drivers are only available for
-Microsoft Windows. Pyodbc will therefore not be installed for non-Windows
-systems.
-
-## Installation
-
-To install and/or upgrade:
-
-```
-pip install --upgrade tagreader
-```
-
-## Usage example ##
-
-```
-import tagreader
-c = tagreader.IMSClient("mysource", "aspenone")
-print(c.search("tag*"))
-df = c.read_tags(["tag1", "tag2"], "18.06.2020 08:00:00", "18.06.2020 09:00:00", 60)
-```
-
-Also see the
-[quickstart](https://github.com/equinor/tagreader-python/blob/master/docs/quickstart.ipynb)
-document and the
-[manual](docs/https://github.com/equinor/tagreader-python/blob/master/docs/manual.md)
-for more information.
-"""
-
-
-def get_install_requirements() -> List[str]:
-    with open("requirements.in") as f:
-        requirements = f.read().splitlines()
-        return requirements
-
-
-setup(
-    name="tagreader",
-    description="Library for reading from Aspen InfoPlus.21 and OSIsoft PI IMS servers",
-    long_description=long_description,
-    long_description_content_type="text/markdown",
-    keywords=["Aspen InfoPlus.21", "OSIsoft PI"],
-    url="https://github.com/equinor/tagreader-python",
-    author="Einar S. Idsø",
-    author_email="eiids@equinor.com",
-    license="MIT",
-    classifiers=[
-        "Development Status :: 5 - Production/Stable",
-        "Intended Audience :: Developers",
-        "Intended Audience :: Science/Research",
-        "License :: OSI Approved :: MIT License",
-        "Natural Language :: English",
-        "Operating System :: MacOS",
-        "Operating System :: Microsoft :: Windows",
-        "Operating System :: POSIX :: Linux",
-        "Programming Language :: Python :: 3.7",
-        "Programming Language :: Python :: 3.8",
-        "Programming Language :: Python :: 3.9",
-        "Programming Language :: Python :: 3.10",
-        "Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator",
-        "Topic :: Software Development :: Libraries :: Python Modules",
-        "Topic :: Utilities",
-    ],
-    packages=["tagreader"],
-    platforms=["Windows"],
-    package_data={"": ["*.md"]},
-    python_requires="~=3.7",
-    setup_requires=["setuptools_scm"],
-    use_scm_version={"write_to": "tagreader/version.py"},
-    install_requires=get_install_requirements(),
-)
diff --git a/tagreader/__init__.py b/tagreader/__init__.py
index 65f9694d..edb8ee76 100644
--- a/tagreader/__init__.py
+++ b/tagreader/__init__.py
@@ -1,6 +1,6 @@
 from .clients import IMSClient, list_sources  # noqa: F401
 from .utils import ReaderType  # noqa: F401
-from .utils import add_statoil_root_certificate, is_equinor, is_windows, is_mac
+from .utils import add_statoil_root_certificate, is_equinor, is_mac, is_windows
 
 if is_windows():
     from .odbc_handlers import list_aspen_servers, list_pi_servers  # noqa: F401
diff --git a/tagreader/cache.py b/tagreader/cache.py
index d3dda67a..c16bcb7b 100644
--- a/tagreader/cache.py
+++ b/tagreader/cache.py
@@ -105,7 +105,7 @@ def fetch_tag_metadata(
                 res[p] = metadata.get(p)
         return res
 
-    def remove(self, filename: str = None) -> None:
+    def remove(self, filename: str = None) -> None:  # type: ignore[assignment]
         if not filename:
             filename = self.filename
         if os.path.isfile(filename):
@@ -372,7 +372,7 @@ def fetch_tag_metadata(
                 res[p] = metadata.get(p)
         return res
 
-    def remove(self, filename: str = None) -> None:
+    def remove(self, filename: str = None) -> None:  # type: ignore[assignment]
         if not filename:
             filename = self.filename
             # self.close()
@@ -417,9 +417,9 @@ def timedelta_to_str(t):  # type: ignore
 
     def delete_key(
         self,
-        tagname: str = None,
-        readtype: ReaderType = None,
-        ts: Union[int, List[int]] = None,
+        tagname: str = None,  # type: ignore[assignment]
+        readtype: ReaderType = None,  # type: ignore[assignment]
+        ts: Union[int, List[int]] = None,  # type: ignore[assignment]
     ) -> None:
         with pd.HDFStore(self.filename) as f:
             for key in f:


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 18e76da4..39ce1368 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -6,17 +6,14 @@ on:
 
 jobs:
   code-test:
-    name: Test (${{ matrix.python-version }} - ${{ matrix.os }})
-    runs-on: ${{ matrix.os }}
-
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, macos-latest, windows-latest]
-        python-version: [3.7, 3.8, 3.9, '3.10', '3.11']
-
+        python-version: ['3.8', '3.9', '3.10', '3.11']
+        os: [windows-latest, ubuntu-latest, macos-latest]
+    runs-on: ${{ matrix.os }}
     steps:
-      - name: Checkout source code
+      - name: Checkout code
         uses: actions/checkout@v3
 
       - name: Install required Linux library for pykerberos
@@ -24,21 +21,23 @@ jobs:
         run: |
           sudo apt-get install libkrb5-dev
 
-      - name: Set up Python ${{ matrix.python-version}}
+      - name: Install Poetry
+        run: |
+          pipx install poetry
+
+      - name: Setup Python
         uses: actions/setup-python@v4
         with:
-          python-version: ${{ matrix.python-version }}
-          cache: 'pip'
+          python-version: ${{matrix.python-version}}
+          cache: 'poetry'
 
-      - name: Install dependencies
-        run: |
-          python -m pip install --upgrade pip
-          pip install -r requirements.in
-          pip install pytest
+      - name: Check pyproject.toml validity
+        run: poetry check --no-interaction
 
+      - name: Install deps
+        run: poetry install --no-interaction
       - name: Run tests
-        run: |
-          python -m pytest tests --junit-xml pytest.xml
+        run: poetry run pytest tests --junit-xml pytest.xml
 
       - name: Upload test results
         if: always()
diff --git a/pytest.xml b/pytest.xml
new file mode 100644
index 00000000..4367dd62
--- /dev/null
+++ b/pytest.xml
@@ -0,0 +1,2608 @@
+<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="40" skipped="12" tests="135" time="34.313" timestamp="2023-05-24T10:32:54.087659" hostname="AC-DQWYQ9T06W"><testcase classname="" name="tests.test_AspenHandlerODBC" file="tests/test_AspenHandlerODBC.py" time="0.000"><skipped message="collection skipped">('/Users/MASL/PycharmProjects/tagreader-python/tests/test_AspenHandlerODBC.py', 6, 'Skipped: All tests in module require Windows')</skipped></testcase><testcase classname="" name="tests.test_AspenHandlerODBC_connect" file="tests/test_AspenHandlerODBC_connect.py" time="0.000"><skipped message="collection skipped">('/Users/MASL/PycharmProjects/tagreader-python/tests/test_AspenHandlerODBC_connect.py', 9, 'Skipped: All tests in module require Windows')</skipped></testcase><testcase classname="" name="tests.test_PIHandlerODBC" file="tests/test_PIHandlerODBC.py" time="0.000"><skipped message="collection skipped">('/Users/MASL/PycharmProjects/tagreader-python/tests/test_PIHandlerODBC.py', 7, 'Skipped: All tests in module require Windows')</skipped></testcase><testcase classname="" name="tests.test_PIHandlerODBC_connect" file="tests/test_PIHandlerODBC_connect.py" time="0.000"><skipped message="collection skipped">('/Users/MASL/PycharmProjects/tagreader-python/tests/test_PIHandlerODBC_connect.py', 9, 'Skipped: All tests in module require Windows')</skipped></testcase><testcase classname="" name="tests.test_data_integrity" file="tests/test_data_integrity.py" time="0.000"><skipped message="collection skipped">('/Users/MASL/PycharmProjects/tagreader-python/tests/test_data_integrity.py', 14, 'Skipped: All tests in module require Windows')</skipped></testcase><testcase classname="" name="tests.extratests.tests.test_extra_AspenHandlerODBC_connect" file="tests/extratests/tests/test_extra_AspenHandlerODBC_connect.py" time="0.000"><skipped message="collection skipped">('/Users/MASL/PycharmProjects/tagreader-python/tests/extratests/tests/test_extra_AspenHandlerODBC_connect.py', 9, 'Skipped: All tests in module require Windows')</skipped></testcase><testcase classname="" name="tests.extratests.tests.test_extra_PIHandlerODBC_connect" file="tests/extratests/tests/test_extra_PIHandlerODBC_connect.py" time="0.000"><skipped message="collection skipped">('/Users/MASL/PycharmProjects/tagreader-python/tests/extratests/tests/test_extra_PIHandlerODBC_connect.py', 6, 'Skipped: All tests in module require Windows')</skipped></testcase><testcase classname="tests.test_AspenHandlerREST" name="test_generate_search_query" file="tests/test_AspenHandlerREST.py" line="17" time="0.000" /><testcase classname="tests.test_AspenHandlerREST" name="test_split_tagmap" file="tests/test_AspenHandlerREST.py" line="37" time="0.000" /><testcase classname="tests.test_AspenHandlerREST" name="test_generate_description_query" file="tests/test_AspenHandlerREST.py" line="45" time="0.000" /><testcase classname="tests.test_AspenHandlerREST" name="test_generate_unit_query" file="tests/test_AspenHandlerREST.py" line="53" time="0.000" /><testcase classname="tests.test_AspenHandlerREST" name="test_generate_map_query" file="tests/test_AspenHandlerREST.py" line="61" time="0.000" /><testcase classname="tests.test_AspenHandlerREST" name="test_generate_tag_read_query[RAW]" file="tests/test_AspenHandlerREST.py" line="68" time="0.510" /><testcase classname="tests.test_AspenHandlerREST" name="test_generate_tag_read_query[SHAPEPRESERVING]" file="tests/test_AspenHandlerREST.py" line="68" time="0.001" /><testcase classname="tests.test_AspenHandlerREST" name="test_generate_tag_read_query[INT]" file="tests/test_AspenHandlerREST.py" line="68" time="0.000" /><testcase classname="tests.test_AspenHandlerREST" name="test_generate_tag_read_query[MIN]" file="tests/test_AspenHandlerREST.py" line="68" time="0.000" /><testcase classname="tests.test_AspenHandlerREST" name="test_generate_tag_read_query[MAX]" file="tests/test_AspenHandlerREST.py" line="68" time="0.000" /><testcase classname="tests.test_AspenHandlerREST" name="test_generate_tag_read_query[RNG]" file="tests/test_AspenHandlerREST.py" line="68" time="0.000" /><testcase classname="tests.test_AspenHandlerREST" name="test_generate_tag_read_query[AVG]" file="tests/test_AspenHandlerREST.py" line="68" time="0.001" /><testcase classname="tests.test_AspenHandlerREST" name="test_generate_tag_read_query[VAR]" file="tests/test_AspenHandlerREST.py" line="68" time="0.001" /><testcase classname="tests.test_AspenHandlerREST" name="test_generate_tag_read_query[STD]" file="tests/test_AspenHandlerREST.py" line="68" time="0.001" /><testcase classname="tests.test_AspenHandlerREST" name="test_generate_tag_read_query[SNAPSHOT]" file="tests/test_AspenHandlerREST.py" line="68" time="0.001" /><testcase classname="tests.test_AspenHandlerREST" name="test_genreadquery_long_sampletime" file="tests/test_AspenHandlerREST.py" line="171" time="0.001" /><testcase classname="tests.test_AspenHandlerREST" name="test_generate_sql_query" file="tests/test_AspenHandlerREST.py" line="189" time="0.000" /><testcase classname="tests.test_AspenHandlerREST" name="test_initialize_connectionstring" file="tests/test_AspenHandlerREST.py" line="209" time="0.000" /><testcase classname="tests.test_AspenHandlerREST_connect" name="test_list_all_aspenone_sources" file="tests/test_AspenHandlerREST_connect.py" line="41" time="0.416" /><testcase classname="tests.test_AspenHandlerREST_connect" name="test_list_sources_aspenone" file="tests/test_AspenHandlerREST_connect.py" line="50" time="0.348" /><testcase classname="tests.test_AspenHandlerREST_connect" name="test_verify_connection" file="tests/test_AspenHandlerREST_connect.py" line="59" time="0.420" /><testcase classname="tests.test_AspenHandlerREST_connect" name="test_search_tag" file="tests/test_AspenHandlerREST_connect.py" line="64" time="0.634"><failure message="AssertionError: assert [] == [('ATCAI', 'Sine Input')]&#10;  Right contains one more item: ('ATCAI', 'Sine Input')&#10;  Use -v to get more diff">Client = &lt;tagreader.clients.IMSClient object at 0x1332e1510&gt;
+
+    def test_search_tag(Client):
+        res = Client.search("sospecificitcannotpossiblyexist")
+        assert 0 == len(res)
+        res = Client.search("ATCAI")
+&gt;       assert res == [("ATCAI", "Sine Input")]
+E       AssertionError: assert [] == [('ATCAI', 'Sine Input')]
+E         Right contains one more item: ('ATCAI', 'Sine Input')
+E         Use -v to get more diff
+
+tests/test_AspenHandlerREST_connect.py:69: AssertionError</failure></testcase><testcase classname="tests.test_AspenHandlerREST_connect" name="test_read_unknown_tag" file="tests/test_AspenHandlerREST_connect.py" line="84" time="0.744"><failure message="AssertionError: assert 0 &gt; 0&#10; +  where 0 = len(DatetimeIndex([], dtype='datetime64[ns, Europe/Oslo]', freq=None))&#10; +    where DatetimeIndex([], dtype='datetime64[ns, Europe/Oslo]', freq=None) = Empty DataFrame\nColumns: [ATCAI, sorandomitcantexist]\nIndex: [].index">Client = &lt;tagreader.clients.IMSClient object at 0x1332e0ed0&gt;
+
+    def test_read_unknown_tag(Client):
+        with pytest.warns(UserWarning):
+            df = Client.read(["sorandomitcantexist"], START_TIME, STOP_TIME)
+        assert len(df.index) == 0
+        with pytest.warns(UserWarning):
+            df = Client.read([TAG, "sorandomitcantexist"], START_TIME, STOP_TIME)
+&gt;       assert len(df.index) &gt; 0
+E       AssertionError: assert 0 &gt; 0
+E        +  where 0 = len(DatetimeIndex([], dtype='datetime64[ns, Europe/Oslo]', freq=None))
+E        +    where DatetimeIndex([], dtype='datetime64[ns, Europe/Oslo]', freq=None) = Empty DataFrame\nColumns: [ATCAI, sorandomitcantexist]\nIndex: [].index
+
+tests/test_AspenHandlerREST_connect.py:91: AssertionError</failure></testcase><testcase classname="tests.test_AspenHandlerREST_connect" name="test_query_sql" file="tests/test_AspenHandlerREST_connect.py" line="94" time="1.127" /><testcase classname="tests.test_PIHandlerREST" name="test_escape_chars" file="tests/test_PIHandlerREST.py" line="18" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_search_query" file="tests/test_PIHandlerREST.py" line="24" time="0.000" /><testcase classname="tests.test_PIHandlerREST" name="test_is_summary" file="tests/test_PIHandlerREST.py" line="41" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query[RAW]" file="tests/test_PIHandlerREST.py" line="56" time="0.002" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query[INT]" file="tests/test_PIHandlerREST.py" line="56" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query[MIN]" file="tests/test_PIHandlerREST.py" line="56" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query[MAX]" file="tests/test_PIHandlerREST.py" line="56" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query[RNG]" file="tests/test_PIHandlerREST.py" line="56" time="0.002" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query[AVG]" file="tests/test_PIHandlerREST.py" line="56" time="0.002" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query[STD]" file="tests/test_PIHandlerREST.py" line="56" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query[VAR]" file="tests/test_PIHandlerREST.py" line="56" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query[SNAPSHOT]" file="tests/test_PIHandlerREST.py" line="56" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query_with_status[RAW]" file="tests/test_PIHandlerREST.py" line="123" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query_with_status[INT]" file="tests/test_PIHandlerREST.py" line="123" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query_with_status[MIN]" file="tests/test_PIHandlerREST.py" line="123" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query_with_status[MAX]" file="tests/test_PIHandlerREST.py" line="123" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query_with_status[RNG]" file="tests/test_PIHandlerREST.py" line="123" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query_with_status[AVG]" file="tests/test_PIHandlerREST.py" line="123" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query_with_status[STD]" file="tests/test_PIHandlerREST.py" line="123" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query_with_status[VAR]" file="tests/test_PIHandlerREST.py" line="123" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_generate_read_query_with_status[SNAPSHOT]" file="tests/test_PIHandlerREST.py" line="123" time="0.001" /><testcase classname="tests.test_PIHandlerREST" name="test_genreadquery_long_sampletime" file="tests/test_PIHandlerREST.py" line="200" time="0.000" /><testcase classname="tests.test_PIHandlerREST_connect" name="test_list_all_piwebapi_sources" file="tests/test_PIHandlerREST_connect.py" line="49" time="0.523" /><testcase classname="tests.test_PIHandlerREST_connect" name="test_list_sources_piwebapi" file="tests/test_PIHandlerREST_connect.py" line="58" time="0.306" /><testcase classname="tests.test_PIHandlerREST_connect" name="test_verify_connection" file="tests/test_PIHandlerREST_connect.py" line="67" time="0.480" /><testcase classname="tests.test_PIHandlerREST_connect" name="test_search_tag" file="tests/test_PIHandlerREST_connect.py" line="72" time="0.529"><failure message="assert 1 == 0&#10; +  where 0 = len([])">Client = &lt;tagreader.clients.IMSClient object at 0x133a0c7d0&gt;
+
+    def test_search_tag(Client):
+        res = Client.search("SINUSOID")
+&gt;       assert 1 == len(res)
+E       assert 1 == 0
+E        +  where 0 = len([])
+
+tests/test_PIHandlerREST_connect.py:75: AssertionError</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_tag_to_webid" file="tests/test_PIHandlerREST_connect.py" line="86" time="0.430"><failure message="UserWarning: Tag SINUSOID not found">PIHandler = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x1333e9e90&gt;
+
+    def test_tag_to_webid(PIHandler):
+&gt;       res = PIHandler.tag_to_webid("SINUSOID")
+
+tests/test_PIHandlerREST_connect.py:88: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x1333e9e90&gt;, tag = 'SINUSOID'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag SINUSOID not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_read[RAW-10]" file="tests/test_PIHandlerREST_connect.py" line="96" time="0.500"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x13324e410&gt;, read_type = 'RAW', size = 10
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 10),
+            # pytest.param(
+            #      "SHAPEPRESERVING", 0, marks=pytest.mark.skip(reason="Not implemented")
+            # ),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "SNAPSHOT":
+            df = Client.read(
+                TAGS["Float32"],
+                read_type=getattr(ReaderType, read_type),
+            )
+        else:
+&gt;           df = Client.read(
+                TAGS["Float32"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                ts=SAMPLE_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+
+tests/test_PIHandlerREST_connect.py:126: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x13324de10&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_read[INT-61]" file="tests/test_PIHandlerREST_connect.py" line="96" time="0.638"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133c77cd0&gt;, read_type = 'INT', size = 61
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 10),
+            # pytest.param(
+            #      "SHAPEPRESERVING", 0, marks=pytest.mark.skip(reason="Not implemented")
+            # ),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "SNAPSHOT":
+            df = Client.read(
+                TAGS["Float32"],
+                read_type=getattr(ReaderType, read_type),
+            )
+        else:
+&gt;           df = Client.read(
+                TAGS["Float32"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                ts=SAMPLE_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+
+tests/test_PIHandlerREST_connect.py:126: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133c77e10&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_read[MIN-60]" file="tests/test_PIHandlerREST_connect.py" line="96" time="0.612"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133ced750&gt;, read_type = 'MIN', size = 60
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 10),
+            # pytest.param(
+            #      "SHAPEPRESERVING", 0, marks=pytest.mark.skip(reason="Not implemented")
+            # ),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "SNAPSHOT":
+            df = Client.read(
+                TAGS["Float32"],
+                read_type=getattr(ReaderType, read_type),
+            )
+        else:
+&gt;           df = Client.read(
+                TAGS["Float32"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                ts=SAMPLE_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+
+tests/test_PIHandlerREST_connect.py:126: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133ced5d0&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_read[MAX-60]" file="tests/test_PIHandlerREST_connect.py" line="96" time="0.533"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133c72c90&gt;, read_type = 'MAX', size = 60
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 10),
+            # pytest.param(
+            #      "SHAPEPRESERVING", 0, marks=pytest.mark.skip(reason="Not implemented")
+            # ),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "SNAPSHOT":
+            df = Client.read(
+                TAGS["Float32"],
+                read_type=getattr(ReaderType, read_type),
+            )
+        else:
+&gt;           df = Client.read(
+                TAGS["Float32"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                ts=SAMPLE_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+
+tests/test_PIHandlerREST_connect.py:126: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133c72cd0&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_read[RNG-60]" file="tests/test_PIHandlerREST_connect.py" line="96" time="0.708"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133c504d0&gt;, read_type = 'RNG', size = 60
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 10),
+            # pytest.param(
+            #      "SHAPEPRESERVING", 0, marks=pytest.mark.skip(reason="Not implemented")
+            # ),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "SNAPSHOT":
+            df = Client.read(
+                TAGS["Float32"],
+                read_type=getattr(ReaderType, read_type),
+            )
+        else:
+&gt;           df = Client.read(
+                TAGS["Float32"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                ts=SAMPLE_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+
+tests/test_PIHandlerREST_connect.py:126: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133c506d0&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_read[AVG-60]" file="tests/test_PIHandlerREST_connect.py" line="96" time="0.501"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133ce5c10&gt;, read_type = 'AVG', size = 60
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 10),
+            # pytest.param(
+            #      "SHAPEPRESERVING", 0, marks=pytest.mark.skip(reason="Not implemented")
+            # ),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "SNAPSHOT":
+            df = Client.read(
+                TAGS["Float32"],
+                read_type=getattr(ReaderType, read_type),
+            )
+        else:
+&gt;           df = Client.read(
+                TAGS["Float32"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                ts=SAMPLE_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+
+tests/test_PIHandlerREST_connect.py:126: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133ce5c90&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_read[VAR-60]" file="tests/test_PIHandlerREST_connect.py" line="96" time="0.531"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133c00790&gt;, read_type = 'VAR', size = 60
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 10),
+            # pytest.param(
+            #      "SHAPEPRESERVING", 0, marks=pytest.mark.skip(reason="Not implemented")
+            # ),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "SNAPSHOT":
+            df = Client.read(
+                TAGS["Float32"],
+                read_type=getattr(ReaderType, read_type),
+            )
+        else:
+&gt;           df = Client.read(
+                TAGS["Float32"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                ts=SAMPLE_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+
+tests/test_PIHandlerREST_connect.py:126: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133c00310&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_read[STD-60]" file="tests/test_PIHandlerREST_connect.py" line="96" time="0.760"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133a10d90&gt;, read_type = 'STD', size = 60
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 10),
+            # pytest.param(
+            #      "SHAPEPRESERVING", 0, marks=pytest.mark.skip(reason="Not implemented")
+            # ),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "SNAPSHOT":
+            df = Client.read(
+                TAGS["Float32"],
+                read_type=getattr(ReaderType, read_type),
+            )
+        else:
+&gt;           df = Client.read(
+                TAGS["Float32"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                ts=SAMPLE_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+
+tests/test_PIHandlerREST_connect.py:126: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133a10f90&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_read[SNAPSHOT-1]" file="tests/test_PIHandlerREST_connect.py" line="96" time="0.486"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133ac2410&gt;, read_type = 'SNAPSHOT', size = 1
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 10),
+            # pytest.param(
+            #      "SHAPEPRESERVING", 0, marks=pytest.mark.skip(reason="Not implemented")
+            # ),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip(reason="Not implemented")),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "SNAPSHOT":
+&gt;           df = Client.read(
+                TAGS["Float32"],
+                read_type=getattr(ReaderType, read_type),
+            )
+
+tests/test_PIHandlerREST_connect.py:121: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:324: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133ac11d0&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_read_with_status" file="tests/test_PIHandlerREST_connect.py" line="147" time="0.560"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133af67d0&gt;
+
+    def test_read_with_status(Client):
+&gt;       df = Client.read(
+            TAGS["Float32"],
+            start_time=START_TIME,
+            end_time=STOP_TIME,
+            ts=SAMPLE_TIME,
+            read_type=ReaderType.RAW,
+            get_status=True,
+        )
+
+tests/test_PIHandlerREST_connect.py:149: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133af6510&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_read_raw_long" file="tests/test_PIHandlerREST_connect.py" line="160" time="0.488"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133a571d0&gt;
+
+    def test_read_raw_long(Client):
+&gt;       df = Client.read(
+            TAGS["Float32"],
+            start_time=START_TIME,
+            end_time="2020-04-11 20:00:00",
+            read_type=ReaderType.RAW,
+        )
+
+tests/test_PIHandlerREST_connect.py:162: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133a57b50&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_read_only_invalid_data_yields_nan_for_invalid" file="tests/test_PIHandlerREST_connect.py" line="170" time="0.550"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133a10ed0&gt;
+
+    def test_read_only_invalid_data_yields_nan_for_invalid(Client):
+        tag = TAGS["Float32"]
+&gt;       df = Client.read(tag, "2012-10-09 10:30:00", "2012-10-09 11:00:00", 600)
+
+tests/test_PIHandlerREST_connect.py:173: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133a10e50&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_read_invalid_data_mixed_with_valid_yields_nan_for_invalid" file="tests/test_PIHandlerREST_connect.py" line="177" time="0.490"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133adda10&gt;
+
+    def test_read_invalid_data_mixed_with_valid_yields_nan_for_invalid(Client):
+        # Hint, found first valid datapoint for tag
+        tag = TAGS["Float32"]
+&gt;       df = Client.read(tag, "2018-04-23 15:20:00", "2018-04-23 15:50:00", 600)
+
+tests/test_PIHandlerREST_connect.py:181: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133adec90&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_digitalread_yields_integers" file="tests/test_PIHandlerREST_connect.py" line="186" time="0.936"><failure message="UserWarning: Tag CDM158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133dc0190&gt;
+
+    def test_digitalread_yields_integers(Client):
+        tag = TAGS["Digital"]
+&gt;       df = Client.read(
+            tag, start_time=START_TIME, end_time=STOP_TIME, ts=600, read_type=ReaderType.INT
+        )
+
+tests/test_PIHandlerREST_connect.py:189: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133dc01d0&gt;, tag = 'CDM158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDM158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_get_unit" file="tests/test_PIHandlerREST_connect.py" line="194" time="0.751"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133260790&gt;
+
+    def test_get_unit(Client):
+&gt;       res = Client.get_units(list(TAGS.values()))
+
+tests/test_PIHandlerREST_connect.py:196: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:429: in get_units
+    unit = self.handler._get_tag_unit(tag)
+tagreader/web_handlers.py:771: in _get_tag_unit
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133260150&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_get_description" file="tests/test_PIHandlerREST_connect.py" line="201" time="0.585"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133adc650&gt;
+
+    def test_get_description(Client):
+&gt;       res = Client.get_descriptions(list(TAGS.values()))
+
+tests/test_PIHandlerREST_connect.py:203: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:445: in get_descriptions
+    desc = self.handler._get_tag_description(tag)
+tagreader/web_handlers.py:782: in _get_tag_description
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133adf490&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_from_DST_folds_time" file="tests/test_PIHandlerREST_connect.py" line="208" time="0.756"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133a10d90&gt;
+
+    def test_from_DST_folds_time(Client):
+        if os.path.exists(SOURCE + ".h5"):
+            os.remove(SOURCE + ".h5")
+        tag = TAGS["Float32"]
+        interval = ["2017-10-29 00:30:00", "2017-10-29 04:30:00"]
+&gt;       df = Client.read([tag], interval[0], interval[1], 600)
+
+tests/test_PIHandlerREST_connect.py:214: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133a10b50&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_to_DST_skips_time" file="tests/test_PIHandlerREST_connect.py" line="225" time="0.533"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133a012d0&gt;
+
+    def test_to_DST_skips_time(Client):
+        if os.path.exists(SOURCE + ".h5"):
+            os.remove(SOURCE + ".h5")
+        tag = TAGS["Float32"]
+        interval = ["2018-03-25 00:30:00", "2018-03-25 03:30:00"]
+&gt;       df = Client.read([tag], interval[0], interval[1], 600)
+
+tests/test_PIHandlerREST_connect.py:231: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133a00850&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_tags_with_no_data_included_in_results" file="tests/test_PIHandlerREST_connect.py" line="249" time="0.849"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133435650&gt;
+
+    def test_tags_with_no_data_included_in_results(Client):
+&gt;       df = Client.read([TAGS["Float32"]], "2099-01-01 00:00:00", "2099-01-02 00:00:00")
+
+tests/test_PIHandlerREST_connect.py:251: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x1334379d0&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_PIHandlerREST_connect" name="test_tags_raw_with_no_data_included_in_results" file="tests/test_PIHandlerREST_connect.py" line="254" time="0.556"><failure message="UserWarning: Tag CDT158 not found">Client = &lt;tagreader.clients.IMSClient object at 0x133a54350&gt;
+
+    def test_tags_raw_with_no_data_included_in_results(Client):
+&gt;       df = Client.read(
+            [TAGS["Float32"]],
+            "2099-01-01 00:00:00",
+            "2099-01-02 00:00:00",
+            read_type=ReaderType.RAW,
+        )
+
+tests/test_PIHandlerREST_connect.py:256: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+tagreader/web_handlers.py:852: in read_tag
+    webid = self.tag_to_webid(tag)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.PIHandlerWeb object at 0x133a55f90&gt;, tag = 'CDT158'
+
+    def tag_to_webid(self, tag):
+        """Given a tag, returns the WebId.
+    
+        :param tag: The tag
+        :type tag: str
+        :raises ConnectionError: If connection or query fails
+        :return: WebId
+        :rtype: str
+        """
+        if tag not in self.webidcache:
+            params = self.generate_search_query(tag=tag, datasource=self.datasource)
+            params["fields"] = "name;webid"
+            url = urljoin(self.base_url, "search", "query")
+            res = self.session.get(url, params=params)
+            res.raise_for_status()
+            j = res.json()
+    
+            if len(j["Errors"]) &gt; 0:
+                msg = f"Received error from server when searching for WebId for {tag}: {j['Errors']}"  # noqa: E501
+                raise ValueError(msg)
+    
+            if len(j["Items"]) &gt; 1:
+                # Compare elements and if same, return the first
+                first = j["Items"][0]
+                for item in j["Items"][1:]:
+                    if item != first:
+                        raise AssertionError(
+                            f"Received {len(j['Items'])} results when trying to find unique WebId for {tag}."  # noqa: E501
+                        )
+            elif len(j["Items"]) == 0:
+&gt;               warnings.warn(f"Tag {tag} not found")
+E               UserWarning: Tag CDT158 not found
+
+tagreader/web_handlers.py:822: UserWarning</failure></testcase><testcase classname="tests.test_bucketcache" name="test_timestamp_to_epoch" file="tests/test_bucketcache.py" line="55" time="0.001" /><testcase classname="tests.test_bucketcache" name="test_safe_tagname" file="tests/test_bucketcache.py" line="66" time="0.000" /><testcase classname="tests.test_bucketcache" name="test_get_intervals_from_dataset_name" file="tests/test_bucketcache.py" line="70" time="0.000" /><testcase classname="tests.test_bucketcache" name="test_key_path_with_time" file="tests/test_bucketcache.py" line="81" time="0.000" /><testcase classname="tests.test_bucketcache" name="test_key_path_stepped" file="tests/test_bucketcache.py" line="96" time="0.000" /><testcase classname="tests.test_bucketcache" name="test_key_path_with_status" file="tests/test_bucketcache.py" line="111" time="0.000" /><testcase classname="tests.test_bucketcache" name="test_key_path_RAW" file="tests/test_bucketcache.py" line="124" time="0.000" /><testcase classname="tests.test_bucketcache" name="test_get_missing_intervals" file="tests/test_bucketcache.py" line="137" time="0.444" /><testcase classname="tests.test_bucketcache" name="test_get_intersecting_datasets" file="tests/test_bucketcache.py" line="219" time="0.027" /><testcase classname="tests.test_bucketcache" name="test_store_metadata" file="tests/test_bucketcache.py" line="334" time="0.012" /><testcase classname="tests.test_bucketcache" name="test_store_empty_df" file="tests/test_bucketcache.py" line="345" time="0.027" /><testcase classname="tests.test_bucketcache" name="test_store_single_df" file="tests/test_bucketcache.py" line="365" time="0.011" /><testcase classname="tests.test_bucketcache" name="test_fetch" file="tests/test_bucketcache.py" line="380" time="0.033" /><testcase classname="tests.test_bucketcache" name="test_store_overlapping_df" file="tests/test_bucketcache.py" line="440" time="0.045" /><testcase classname="tests.test_cache" name="test_safe_tagname" file="tests/test_cache.py" line="34" time="0.000" /><testcase classname="tests.test_cache" name="test_key_path" file="tests/test_cache.py" line="38" time="0.000" /><testcase classname="tests.test_cache" name="test_cache_single_store_and_fetch" file="tests/test_cache.py" line="42" time="0.012" /><testcase classname="tests.test_cache" name="test_cache_multiple_store_single_fetch" file="tests/test_cache.py" line="48" time="0.018" /><testcase classname="tests.test_cache" name="test_interval_reads" file="tests/test_cache.py" line="57" time="0.018" /><testcase classname="tests.test_cache" name="test_match_tag" file="tests/test_cache.py" line="78" time="0.000" /><testcase classname="tests.test_cache" name="test_delete_tag" file="tests/test_cache.py" line="147" time="0.015" /><testcase classname="tests.test_cache" name="test_store_empty_df" file="tests/test_cache.py" line="160" time="0.005" /><testcase classname="tests.test_cache" name="test_store_metadata" file="tests/test_cache.py" line="171" time="0.006" /><testcase classname="tests.test_cache" name="test_to_DST_skips_time" file="tests/test_cache.py" line="182" time="0.012" /><testcase classname="tests.test_cache" name="test_from_DST_folds_time" file="tests/test_cache.py" line="200" time="0.006" /><testcase classname="tests.test_clients" name="test_get_next_timeslice" file="tests/test_clients.py" line="15" time="0.001" /><testcase classname="tests.test_clients" name="test_get_missing_intervals" file="tests/test_clients.py" line="26" time="0.001" /><testcase classname="tests.test_clients.TestODBC" name="test_PI_init_odbc_client_with_host_port" file="tests/test_clients.py" line="55" time="0.000"><skipped type="pytest.skip" message="ODBC drivers require Windows and are unavailable in GitHub Actions">/Users/MASL/PycharmProjects/tagreader-python/tests/test_clients.py:56: ODBC drivers require Windows and are unavailable in GitHub Actions</skipped></testcase><testcase classname="tests.test_clients.TestODBC" name="test_IP21_init_odbc_client_with_host_port" file="tests/test_clients.py" line="65" time="0.000"><skipped type="pytest.skip" message="ODBC drivers require Windows and are unavailable in GitHub Actions">/Users/MASL/PycharmProjects/tagreader-python/tests/test_clients.py:66: ODBC drivers require Windows and are unavailable in GitHub Actions</skipped></testcase><testcase classname="tests.test_clients.TestODBC" name="test_PI_connection_string_override" file="tests/test_clients.py" line="75" time="0.000"><skipped type="pytest.skip" message="ODBC drivers require Windows and are unavailable in GitHub Actions">/Users/MASL/PycharmProjects/tagreader-python/tests/test_clients.py:76: ODBC drivers require Windows and are unavailable in GitHub Actions</skipped></testcase><testcase classname="tests.test_clients.TestODBC" name="test_IP21_connection_string_override" file="tests/test_clients.py" line="85" time="0.000"><skipped type="pytest.skip" message="ODBC drivers require Windows and are unavailable in GitHub Actions">/Users/MASL/PycharmProjects/tagreader-python/tests/test_clients.py:86: ODBC drivers require Windows and are unavailable in GitHub Actions</skipped></testcase><testcase classname="tests.test_clients.TestODBC" name="test_init_odbc_clients" file="tests/test_clients.py" line="95" time="0.000"><skipped type="pytest.skip" message="ODBC drivers require Windows and are unavailable in GitHub Actions">/Users/MASL/PycharmProjects/tagreader-python/tests/test_clients.py:96: ODBC drivers require Windows and are unavailable in GitHub Actions</skipped></testcase><testcase classname="tests.test_utils" name="test_ensure_is_datetime_string" file="tests/test_utils.py" line="11" time="0.003" /><testcase classname="tests.test_utils" name="test_ensure_is_datetime_pd_timestamp" file="tests/test_utils.py" line="33" time="0.000" /><testcase classname="tests.test_utils" name="test_ensure_is_datetime_datetime" file="tests/test_utils.py" line="40" time="0.000" /><testcase classname="tests.test_utils" name="test_urljoin" file="tests/test_utils.py" line="48" time="0.000" /><testcase classname="tests.test_utils" name="test_equinor" file="tests/test_utils.py" line="56" time="0.020" /><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_search_tag_multiple_mappings" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="50" time="0.480"><failure message="AssertionError: assert [] == [('20B-LIC__8...R.SEP. OLJE')]&#10;  Right contains one more item: ('20B-LIC__811', 'V 1.TR.SEP. OLJE')&#10;  Use -v to get more diff">Client = &lt;tagreader.clients.IMSClient object at 0x1332e0690&gt;
+
+    def test_search_tag_multiple_mappings(Client):
+        # Tag reports 5 default mappings using current methods
+        res = Client.search("20B-LIC__811")
+&gt;       assert res == [("20B-LIC__811", "V 1.TR.SEP. OLJE")]
+E       AssertionError: assert [] == [('20B-LIC__8...R.SEP. OLJE')]
+E         Right contains one more item: ('20B-LIC__811', 'V 1.TR.SEP. OLJE')
+E         Use -v to get more diff
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:54: AssertionError</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_search_tag_double_spaces" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="56" time="0.543" /><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_search_tag_asterisk" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="64" time="0.494"><failure message="assert 0 == 2&#10; +  where 0 = len([])">Client = &lt;tagreader.clients.IMSClient object at 0x133d79b50&gt;
+
+    def test_search_tag_asterisk(Client):
+        res = Client.search("ATCB*")
+&gt;       assert len(res) == 2
+E       assert 0 == 2
+E        +  where 0 = len([])
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:67: AssertionError</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_get_unit" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="69" time="0.776"><failure message="KeyError">self = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133d783d0&gt;, tag = 'ATCAI'
+
+    def _get_tag_unit(self, tag):
+        query = self.generate_get_unit_query(tag)
+        url = urljoin(self.base_url, "TagInfo")
+        res = self.session.get(url, params=query)
+        res.raise_for_status()
+        j = res.json()
+        try:
+&gt;           attrdata = j["data"]["tags"][0]["attrData"]
+E           KeyError: 'attrData'
+
+tagreader/web_handlers.py:383: KeyError
+
+During handling of the above exception, another exception occurred:
+
+AspenHandler = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133d783d0&gt;, Client = &lt;tagreader.clients.IMSClient object at 0x133d2dfd0&gt;
+
+    def test_get_unit(AspenHandler, Client):
+&gt;       assert AspenHandler._get_tag_unit("ATCAI") == "psig"
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:71: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133d783d0&gt;, tag = 'ATCAI'
+
+    def _get_tag_unit(self, tag):
+        query = self.generate_get_unit_query(tag)
+        url = urljoin(self.base_url, "TagInfo")
+        res = self.session.get(url, params=query)
+        res.raise_for_status()
+        j = res.json()
+        try:
+            attrdata = j["data"]["tags"][0]["attrData"]
+        except Exception:
+            print(f"Error. I got this: {j}")
+&gt;           raise KeyError
+E           KeyError
+
+tagreader/web_handlers.py:386: KeyError</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_get_description" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="80" time="0.411"><failure message="AssertionError: assert '' == 'Sine Input'&#10;  - Sine Input">AspenHandler = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133a13950&gt;
+
+    def test_get_description(AspenHandler):
+&gt;       assert AspenHandler._get_tag_description("ATCAI") == "Sine Input"
+E       AssertionError: assert '' == 'Sine Input'
+E         - Sine Input
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:82: AssertionError</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_get_maps" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="89" time="0.612"><failure message="KeyError: 'categories'">AspenHandler = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133d0c850&gt;
+
+    def test_get_maps(AspenHandler):
+&gt;       assert AspenHandler._get_maps("ATCAI") == {
+            "IP_AnalogMap": True,
+            "IP_AnalogAlmMap": False,
+        }
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:91: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133d0c850&gt;, tagname = 'ATCAI'
+
+    def _get_maps(self, tagname):
+        params = self.generate_get_map_query(tagname)
+        url = urljoin(self.base_url, "TagInfo")
+        res = self.session.get(url, params=params)
+        res.raise_for_status()
+        j = res.json()
+    
+        if "tags" not in j["data"]:
+            return {}
+    
+        ret = {}
+&gt;       for item in j["data"]["tags"][0]["categories"][0]["ta"]:
+E       KeyError: 'categories'
+
+tagreader/web_handlers.py:323: KeyError</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_get_default_mapname" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="99" time="0.384"><failure message="KeyError: 'categories'">AspenHandler = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133d4e4d0&gt;
+
+    def test_get_default_mapname(AspenHandler):
+&gt;       assert AspenHandler._get_default_mapname("ATCAI") == "IP_AnalogMap"
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:101: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/web_handlers.py:329: in _get_default_mapname
+    allmaps = self._get_maps(tagname)
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133d4e4d0&gt;, tagname = 'ATCAI'
+
+    def _get_maps(self, tagname):
+        params = self.generate_get_map_query(tagname)
+        url = urljoin(self.base_url, "TagInfo")
+        res = self.session.get(url, params=params)
+        res.raise_for_status()
+        j = res.json()
+    
+        if "tags" not in j["data"]:
+            return {}
+    
+        ret = {}
+&gt;       for item in j["data"]["tags"][0]["categories"][0]["ta"]:
+E       KeyError: 'categories'
+
+tagreader/web_handlers.py:323: KeyError</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_read[RAW-12]" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="104" time="0.546"><failure message="UserWarning: Tag Name 17B-HIC__063 is invalid">Client = &lt;tagreader.clients.IMSClient object at 0x133d4ce90&gt;, read_type = 'RAW', size = 12
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 12),  # 14, but two duplicate times that are deduplicated
+            # pytest.param("SHAPEPRESERVING", 0, marks=pytest.mark.skip),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "RAW":
+&gt;           df = Client.read(
+                TAGS["HIC"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                read_type=getattr(ReaderType, read_type),
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:127: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133d4f350&gt;, tag = '17B-HIC__063;CS ProcVal', start_time = Timestamp('2018-05-01 10:00:00+0200', tz='Europe/Oslo')
+stop_time = Timestamp('2018-05-01 11:00:00+0200', tz='Europe/Oslo'), sample_time = Timedelta('0 days 00:01:00'), read_type = &lt;ReaderType.RAW: 1&gt;, metadata = {}, get_status = False
+
+    def read_tag(
+        self,
+        tag,
+        start_time,
+        stop_time,
+        sample_time,
+        read_type,
+        metadata=None,
+        get_status=False,
+    ):
+        if read_type not in [
+            ReaderType.INT,
+            ReaderType.MIN,
+            ReaderType.MAX,
+            ReaderType.RNG,
+            ReaderType.AVG,
+            ReaderType.VAR,
+            ReaderType.STD,
+            ReaderType.SNAPSHOT,
+            ReaderType.RAW,
+        ]:
+            raise NotImplementedError
+    
+        if read_type == ReaderType.SNAPSHOT:
+            url = urljoin(self.base_url, "Attribute")
+        else:
+            url = urljoin(self.base_url, "History")
+    
+        # Actual and bestfit read types allow specifying maxpoints.
+        # Aggregate reads limit to 10 000 points and issue a moredata-token.
+        # TODO: May need to look into using this later - most likely more
+        # efficient than creating new query starting at previous stoptime.
+        # Interpolated reads return error message if more than 100 000 points,
+        # so we need to limit the range. Note -1 because INT normally includes
+        # both start and end time.
+        if read_type == ReaderType.INT:
+            stop_time = min(stop_time, start_time + sample_time * (self._max_rows - 1))
+    
+        tagname, mapname = self.split_tagmap(tag)
+    
+        params = self.generate_read_query(
+            tagname, mapname, start_time, stop_time, sample_time, read_type
+        )
+    
+        res = self.session.get(url, params=params)
+        res.raise_for_status()
+    
+        if len(res.text) == 0:  # res.text='' for timestamps in future
+            return pd.DataFrame(columns=[tag])
+    
+        try:
+            j = res.json()
+        except JSONDecodeError:
+            # AspenOne sometimes returns completely and utterly invalid -nan.
+            # Since json/simplejson has no mechanism to handle this, we need to
+            # pre-process
+            import json
+    
+            txt = res.text.replace('"v":nan', '"v":NaN').replace('"v":-nan', '"v":NaN')
+            j = json.loads(txt)
+    
+        if "er" in j["data"][0]["samples"][0]:
+&gt;           warnings.warn(j["data"][0]["samples"][0]["es"])
+E           UserWarning: Tag Name 17B-HIC__063 is invalid
+
+tagreader/web_handlers.py:486: UserWarning</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_read[INT-61]" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="104" time="0.481"><failure message="UserWarning: Tag Name 17B-HIC__063 is invalid">Client = &lt;tagreader.clients.IMSClient object at 0x133d97350&gt;, read_type = 'INT', size = 61
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 12),  # 14, but two duplicate times that are deduplicated
+            # pytest.param("SHAPEPRESERVING", 0, marks=pytest.mark.skip),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "RAW":
+            df = Client.read(
+                TAGS["HIC"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+        elif read_type == "SNAPSHOT":
+            df = Client.read(TAGS["HIC"], read_type=getattr(ReaderType, read_type))
+        else:
+&gt;           df = Client.read(
+                TAGS["HIC"], START_TIME, STOP_TIME, 60, getattr(ReaderType, read_type)
+            )
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:136: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133d97290&gt;, tag = '17B-HIC__063;CS ProcVal', start_time = Timestamp('2018-05-01 10:00:00+0200', tz='Europe/Oslo')
+stop_time = Timestamp('2018-05-01 11:00:00+0200', tz='Europe/Oslo'), sample_time = Timedelta('0 days 00:01:00'), read_type = &lt;ReaderType.INT: 3&gt;, metadata = {}, get_status = False
+
+    def read_tag(
+        self,
+        tag,
+        start_time,
+        stop_time,
+        sample_time,
+        read_type,
+        metadata=None,
+        get_status=False,
+    ):
+        if read_type not in [
+            ReaderType.INT,
+            ReaderType.MIN,
+            ReaderType.MAX,
+            ReaderType.RNG,
+            ReaderType.AVG,
+            ReaderType.VAR,
+            ReaderType.STD,
+            ReaderType.SNAPSHOT,
+            ReaderType.RAW,
+        ]:
+            raise NotImplementedError
+    
+        if read_type == ReaderType.SNAPSHOT:
+            url = urljoin(self.base_url, "Attribute")
+        else:
+            url = urljoin(self.base_url, "History")
+    
+        # Actual and bestfit read types allow specifying maxpoints.
+        # Aggregate reads limit to 10 000 points and issue a moredata-token.
+        # TODO: May need to look into using this later - most likely more
+        # efficient than creating new query starting at previous stoptime.
+        # Interpolated reads return error message if more than 100 000 points,
+        # so we need to limit the range. Note -1 because INT normally includes
+        # both start and end time.
+        if read_type == ReaderType.INT:
+            stop_time = min(stop_time, start_time + sample_time * (self._max_rows - 1))
+    
+        tagname, mapname = self.split_tagmap(tag)
+    
+        params = self.generate_read_query(
+            tagname, mapname, start_time, stop_time, sample_time, read_type
+        )
+    
+        res = self.session.get(url, params=params)
+        res.raise_for_status()
+    
+        if len(res.text) == 0:  # res.text='' for timestamps in future
+            return pd.DataFrame(columns=[tag])
+    
+        try:
+            j = res.json()
+        except JSONDecodeError:
+            # AspenOne sometimes returns completely and utterly invalid -nan.
+            # Since json/simplejson has no mechanism to handle this, we need to
+            # pre-process
+            import json
+    
+            txt = res.text.replace('"v":nan', '"v":NaN').replace('"v":-nan', '"v":NaN')
+            j = json.loads(txt)
+    
+        if "er" in j["data"][0]["samples"][0]:
+&gt;           warnings.warn(j["data"][0]["samples"][0]["es"])
+E           UserWarning: Tag Name 17B-HIC__063 is invalid
+
+tagreader/web_handlers.py:486: UserWarning</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_read[MIN-60]" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="104" time="0.477"><failure message="UserWarning: Tag Name 17B-HIC__063 is invalid">Client = &lt;tagreader.clients.IMSClient object at 0x137b75650&gt;, read_type = 'MIN', size = 60
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 12),  # 14, but two duplicate times that are deduplicated
+            # pytest.param("SHAPEPRESERVING", 0, marks=pytest.mark.skip),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "RAW":
+            df = Client.read(
+                TAGS["HIC"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+        elif read_type == "SNAPSHOT":
+            df = Client.read(TAGS["HIC"], read_type=getattr(ReaderType, read_type))
+        else:
+&gt;           df = Client.read(
+                TAGS["HIC"], START_TIME, STOP_TIME, 60, getattr(ReaderType, read_type)
+            )
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:136: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x137b75c10&gt;, tag = '17B-HIC__063;CS ProcVal', start_time = Timestamp('2018-05-01 10:00:00+0200', tz='Europe/Oslo')
+stop_time = Timestamp('2018-05-01 11:00:00+0200', tz='Europe/Oslo'), sample_time = Timedelta('0 days 00:01:00'), read_type = &lt;ReaderType.MIN: 4&gt;, metadata = {}, get_status = False
+
+    def read_tag(
+        self,
+        tag,
+        start_time,
+        stop_time,
+        sample_time,
+        read_type,
+        metadata=None,
+        get_status=False,
+    ):
+        if read_type not in [
+            ReaderType.INT,
+            ReaderType.MIN,
+            ReaderType.MAX,
+            ReaderType.RNG,
+            ReaderType.AVG,
+            ReaderType.VAR,
+            ReaderType.STD,
+            ReaderType.SNAPSHOT,
+            ReaderType.RAW,
+        ]:
+            raise NotImplementedError
+    
+        if read_type == ReaderType.SNAPSHOT:
+            url = urljoin(self.base_url, "Attribute")
+        else:
+            url = urljoin(self.base_url, "History")
+    
+        # Actual and bestfit read types allow specifying maxpoints.
+        # Aggregate reads limit to 10 000 points and issue a moredata-token.
+        # TODO: May need to look into using this later - most likely more
+        # efficient than creating new query starting at previous stoptime.
+        # Interpolated reads return error message if more than 100 000 points,
+        # so we need to limit the range. Note -1 because INT normally includes
+        # both start and end time.
+        if read_type == ReaderType.INT:
+            stop_time = min(stop_time, start_time + sample_time * (self._max_rows - 1))
+    
+        tagname, mapname = self.split_tagmap(tag)
+    
+        params = self.generate_read_query(
+            tagname, mapname, start_time, stop_time, sample_time, read_type
+        )
+    
+        res = self.session.get(url, params=params)
+        res.raise_for_status()
+    
+        if len(res.text) == 0:  # res.text='' for timestamps in future
+            return pd.DataFrame(columns=[tag])
+    
+        try:
+            j = res.json()
+        except JSONDecodeError:
+            # AspenOne sometimes returns completely and utterly invalid -nan.
+            # Since json/simplejson has no mechanism to handle this, we need to
+            # pre-process
+            import json
+    
+            txt = res.text.replace('"v":nan', '"v":NaN').replace('"v":-nan', '"v":NaN')
+            j = json.loads(txt)
+    
+        if "er" in j["data"][0]["samples"][0]:
+&gt;           warnings.warn(j["data"][0]["samples"][0]["es"])
+E           UserWarning: Tag Name 17B-HIC__063 is invalid
+
+tagreader/web_handlers.py:486: UserWarning</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_read[MAX-60]" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="104" time="0.495"><failure message="UserWarning: Tag Name 17B-HIC__063 is invalid">Client = &lt;tagreader.clients.IMSClient object at 0x133d669d0&gt;, read_type = 'MAX', size = 60
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 12),  # 14, but two duplicate times that are deduplicated
+            # pytest.param("SHAPEPRESERVING", 0, marks=pytest.mark.skip),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "RAW":
+            df = Client.read(
+                TAGS["HIC"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+        elif read_type == "SNAPSHOT":
+            df = Client.read(TAGS["HIC"], read_type=getattr(ReaderType, read_type))
+        else:
+&gt;           df = Client.read(
+                TAGS["HIC"], START_TIME, STOP_TIME, 60, getattr(ReaderType, read_type)
+            )
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:136: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133d66750&gt;, tag = '17B-HIC__063;CS ProcVal', start_time = Timestamp('2018-05-01 10:00:00+0200', tz='Europe/Oslo')
+stop_time = Timestamp('2018-05-01 11:00:00+0200', tz='Europe/Oslo'), sample_time = Timedelta('0 days 00:01:00'), read_type = &lt;ReaderType.MAX: 5&gt;, metadata = {}, get_status = False
+
+    def read_tag(
+        self,
+        tag,
+        start_time,
+        stop_time,
+        sample_time,
+        read_type,
+        metadata=None,
+        get_status=False,
+    ):
+        if read_type not in [
+            ReaderType.INT,
+            ReaderType.MIN,
+            ReaderType.MAX,
+            ReaderType.RNG,
+            ReaderType.AVG,
+            ReaderType.VAR,
+            ReaderType.STD,
+            ReaderType.SNAPSHOT,
+            ReaderType.RAW,
+        ]:
+            raise NotImplementedError
+    
+        if read_type == ReaderType.SNAPSHOT:
+            url = urljoin(self.base_url, "Attribute")
+        else:
+            url = urljoin(self.base_url, "History")
+    
+        # Actual and bestfit read types allow specifying maxpoints.
+        # Aggregate reads limit to 10 000 points and issue a moredata-token.
+        # TODO: May need to look into using this later - most likely more
+        # efficient than creating new query starting at previous stoptime.
+        # Interpolated reads return error message if more than 100 000 points,
+        # so we need to limit the range. Note -1 because INT normally includes
+        # both start and end time.
+        if read_type == ReaderType.INT:
+            stop_time = min(stop_time, start_time + sample_time * (self._max_rows - 1))
+    
+        tagname, mapname = self.split_tagmap(tag)
+    
+        params = self.generate_read_query(
+            tagname, mapname, start_time, stop_time, sample_time, read_type
+        )
+    
+        res = self.session.get(url, params=params)
+        res.raise_for_status()
+    
+        if len(res.text) == 0:  # res.text='' for timestamps in future
+            return pd.DataFrame(columns=[tag])
+    
+        try:
+            j = res.json()
+        except JSONDecodeError:
+            # AspenOne sometimes returns completely and utterly invalid -nan.
+            # Since json/simplejson has no mechanism to handle this, we need to
+            # pre-process
+            import json
+    
+            txt = res.text.replace('"v":nan', '"v":NaN').replace('"v":-nan', '"v":NaN')
+            j = json.loads(txt)
+    
+        if "er" in j["data"][0]["samples"][0]:
+&gt;           warnings.warn(j["data"][0]["samples"][0]["es"])
+E           UserWarning: Tag Name 17B-HIC__063 is invalid
+
+tagreader/web_handlers.py:486: UserWarning</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_read[RNG-60]" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="104" time="0.471"><failure message="UserWarning: Tag Name 17B-HIC__063 is invalid">Client = &lt;tagreader.clients.IMSClient object at 0x133d2e5d0&gt;, read_type = 'RNG', size = 60
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 12),  # 14, but two duplicate times that are deduplicated
+            # pytest.param("SHAPEPRESERVING", 0, marks=pytest.mark.skip),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "RAW":
+            df = Client.read(
+                TAGS["HIC"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+        elif read_type == "SNAPSHOT":
+            df = Client.read(TAGS["HIC"], read_type=getattr(ReaderType, read_type))
+        else:
+&gt;           df = Client.read(
+                TAGS["HIC"], START_TIME, STOP_TIME, 60, getattr(ReaderType, read_type)
+            )
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:136: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133d2fed0&gt;, tag = '17B-HIC__063;CS ProcVal', start_time = Timestamp('2018-05-01 10:00:00+0200', tz='Europe/Oslo')
+stop_time = Timestamp('2018-05-01 11:00:00+0200', tz='Europe/Oslo'), sample_time = Timedelta('0 days 00:01:00'), read_type = &lt;ReaderType.RNG: 9&gt;, metadata = {}, get_status = False
+
+    def read_tag(
+        self,
+        tag,
+        start_time,
+        stop_time,
+        sample_time,
+        read_type,
+        metadata=None,
+        get_status=False,
+    ):
+        if read_type not in [
+            ReaderType.INT,
+            ReaderType.MIN,
+            ReaderType.MAX,
+            ReaderType.RNG,
+            ReaderType.AVG,
+            ReaderType.VAR,
+            ReaderType.STD,
+            ReaderType.SNAPSHOT,
+            ReaderType.RAW,
+        ]:
+            raise NotImplementedError
+    
+        if read_type == ReaderType.SNAPSHOT:
+            url = urljoin(self.base_url, "Attribute")
+        else:
+            url = urljoin(self.base_url, "History")
+    
+        # Actual and bestfit read types allow specifying maxpoints.
+        # Aggregate reads limit to 10 000 points and issue a moredata-token.
+        # TODO: May need to look into using this later - most likely more
+        # efficient than creating new query starting at previous stoptime.
+        # Interpolated reads return error message if more than 100 000 points,
+        # so we need to limit the range. Note -1 because INT normally includes
+        # both start and end time.
+        if read_type == ReaderType.INT:
+            stop_time = min(stop_time, start_time + sample_time * (self._max_rows - 1))
+    
+        tagname, mapname = self.split_tagmap(tag)
+    
+        params = self.generate_read_query(
+            tagname, mapname, start_time, stop_time, sample_time, read_type
+        )
+    
+        res = self.session.get(url, params=params)
+        res.raise_for_status()
+    
+        if len(res.text) == 0:  # res.text='' for timestamps in future
+            return pd.DataFrame(columns=[tag])
+    
+        try:
+            j = res.json()
+        except JSONDecodeError:
+            # AspenOne sometimes returns completely and utterly invalid -nan.
+            # Since json/simplejson has no mechanism to handle this, we need to
+            # pre-process
+            import json
+    
+            txt = res.text.replace('"v":nan', '"v":NaN').replace('"v":-nan', '"v":NaN')
+            j = json.loads(txt)
+    
+        if "er" in j["data"][0]["samples"][0]:
+&gt;           warnings.warn(j["data"][0]["samples"][0]["es"])
+E           UserWarning: Tag Name 17B-HIC__063 is invalid
+
+tagreader/web_handlers.py:486: UserWarning</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_read[AVG-60]" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="104" time="0.497"><failure message="UserWarning: Tag Name 17B-HIC__063 is invalid">Client = &lt;tagreader.clients.IMSClient object at 0x137914150&gt;, read_type = 'AVG', size = 60
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 12),  # 14, but two duplicate times that are deduplicated
+            # pytest.param("SHAPEPRESERVING", 0, marks=pytest.mark.skip),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "RAW":
+            df = Client.read(
+                TAGS["HIC"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+        elif read_type == "SNAPSHOT":
+            df = Client.read(TAGS["HIC"], read_type=getattr(ReaderType, read_type))
+        else:
+&gt;           df = Client.read(
+                TAGS["HIC"], START_TIME, STOP_TIME, 60, getattr(ReaderType, read_type)
+            )
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:136: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x137917ed0&gt;, tag = '17B-HIC__063;CS ProcVal', start_time = Timestamp('2018-05-01 10:00:00+0200', tz='Europe/Oslo')
+stop_time = Timestamp('2018-05-01 11:00:00+0200', tz='Europe/Oslo'), sample_time = Timedelta('0 days 00:01:00'), read_type = &lt;ReaderType.AVG: 6&gt;, metadata = {}, get_status = False
+
+    def read_tag(
+        self,
+        tag,
+        start_time,
+        stop_time,
+        sample_time,
+        read_type,
+        metadata=None,
+        get_status=False,
+    ):
+        if read_type not in [
+            ReaderType.INT,
+            ReaderType.MIN,
+            ReaderType.MAX,
+            ReaderType.RNG,
+            ReaderType.AVG,
+            ReaderType.VAR,
+            ReaderType.STD,
+            ReaderType.SNAPSHOT,
+            ReaderType.RAW,
+        ]:
+            raise NotImplementedError
+    
+        if read_type == ReaderType.SNAPSHOT:
+            url = urljoin(self.base_url, "Attribute")
+        else:
+            url = urljoin(self.base_url, "History")
+    
+        # Actual and bestfit read types allow specifying maxpoints.
+        # Aggregate reads limit to 10 000 points and issue a moredata-token.
+        # TODO: May need to look into using this later - most likely more
+        # efficient than creating new query starting at previous stoptime.
+        # Interpolated reads return error message if more than 100 000 points,
+        # so we need to limit the range. Note -1 because INT normally includes
+        # both start and end time.
+        if read_type == ReaderType.INT:
+            stop_time = min(stop_time, start_time + sample_time * (self._max_rows - 1))
+    
+        tagname, mapname = self.split_tagmap(tag)
+    
+        params = self.generate_read_query(
+            tagname, mapname, start_time, stop_time, sample_time, read_type
+        )
+    
+        res = self.session.get(url, params=params)
+        res.raise_for_status()
+    
+        if len(res.text) == 0:  # res.text='' for timestamps in future
+            return pd.DataFrame(columns=[tag])
+    
+        try:
+            j = res.json()
+        except JSONDecodeError:
+            # AspenOne sometimes returns completely and utterly invalid -nan.
+            # Since json/simplejson has no mechanism to handle this, we need to
+            # pre-process
+            import json
+    
+            txt = res.text.replace('"v":nan', '"v":NaN').replace('"v":-nan', '"v":NaN')
+            j = json.loads(txt)
+    
+        if "er" in j["data"][0]["samples"][0]:
+&gt;           warnings.warn(j["data"][0]["samples"][0]["es"])
+E           UserWarning: Tag Name 17B-HIC__063 is invalid
+
+tagreader/web_handlers.py:486: UserWarning</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_read[VAR-60]" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="104" time="0.494"><failure message="UserWarning: Tag Name 17B-HIC__063 is invalid">Client = &lt;tagreader.clients.IMSClient object at 0x133dc2d10&gt;, read_type = 'VAR', size = 60
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 12),  # 14, but two duplicate times that are deduplicated
+            # pytest.param("SHAPEPRESERVING", 0, marks=pytest.mark.skip),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "RAW":
+            df = Client.read(
+                TAGS["HIC"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+        elif read_type == "SNAPSHOT":
+            df = Client.read(TAGS["HIC"], read_type=getattr(ReaderType, read_type))
+        else:
+&gt;           df = Client.read(
+                TAGS["HIC"], START_TIME, STOP_TIME, 60, getattr(ReaderType, read_type)
+            )
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:136: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133dc1a50&gt;, tag = '17B-HIC__063;CS ProcVal', start_time = Timestamp('2018-05-01 10:00:00+0200', tz='Europe/Oslo')
+stop_time = Timestamp('2018-05-01 11:00:00+0200', tz='Europe/Oslo'), sample_time = Timedelta('0 days 00:01:00'), read_type = &lt;ReaderType.VAR: 7&gt;, metadata = {}, get_status = False
+
+    def read_tag(
+        self,
+        tag,
+        start_time,
+        stop_time,
+        sample_time,
+        read_type,
+        metadata=None,
+        get_status=False,
+    ):
+        if read_type not in [
+            ReaderType.INT,
+            ReaderType.MIN,
+            ReaderType.MAX,
+            ReaderType.RNG,
+            ReaderType.AVG,
+            ReaderType.VAR,
+            ReaderType.STD,
+            ReaderType.SNAPSHOT,
+            ReaderType.RAW,
+        ]:
+            raise NotImplementedError
+    
+        if read_type == ReaderType.SNAPSHOT:
+            url = urljoin(self.base_url, "Attribute")
+        else:
+            url = urljoin(self.base_url, "History")
+    
+        # Actual and bestfit read types allow specifying maxpoints.
+        # Aggregate reads limit to 10 000 points and issue a moredata-token.
+        # TODO: May need to look into using this later - most likely more
+        # efficient than creating new query starting at previous stoptime.
+        # Interpolated reads return error message if more than 100 000 points,
+        # so we need to limit the range. Note -1 because INT normally includes
+        # both start and end time.
+        if read_type == ReaderType.INT:
+            stop_time = min(stop_time, start_time + sample_time * (self._max_rows - 1))
+    
+        tagname, mapname = self.split_tagmap(tag)
+    
+        params = self.generate_read_query(
+            tagname, mapname, start_time, stop_time, sample_time, read_type
+        )
+    
+        res = self.session.get(url, params=params)
+        res.raise_for_status()
+    
+        if len(res.text) == 0:  # res.text='' for timestamps in future
+            return pd.DataFrame(columns=[tag])
+    
+        try:
+            j = res.json()
+        except JSONDecodeError:
+            # AspenOne sometimes returns completely and utterly invalid -nan.
+            # Since json/simplejson has no mechanism to handle this, we need to
+            # pre-process
+            import json
+    
+            txt = res.text.replace('"v":nan', '"v":NaN').replace('"v":-nan', '"v":NaN')
+            j = json.loads(txt)
+    
+        if "er" in j["data"][0]["samples"][0]:
+&gt;           warnings.warn(j["data"][0]["samples"][0]["es"])
+E           UserWarning: Tag Name 17B-HIC__063 is invalid
+
+tagreader/web_handlers.py:486: UserWarning</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_read[STD-60]" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="104" time="0.542"><failure message="UserWarning: Tag Name 17B-HIC__063 is invalid">Client = &lt;tagreader.clients.IMSClient object at 0x137bb8350&gt;, read_type = 'STD', size = 60
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 12),  # 14, but two duplicate times that are deduplicated
+            # pytest.param("SHAPEPRESERVING", 0, marks=pytest.mark.skip),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "RAW":
+            df = Client.read(
+                TAGS["HIC"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+        elif read_type == "SNAPSHOT":
+            df = Client.read(TAGS["HIC"], read_type=getattr(ReaderType, read_type))
+        else:
+&gt;           df = Client.read(
+                TAGS["HIC"], START_TIME, STOP_TIME, 60, getattr(ReaderType, read_type)
+            )
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:136: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x137bb93d0&gt;, tag = '17B-HIC__063;CS ProcVal', start_time = Timestamp('2018-05-01 10:00:00+0200', tz='Europe/Oslo')
+stop_time = Timestamp('2018-05-01 11:00:00+0200', tz='Europe/Oslo'), sample_time = Timedelta('0 days 00:01:00'), read_type = &lt;ReaderType.STD: 8&gt;, metadata = {}, get_status = False
+
+    def read_tag(
+        self,
+        tag,
+        start_time,
+        stop_time,
+        sample_time,
+        read_type,
+        metadata=None,
+        get_status=False,
+    ):
+        if read_type not in [
+            ReaderType.INT,
+            ReaderType.MIN,
+            ReaderType.MAX,
+            ReaderType.RNG,
+            ReaderType.AVG,
+            ReaderType.VAR,
+            ReaderType.STD,
+            ReaderType.SNAPSHOT,
+            ReaderType.RAW,
+        ]:
+            raise NotImplementedError
+    
+        if read_type == ReaderType.SNAPSHOT:
+            url = urljoin(self.base_url, "Attribute")
+        else:
+            url = urljoin(self.base_url, "History")
+    
+        # Actual and bestfit read types allow specifying maxpoints.
+        # Aggregate reads limit to 10 000 points and issue a moredata-token.
+        # TODO: May need to look into using this later - most likely more
+        # efficient than creating new query starting at previous stoptime.
+        # Interpolated reads return error message if more than 100 000 points,
+        # so we need to limit the range. Note -1 because INT normally includes
+        # both start and end time.
+        if read_type == ReaderType.INT:
+            stop_time = min(stop_time, start_time + sample_time * (self._max_rows - 1))
+    
+        tagname, mapname = self.split_tagmap(tag)
+    
+        params = self.generate_read_query(
+            tagname, mapname, start_time, stop_time, sample_time, read_type
+        )
+    
+        res = self.session.get(url, params=params)
+        res.raise_for_status()
+    
+        if len(res.text) == 0:  # res.text='' for timestamps in future
+            return pd.DataFrame(columns=[tag])
+    
+        try:
+            j = res.json()
+        except JSONDecodeError:
+            # AspenOne sometimes returns completely and utterly invalid -nan.
+            # Since json/simplejson has no mechanism to handle this, we need to
+            # pre-process
+            import json
+    
+            txt = res.text.replace('"v":nan', '"v":NaN').replace('"v":-nan', '"v":NaN')
+            j = json.loads(txt)
+    
+        if "er" in j["data"][0]["samples"][0]:
+&gt;           warnings.warn(j["data"][0]["samples"][0]["es"])
+E           UserWarning: Tag Name 17B-HIC__063 is invalid
+
+tagreader/web_handlers.py:486: UserWarning</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_read[SNAPSHOT-1]" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="104" time="0.520"><failure message="UserWarning: Invalid Tag ( 17B-HIC__063 )">Client = &lt;tagreader.clients.IMSClient object at 0x133c4e910&gt;, read_type = 'SNAPSHOT', size = 1
+
+    @pytest.mark.parametrize(
+        ("read_type", "size"),
+        [
+            ("RAW", 12),  # 14, but two duplicate times that are deduplicated
+            # pytest.param("SHAPEPRESERVING", 0, marks=pytest.mark.skip),
+            ("INT", 61),
+            ("MIN", 60),
+            ("MAX", 60),
+            ("RNG", 60),
+            ("AVG", 60),
+            ("VAR", 60),
+            ("STD", 60),
+            # pytest.param("COUNT", 0, marks=pytest.mark.skip),
+            # pytest.param("GOOD", 0, marks=pytest.mark.skip),
+            # pytest.param("BAD", 0, marks=pytest.mark.skip),
+            # pytest.param("TOTAL", 0, marks=pytest.mark.skip),
+            # pytest.param("SUM", 0, marks=pytest.mark.skip),
+            ("SNAPSHOT", 1),
+        ],
+    )
+    def test_read(Client, read_type, size):
+        if read_type == "RAW":
+            df = Client.read(
+                TAGS["HIC"],
+                start_time=START_TIME,
+                end_time=STOP_TIME,
+                read_type=getattr(ReaderType, read_type),
+            )
+        elif read_type == "SNAPSHOT":
+&gt;           df = Client.read(TAGS["HIC"], read_type=getattr(ReaderType, read_type))
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:134: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:324: in _read_single_tag
+    df = self.handler.read_tag(
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133c4e6d0&gt;, tag = '17B-HIC__063;CS ProcVal', start_time = None, stop_time = None, sample_time = Timedelta('0 days 00:01:00')
+read_type = &lt;ReaderType.SNAPSHOT: 15&gt;, metadata = {}, get_status = False
+
+    def read_tag(
+        self,
+        tag,
+        start_time,
+        stop_time,
+        sample_time,
+        read_type,
+        metadata=None,
+        get_status=False,
+    ):
+        if read_type not in [
+            ReaderType.INT,
+            ReaderType.MIN,
+            ReaderType.MAX,
+            ReaderType.RNG,
+            ReaderType.AVG,
+            ReaderType.VAR,
+            ReaderType.STD,
+            ReaderType.SNAPSHOT,
+            ReaderType.RAW,
+        ]:
+            raise NotImplementedError
+    
+        if read_type == ReaderType.SNAPSHOT:
+            url = urljoin(self.base_url, "Attribute")
+        else:
+            url = urljoin(self.base_url, "History")
+    
+        # Actual and bestfit read types allow specifying maxpoints.
+        # Aggregate reads limit to 10 000 points and issue a moredata-token.
+        # TODO: May need to look into using this later - most likely more
+        # efficient than creating new query starting at previous stoptime.
+        # Interpolated reads return error message if more than 100 000 points,
+        # so we need to limit the range. Note -1 because INT normally includes
+        # both start and end time.
+        if read_type == ReaderType.INT:
+            stop_time = min(stop_time, start_time + sample_time * (self._max_rows - 1))
+    
+        tagname, mapname = self.split_tagmap(tag)
+    
+        params = self.generate_read_query(
+            tagname, mapname, start_time, stop_time, sample_time, read_type
+        )
+    
+        res = self.session.get(url, params=params)
+        res.raise_for_status()
+    
+        if len(res.text) == 0:  # res.text='' for timestamps in future
+            return pd.DataFrame(columns=[tag])
+    
+        try:
+            j = res.json()
+        except JSONDecodeError:
+            # AspenOne sometimes returns completely and utterly invalid -nan.
+            # Since json/simplejson has no mechanism to handle this, we need to
+            # pre-process
+            import json
+    
+            txt = res.text.replace('"v":nan', '"v":NaN').replace('"v":-nan', '"v":NaN')
+            j = json.loads(txt)
+    
+        if "er" in j["data"][0]["samples"][0]:
+&gt;           warnings.warn(j["data"][0]["samples"][0]["es"])
+E           UserWarning: Invalid Tag ( 17B-HIC__063 )
+
+tagreader/web_handlers.py:486: UserWarning</failure></testcase><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_tags_with_no_data_included_in_results" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="147" time="0.841" /><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_tags_with_qnan_returned_as_nan" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="173" time="0.449" /><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_tag_with_status" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="181" time="0.527" /><testcase classname="tests.extratests.tests.test_extra_AspenHandlerREST_connect" name="test_nan_in_json" file="tests/extratests/tests/test_extra_AspenHandlerREST_connect.py" line="197" time="0.538"><failure message="UserWarning: Tag Name 16A-AI___127D is invalid">def test_nan_in_json():
+        c = IMSClient("SNA", "aspenone")
+        c.cache = None
+        c.connect()
+        # This used to raise JSONDecodeError
+&gt;       df = c.read("16A-AI___127D", "2021-03-23 06:00:00", "2021-03-23 08:00:00", 3600)
+
+tests/extratests/tests/test_extra_AspenHandlerREST_connect.py:203: 
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+tagreader/clients.py:517: in read
+    self._read_single_tag(
+tagreader/clients.py:376: in _read_single_tag
+    df = self.handler.read_tag(
+_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+self = &lt;tagreader.web_handlers.AspenHandlerWeb object at 0x133d85810&gt;, tag = '16A-AI___127D', start_time = Timestamp('2021-03-23 06:00:00+0100', tz='Europe/Oslo')
+stop_time = Timestamp('2021-03-23 08:00:00+0100', tz='Europe/Oslo'), sample_time = Timedelta('0 days 01:00:00'), read_type = &lt;ReaderType.INT: 3&gt;, metadata = {}, get_status = False
+
+    def read_tag(
+        self,
+        tag,
+        start_time,
+        stop_time,
+        sample_time,
+        read_type,
+        metadata=None,
+        get_status=False,
+    ):
+        if read_type not in [
+            ReaderType.INT,
+            ReaderType.MIN,
+            ReaderType.MAX,
+            ReaderType.RNG,
+            ReaderType.AVG,
+            ReaderType.VAR,
+            ReaderType.STD,
+            ReaderType.SNAPSHOT,
+            ReaderType.RAW,
+        ]:
+            raise NotImplementedError
+    
+        if read_type == ReaderType.SNAPSHOT:
+            url = urljoin(self.base_url, "Attribute")
+        else:
+            url = urljoin(self.base_url, "History")
+    
+        # Actual and bestfit read types allow specifying maxpoints.
+        # Aggregate reads limit to 10 000 points and issue a moredata-token.
+        # TODO: May need to look into using this later - most likely more
+        # efficient than creating new query starting at previous stoptime.
+        # Interpolated reads return error message if more than 100 000 points,
+        # so we need to limit the range. Note -1 because INT normally includes
+        # both start and end time.
+        if read_type == ReaderType.INT:
+            stop_time = min(stop_time, start_time + sample_time * (self._max_rows - 1))
+    
+        tagname, mapname = self.split_tagmap(tag)
+    
+        params = self.generate_read_query(
+            tagname, mapname, start_time, stop_time, sample_time, read_type
+        )
+    
+        res = self.session.get(url, params=params)
+        res.raise_for_status()
+    
+        if len(res.text) == 0:  # res.text='' for timestamps in future
+            return pd.DataFrame(columns=[tag])
+    
+        try:
+            j = res.json()
+        except JSONDecodeError:
+            # AspenOne sometimes returns completely and utterly invalid -nan.
+            # Since json/simplejson has no mechanism to handle this, we need to
+            # pre-process
+            import json
+    
+            txt = res.text.replace('"v":nan', '"v":NaN').replace('"v":-nan', '"v":NaN')
+            j = json.loads(txt)
+    
+        if "er" in j["data"][0]["samples"][0]:
+&gt;           warnings.warn(j["data"][0]["samples"][0]["es"])
+E           UserWarning: Tag Name 16A-AI___127D is invalid
+
+tagreader/web_handlers.py:486: UserWarning</failure></testcase></testsuite></testsuites>
\ No newline at end of file