From b885faf2fcbce5f2dabf698d84ecc29b95e2d698 Mon Sep 17 00:00:00 2001 From: Andrew Meyer <75644492+ameyer-rigetti@users.noreply.github.com> Date: Mon, 26 Apr 2021 11:44:12 -0700 Subject: [PATCH] Update: Add concurrent programming support (#1326) --- .github/workflows/test.yml | 33 ++- .gitlab-ci.yml | 20 +- CHANGELOG.md | 2 + CONTRIBUTING.md | 7 +- Makefile | 2 +- docs/source/advanced_usage.rst | 85 ++++++- poetry.lock | 304 +++++++++++++------------- pyquil/api/_benchmark.py | 2 +- pyquil/api/_compiler.py | 4 +- pyquil/api/_compiler_client.py | 2 +- pyquil/api/_engagement_manager.py | 28 ++- pyquil/api/_qcs_client.py | 18 +- pyquil/api/_qpu.py | 2 +- pyquil/api/_qpu_client.py | 6 +- pyquil/api/_quantum_computer.py | 4 +- pyquil/api/_qvm.py | 4 +- pyquil/api/_qvm_client.py | 2 +- pyquil/api/_wavefunction_simulator.py | 2 +- pyquil/quantum_processor/qcs.py | 4 +- scripts/ci_install_deps | 1 - test/e2e/conftest.py | 26 +++ test/e2e/test_e2e.py | 81 ++++--- test/unit/utils.py | 11 + 23 files changed, 438 insertions(+), 212 deletions(-) create mode 100644 test/e2e/conftest.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 35bf2a5b0..abedf401a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,6 +46,7 @@ jobs: sudo apt update . scripts/ci_install_deps poetry run make check-format + check-style: name: Check style runs-on: ubuntu-latest @@ -64,6 +65,7 @@ jobs: sudo apt update . scripts/ci_install_deps poetry run make check-style + check-types: name: Check types runs-on: ubuntu-latest @@ -82,8 +84,9 @@ jobs: sudo apt update . scripts/ci_install_deps poetry run make check-types - test-pyquil: - name: Test pyQuil + + test-unit: + name: Test Unit runs-on: ubuntu-latest strategy: matrix: @@ -98,10 +101,34 @@ jobs: with: path: .venv key: poetry-${{ hashFiles('poetry.lock') }} - - name: Test pyQuil (Python ${{ matrix.python-version }}) + - name: Test Unit (Python ${{ matrix.python-version }}) run: | sudo apt update . scripts/ci_install_deps docker run --rm -itd -p 5555:5555 rigetti/quilc -R docker run --rm -itd -p 5000:5000 rigetti/qvm -S poetry run make test + + test-e2e: + name: Test e2e QVM + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.7, 3.8] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - uses: actions/cache@v2 + with: + path: .venv + key: poetry-${{ hashFiles('poetry.lock') }} + - name: Test e2e QVM (Python ${{ matrix.python-version }}) + run: | + sudo apt update + . scripts/ci_install_deps + docker run --rm -itd -p 5555:5555 rigetti/quilc -R + docker run --rm -itd -p 5000:5000 rigetti/qvm -S + poetry run make e2e TEST_QUANTUM_PROCESSOR=2q-qvm diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1b45351b2..c4aee62ea 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -73,7 +73,7 @@ Test Unit (3.7): - . scripts/ci_install_deps - poetry run make test -Test e2e (3.7): +Test e2e QPU (3.7): stage: test image: python:3.7 coverage: '/TOTAL.*?(\d+)\%/' @@ -83,6 +83,14 @@ Test e2e (3.7): rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "rc"' +Test e2e QVM (3.7): + stage: test + image: python:3.7 + coverage: '/TOTAL.*?(\d+)\%/' + script: + - . scripts/ci_install_deps + - poetry run make e2e TEST_QUANTUM_PROCESSOR=2q-qvm + Test Unit (3.8): stage: test image: python:3.8 @@ -91,7 +99,7 @@ Test Unit (3.8): - . scripts/ci_install_deps - poetry run make test -Test e2e (3.8): +Test e2e QPU (3.8): stage: test image: python:3.8 coverage: '/TOTAL.*?(\d+)\%/' @@ -101,6 +109,14 @@ Test e2e (3.8): rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "rc"' +Test e2e QVM (3.8): + stage: test + image: python:3.8 + coverage: '/TOTAL.*?(\d+)\%/' + script: + - . scripts/ci_install_deps + - poetry run make e2e TEST_QUANTUM_PROCESSOR=2q-qvm + Coverage: stage: test script: diff --git a/CHANGELOG.md b/CHANGELOG.md index ff4bf630a..cf3756d4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ Changelog ### Improvements and Changes +- Added support and documentation for concurrent compilation and execution (see "Advanced Usage" in docs) + - `pyquil.version.__version__` has been moved to `pyquil.__version__`. - `PyquilConfig` has been replaced by `api.QCSClientConfiguration`. As a result, the only supported configuration-related diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5dda9a095..87383f8ff 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -175,8 +175,7 @@ To skip [slow tests](#slow-tests), you may run: make test-fast ``` -If you have access to a real Rigetti QPU via QCS (as opposed to a local QVM), you can run end-to-end -tests with: +You can run end-to-end tests with: ```bash make e2e TEST_QUANTUM_PROCESSOR= @@ -188,6 +187,10 @@ Or you may run all tests (unit/integration/e2e) with: make test-all TEST_QUANTUM_PROCESSOR= ``` +> **Note:** for `TEST_QUANTUM_PROCESSOR`, supply a value similar to what you would supply to +> `get_qc()`. End-to-end tests are most useful against a real QPU, but they can also be run +> against a QVM (e.g. `make e2e TEST_QUANTUM_PROCESSOR=2q-qvm`). + #### Slow Tests Some tests (particularly those related to operator estimation and readout symmetrization) diff --git a/Makefile b/Makefile index 8b125934b..7e9c3510d 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,7 @@ test-fast: .PHONY: e2e e2e: - pytest -n auto -v --cov=pyquil test/e2e + pytest -n 1 -v --cov=pyquil test/e2e .PHONY: test-all test-all: test e2e diff --git a/docs/source/advanced_usage.rst b/docs/source/advanced_usage.rst index e9ffee1a8..1d3b857de 100644 --- a/docs/source/advanced_usage.rst +++ b/docs/source/advanced_usage.rst @@ -15,8 +15,8 @@ pyQuil Configuration :py:class:`~pyquil.api.QCSClientConfiguration` instructs pyQuil on how to connect with the components needed to compile and run programs (quilc, QVMs, and QCS). Any APIs that take a configuration object -as input (e.g. :func:`get_qc()`) typically do so optionally, so that a default configuration can be loaded for -you if one is not provided. You can override this default configuration by either instantiating your own +as input (e.g. :py:func:`~pyquil.get_qc()`) typically do so optionally, so that a default configuration can be loaded +for you if one is not provided. You can override this default configuration by either instantiating your own :py:class:`~pyquil.api.QCSClientConfiguration` object and providing it as input to the function in question, or by setting the ``QCS_SETTINGS_FILE_PATH`` and/or ``QCS_SECRETS_FILE_PATH`` environment variables to have pyQuil load its settings and secrets from specific locations. By default, configuration will be loaded from @@ -32,6 +32,87 @@ locally): - QVM URL: ``http://127.0.0.1:5000`` - quilc URL: ``tcp://127.0.0.1:5555`` +Concurrency +~~~~~~~~~~~ + +Using pyQuil for concurrent programming is as simple as calling :py:func:`~pyquil.get_qc()` from within a given thread +or process, then using the returned :py:class:`~pyquil.api.QuantumComputer` as usual. While +:py:class:`~pyquil.api.QuantumComputer` objects as a whole are not safe to share between threads or processes (due to +state related to currently-running compilation or execution requests), some information they use is. Information related +to client configuration (:py:class:`~pyquil.api.QCSClientConfiguration`) and QPU auth +(:py:class:`~pyquil.api.EngagementManager`) can be safely extracted and shared among +:py:class:`~pyquil.api.QuantumComputer` instances, as shown below, to save your code from redundant disk reads and +auth-related HTTP requests. + +.. note:: + QVMs process incoming requests in parallel, while QPUs process them sequentially. If you encounter timeouts while + trying to run large numbers of programs against a QPU, try increasing the ``timeout`` parameter on calls to + :py:func:`~pyquil.get_qc()` (specified in seconds). + +Using Multithreading +-------------------- + +.. code:: python + + from multiprocessing.pool import ThreadPool + + from pyquil import get_qc, Program + from pyquil.api import EngagementManager, QCSClientConfiguration + + + configuration = QCSClientConfiguration.load() + engagement_manager = EngagementManager(client_configuration=configuration) + + + def run(program: Program): + qc = get_qc("Aspen-8", client_configuration=configuration, engagement_manager=engagement_manager) + return qc.run(qc.compile(program)) + + + programs = [Program("DECLARE ro BIT", "RX(pi) 0", "MEASURE 0 ro").wrap_in_numshots_loop(10)] * 20 + with ThreadPool(5) as pool: + results = pool.map(run, programs) + + for i, result in enumerate(results): + print(f"Results for program {i}:\n{result}\n") + + +Using Multiprocessing +--------------------- + +.. code:: python + + from multiprocessing.pool import Pool + + from pyquil import get_qc, Program + from pyquil.api import EngagementManager, QCSClientConfiguration + + + configuration = QCSClientConfiguration.load() + engagement_manager = EngagementManager(client_configuration=configuration) + + + def run(program: Program): + qc = get_qc("Aspen-8", client_configuration=configuration, engagement_manager=engagement_manager) + return qc.run(qc.compile(program)) + + + programs = [Program("DECLARE ro BIT", "RX(pi) 0", "MEASURE 0 ro").wrap_in_numshots_loop(10)] * 20 + with Pool(5) as pool: + results = pool.map(run, programs) + + for i, result in enumerate(results): + print(f"Results for program {i}:\n{result}\n") + +.. note:: + If you encounter error messages on macOS similar to the following: + + .. parsed-literal:: + +[__NSCFConstantString initialize] may have been in progress in another thread when fork() was called. + + try setting the environment variable ``OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES``. + + Using Qubit Placeholders ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/poetry.lock b/poetry.lock index 97756b087..dcce37ea8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -298,7 +298,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "importlib-metadata" -version = "3.7.3" +version = "3.10.1" description = "Read metadata from Python packages" category = "main" optional = false @@ -310,7 +310,7 @@ zipp = ">=0.5" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" @@ -322,7 +322,7 @@ python-versions = "*" [[package]] name = "ipython" -version = "7.21.0" +version = "7.22.0" description = "IPython: Productive Interactive Computing" category = "main" optional = false @@ -341,7 +341,7 @@ pygments = "*" traitlets = ">=4.2" [package.extras] -all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.14)", "pygments", "qtconsole", "requests", "testpath"] +all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.16)", "pygments", "qtconsole", "requests", "testpath"] doc = ["Sphinx (>=1.3)"] kernel = ["ipykernel"] nbconvert = ["nbconvert"] @@ -349,7 +349,7 @@ nbformat = ["nbformat"] notebook = ["notebook", "ipywidgets"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"] +test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.16)"] [[package]] name = "ipython-genutils" @@ -416,14 +416,15 @@ format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator [[package]] name = "jupyter-client" -version = "6.1.12" +version = "6.2.0" description = "Jupyter protocol implementation and client libraries" category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6.1" [package.dependencies] jupyter-core = ">=4.6.0" +nest-asyncio = ">=1.5" python-dateutil = ">=2.1" pyzmq = ">=13" tornado = ">=4.1" @@ -431,7 +432,7 @@ traitlets = "*" [package.extras] doc = ["sphinx (>=1.3.6)", "sphinx-rtd-theme", "sphinxcontrib-github-alt"] -test = ["async-generator", "ipykernel", "ipython", "mock", "pytest-asyncio", "pytest-timeout", "pytest", "jedi (<0.18)"] +test = ["async-generator", "ipykernel", "ipython", "mock", "pytest-asyncio", "pytest-timeout", "pytest", "mypy", "pre-commit", "jedi (<0.18)"] [[package]] name = "jupyter-core" @@ -576,7 +577,7 @@ webpdf = ["pyppeteer (==0.2.2)"] [[package]] name = "nbformat" -version = "5.1.2" +version = "5.1.3" description = "The Jupyter Notebook format" category = "main" optional = false @@ -594,7 +595,7 @@ test = ["check-manifest", "fastjsonschema", "testpath", "pytest", "pytest-cov"] [[package]] name = "nbsphinx" -version = "0.8.2" +version = "0.8.3" description = "Jupyter Notebook Tools for Sphinx" category = "main" optional = false @@ -618,14 +619,14 @@ python-versions = ">=3.5" [[package]] name = "networkx" -version = "2.5" +version = "2.5.1" description = "Python package for creating and manipulating graphs and networks" category = "main" optional = false python-versions = ">=3.6" [package.dependencies] -decorator = ">=4.3.0" +decorator = ">=4.3,<5" [package.extras] all = ["numpy", "scipy", "pandas", "matplotlib", "pygraphviz", "pydot", "pyyaml", "lxml", "pytest"] @@ -642,7 +643,7 @@ scipy = ["scipy"] [[package]] name = "numpy" -version = "1.20.1" +version = "1.20.2" description = "NumPy is the fundamental package for array computing with Python." category = "main" optional = false @@ -669,7 +670,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "parso" -version = "0.8.1" +version = "0.8.2" description = "A Python Parser" category = "main" optional = false @@ -722,7 +723,7 @@ dev = ["pre-commit", "tox"] [[package]] name = "prompt-toolkit" -version = "3.0.17" +version = "3.0.18" description = "Library for building powerful interactive command lines in Python" category = "main" optional = false @@ -780,7 +781,7 @@ email = ["email-validator (>=1.0.3)"] [[package]] name = "pyflakes" -version = "2.3.0" +version = "2.3.1" description = "passive checker of Python programs" category = "dev" optional = false @@ -825,7 +826,7 @@ python-versions = ">=3.5" [[package]] name = "pytest" -version = "6.2.2" +version = "6.2.3" description = "pytest: simple powerful testing with Python" category = "dev" optional = false @@ -1007,7 +1008,7 @@ sphinx = ">=1.3.1" [[package]] name = "regex" -version = "2021.3.17" +version = "2021.4.4" description = "Alternative regular expression module, to replace re." category = "dev" optional = false @@ -1078,7 +1079,7 @@ idna2008 = ["idna"] [[package]] name = "rpcq" -version = "3.7.0" +version = "3.9.1" description = "The RPC framework and message specification for Rigetti QCS." category = "main" optional = false @@ -1092,11 +1093,11 @@ pyzmq = ">=17" [[package]] name = "ruamel.yaml" -version = "0.16.13" +version = "0.17.4" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" category = "main" optional = false -python-versions = "*" +python-versions = ">=3" [package.dependencies] "ruamel.yaml.clib" = {version = ">=0.1.2", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.10\""} @@ -1150,7 +1151,7 @@ python-versions = "*" [[package]] name = "sphinx" -version = "3.5.2" +version = "3.5.4" description = "Python documentation generator" category = "main" optional = false @@ -1160,7 +1161,7 @@ python-versions = ">=3.5" alabaster = ">=0.7,<0.8" babel = ">=1.3" colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.12" +docutils = ">=0.12,<0.17" imagesize = "*" Jinja2 = ">=2.3" packaging = "*" @@ -1181,11 +1182,11 @@ test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] [[package]] name = "sphinx-autodoc-typehints" -version = "1.11.1" +version = "1.12.0" description = "Type hints (PEP 484) support for the Sphinx autodoc extension" category = "main" optional = false -python-versions = ">=3.5.2" +python-versions = ">=3.6" [package.dependencies] Sphinx = ">=3.0" @@ -1196,13 +1197,14 @@ type_comments = ["typed-ast (>=1.4.0)"] [[package]] name = "sphinx-rtd-theme" -version = "0.5.1" +version = "0.5.2" description = "Read the Docs theme for Sphinx" category = "main" optional = false python-versions = "*" [package.dependencies] +docutils = "<0.17" sphinx = "*" [package.extras] @@ -1322,7 +1324,7 @@ test = ["pytest"] [[package]] name = "typed-ast" -version = "1.4.2" +version = "1.4.3" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" optional = false @@ -1587,16 +1589,16 @@ imagesize = [ {file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"}, ] importlib-metadata = [ - {file = "importlib_metadata-3.7.3-py3-none-any.whl", hash = "sha256:b74159469b464a99cb8cc3e21973e4d96e05d3024d337313fedb618a6e86e6f4"}, - {file = "importlib_metadata-3.7.3.tar.gz", hash = "sha256:742add720a20d0467df2f444ae41704000f50e1234f46174b51f9c6031a1bd71"}, + {file = "importlib_metadata-3.10.1-py3-none-any.whl", hash = "sha256:2ec0faae539743ae6aaa84b49a169670a465f7f5d64e6add98388cc29fd1f2f6"}, + {file = "importlib_metadata-3.10.1.tar.gz", hash = "sha256:c9356b657de65c53744046fa8f7358afe0714a1af7d570c00c3835c2d724a7c1"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] ipython = [ - {file = "ipython-7.21.0-py3-none-any.whl", hash = "sha256:34207ffb2f653bced2bc8e3756c1db86e7d93e44ed049daae9814fed66d408ec"}, - {file = "ipython-7.21.0.tar.gz", hash = "sha256:04323f72d5b85b606330b6d7e2dc8d2683ad46c3905e955aa96ecc7a99388e70"}, + {file = "ipython-7.22.0-py3-none-any.whl", hash = "sha256:c0ce02dfaa5f854809ab7413c601c4543846d9da81010258ecdab299b542d199"}, + {file = "ipython-7.22.0.tar.gz", hash = "sha256:9c900332d4c5a6de534b4befeeb7de44ad0cc42e8327fa41b7685abde58cec74"}, ] ipython-genutils = [ {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, @@ -1619,8 +1621,8 @@ jsonschema = [ {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, ] jupyter-client = [ - {file = "jupyter_client-6.1.12-py3-none-any.whl", hash = "sha256:e053a2c44b6fa597feebe2b3ecb5eea3e03d1d91cc94351a52931ee1426aecfc"}, - {file = "jupyter_client-6.1.12.tar.gz", hash = "sha256:c4bca1d0846186ca8be97f4d2fa6d2bae889cce4892a167ffa1ba6bd1f73e782"}, + {file = "jupyter_client-6.2.0-py3-none-any.whl", hash = "sha256:9715152067e3f7ea3b56f341c9a0f9715c8c7cc316ee0eb13c3c84f5ca0065f5"}, + {file = "jupyter_client-6.2.0.tar.gz", hash = "sha256:e2ab61d79fbf8b56734a4c2499f19830fbd7f6fefb3e87868ef0545cb3c17eb9"}, ] jupyter-core = [ {file = "jupyter_core-4.7.1-py3-none-any.whl", hash = "sha256:8c6c0cac5c1b563622ad49321d5ec47017bd18b94facb381c6973a0486395f8e"}, @@ -1748,46 +1750,46 @@ nbconvert = [ {file = "nbconvert-6.0.7.tar.gz", hash = "sha256:cbbc13a86dfbd4d1b5dee106539de0795b4db156c894c2c5dc382062bbc29002"}, ] nbformat = [ - {file = "nbformat-5.1.2-py3-none-any.whl", hash = "sha256:3949fdc8f5fa0b1afca16fb307546e78494fa7a7bceff880df8168eafda0e7ac"}, - {file = "nbformat-5.1.2.tar.gz", hash = "sha256:1d223e64a18bfa7cdf2db2e9ba8a818312fc2a0701d2e910b58df66809385a56"}, + {file = "nbformat-5.1.3-py3-none-any.whl", hash = "sha256:eb8447edd7127d043361bc17f2f5a807626bc8e878c7709a1c647abda28a9171"}, + {file = "nbformat-5.1.3.tar.gz", hash = "sha256:b516788ad70771c6250977c1374fcca6edebe6126fd2adb5a69aa5c2356fd1c8"}, ] nbsphinx = [ - {file = "nbsphinx-0.8.2-py3-none-any.whl", hash = "sha256:044c5a8b13a03492372954940e99dedfaec206bd0be0faefb0e381c9f4435ac6"}, - {file = "nbsphinx-0.8.2.tar.gz", hash = "sha256:53352237e2363079f6e38637a8ce90b47e720c8e2eb133a6a6f66fc13ff494cb"}, + {file = "nbsphinx-0.8.3-py3-none-any.whl", hash = "sha256:2f511896259c8d0a8f479be9a32add8d01bcc7bbf9176aa18ce80a6324394a89"}, + {file = "nbsphinx-0.8.3.tar.gz", hash = "sha256:447a46ec648fbadf9b389971869efb7f62b5a2498fa71f4f4279be18ceb86365"}, ] nest-asyncio = [ {file = "nest_asyncio-1.5.1-py3-none-any.whl", hash = "sha256:76d6e972265063fe92a90b9cc4fb82616e07d586b346ed9d2c89a4187acea39c"}, {file = "nest_asyncio-1.5.1.tar.gz", hash = "sha256:afc5a1c515210a23c461932765691ad39e8eba6551c055ac8d5546e69250d0aa"}, ] networkx = [ - {file = "networkx-2.5-py3-none-any.whl", hash = "sha256:8c5812e9f798d37c50570d15c4a69d5710a18d77bafc903ee9c5fba7454c616c"}, - {file = "networkx-2.5.tar.gz", hash = "sha256:7978955423fbc9639c10498878be59caf99b44dc304c2286162fd24b458c1602"}, + {file = "networkx-2.5.1-py3-none-any.whl", hash = "sha256:0635858ed7e989f4c574c2328380b452df892ae85084144c73d8cd819f0c4e06"}, + {file = "networkx-2.5.1.tar.gz", hash = "sha256:109cd585cac41297f71103c3c42ac6ef7379f29788eb54cb751be5a663bb235a"}, ] numpy = [ - {file = "numpy-1.20.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ae61f02b84a0211abb56462a3b6cd1e7ec39d466d3160eb4e1da8bf6717cdbeb"}, - {file = "numpy-1.20.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:65410c7f4398a0047eea5cca9b74009ea61178efd78d1be9847fac1d6716ec1e"}, - {file = "numpy-1.20.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2d7e27442599104ee08f4faed56bb87c55f8b10a5494ac2ead5c98a4b289e61f"}, - {file = "numpy-1.20.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:4ed8e96dc146e12c1c5cdd6fb9fd0757f2ba66048bf94c5126b7efebd12d0090"}, - {file = "numpy-1.20.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:ecb5b74c702358cdc21268ff4c37f7466357871f53a30e6f84c686952bef16a9"}, - {file = "numpy-1.20.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b9410c0b6fed4a22554f072a86c361e417f0258838957b78bd063bde2c7f841f"}, - {file = "numpy-1.20.1-cp37-cp37m-win32.whl", hash = "sha256:3d3087e24e354c18fb35c454026af3ed8997cfd4997765266897c68d724e4845"}, - {file = "numpy-1.20.1-cp37-cp37m-win_amd64.whl", hash = "sha256:89f937b13b8dd17b0099c7c2e22066883c86ca1575a975f754babc8fbf8d69a9"}, - {file = "numpy-1.20.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a1d7995d1023335e67fb070b2fae6f5968f5be3802b15ad6d79d81ecaa014fe0"}, - {file = "numpy-1.20.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:60759ab15c94dd0e1ed88241fd4fa3312db4e91d2c8f5a2d4cf3863fad83d65b"}, - {file = "numpy-1.20.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:125a0e10ddd99a874fd357bfa1b636cd58deb78ba4a30b5ddb09f645c3512e04"}, - {file = "numpy-1.20.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c26287dfc888cf1e65181f39ea75e11f42ffc4f4529e5bd19add57ad458996e2"}, - {file = "numpy-1.20.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:7199109fa46277be503393be9250b983f325880766f847885607d9b13848f257"}, - {file = "numpy-1.20.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:72251e43ac426ff98ea802a931922c79b8d7596480300eb9f1b1e45e0543571e"}, - {file = "numpy-1.20.1-cp38-cp38-win32.whl", hash = "sha256:c91ec9569facd4757ade0888371eced2ecf49e7982ce5634cc2cf4e7331a4b14"}, - {file = "numpy-1.20.1-cp38-cp38-win_amd64.whl", hash = "sha256:13adf545732bb23a796914fe5f891a12bd74cf3d2986eed7b7eba2941eea1590"}, - {file = "numpy-1.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:104f5e90b143dbf298361a99ac1af4cf59131218a045ebf4ee5990b83cff5fab"}, - {file = "numpy-1.20.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:89e5336f2bec0c726ac7e7cdae181b325a9c0ee24e604704ed830d241c5e47ff"}, - {file = "numpy-1.20.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:032be656d89bbf786d743fee11d01ef318b0781281241997558fa7950028dd29"}, - {file = "numpy-1.20.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:66b467adfcf628f66ea4ac6430ded0614f5cc06ba530d09571ea404789064adc"}, - {file = "numpy-1.20.1-cp39-cp39-win32.whl", hash = "sha256:12e4ba5c6420917571f1a5becc9338abbde71dd811ce40b37ba62dec7b39af6d"}, - {file = "numpy-1.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:9c94cab5054bad82a70b2e77741271790304651d584e2cdfe2041488e753863b"}, - {file = "numpy-1.20.1-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:9eb551d122fadca7774b97db8a112b77231dcccda8e91a5bc99e79890797175e"}, - {file = "numpy-1.20.1.zip", hash = "sha256:3bc63486a870294683980d76ec1e3efc786295ae00128f9ea38e2c6e74d5a60a"}, + {file = "numpy-1.20.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e9459f40244bb02b2f14f6af0cd0732791d72232bbb0dc4bab57ef88e75f6935"}, + {file = "numpy-1.20.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:a8e6859913ec8eeef3dbe9aed3bf475347642d1cdd6217c30f28dee8903528e6"}, + {file = "numpy-1.20.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:9cab23439eb1ebfed1aaec9cd42b7dc50fc96d5cd3147da348d9161f0501ada5"}, + {file = "numpy-1.20.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:9c0fab855ae790ca74b27e55240fe4f2a36a364a3f1ebcfd1fb5ac4088f1cec3"}, + {file = "numpy-1.20.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:61d5b4cf73622e4d0c6b83408a16631b670fc045afd6540679aa35591a17fe6d"}, + {file = "numpy-1.20.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d15007f857d6995db15195217afdbddfcd203dfaa0ba6878a2f580eaf810ecd6"}, + {file = "numpy-1.20.2-cp37-cp37m-win32.whl", hash = "sha256:d76061ae5cab49b83a8cf3feacefc2053fac672728802ac137dd8c4123397677"}, + {file = "numpy-1.20.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bad70051de2c50b1a6259a6df1daaafe8c480ca98132da98976d8591c412e737"}, + {file = "numpy-1.20.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:719656636c48be22c23641859ff2419b27b6bdf844b36a2447cb39caceb00935"}, + {file = "numpy-1.20.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:aa046527c04688af680217fffac61eec2350ef3f3d7320c07fd33f5c6e7b4d5f"}, + {file = "numpy-1.20.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2428b109306075d89d21135bdd6b785f132a1f5a3260c371cee1fae427e12727"}, + {file = "numpy-1.20.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:e8e4fbbb7e7634f263c5b0150a629342cc19b47c5eba8d1cd4363ab3455ab576"}, + {file = "numpy-1.20.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:edb1f041a9146dcf02cd7df7187db46ab524b9af2515f392f337c7cbbf5b52cd"}, + {file = "numpy-1.20.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:c73a7975d77f15f7f68dacfb2bca3d3f479f158313642e8ea9058eea06637931"}, + {file = "numpy-1.20.2-cp38-cp38-win32.whl", hash = "sha256:6c915ee7dba1071554e70a3664a839fbc033e1d6528199d4621eeaaa5487ccd2"}, + {file = "numpy-1.20.2-cp38-cp38-win_amd64.whl", hash = "sha256:471c0571d0895c68da309dacee4e95a0811d0a9f9f532a48dc1bea5f3b7ad2b7"}, + {file = "numpy-1.20.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4703b9e937df83f5b6b7447ca5912b5f5f297aba45f91dbbbc63ff9278c7aa98"}, + {file = "numpy-1.20.2-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:abc81829c4039e7e4c30f7897938fa5d4916a09c2c7eb9b244b7a35ddc9656f4"}, + {file = "numpy-1.20.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:377751954da04d4a6950191b20539066b4e19e3b559d4695399c5e8e3e683bf6"}, + {file = "numpy-1.20.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:6e51e417d9ae2e7848314994e6fc3832c9d426abce9328cf7571eefceb43e6c9"}, + {file = "numpy-1.20.2-cp39-cp39-win32.whl", hash = "sha256:780ae5284cb770ade51d4b4a7dce4faa554eb1d88a56d0e8b9f35fca9b0270ff"}, + {file = "numpy-1.20.2-cp39-cp39-win_amd64.whl", hash = "sha256:924dc3f83de20437de95a73516f36e09918e9c9c18d5eac520062c49191025fb"}, + {file = "numpy-1.20.2-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:97ce8b8ace7d3b9288d88177e66ee75480fb79b9cf745e91ecfe65d91a856042"}, + {file = "numpy-1.20.2.zip", hash = "sha256:878922bf5ad7550aa044aa9301d417e2d3ae50f0f577de92051d739ac6096cee"}, ] packaging = [ {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, @@ -1797,8 +1799,8 @@ pandocfilters = [ {file = "pandocfilters-1.4.3.tar.gz", hash = "sha256:bc63fbb50534b4b1f8ebe1860889289e8af94a23bff7445259592df25a3906eb"}, ] parso = [ - {file = "parso-0.8.1-py2.py3-none-any.whl", hash = "sha256:15b00182f472319383252c18d5913b69269590616c947747bc50bf4ac768f410"}, - {file = "parso-0.8.1.tar.gz", hash = "sha256:8519430ad07087d4c997fda3a7918f7cfa27cb58972a8c89c2a0295a1c940e9e"}, + {file = "parso-0.8.2-py2.py3-none-any.whl", hash = "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"}, + {file = "parso-0.8.2.tar.gz", hash = "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398"}, ] pathspec = [ {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, @@ -1817,8 +1819,8 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] prompt-toolkit = [ - {file = "prompt_toolkit-3.0.17-py3-none-any.whl", hash = "sha256:4cea7d09e46723885cb8bc54678175453e5071e9449821dce6f017b1d1fbfc1a"}, - {file = "prompt_toolkit-3.0.17.tar.gz", hash = "sha256:9397a7162cf45449147ad6042fa37983a081b8a73363a5253dd4072666333137"}, + {file = "prompt_toolkit-3.0.18-py3-none-any.whl", hash = "sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04"}, + {file = "prompt_toolkit-3.0.18.tar.gz", hash = "sha256:e1b4f11b9336a28fa11810bc623c357420f69dfdb6d2dac41ca2c21a55c033bc"}, ] ptyprocess = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, @@ -1861,8 +1863,8 @@ pydantic = [ {file = "pydantic-1.8.1.tar.gz", hash = "sha256:26cf3cb2e68ec6c0cfcb6293e69fb3450c5fd1ace87f46b64f678b0d29eac4c3"}, ] pyflakes = [ - {file = "pyflakes-2.3.0-py2.py3-none-any.whl", hash = "sha256:910208209dcea632721cb58363d0f72913d9e8cf64dc6f8ae2e02a3609aba40d"}, - {file = "pyflakes-2.3.0.tar.gz", hash = "sha256:e59fd8e750e588358f1b8885e5a4751203a0516e0ee6d34811089ac294c8806f"}, + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] pygments = [ {file = "Pygments-2.8.1-py3-none-any.whl", hash = "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8"}, @@ -1880,8 +1882,8 @@ pyrsistent = [ {file = "pyrsistent-0.17.3.tar.gz", hash = "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"}, ] pytest = [ - {file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"}, - {file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"}, + {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"}, + {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"}, ] pytest-cov = [ {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, @@ -2001,47 +2003,47 @@ recommonmark = [ {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, ] regex = [ - {file = "regex-2021.3.17-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b97ec5d299c10d96617cc851b2e0f81ba5d9d6248413cd374ef7f3a8871ee4a6"}, - {file = "regex-2021.3.17-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:cb4ee827857a5ad9b8ae34d3c8cc51151cb4a3fe082c12ec20ec73e63cc7c6f0"}, - {file = "regex-2021.3.17-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:633497504e2a485a70a3268d4fc403fe3063a50a50eed1039083e9471ad0101c"}, - {file = "regex-2021.3.17-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:a59a2ee329b3de764b21495d78c92ab00b4ea79acef0f7ae8c1067f773570afa"}, - {file = "regex-2021.3.17-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f85d6f41e34f6a2d1607e312820971872944f1661a73d33e1e82d35ea3305e14"}, - {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:4651f839dbde0816798e698626af6a2469eee6d9964824bb5386091255a1694f"}, - {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:39c44532d0e4f1639a89e52355b949573e1e2c5116106a395642cbbae0ff9bcd"}, - {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:3d9a7e215e02bd7646a91fb8bcba30bc55fd42a719d6b35cf80e5bae31d9134e"}, - {file = "regex-2021.3.17-cp36-cp36m-win32.whl", hash = "sha256:159fac1a4731409c830d32913f13f68346d6b8e39650ed5d704a9ce2f9ef9cb3"}, - {file = "regex-2021.3.17-cp36-cp36m-win_amd64.whl", hash = "sha256:13f50969028e81765ed2a1c5fcfdc246c245cf8d47986d5172e82ab1a0c42ee5"}, - {file = "regex-2021.3.17-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b9d8d286c53fe0cbc6d20bf3d583cabcd1499d89034524e3b94c93a5ab85ca90"}, - {file = "regex-2021.3.17-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:201e2619a77b21a7780580ab7b5ce43835e242d3e20fef50f66a8df0542e437f"}, - {file = "regex-2021.3.17-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d47d359545b0ccad29d572ecd52c9da945de7cd6cf9c0cfcb0269f76d3555689"}, - {file = "regex-2021.3.17-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ea2f41445852c660ba7c3ebf7d70b3779b20d9ca8ba54485a17740db49f46932"}, - {file = "regex-2021.3.17-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:486a5f8e11e1f5bbfcad87f7c7745eb14796642323e7e1829a331f87a713daaa"}, - {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:18e25e0afe1cf0f62781a150c1454b2113785401ba285c745acf10c8ca8917df"}, - {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:a2ee026f4156789df8644d23ef423e6194fad0bc53575534101bb1de5d67e8ce"}, - {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:4c0788010a93ace8a174d73e7c6c9d3e6e3b7ad99a453c8ee8c975ddd9965643"}, - {file = "regex-2021.3.17-cp37-cp37m-win32.whl", hash = "sha256:575a832e09d237ae5fedb825a7a5bc6a116090dd57d6417d4f3b75121c73e3be"}, - {file = "regex-2021.3.17-cp37-cp37m-win_amd64.whl", hash = "sha256:8e65e3e4c6feadf6770e2ad89ad3deb524bcb03d8dc679f381d0568c024e0deb"}, - {file = "regex-2021.3.17-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a0df9a0ad2aad49ea3c7f65edd2ffb3d5c59589b85992a6006354f6fb109bb18"}, - {file = "regex-2021.3.17-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b98bc9db003f1079caf07b610377ed1ac2e2c11acc2bea4892e28cc5b509d8d5"}, - {file = "regex-2021.3.17-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:808404898e9a765e4058bf3d7607d0629000e0a14a6782ccbb089296b76fa8fe"}, - {file = "regex-2021.3.17-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:5770a51180d85ea468234bc7987f5597803a4c3d7463e7323322fe4a1b181578"}, - {file = "regex-2021.3.17-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:976a54d44fd043d958a69b18705a910a8376196c6b6ee5f2596ffc11bff4420d"}, - {file = "regex-2021.3.17-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:63f3ca8451e5ff7133ffbec9eda641aeab2001be1a01878990f6c87e3c44b9d5"}, - {file = "regex-2021.3.17-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bcd945175c29a672f13fce13a11893556cd440e37c1b643d6eeab1988c8b209c"}, - {file = "regex-2021.3.17-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:3d9356add82cff75413bec360c1eca3e58db4a9f5dafa1f19650958a81e3249d"}, - {file = "regex-2021.3.17-cp38-cp38-win32.whl", hash = "sha256:f5d0c921c99297354cecc5a416ee4280bd3f20fd81b9fb671ca6be71499c3fdf"}, - {file = "regex-2021.3.17-cp38-cp38-win_amd64.whl", hash = "sha256:14de88eda0976020528efc92d0a1f8830e2fb0de2ae6005a6fc4e062553031fa"}, - {file = "regex-2021.3.17-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4c2e364491406b7888c2ad4428245fc56c327e34a5dfe58fd40df272b3c3dab3"}, - {file = "regex-2021.3.17-cp39-cp39-manylinux1_i686.whl", hash = "sha256:8bd4f91f3fb1c9b1380d6894bd5b4a519409135bec14c0c80151e58394a4e88a"}, - {file = "regex-2021.3.17-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:882f53afe31ef0425b405a3f601c0009b44206ea7f55ee1c606aad3cc213a52c"}, - {file = "regex-2021.3.17-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:07ef35301b4484bce843831e7039a84e19d8d33b3f8b2f9aab86c376813d0139"}, - {file = "regex-2021.3.17-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:360a01b5fa2ad35b3113ae0c07fb544ad180603fa3b1f074f52d98c1096fa15e"}, - {file = "regex-2021.3.17-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:709f65bb2fa9825f09892617d01246002097f8f9b6dde8d1bb4083cf554701ba"}, - {file = "regex-2021.3.17-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:c66221e947d7207457f8b6f42b12f613b09efa9669f65a587a2a71f6a0e4d106"}, - {file = "regex-2021.3.17-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:c782da0e45aff131f0bed6e66fbcfa589ff2862fc719b83a88640daa01a5aff7"}, - {file = "regex-2021.3.17-cp39-cp39-win32.whl", hash = "sha256:dc9963aacb7da5177e40874585d7407c0f93fb9d7518ec58b86e562f633f36cd"}, - {file = "regex-2021.3.17-cp39-cp39-win_amd64.whl", hash = "sha256:a0d04128e005142260de3733591ddf476e4902c0c23c1af237d9acf3c96e1b38"}, - {file = "regex-2021.3.17.tar.gz", hash = "sha256:4b8a1fb724904139149a43e172850f35aa6ea97fb0545244dc0b805e0154ed68"}, + {file = "regex-2021.4.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7"}, + {file = "regex-2021.4.4-cp36-cp36m-win32.whl", hash = "sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29"}, + {file = "regex-2021.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79"}, + {file = "regex-2021.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439"}, + {file = "regex-2021.4.4-cp37-cp37m-win32.whl", hash = "sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d"}, + {file = "regex-2021.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3"}, + {file = "regex-2021.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87"}, + {file = "regex-2021.4.4-cp38-cp38-win32.whl", hash = "sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac"}, + {file = "regex-2021.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2"}, + {file = "regex-2021.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"}, + {file = "regex-2021.4.4-cp39-cp39-win32.whl", hash = "sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6"}, + {file = "regex-2021.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07"}, + {file = "regex-2021.4.4.tar.gz", hash = "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb"}, ] requests = [ {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, @@ -2063,11 +2065,11 @@ rfc3986 = [ {file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"}, ] rpcq = [ - {file = "rpcq-3.7.0.tar.gz", hash = "sha256:d4695048f874aa255764336fa1965f6fc1ea001a31e28681bbbef708cac531e1"}, + {file = "rpcq-3.9.1.tar.gz", hash = "sha256:bd5b9aa8fd14ce68d63306836f02053b674d2fadba05924133e30d12d7b8f651"}, ] "ruamel.yaml" = [ - {file = "ruamel.yaml-0.16.13-py2.py3-none-any.whl", hash = "sha256:64b06e7873eb8e1125525ecef7345447d786368cadca92a7cd9b59eae62e95a3"}, - {file = "ruamel.yaml-0.16.13.tar.gz", hash = "sha256:bb48c514222702878759a05af96f4b7ecdba9b33cd4efcf25c86b882cef3a942"}, + {file = "ruamel.yaml-0.17.4-py3-none-any.whl", hash = "sha256:ac79fb25f5476e8e9ed1c53b8a2286d2c3f5dde49eb37dbcee5c7eb6a8415a22"}, + {file = "ruamel.yaml-0.17.4.tar.gz", hash = "sha256:44bc6b54fddd45e4bc0619059196679f9e8b79c027f4131bb072e6a22f4d5e28"}, ] "ruamel.yaml.clib" = [ {file = "ruamel.yaml.clib-0.2.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:28116f204103cb3a108dfd37668f20abe6e3cafd0d3fd40dba126c732457b3cc"}, @@ -2136,16 +2138,16 @@ snowballstemmer = [ {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] sphinx = [ - {file = "Sphinx-3.5.2-py3-none-any.whl", hash = "sha256:ef64a814576f46ec7de06adf11b433a0d6049be007fefe7fd0d183d28b581fac"}, - {file = "Sphinx-3.5.2.tar.gz", hash = "sha256:672cfcc24b6b69235c97c750cb190a44ecd72696b4452acaf75c2d9cc78ca5ff"}, + {file = "Sphinx-3.5.4-py3-none-any.whl", hash = "sha256:2320d4e994a191f4b4be27da514e46b3d6b420f2ff895d064f52415d342461e8"}, + {file = "Sphinx-3.5.4.tar.gz", hash = "sha256:19010b7b9fa0dc7756a6e105b2aacd3a80f798af3c25c273be64d7beeb482cb1"}, ] sphinx-autodoc-typehints = [ - {file = "sphinx-autodoc-typehints-1.11.1.tar.gz", hash = "sha256:244ba6d3e2fdb854622f643c7763d6f95b6886eba24bec28e86edf205e4ddb20"}, - {file = "sphinx_autodoc_typehints-1.11.1-py3-none-any.whl", hash = "sha256:da049791d719f4c9813642496ee4764203e317f0697eb75446183fa2a68e3f77"}, + {file = "sphinx-autodoc-typehints-1.12.0.tar.gz", hash = "sha256:193617d9dbe0847281b1399d369e74e34cd959c82e02c7efde077fca908a9f52"}, + {file = "sphinx_autodoc_typehints-1.12.0-py3-none-any.whl", hash = "sha256:5e81776ec422dd168d688ab60f034fccfafbcd94329e9537712c93003bddc04a"}, ] sphinx-rtd-theme = [ - {file = "sphinx_rtd_theme-0.5.1-py2.py3-none-any.whl", hash = "sha256:fa6bebd5ab9a73da8e102509a86f3fcc36dec04a0b52ea80e5a033b2aba00113"}, - {file = "sphinx_rtd_theme-0.5.1.tar.gz", hash = "sha256:eda689eda0c7301a80cf122dad28b1861e5605cbf455558f3775e1e8200e83a5"}, + {file = "sphinx_rtd_theme-0.5.2-py2.py3-none-any.whl", hash = "sha256:4a05bdbe8b1446d77a01e20a23ebc6777c74f43237035e76be89699308987d6f"}, + {file = "sphinx_rtd_theme-0.5.2.tar.gz", hash = "sha256:32bd3b5d13dc8186d7a42fc816a23d32e83a4827d7d9882948e7b837c232da5a"}, ] sphinxcontrib-applehelp = [ {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, @@ -2227,36 +2229,36 @@ traitlets = [ {file = "traitlets-5.0.5.tar.gz", hash = "sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396"}, ] typed-ast = [ - {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70"}, - {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487"}, - {file = "typed_ast-1.4.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412"}, - {file = "typed_ast-1.4.2-cp35-cp35m-win32.whl", hash = "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400"}, - {file = "typed_ast-1.4.2-cp35-cp35m-win_amd64.whl", hash = "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606"}, - {file = "typed_ast-1.4.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64"}, - {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07"}, - {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc"}, - {file = "typed_ast-1.4.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a"}, - {file = "typed_ast-1.4.2-cp36-cp36m-win32.whl", hash = "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151"}, - {file = "typed_ast-1.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3"}, - {file = "typed_ast-1.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41"}, - {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f"}, - {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581"}, - {file = "typed_ast-1.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37"}, - {file = "typed_ast-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd"}, - {file = "typed_ast-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496"}, - {file = "typed_ast-1.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc"}, - {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"}, - {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea"}, - {file = "typed_ast-1.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787"}, - {file = "typed_ast-1.4.2-cp38-cp38-win32.whl", hash = "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2"}, - {file = "typed_ast-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937"}, - {file = "typed_ast-1.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1"}, - {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6"}, - {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166"}, - {file = "typed_ast-1.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d"}, - {file = "typed_ast-1.4.2-cp39-cp39-win32.whl", hash = "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b"}, - {file = "typed_ast-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440"}, - {file = "typed_ast-1.4.2.tar.gz", hash = "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, + {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, + {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, + {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, + {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, + {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, + {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, + {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, + {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, + {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, ] typing-extensions = [ {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, diff --git a/pyquil/api/_benchmark.py b/pyquil/api/_benchmark.py index 11626fa97..1b99e14f7 100644 --- a/pyquil/api/_benchmark.py +++ b/pyquil/api/_benchmark.py @@ -35,7 +35,7 @@ class BenchmarkConnection(AbstractBenchmarker): """ @_record_call - def __init__(self, *, timeout: float = 5.0, client_configuration: Optional[QCSClientConfiguration] = None): + def __init__(self, *, timeout: float = 10.0, client_configuration: Optional[QCSClientConfiguration] = None): """ Client to communicate with the benchmarking data endpoint. diff --git a/pyquil/api/_compiler.py b/pyquil/api/_compiler.py index 1e122483b..1c4c13590 100644 --- a/pyquil/api/_compiler.py +++ b/pyquil/api/_compiler.py @@ -83,7 +83,7 @@ def __init__( *, quantum_processor_id: str, quantum_processor: AbstractQuantumProcessor, - timeout: float = 5.0, + timeout: float = 10.0, client_configuration: Optional[QCSClientConfiguration] = None, ) -> None: """ @@ -191,7 +191,7 @@ def __init__( self, *, quantum_processor: AbstractQuantumProcessor, - timeout: float = 5.0, + timeout: float = 10.0, client_configuration: Optional[QCSClientConfiguration] = None, ) -> None: """ diff --git a/pyquil/api/_compiler_client.py b/pyquil/api/_compiler_client.py index 9714ca304..1d8e2bc10 100644 --- a/pyquil/api/_compiler_client.py +++ b/pyquil/api/_compiler_client.py @@ -151,7 +151,7 @@ class CompilerClient: Client for making requests to a Quil compiler. """ - def __init__(self, *, client_configuration: QCSClientConfiguration, request_timeout: float = 5.0) -> None: + def __init__(self, *, client_configuration: QCSClientConfiguration, request_timeout: float = 10.0) -> None: """ Instantiate a new compiler client. diff --git a/pyquil/api/_engagement_manager.py b/pyquil/api/_engagement_manager.py index 8d8736ebc..1d8032230 100644 --- a/pyquil/api/_engagement_manager.py +++ b/pyquil/api/_engagement_manager.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################################################## +import threading from datetime import datetime from typing import Dict, Optional @@ -31,6 +32,8 @@ class EngagementManager: Fetches (and caches) engagements for use when accessing a QPU. """ + _lock = threading.Lock() + def __init__(self, *, client_configuration: QCSClientConfiguration) -> None: """ Instantiate a new engagement manager. @@ -40,7 +43,7 @@ def __init__(self, *, client_configuration: QCSClientConfiguration) -> None: self._client_configuration = client_configuration self._cached_engagements: Dict[str, EngagementWithCredentials] = {} - def get_engagement(self, *, quantum_processor_id: str, request_timeout: float = 5.0) -> EngagementWithCredentials: + def get_engagement(self, *, quantum_processor_id: str, request_timeout: float = 10.0) -> EngagementWithCredentials: """ Gets an engagement for the given quantum processor. If an engagement was already fetched previously and remains valid, it will be returned instead of creating a new engagement. @@ -49,16 +52,19 @@ def get_engagement(self, *, quantum_processor_id: str, request_timeout: float = :param request_timeout: Timeout for request, in seconds. :return: Fetched or cached engagement. """ - if not self._engagement_valid(self._cached_engagements.get(quantum_processor_id)): - with qcs_client( - client_configuration=self._client_configuration, request_timeout=request_timeout - ) as client: # type: httpx.Client - request = CreateEngagementRequest(quantum_processor_id=quantum_processor_id) - self._cached_engagements[quantum_processor_id] = create_engagement( - client=client, json_body=request - ).parsed - - return self._cached_engagements[quantum_processor_id] + EngagementManager._lock.acquire() + try: + if not self._engagement_valid(self._cached_engagements.get(quantum_processor_id)): + with qcs_client( + client_configuration=self._client_configuration, request_timeout=request_timeout + ) as client: # type: httpx.Client + request = CreateEngagementRequest(quantum_processor_id=quantum_processor_id) + self._cached_engagements[quantum_processor_id] = create_engagement( + client=client, json_body=request + ).parsed + return self._cached_engagements[quantum_processor_id] + finally: + EngagementManager._lock.release() @staticmethod def _engagement_valid(engagement: Optional[EngagementWithCredentials]) -> bool: diff --git a/pyquil/api/_qcs_client.py b/pyquil/api/_qcs_client.py index 779b4ef32..98510a192 100644 --- a/pyquil/api/_qcs_client.py +++ b/pyquil/api/_qcs_client.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################################################## +import asyncio from contextlib import contextmanager from typing import Iterator @@ -21,14 +22,29 @@ @contextmanager -def qcs_client(*, client_configuration: QCSClientConfiguration, request_timeout: float = 5.0) -> Iterator[httpx.Client]: +def qcs_client( + *, + client_configuration: QCSClientConfiguration, + request_timeout: float = 10.0, +) -> Iterator[httpx.Client]: """ Build a QCS client. :param client_configuration: Client configuration. :param request_timeout: Time limit for requests, in seconds. """ + _ensure_event_loop() with build_sync_client( configuration=client_configuration, client_kwargs={"timeout": request_timeout} ) as client: # type: httpx.Client yield client + + +def _ensure_event_loop() -> None: + try: + asyncio.get_event_loop() + except RuntimeError as ex: + if len(ex.args) > 0 and "There is no current event loop in thread" in ex.args[0]: + asyncio.set_event_loop(asyncio.new_event_loop()) + else: + raise diff --git a/pyquil/api/_qpu.py b/pyquil/api/_qpu.py index 650f2ea73..a41697150 100644 --- a/pyquil/api/_qpu.py +++ b/pyquil/api/_qpu.py @@ -106,7 +106,7 @@ def __init__( *, quantum_processor_id: str, priority: int = 1, - timeout: float = 5.0, + timeout: float = 10.0, client_configuration: Optional[QCSClientConfiguration] = None, engagement_manager: Optional[EngagementManager] = None, ) -> None: diff --git a/pyquil/api/_qpu_client.py b/pyquil/api/_qpu_client.py index 021dc4549..c4d56864e 100644 --- a/pyquil/api/_qpu_client.py +++ b/pyquil/api/_qpu_client.py @@ -100,7 +100,11 @@ class QPUClient: """ def __init__( - self, *, quantum_processor_id: str, engagement_manager: EngagementManager, request_timeout: float = 5.0 + self, + *, + quantum_processor_id: str, + engagement_manager: EngagementManager, + request_timeout: float = 10.0, ) -> None: """ Instantiate a new QPU client, authenticated using the given engagement. diff --git a/pyquil/api/_quantum_computer.py b/pyquil/api/_quantum_computer.py index 5479217f3..f1bf8023b 100644 --- a/pyquil/api/_quantum_computer.py +++ b/pyquil/api/_quantum_computer.py @@ -504,7 +504,7 @@ def __repr__(self) -> str: def list_quantum_computers( qpus: bool = True, qvms: bool = True, - timeout: float = 5.0, + timeout: float = 10.0, client_configuration: Optional[QCSClientConfiguration] = None, ) -> List[str]: """ @@ -793,7 +793,7 @@ def get_qc( *, as_qvm: Optional[bool] = None, noisy: Optional[bool] = None, - timeout: float = 5.0, + timeout: float = 10.0, client_configuration: Optional[QCSClientConfiguration] = None, engagement_manager: Optional[EngagementManager] = None, ) -> QuantumComputer: diff --git a/pyquil/api/_qvm.py b/pyquil/api/_qvm.py index 08a47b5da..96fadf1b0 100644 --- a/pyquil/api/_qvm.py +++ b/pyquil/api/_qvm.py @@ -74,7 +74,7 @@ def __init__( gate_noise: Optional[Tuple[float, float, float]] = None, measurement_noise: Optional[Tuple[float, float, float]] = None, random_seed: Optional[int] = None, - timeout: float = 5.0, + timeout: float = 10.0, client_configuration: Optional[QCSClientConfiguration] = None, ): """ @@ -389,7 +389,7 @@ def __init__( gate_noise: Optional[Tuple[float, float, float]] = None, measurement_noise: Optional[Tuple[float, float, float]] = None, random_seed: Optional[int] = None, - timeout: float = 5.0, + timeout: float = 10.0, client_configuration: Optional[QCSClientConfiguration] = None, ) -> None: """ diff --git a/pyquil/api/_qvm_client.py b/pyquil/api/_qvm_client.py index ffffe654c..89384e2a4 100644 --- a/pyquil/api/_qvm_client.py +++ b/pyquil/api/_qvm_client.py @@ -159,7 +159,7 @@ class QVMClient: Client for making requests to a Quantum Virtual Machine. """ - def __init__(self, *, client_configuration: QCSClientConfiguration, request_timeout: float = 5.0) -> None: + def __init__(self, *, client_configuration: QCSClientConfiguration, request_timeout: float = 10.0) -> None: """ Instantiate a new compiler client. diff --git a/pyquil/api/_wavefunction_simulator.py b/pyquil/api/_wavefunction_simulator.py index 68c263b08..62d68a7f5 100644 --- a/pyquil/api/_wavefunction_simulator.py +++ b/pyquil/api/_wavefunction_simulator.py @@ -43,7 +43,7 @@ def __init__( self, *, random_seed: Optional[int] = None, - timeout: float = 5.0, + timeout: float = 10.0, client_configuration: Optional[QCSClientConfiguration] = None, ) -> None: """ diff --git a/pyquil/quantum_processor/qcs.py b/pyquil/quantum_processor/qcs.py index ba4ec883f..9690d0940 100644 --- a/pyquil/quantum_processor/qcs.py +++ b/pyquil/quantum_processor/qcs.py @@ -59,7 +59,9 @@ def __repr__(self) -> str: def get_qcs_quantum_processor( - quantum_processor_id: str, client_configuration: QCSClientConfiguration, timeout: float = 5.0 + quantum_processor_id: str, + client_configuration: QCSClientConfiguration, + timeout: float = 10.0, ) -> QCSQuantumProcessor: with qcs_client(client_configuration=client_configuration, request_timeout=timeout) as client: # type: httpx.Client isa = get_instruction_set_architecture(client=client, quantum_processor_id=quantum_processor_id).parsed diff --git a/scripts/ci_install_deps b/scripts/ci_install_deps index 8672c5ac0..aeb249d98 100755 --- a/scripts/ci_install_deps +++ b/scripts/ci_install_deps @@ -4,6 +4,5 @@ curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poet . $HOME/.poetry/env poetry --version poetry config virtualenvs.in-project true -poetry config experimental.new-installer false # Address flaky poetry installs, see: https://github.com/python-poetry/poetry/issues/3199 poetry run python -m ensurepip --upgrade poetry install -vv diff --git a/test/e2e/conftest.py b/test/e2e/conftest.py new file mode 100644 index 000000000..7bb6532bd --- /dev/null +++ b/test/e2e/conftest.py @@ -0,0 +1,26 @@ +import os + +import pytest +from qcs_api_client.client import QCSClientConfiguration + +from pyquil.api import EngagementManager + + +@pytest.fixture() +def quantum_processor_id() -> str: + quantum_processor_id = os.environ.get("TEST_QUANTUM_PROCESSOR") + + if quantum_processor_id is None: + raise Exception("'TEST_QUANTUM_PROCESSOR' env var required for e2e tests.") + + return quantum_processor_id + + +@pytest.fixture() +def client_configuration() -> QCSClientConfiguration: + return QCSClientConfiguration.load() + + +@pytest.fixture() +def engagement_manager(client_configuration: QCSClientConfiguration) -> EngagementManager: + return EngagementManager(client_configuration=client_configuration) diff --git a/test/e2e/test_e2e.py b/test/e2e/test_e2e.py index b13ed8b84..bdfa36cae 100644 --- a/test/e2e/test_e2e.py +++ b/test/e2e/test_e2e.py @@ -13,35 +13,66 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################################################## -import pytest -import os +from multiprocessing.pool import Pool, ThreadPool + +from qcs_api_client.client import QCSClientConfiguration from pyquil import get_qc, Program -from pyquil.api import QuantumComputer, QPU +from pyquil.api import EngagementManager from pyquil.gates import H, CNOT, MEASURE from pyquil.quilbase import Declare +TEST_PROGRAM = Program( + Declare("ro", "BIT", 2), + H(0), + CNOT(0, 1), + MEASURE(0, ("ro", 0)), + MEASURE(1, ("ro", 1)), +).wrap_in_numshots_loop(1000) + + +def test_basic_program(quantum_processor_id: str, client_configuration: QCSClientConfiguration): + qc = get_qc(quantum_processor_id, client_configuration=client_configuration) + + results = qc.run(qc.compile(TEST_PROGRAM)) -@pytest.fixture() -def qc() -> QuantumComputer: - quantum_processor_id = os.environ.get("TEST_QUANTUM_PROCESSOR") - if quantum_processor_id is None: - raise Exception("'TEST_QUANTUM_PROCESSOR' env var required for e2e tests.") - - computer = get_qc(quantum_processor_id) - if not isinstance(computer.qam, QPU): - raise ValueError("'TARGET_QUANTUM_PROCESSOR' should name a QPU. Ensure it is not a QVM.") - return computer - - -def test_basic_program(qc: QuantumComputer): - program = Program( - Declare("ro", "BIT", 2), - H(0), - CNOT(0, 1), - MEASURE(0, ("ro", 0)), - MEASURE(1, ("ro", 1)), - ).wrap_in_numshots_loop(1000) - compiled = qc.compile(program) - results = qc.run(compiled) assert results.shape == (1000, 2) + + +def test_multithreading( + quantum_processor_id: str, client_configuration: QCSClientConfiguration, engagement_manager: EngagementManager +): + args = [(TEST_PROGRAM, quantum_processor_id, client_configuration, engagement_manager) for _ in range(20)] + with ThreadPool(10) as pool: + results = pool.starmap(run_program, args) + + assert len(results) == 20 + for result in results: + assert result.shape == (1000, 2) + + +def test_multiprocessing( + quantum_processor_id: str, client_configuration: QCSClientConfiguration, engagement_manager: EngagementManager +): + args = [(TEST_PROGRAM, quantum_processor_id, client_configuration, engagement_manager) for _ in range(20)] + with Pool(10) as pool: + results = pool.starmap(run_program, args) + + assert len(results) == 20 + for result in results: + assert result.shape == (1000, 2) + + +# NOTE: This must be outside of the test function, or multiprocessing complains that it can't be pickled +def run_program( + program: Program, + quantum_processor_id: str, + client_configuration: QCSClientConfiguration, + engagement_manager: EngagementManager, +): + qc = get_qc( + quantum_processor_id, + client_configuration=client_configuration, + engagement_manager=engagement_manager, + ) + return qc.run(qc.compile(program)) diff --git a/test/unit/utils.py b/test/unit/utils.py index 0f81805ab..ddccb2d91 100644 --- a/test/unit/utils.py +++ b/test/unit/utils.py @@ -3,9 +3,11 @@ import signal from contextlib import contextmanager from multiprocessing import Process +from socket import socket import rpcq from qcs_api_client.client import QCSClientConfiguration +from retry import retry from pyquil import Program from pyquil.api._abstract_compiler import AbstractCompiler @@ -25,9 +27,18 @@ def run_rpcq_server(server: rpcq.Server, port: int): def run_server(): server.run(endpoint=f"tcp://*:{port}", loop=asyncio.new_event_loop()) + @retry(tries=10, delay=0.25) + def check_connection(): + s = socket() + try: + s.connect(("localhost", port)) + finally: + s.close() + proc = Process(target=run_server) try: proc.start() + check_connection() yield finally: os.kill(proc.pid, signal.SIGINT)