diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2950dcf7..07e22244 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @samwelkanda @jobala @ddyett @MichaelMainer @shemogumbe +@samwelkanda @baywet @darrelmiller @zengin @MichaelMainer @ddyett @shemogumbe diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..798aaf50 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: pip + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5adb1fae..e2938bd6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,14 +5,14 @@ name: CI on: pull_request: - branches: [master, dev] + branches: [kiota/long-term-branch] jobs: build: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8] + python-version: ['3.6', '3.7', '3.8', '3.9', '3.10'] steps: - uses: actions/checkout@v2 @@ -33,7 +33,7 @@ jobs: pipenv run isort . - name: Lint with Pylint run: | - pipenv run pylint msgraph --disable=W --rcfile=.pylintrc + pipenv run pylint src --disable=W --rcfile=.pylintrc - name: Test with pytest run: | pipenv run pytest diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..89c79e45 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,69 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: ["kiota/long-term-branch"] + pull_request: + # The branches below must be a subset of the branches above + branches: ["kiota/long-term-branch"] + schedule: + - cron: "32 11 * * 6" + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ["python"] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5a800ab9..bcdbbe8f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,18 +11,19 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 - - name: Set up Python 3.8 - uses: actions/setup-python@v1 + uses: actions/checkout@v3 + - name: Set up Python 3.9 + uses: actions/setup-python@v3 with: - python-version: 3.8 - - name: Install flit + python-version: 3.9 + - name: Install dependencies run: | - pip install flit - - name: Publish the distibution to PyPI - if: github.repository == 'microsoftgraph/msgraph-sdk-python-core' - run: flit publish - env: - FLIT_INDEX_URL: https://upload.pypi.org/legacy/ - FLIT_USERNAME: __token__ - FLIT_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + python -m pip install --upgrade pip + pip install build + - name: Build package + run: python -m build + - name: Publish package + uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index b3b28cbc..00000000 --- a/.isort.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[settings] -profile=hug diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 389a73a3..00000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,32 +0,0 @@ -default_language_version: - python: python3 -repos: - - repo: local - hooks: - - id: yapf - name: yapf - stages: [commit] - language: system - entry: pipenv run yapf -ir . - types: [python] - - - id: isort - name: isort - stages: [commit] - language: system - entry: pipenv run isort . - types: [python] - - - id: pylint - name: pylint - stages: [commit] - language: system - entry: pipenv run pylint msgraph --disable=W --rcfile=.pylintrc - types: [python] - - - id: pytest - name: pytest - stages: [commit] - language: system - entry: pipenv run pytest tests - types: [python] diff --git a/.pylintrc b/.pylintrc index ef8b4192..99ccac38 100644 --- a/.pylintrc +++ b/.pylintrc @@ -60,15 +60,9 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". -disable=print-statement, - parameter-unpacking, - unpacking-in-except, - old-raise-syntax, - backtick, - long-suffix, +disable=long-suffix, old-ne-operator, old-octal-literal, - import-star-module-level, non-ascii-bytes-literal, raw-checker-failed, bad-inline-option, @@ -78,74 +72,16 @@ disable=print-statement, useless-suppression, deprecated-pragma, use-symbolic-message-instead, - apply-builtin, - basestring-builtin, - buffer-builtin, - cmp-builtin, - coerce-builtin, - execfile-builtin, - file-builtin, - long-builtin, - raw_input-builtin, - reduce-builtin, - standarderror-builtin, - unicode-builtin, - xrange-builtin, - coerce-method, - delslice-method, - getslice-method, - setslice-method, - no-absolute-import, - old-division, - dict-iter-method, - dict-view-method, - next-method-called, - metaclass-assignment, - indexing-exception, - raising-string, - reload-builtin, - oct-method, - hex-method, - nonzero-method, - cmp-method, - input-builtin, - round-builtin, - intern-builtin, - unichr-builtin, - map-builtin-not-iterating, - zip-builtin-not-iterating, - range-builtin-not-iterating, - filter-builtin-not-iterating, - using-cmp-argument, eq-without-hash, - div-method, - idiv-method, - rdiv-method, - exception-message-attribute, - invalid-str-codec, - sys-max-int, - bad-python3-import, - deprecated-string-function, - deprecated-str-translate-call, - deprecated-itertools-function, - deprecated-types-field, - next-method-defined, - dict-items-not-iterating, - dict-keys-not-iterating, - dict-values-not-iterating, - deprecated-operator-function, - deprecated-urllib-function, - xreadlines-attribute, - deprecated-sys-function, - exception-escape, - comprehension-escape, too-few-public-methods, - no-self-use, missing-module-docstring, missing-class-docstring, missing-function-docstring, - C0330, + C0103, R0801, + R0904, + R0911, + # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option @@ -321,13 +257,6 @@ max-line-length=100 # Maximum number of lines in a module. max-module-lines=1000 -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma, - dict-separator - # Allow the body of a class to be on the same line as the declaration if body # contains single statement. single-line-class-stmt=no @@ -585,4 +514,4 @@ preferred-modules= # Exceptions that will emit a warning when being caught. Defaults to # "BaseException, Exception". overgeneral-exceptions=BaseException, - Exception + Exception \ No newline at end of file diff --git a/.style.yapf b/.style.yapf deleted file mode 100644 index f4b72322..00000000 --- a/.style.yapf +++ /dev/null @@ -1,5 +0,0 @@ -[style] -based_on_style = pep8 -dedent_closing_brackets = true -each_dict_entry_on_separate_line = true -column_limit = 100 \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index fe912239..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "python.linting.enabled": true, - "python.linting.pylintPath": "pylint", - "editor.formatOnSave": true, - "python.formatting.provider": "yapf", - "python.linting.pylintEnabled": true, -} \ No newline at end of file diff --git a/CODE_OF_CONDUCT.rst b/CODE_OF_CONDUCT.rst index a0ec06fd..6257f2e7 100644 --- a/CODE_OF_CONDUCT.rst +++ b/CODE_OF_CONDUCT.rst @@ -1,4 +1,9 @@ -This project has adopted the `Microsoft Open Source Code of Conduct `_. For -more information see the `Code of Conduct FAQ `_ or contact opencode@microsoft.com -with any additional questions or comments. +# Microsoft Open Source Code of Conduct +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +Resources: + +- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) +- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns \ No newline at end of file diff --git a/Pipfile b/Pipfile index 11fa7477..7a773809 100644 --- a/Pipfile +++ b/Pipfile @@ -4,16 +4,25 @@ verify_ssl = true name = "pypi" [packages] # Packages required to run the application -requests = "==2.26.0" +microsoft-kiota-abstractions = "==0.1.0" +microsoft-kiota-http = "==0.2.0" +microsoft-kiota-authentication-azure = "==0.1.0" +httpx = {version = "==0.23.0", extras = ["http2"]} [dev-packages] # Packages required to develop the application -coverage = "==5.0.3" -responses = "==0.10.12" -flit = "==2.2.0" -isort = "==5.7.0" -yapf = "==0.30.0" -mypy = "==0.800" -pylint = "==2.7.4" -pytest = "==6.2.4" -pre-commit = "==2.13.0" +setuptools = "==65.5.1" +build = "==0.9.0" +bumpver = "==2022.1119" +pylint = "==2.15.4" +mypy = "==0.982" +yapf = "==0.32.0" +isort = "==5.10.1" +pytest = "==7.1.3" +pytest-cov = "==4.0.0" +types-python-dateutil = "*" +toml = "==0.10.2" +pytest-trio = "==0.7.0" +pytest-mock = "==3.10.0" +asyncmock = "==0.4.2" azure-identity = "*" +trio = "==0.22.0" diff --git a/Pipfile.lock b/Pipfile.lock index 2eaba360..b59c7ee6 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "770cea0cbfb09351eaa005ec03e5d7a25acc5ba4580707e4fb67547ef4e5aafa" + "sha256": "4ce7b4543429c9a59630b86da24aa3331172932bad9a6d2e8664bd995f87da07" }, "pipfile-spec": 6, "requires": {}, @@ -14,282 +14,786 @@ ] }, "default": { + "aiohttp": { + "hashes": [ + "sha256:01d7bdb774a9acc838e6b8f1d114f45303841b89b95984cbb7d80ea41172a9e3", + "sha256:03a6d5349c9ee8f79ab3ff3694d6ce1cfc3ced1c9d36200cb8f08ba06bd3b782", + "sha256:04d48b8ce6ab3cf2097b1855e1505181bdd05586ca275f2505514a6e274e8e75", + "sha256:0770e2806a30e744b4e21c9d73b7bee18a1cfa3c47991ee2e5a65b887c49d5cf", + "sha256:07b05cd3305e8a73112103c834e91cd27ce5b4bd07850c4b4dbd1877d3f45be7", + "sha256:086f92daf51a032d062ec5f58af5ca6a44d082c35299c96376a41cbb33034675", + "sha256:099ebd2c37ac74cce10a3527d2b49af80243e2a4fa39e7bce41617fbc35fa3c1", + "sha256:0c7ebbbde809ff4e970824b2b6cb7e4222be6b95a296e46c03cf050878fc1785", + "sha256:102e487eeb82afac440581e5d7f8f44560b36cf0bdd11abc51a46c1cd88914d4", + "sha256:11691cf4dc5b94236ccc609b70fec991234e7ef8d4c02dd0c9668d1e486f5abf", + "sha256:11a67c0d562e07067c4e86bffc1553f2cf5b664d6111c894671b2b8712f3aba5", + "sha256:12de6add4038df8f72fac606dff775791a60f113a725c960f2bab01d8b8e6b15", + "sha256:13487abd2f761d4be7c8ff9080de2671e53fff69711d46de703c310c4c9317ca", + "sha256:15b09b06dae900777833fe7fc4b4aa426556ce95847a3e8d7548e2d19e34edb8", + "sha256:1c182cb873bc91b411e184dab7a2b664d4fea2743df0e4d57402f7f3fa644bac", + "sha256:1ed0b6477896559f17b9eaeb6d38e07f7f9ffe40b9f0f9627ae8b9926ae260a8", + "sha256:28d490af82bc6b7ce53ff31337a18a10498303fe66f701ab65ef27e143c3b0ef", + "sha256:2e5d962cf7e1d426aa0e528a7e198658cdc8aa4fe87f781d039ad75dcd52c516", + "sha256:2ed076098b171573161eb146afcb9129b5ff63308960aeca4b676d9d3c35e700", + "sha256:2f2f69dca064926e79997f45b2f34e202b320fd3782f17a91941f7eb85502ee2", + "sha256:31560d268ff62143e92423ef183680b9829b1b482c011713ae941997921eebc8", + "sha256:31d1e1c0dbf19ebccbfd62eff461518dcb1e307b195e93bba60c965a4dcf1ba0", + "sha256:37951ad2f4a6df6506750a23f7cbabad24c73c65f23f72e95897bb2cecbae676", + "sha256:3af642b43ce56c24d063325dd2cf20ee012d2b9ba4c3c008755a301aaea720ad", + "sha256:44db35a9e15d6fe5c40d74952e803b1d96e964f683b5a78c3cc64eb177878155", + "sha256:473d93d4450880fe278696549f2e7aed8cd23708c3c1997981464475f32137db", + "sha256:477c3ea0ba410b2b56b7efb072c36fa91b1e6fc331761798fa3f28bb224830dd", + "sha256:4a4a4e30bf1edcad13fb0804300557aedd07a92cabc74382fdd0ba6ca2661091", + "sha256:4aed991a28ea3ce320dc8ce655875e1e00a11bdd29fe9444dd4f88c30d558602", + "sha256:51467000f3647d519272392f484126aa716f747859794ac9924a7aafa86cd411", + "sha256:55c3d1072704d27401c92339144d199d9de7b52627f724a949fc7d5fc56d8b93", + "sha256:589c72667a5febd36f1315aa6e5f56dd4aa4862df295cb51c769d16142ddd7cd", + "sha256:5bfde62d1d2641a1f5173b8c8c2d96ceb4854f54a44c23102e2ccc7e02f003ec", + "sha256:5c23b1ad869653bc818e972b7a3a79852d0e494e9ab7e1a701a3decc49c20d51", + "sha256:61bfc23df345d8c9716d03717c2ed5e27374e0fe6f659ea64edcd27b4b044cf7", + "sha256:6ae828d3a003f03ae31915c31fa684b9890ea44c9c989056fea96e3d12a9fa17", + "sha256:6c7cefb4b0640703eb1069835c02486669312bf2f12b48a748e0a7756d0de33d", + "sha256:6d69f36d445c45cda7b3b26afef2fc34ef5ac0cdc75584a87ef307ee3c8c6d00", + "sha256:6f0d5f33feb5f69ddd57a4a4bd3d56c719a141080b445cbf18f238973c5c9923", + "sha256:6f8b01295e26c68b3a1b90efb7a89029110d3a4139270b24fda961893216c440", + "sha256:713ac174a629d39b7c6a3aa757b337599798da4c1157114a314e4e391cd28e32", + "sha256:718626a174e7e467f0558954f94af117b7d4695d48eb980146016afa4b580b2e", + "sha256:7187a76598bdb895af0adbd2fb7474d7f6025d170bc0a1130242da817ce9e7d1", + "sha256:71927042ed6365a09a98a6377501af5c9f0a4d38083652bcd2281a06a5976724", + "sha256:7d08744e9bae2ca9c382581f7dce1273fe3c9bae94ff572c3626e8da5b193c6a", + "sha256:7dadf3c307b31e0e61689cbf9e06be7a867c563d5a63ce9dca578f956609abf8", + "sha256:81e3d8c34c623ca4e36c46524a3530e99c0bc95ed068fd6e9b55cb721d408fb2", + "sha256:844a9b460871ee0a0b0b68a64890dae9c415e513db0f4a7e3cab41a0f2fedf33", + "sha256:8b7ef7cbd4fec9a1e811a5de813311ed4f7ac7d93e0fda233c9b3e1428f7dd7b", + "sha256:97ef77eb6b044134c0b3a96e16abcb05ecce892965a2124c566af0fd60f717e2", + "sha256:99b5eeae8e019e7aad8af8bb314fb908dd2e028b3cdaad87ec05095394cce632", + "sha256:a25fa703a527158aaf10dafd956f7d42ac6d30ec80e9a70846253dd13e2f067b", + "sha256:a2f635ce61a89c5732537a7896b6319a8fcfa23ba09bec36e1b1ac0ab31270d2", + "sha256:a79004bb58748f31ae1cbe9fa891054baaa46fb106c2dc7af9f8e3304dc30316", + "sha256:a996d01ca39b8dfe77440f3cd600825d05841088fd6bc0144cc6c2ec14cc5f74", + "sha256:b0e20cddbd676ab8a64c774fefa0ad787cc506afd844de95da56060348021e96", + "sha256:b6613280ccedf24354406caf785db748bebbddcf31408b20c0b48cb86af76866", + "sha256:b9d00268fcb9f66fbcc7cd9fe423741d90c75ee029a1d15c09b22d23253c0a44", + "sha256:bb01ba6b0d3f6c68b89fce7305080145d4877ad3acaed424bae4d4ee75faa950", + "sha256:c2aef4703f1f2ddc6df17519885dbfa3514929149d3ff900b73f45998f2532fa", + "sha256:c34dc4958b232ef6188c4318cb7b2c2d80521c9a56c52449f8f93ab7bc2a8a1c", + "sha256:c3630c3ef435c0a7c549ba170a0633a56e92629aeed0e707fec832dee313fb7a", + "sha256:c3d6a4d0619e09dcd61021debf7059955c2004fa29f48788a3dfaf9c9901a7cd", + "sha256:d15367ce87c8e9e09b0f989bfd72dc641bcd04ba091c68cd305312d00962addd", + "sha256:d2f9b69293c33aaa53d923032fe227feac867f81682f002ce33ffae978f0a9a9", + "sha256:e999f2d0e12eea01caeecb17b653f3713d758f6dcc770417cf29ef08d3931421", + "sha256:ea302f34477fda3f85560a06d9ebdc7fa41e82420e892fc50b577e35fc6a50b2", + "sha256:eaba923151d9deea315be1f3e2b31cc39a6d1d2f682f942905951f4e40200922", + "sha256:ef9612483cb35171d51d9173647eed5d0069eaa2ee812793a75373447d487aa4", + "sha256:f5315a2eb0239185af1bddb1abf472d877fede3cc8d143c6cddad37678293237", + "sha256:fa0ffcace9b3aa34d205d8130f7873fcfefcb6a4dd3dd705b0dab69af6712642", + "sha256:fc5471e1a54de15ef71c1bc6ebe80d4dc681ea600e68bfd1cbce40427f0b7578" + ], + "markers": "python_version >= '3.6'", + "version": "==3.8.1" + }, + "aiosignal": { + "hashes": [ + "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc", + "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17" + ], + "markers": "python_version >= '3.7'", + "version": "==1.3.1" + }, + "anyio": { + "hashes": [ + "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421", + "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3" + ], + "markers": "python_full_version >= '3.6.2'", + "version": "==3.6.2" + }, + "async-timeout": { + "hashes": [ + "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15", + "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c" + ], + "markers": "python_version >= '3.6'", + "version": "==4.0.2" + }, + "attrs": { + "hashes": [ + "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6", + "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c" + ], + "markers": "python_version >= '3.5'", + "version": "==22.1.0" + }, + "azure-core": { + "hashes": [ + "sha256:3d70e9ec64de92dfae330c15bc69085caceb2d83813ef6c01cc45326f2a4be83", + "sha256:88d2db5cf9a135a7287dc45fdde6b96f9ca62c9567512a3bb3e20e322ce7deb2" + ], + "version": "==1.21.1" + }, "certifi": { "hashes": [ - "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee", - "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8" + "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14", + "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382" ], - "version": "==2021.5.30" + "markers": "python_version >= '3.6'", + "version": "==2022.9.24" }, "charset-normalizer": { "hashes": [ - "sha256:3c502a35807c9df35697b0f44b1d65008f83071ff29c69677c7c22573bc5a45a", - "sha256:951567c2f7433a70ab63f1be67e5ee05d3925d9423306ecb71a3b272757bcc95" + "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845", + "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" + ], + "markers": "python_full_version >= '3.6.0'", + "version": "==2.1.1" + }, + "frozenlist": { + "hashes": [ + "sha256:008a054b75d77c995ea26629ab3a0c0d7281341f2fa7e1e85fa6153ae29ae99c", + "sha256:02c9ac843e3390826a265e331105efeab489ffaf4dd86384595ee8ce6d35ae7f", + "sha256:034a5c08d36649591be1cbb10e09da9f531034acfe29275fc5454a3b101ce41a", + "sha256:05cdb16d09a0832eedf770cb7bd1fe57d8cf4eaf5aced29c4e41e3f20b30a784", + "sha256:0693c609e9742c66ba4870bcee1ad5ff35462d5ffec18710b4ac89337ff16e27", + "sha256:0771aed7f596c7d73444c847a1c16288937ef988dc04fb9f7be4b2aa91db609d", + "sha256:0af2e7c87d35b38732e810befb9d797a99279cbb85374d42ea61c1e9d23094b3", + "sha256:14143ae966a6229350021384870458e4777d1eae4c28d1a7aa47f24d030e6678", + "sha256:180c00c66bde6146a860cbb81b54ee0df350d2daf13ca85b275123bbf85de18a", + "sha256:1841e200fdafc3d51f974d9d377c079a0694a8f06de2e67b48150328d66d5483", + "sha256:23d16d9f477bb55b6154654e0e74557040575d9d19fe78a161bd33d7d76808e8", + "sha256:2b07ae0c1edaa0a36339ec6cce700f51b14a3fc6545fdd32930d2c83917332cf", + "sha256:2c926450857408e42f0bbc295e84395722ce74bae69a3b2aa2a65fe22cb14b99", + "sha256:2e24900aa13212e75e5b366cb9065e78bbf3893d4baab6052d1aca10d46d944c", + "sha256:303e04d422e9b911a09ad499b0368dc551e8c3cd15293c99160c7f1f07b59a48", + "sha256:352bd4c8c72d508778cf05ab491f6ef36149f4d0cb3c56b1b4302852255d05d5", + "sha256:3843f84a6c465a36559161e6c59dce2f2ac10943040c2fd021cfb70d58c4ad56", + "sha256:394c9c242113bfb4b9aa36e2b80a05ffa163a30691c7b5a29eba82e937895d5e", + "sha256:3bbdf44855ed8f0fbcd102ef05ec3012d6a4fd7c7562403f76ce6a52aeffb2b1", + "sha256:40de71985e9042ca00b7953c4f41eabc3dc514a2d1ff534027f091bc74416401", + "sha256:41fe21dc74ad3a779c3d73a2786bdf622ea81234bdd4faf90b8b03cad0c2c0b4", + "sha256:47df36a9fe24054b950bbc2db630d508cca3aa27ed0566c0baf661225e52c18e", + "sha256:4ea42116ceb6bb16dbb7d526e242cb6747b08b7710d9782aa3d6732bd8d27649", + "sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a", + "sha256:5c11e43016b9024240212d2a65043b70ed8dfd3b52678a1271972702d990ac6d", + "sha256:5cf820485f1b4c91e0417ea0afd41ce5cf5965011b3c22c400f6d144296ccbc0", + "sha256:5d8860749e813a6f65bad8285a0520607c9500caa23fea6ee407e63debcdbef6", + "sha256:6327eb8e419f7d9c38f333cde41b9ae348bec26d840927332f17e887a8dcb70d", + "sha256:65a5e4d3aa679610ac6e3569e865425b23b372277f89b5ef06cf2cdaf1ebf22b", + "sha256:66080ec69883597e4d026f2f71a231a1ee9887835902dbe6b6467d5a89216cf6", + "sha256:783263a4eaad7c49983fe4b2e7b53fa9770c136c270d2d4bbb6d2192bf4d9caf", + "sha256:7f44e24fa70f6fbc74aeec3e971f60a14dde85da364aa87f15d1be94ae75aeef", + "sha256:7fdfc24dcfce5b48109867c13b4cb15e4660e7bd7661741a391f821f23dfdca7", + "sha256:810860bb4bdce7557bc0febb84bbd88198b9dbc2022d8eebe5b3590b2ad6c842", + "sha256:841ea19b43d438a80b4de62ac6ab21cfe6827bb8a9dc62b896acc88eaf9cecba", + "sha256:84610c1502b2461255b4c9b7d5e9c48052601a8957cd0aea6ec7a7a1e1fb9420", + "sha256:899c5e1928eec13fd6f6d8dc51be23f0d09c5281e40d9cf4273d188d9feeaf9b", + "sha256:8bae29d60768bfa8fb92244b74502b18fae55a80eac13c88eb0b496d4268fd2d", + "sha256:8df3de3a9ab8325f94f646609a66cbeeede263910c5c0de0101079ad541af332", + "sha256:8fa3c6e3305aa1146b59a09b32b2e04074945ffcfb2f0931836d103a2c38f936", + "sha256:924620eef691990dfb56dc4709f280f40baee568c794b5c1885800c3ecc69816", + "sha256:9309869032abb23d196cb4e4db574232abe8b8be1339026f489eeb34a4acfd91", + "sha256:9545a33965d0d377b0bc823dcabf26980e77f1b6a7caa368a365a9497fb09420", + "sha256:9ac5995f2b408017b0be26d4a1d7c61bce106ff3d9e3324374d66b5964325448", + "sha256:9bbbcedd75acdfecf2159663b87f1bb5cfc80e7cd99f7ddd9d66eb98b14a8411", + "sha256:a4ae8135b11652b08a8baf07631d3ebfe65a4c87909dbef5fa0cdde440444ee4", + "sha256:a6394d7dadd3cfe3f4b3b186e54d5d8504d44f2d58dcc89d693698e8b7132b32", + "sha256:a97b4fe50b5890d36300820abd305694cb865ddb7885049587a5678215782a6b", + "sha256:ae4dc05c465a08a866b7a1baf360747078b362e6a6dbeb0c57f234db0ef88ae0", + "sha256:b1c63e8d377d039ac769cd0926558bb7068a1f7abb0f003e3717ee003ad85530", + "sha256:b1e2c1185858d7e10ff045c496bbf90ae752c28b365fef2c09cf0fa309291669", + "sha256:b4395e2f8d83fbe0c627b2b696acce67868793d7d9750e90e39592b3626691b7", + "sha256:b756072364347cb6aa5b60f9bc18e94b2f79632de3b0190253ad770c5df17db1", + "sha256:ba64dc2b3b7b158c6660d49cdb1d872d1d0bf4e42043ad8d5006099479a194e5", + "sha256:bed331fe18f58d844d39ceb398b77d6ac0b010d571cba8267c2e7165806b00ce", + "sha256:c188512b43542b1e91cadc3c6c915a82a5eb95929134faf7fd109f14f9892ce4", + "sha256:c21b9aa40e08e4f63a2f92ff3748e6b6c84d717d033c7b3438dd3123ee18f70e", + "sha256:ca713d4af15bae6e5d79b15c10c8522859a9a89d3b361a50b817c98c2fb402a2", + "sha256:cd4210baef299717db0a600d7a3cac81d46ef0e007f88c9335db79f8979c0d3d", + "sha256:cfe33efc9cb900a4c46f91a5ceba26d6df370ffddd9ca386eb1d4f0ad97b9ea9", + "sha256:d5cd3ab21acbdb414bb6c31958d7b06b85eeb40f66463c264a9b343a4e238642", + "sha256:dfbac4c2dfcc082fcf8d942d1e49b6aa0766c19d3358bd86e2000bf0fa4a9cf0", + "sha256:e235688f42b36be2b6b06fc37ac2126a73b75fb8d6bc66dd632aa35286238703", + "sha256:eb82dbba47a8318e75f679690190c10a5e1f447fbf9df41cbc4c3afd726d88cb", + "sha256:ebb86518203e12e96af765ee89034a1dbb0c3c65052d1b0c19bbbd6af8a145e1", + "sha256:ee78feb9d293c323b59a6f2dd441b63339a30edf35abcb51187d2fc26e696d13", + "sha256:eedab4c310c0299961ac285591acd53dc6723a1ebd90a57207c71f6e0c2153ab", + "sha256:efa568b885bca461f7c7b9e032655c0c143d305bf01c30caf6db2854a4532b38", + "sha256:efce6ae830831ab6a22b9b4091d411698145cb9b8fc869e1397ccf4b4b6455cb", + "sha256:f163d2fd041c630fed01bc48d28c3ed4a3b003c00acd396900e11ee5316b56bb", + "sha256:f20380df709d91525e4bee04746ba612a4df0972c1b8f8e1e8af997e678c7b81", + "sha256:f30f1928162e189091cf4d9da2eac617bfe78ef907a761614ff577ef4edfb3c8", + "sha256:f470c92737afa7d4c3aacc001e335062d582053d4dbe73cda126f2d7031068dd", + "sha256:ff8bf625fe85e119553b5383ba0fb6aa3d0ec2ae980295aaefa552374926b3f4" + ], + "markers": "python_version >= '3.7'", + "version": "==1.3.3" + }, + "h11": { + "hashes": [ + "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6", + "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042" + ], + "markers": "python_version >= '3.6'", + "version": "==0.12.0" + }, + "h2": { + "hashes": [ + "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d", + "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb" + ], + "version": "==4.1.0" + }, + "hpack": { + "hashes": [ + "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c", + "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095" ], - "markers": "python_version >= '3'", - "version": "==2.0.2" + "markers": "python_full_version >= '3.6.1'", + "version": "==4.0.0" + }, + "httpcore": { + "hashes": [ + "sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6", + "sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b" + ], + "markers": "python_version >= '3.7'", + "version": "==0.15.0" + }, + "httpx": { + "extras": [ + "http2" + ], + "hashes": [ + "sha256:42974f577483e1e932c3cdc3cd2303e883cbfba17fe228b0f63589764d7b9c4b", + "sha256:f28eac771ec9eb4866d3fb4ab65abd42d38c424739e80c08d8d20570de60b0ef" + ], + "index": "pypi", + "version": "==0.23.0" + }, + "hyperframe": { + "hashes": [ + "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15", + "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914" + ], + "markers": "python_full_version >= '3.6.1'", + "version": "==6.0.1" }, "idna": { "hashes": [ - "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a", - "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3" + "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", + "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" ], - "markers": "python_version >= '3'", - "version": "==3.2" + "markers": "python_version >= '3.5'", + "version": "==3.4" }, - "requests": { + "microsoft-kiota-abstractions": { + "hashes": [ + "sha256:b21e4182ceac16ba0cbc8e37d4487b8fed3b4724692b42faa3427a278328b4d4", + "sha256:e0597e6691a7bae7977ac46ea625a921ac25df578ea6bacaacadebbcd77ac555" + ], + "index": "pypi", + "version": "==0.1.0" + }, + "microsoft-kiota-authentication-azure": { + "hashes": [ + "sha256:1d8654e75b8691744b00e97dc126d4f0c67af299c84b382c5397982802aa5d3a", + "sha256:b5f37aa4a374205322ec025dcf6530adf1b54c98d79888b13dc96d7b8252d6ec" + ], + "index": "pypi", + "version": "==0.1.0" + }, + "microsoft-kiota-http": { "hashes": [ - "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", - "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" + "sha256:12392f4d270001dfeba0464e602ef17922e9990cd6db1e26746b1c9d5263cbbb", + "sha256:34f61b0b732df4875f8f7058907af8f22fa6bc919aff9dc70dd7a806288ee7ab" ], "index": "pypi", - "version": "==2.26.0" + "version": "==0.2.0" + }, + "multidict": { + "hashes": [ + "sha256:0327292e745a880459ef71be14e709aaea2f783f3537588fb4ed09b6c01bca60", + "sha256:041b81a5f6b38244b34dc18c7b6aba91f9cdaf854d9a39e5ff0b58e2b5773b9c", + "sha256:0556a1d4ea2d949efe5fd76a09b4a82e3a4a30700553a6725535098d8d9fb672", + "sha256:05f6949d6169878a03e607a21e3b862eaf8e356590e8bdae4227eedadacf6e51", + "sha256:07a017cfa00c9890011628eab2503bee5872f27144936a52eaab449be5eaf032", + "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2", + "sha256:19adcfc2a7197cdc3987044e3f415168fc5dc1f720c932eb1ef4f71a2067e08b", + "sha256:19d9bad105dfb34eb539c97b132057a4e709919ec4dd883ece5838bcbf262b80", + "sha256:225383a6603c086e6cef0f2f05564acb4f4d5f019a4e3e983f572b8530f70c88", + "sha256:23b616fdc3c74c9fe01d76ce0d1ce872d2d396d8fa8e4899398ad64fb5aa214a", + "sha256:2957489cba47c2539a8eb7ab32ff49101439ccf78eab724c828c1a54ff3ff98d", + "sha256:2d36e929d7f6a16d4eb11b250719c39560dd70545356365b494249e2186bc389", + "sha256:2e4a0785b84fb59e43c18a015ffc575ba93f7d1dbd272b4cdad9f5134b8a006c", + "sha256:3368bf2398b0e0fcbf46d85795adc4c259299fec50c1416d0f77c0a843a3eed9", + "sha256:373ba9d1d061c76462d74e7de1c0c8e267e9791ee8cfefcf6b0b2495762c370c", + "sha256:4070613ea2227da2bfb2c35a6041e4371b0af6b0be57f424fe2318b42a748516", + "sha256:45183c96ddf61bf96d2684d9fbaf6f3564d86b34cb125761f9a0ef9e36c1d55b", + "sha256:4571f1beddff25f3e925eea34268422622963cd8dc395bb8778eb28418248e43", + "sha256:47e6a7e923e9cada7c139531feac59448f1f47727a79076c0b1ee80274cd8eee", + "sha256:47fbeedbf94bed6547d3aa632075d804867a352d86688c04e606971595460227", + "sha256:497988d6b6ec6ed6f87030ec03280b696ca47dbf0648045e4e1d28b80346560d", + "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae", + "sha256:50bd442726e288e884f7be9071016c15a8742eb689a593a0cac49ea093eef0a7", + "sha256:514fe2b8d750d6cdb4712346a2c5084a80220821a3e91f3f71eec11cf8d28fd4", + "sha256:5774d9218d77befa7b70d836004a768fb9aa4fdb53c97498f4d8d3f67bb9cfa9", + "sha256:5fdda29a3c7e76a064f2477c9aab1ba96fd94e02e386f1e665bca1807fc5386f", + "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013", + "sha256:626fe10ac87851f4cffecee161fc6f8f9853f0f6f1035b59337a51d29ff3b4f9", + "sha256:6701bf8a5d03a43375909ac91b6980aea74b0f5402fbe9428fc3f6edf5d9677e", + "sha256:684133b1e1fe91eda8fa7447f137c9490a064c6b7f392aa857bba83a28cfb693", + "sha256:6f3cdef8a247d1eafa649085812f8a310e728bdf3900ff6c434eafb2d443b23a", + "sha256:75bdf08716edde767b09e76829db8c1e5ca9d8bb0a8d4bd94ae1eafe3dac5e15", + "sha256:7c40b7bbece294ae3a87c1bc2abff0ff9beef41d14188cda94ada7bcea99b0fb", + "sha256:8004dca28e15b86d1b1372515f32eb6f814bdf6f00952699bdeb541691091f96", + "sha256:8064b7c6f0af936a741ea1efd18690bacfbae4078c0c385d7c3f611d11f0cf87", + "sha256:89171b2c769e03a953d5969b2f272efa931426355b6c0cb508022976a17fd376", + "sha256:8cbf0132f3de7cc6c6ce00147cc78e6439ea736cee6bca4f068bcf892b0fd658", + "sha256:9cc57c68cb9139c7cd6fc39f211b02198e69fb90ce4bc4a094cf5fe0d20fd8b0", + "sha256:a007b1638e148c3cfb6bf0bdc4f82776cef0ac487191d093cdc316905e504071", + "sha256:a2c34a93e1d2aa35fbf1485e5010337c72c6791407d03aa5f4eed920343dd360", + "sha256:a45e1135cb07086833ce969555df39149680e5471c04dfd6a915abd2fc3f6dbc", + "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3", + "sha256:aef9cc3d9c7d63d924adac329c33835e0243b5052a6dfcbf7732a921c6e918ba", + "sha256:b9d153e7f1f9ba0b23ad1568b3b9e17301e23b042c23870f9ee0522dc5cc79e8", + "sha256:bfba7c6d5d7c9099ba21f84662b037a0ffd4a5e6b26ac07d19e423e6fdf965a9", + "sha256:c207fff63adcdf5a485969131dc70e4b194327666b7e8a87a97fbc4fd80a53b2", + "sha256:d0509e469d48940147e1235d994cd849a8f8195e0bca65f8f5439c56e17872a3", + "sha256:d16cce709ebfadc91278a1c005e3c17dd5f71f5098bfae1035149785ea6e9c68", + "sha256:d48b8ee1d4068561ce8033d2c344cf5232cb29ee1a0206a7b828c79cbc5982b8", + "sha256:de989b195c3d636ba000ee4281cd03bb1234635b124bf4cd89eeee9ca8fcb09d", + "sha256:e07c8e79d6e6fd37b42f3250dba122053fddb319e84b55dd3a8d6446e1a7ee49", + "sha256:e2c2e459f7050aeb7c1b1276763364884595d47000c1cddb51764c0d8976e608", + "sha256:e5b20e9599ba74391ca0cfbd7b328fcc20976823ba19bc573983a25b32e92b57", + "sha256:e875b6086e325bab7e680e4316d667fc0e5e174bb5611eb16b3ea121c8951b86", + "sha256:f4f052ee022928d34fe1f4d2bc743f32609fb79ed9c49a1710a5ad6b2198db20", + "sha256:fcb91630817aa8b9bc4a74023e4198480587269c272c58b3279875ed7235c293", + "sha256:fd9fc9c4849a07f3635ccffa895d57abce554b467d611a5009ba4f39b78a8849", + "sha256:feba80698173761cddd814fa22e88b0661e98cb810f9f986c54aa34d281e4937", + "sha256:feea820722e69451743a3d56ad74948b68bf456984d63c1a92e8347b7b88452d" + ], + "markers": "python_version >= '3.7'", + "version": "==6.0.2" + }, + "requests": { + "hashes": [ + "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", + "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" + ], + "markers": "python_version >= '3.7' and python_version < '4'", + "version": "==2.28.1" + }, + "rfc3986": { + "extras": [ + "idna2008" + ], + "hashes": [ + "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835", + "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97" + ], + "version": "==1.5.0" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "sniffio": { + "hashes": [ + "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101", + "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384" + ], + "markers": "python_version >= '3.7'", + "version": "==1.3.0" + }, + "uritemplate": { + "hashes": [ + "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0", + "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e" + ], + "markers": "python_version >= '3.6'", + "version": "==4.1.1" }, "urllib3": { "hashes": [ - "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4", - "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f" + "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e", + "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", + "version": "==1.26.12" + }, + "yarl": { + "hashes": [ + "sha256:076eede537ab978b605f41db79a56cad2e7efeea2aa6e0fa8f05a26c24a034fb", + "sha256:07b21e274de4c637f3e3b7104694e53260b5fc10d51fb3ec5fed1da8e0f754e3", + "sha256:0ab5a138211c1c366404d912824bdcf5545ccba5b3ff52c42c4af4cbdc2c5035", + "sha256:0c03f456522d1ec815893d85fccb5def01ffaa74c1b16ff30f8aaa03eb21e453", + "sha256:12768232751689c1a89b0376a96a32bc7633c08da45ad985d0c49ede691f5c0d", + "sha256:19cd801d6f983918a3f3a39f3a45b553c015c5aac92ccd1fac619bd74beece4a", + "sha256:1ca7e596c55bd675432b11320b4eacc62310c2145d6801a1f8e9ad160685a231", + "sha256:1e4808f996ca39a6463f45182e2af2fae55e2560be586d447ce8016f389f626f", + "sha256:205904cffd69ae972a1707a1bd3ea7cded594b1d773a0ce66714edf17833cdae", + "sha256:20df6ff4089bc86e4a66e3b1380460f864df3dd9dccaf88d6b3385d24405893b", + "sha256:21ac44b763e0eec15746a3d440f5e09ad2ecc8b5f6dcd3ea8cb4773d6d4703e3", + "sha256:29e256649f42771829974e742061c3501cc50cf16e63f91ed8d1bf98242e5507", + "sha256:2d800b9c2eaf0684c08be5f50e52bfa2aa920e7163c2ea43f4f431e829b4f0fd", + "sha256:2d93a049d29df172f48bcb09acf9226318e712ce67374f893b460b42cc1380ae", + "sha256:31a9a04ecccd6b03e2b0e12e82131f1488dea5555a13a4d32f064e22a6003cfe", + "sha256:3d1a50e461615747dd93c099f297c1994d472b0f4d2db8a64e55b1edf704ec1c", + "sha256:449c957ffc6bc2309e1fbe67ab7d2c1efca89d3f4912baeb8ead207bb3cc1cd4", + "sha256:4a88510731cd8d4befaba5fbd734a7dd914de5ab8132a5b3dde0bbd6c9476c64", + "sha256:4c322cbaa4ed78a8aac89b2174a6df398faf50e5fc12c4c191c40c59d5e28357", + "sha256:5395da939ffa959974577eff2cbfc24b004a2fb6c346918f39966a5786874e54", + "sha256:5587bba41399854703212b87071c6d8638fa6e61656385875f8c6dff92b2e461", + "sha256:56c11efb0a89700987d05597b08a1efcd78d74c52febe530126785e1b1a285f4", + "sha256:5999c4662631cb798496535afbd837a102859568adc67d75d2045e31ec3ac497", + "sha256:59ddd85a1214862ce7c7c66457f05543b6a275b70a65de366030d56159a979f0", + "sha256:6347f1a58e658b97b0a0d1ff7658a03cb79bdbda0331603bed24dd7054a6dea1", + "sha256:6628d750041550c5d9da50bb40b5cf28a2e63b9388bac10fedd4f19236ef4957", + "sha256:6afb336e23a793cd3b6476c30f030a0d4c7539cd81649683b5e0c1b0ab0bf350", + "sha256:6c8148e0b52bf9535c40c48faebb00cb294ee577ca069d21bd5c48d302a83780", + "sha256:76577f13333b4fe345c3704811ac7509b31499132ff0181f25ee26619de2c843", + "sha256:7c0da7e44d0c9108d8b98469338705e07f4bb7dab96dbd8fa4e91b337db42548", + "sha256:7de89c8456525650ffa2bb56a3eee6af891e98f498babd43ae307bd42dca98f6", + "sha256:7ec362167e2c9fd178f82f252b6d97669d7245695dc057ee182118042026da40", + "sha256:7fce6cbc6c170ede0221cc8c91b285f7f3c8b9fe28283b51885ff621bbe0f8ee", + "sha256:85cba594433915d5c9a0d14b24cfba0339f57a2fff203a5d4fd070e593307d0b", + "sha256:8b0af1cf36b93cee99a31a545fe91d08223e64390c5ecc5e94c39511832a4bb6", + "sha256:9130ddf1ae9978abe63808b6b60a897e41fccb834408cde79522feb37fb72fb0", + "sha256:99449cd5366fe4608e7226c6cae80873296dfa0cde45d9b498fefa1de315a09e", + "sha256:9de955d98e02fab288c7718662afb33aab64212ecb368c5dc866d9a57bf48880", + "sha256:a0fb2cb4204ddb456a8e32381f9a90000429489a25f64e817e6ff94879d432fc", + "sha256:a165442348c211b5dea67c0206fc61366212d7082ba8118c8c5c1c853ea4d82e", + "sha256:ab2a60d57ca88e1d4ca34a10e9fb4ab2ac5ad315543351de3a612bbb0560bead", + "sha256:abc06b97407868ef38f3d172762f4069323de52f2b70d133d096a48d72215d28", + "sha256:af887845b8c2e060eb5605ff72b6f2dd2aab7a761379373fd89d314f4752abbf", + "sha256:b19255dde4b4f4c32e012038f2c169bb72e7f081552bea4641cab4d88bc409dd", + "sha256:b3ded839a5c5608eec8b6f9ae9a62cb22cd037ea97c627f38ae0841a48f09eae", + "sha256:c1445a0c562ed561d06d8cbc5c8916c6008a31c60bc3655cdd2de1d3bf5174a0", + "sha256:d0272228fabe78ce00a3365ffffd6f643f57a91043e119c289aaba202f4095b0", + "sha256:d0b51530877d3ad7a8d47b2fff0c8df3b8f3b8deddf057379ba50b13df2a5eae", + "sha256:d0f77539733e0ec2475ddcd4e26777d08996f8cd55d2aef82ec4d3896687abda", + "sha256:d2b8f245dad9e331540c350285910b20dd913dc86d4ee410c11d48523c4fd546", + "sha256:dd032e8422a52e5a4860e062eb84ac94ea08861d334a4bcaf142a63ce8ad4802", + "sha256:de49d77e968de6626ba7ef4472323f9d2e5a56c1d85b7c0e2a190b2173d3b9be", + "sha256:de839c3a1826a909fdbfe05f6fe2167c4ab033f1133757b5936efe2f84904c07", + "sha256:e80ed5a9939ceb6fda42811542f31c8602be336b1fb977bccb012e83da7e4936", + "sha256:ea30a42dc94d42f2ba4d0f7c0ffb4f4f9baa1b23045910c0c32df9c9902cb272", + "sha256:ea513a25976d21733bff523e0ca836ef1679630ef4ad22d46987d04b372d57fc", + "sha256:ed19b74e81b10b592084a5ad1e70f845f0aacb57577018d31de064e71ffa267a", + "sha256:f5af52738e225fcc526ae64071b7e5342abe03f42e0e8918227b38c9aa711e28", + "sha256:fae37373155f5ef9b403ab48af5136ae9851151f7aacd9926251ab26b953118b" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.26.6" + "markers": "python_version >= '3.7'", + "version": "==1.8.1" } }, "develop": { "astroid": { "hashes": [ - "sha256:38b95085e9d92e2ca06cf8b35c12a74fa81da395a6f9e65803742e6509c05892", - "sha256:606b2911d10c3dcf35e58d2ee5c97360e8477d7b9f3efc3f24811c93e6fc2cd9" + "sha256:1c00a14f5a3ed0339d38d2e2e5b74ea2591df5861c0936bb292b84ccf3a78d83", + "sha256:72702205200b2a638358369d90c222d74ebc376787af8fb2f7f2a86f7b5cc85f" ], - "markers": "python_version ~= '3.6'", - "version": "==2.6.2" + "markers": "python_full_version >= '3.7.2'", + "version": "==2.12.12" }, - "atomicwrites": { + "async-generator": { "hashes": [ - "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197", - "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a" + "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b", + "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144" ], - "markers": "sys_platform == 'win32'", - "version": "==1.4.0" + "markers": "python_version >= '3.5'", + "version": "==1.10" + }, + "asyncmock": { + "hashes": [ + "sha256:c251889d542e98fe5f7ece2b5b8643b7d62b50a5657d34a4cbce8a1d5170d750", + "sha256:fd8bc4e7813251a8959d1140924ccba3adbbc7af885dba7047c67f73c0b664b1" + ], + "index": "pypi", + "version": "==0.4.2" }, "attrs": { "hashes": [ - "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", - "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb" + "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6", + "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==21.2.0" + "markers": "python_version >= '3.5'", + "version": "==22.1.0" }, "azure-core": { "hashes": [ - "sha256:83ef981ca4ad167c1df7b1ec5c4bda74999fcd21cacc048e83cf8ce51c61d5c2", - "sha256:b1c7d2e01846074f258c8b2e592239aef836a2b1c27d8d0e8491a2c7e2906ef4" + "sha256:3d70e9ec64de92dfae330c15bc69085caceb2d83813ef6c01cc45326f2a4be83", + "sha256:88d2db5cf9a135a7287dc45fdde6b96f9ca62c9567512a3bb3e20e322ce7deb2" ], - "version": "==1.16.0" + "version": "==1.21.1" }, "azure-identity": { "hashes": [ - "sha256:2e70b00874e4f288e37804bc06bfaf216de8565c759594bf79cccfbf9ca2c78a", - "sha256:87f63bf1f3f10b67a3e6689f06d0cf6cacdcd8e375c6da12ea71c111cfe58457" + "sha256:2a58ce4a209a013e37eaccfd5937570ab99e9118b3e1acf875eed3a85d541b92", + "sha256:7f9b1ae7d97ea7af3f38dd09305e19ab81a1e16ab66ea186b6579d85c1ca2347" ], "index": "pypi", - "version": "==1.6.0" + "version": "==1.12.0" }, - "backports.entry-points-selectable": { + "build": { "hashes": [ - "sha256:988468260ec1c196dab6ae1149260e2f5472c9110334e5d51adcb77867361f6a", - "sha256:a6d9a871cde5e15b4c4a53e3d43ba890cc6861ec1332c9c2428c92f977192acc" + "sha256:1a07724e891cbd898923145eb7752ee7653674c511378eb9c7691aab1612bc3c", + "sha256:38a7a2b7a0bdc61a42a0a67509d88c71ecfc37b393baba770fae34e20929ff69" ], - "markers": "python_version >= '2.7'", - "version": "==1.1.0" + "index": "pypi", + "version": "==0.9.0" + }, + "bumpver": { + "hashes": [ + "sha256:afed711d286403ed848daabba6006318ed2fb8d4c1f192466ac20756afc31a61", + "sha256:c4cc84a881bf945510a4831200fd082c3520665c61e73ff7732e26249a6a86c9" + ], + "index": "pypi", + "version": "==2022.1119" }, "certifi": { "hashes": [ - "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee", - "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8" + "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14", + "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382" ], - "version": "==2021.5.30" + "markers": "python_version >= '3.6'", + "version": "==2022.9.24" }, "cffi": { "hashes": [ - "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d", - "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771", - "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872", - "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c", - "sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc", - "sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762", - "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202", - "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5", - "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548", - "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a", - "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f", - "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20", - "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218", - "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c", - "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e", - "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56", - "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224", - "sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a", - "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2", - "sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a", - "sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819", - "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346", - "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b", - "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e", - "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534", - "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb", - "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0", - "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156", - "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd", - "sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87", - "sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc", - "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195", - "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33", - "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f", - "sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d", - "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd", - "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728", - "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7", - "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca", - "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99", - "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf", - "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e", - "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c", - "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5", - "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69" - ], - "version": "==1.14.6" - }, - "cfgv": { - "hashes": [ - "sha256:9e600479b3b99e8af981ecdfc80a0296104ee610cab48a5ae4ffd0b668650eb1", - "sha256:b449c9c6118fe8cca7fa5e00b9ec60ba08145d281d52164230a69211c5d597a1" + "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5", + "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef", + "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104", + "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426", + "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405", + "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375", + "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a", + "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e", + "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc", + "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf", + "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185", + "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497", + "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3", + "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35", + "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c", + "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83", + "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21", + "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca", + "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984", + "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac", + "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd", + "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee", + "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a", + "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2", + "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192", + "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7", + "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585", + "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f", + "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e", + "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27", + "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b", + "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e", + "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e", + "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d", + "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c", + "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415", + "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82", + "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02", + "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314", + "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325", + "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c", + "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3", + "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914", + "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045", + "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d", + "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9", + "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5", + "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2", + "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c", + "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3", + "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2", + "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8", + "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d", + "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d", + "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9", + "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162", + "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76", + "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4", + "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e", + "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9", + "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6", + "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b", + "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01", + "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0" ], - "markers": "python_full_version >= '3.6.1'", - "version": "==3.3.0" + "markers": "os_name == 'nt' and implementation_name != 'pypy'", + "version": "==1.15.1" }, "charset-normalizer": { "hashes": [ - "sha256:3c502a35807c9df35697b0f44b1d65008f83071ff29c69677c7c22573bc5a45a", - "sha256:951567c2f7433a70ab63f1be67e5ee05d3925d9423306ecb71a3b272757bcc95" + "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845", + "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" + ], + "markers": "python_full_version >= '3.6.0'", + "version": "==2.1.1" + }, + "click": { + "hashes": [ + "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", + "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" ], - "markers": "python_version >= '3'", - "version": "==2.0.2" + "markers": "python_version >= '3.6'", + "version": "==8.1.3" }, "colorama": { "hashes": [ - "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b", - "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2" + "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", + "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" ], - "markers": "sys_platform == 'win32' and sys_platform == 'win32'", - "version": "==0.4.4" + "markers": "os_name == 'nt'", + "version": "==0.4.6" }, "coverage": { + "extras": [ + "toml" + ], "hashes": [ - "sha256:15cf13a6896048d6d947bf7d222f36e4809ab926894beb748fc9caa14605d9c3", - "sha256:1daa3eceed220f9fdb80d5ff950dd95112cd27f70d004c7918ca6dfc6c47054c", - "sha256:1e44a022500d944d42f94df76727ba3fc0a5c0b672c358b61067abb88caee7a0", - "sha256:25dbf1110d70bab68a74b4b9d74f30e99b177cde3388e07cc7272f2168bd1477", - "sha256:3230d1003eec018ad4a472d254991e34241e0bbd513e97a29727c7c2f637bd2a", - "sha256:3dbb72eaeea5763676a1a1efd9b427a048c97c39ed92e13336e726117d0b72bf", - "sha256:5012d3b8d5a500834783689a5d2292fe06ec75dc86ee1ccdad04b6f5bf231691", - "sha256:51bc7710b13a2ae0c726f69756cf7ffd4362f4ac36546e243136187cfcc8aa73", - "sha256:527b4f316e6bf7755082a783726da20671a0cc388b786a64417780b90565b987", - "sha256:722e4557c8039aad9592c6a4213db75da08c2cd9945320220634f637251c3894", - "sha256:76e2057e8ffba5472fd28a3a010431fd9e928885ff480cb278877c6e9943cc2e", - "sha256:77afca04240c40450c331fa796b3eab6f1e15c5ecf8bf2b8bee9706cd5452fef", - "sha256:7afad9835e7a651d3551eab18cbc0fdb888f0a6136169fbef0662d9cdc9987cf", - "sha256:9bea19ac2f08672636350f203db89382121c9c2ade85d945953ef3c8cf9d2a68", - "sha256:a8b8ac7876bc3598e43e2603f772d2353d9931709345ad6c1149009fd1bc81b8", - "sha256:b0840b45187699affd4c6588286d429cd79a99d509fe3de0f209594669bb0954", - "sha256:b26aaf69713e5674efbde4d728fb7124e429c9466aeaf5f4a7e9e699b12c9fe2", - "sha256:b63dd43f455ba878e5e9f80ba4f748c0a2156dde6e0e6e690310e24d6e8caf40", - "sha256:be18f4ae5a9e46edae3f329de2191747966a34a3d93046dbdf897319923923bc", - "sha256:c312e57847db2526bc92b9bfa78266bfbaabac3fdcd751df4d062cd4c23e46dc", - "sha256:c60097190fe9dc2b329a0eb03393e2e0829156a589bd732e70794c0dd804258e", - "sha256:c62a2143e1313944bf4a5ab34fd3b4be15367a02e9478b0ce800cb510e3bbb9d", - "sha256:cc1109f54a14d940b8512ee9f1c3975c181bbb200306c6d8b87d93376538782f", - "sha256:cd60f507c125ac0ad83f05803063bed27e50fa903b9c2cfee3f8a6867ca600fc", - "sha256:d513cc3db248e566e07a0da99c230aca3556d9b09ed02f420664e2da97eac301", - "sha256:d649dc0bcace6fcdb446ae02b98798a856593b19b637c1b9af8edadf2b150bea", - "sha256:d7008a6796095a79544f4da1ee49418901961c97ca9e9d44904205ff7d6aa8cb", - "sha256:da93027835164b8223e8e5af2cf902a4c80ed93cb0909417234f4a9df3bcd9af", - "sha256:e69215621707119c6baf99bda014a45b999d37602cb7043d943c76a59b05bf52", - "sha256:ea9525e0fef2de9208250d6c5aeeee0138921057cd67fcef90fbed49c4d62d37", - "sha256:fca1669d464f0c9831fd10be2eef6b86f5ebd76c724d1e0706ebdff86bb4adf0" + "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79", + "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a", + "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f", + "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a", + "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa", + "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398", + "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba", + "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d", + "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf", + "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b", + "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518", + "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d", + "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795", + "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2", + "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e", + "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32", + "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745", + "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b", + "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e", + "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d", + "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f", + "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660", + "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62", + "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6", + "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04", + "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c", + "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5", + "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef", + "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc", + "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae", + "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578", + "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466", + "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4", + "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91", + "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0", + "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4", + "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b", + "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe", + "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b", + "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75", + "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b", + "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c", + "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72", + "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b", + "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f", + "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e", + "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53", + "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3", + "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84", + "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987" ], - "index": "pypi", - "version": "==5.0.3" + "markers": "python_version >= '3.7'", + "version": "==6.5.0" }, "cryptography": { "hashes": [ - "sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d", - "sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959", - "sha256:240f5c21aef0b73f40bb9f78d2caff73186700bf1bc6b94285699aff98cc16c6", - "sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873", - "sha256:37340614f8a5d2fb9aeea67fd159bfe4f5f4ed535b1090ce8ec428b2f15a11f2", - "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713", - "sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1", - "sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177", - "sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250", - "sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca", - "sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d", - "sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9" + "sha256:068147f32fa662c81aebab95c74679b401b12b57494872886eb5c1139250ec5d", + "sha256:06fc3cc7b6f6cca87bd56ec80a580c88f1da5306f505876a71c8cfa7050257dd", + "sha256:25c1d1f19729fb09d42e06b4bf9895212292cb27bb50229f5aa64d039ab29146", + "sha256:402852a0aea73833d982cabb6d0c3bb582c15483d29fb7085ef2c42bfa7e38d7", + "sha256:4e269dcd9b102c5a3d72be3c45d8ce20377b8076a43cbed6f660a1afe365e436", + "sha256:5419a127426084933076132d317911e3c6eb77568a1ce23c3ac1e12d111e61e0", + "sha256:554bec92ee7d1e9d10ded2f7e92a5d70c1f74ba9524947c0ba0c850c7b011828", + "sha256:5e89468fbd2fcd733b5899333bc54d0d06c80e04cd23d8c6f3e0542358c6060b", + "sha256:65535bc550b70bd6271984d9863a37741352b4aad6fb1b3344a54e6950249b55", + "sha256:6ab9516b85bebe7aa83f309bacc5f44a61eeb90d0b4ec125d2d003ce41932d36", + "sha256:6addc3b6d593cd980989261dc1cce38263c76954d758c3c94de51f1e010c9a50", + "sha256:728f2694fa743a996d7784a6194da430f197d5c58e2f4e278612b359f455e4a2", + "sha256:785e4056b5a8b28f05a533fab69febf5004458e20dad7e2e13a3120d8ecec75a", + "sha256:78cf5eefac2b52c10398a42765bfa981ce2372cbc0457e6bf9658f41ec3c41d8", + "sha256:7f836217000342d448e1c9a342e9163149e45d5b5eca76a30e84503a5a96cab0", + "sha256:8d41a46251bf0634e21fac50ffd643216ccecfaf3701a063257fe0b2be1b6548", + "sha256:984fe150f350a3c91e84de405fe49e688aa6092b3525f407a18b9646f6612320", + "sha256:9b24bcff7853ed18a63cfb0c2b008936a9554af24af2fb146e16d8e1aed75748", + "sha256:b1b35d9d3a65542ed2e9d90115dfd16bbc027b3f07ee3304fc83580f26e43249", + "sha256:b1b52c9e5f8aa2b802d48bd693190341fae201ea51c7a167d69fc48b60e8a959", + "sha256:bbf203f1a814007ce24bd4d51362991d5cb90ba0c177a9c08825f2cc304d871f", + "sha256:be243c7e2bfcf6cc4cb350c0d5cdf15ca6383bbcb2a8ef51d3c9411a9d4386f0", + "sha256:bfbe6ee19615b07a98b1d2287d6a6073f734735b49ee45b11324d85efc4d5cbd", + "sha256:c46837ea467ed1efea562bbeb543994c2d1f6e800785bd5a2c98bc096f5cb220", + "sha256:dfb4f4dd568de1b6af9f4cda334adf7d72cf5bc052516e1b2608b683375dd95c", + "sha256:ed7b00096790213e09eb11c97cc6e2b757f15f3d2f85833cd2d3ec3fe37c1722" ], - "version": "==3.4.7" + "markers": "python_version >= '3.6'", + "version": "==38.0.3" }, - "distlib": { + "dill": { "hashes": [ - "sha256:106fef6dc37dd8c0e2c0a60d3fca3e77460a48907f335fa28420463a6f799736", - "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c" + "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0", + "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373" ], - "version": "==0.3.2" + "markers": "python_version >= '3.7'", + "version": "==0.3.6" }, "docutils": { "hashes": [ - "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125", - "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61" + "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6", + "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==0.17.1" + "markers": "python_version >= '3.7'", + "version": "==0.19" }, - "filelock": { + "exceptiongroup": { "hashes": [ - "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", - "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836" + "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828", + "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec" ], - "version": "==3.0.12" + "markers": "python_version < '3.11'", + "version": "==1.0.4" }, "flit": { "hashes": [ - "sha256:6ec794b13bddffafa327854f0812022e6c993287f536ffbef3563462b4d2eade", - "sha256:a273dc2a5ab1f42f0e02878347d94f5462c42cbe232a5014b6778c9e5975327f" + "sha256:06a93a6737fa9380ba85fe8d7f28efb6c93c4f4ee9c7d00cc3375a81f33b91a4", + "sha256:3c9bd9c140515bfe62dd938c6610d10d6efb9e35cc647fc614fe5fb3a5036682" ], "index": "pypi", - "version": "==2.2.0" + "version": "==3.7.1" }, "flit-core": { "hashes": [ - "sha256:6f25843e908dfc3e907b6b9ee71e3d185bcb5aebab8c3431e4e34c261e5ff1b5", - "sha256:ff87f25c5dbc24ef30ea334074e35030e4885e4c5de3bf4e21f15746f6d99431" + "sha256:14955af340c43035dbfa96b5ee47407e377ee337f69e70f73064940d27d0a44f", + "sha256:e454fdbf68c7036e1c7435ec7479383f9d9a1650ca5b304feb184eba1efcdcef" ], - "markers": "python_version >= '3.4'", - "version": "==3.2.0" - }, - "identify": { - "hashes": [ - "sha256:7abaecbb414e385752e8ce02d8c494f4fbc780c975074b46172598a28f1ab839", - "sha256:a0e700637abcbd1caae58e0463861250095dfe330a8371733a471af706a4a29a" - ], - "markers": "python_full_version >= '3.6.1'", - "version": "==2.2.11" + "markers": "python_version >= '3.6'", + "version": "==3.7.1" }, "idna": { "hashes": [ - "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a", - "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3" + "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", + "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" ], - "markers": "python_version >= '3'", - "version": "==3.2" + "markers": "python_version >= '3.5'", + "version": "==3.4" }, "iniconfig": { "hashes": [ @@ -300,88 +804,104 @@ }, "isort": { "hashes": [ - "sha256:c729845434366216d320e936b8ad6f9d681aab72dc7cbc2d51bedc3582f3ad1e", - "sha256:fff4f0c04e1825522ce6949973e83110a6e907750cd92d128b0d14aaaadbffdc" + "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7", + "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951" ], "index": "pypi", - "version": "==5.7.0" + "version": "==5.10.1" }, "lazy-object-proxy": { "hashes": [ - "sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653", - "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61", - "sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2", - "sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837", - "sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3", - "sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43", - "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726", - "sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3", - "sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587", - "sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8", - "sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a", - "sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd", - "sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f", - "sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad", - "sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4", - "sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b", - "sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf", - "sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981", - "sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741", - "sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e", - "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93", - "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.6.0" + "sha256:0c1c7c0433154bb7c54185714c6929acc0ba04ee1b167314a779b9025517eada", + "sha256:14010b49a2f56ec4943b6cf925f597b534ee2fe1f0738c84b3bce0c1a11ff10d", + "sha256:4e2d9f764f1befd8bdc97673261b8bb888764dfdbd7a4d8f55e4fbcabb8c3fb7", + "sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe", + "sha256:5b51d6f3bfeb289dfd4e95de2ecd464cd51982fe6f00e2be1d0bf94864d58acd", + "sha256:6850e4aeca6d0df35bb06e05c8b934ff7c533734eb51d0ceb2d63696f1e6030c", + "sha256:6f593f26c470a379cf7f5bc6db6b5f1722353e7bf937b8d0d0b3fba911998858", + "sha256:71d9ae8a82203511a6f60ca5a1b9f8ad201cac0fc75038b2dc5fa519589c9288", + "sha256:7e1561626c49cb394268edd00501b289053a652ed762c58e1081224c8d881cec", + "sha256:8f6ce2118a90efa7f62dd38c7dbfffd42f468b180287b748626293bf12ed468f", + "sha256:ae032743794fba4d171b5b67310d69176287b5bf82a21f588282406a79498891", + "sha256:afcaa24e48bb23b3be31e329deb3f1858f1f1df86aea3d70cb5c8578bfe5261c", + "sha256:b70d6e7a332eb0217e7872a73926ad4fdc14f846e85ad6749ad111084e76df25", + "sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156", + "sha256:ce58b2b3734c73e68f0e30e4e725264d4d6be95818ec0a0be4bb6bf9a7e79aa8", + "sha256:d176f392dbbdaacccf15919c77f526edf11a34aece58b55ab58539807b85436f", + "sha256:e20bfa6db17a39c706d24f82df8352488d2943a3b7ce7d4c22579cb89ca8896e", + "sha256:eac3a9a5ef13b332c059772fd40b4b1c3d45a3a2b05e33a361dee48e54a4dad0", + "sha256:eb329f8d8145379bf5dbe722182410fe8863d186e51bf034d2075eb8d85ee25b" + ], + "markers": "python_version >= '3.7'", + "version": "==1.8.0" + }, + "lexid": { + "hashes": [ + "sha256:509a3a4cc926d3dbf22b203b18a4c66c25e6473fb7c0e0d30374533ac28bafe5", + "sha256:5526bb5606fd74c7add23320da5f02805bddd7c77916f2dc1943e6bada8605ed" + ], + "markers": "python_version >= '2.7'", + "version": "==2021.1006" }, "mccabe": { "hashes": [ - "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", - "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" + ], + "markers": "python_version >= '3.6'", + "version": "==0.7.0" + }, + "mock": { + "hashes": [ + "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62", + "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc" ], - "version": "==0.6.1" + "markers": "python_version >= '3.6'", + "version": "==4.0.3" }, "msal": { "hashes": [ - "sha256:5cc93f09523c703d4e00a901cf719ade4faf2c3d14961ba52060ae78d5b25327", - "sha256:c7550f960916a9fb0bed6ebd23b73432415f81eeb3469264ab26c511d53b2652" + "sha256:78344cd4c91d6134a593b5e3e45541e666e37b747ff8a6316c3668dd1e6ab6b2", + "sha256:d2f1c26368ecdc28c8657d457352faa0b81b1845a7b889d8676787721ba86792" ], - "version": "==1.12.0" + "version": "==1.20.0" }, "msal-extensions": { "hashes": [ - "sha256:5523dfa15da88297e90d2e73486c8ef875a17f61ea7b7e2953a300432c2e7861", - "sha256:a530c2d620061822f2ced8e29da301bc928b146970df635c852907423e8ddddc" + "sha256:91e3db9620b822d0ed2b4d1850056a0f133cba04455e62f11612e40f5502f2ee", + "sha256:c676aba56b0cce3783de1b5c5ecfe828db998167875126ca4b47dc6436451354" ], - "version": "==0.3.0" + "version": "==1.0.0" }, "mypy": { "hashes": [ - "sha256:0d2fc8beb99cd88f2d7e20d69131353053fbecea17904ee6f0348759302c52fa", - "sha256:2b216eacca0ec0ee124af9429bfd858d5619a0725ee5f88057e6e076f9eb1a7b", - "sha256:319ee5c248a7c3f94477f92a729b7ab06bf8a6d04447ef3aa8c9ba2aa47c6dcf", - "sha256:3e0c159a7853e3521e3f582adb1f3eac66d0b0639d434278e2867af3a8c62653", - "sha256:5615785d3e2f4f03ab7697983d82c4b98af5c321614f51b8f1034eb9ebe48363", - "sha256:5ff616787122774f510caeb7b980542a7cc2222be3f00837a304ea85cd56e488", - "sha256:6f8425fecd2ba6007e526209bb985ce7f49ed0d2ac1cc1a44f243380a06a84fb", - "sha256:74f5aa50d0866bc6fb8e213441c41e466c86678c800700b87b012ed11c0a13e0", - "sha256:90b6f46dc2181d74f80617deca611925d7e63007cf416397358aa42efb593e07", - "sha256:947126195bfe4709c360e89b40114c6746ae248f04d379dca6f6ab677aa07641", - "sha256:a301da58d566aca05f8f449403c710c50a9860782148332322decf73a603280b", - "sha256:aa9d4901f3ee1a986a3a79fe079ffbf7f999478c281376f48faa31daaa814e86", - "sha256:b9150db14a48a8fa114189bfe49baccdff89da8c6639c2717750c7ae62316738", - "sha256:b95068a3ce3b50332c40e31a955653be245666a4bc7819d3c8898aa9fb9ea496", - "sha256:ca7ad5aed210841f1e77f5f2f7d725b62c78fa77519312042c719ed2ab937876", - "sha256:d16c54b0dffb861dc6318a8730952265876d90c5101085a4bc56913e8521ba19", - "sha256:e0202e37756ed09daf4b0ba64ad2c245d357659e014c3f51d8cd0681ba66940a", - "sha256:e1c84c65ff6d69fb42958ece5b1255394714e0aac4df5ffe151bc4fe19c7600a", - "sha256:e32b7b282c4ed4e378bba8b8dfa08e1cfa6f6574067ef22f86bee5b1039de0c9", - "sha256:e3b8432f8df19e3c11235c4563a7250666dc9aa7cdda58d21b4177b20256ca9f", - "sha256:e497a544391f733eca922fdcb326d19e894789cd4ff61d48b4b195776476c5cf", - "sha256:f5fdf935a46aa20aa937f2478480ebf4be9186e98e49cc3843af9a5795a49a25" + "sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d", + "sha256:14d53cdd4cf93765aa747a7399f0961a365bcddf7855d9cef6306fa41de01c24", + "sha256:175f292f649a3af7082fe36620369ffc4661a71005aa9f8297ea473df5772046", + "sha256:26ae64555d480ad4b32a267d10cab7aec92ff44de35a7cd95b2b7cb8e64ebe3e", + "sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3", + "sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5", + "sha256:58f27ebafe726a8e5ccb58d896451dd9a662a511a3188ff6a8a6a919142ecc20", + "sha256:6389af3e204975d6658de4fb8ac16f58c14e1bacc6142fee86d1b5b26aa52bda", + "sha256:724d36be56444f569c20a629d1d4ee0cb0ad666078d59bb84f8f887952511ca1", + "sha256:75838c649290d83a2b83a88288c1eb60fe7a05b36d46cbea9d22efc790002146", + "sha256:7b35ce03a289480d6544aac85fa3674f493f323d80ea7226410ed065cd46f206", + "sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746", + "sha256:86ebe67adf4d021b28c3f547da6aa2cce660b57f0432617af2cca932d4d378a6", + "sha256:8ee8c2472e96beb1045e9081de8e92f295b89ac10c4109afdf3a23ad6e644f3e", + "sha256:91781eff1f3f2607519c8b0e8518aad8498af1419e8442d5d0afb108059881fc", + "sha256:a692a8e7d07abe5f4b2dd32d731812a0175626a90a223d4b58f10f458747dd8a", + "sha256:a705a93670c8b74769496280d2fe6cd59961506c64f329bb179970ff1d24f9f8", + "sha256:c6e564f035d25c99fd2b863e13049744d96bd1947e3d3d2f16f5828864506763", + "sha256:cebca7fd333f90b61b3ef7f217ff75ce2e287482206ef4a8b18f32b49927b1a2", + "sha256:d6af646bd46f10d53834a8e8983e130e47d8ab2d4b7a97363e35b24e1d588947", + "sha256:e7aeaa763c7ab86d5b66ff27f68493d672e44c8099af636d433a7f3fa5596d40", + "sha256:eaa97b9ddd1dd9901a22a879491dbb951b5dec75c3b90032e2baa7336777363b", + "sha256:eb7a068e503be3543c4bd329c994103874fa543c1727ba5288393c21d912d795", + "sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c" ], "index": "pypi", - "version": "==0.800" + "version": "==0.982" }, "mypy-extensions": { "hashes": [ @@ -390,177 +910,178 @@ ], "version": "==0.4.3" }, - "nodeenv": { + "outcome": { "hashes": [ - "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b", - "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7" + "sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672", + "sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5" ], - "version": "==1.6.0" + "markers": "python_version >= '3.7'", + "version": "==1.2.0" }, "packaging": { "hashes": [ - "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7", - "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14" + "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", + "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" ], "markers": "python_version >= '3.6'", - "version": "==21.0" + "version": "==21.3" }, - "platformdirs": { + "pathlib2": { "hashes": [ - "sha256:0b9547541f599d3d242078ae60b927b3e453f0ad52f58b4d4bc3be86aed3ec41", - "sha256:3b00d081227d9037bbbca521a5787796b5ef5000faea1e43fd76f1d44b06fcfa" + "sha256:5266a0fd000452f1b3467d782f079a4343c63aaa119221fbdc4e39577489ca5b", + "sha256:9fe0edad898b83c0c3e199c842b27ed216645d2e177757b2dd67384d4113c641" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==2.0.2" + "version": "==2.3.7.post1" }, - "pluggy": { + "pep517": { "hashes": [ - "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", - "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" + "sha256:4ba4446d80aed5b5eac6509ade100bff3e7943a8489de249654a5ae9b33ee35b", + "sha256:ae69927c5c172be1add9203726d4b84cf3ebad1edcd5f71fcdc746e66e829f59" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.13.1" + "markers": "python_version >= '3.6'", + "version": "==0.13.0" }, - "portalocker": { + "platformdirs": { "hashes": [ - "sha256:34cb36c618d88bcd9079beb36dcdc1848a3e3d92ac4eac59055bdeafc39f9d4a", - "sha256:6d6f5de5a3e68c4dd65a98ec1babb26d28ccc5e770e07b672d65d5a35e4b2d8a" + "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7", + "sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10" ], - "markers": "platform_system == 'Windows'", - "version": "==1.7.1" + "markers": "python_version >= '3.7'", + "version": "==2.5.4" }, - "pre-commit": { + "pluggy": { "hashes": [ - "sha256:764972c60693dc668ba8e86eb29654ec3144501310f7198742a767bec385a378", - "sha256:b679d0fddd5b9d6d98783ae5f10fd0c4c59954f375b70a58cbe1ce9bcf9809a4" + "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", + "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" ], - "index": "pypi", - "version": "==2.13.0" + "markers": "python_version >= '3.6'", + "version": "==1.0.0" + }, + "portalocker": { + "hashes": [ + "sha256:102ed1f2badd8dec9af3d732ef70e94b215b85ba45a8d7ff3c0003f19b442f4e", + "sha256:964f6830fb42a74b5d32bce99ed37d8308c1d7d44ddf18f3dd89f4680de97b39" + ], + "markers": "python_version >= '3.5' and platform_system == 'Windows'", + "version": "==2.6.0" }, "py": { "hashes": [ - "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3", - "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a" + "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", + "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.10.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==1.11.0" }, "pycparser": { "hashes": [ - "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", - "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" + "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", + "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.20" + "version": "==2.21" }, "pyjwt": { "extras": [ "crypto" ], "hashes": [ - "sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1", - "sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130" + "sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd", + "sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14" ], - "markers": "python_version >= '3.6'", - "version": "==2.1.0" + "markers": "python_version >= '3.7'", + "version": "==2.6.0" }, "pylint": { "hashes": [ - "sha256:209d712ec870a0182df034ae19f347e725c1e615b2269519ab58a35b3fcbbe7a", - "sha256:bd38914c7731cdc518634a8d3c5585951302b6e2b6de60fbb3f7a0220e21eeee" + "sha256:5441e9294335d354b7bad57c1044e5bd7cce25c433475d76b440e53452fa5cb8", + "sha256:629cf1dbdfb6609d7e7a45815a8bb59300e34aa35783b5ac563acaca2c4022e9" ], "index": "pypi", - "version": "==2.7.4" + "version": "==2.15.4" }, "pyparsing": { "hashes": [ - "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", - "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" + "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", + "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.4.7" + "markers": "python_full_version >= '3.6.8'", + "version": "==3.0.9" }, "pytest": { "hashes": [ - "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b", - "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890" + "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7", + "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39" + ], + "index": "pypi", + "version": "==7.1.3" + }, + "pytest-asyncio": { + "hashes": [ + "sha256:7a97e37cfe1ed296e2e84941384bdd37c376453912d397ed39293e0916f521fa", + "sha256:ac4ebf3b6207259750bc32f4c1d8fcd7e79739edbc67ad0c58dd150b1d072fed" + ], + "index": "pypi", + "version": "==0.19.0" + }, + "pytest-cov": { + "hashes": [ + "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b", + "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470" ], "index": "pypi", - "version": "==6.2.4" + "version": "==4.0.0" }, - "pytoml": { + "pytest-mock": { "hashes": [ - "sha256:57a21e6347049f73bfb62011ff34cd72774c031b9828cb628a752225136dfc33", - "sha256:8eecf7c8d0adcff3b375b09fe403407aa9b645c499e5ab8cac670ac4a35f61e7" + "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b", + "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f" ], - "version": "==0.1.21" + "index": "pypi", + "version": "==3.10.0" + }, + "pytest-trio": { + "hashes": [ + "sha256:c01b741819aec2c419555f28944e132d3c711dae1e673d63260809bf92c30c31" + ], + "index": "pypi", + "version": "==0.7.0" }, "pywin32": { "hashes": [ - "sha256:595d397df65f1b2e0beaca63a883ae6d8b6df1cdea85c16ae85f6d2e648133fe", - "sha256:87604a4087434cd814ad8973bd47d6524bd1fa9e971ce428e76b62a5e0860fdf", - "sha256:88981dd3cfb07432625b180f49bf4e179fb8cbb5704cd512e38dd63636af7a17", - "sha256:8c9d33968aa7fcddf44e47750e18f3d034c3e443a707688a008a2e52bbef7e96", - "sha256:93367c96e3a76dfe5003d8291ae16454ca7d84bb24d721e0b74a07610b7be4a7", - "sha256:9635df6998a70282bd36e7ac2a5cef9ead1627b0a63b17c731312c7a0daebb72", - "sha256:98f62a3f60aa64894a290fb7494bfa0bfa0a199e9e052e1ac293b2ad3cd2818b", - "sha256:c866f04a182a8cb9b7855de065113bbd2e40524f570db73ef1ee99ff0a5cc2f0", - "sha256:dafa18e95bf2a92f298fe9c582b0e205aca45c55f989937c52c454ce65b93c78", - "sha256:fb3b4933e0382ba49305cc6cd3fb18525df7fd96aa434de19ce0878133bf8e4a" + "sha256:109f98980bfb27e78f4df8a51a8198e10b0f347257d1e265bb1a32993d0c973d", + "sha256:13362cc5aa93c2beaf489c9c9017c793722aeb56d3e5166dadd5ef82da021fe1", + "sha256:19ca459cd2e66c0e2cc9a09d589f71d827f26d47fe4a9d09175f6aa0256b51c2", + "sha256:326f42ab4cfff56e77e3e595aeaf6c216712bbdd91e464d167c6434b28d65990", + "sha256:421f6cd86e84bbb696d54563c48014b12a23ef95a14e0bdba526be756d89f116", + "sha256:48d8b1659284f3c17b68587af047d110d8c44837736b8932c034091683e05863", + "sha256:4ecd404b2c6eceaca52f8b2e3e91b2187850a1ad3f8b746d0796a98b4cea04db", + "sha256:50768c6b7c3f0b38b7fb14dd4104da93ebced5f1a50dc0e834594bff6fbe1271", + "sha256:56d7a9c6e1a6835f521788f53b5af7912090674bb84ef5611663ee1595860fc7", + "sha256:73e819c6bed89f44ff1d690498c0a811948f73777e5f97c494c152b850fad478", + "sha256:742eb905ce2187133a29365b428e6c3b9001d79accdc30aa8969afba1d8470f4", + "sha256:9d968c677ac4d5cbdaa62fd3014ab241718e619d8e36ef8e11fb930515a1e918", + "sha256:9dd98384da775afa009bc04863426cb30596fd78c6f8e4e2e5bbf4edf8029504", + "sha256:a55db448124d1c1484df22fa8bbcbc45c64da5e6eae74ab095b9ea62e6d00496" ], "markers": "platform_system == 'Windows'", - "version": "==301" - }, - "pyyaml": { - "hashes": [ - "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", - "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", - "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", - "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", - "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", - "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", - "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", - "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", - "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", - "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", - "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", - "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", - "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", - "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", - "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", - "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", - "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", - "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", - "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", - "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", - "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", - "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", - "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", - "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", - "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", - "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", - "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", - "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", - "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==5.4.1" + "version": "==305" }, "requests": { "hashes": [ - "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", - "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" + "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", + "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" ], - "index": "pypi", - "version": "==2.26.0" + "markers": "python_version >= '3.7' and python_version < '4'", + "version": "==2.28.1" }, - "responses": { + "setuptools": { "hashes": [ - "sha256:0474ce3c897fbcc1aef286117c93499882d5c440f06a805947e4b1cb5ab3d474", - "sha256:f83613479a021e233e82d52ffb3e2e0e2836d24b0cc88a0fa31978789f78d0e5" + "sha256:d0b9a8433464d5800cbe05094acf5c6d52a91bfac9b52bcfc4d41382be5d5d31", + "sha256:e197a19aa8ec9722928f2206f8de752def0e4c9fc6953527360d1c36d94ddb2f" ], "index": "pypi", - "version": "==0.10.12" + "version": "==65.5.1" }, "six": { "hashes": [ @@ -570,86 +1091,169 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.16.0" }, + "sniffio": { + "hashes": [ + "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101", + "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384" + ], + "markers": "python_version >= '3.7'", + "version": "==1.3.0" + }, + "sortedcontainers": { + "hashes": [ + "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", + "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0" + ], + "version": "==2.4.0" + }, "toml": { "hashes": [ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "index": "pypi", "version": "==0.10.2" }, - "typed-ast": { - "hashes": [ - "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace", - "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff", - "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266", - "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528", - "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6", - "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808", - "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4", - "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363", - "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341", - "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04", - "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41", - "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e", - "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3", - "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899", - "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805", - "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c", - "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c", - "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39", - "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a", - "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3", - "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7", - "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f", - "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075", - "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0", - "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40", - "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428", - "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927", - "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3", - "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f", - "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65" - ], - "version": "==1.4.3" + "tomli": { + "hashes": [ + "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + ], + "markers": "python_version < '3.11'", + "version": "==2.0.1" }, - "typing-extensions": { + "tomli-w": { "hashes": [ - "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", - "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", - "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" + "sha256:9f2a07e8be30a0729e533ec968016807069991ae2fd921a78d42f429ae5f4463", + "sha256:f463434305e0336248cac9c2dc8076b707d8a12d019dd349f5c1e382dd1ae1b9" ], - "version": "==3.10.0.0" + "markers": "python_version >= '3.7'", + "version": "==1.0.0" }, - "urllib3": { + "tomlkit": { + "hashes": [ + "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b", + "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73" + ], + "markers": "python_version >= '3.6'", + "version": "==0.11.6" + }, + "trio": { "hashes": [ - "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4", - "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f" + "sha256:ce68f1c5400a47b137c5a4de72c7c901bd4e7a24fbdebfe9b41de8c6c04eaacf", + "sha256:f1dd0780a89bfc880c7c7994519cb53f62aacb2c25ff487001c0052bd721cdf0" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.26.6" + "index": "pypi", + "version": "==0.22.0" }, - "virtualenv": { + "types-cryptography": { "hashes": [ - "sha256:51df5d8a2fad5d1b13e088ff38a433475768ff61f202356bb9812c454c20ae45", - "sha256:e4fc84337dce37ba34ef520bf2d4392b392999dbe47df992870dc23230f6b758" + "sha256:b9f8aa93d9e4d7ff920961cdce3dc7cca479946890924d68356b66f15f4f35e3", + "sha256:f108e7a7161eedd9fe391e1a8ae5a98d40c1da2f0418bc0812a9119990272314" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==20.6.0" + "version": "==3.3.23.1" + }, + "types-python-dateutil": { + "hashes": [ + "sha256:351a8ca9afd4aea662f87c1724d2e1ae59f9f5f99691be3b3b11d2393cd3aaa1", + "sha256:722a55be8e2eeff749c3e166e7895b0e2f4d29ab4921c0cff27aa6b997d7ee2e" + ], + "index": "pypi", + "version": "==2.8.19.4" + }, + "typing-extensions": { + "hashes": [ + "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa", + "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e" + ], + "markers": "python_version >= '3.7'", + "version": "==4.4.0" + }, + "urllib3": { + "hashes": [ + "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e", + "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", + "version": "==1.26.12" }, "wrapt": { "hashes": [ - "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7" + "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3", + "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b", + "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4", + "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2", + "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656", + "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3", + "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff", + "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310", + "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a", + "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57", + "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069", + "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383", + "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe", + "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87", + "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d", + "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b", + "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907", + "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f", + "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0", + "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28", + "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1", + "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853", + "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc", + "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3", + "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3", + "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164", + "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1", + "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c", + "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1", + "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7", + "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1", + "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320", + "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed", + "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1", + "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248", + "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c", + "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456", + "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77", + "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef", + "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1", + "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7", + "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86", + "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4", + "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d", + "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d", + "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8", + "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5", + "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471", + "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00", + "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68", + "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3", + "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d", + "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735", + "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d", + "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569", + "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7", + "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59", + "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5", + "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb", + "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b", + "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f", + "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462", + "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015", + "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af" ], - "version": "==1.12.1" + "markers": "python_version < '3.11'", + "version": "==1.14.1" }, "yapf": { "hashes": [ - "sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427", - "sha256:3abf61ba67cf603069710d30acbc88cfe565d907e16ad81429ae90ce9651e0c9" + "sha256:8fea849025584e486fd06d6ba2bed717f396080fd3cc236ba10cb97c4c51cf32", + "sha256:a3f5085d37ef7e3e004c4ba9f9b3e40c54ff1901cd111f05145ae313a7c67d1b" ], "index": "pypi", - "version": "==0.30.0" + "version": "==0.32.0" } } } diff --git a/README.md b/README.md index 5ae5ceb0..3fd071ca 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![CI Actions Status](https://github.com/microsoftgraph/msgraph-sdk-python-core/workflows/msgraph-sdk-python-core/badge.svg)](https://github.com/microsoftgraph/msgraph-sdk-python-core/actions) - +[![Downloads](https://pepy.tech/badge/msgraph-core)](https://pepy.tech/project/msgraph-core) ## Microsoft Graph Core Python Client Library (preview). The Microsoft Graph Core Python client library is a lightweight wrapper around the Microsoft Graph API. It provides functionality to create clients with desired configuration and middleware. diff --git a/SECURTITY.md b/SECURTITY.md new file mode 100644 index 00000000..869fdfe2 --- /dev/null +++ b/SECURTITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 00000000..eaf439ae --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,25 @@ +# TODO: The maintainer of this repo has not yet edited this file + +**REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? + +- **No CSS support:** Fill out this template with information about how to file issues and get help. +- **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps. +- **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide. + +*Then remove this first heading from this SUPPORT.MD file before publishing your repo.* + +# Support + +## How to file issues and get help + +This project uses GitHub Issues to track bugs and feature requests. Please search the existing +issues before filing new issues to avoid duplicates. For new issues, file your bug or +feature request as a new Issue. + +For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE +FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER +CHANNEL. WHERE WILL YOU HELP PEOPLE?**. + +## Microsoft Support Policy + +Support for this **PROJECT or PRODUCT** is limited to the resources listed above. diff --git a/msgraph/core/__init__.py b/msgraph/core/__init__.py deleted file mode 100644 index e726d590..00000000 --- a/msgraph/core/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -from ._client_factory import HTTPClientFactory -from ._constants import SDK_VERSION -from ._enums import APIVersion, NationalClouds -from ._graph_client import GraphClient - -__version__ = SDK_VERSION diff --git a/msgraph/core/_client_factory.py b/msgraph/core/_client_factory.py deleted file mode 100644 index a316fa59..00000000 --- a/msgraph/core/_client_factory.py +++ /dev/null @@ -1,94 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import functools - -from requests import Session - -from ._constants import DEFAULT_CONNECTION_TIMEOUT, DEFAULT_REQUEST_TIMEOUT -from ._enums import APIVersion, NationalClouds -from .middleware.abc_token_credential import TokenCredential -from .middleware.authorization import AuthorizationHandler -from .middleware.middleware import BaseMiddleware, MiddlewarePipeline -from .middleware.retry import RetryHandler -from .middleware.telemetry import TelemetryHandler - - -class HTTPClientFactory: - """Constructs native HTTP Client(session) instances configured with either custom or default - pipeline of middleware. - - :func: Class constructor accepts a user provided session object and kwargs to configure the - request handling behaviour of the client - :keyword enum api_version: The Microsoft Graph API version to be used, for example - `APIVersion.v1` (default). This value is used in setting the base url for all requests for - that session. - :class:`~msgraphcore.enums.APIVersion` defines valid API versions. - :keyword enum cloud: a supported Microsoft Graph cloud endpoint. - Defaults to `NationalClouds.Global` - :class:`~msgraphcore.enums.NationalClouds` defines supported sovereign clouds. - :keyword tuple timeout: Default connection and read timeout values for all session requests. - Specify a tuple in the form of Tuple(connect_timeout, read_timeout) if you would like to set - the values separately. If you specify a single value for the timeout, the timeout value will - be applied to both the connect and the read timeouts. - :keyword obj session: A custom Session instance from the python requests library - """ - def __init__(self, **kwargs): - """Class constructor that accepts a user provided session object and kwargs - to configure the request handling behaviour of the client""" - self.api_version = kwargs.get('api_version', APIVersion.v1) - self.endpoint = kwargs.get('cloud', NationalClouds.Global) - self.timeout = kwargs.get('timeout', (DEFAULT_CONNECTION_TIMEOUT, DEFAULT_REQUEST_TIMEOUT)) - self.session = kwargs.get('session', Session()) - - self._set_base_url() - self._set_default_timeout() - - def create_with_default_middleware(self, credential: TokenCredential, **kwargs) -> Session: - """Applies the default middleware chain to the HTTP Client - - :param credential: TokenCredential used to acquire an access token for the Microsoft - Graph API. Created through one of the credential classes from `azure.identity` - """ - middleware = [ - AuthorizationHandler(credential, **kwargs), - RetryHandler(**kwargs), - TelemetryHandler(), - ] - self._register(middleware) - return self.session - - def create_with_custom_middleware(self, middleware: [BaseMiddleware]) -> Session: - """Applies a custom middleware chain to the HTTP Client - - :param list middleware: Custom middleware(HTTPAdapter) list that will be used to create - a middleware pipeline. The middleware should be arranged in the order in which they will - modify the request. - """ - if not middleware: - raise ValueError("Please provide a list of custom middleware") - self._register(middleware) - return self.session - - def _set_base_url(self): - """Helper method to set the base url""" - base_url = self.endpoint + '/' + self.api_version - self.session.base_url = base_url - - def _set_default_timeout(self): - """Helper method to set a default timeout for the session - Reference: https://github.com/psf/requests/issues/2011 - """ - self.session.request = functools.partial(self.session.request, timeout=self.timeout) - - def _register(self, middleware: [BaseMiddleware]) -> None: - """ - Helper method that constructs a middleware_pipeline with the specified middleware - """ - if middleware: - middleware_pipeline = MiddlewarePipeline() - for ware in middleware: - middleware_pipeline.add_middleware(ware) - - self.session.mount('https://', middleware_pipeline) diff --git a/msgraph/core/_graph_client.py b/msgraph/core/_graph_client.py deleted file mode 100644 index 8b70c379..00000000 --- a/msgraph/core/_graph_client.py +++ /dev/null @@ -1,176 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import json - -from requests import Request, Session - -from ._client_factory import HTTPClientFactory -from .middleware.request_context import RequestContext - -# These are middleware options that can be configured per request. -# Supports options for default middleware as well as custom middleware. -supported_options = [ - # Auth Options - 'scopes', - - # Retry Options - 'max_retries', - 'retry_backoff_factor', - 'retry_backoff_max', - 'retry_time_limit', - 'retry_on_status_codes', - - # Custom middleware options - 'custom_option', -] - - -def collect_options(func): - """Collect middleware options into a middleware control dict and pass it as a header""" - def wrapper(*args, **kwargs): - - middleware_control = dict() - - for option in supported_options: - value = kwargs.pop(option, None) - if value: - middleware_control.update({option: value}) - - if 'headers' in kwargs.keys(): - kwargs['headers'].update({'middleware_control': json.dumps(middleware_control)}) - else: - kwargs['headers'] = {'middleware_control': json.dumps(middleware_control)} - - return func(*args, **kwargs) - - return wrapper - - -class GraphClient: - """Constructs a custom HTTPClient to be used for requests against Microsoft Graph - - :keyword credential: TokenCredential used to acquire an access token for the Microsoft - Graph API. Created through one of the credential classes from `azure.identity` - :keyword list middleware: Custom middleware(HTTPAdapter) list that will be used to create - a middleware pipeline. The middleware should be arranged in the order in which they will - modify the request. - :keyword enum api_version: The Microsoft Graph API version to be used, for example - `APIVersion.v1` (default). This value is used in setting the base url for all requests for - that session. - :class:`~msgraphcore.enums.APIVersion` defines valid API versions. - :keyword enum cloud: a supported Microsoft Graph cloud endpoint. - Defaults to `NationalClouds.Global` - :class:`~msgraphcore.enums.NationalClouds` defines supported sovereign clouds. - :keyword tuple timeout: Default connection and read timeout values for all session requests. - Specify a tuple in the form of Tuple(connect_timeout, read_timeout) if you would like to set - the values separately. If you specify a single value for the timeout, the timeout value will - be applied to both the connect and the read timeouts. - :keyword obj session: A custom Session instance from the python requests library - """ - __instance = None - - def __new__(cls, *args, **kwargs): - if not GraphClient.__instance: - GraphClient.__instance = object.__new__(cls) - return GraphClient.__instance - - def __init__(self, **kwargs): - """ - Class constructor that accepts a session object and kwargs to - be passed to the HTTPClientFactory - """ - self.graph_session = self.get_graph_session(**kwargs) - - @collect_options - def get(self, url: str, **kwargs): - r"""Sends a GET request. Returns :class:`Response` object. - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - return self.graph_session.get(self._graph_url(url), **kwargs) - - def options(self, url, **kwargs): - r"""Sends a OPTIONS request. Returns :class:`Response` object. - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - return self.graph_session.options(self._graph_url(url), **kwargs) - - def head(self, url, **kwargs): - r"""Sends a HEAD request. Returns :class:`Response` object. - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - return self.graph_session.head(self._graph_url(url), **kwargs) - - def post(self, url, data=None, json=None, **kwargs): - r"""Sends a POST request. Returns :class:`Response` object. - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param json: (optional) json to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - return self.graph_session.post(self._graph_url(url), data=data, json=json, **kwargs) - - def put(self, url, data=None, **kwargs): - r"""Sends a PUT request. Returns :class:`Response` object. - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - return self.graph_session.put(self._graph_url(url), data=data, **kwargs) - - def patch(self, url, data=None, **kwargs): - r"""Sends a PATCH request. Returns :class:`Response` object. - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - return self.graph_session.patch(self._graph_url(url), data=data, **kwargs) - - def delete(self, url, **kwargs): - r"""Sends a DELETE request. Returns :class:`Response` object. - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - return self.graph_session.delete(self._graph_url(url), **kwargs) - - def _graph_url(self, url: str) -> str: - """Appends BASE_URL to user provided path - :param url: user provided path - :return: graph_url - """ - return self.graph_session.base_url + url if (url[0] == '/') else url - - @staticmethod - def get_graph_session(**kwargs): - """Method to always return a single instance of a HTTP Client""" - - credential = kwargs.pop('credential', None) - middleware = kwargs.pop('middleware', None) - - if credential and middleware: - raise ValueError( - "Invalid parameters! Both TokenCredential and middleware cannot be passed" - ) - if not credential and not middleware: - raise ValueError("Invalid parameters!. Missing TokenCredential or middleware") - - if credential: - return HTTPClientFactory(**kwargs).create_with_default_middleware(credential, **kwargs) - return HTTPClientFactory(**kwargs).create_with_custom_middleware(middleware) diff --git a/msgraph/core/middleware/__init__.py b/msgraph/core/middleware/__init__.py deleted file mode 100644 index b74cfa3b..00000000 --- a/msgraph/core/middleware/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ diff --git a/msgraph/core/middleware/abc_token_credential.py b/msgraph/core/middleware/abc_token_credential.py deleted file mode 100644 index 724394b9..00000000 --- a/msgraph/core/middleware/abc_token_credential.py +++ /dev/null @@ -1,11 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -from abc import ABC, abstractmethod - - -class TokenCredential(ABC): - @abstractmethod - def get_token(self, *scopes, **kwargs): - pass diff --git a/msgraph/core/middleware/authorization.py b/msgraph/core/middleware/authorization.py deleted file mode 100644 index 43884d67..00000000 --- a/msgraph/core/middleware/authorization.py +++ /dev/null @@ -1,36 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -from .._enums import FeatureUsageFlag -from .abc_token_credential import TokenCredential -from .middleware import BaseMiddleware - - -class AuthorizationHandler(BaseMiddleware): - def __init__(self, credential: TokenCredential, **kwargs): - super().__init__() - self.credential = credential - self.scopes = kwargs.get("scopes", ['.default']) - self.retry_count = 0 - - def send(self, request, **kwargs): - context = request.context - request.headers.update( - {'Authorization': 'Bearer {}'.format(self._get_access_token(context))} - ) - context.set_feature_usage = FeatureUsageFlag.AUTH_HANDLER_ENABLED - response = super().send(request, **kwargs) - - # Token might have expired just before transmission, retry the request one more time - if response.status_code == 401 and self.retry_count < 2: - self.retry_count += 1 - return self.send(request, **kwargs) - return response - - def _get_access_token(self, context): - return self.credential.get_token(*self.get_scopes(context))[0] - - def get_scopes(self, context): - # Checks if there are any options for this middleware - return context.middleware_control.get('scopes', self.scopes) diff --git a/msgraph/core/middleware/middleware.py b/msgraph/core/middleware/middleware.py deleted file mode 100644 index ac31134f..00000000 --- a/msgraph/core/middleware/middleware.py +++ /dev/null @@ -1,66 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import json -import ssl - -from requests.adapters import HTTPAdapter -from urllib3 import PoolManager - -from .request_context import RequestContext - - -class MiddlewarePipeline(HTTPAdapter): - """MiddlewarePipeline, entry point of middleware - The pipeline is implemented as a linked-list, read more about - it here https://buffered.dev/middleware-python-requests/ - """ - def __init__(self): - super().__init__() - self._current_middleware = None - self._first_middleware = None - self.poolmanager = PoolManager(ssl_version=ssl.PROTOCOL_TLSv1_2) - - def add_middleware(self, middleware): - if self._middleware_present(): - self._current_middleware.next = middleware - self._current_middleware = middleware - else: - self._first_middleware = middleware - self._current_middleware = self._first_middleware - - def send(self, request, **kwargs): - - middleware_control_json = request.headers.pop('middleware_control', None) - if middleware_control_json: - middleware_control = json.loads(middleware_control_json) - else: - middleware_control = dict() - - request.context = RequestContext(middleware_control, request.headers) - - if self._middleware_present(): - return self._first_middleware.send(request, **kwargs) - # No middleware in pipeline, call superclass' send - return super().send(request, **kwargs) - - def _middleware_present(self): - return self._current_middleware - - -class BaseMiddleware(HTTPAdapter): - """Base class for middleware - - Handles moving a Request to the next middleware in the pipeline. - If the current middleware is the last one in the pipeline, it - makes a network request - """ - def __init__(self): - super().__init__() - self.next = None - - def send(self, request, **kwargs): - if self.next is None: - return super().send(request, **kwargs) - return self.next.send(request, **kwargs) diff --git a/msgraph/core/middleware/retry.py b/msgraph/core/middleware/retry.py deleted file mode 100644 index 520fd8ea..00000000 --- a/msgraph/core/middleware/retry.py +++ /dev/null @@ -1,221 +0,0 @@ -import datetime -import random -import time -from email.utils import parsedate_to_datetime - -from .._enums import FeatureUsageFlag -from .middleware import BaseMiddleware - - -class RetryHandler(BaseMiddleware): - """ - TransportAdapter that allows us to specify the retry policy for all requests - - Retry configuration. - - :param int max_retries: - Maximum number of retries to allow. Takes precedence over other counts. - Set to ``0`` to fail on the first retry. - :param iterable retry_on_status_codes: - A set of integer HTTP status codes that we should force a retry on. - A retry is initiated if the request method is in ``allowed_methods`` - and the response status code is in ``RETRY STATUS CODES``. - :param float retry_backoff_factor: - A backoff factor to apply between attempts after the second try - (most errors are resolved immediately by a second try without a - delay). - The request will sleep for:: - {backoff factor} * (2 ** ({retry number} - 1)) - seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep - for [0.0s, 0.2s, 0.4s, ...] between retries. It will never be longer - than :attr:`RetryHandler.MAXIMUM_BACKOFF`. - By default, backoff is set to 0.5. - :param int retry_time_limit: - The maximum cumulative time in seconds that total retries should take. - The cumulative retry time and retry-after value for each request retry - will be evaluated against this value; if the cumulative retry time plus - the retry-after value is greater than the retry_time_limit, the failed - response will be immediately returned, else the request retry continues. - """ - - DEFAULT_MAX_RETRIES = 3 - MAX_RETRIES = 10 - DEFAULT_DELAY = 3 - MAX_DELAY = 180 - DEFAULT_BACKOFF_FACTOR = 0.5 - MAXIMUM_BACKOFF = 120 - _DEFAULT_RETRY_STATUS_CODES = {429, 503, 504} - - def __init__(self, **kwargs): - super().__init__() - self.max_retries: int = min( - kwargs.pop('max_retries', self.DEFAULT_MAX_RETRIES), self.MAX_RETRIES - ) - self.backoff_factor: float = kwargs.pop('retry_backoff_factor', self.DEFAULT_BACKOFF_FACTOR) - self.backoff_max: int = kwargs.pop('retry_backoff_max', self.MAXIMUM_BACKOFF) - self.timeout: int = kwargs.pop('retry_time_limit', self.MAX_DELAY) - - status_codes: [int] = kwargs.pop('retry_on_status_codes', []) - - self._retry_on_status_codes: set = set(status_codes) | self._DEFAULT_RETRY_STATUS_CODES - self._allowed_methods: set = frozenset( - ['HEAD', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'] - ) - self._respect_retry_after_header: bool = True - - @classmethod - def disable_retries(cls): - """ - Disable retries by setting retry_total to zero. - retry_total takes precedence over all other counts. - """ - return cls(max_retries=0) - - def get_retry_options(self, middleware_control): - """ - Check if request specific configs have been passed and override any session defaults - Then configure retry settings into the form of a dict. - """ - if middleware_control: - return { - 'total': - min(middleware_control.get('max_retries', self.max_retries), self.MAX_RETRIES), - 'backoff': - middleware_control.get('retry_backoff_factor', self.backoff_factor), - 'max_backoff': - middleware_control.get('retry_backoff_max', self.backoff_max), - 'timeout': - middleware_control.get('retry_time_limit', self.timeout), - 'retry_codes': - set(middleware_control.get('retry_on_status_codes', self._retry_on_status_codes)) - | set(self._DEFAULT_RETRY_STATUS_CODES), - 'methods': - self._allowed_methods, - } - return { - 'total': self.max_retries, - 'backoff': self.backoff_factor, - 'max_backoff': self.backoff_max, - 'timeout': self.timeout, - 'retry_codes': self._retry_on_status_codes, - 'methods': self._allowed_methods, - } - - def send(self, request, **kwargs): - """ - Sends the http request object to the next middleware or retries the request if necessary. - """ - retry_options = self.get_retry_options(request.context.middleware_control) - absolute_time_limit = min(retry_options['timeout'], self.MAX_DELAY) - response = None - retry_count = 0 - retry_valid = True - - while retry_valid: - start_time = time.time() - if retry_count > 0: - request.headers.update({'retry-attempt': '{}'.format(retry_count)}) - response = super().send(request, **kwargs) - # Check if the request needs to be retried based on the response method - # and status code - if self.should_retry(retry_options, response): - # check that max retries has not been hit - retry_valid = self.check_retry_valid(retry_options, retry_count) - - # Get the delay time between retries - delay = self.get_delay_time(retry_options, retry_count, response) - - if retry_valid and delay < absolute_time_limit: - time.sleep(delay) - end_time = time.time() - absolute_time_limit -= (end_time - start_time) - # increment the count for retries - retry_count += 1 - - continue - break - return response - - def should_retry(self, retry_options, response): - """ - Determines whether the request should be retried - Checks if the request method is in allowed methods - Checks if the response status code is in retryable status codes. - """ - if not self._is_method_retryable(retry_options, response.request): - return False - if not self._is_request_payload_buffered(response): - return False - return retry_options['total'] and response.status_code in retry_options['retry_codes'] - - def _is_method_retryable(self, retry_options, request): - """ - Checks if a given request should be retried upon, depending on - whether the HTTP method is in the set of allowed methods - """ - if request.method.upper() not in retry_options['methods']: - return False - return True - - def _is_request_payload_buffered(self, response): - """ - Checks if the request payload is buffered/rewindable. - Payloads with forward only streams will return false and have the responses - returned without any retry attempt. - """ - if response.request.method.upper() in frozenset(['HEAD', 'GET', 'DELETE', 'OPTIONS']): - return True - if response.request.headers['Content-Type'] == "application/octet-stream": - return False - return True - - def check_retry_valid(self, retry_options, retry_count): - """ - Check that the max retries limit has not been hit - """ - if retry_count < retry_options['total']: - return True - return False - - def get_delay_time(self, retry_options, retry_count, response=None): - """ - Get the time in seconds to delay between retry attempts. - Respects a retry-after header in the response if provided - If no retry-after response header, it defaults to exponential backoff - """ - retry_after = self._get_retry_after(response) - if retry_after: - return retry_after - return self._get_delay_time_exp_backoff(retry_options, retry_count) - - def _get_delay_time_exp_backoff(self, retry_options, retry_count): - """ - Get time in seconds to delay between retry attempts based on an exponential - backoff value. - """ - exp_backoff_value = retry_options['backoff'] * +(2**(retry_count - 1)) - backoff_value = exp_backoff_value + (random.randint(0, 1000) / 1000) - - backoff = min(retry_options['max_backoff'], backoff_value) - return backoff - - def _get_retry_after(self, response): - """ - Check if retry-after is specified in the response header and get the value - """ - retry_after = response.headers.get("retry-after") - if retry_after: - return self._parse_retry_after(retry_after) - return None - - def _parse_retry_after(self, retry_after): - """ - Helper to parse Retry-After and get value in seconds. - """ - try: - delay = int(retry_after) - except ValueError: - # Not an integer? Try HTTP date - retry_date = parsedate_to_datetime(retry_after) - delay = (retry_date - datetime.datetime.now(retry_date.tzinfo)).total_seconds() - return max(0, delay) diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 53c16932..00000000 --- a/mypy.ini +++ /dev/null @@ -1,3 +0,0 @@ -[mypy] -files=msgraphcore -ignore_missing_imports=true diff --git a/pyproject.toml b/pyproject.toml index 3fdbaf06..22387f4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,68 @@ [build-system] -requires = ["flit_core >=2,<4", "requests >= 2.23.0"] -build-backend = "flit_core.buildapi" - -[tool.flit.metadata] -module = "msgraph" -author = "Microsoft" -author-email = "graphtooling+python@microsoft.com" -home-page = "https://github.com/microsoftgraph/msgraph-sdk-python-core" -dist-name="msgraph-core" -requires-python=">=3.5" -classifiers = [ "License :: OSI Approved :: MIT License",] -description-file = "README.md" +requires = ["setuptools>=65.5.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "msgraph-core" +version = "0.2.2" +authors = [{name = "Microsoft", email = "graphtooling+python@microsoft.com"}] +description = "Core component of the Microsoft Graph Python SDK" +dependencies = [ + "microsoft-kiota-abstractions >=0.1.0", + "microsoft-kiota-http >=0.1.0", + "httpx[http2] >=0.23.0", +] +requires-python = ">=3.6" +license = {file = "LICENSE"} +readme = "README.md" +keywords = ["msgraph", "openAPI", "Microsoft", "Graph"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "License :: OSI Approved :: MIT License", +] + +[project.optional-dependencies] +dev = ["yapf", "bumpver", "isort", "pylint", "pytest", "mypy"] + +[project.urls] +homepage = "https://github.com/microsoftgraph/msgraph-sdk-python-core#readme" +repository = "https://github.com/microsoftgraph/msgraph-sdk-python-core" +documentation = "https://github.com/microsoftgraph/msgraph-sdk-python-core/docs" + +[tool.mypy] +warn_unused_configs = true +files = "src" +ignore_missing_imports = true + +[tool.yapf] +based_on_style = "pep8" +dedent_closing_brackets = true +each_dict_entry_on_separate_line = true +column_limit = 100 + +[tool.isort] +profile = "hug" + +[tool.pytest.ini_options] +pythonpath = [ + "src" +] + +[tool.bumpver] +current_version = "0.2.2" +version_pattern = "MAJOR.MINOR.PATCH" +commit_message = "bump version {old_version} -> {new_version}" +commit = true +tag = false +push = false + +[tool.bumpver.file_patterns] +"pyproject.toml" = ['current_version = "{version}"', 'version = "{version}"'] +"src/msgraph_core/__init__.py" = ["{version}"] diff --git a/msgraph/__init__.py b/src/msgraph_core/__init__.py similarity index 66% rename from msgraph/__init__.py rename to src/msgraph_core/__init__.py index 4080dc1f..a979087b 100644 --- a/msgraph/__init__.py +++ b/src/msgraph_core/__init__.py @@ -8,6 +8,9 @@ """ Core component of the Microsoft Graph Python SDK consisting of HTTP/Graph Client and a configurable middleware pipeline (Preview). """ -from .core import SDK_VERSION +from ._constants import SDK_VERSION +from ._enums import APIVersion, NationalClouds +from .base_graph_request_adapter import BaseGraphRequestAdapter +from .graph_client_factory import GraphClientFactory __version__ = SDK_VERSION diff --git a/msgraph/core/_constants.py b/src/msgraph_core/_constants.py similarity index 100% rename from msgraph/core/_constants.py rename to src/msgraph_core/_constants.py diff --git a/msgraph/core/_enums.py b/src/msgraph_core/_enums.py similarity index 100% rename from msgraph/core/_enums.py rename to src/msgraph_core/_enums.py diff --git a/src/msgraph_core/base_graph_request_adapter.py b/src/msgraph_core/base_graph_request_adapter.py new file mode 100644 index 00000000..2acfdc10 --- /dev/null +++ b/src/msgraph_core/base_graph_request_adapter.py @@ -0,0 +1,29 @@ +import httpx +from kiota_abstractions.authentication import AuthenticationProvider +from kiota_abstractions.serialization import ( + ParseNodeFactory, + ParseNodeFactoryRegistry, + SerializationWriterFactory, + SerializationWriterFactoryRegistry, +) +from kiota_http.httpx_request_adapter import HttpxRequestAdapter + +from .graph_client_factory import GraphClientFactory + + +class BaseGraphRequestAdapter(HttpxRequestAdapter): + + def __init__( + self, + authentication_provider: AuthenticationProvider, + parse_node_factory: ParseNodeFactory = ParseNodeFactoryRegistry(), + serialization_writer_factory: + SerializationWriterFactory = SerializationWriterFactoryRegistry(), + http_client: httpx.AsyncClient = GraphClientFactory.create_with_default_middleware() + ) -> None: + super().__init__( + authentication_provider=authentication_provider, + parse_node_factory=parse_node_factory, + serialization_writer_factory=serialization_writer_factory, + http_client=http_client + ) diff --git a/src/msgraph_core/graph_client_factory.py b/src/msgraph_core/graph_client_factory.py new file mode 100644 index 00000000..3c1ac04f --- /dev/null +++ b/src/msgraph_core/graph_client_factory.py @@ -0,0 +1,78 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from __future__ import annotations + +from typing import List, Optional + +import httpx +from kiota_http.kiota_client_factory import KiotaClientFactory +from kiota_http.middleware.middleware import BaseMiddleware + +from ._enums import APIVersion, NationalClouds +from .middleware import AsyncGraphTransport, GraphTelemetryHandler + + +class GraphClientFactory(KiotaClientFactory): + """Constructs httpx.AsyncClient instances configured with either custom or default + pipeline of graph specific middleware. + """ + + @staticmethod + def create_with_default_middleware( + api_version: APIVersion = APIVersion.v1, + host: NationalClouds = NationalClouds.Global + ) -> httpx.AsyncClient: + """Constructs native HTTP AsyncClient(httpx.AsyncClient) instances configured with + a custom transport loaded with a default pipeline of middleware. + Returns: + httpx.AsycClient: An instance of the AsyncClient object + """ + client = KiotaClientFactory.get_default_client() + client.base_url = GraphClientFactory._get_base_url(host, api_version) + current_transport = client._transport + + middleware = KiotaClientFactory.get_default_middleware() + middleware.append(GraphTelemetryHandler()) + middleware_pipeline = KiotaClientFactory.create_middleware_pipeline( + middleware, current_transport + ) + + client._transport = AsyncGraphTransport( + transport=current_transport, pipeline=middleware_pipeline + ) + client._transport.pipeline + return client + + @staticmethod + def create_with_custom_middleware( + middleware: Optional[List[BaseMiddleware]], + api_version: APIVersion = APIVersion.v1, + host: NationalClouds = NationalClouds.Global, + ) -> httpx.AsyncClient: + """Applies a custom middleware chain to the HTTP Client + + Args: + middleware(List[BaseMiddleware]): Custom middleware list that will be used to create + a middleware pipeline. The middleware should be arranged in the order in which they will + modify the request. + """ + client = KiotaClientFactory.get_default_client() + client.base_url = GraphClientFactory._get_base_url(host, api_version) + current_transport = client._transport + + middleware_pipeline = KiotaClientFactory.create_middleware_pipeline( + middleware, current_transport + ) + + client._transport = AsyncGraphTransport( + transport=current_transport, pipeline=middleware_pipeline + ) + return client + + @staticmethod + def _get_base_url(host: str, api_version: APIVersion) -> str: + """Helper method to set the complete base url""" + base_url = f'{host}/{api_version}' + return base_url diff --git a/tests/integration/__init__.py b/src/msgraph_core/middleware/__init__.py similarity index 50% rename from tests/integration/__init__.py rename to src/msgraph_core/middleware/__init__.py index b74cfa3b..1ecb083a 100644 --- a/tests/integration/__init__.py +++ b/src/msgraph_core/middleware/__init__.py @@ -2,3 +2,6 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +from .async_graph_transport import AsyncGraphTransport +from .request_context import GraphRequestContext +from .telemetry import GraphTelemetryHandler diff --git a/src/msgraph_core/middleware/async_graph_transport.py b/src/msgraph_core/middleware/async_graph_transport.py new file mode 100644 index 00000000..9186231b --- /dev/null +++ b/src/msgraph_core/middleware/async_graph_transport.py @@ -0,0 +1,44 @@ +import json + +import httpx +from kiota_http.middleware import MiddlewarePipeline, RedirectHandler, RetryHandler + +from .._enums import FeatureUsageFlag +from .request_context import GraphRequestContext + + +class AsyncGraphTransport(httpx.AsyncBaseTransport): + """A custom transport for requests to the Microsoft Graph API + """ + + def __init__(self, transport: httpx.AsyncBaseTransport, pipeline: MiddlewarePipeline) -> None: + self.transport = transport + self.pipeline = pipeline + + async def handle_async_request(self, request: httpx.Request) -> httpx.Response: + if self.pipeline: + self.set_request_context_and_feature_usage(request) + response = await self.pipeline.send(request) + return response + + response = await self.transport.handle_async_request(request) + return response + + def set_request_context_and_feature_usage(self, request: httpx.Request) -> httpx.Request: + + request_options = {} + options = request.headers.get('request_options', None) + if options: + request_options = json.loads(options) + + context = GraphRequestContext(request_options, request.headers) + middleware = self.pipeline._first_middleware + while middleware: + if isinstance(middleware, RedirectHandler): + context.feature_usage = FeatureUsageFlag.REDIRECT_HANDLER_ENABLED + if isinstance(middleware, RetryHandler): + context.feature_usage = FeatureUsageFlag.RETRY_HANDLER_ENABLED + + middleware = middleware.next + request.context = context #type: ignore + return request diff --git a/msgraph/core/middleware/request_context.py b/src/msgraph_core/middleware/request_context.py similarity index 66% rename from msgraph/core/middleware/request_context.py rename to src/msgraph_core/middleware/request_context.py index ff44b23a..1f520a15 100644 --- a/msgraph/core/middleware/request_context.py +++ b/src/msgraph_core/middleware/request_context.py @@ -4,16 +4,19 @@ # ------------------------------------ import uuid +import httpx + from .._enums import FeatureUsageFlag -class RequestContext: - """A request context contains data that is persisted throughout the request and - includes a ClientRequestId property, MiddlewareControl property to control behavior - of middleware as well as a FeatureUsage  property to keep track of middleware used +class GraphRequestContext: + """A request context contains data that is persisted throughout the request and + includes a ClientRequestId property, MiddlewareControl property to control behavior + of middleware as well as a FeatureUsage property to keep track of middleware used in making the request. """ - def __init__(self, middleware_control, headers): + + def __init__(self, middleware_control: dict, headers: httpx.Headers): """Constructor for request context instances Args: @@ -26,12 +29,12 @@ def __init__(self, middleware_control, headers): """ self.middleware_control = middleware_control self.client_request_id = headers.get('client-request-id', str(uuid.uuid4())) - self._feature_usage = FeatureUsageFlag.NONE + self._feature_usage: int = FeatureUsageFlag.NONE @property def feature_usage(self): return hex(self._feature_usage) @feature_usage.setter - def set_feature_usage(self, flag: FeatureUsageFlag): + def feature_usage(self, flag: FeatureUsageFlag) -> None: self._feature_usage = self._feature_usage | flag diff --git a/msgraph/core/middleware/telemetry.py b/src/msgraph_core/middleware/telemetry.py similarity index 79% rename from msgraph/core/middleware/telemetry.py rename to src/msgraph_core/middleware/telemetry.py index 9db9b707..33ac0b66 100644 --- a/msgraph/core/middleware/telemetry.py +++ b/src/msgraph_core/middleware/telemetry.py @@ -1,17 +1,29 @@ +import http +import json import platform +import httpx +from kiota_http.middleware import BaseMiddleware from urllib3.util import parse_url from .._constants import SDK_VERSION from .._enums import NationalClouds -from .middleware import BaseMiddleware +from .async_graph_transport import AsyncGraphTransport +from .request_context import GraphRequestContext -class TelemetryHandler(BaseMiddleware): +class GraphRequest(httpx.Request): + context: GraphRequestContext + + +class GraphTelemetryHandler(BaseMiddleware): """Middleware component that attaches metadata to a Graph request in order to help the SDK team improve the developer experience. """ - def send(self, request, **kwargs): + + async def send(self, request: GraphRequest, transport: AsyncGraphTransport): + """Adds telemetry headers and sends the http request. + """ if self.is_graph_url(request.url): self._add_client_request_id_header(request) @@ -19,7 +31,7 @@ def send(self, request, **kwargs): self._add_host_os_header(request) self._add_runtime_environment_header(request) - response = super().send(request, **kwargs) + response = await super().send(request, transport) return response def is_graph_url(self, url): @@ -27,18 +39,13 @@ def is_graph_url(self, url): non-graph endpoints""" endpoints = set(item.value for item in NationalClouds) - base_url = parse_url(url) - endpoint = "{}://{}".format( - base_url.scheme, - base_url.netloc, - ) + base_url = parse_url(str(url)) + endpoint = f"{base_url.scheme}://{base_url.netloc}" return endpoint in endpoints def _add_client_request_id_header(self, request) -> None: """Add a client-request-id header with GUID value to request""" - request.headers.update( - {'client-request-id': '{}'.format(request.context.client_request_id)} - ) + request.headers.update({'client-request-id': f'{request.context.client_request_id}'}) def _append_sdk_version_header(self, request) -> None: """Add SdkVersion request header to each request to identify the language and diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..6bff588a --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,51 @@ +import httpx +import pytest +from kiota_abstractions.authentication import AnonymousAuthenticationProvider + +from msgraph_core import APIVersion, NationalClouds +from msgraph_core.graph_client_factory import GraphClientFactory +from msgraph_core.middleware import GraphRequestContext + +BASE_URL = NationalClouds.Global + '/' + APIVersion.v1 + + +class MockAuthenticationProvider(AnonymousAuthenticationProvider): + + async def get_authorization_token(self, request: httpx.Request) -> str: + """Returns a string representing a dummy token + Args: + request (GraphRequest): Graph request object + """ + request.headers['Authorization'] = 'Sample token' + return + + +@pytest.fixture +def mock_auth_provider(): + return MockAuthenticationProvider() + + +@pytest.fixture +def mock_transport(): + client = GraphClientFactory.create_with_default_middleware() + return client._transport + + +@pytest.fixture +def mock_request(): + req = httpx.Request('GET', "https://example.org") + return req + + +@pytest.fixture +def mock_graph_request(): + req = httpx.Request('GET', BASE_URL) + req.context = GraphRequestContext({}, req.headers) + return req + + +@pytest.fixture +def mock_response(): + return httpx.Response( + json={'message': 'Success!'}, status_code=200, headers={"Content-Type": "application/json"} + ) diff --git a/tests/integration/test_graphclient.py b/tests/integration/test_graphclient.py deleted file mode 100644 index 3772a9d7..00000000 --- a/tests/integration/test_graphclient.py +++ /dev/null @@ -1,91 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import pytest -from azure.identity import EnvironmentCredential -from requests import Session - -from msgraph.core import APIVersion, GraphClient -from msgraph.core.middleware.authorization import AuthorizationHandler - - -def test_graph_client_with_default_middleware(): - """ - Test that a graph client uses default middleware if none are provided - """ - # credential = _CustomTokenCredential() - client = GraphClient(credential=EnvironmentCredential()) - response = client.get('https://graph.microsoft.com/v1.0/users') - assert response.status_code == 200 - - -def test_graph_client_with_user_provided_session(): - """ - Test that the graph client works with a user provided session object - """ - - session = Session() - client = GraphClient(session=session, credential=EnvironmentCredential()) - response = client.get('https://graph.microsoft.com/v1.0/users', ) - assert response.status_code == 200 - - -def test_graph_client_with_custom_settings(): - """ - Test that the graph client works with user provided configuration - """ - credential = EnvironmentCredential() - client = GraphClient(api_version=APIVersion.beta, credential=credential) - response = client.get('https://graph.microsoft.com/v1.0/users', ) - assert response.status_code == 200 - - -def test_graph_client_with_custom_middleware(): - """ - Test client factory works with user provided middleware - """ - credential = EnvironmentCredential() - middleware = [ - AuthorizationHandler(credential), - ] - client = GraphClient(middleware=middleware) - response = client.get('https://graph.microsoft.com/v1.0/users', ) - assert response.status_code == 200 - - -def test_graph_client_adds_context_to_request(): - """ - Test the graph client adds a context object to a request - """ - credential = EnvironmentCredential() - scopes = ['https://graph.microsoft.com/.default'] - client = GraphClient(credential=credential) - response = client.get('https://graph.microsoft.com/v1.0/users', scopes=scopes) - assert response.status_code == 200 - assert hasattr(response.request, 'context') - - -def test_graph_client_picks_options_from_kwargs(): - """ - Test the graph client picks middleware options from kwargs and sets them in the context - """ - credential = EnvironmentCredential() - scopes = ['https://graph.microsoft.com/.default'] - client = GraphClient(credential=credential) - response = client.get('https://graph.microsoft.com/v1.0/users', scopes=scopes) - assert response.status_code == 200 - assert 'scopes' in response.request.context.middleware_control.keys() - assert response.request.context.middleware_control['scopes'] == scopes - - -def test_graph_client_allows_passing_optional_kwargs(): - """ - Test the graph client allows passing optional kwargs native to the requests library - such as stream, proxy and cert. - """ - credential = EnvironmentCredential() - scopes = ['https://graph.microsoft.com/.default'] - client = GraphClient(credential=credential) - response = client.get('https://graph.microsoft.com/v1.0/users', scopes=scopes, stream=True) - assert response.status_code == 200 diff --git a/tests/integration/test_http_client_factory.py b/tests/integration/test_http_client_factory.py deleted file mode 100644 index ea4cbb0e..00000000 --- a/tests/integration/test_http_client_factory.py +++ /dev/null @@ -1,86 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import pytest -from azure.identity import EnvironmentCredential -from requests import Session - -from msgraph.core import APIVersion, HTTPClientFactory -from msgraph.core.middleware.authorization import AuthorizationHandler - - -def test_client_factory_with_default_middleware(): - """ - Test that a client created from client factory with default middleware - works as expected. - """ - credential = EnvironmentCredential() - client = HTTPClientFactory().create_with_default_middleware(credential) - response = client.get('https://graph.microsoft.com/v1.0/users') - assert response.status_code == 200 - - -def test_client_factory_with_user_provided_session(): - """ - Test that the client works with a user provided session object - """ - - session = Session() - credential = EnvironmentCredential() - client = HTTPClientFactory(session=session).create_with_default_middleware(credential) - response = client.get('https://graph.microsoft.com/v1.0/users') - assert response.status_code == 200 - - -def test_client_factory_with_custom_settings(): - """ - Test that the client works with user provided configuration - """ - credential = EnvironmentCredential() - client = HTTPClientFactory(api_version=APIVersion.beta - ).create_with_default_middleware(credential) - response = client.get('https://graph.microsoft.com/v1.0/users') - assert response.status_code == 200 - - -def test_client_factory_with_custom_middleware(): - """ - Test client factory works with user provided middleware - """ - credential = EnvironmentCredential() - middleware = [ - AuthorizationHandler(credential), - ] - client = HTTPClientFactory().create_with_custom_middleware(middleware) - response = client.get('https://graph.microsoft.com/v1.0/users') - assert response.status_code == 200 - - -def test_context_object_is_attached_to_requests_from_client_factory(): - """ - Test that requests from a native HTTP client have a context object attached - """ - credential = EnvironmentCredential() - middleware = [ - AuthorizationHandler(credential), - ] - client = HTTPClientFactory().create_with_custom_middleware(middleware) - response = client.get('https://graph.microsoft.com/v1.0/users') - assert response.status_code == 200 - assert hasattr(response.request, 'context') - - -def test_middleware_control_is_empty_for_requests_from_client_factory(): - """ - Test that requests from a native HTTP client have no middlware options in the middleware - control - """ - credential = EnvironmentCredential() - middleware = [ - AuthorizationHandler(credential), - ] - client = HTTPClientFactory().create_with_custom_middleware(middleware) - response = client.get('https://graph.microsoft.com/v1.0/users') - assert response.status_code == 200 - assert response.request.context.middleware_control == {} diff --git a/tests/integration/test_retry.py b/tests/integration/test_retry.py deleted file mode 100644 index af0f8322..00000000 --- a/tests/integration/test_retry.py +++ /dev/null @@ -1,91 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import pytest -from azure.identity import EnvironmentCredential - -from msgraph.core import GraphClient - - -@pytest.fixture -def graph_client(): - scopes = ['https://graph.microsoft.com/.default'] - credential = EnvironmentCredential() - client = GraphClient(credential=credential, scopes=scopes) - return client - - -def test_no_retry_success_response(graph_client): - """ - Test that a request with valid http header and a success response is not retried - """ - response = graph_client.get('https://graph.microsoft.com/v1.0/users') - - assert response.status_code == 200 - with pytest.raises(KeyError): - response.request.headers["retry-attempt"] - - -def test_valid_retry_429(graph_client): - """ - Test that a request with valid http header and 503 response is retried - """ - response = graph_client.get('https://httpbin.org/status/429') - - assert response.status_code == 429 - assert response.request.headers["retry-attempt"] == "3" - - -def test_valid_retry_503(graph_client): - """ - Test that a request with valid http header and 503 response is retried - """ - response = graph_client.get('https://httpbin.org/status/503') - - assert response.status_code == 503 - assert response.request.headers["retry-attempt"] == "3" - - -def test_valid_retry_504(graph_client): - """ - Test that a request with valid http header and 503 response is retried - """ - response = graph_client.get('https://httpbin.org/status/504') - - assert response.status_code == 504 - assert response.request.headers["retry-attempt"] == "3" - - -def test_request_specific_options_override_default(graph_client): - """ - Test that retry options passed to the request take precedence over - the default options. - """ - response_1 = graph_client.get('https://httpbin.org/status/429') - response_2 = graph_client.get('https://httpbin.org/status/503', max_retries=2) - response_3 = graph_client.get('https://httpbin.org/status/504') - response_4 = graph_client.get('https://httpbin.org/status/429', max_retries=1) - - assert response_1.status_code == 429 - assert response_1.request.headers["Retry-Attempt"] == "3" - assert response_2.status_code == 503 - assert response_2.request.headers["Retry-Attempt"] == "2" - assert response_3.status_code == 504 - assert response_3.request.headers["Retry-Attempt"] == "3" - assert response_4.status_code == 429 - assert response_4.request.headers["Retry-Attempt"] == "1" - - -def test_retries_time_limit(graph_client): - """ - Test that the cumulative retry time plus the retry-after values does not exceed the - provided retries time limit - """ - - response = graph_client.get('https://httpbin.org/status/503', retry_time_limit=0.1) - - assert response.status_code == 503 - headers = response.request.headers - with pytest.raises(KeyError): - response.request.headers["retry-attempt"] diff --git a/tests/integration/test_telemetry.py b/tests/integration/test_telemetry.py deleted file mode 100644 index 6346ecef..00000000 --- a/tests/integration/test_telemetry.py +++ /dev/null @@ -1,72 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import platform -import re -import uuid - -import pytest -from azure.identity import EnvironmentCredential - -from msgraph.core import SDK_VERSION, APIVersion, GraphClient, NationalClouds - -BASE_URL = NationalClouds.Global + '/' + APIVersion.v1 - - -@pytest.fixture -def graph_client(): - scopes = ['https://graph.microsoft.com/.default'] - credential = EnvironmentCredential() - client = GraphClient(credential=credential, scopes=scopes) - return client - - -def test_telemetry_handler(graph_client): - """ - Test telemetry handler updates the graph request with the requisite headers - """ - response = graph_client.get('https://graph.microsoft.com/v1.0/users') - system = platform.system() - version = platform.version() - host_os = f'{system} {version}' - python_version = platform.python_version() - runtime_environment = f'Python/{python_version}' - - assert response.status_code == 200 - assert response.request.headers["client-request-id"] - assert response.request.headers["sdkVersion"].startswith('graph-python-core/' + SDK_VERSION) - assert response.request.headers["HostOs"] == host_os - assert response.request.headers["RuntimeEnvironment"] == runtime_environment - - -def test_telemetry_handler_non_graph_url(graph_client): - """ - Test telemetry handler does not updates the request headers for non-graph requests - """ - response = graph_client.get('https://httpbin.org/status/200') - - assert response.status_code == 200 - with pytest.raises(KeyError): - response.request.headers["client-request-id"] - response.request.headers["sdkVersion"] - response.request.headers["HostOs"] - response.request.headers["RuntimeEnvironment"] - - -def test_custom_client_request_id(graph_client): - """ - Test customer provided client request id overrides default value - """ - custom_id = str(uuid.uuid4()) - response = graph_client.get( - 'https://httpbin.org/status/200', headers={"client-request-id": custom_id} - ) - - assert response.status_code == 200 - assert response.request.context.client_request_id == custom_id - with pytest.raises(KeyError): - response.request.headers["client-request-id"] - response.request.headers["sdkVersion"] - response.request.headers["HostOs"] - response.request.headers["RuntimeEnvironment"] diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py deleted file mode 100644 index b74cfa3b..00000000 --- a/tests/unit/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ diff --git a/tests/unit/test_async_graph_transport.py b/tests/unit/test_async_graph_transport.py new file mode 100644 index 00000000..619ced98 --- /dev/null +++ b/tests/unit/test_async_graph_transport.py @@ -0,0 +1,18 @@ +import pytest +from kiota_http.kiota_client_factory import KiotaClientFactory + +from msgraph_core._enums import FeatureUsageFlag +from msgraph_core.middleware import AsyncGraphTransport, GraphRequestContext + + +def test_set_request_context_and_feature_usage(mock_request, mock_transport): + middleware = KiotaClientFactory.get_default_middleware() + pipeline = KiotaClientFactory.create_middleware_pipeline(middleware, mock_transport) + transport = AsyncGraphTransport(mock_transport, pipeline) + transport.set_request_context_and_feature_usage(mock_request) + + assert hasattr(mock_request, 'context') + assert isinstance(mock_request.context, GraphRequestContext) + assert mock_request.context.feature_usage == hex( + FeatureUsageFlag.RETRY_HANDLER_ENABLED | FeatureUsageFlag.REDIRECT_HANDLER_ENABLED + ) diff --git a/tests/unit/test_auth_handler.py b/tests/unit/test_auth_handler.py deleted file mode 100644 index 50b40dcd..00000000 --- a/tests/unit/test_auth_handler.py +++ /dev/null @@ -1,35 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import pytest - -from msgraph.core.middleware.authorization import AuthorizationHandler -from msgraph.core.middleware.request_context import RequestContext - - -def test_context_options_override_default_scopes(): - """ Test scopes found in the request context override default scopes""" - default_scopes = ['.default'] - middleware_control = { - 'scopes': ['email.read'], - } - request_context = RequestContext(middleware_control, {}) - - auth_handler = AuthorizationHandler(None, scopes=default_scopes) - - auth_handler_scopes = auth_handler.get_scopes(request_context) - assert auth_handler_scopes == middleware_control['scopes'] - - -def test_auth_handler_get_scopes_does_not_overwrite_default_scopes(): - default_scopes = ['.default'] - middleware_control = { - 'scopes': ['email.read'], - } - request_context = RequestContext(middleware_control, {}) - auth_handler = AuthorizationHandler(None, scopes=default_scopes) - - auth_handler_scopes = auth_handler.get_scopes(request_context) - - assert auth_handler.scopes == default_scopes diff --git a/tests/unit/test_base_graph_request_adapter.py b/tests/unit/test_base_graph_request_adapter.py new file mode 100644 index 00000000..59e7c891 --- /dev/null +++ b/tests/unit/test_base_graph_request_adapter.py @@ -0,0 +1,24 @@ +import httpx +import pytest +from kiota_abstractions.serialization import ( + ParseNodeFactoryRegistry, + SerializationWriterFactoryRegistry, +) + +from msgraph_core.base_graph_request_adapter import BaseGraphRequestAdapter + + +def test_create_graph_request_adapter(mock_auth_provider): + request_adapter = BaseGraphRequestAdapter(mock_auth_provider) + assert request_adapter._authentication_provider is mock_auth_provider + assert isinstance(request_adapter._parse_node_factory, ParseNodeFactoryRegistry) + assert isinstance( + request_adapter._serialization_writer_factory, SerializationWriterFactoryRegistry + ) + assert isinstance(request_adapter._http_client, httpx.AsyncClient) + assert request_adapter.base_url == '' + + +def test_create_request_adapter_no_auth_provider(): + with pytest.raises(TypeError): + BaseGraphRequestAdapter(None) diff --git a/tests/unit/test_client_factory.py b/tests/unit/test_client_factory.py deleted file mode 100644 index db414b45..00000000 --- a/tests/unit/test_client_factory.py +++ /dev/null @@ -1,78 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import pytest -from requests import Session -from requests.adapters import HTTPAdapter - -from msgraph.core import APIVersion, HTTPClientFactory, NationalClouds -from msgraph.core._constants import DEFAULT_CONNECTION_TIMEOUT, DEFAULT_REQUEST_TIMEOUT -from msgraph.core.middleware.authorization import AuthorizationHandler - - -def test_initialize_with_default_config(): - """Test creation of HTTP Client will use the default configuration - if none are passed""" - client = HTTPClientFactory() - - assert client.api_version == APIVersion.v1 - assert client.endpoint == NationalClouds.Global - assert client.timeout == (DEFAULT_CONNECTION_TIMEOUT, DEFAULT_REQUEST_TIMEOUT) - assert isinstance(client.session, Session) - - -def test_initialize_with_custom_config(): - """Test creation of HTTP Client will use custom configuration if they are passed""" - client = HTTPClientFactory(api_version=APIVersion.beta, timeout=(5, 5)) - - assert client.api_version == APIVersion.beta - assert client.endpoint == NationalClouds.Global - assert client.timeout == (5, 5) - assert isinstance(client.session, Session) - - -def test_create_with_default_middleware(): - """Test creation of HTTP Client using default middleware""" - credential = _CustomTokenCredential() - client = HTTPClientFactory().create_with_default_middleware(credential=credential) - middleware = client.get_adapter('https://') - - assert isinstance(middleware, HTTPAdapter) - - -def test_create_with_custom_middleware(): - """Test creation of HTTP Clients with custom middleware""" - credential = _CustomTokenCredential() - middleware = [ - AuthorizationHandler(credential), - ] - client = HTTPClientFactory().create_with_custom_middleware(middleware=middleware) - custom_middleware = client.get_adapter('https://') - - assert isinstance(custom_middleware, HTTPAdapter) - - -def test_get_base_url(): - """ - Test base url is formed by combining the national cloud endpoint with - Api version - """ - client = HTTPClientFactory(api_version=APIVersion.beta, cloud=NationalClouds.Germany) - assert client.session.base_url == client.endpoint + '/' + client.api_version - - -def test_register_middleware(): - credential = _CustomTokenCredential() - middleware = [ - AuthorizationHandler(credential), - ] - client = HTTPClientFactory() - client._register(middleware) - - assert isinstance(client.session.get_adapter('https://'), HTTPAdapter) - - -class _CustomTokenCredential: - def get_token(self, scopes): - return ['{token:https://graph.microsoft.com/}'] diff --git a/tests/unit/test_graph_client.py b/tests/unit/test_graph_client.py deleted file mode 100644 index b908fa5b..00000000 --- a/tests/unit/test_graph_client.py +++ /dev/null @@ -1,96 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import pytest -import responses -from requests import Session -from requests.adapters import HTTPAdapter - -from msgraph.core import APIVersion, GraphClient, NationalClouds -from msgraph.core.middleware.authorization import AuthorizationHandler - - -def test_graph_client_with_default_middleware(): - """ - Test creating a graph client with default middleware works as expected - """ - credential = _CustomTokenCredential() - client = GraphClient(credential=credential) - - assert isinstance(client.graph_session, Session) - assert isinstance(client.graph_session.get_adapter('https://'), HTTPAdapter) - assert client.graph_session.base_url == NationalClouds.Global + '/' + APIVersion.v1 - - -def test_graph_client_with_custom_middleware(): - """ - Test creating a graph client with custom middleware works as expected - """ - credential = _CustomTokenCredential() - middleware = [ - AuthorizationHandler(credential), - ] - client = GraphClient(middleware=middleware) - - assert isinstance(client.graph_session, Session) - assert isinstance(client.graph_session.get_adapter('https://'), HTTPAdapter) - assert client.graph_session.base_url == NationalClouds.Global + '/' + APIVersion.v1 - - -def test_graph_client_with_custom_configuration(): - """ - Test creating a graph client with custom middleware works as expected - """ - credential = _CustomTokenCredential() - client = GraphClient( - credential=credential, api_version=APIVersion.beta, cloud=NationalClouds.China - ) - - assert client.graph_session.base_url == NationalClouds.China + '/' + APIVersion.beta - - -def test_graph_client_uses_same_session(): - """ - Test graph client is a singleton class and uses the same session - """ - credential = _CustomTokenCredential() - client = GraphClient(credential=credential) - - client2 = GraphClient(credential=credential) - assert client is client2 - - -@responses.activate -def test_graph_client_builds_graph_urls(): - """ - Test that the graph client builds full urls if supplied with partial - """ - credential = _CustomTokenCredential() - client = GraphClient(credential=credential) - graph_url = client.graph_session.base_url + '/me' - - responses.add(responses.GET, graph_url, status=200) - - client.get('/me', headers={}) - assert graph_url == responses.calls[0].request.url - - -@responses.activate -def test_does_not_build_graph_urls_for_full_urls(): - """ - Test that the graph client builds full urls if supplied with partial - """ - other_url = 'https://microsoft.com/' - responses.add(responses.GET, other_url, status=200) - - credential = _CustomTokenCredential() - client = GraphClient(credential=credential) - client.get(other_url, headers={}) - request_url = responses.calls[0].request.url - assert other_url == request_url - - -class _CustomTokenCredential: - def get_token(self, scopes): - return ['{token:https://graph.microsoft.com/}'] diff --git a/tests/unit/test_graph_client_factory.py b/tests/unit/test_graph_client_factory.py new file mode 100644 index 00000000..621a6ebb --- /dev/null +++ b/tests/unit/test_graph_client_factory.py @@ -0,0 +1,58 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +import httpx +import pytest +from kiota_http.middleware import MiddlewarePipeline, RedirectHandler + +from msgraph_core import APIVersion, GraphClientFactory, NationalClouds +from msgraph_core.middleware import AsyncGraphTransport, GraphTelemetryHandler + + +def test_create_with_default_middleware(): + """Test creation of GraphClient using default middleware""" + client = GraphClientFactory.create_with_default_middleware() + + assert isinstance(client, httpx.AsyncClient) + assert isinstance(client._transport, AsyncGraphTransport) + pipeline = client._transport.pipeline + assert isinstance(pipeline, MiddlewarePipeline) + assert isinstance(pipeline._first_middleware, RedirectHandler) + assert isinstance(pipeline._current_middleware, GraphTelemetryHandler) + + +def test_create_with_custom_middleware(): + """Test creation of HTTP Clients with custom middleware""" + middleware = [ + GraphTelemetryHandler(), + ] + client = GraphClientFactory.create_with_custom_middleware(middleware=middleware) + + assert isinstance(client, httpx.AsyncClient) + assert isinstance(client._transport, AsyncGraphTransport) + pipeline = client._transport.pipeline + assert isinstance(pipeline._first_middleware, GraphTelemetryHandler) + + +def test_graph_client_factory_with_custom_configuration(): + """ + Test creating a graph client with custom url overrides the default + """ + graph_client = GraphClientFactory.create_with_default_middleware( + api_version=APIVersion.beta, host=NationalClouds.China + ) + assert isinstance(graph_client, httpx.AsyncClient) + assert str(graph_client.base_url) == f'{NationalClouds.China}/{APIVersion.beta}/' + + +def test_get_base_url(): + """ + Test base url is formed by combining the national cloud endpoint with + Api version + """ + url = GraphClientFactory._get_base_url( + host=NationalClouds.Germany, + api_version=APIVersion.beta, + ) + assert url == f'{NationalClouds.Germany}/{APIVersion.beta}' diff --git a/tests/unit/test_graph_telemetry_handler.py b/tests/unit/test_graph_telemetry_handler.py new file mode 100644 index 00000000..de9684a6 --- /dev/null +++ b/tests/unit/test_graph_telemetry_handler.py @@ -0,0 +1,108 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +import platform +import re +import uuid + +import httpx +import pytest + +from msgraph_core import SDK_VERSION, APIVersion, NationalClouds +from msgraph_core.middleware import GraphRequestContext, GraphTelemetryHandler + +BASE_URL = NationalClouds.Global + '/' + APIVersion.v1 + + +def test_is_graph_url(mock_graph_request): + """ + Test method that checks whether a request url is a graph endpoint + """ + telemetry_handler = GraphTelemetryHandler() + assert telemetry_handler.is_graph_url(mock_graph_request.url) + + +def test_is_not_graph_url(mock_request): + """ + Test method that checks whether a request url is a graph endpoint with a + non-graph url. + """ + telemetry_handler = GraphTelemetryHandler() + assert not telemetry_handler.is_graph_url(mock_request.url) + + +def test_add_client_request_id_header(mock_graph_request): + """ + Test that client_request_id is added to the request headers + """ + telemetry_handler = GraphTelemetryHandler() + telemetry_handler._add_client_request_id_header(mock_graph_request) + + assert 'client-request-id' in mock_graph_request.headers + assert _is_valid_uuid(mock_graph_request.headers.get('client-request-id')) + + +def test_custom_client_request_id_header(): + """ + Test that a custom client request id is used, if provided + """ + custom_id = str(uuid.uuid4()) + request = httpx.Request('GET', BASE_URL) + request.context = GraphRequestContext({}, {'client-request-id': custom_id}) + + telemetry_handler = GraphTelemetryHandler() + telemetry_handler._add_client_request_id_header(request) + + assert 'client-request-id' in request.headers + assert _is_valid_uuid(request.headers.get('client-request-id')) + assert request.headers.get('client-request-id') == custom_id + + +def test_append_sdk_version_header(mock_graph_request): + """ + Test that sdkVersion is added to the request headers + """ + telemetry_handler = GraphTelemetryHandler() + telemetry_handler._append_sdk_version_header(mock_graph_request) + + assert 'sdkVersion' in mock_graph_request.headers + assert mock_graph_request.headers.get('sdkVersion' + ).startswith('graph-python-core/' + SDK_VERSION) + + +def test_add_host_os_header(mock_graph_request): + """ + Test that HostOs is added to the request headers + """ + system = platform.system() + version = platform.version() + host_os = f'{system} {version}' + + telemetry_handler = GraphTelemetryHandler() + telemetry_handler._add_host_os_header(mock_graph_request) + + assert 'HostOs' in mock_graph_request.headers + assert mock_graph_request.headers.get('HostOs') == host_os + + +def test_add_runtime_environment_header(mock_graph_request): + """ + Test that RuntimeEnvironment is added to the request headers + """ + python_version = platform.python_version() + runtime_environment = f'Python/{python_version}' + + telemetry_handler = GraphTelemetryHandler() + telemetry_handler._add_runtime_environment_header(mock_graph_request) + + assert 'RuntimeEnvironment' in mock_graph_request.headers + assert mock_graph_request.headers.get('RuntimeEnvironment') == runtime_environment + + +def _is_valid_uuid(guid): + regex = "^[{]?[0-9a-fA-F]{8}" + "-([0-9a-fA-F]{4}-)" + "{3}[0-9a-fA-F]{12}[}]?$" + pattern = re.compile(regex) + if re.search(pattern, guid): + return True + return False diff --git a/tests/unit/test_middleware_pipeline.py b/tests/unit/test_middleware_pipeline.py deleted file mode 100644 index e4560c37..00000000 --- a/tests/unit/test_middleware_pipeline.py +++ /dev/null @@ -1,89 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -from collections import OrderedDict -from unittest import TestCase - -from msgraph.core.middleware.middleware import BaseMiddleware, MiddlewarePipeline - - -class MiddlewarePipelineTest(TestCase): - def test_adds_middlewares_in_order(self): - middleware_pipeline = MiddlewarePipeline() - middleware_pipeline.add_middleware(MockRequestMiddleware1()) - middleware_pipeline.add_middleware(MockRequestMiddleware2()) - - first_middleware = middleware_pipeline._first_middleware - second_middleware = middleware_pipeline._first_middleware.next - - self.assertIsInstance(first_middleware, MockRequestMiddleware1) - self.assertIsInstance(second_middleware, MockRequestMiddleware2) - - def test_request_object_is_modified_in_order(self): - middleware_pipeline = MiddlewarePipeline() - middleware_pipeline.add_middleware(MockRequestMiddleware1()) - middleware_pipeline.add_middleware(MockRequestMiddleware2()) - - request = OrderedDict() - request.headers = {} - result = middleware_pipeline.send(request) - - second, _ = result.popitem() - first, _ = result.popitem() - - self.assertEqual(second, 'middleware2') - self.assertEqual(first, 'middleware1') - - def test_response_object_is_modified_in_reverse_order(self): - middleware_pipeline = MiddlewarePipeline() - middleware_pipeline.add_middleware( - MockResponseMiddleware1() - ) # returns world as the response - middleware_pipeline.add_middleware( - MockResponseMiddleware2() - ) # returns hello as the response - - # Responses are passed through the list of middlewares in reverse order. - # This will return hello world - request = OrderedDict() - request.headers = {} - resp = middleware_pipeline.send(request) - - self.assertEqual(resp, 'Hello World') - - -class MockRequestMiddleware1(BaseMiddleware): - def __init__(self): - super().__init__() - - def send(self, request, **kwargs): - request['middleware1'] = 1 - return super().send(request, **kwargs) - - -class MockRequestMiddleware2(BaseMiddleware): - def __init__(self): - super().__init__() - - def send(self, request, **kwargs): - request['middleware2'] = 2 - return request - - -class MockResponseMiddleware1(BaseMiddleware): - def __init__(self): - super().__init__() - - def send(self, request, **kwargs): - resp = super().send(request, **kwargs) - resp += 'World' - return resp - - -class MockResponseMiddleware2(BaseMiddleware): - def __init__(self): - super().__init__() - - def send(self, request, **kwargs): - return 'Hello ' diff --git a/tests/unit/test_retry_handler.py b/tests/unit/test_retry_handler.py deleted file mode 100644 index 9201801c..00000000 --- a/tests/unit/test_retry_handler.py +++ /dev/null @@ -1,192 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -from email.utils import formatdate -from time import time - -import pytest -import requests -import responses - -from msgraph.core import APIVersion, NationalClouds -from msgraph.core.middleware.retry import RetryHandler - -BASE_URL = NationalClouds.Global + '/' + APIVersion.v1 - - -def test_no_config(): - """ - Test that default values are used if no custom confguration is passed - """ - retry_handler = RetryHandler() - assert retry_handler.max_retries == retry_handler.DEFAULT_MAX_RETRIES - assert retry_handler.timeout == retry_handler.MAX_DELAY - assert retry_handler.backoff_max == retry_handler.MAXIMUM_BACKOFF - assert retry_handler.backoff_factor == retry_handler.DEFAULT_BACKOFF_FACTOR - assert retry_handler._allowed_methods == frozenset( - ['HEAD', 'GET', 'PUT', 'POST', 'PATCH', 'DELETE', 'OPTIONS'] - ) - assert retry_handler._respect_retry_after_header - assert retry_handler._retry_on_status_codes == retry_handler._DEFAULT_RETRY_STATUS_CODES - - -def test_custom_config(): - """ - Test that default configuration is overrriden if custom configuration is provided - """ - retry_handler = RetryHandler( - max_retries=10, - retry_backoff_factor=0.2, - retry_backoff_max=200, - retry_time_limit=100, - retry_on_status_codes=[502, 503] - ) - - assert retry_handler.max_retries == 10 - assert retry_handler.timeout == 100 - assert retry_handler.backoff_max == 200 - assert retry_handler.backoff_factor == 0.2 - assert retry_handler._retry_on_status_codes == {429, 502, 503, 504} - - -def test_disable_retries(): - """ - Test that when disable_retries class method is called, total retries are set to zero - """ - retry_handler = RetryHandler() - retry_handler = retry_handler.disable_retries() - assert retry_handler.max_retries == 0 - retry_options = retry_handler.get_retry_options({}) - assert not retry_handler.check_retry_valid(retry_options, 0) - - -@responses.activate -def test_method_retryable_with_valid_method(): - """ - Test if method is retryable with a retryable request method. - """ - responses.add(responses.GET, BASE_URL, status=502) - response = requests.get(BASE_URL) - - retry_handler = RetryHandler() - settings = retry_handler.get_retry_options({}) - - assert retry_handler._is_method_retryable(settings, response.request) - - -@responses.activate -def test_should_retry_valid(): - """ - Test the should_retry method with a valid HTTP method and response code - """ - responses.add(responses.GET, BASE_URL, status=503) - response = requests.get(BASE_URL) - - retry_handler = RetryHandler() - settings = retry_handler.get_retry_options({}) - - assert retry_handler.should_retry(settings, response) - - -@responses.activate -def test_should_retry_invalid(): - """ - Test the should_retry method with an valid HTTP response code - """ - responses.add(responses.GET, BASE_URL, status=502) - response = requests.get(BASE_URL) - - retry_handler = RetryHandler() - settings = retry_handler.get_retry_options({}) - - assert not retry_handler.should_retry(settings, response) - - -@responses.activate -def test_is_request_payload_buffered_valid(): - """ - Test for _is_request_payload_buffered helper method. - Should return true request payload is buffered/rewindable. - """ - responses.add(responses.GET, BASE_URL, status=429) - response = requests.get(BASE_URL) - - retry_handler = RetryHandler() - - assert retry_handler._is_request_payload_buffered(response) - - -@responses.activate -def test_is_request_payload_buffered_invalid(): - """ - Test for _is_request_payload_buffered helper method. - Should return false if request payload is forward streamed. - """ - responses.add(responses.POST, BASE_URL, status=429) - response = requests.post(BASE_URL, headers={'Content-Type': "application/octet-stream"}) - - retry_handler = RetryHandler() - - assert not retry_handler._is_request_payload_buffered(response) - - -def test_check_retry_valid(): - """ - Test that a retry is valid if the maximum number of retries has not been reached - """ - retry_handler = RetryHandler() - settings = retry_handler.get_retry_options({}) - - assert retry_handler.check_retry_valid(settings, 0) - - -def test_check_retry_valid_no_retries(): - """ - Test that a retry is not valid if maximum number of retries has been reached - """ - retry_handler = RetryHandler(max_retries=2) - settings = retry_handler.get_retry_options({}) - - assert not retry_handler.check_retry_valid(settings, 2) - - -@responses.activate -def test_get_retry_after(): - """ - Test the _get_retry_after method with an integer value for retry header. - """ - responses.add(responses.GET, BASE_URL, headers={'Retry-After': "120"}, status=503) - response = requests.get(BASE_URL) - - retry_handler = RetryHandler() - - assert retry_handler._get_retry_after(response) == 120 - - -@responses.activate -def test_get_retry_after_no_header(): - """ - Test the _get_retry_after method with no Retry-After header. - """ - responses.add(responses.GET, BASE_URL, status=503) - response = requests.get(BASE_URL) - - retry_handler = RetryHandler() - - assert retry_handler._get_retry_after(response) is None - - -@responses.activate -def test_get_retry_after_http_date(): - """ - Test the _get_retry_after method with a http date as Retry-After value. - """ - timevalue = time() + 120 - http_date = formatdate(timeval=timevalue, localtime=False, usegmt=True) - responses.add(responses.GET, BASE_URL, headers={'retry-after': f'{http_date}'}, status=503) - response = requests.get(BASE_URL) - - retry_handler = RetryHandler() - - assert retry_handler._get_retry_after(response) < 120 diff --git a/tests/unit/test_telemetry_handler.py b/tests/unit/test_telemetry_handler.py deleted file mode 100644 index 0d58eb93..00000000 --- a/tests/unit/test_telemetry_handler.py +++ /dev/null @@ -1,146 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import platform -import re -import uuid - -import pytest -import requests -import responses - -from msgraph.core import SDK_VERSION, APIVersion, GraphClient, NationalClouds -from msgraph.core.middleware.request_context import RequestContext -from msgraph.core.middleware.telemetry import TelemetryHandler - -BASE_URL = NationalClouds.Global + '/' + APIVersion.v1 - - -@responses.activate -def test_is_graph_url(): - """ - Test method that checks whether a request url is a graph endpoint - """ - responses.add(responses.GET, BASE_URL) - response = requests.get(BASE_URL) - request = response.request - - telemetry_handler = TelemetryHandler() - assert telemetry_handler.is_graph_url(request.url) - - -@responses.activate -def test_is_not_graph_url(): - """ - Test method that checks whether a request url is a graph endpoint with a - non-graph url - """ - responses.add(responses.GET, 'https://httpbin.org/status/200') - response = requests.get('https://httpbin.org/status/200') - request = response.request - - telemetry_handler = TelemetryHandler() - assert not telemetry_handler.is_graph_url(request.url) - - -@responses.activate -def test_add_client_request_id_header(): - """ - Test that client_request_id is added to the request headers - """ - responses.add(responses.GET, BASE_URL) - response = requests.get(BASE_URL) - request = response.request - request.context = RequestContext({}, {}) - - telemetry_handler = TelemetryHandler() - telemetry_handler._add_client_request_id_header(request) - - assert 'client-request-id' in request.headers - assert _is_valid_uuid(request.headers.get('client-request-id')) - - -@responses.activate -def test_custom_client_request_id_header(): - """ - Test that a custom client request id is used, if provided - """ - custom_id = str(uuid.uuid4()) - responses.add(responses.GET, BASE_URL) - response = requests.get(BASE_URL) - request = response.request - request.context = RequestContext({}, {'client-request-id': custom_id}) - - telemetry_handler = TelemetryHandler() - telemetry_handler._add_client_request_id_header(request) - - assert 'client-request-id' in request.headers - assert _is_valid_uuid(request.headers.get('client-request-id')) - assert request.headers.get('client-request-id') == custom_id - - -@responses.activate -def test_append_sdk_version_header(): - """ - Test that sdkVersion is added to the request headers - """ - responses.add(responses.GET, BASE_URL) - response = requests.get(BASE_URL) - request = response.request - request.context = RequestContext({}, {}) - - telemetry_handler = TelemetryHandler() - telemetry_handler._append_sdk_version_header(request) - - assert 'sdkVersion' in request.headers - assert request.headers.get('sdkVersion').startswith('graph-python-core/' + SDK_VERSION) - - -@responses.activate -def test_add_host_os_header(): - """ - Test that HostOs is added to the request headers - """ - system = platform.system() - version = platform.version() - host_os = f'{system} {version}' - - responses.add(responses.GET, BASE_URL) - response = requests.get(BASE_URL) - request = response.request - request.context = RequestContext({}, {}) - - telemetry_handler = TelemetryHandler() - telemetry_handler._add_host_os_header(request) - - assert 'HostOs' in request.headers - assert request.headers.get('HostOs') == host_os - - -@responses.activate -def test_add_runtime_environment_header(): - """ - Test that RuntimeEnvironment is added to the request headers - """ - python_version = platform.python_version() - runtime_environment = f'Python/{python_version}' - - responses.add(responses.GET, BASE_URL) - response = requests.get(BASE_URL) - request = response.request - request.context = RequestContext({}, {}) - - telemetry_handler = TelemetryHandler() - telemetry_handler._add_runtime_environment_header(request) - - assert 'RuntimeEnvironment' in request.headers - assert request.headers.get('RuntimeEnvironment') == runtime_environment - - -def _is_valid_uuid(guid): - regex = "^[{]?[0-9a-fA-F]{8}" + "-([0-9a-fA-F]{4}-)" + "{3}[0-9a-fA-F]{12}[}]?$" - pattern = re.compile(regex) - if re.search(pattern, guid): - return True - return False