├── .coveragerc
├── .gitignore
├── .travis.yml
├── .vscode
└── settings.json
├── LICENSE.md
├── MANIFEST.in
├── README.rst
├── SECURITY.md
├── autorest_setup.sh
├── dev_requirements.txt
├── doc
├── conf.py
├── index.md
├── make.bat
└── requirements.txt
├── msrest
├── __init__.py
├── async_client.py
├── async_paging.py
├── authentication.py
├── configuration.py
├── exceptions.py
├── http_logger.py
├── paging.py
├── pipeline
│ ├── __init__.py
│ ├── aiohttp.py
│ ├── async_abc.py
│ ├── async_requests.py
│ ├── requests.py
│ └── universal.py
├── polling
│ ├── __init__.py
│ ├── async_poller.py
│ └── poller.py
├── py.typed
├── serialization.py
├── service_client.py
├── universal_http
│ ├── __init__.py
│ ├── aiohttp.py
│ ├── async_abc.py
│ ├── async_requests.py
│ └── requests.py
└── version.py
├── pylintrc
├── setup.cfg
├── setup.py
├── tests
├── __init__.py
├── asynctests
│ ├── test_async_client.py
│ ├── test_async_paging.py
│ ├── test_pipeline.py
│ ├── test_polling.py
│ └── test_universal_http.py
├── conftest.py
├── storage_models
│ ├── __init__.py
│ ├── account_sas_parameters.py
│ ├── check_name_availability_result.py
│ ├── custom_domain.py
│ ├── encryption.py
│ ├── encryption_service.py
│ ├── encryption_services.py
│ ├── endpoints.py
│ ├── list_account_sas_response.py
│ ├── list_service_sas_response.py
│ ├── resource.py
│ ├── service_sas_parameters.py
│ ├── sku.py
│ ├── storage_account.py
│ ├── storage_account_check_name_availability_parameters.py
│ ├── storage_account_create_parameters.py
│ ├── storage_account_key.py
│ ├── storage_account_list_keys_result.py
│ ├── storage_account_paged.py
│ ├── storage_account_regenerate_key_parameters.py
│ ├── storage_account_update_parameters.py
│ ├── storage_management_client_enums.py
│ ├── usage.py
│ ├── usage_name.py
│ └── usage_paged.py
├── test_auth.py
├── test_client.py
├── test_exceptions.py
├── test_paging.py
├── test_pipeline.py
├── test_polling.py
├── test_requests_universal.py
├── test_runtime.py
├── test_serialization.py
├── test_universal_pipeline.py
└── test_xml_serialization.py
└── tox.ini
/.coveragerc:
--------------------------------------------------------------------------------
1 | [report]
2 | exclude_lines =
3 | pragma: no cover
4 | if TYPE_CHECKING:
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | env*
3 | .venv
4 | *.egg-info
5 | .tox
6 | .coverage
7 | coverage.xml
8 | *.pyc
9 | .idea
10 | build/
11 | dist/
12 | .cache
13 | .pytest_cache
14 | .mypy_cache
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: xenial
2 | language: python
3 | cache: pip
4 | _test: &_test
5 | install:
6 | - pip install tox tox-virtualenv-no-download
7 | script:
8 | - tox
9 | after_success:
10 | - bash <(curl -s https://codecov.io/bash) -e TOXENV -f $TRAVIS_BUILD_DIR/coverage.xml
11 | _autorest_install: &_autorest_install
12 | before_install:
13 | - git clone https://github.com/Azure/autorest.python.git --branch autorestv3 --single-branch
14 | - nvm install 10
15 | - pushd autorest.python
16 | - npm install "@microsoft.azure/autorest.testserver" # Install test server pre-requisites
17 | - popd
18 | jobs:
19 | include:
20 | - stage: MyPy
21 | python: 3.6
22 | install:
23 | - pip install -r dev_requirements.txt
24 | script:
25 | - mypy msrest
26 | - python -c 'import typing; typing.TYPE_CHECKING = True; import msrest' # Testing there is no circular dependencies in Type checking mode
27 | - stage: Test
28 | python: 2.7
29 | env: TOXENV=py27
30 | <<: *_test
31 | - stage: Test
32 | python: 3.5
33 | env: TOXENV=py35
34 | <<: *_test
35 | - stage: Test
36 | python: 3.6
37 | env: TOXENV=py36
38 | <<: *_test
39 | - stage: Test
40 | python: 3.7
41 | env: TOXENV=py37
42 | <<: *_test
43 | - stage: Test
44 | python: 3.8
45 | env: TOXENV=py38
46 | <<: *_test
47 | - stage: Test
48 | python: 2.7
49 | env: TOXENV=py27-autorest
50 | <<: *_autorest_install
51 | <<: *_test
52 | - stage: Test
53 | python: 3.5
54 | env: TOXENV=py35-autorest
55 | <<: *_autorest_install
56 | <<: *_test
57 | - stage: Test
58 | python: 3.6
59 | env: TOXENV=py36-autorest
60 | <<: *_autorest_install
61 | <<: *_test
62 | - stage: Test
63 | python: 3.7
64 | env: TOXENV=py37-autorest
65 | <<: *_autorest_install
66 | <<: *_test
67 | - stage: Test
68 | python: 3.8
69 | env: TOXENV=py38-autorest
70 | <<: *_autorest_install
71 | <<: *_test
72 |
73 | allow_failures:
74 | - env: TOXENV=py27-autorest
75 | - env: TOXENV=py35-autorest
76 | - env: TOXENV=py36-autorest
77 | - env: TOXENV=py37-autorest
78 | - env: TOXENV=py38-autorest
79 | deploy:
80 | provider: pypi
81 | user: Laurent.Mazuel
82 | skip_upload_docs: true
83 | # password: use $PYPI_PASSWORD
84 | distributions: "sdist bdist_wheel"
85 | on:
86 | tags: true
87 | python: '3.6'
88 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "python.testing.pytestArgs": [],
4 | "python.testing.pytestEnabled": true,
5 | "files.exclude": {
6 | "**/*.pyc": true
7 | },
8 | "git.ignoreLimitWarning": true,
9 | "python.linting.pylintEnabled": true
10 | }
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Microsoft Azure
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.rst
2 | recursive-include tests *.py
3 | include msrest/py.typed
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Security
4 |
5 | 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/).
6 |
7 | 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.
8 |
9 | ## Reporting Security Issues
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues.**
12 |
13 | 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).
14 |
15 | 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).
16 |
17 | 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).
18 |
19 | 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:
20 |
21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22 | * Full paths of source file(s) related to the manifestation of the issue
23 | * The location of the affected source code (tag/branch/commit or direct URL)
24 | * Any special configuration required to reproduce the issue
25 | * Step-by-step instructions to reproduce the issue
26 | * Proof-of-concept or exploit code (if possible)
27 | * Impact of the issue, including how an attacker might exploit the issue
28 |
29 | This information will help us triage your report more quickly.
30 |
31 | 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.
32 |
33 | ## Preferred Languages
34 |
35 | We prefer all communications to be in English.
36 |
37 | ## Policy
38 |
39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
40 |
41 |
42 |
--------------------------------------------------------------------------------
/autorest_setup.sh:
--------------------------------------------------------------------------------
1 | pushd autorest.python/test/vanilla/ && pip install -r requirements.txt && popd
--------------------------------------------------------------------------------
/dev_requirements.txt:
--------------------------------------------------------------------------------
1 | -e .
2 | mock;python_version<="2.7"
3 | futures;python_version<="2.7"
4 | httpretty>=0.8.10
5 | coverage<5.0.0
6 | pytest
7 | pytest-cov
8 | pytest-asyncio;python_full_version>="3.5.2"
9 | mypy;python_full_version>="3.5.2"
10 | pylint
11 | aiohttp;python_full_version>="3.5.2"
12 | # async in msrest was experimental, we won't update
13 | trio==0.14.0;python_version == '3.5'
14 | trio==0.16.0;python_version >= '3.6' and python_version < '3.10'
15 | trio==0.20.0;python_version >= '3.10'
16 |
--------------------------------------------------------------------------------
/doc/index.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | msrest's documentation has moved from ReadTheDocs to docs.microsoft.com.
6 |
7 |
--------------------------------------------------------------------------------
/doc/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=_build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. xml to make Docutils-native XML files
37 | echo. pseudoxml to make pseudoxml-XML files for display purposes
38 | echo. linkcheck to check all external links for integrity
39 | echo. doctest to run all doctests embedded in the documentation if enabled
40 | goto end
41 | )
42 |
43 | if "%1" == "clean" (
44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
45 | del /q /s %BUILDDIR%\*
46 | goto end
47 | )
48 |
49 |
50 | %SPHINXBUILD% 2> nul
51 | if errorlevel 9009 (
52 | echo.
53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
54 | echo.installed, then set the SPHINXBUILD environment variable to point
55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
56 | echo.may add the Sphinx directory to PATH.
57 | echo.
58 | echo.If you don't have Sphinx installed, grab it from
59 | echo.http://sphinx-doc.org/
60 | exit /b 1
61 | )
62 |
63 | if "%1" == "html" (
64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
65 | if errorlevel 1 exit /b 1
66 | echo.
67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
68 | goto end
69 | )
70 |
71 | if "%1" == "dirhtml" (
72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
73 | if errorlevel 1 exit /b 1
74 | echo.
75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
76 | goto end
77 | )
78 |
79 | if "%1" == "singlehtml" (
80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
81 | if errorlevel 1 exit /b 1
82 | echo.
83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
84 | goto end
85 | )
86 |
87 | if "%1" == "pickle" (
88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
89 | if errorlevel 1 exit /b 1
90 | echo.
91 | echo.Build finished; now you can process the pickle files.
92 | goto end
93 | )
94 |
95 | if "%1" == "json" (
96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
97 | if errorlevel 1 exit /b 1
98 | echo.
99 | echo.Build finished; now you can process the JSON files.
100 | goto end
101 | )
102 |
103 | if "%1" == "htmlhelp" (
104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
105 | if errorlevel 1 exit /b 1
106 | echo.
107 | echo.Build finished; now you can run HTML Help Workshop with the ^
108 | .hhp project file in %BUILDDIR%/htmlhelp.
109 | goto end
110 | )
111 |
112 | if "%1" == "qthelp" (
113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
114 | if errorlevel 1 exit /b 1
115 | echo.
116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
117 | .qhcp project file in %BUILDDIR%/qthelp, like this:
118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pydocumentdb.qhcp
119 | echo.To view the help file:
120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pydocumentdb.ghc
121 | goto end
122 | )
123 |
124 | if "%1" == "devhelp" (
125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished.
129 | goto end
130 | )
131 |
132 | if "%1" == "epub" (
133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
134 | if errorlevel 1 exit /b 1
135 | echo.
136 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
137 | goto end
138 | )
139 |
140 | if "%1" == "latex" (
141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
142 | if errorlevel 1 exit /b 1
143 | echo.
144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
145 | goto end
146 | )
147 |
148 | if "%1" == "latexpdf" (
149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
150 | cd %BUILDDIR%/latex
151 | make all-pdf
152 | cd %BUILDDIR%/..
153 | echo.
154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
155 | goto end
156 | )
157 |
158 | if "%1" == "latexpdfja" (
159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
160 | cd %BUILDDIR%/latex
161 | make all-pdf-ja
162 | cd %BUILDDIR%/..
163 | echo.
164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
165 | goto end
166 | )
167 |
168 | if "%1" == "text" (
169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
170 | if errorlevel 1 exit /b 1
171 | echo.
172 | echo.Build finished. The text files are in %BUILDDIR%/text.
173 | goto end
174 | )
175 |
176 | if "%1" == "man" (
177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
178 | if errorlevel 1 exit /b 1
179 | echo.
180 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
181 | goto end
182 | )
183 |
184 | if "%1" == "texinfo" (
185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
186 | if errorlevel 1 exit /b 1
187 | echo.
188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
189 | goto end
190 | )
191 |
192 | if "%1" == "gettext" (
193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
194 | if errorlevel 1 exit /b 1
195 | echo.
196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
197 | goto end
198 | )
199 |
200 | if "%1" == "changes" (
201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
202 | if errorlevel 1 exit /b 1
203 | echo.
204 | echo.The overview file is in %BUILDDIR%/changes.
205 | goto end
206 | )
207 |
208 | if "%1" == "linkcheck" (
209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
210 | if errorlevel 1 exit /b 1
211 | echo.
212 | echo.Link check complete; look for any errors in the above output ^
213 | or in %BUILDDIR%/linkcheck/output.txt.
214 | goto end
215 | )
216 |
217 | if "%1" == "doctest" (
218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
219 | if errorlevel 1 exit /b 1
220 | echo.
221 | echo.Testing of doctests in the sources finished, look at the ^
222 | results in %BUILDDIR%/doctest/output.txt.
223 | goto end
224 | )
225 |
226 | if "%1" == "xml" (
227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
228 | if errorlevel 1 exit /b 1
229 | echo.
230 | echo.Build finished. The XML files are in %BUILDDIR%/xml.
231 | goto end
232 | )
233 |
234 | if "%1" == "pseudoxml" (
235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
236 | if errorlevel 1 exit /b 1
237 | echo.
238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
239 | goto end
240 | )
241 |
242 | :end
243 |
--------------------------------------------------------------------------------
/doc/requirements.txt:
--------------------------------------------------------------------------------
1 | sphinx
2 | sphinx_rtd_theme
3 | recommonmark
--------------------------------------------------------------------------------
/msrest/__init__.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 |
27 | from .version import msrest_version
28 | from .configuration import Configuration
29 | from .service_client import ServiceClient, SDKClient
30 | from .serialization import Serializer, Deserializer
31 |
32 | __all__ = [
33 | "ServiceClient",
34 | "SDKClient",
35 | "Serializer",
36 | "Deserializer",
37 | "Configuration"
38 | ]
39 |
40 | __version__ = msrest_version
41 |
--------------------------------------------------------------------------------
/msrest/async_client.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 |
27 | import functools
28 | import logging
29 |
30 | from typing import Any, Dict, List, Union, TYPE_CHECKING
31 |
32 | from .universal_http import ClientRequest
33 | from .universal_http.async_requests import AsyncRequestsHTTPSender
34 | from .pipeline import Request, AsyncPipeline, AsyncHTTPPolicy, SansIOHTTPPolicy
35 | from .pipeline.async_requests import (
36 | AsyncPipelineRequestsHTTPSender,
37 | AsyncRequestsCredentialsPolicy
38 | )
39 | from .pipeline.universal import (
40 | HTTPLogger,
41 | RawDeserializer,
42 | )
43 |
44 | from .service_client import _ServiceClientCore
45 |
46 | if TYPE_CHECKING:
47 | from .configuration import Configuration # pylint: disable=unused-import
48 |
49 | _LOGGER = logging.getLogger(__name__)
50 |
51 |
52 | class SDKClientAsync:
53 | """The base class of all generated SDK async client.
54 | """
55 |
56 | def __init__(self, config: 'Configuration') -> None:
57 | self._client = ServiceClientAsync(config)
58 |
59 | async def __aenter__(self):
60 | await self._client.__aenter__()
61 | return self
62 |
63 | async def __aexit__(self, *exc_details):
64 | await self._client.__aexit__(*exc_details)
65 |
66 |
67 | class ServiceClientAsync(_ServiceClientCore):
68 |
69 | def __init__(self, config: 'Configuration') -> None:
70 | super(ServiceClientAsync, self).__init__(config)
71 |
72 | self.config.pipeline = self._create_default_pipeline() # type: ignore
73 |
74 | def _create_default_pipeline(self):
75 | creds = self.config.credentials
76 |
77 | policies = [
78 | self.config.user_agent_policy, # UserAgent policy
79 | RawDeserializer(), # Deserialize the raw bytes
80 | self.config.http_logger_policy # HTTP request/response log
81 | ] # type: List[Union[AsyncHTTPPolicy, SansIOHTTPPolicy]]
82 | if creds:
83 | if isinstance(creds, (AsyncHTTPPolicy, SansIOHTTPPolicy)):
84 | policies.insert(1, creds)
85 | else:
86 | # Assume this is the old credentials class, and then requests. Wrap it.
87 | policies.insert(1, AsyncRequestsCredentialsPolicy(creds))
88 |
89 | return AsyncPipeline(
90 | policies,
91 | AsyncPipelineRequestsHTTPSender(
92 | AsyncRequestsHTTPSender(self.config) # Send HTTP request using requests
93 | )
94 | )
95 |
96 | async def __aenter__(self):
97 | await self.config.pipeline.__aenter__()
98 | return self
99 |
100 | async def __aexit__(self, *exc_details):
101 | await self.config.pipeline.__aexit__(*exc_details)
102 |
103 | async def async_send(self, request, **kwargs):
104 | """Prepare and send request object according to configuration.
105 |
106 | :param ClientRequest request: The request object to be sent.
107 | :param dict headers: Any headers to add to the request.
108 | :param content: Any body data to add to the request.
109 | :param config: Any specific config overrides
110 | """
111 | kwargs.setdefault('stream', True)
112 | # In the current backward compatible implementation, return the HTTP response
113 | # and plug context inside. Could be remove if we modify Autorest,
114 | # but we still need it to be backward compatible
115 | pipeline_response = await self.config.pipeline.run(request, **kwargs)
116 | response = pipeline_response.http_response
117 | response.context = pipeline_response.context
118 | return response
119 |
120 | def stream_download_async(self, response, user_callback):
121 | """Async Generator for streaming request body data.
122 |
123 | :param response: The initial response
124 | :param user_callback: Custom callback for monitoring progress.
125 | """
126 | block = self.config.connection.data_block_size
127 | return response.stream_download(block, user_callback)
128 |
--------------------------------------------------------------------------------
/msrest/async_paging.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 | from collections.abc import AsyncIterator
27 | import logging
28 |
29 | _LOGGER = logging.getLogger(__name__)
30 |
31 | class AsyncPagedMixin(AsyncIterator):
32 |
33 | def __init__(self, *args, **kwargs):
34 | """Bring async to Paging.
35 |
36 | "async_command" is mandatory keyword argument for this mixin to work.
37 | """
38 | self._async_get_next = kwargs.get("async_command")
39 | if not self._async_get_next:
40 | _LOGGER.debug("Paging async iterator protocol is not available for %s",
41 | self.__class__.__name__)
42 |
43 | async def async_get(self, url):
44 | """Get an arbitrary page.
45 |
46 | This resets the iterator and then fully consumes it to return the
47 | specific page **only**.
48 |
49 | :param str url: URL to arbitrary page results.
50 | """
51 | self.reset()
52 | self.next_link = url
53 | return await self.async_advance_page()
54 |
55 | async def async_advance_page(self):
56 | if not self._async_get_next:
57 | raise NotImplementedError(
58 | "The class %s does not support async paging at the moment.",
59 | self.__class__.__name__
60 | )
61 | if self.next_link is None:
62 | raise StopAsyncIteration("End of paging")
63 | self._current_page_iter_index = 0
64 | self._response = await self._async_get_next(self.next_link)
65 | self._derserializer(self, self._response)
66 | return self.current_page
67 |
68 | async def __anext__(self):
69 | """Iterate through responses."""
70 | # Storing the list iterator might work out better, but there's no
71 | # guarantee that some code won't replace the list entirely with a copy,
72 | # invalidating an list iterator that might be saved between iterations.
73 | if self.current_page and self._current_page_iter_index < len(self.current_page):
74 | response = self.current_page[self._current_page_iter_index]
75 | self._current_page_iter_index += 1
76 | return response
77 | else:
78 | await self.async_advance_page()
79 | return await self.__anext__()
80 |
--------------------------------------------------------------------------------
/msrest/configuration.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 |
27 |
28 | try:
29 | import configparser
30 | from configparser import NoOptionError
31 | except ImportError:
32 | import ConfigParser as configparser # type: ignore
33 | from ConfigParser import NoOptionError # type: ignore
34 |
35 | from typing import TYPE_CHECKING, Optional, Dict, List, Any, Callable, Union # pylint: disable=unused-import
36 |
37 | from .pipeline import Pipeline
38 | from .universal_http.requests import (
39 | RequestHTTPSenderConfiguration
40 | )
41 | from .pipeline.universal import (
42 | UserAgentPolicy,
43 | HTTPLogger,
44 | )
45 |
46 | if TYPE_CHECKING:
47 | from .pipeline import AsyncPipeline
48 |
49 | class Configuration(RequestHTTPSenderConfiguration):
50 | """Client configuration.
51 |
52 | :param str baseurl: REST API base URL.
53 | :param str filepath: Path to existing config file (optional).
54 | """
55 |
56 | def __init__(self, base_url, filepath=None):
57 | # type: (str, Optional[str]) -> None
58 |
59 | super(Configuration, self).__init__(filepath)
60 | # Service
61 | self.base_url = base_url
62 |
63 | # User-Agent as a policy
64 | self.user_agent_policy = UserAgentPolicy()
65 |
66 | # HTTP logger policy
67 | self.http_logger_policy = HTTPLogger()
68 |
69 | # The pipeline. We don't know until a ServiceClient use this configuration if it will be sync or async
70 | # We instantiate with a default empty Pipeline for mypy mostly, trying to use a pipeline from a pure
71 | # configuration object doesn't make sense.
72 | self.pipeline = Pipeline() # type: Union[Pipeline, AsyncPipeline]
73 |
74 | # If set to True, ServiceClient will own the sessionn
75 | self.keep_alive = False
76 |
77 | # Potential credentials pre-declared
78 | self.credentials = None
79 |
80 | if filepath:
81 | self.load(filepath)
82 |
83 | @property
84 | def user_agent(self):
85 | # type: () -> str
86 | """The current user agent value."""
87 | return self.user_agent_policy.user_agent
88 |
89 | def add_user_agent(self, value):
90 | # type: (str) -> None
91 | """Add value to current user agent with a space.
92 |
93 | :param str value: value to add to user agent.
94 | """
95 | self.user_agent_policy.add_user_agent(value)
96 |
97 | @property
98 | def enable_http_logger(self):
99 | return self.http_logger_policy.enable_http_logger
100 |
101 | @enable_http_logger.setter
102 | def enable_http_logger(self, value):
103 | self.http_logger_policy.enable_http_logger = value
104 |
--------------------------------------------------------------------------------
/msrest/http_logger.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 |
27 | import logging
28 | import re
29 | import types
30 |
31 | from typing import Any, Optional, TYPE_CHECKING # pylint: disable=unused-import
32 |
33 | if TYPE_CHECKING:
34 | from .universal_http import ClientRequest, ClientResponse # pylint: disable=unused-import
35 |
36 | _LOGGER = logging.getLogger(__name__)
37 |
38 |
39 | def log_request(_, request, *_args, **_kwargs):
40 | # type: (Any, ClientRequest, str, str) -> None
41 | """Log a client request.
42 |
43 | :param _: Unused in current version (will be None)
44 | :param requests.Request request: The request object.
45 | """
46 | if not _LOGGER.isEnabledFor(logging.DEBUG):
47 | return
48 |
49 | try:
50 | _LOGGER.debug("Request URL: %r", request.url)
51 | _LOGGER.debug("Request method: %r", request.method)
52 | _LOGGER.debug("Request headers:")
53 | for header, value in request.headers.items():
54 | if header.lower() == 'authorization':
55 | value = '*****'
56 | _LOGGER.debug(" %r: %r", header, value)
57 | _LOGGER.debug("Request body:")
58 |
59 | # We don't want to log the binary data of a file upload.
60 | if isinstance(request.body, types.GeneratorType):
61 | _LOGGER.debug("File upload")
62 | else:
63 | _LOGGER.debug(str(request.body))
64 | except Exception as err: # pylint: disable=broad-except
65 | _LOGGER.debug("Failed to log request: %r", err)
66 |
67 |
68 | def log_response(_, _request, response, *_args, **kwargs):
69 | # type: (Any, ClientRequest, ClientResponse, str, Any) -> Optional[ClientResponse]
70 | """Log a server response.
71 |
72 | :param _: Unused in current version (will be None)
73 | :param requests.Request request: The request object.
74 | :param requests.Response response: The response object.
75 | """
76 | if not _LOGGER.isEnabledFor(logging.DEBUG):
77 | return None
78 |
79 | try:
80 | _LOGGER.debug("Response status: %r", response.status_code)
81 | _LOGGER.debug("Response headers:")
82 | for res_header, value in response.headers.items():
83 | _LOGGER.debug(" %r: %r", res_header, value)
84 |
85 | # We don't want to log binary data if the response is a file.
86 | _LOGGER.debug("Response content:")
87 | pattern = re.compile(r'attachment; ?filename=["\w.]+', re.IGNORECASE)
88 | header = response.headers.get('content-disposition')
89 |
90 | if header and pattern.match(header):
91 | filename = header.partition('=')[2]
92 | _LOGGER.debug("File attachments: %s", filename)
93 | elif response.headers.get("content-type", "").endswith("octet-stream"):
94 | _LOGGER.debug("Body contains binary data.")
95 | elif response.headers.get("content-type", "").startswith("image"):
96 | _LOGGER.debug("Body contains image data.")
97 | else:
98 | if kwargs.get('stream', False):
99 | _LOGGER.debug("Body is streamable")
100 | else:
101 | _LOGGER.debug(response.text())
102 | return response
103 | except Exception as err: # pylint: disable=broad-except
104 | _LOGGER.debug("Failed to log response: %s", repr(err))
105 | return response
106 |
--------------------------------------------------------------------------------
/msrest/paging.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 | import sys
27 | try:
28 | from collections.abc import Iterator
29 | xrange = range
30 | except ImportError:
31 | from collections import Iterator
32 |
33 | from typing import Dict, Any, List, Callable, Optional, TYPE_CHECKING # pylint: disable=unused-import
34 |
35 | from .serialization import Deserializer
36 | from .pipeline import ClientRawResponse
37 |
38 | if TYPE_CHECKING:
39 | from .universal_http import ClientResponse # pylint: disable=unused-import
40 | from .serialization import Model # pylint: disable=unused-import
41 |
42 | if sys.version_info >= (3, 5, 2):
43 | # Not executed on old Python, no syntax error
44 | from .async_paging import AsyncPagedMixin # type: ignore
45 | else:
46 | class AsyncPagedMixin(object): # type: ignore
47 | pass
48 |
49 | class Paged(AsyncPagedMixin, Iterator):
50 | """A container for paged REST responses.
51 |
52 | :param ClientResponse response: server response object.
53 | :param callable command: Function to retrieve the next page of items.
54 | :param dict classes: A dictionary of class dependencies for
55 | deserialization.
56 | :param dict raw_headers: A dict of raw headers to add if "raw" is called
57 | """
58 | _validation = {} # type: Dict[str, Dict[str, Any]]
59 | _attribute_map = {} # type: Dict[str, Dict[str, Any]]
60 |
61 | def __init__(self, command, classes, raw_headers=None, **kwargs):
62 | # type: (Callable[[str], ClientResponse], Dict[str, Model], Dict[str, str], Any) -> None
63 | super(Paged, self).__init__(**kwargs) # type: ignore
64 | # Sets next_link, current_page, and _current_page_iter_index.
65 | self.next_link = ""
66 | self._current_page_iter_index = 0
67 | self.reset()
68 | self._derserializer = Deserializer(classes)
69 | self._get_next = command
70 | self._response = None # type: Optional[ClientResponse]
71 | self._raw_headers = raw_headers
72 |
73 | def __iter__(self):
74 | """Return 'self'."""
75 | # Since iteration mutates this object, consider it an iterator in-and-of
76 | # itself.
77 | return self
78 |
79 | @classmethod
80 | def _get_subtype_map(cls):
81 | """Required for parity to Model object for deserialization."""
82 | return {}
83 |
84 | @property
85 | def raw(self):
86 | # type: () -> ClientRawResponse
87 | """Get current page as ClientRawResponse.
88 |
89 | :rtype: ClientRawResponse
90 | """
91 | raw = ClientRawResponse(self.current_page, self._response)
92 | if self._raw_headers:
93 | raw.add_headers(self._raw_headers)
94 | return raw
95 |
96 | def get(self, url):
97 | # type: (str) -> List[Model]
98 | """Get an arbitrary page.
99 |
100 | This resets the iterator and then fully consumes it to return the
101 | specific page **only**.
102 |
103 | :param str url: URL to arbitrary page results.
104 | """
105 | self.reset()
106 | self.next_link = url
107 | return self.advance_page()
108 |
109 | def reset(self):
110 | # type: () -> None
111 | """Reset iterator to first page."""
112 | self.next_link = ""
113 | self.current_page = [] # type: List[Model]
114 | self._current_page_iter_index = 0
115 |
116 | def advance_page(self):
117 | # type: () -> List[Model]
118 | """Force moving the cursor to the next azure call.
119 |
120 | This method is for advanced usage, iterator protocol is preferred.
121 |
122 | :raises: StopIteration if no further page
123 | :return: The current page list
124 | :rtype: list
125 | """
126 | if self.next_link is None:
127 | raise StopIteration("End of paging")
128 | self._current_page_iter_index = 0
129 | self._response = self._get_next(self.next_link)
130 | self._derserializer(self, self._response)
131 | return self.current_page
132 |
133 | def __next__(self):
134 | """Iterate through responses."""
135 | # Storing the list iterator might work out better, but there's no
136 | # guarantee that some code won't replace the list entirely with a copy,
137 | # invalidating an list iterator that might be saved between iterations.
138 | if self.current_page and self._current_page_iter_index < len(self.current_page):
139 | response = self.current_page[self._current_page_iter_index]
140 | self._current_page_iter_index += 1
141 | return response
142 | else:
143 | self.advance_page()
144 | return self.__next__()
145 |
146 | next = __next__ # Python 2 compatibility.
147 |
--------------------------------------------------------------------------------
/msrest/pipeline/aiohttp.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 | from typing import Any, Optional
27 |
28 | from ..universal_http.aiohttp import AioHTTPSender as _AioHTTPSenderDriver
29 | from . import AsyncHTTPSender, Request, Response
30 |
31 | # Matching requests, because why not?
32 | CONTENT_CHUNK_SIZE = 10 * 1024
33 |
34 | class AioHTTPSender(AsyncHTTPSender):
35 | """AioHttp HTTP sender implementation.
36 | """
37 |
38 | def __init__(self, driver: Optional[_AioHTTPSenderDriver] = None, *, loop=None) -> None:
39 | self.driver = driver or _AioHTTPSenderDriver(loop=loop)
40 |
41 | async def __aenter__(self):
42 | await self.driver.__aenter__()
43 |
44 | async def __aexit__(self, *exc_details): # pylint: disable=arguments-differ
45 | await self.driver.__aexit__(*exc_details)
46 |
47 | def build_context(self) -> Any:
48 | """Allow the sender to build a context that will be passed
49 | across the pipeline with the request.
50 |
51 | Return type has no constraints. Implementation is not
52 | required and None by default.
53 | """
54 | return None
55 |
56 | async def send(self, request: Request, **config: Any) -> Response:
57 | """Send the request using this HTTP sender.
58 | """
59 | return Response(
60 | request,
61 | await self.driver.send(request.http_request)
62 | )
63 |
--------------------------------------------------------------------------------
/msrest/pipeline/async_abc.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 | import abc
27 |
28 | from typing import Any, List, Union, Callable, AsyncIterator, Optional, Generic, TypeVar
29 |
30 | from . import Request, Response, Pipeline, SansIOHTTPPolicy
31 |
32 |
33 | AsyncHTTPResponseType = TypeVar("AsyncHTTPResponseType")
34 | HTTPRequestType = TypeVar("HTTPRequestType")
35 |
36 | try:
37 | from contextlib import AbstractAsyncContextManager # type: ignore
38 | except ImportError: # Python <= 3.7
39 | class AbstractAsyncContextManager(object): # type: ignore
40 | async def __aenter__(self):
41 | """Return `self` upon entering the runtime context."""
42 | return self
43 |
44 | @abc.abstractmethod
45 | async def __aexit__(self, exc_type, exc_value, traceback):
46 | """Raise any exception triggered within the runtime context."""
47 | return None
48 |
49 |
50 |
51 |
52 | class AsyncHTTPPolicy(abc.ABC, Generic[HTTPRequestType, AsyncHTTPResponseType]):
53 | """An http policy ABC.
54 | """
55 | def __init__(self) -> None:
56 | # next will be set once in the pipeline
57 | self.next = None # type: Optional[Union[AsyncHTTPPolicy[HTTPRequestType, AsyncHTTPResponseType], AsyncHTTPSender[HTTPRequestType, AsyncHTTPResponseType]]]
58 |
59 | @abc.abstractmethod
60 | async def send(self, request: Request, **kwargs: Any) -> Response[HTTPRequestType, AsyncHTTPResponseType]:
61 | """Mutate the request.
62 |
63 | Context content is dependent of the HTTPSender.
64 | """
65 | pass
66 |
67 |
68 | class _SansIOAsyncHTTPPolicyRunner(AsyncHTTPPolicy[HTTPRequestType, AsyncHTTPResponseType]):
69 | """Async implementation of the SansIO policy.
70 | """
71 |
72 | def __init__(self, policy: SansIOHTTPPolicy) -> None:
73 | super(_SansIOAsyncHTTPPolicyRunner, self).__init__()
74 | self._policy = policy
75 |
76 | async def send(self, request: Request, **kwargs: Any) -> Response[HTTPRequestType, AsyncHTTPResponseType]:
77 | self._policy.on_request(request, **kwargs)
78 | try:
79 | response = await self.next.send(request, **kwargs) # type: ignore
80 | except Exception:
81 | if not self._policy.on_exception(request, **kwargs):
82 | raise
83 | else:
84 | self._policy.on_response(request, response, **kwargs)
85 | return response
86 |
87 |
88 | class AsyncHTTPSender(AbstractAsyncContextManager, abc.ABC, Generic[HTTPRequestType, AsyncHTTPResponseType]):
89 | """An http sender ABC.
90 | """
91 |
92 | @abc.abstractmethod
93 | async def send(self, request: Request[HTTPRequestType], **config: Any) -> Response[HTTPRequestType, AsyncHTTPResponseType]:
94 | """Send the request using this HTTP sender.
95 | """
96 | pass
97 |
98 | def build_context(self) -> Any:
99 | """Allow the sender to build a context that will be passed
100 | across the pipeline with the request.
101 |
102 | Return type has no constraints. Implementation is not
103 | required and None by default.
104 | """
105 | return None
106 |
107 | def __enter__(self):
108 | raise TypeError("Use async with instead")
109 |
110 | def __exit__(self, exc_type, exc_val, exc_tb):
111 | # __exit__ should exist in pair with __enter__ but never executed
112 | pass # pragma: no cover
113 |
114 |
115 | class AsyncPipeline(AbstractAsyncContextManager, Generic[HTTPRequestType, AsyncHTTPResponseType]):
116 | """A pipeline implementation.
117 |
118 | This is implemented as a context manager, that will activate the context
119 | of the HTTP sender.
120 | """
121 |
122 | def __init__(self, policies: List[Union[AsyncHTTPPolicy, SansIOHTTPPolicy]] = None, sender: Optional[AsyncHTTPSender[HTTPRequestType, AsyncHTTPResponseType]] = None) -> None:
123 | self._impl_policies = [] # type: List[AsyncHTTPPolicy[HTTPRequestType, AsyncHTTPResponseType]]
124 | if sender:
125 | self._sender = sender
126 | else:
127 | # Import default only if nothing is provided
128 | from .aiohttp import AioHTTPSender
129 | self._sender = AioHTTPSender()
130 |
131 | for policy in (policies or []):
132 | if isinstance(policy, SansIOHTTPPolicy):
133 | self._impl_policies.append(_SansIOAsyncHTTPPolicyRunner(policy))
134 | else:
135 | self._impl_policies.append(policy)
136 | for index in range(len(self._impl_policies)-1):
137 | self._impl_policies[index].next = self._impl_policies[index+1]
138 | if self._impl_policies:
139 | self._impl_policies[-1].next = self._sender
140 |
141 | def __enter__(self):
142 | raise TypeError("Use 'async with' instead")
143 |
144 | def __exit__(self, exc_type, exc_val, exc_tb):
145 | # __exit__ should exist in pair with __enter__ but never executed
146 | pass # pragma: no cover
147 |
148 | async def __aenter__(self) -> 'AsyncPipeline':
149 | await self._sender.__aenter__()
150 | return self
151 |
152 | async def __aexit__(self, *exc_details): # pylint: disable=arguments-differ
153 | await self._sender.__aexit__(*exc_details)
154 |
155 | async def run(self, request: Request, **kwargs: Any) -> Response[HTTPRequestType, AsyncHTTPResponseType]:
156 | context = self._sender.build_context()
157 | pipeline_request = Request(request, context)
158 | first_node = self._impl_policies[0] if self._impl_policies else self._sender
159 | return await first_node.send(pipeline_request, **kwargs) # type: ignore
160 |
161 | __all__ = [
162 | 'AsyncHTTPPolicy',
163 | 'AsyncHTTPSender',
164 | 'AsyncPipeline',
165 | ]
--------------------------------------------------------------------------------
/msrest/pipeline/async_requests.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 | import asyncio
27 | from collections.abc import AsyncIterator
28 | import functools
29 | import logging
30 | from typing import Any, Callable, Optional, AsyncIterator as AsyncIteratorType
31 |
32 | from oauthlib import oauth2
33 | import requests
34 | from requests.models import CONTENT_CHUNK_SIZE
35 |
36 | from ..exceptions import (
37 | TokenExpiredError,
38 | ClientRequestError,
39 | raise_with_traceback
40 | )
41 | from ..universal_http.async_requests import AsyncBasicRequestsHTTPSender
42 | from . import AsyncHTTPSender, AsyncHTTPPolicy, Response, Request
43 | from .requests import RequestsContext
44 |
45 |
46 | _LOGGER = logging.getLogger(__name__)
47 |
48 |
49 | class AsyncPipelineRequestsHTTPSender(AsyncHTTPSender):
50 | """Implements a basic Pipeline, that supports universal HTTP lib "requests" driver.
51 | """
52 |
53 | def __init__(self, universal_http_requests_driver: Optional[AsyncBasicRequestsHTTPSender]=None) -> None:
54 | self.driver = universal_http_requests_driver or AsyncBasicRequestsHTTPSender()
55 |
56 | async def __aenter__(self) -> 'AsyncPipelineRequestsHTTPSender':
57 | await self.driver.__aenter__()
58 | return self
59 |
60 | async def __aexit__(self, *exc_details): # pylint: disable=arguments-differ
61 | await self.driver.__aexit__(*exc_details)
62 |
63 | async def close(self):
64 | await self.__aexit__()
65 |
66 | def build_context(self):
67 | # type: () -> RequestsContext
68 | return RequestsContext(
69 | session=self.driver.session,
70 | )
71 |
72 | async def send(self, request: Request, **kwargs) -> Response:
73 | """Send request object according to configuration.
74 |
75 | :param Request request: The request object to be sent.
76 | """
77 | if request.context is None: # Should not happen, but make mypy happy and does not hurt
78 | request.context = self.build_context()
79 |
80 | if request.context.session is not self.driver.session:
81 | kwargs['session'] = request.context.session
82 |
83 | return Response(
84 | request,
85 | await self.driver.send(request.http_request, **kwargs)
86 | )
87 |
88 |
89 | class AsyncRequestsCredentialsPolicy(AsyncHTTPPolicy):
90 | """Implementation of request-oauthlib except and retry logic.
91 | """
92 | def __init__(self, credentials):
93 | super(AsyncRequestsCredentialsPolicy, self).__init__()
94 | self._creds = credentials
95 |
96 | async def send(self, request, **kwargs):
97 | session = request.context.session
98 | try:
99 | self._creds.signed_session(session)
100 | except TypeError: # Credentials does not support session injection
101 | _LOGGER.warning("Your credentials class does not support session injection. Performance will not be at the maximum.")
102 | request.context.session = session = self._creds.signed_session()
103 |
104 | try:
105 | try:
106 | return await self.next.send(request, **kwargs)
107 | except (oauth2.rfc6749.errors.InvalidGrantError,
108 | oauth2.rfc6749.errors.TokenExpiredError) as err:
109 | error = "Token expired or is invalid. Attempting to refresh."
110 | _LOGGER.warning(error)
111 |
112 | try:
113 | try:
114 | self._creds.refresh_session(session)
115 | except TypeError: # Credentials does not support session injection
116 | _LOGGER.warning("Your credentials class does not support session injection. Performance will not be at the maximum.")
117 | request.context.session = session = self._creds.refresh_session()
118 |
119 | return await self.next.send(request, **kwargs)
120 | except (oauth2.rfc6749.errors.InvalidGrantError,
121 | oauth2.rfc6749.errors.TokenExpiredError) as err:
122 | msg = "Token expired or is invalid."
123 | raise_with_traceback(TokenExpiredError, msg, err)
124 |
125 | except (requests.RequestException,
126 | oauth2.rfc6749.errors.OAuth2Error) as err:
127 | msg = "Error occurred in request."
128 | raise_with_traceback(ClientRequestError, msg, err)
129 |
130 |
--------------------------------------------------------------------------------
/msrest/pipeline/requests.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 | """
27 | This module is the requests implementation of Pipeline ABC
28 | """
29 | from __future__ import absolute_import # we have a "requests" module that conflicts with "requests" on Py2.7
30 | import contextlib
31 | import logging
32 | import threading
33 | from typing import TYPE_CHECKING, List, Callable, Iterator, Any, Union, Dict, Optional # pylint: disable=unused-import
34 | import warnings
35 |
36 | from oauthlib import oauth2
37 | import requests
38 | from requests.models import CONTENT_CHUNK_SIZE
39 |
40 | from urllib3 import Retry # Needs requests 2.16 at least to be safe
41 |
42 | from ..exceptions import (
43 | TokenExpiredError,
44 | ClientRequestError,
45 | raise_with_traceback
46 | )
47 | from ..universal_http import ClientRequest
48 | from ..universal_http.requests import BasicRequestsHTTPSender
49 | from . import HTTPSender, HTTPPolicy, Response, Request
50 |
51 |
52 | _LOGGER = logging.getLogger(__name__)
53 |
54 |
55 | class RequestsCredentialsPolicy(HTTPPolicy):
56 | """Implementation of request-oauthlib except and retry logic.
57 | """
58 | def __init__(self, credentials):
59 | super(RequestsCredentialsPolicy, self).__init__()
60 | self._creds = credentials
61 |
62 | def send(self, request, **kwargs):
63 | session = request.context.session
64 | try:
65 | self._creds.signed_session(session)
66 | except TypeError: # Credentials does not support session injection
67 | _LOGGER.warning("Your credentials class does not support session injection. Performance will not be at the maximum.")
68 | request.context.session = session = self._creds.signed_session()
69 |
70 | try:
71 | try:
72 | return self.next.send(request, **kwargs)
73 | except (oauth2.rfc6749.errors.InvalidGrantError,
74 | oauth2.rfc6749.errors.TokenExpiredError) as err:
75 | error = "Token expired or is invalid. Attempting to refresh."
76 | _LOGGER.warning(error)
77 |
78 | try:
79 | try:
80 | self._creds.refresh_session(session)
81 | except TypeError: # Credentials does not support session injection
82 | _LOGGER.warning("Your credentials class does not support session injection. Performance will not be at the maximum.")
83 | request.context.session = session = self._creds.refresh_session()
84 |
85 | return self.next.send(request, **kwargs)
86 | except (oauth2.rfc6749.errors.InvalidGrantError,
87 | oauth2.rfc6749.errors.TokenExpiredError) as err:
88 | msg = "Token expired or is invalid."
89 | raise_with_traceback(TokenExpiredError, msg, err)
90 |
91 | except (requests.RequestException,
92 | oauth2.rfc6749.errors.OAuth2Error) as err:
93 | msg = "Error occurred in request."
94 | raise_with_traceback(ClientRequestError, msg, err)
95 |
96 | class RequestsPatchSession(HTTPPolicy):
97 | """Implements request level configuration
98 | that are actually to be done at the session level.
99 |
100 | This is highly deprecated, and is totally legacy.
101 | The pipeline structure allows way better design for this.
102 | """
103 | _protocols = ['http://', 'https://']
104 |
105 | def send(self, request, **kwargs):
106 | """Patch the current session with Request level operation config.
107 |
108 | This is deprecated, we shouldn't patch the session with
109 | arguments at the Request, and "config" should be used.
110 | """
111 | session = request.context.session
112 |
113 | old_max_redirects = None
114 | if 'max_redirects' in kwargs:
115 | warnings.warn("max_redirects in operation kwargs is deprecated, use config.redirect_policy instead",
116 | DeprecationWarning)
117 | old_max_redirects = session.max_redirects
118 | session.max_redirects = int(kwargs['max_redirects'])
119 |
120 | old_trust_env = None
121 | if 'use_env_proxies' in kwargs:
122 | warnings.warn("use_env_proxies in operation kwargs is deprecated, use config.proxies instead",
123 | DeprecationWarning)
124 | old_trust_env = session.trust_env
125 | session.trust_env = bool(kwargs['use_env_proxies'])
126 |
127 | old_retries = {}
128 | if 'retries' in kwargs:
129 | warnings.warn("retries in operation kwargs is deprecated, use config.retry_policy instead",
130 | DeprecationWarning)
131 | max_retries = kwargs['retries']
132 | for protocol in self._protocols:
133 | old_retries[protocol] = session.adapters[protocol].max_retries
134 | session.adapters[protocol].max_retries = max_retries
135 |
136 | try:
137 | return self.next.send(request, **kwargs)
138 | finally:
139 | if old_max_redirects:
140 | session.max_redirects = old_max_redirects
141 |
142 | if old_trust_env:
143 | session.trust_env = old_trust_env
144 |
145 | if old_retries:
146 | for protocol in self._protocols:
147 | session.adapters[protocol].max_retries = old_retries[protocol]
148 |
149 | class RequestsContext(object):
150 | def __init__(self, session):
151 | self.session = session
152 |
153 |
154 | class PipelineRequestsHTTPSender(HTTPSender):
155 | """Implements a basic Pipeline, that supports universal HTTP lib "requests" driver.
156 | """
157 |
158 | def __init__(self, universal_http_requests_driver=None):
159 | # type: (Optional[BasicRequestsHTTPSender]) -> None
160 | self.driver = universal_http_requests_driver or BasicRequestsHTTPSender()
161 |
162 | def __enter__(self):
163 | # type: () -> PipelineRequestsHTTPSender
164 | self.driver.__enter__()
165 | return self
166 |
167 | def __exit__(self, *exc_details): # pylint: disable=arguments-differ
168 | self.driver.__exit__(*exc_details)
169 |
170 | def close(self):
171 | self.__exit__()
172 |
173 | def build_context(self):
174 | # type: () -> RequestsContext
175 | return RequestsContext(
176 | session=self.driver.session,
177 | )
178 |
179 | def send(self, request, **kwargs):
180 | # type: (Request[ClientRequest], Any) -> Response
181 | """Send request object according to configuration.
182 |
183 | :param Request request: The request object to be sent.
184 | """
185 | if request.context is None: # Should not happen, but make mypy happy and does not hurt
186 | request.context = self.build_context()
187 |
188 | if request.context.session is not self.driver.session:
189 | kwargs['session'] = request.context.session
190 |
191 | return Response(
192 | request,
193 | self.driver.send(request.http_request, **kwargs)
194 | )
195 |
--------------------------------------------------------------------------------
/msrest/polling/__init__.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 | import sys
27 |
28 | from .poller import LROPoller, NoPolling, PollingMethod
29 | __all__ = ['LROPoller', 'NoPolling', 'PollingMethod']
30 |
31 | if sys.version_info >= (3, 5, 2):
32 | # Not executed on old Python, no syntax error
33 | from .async_poller import AsyncNoPolling, AsyncPollingMethod, async_poller
34 | __all__ += ['AsyncNoPolling', 'AsyncPollingMethod', 'async_poller']
35 |
--------------------------------------------------------------------------------
/msrest/polling/async_poller.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 | from .poller import NoPolling as _NoPolling
27 |
28 | from ..serialization import Model
29 | from ..async_client import ServiceClientAsync
30 | from ..pipeline import ClientRawResponse
31 |
32 | class AsyncPollingMethod(object):
33 | """ABC class for polling method.
34 | """
35 | def initialize(self, client, initial_response, deserialization_callback):
36 | raise NotImplementedError("This method needs to be implemented")
37 |
38 | async def run(self):
39 | raise NotImplementedError("This method needs to be implemented")
40 |
41 | def status(self):
42 | raise NotImplementedError("This method needs to be implemented")
43 |
44 | def finished(self):
45 | raise NotImplementedError("This method needs to be implemented")
46 |
47 | def resource(self):
48 | raise NotImplementedError("This method needs to be implemented")
49 |
50 |
51 | class AsyncNoPolling(_NoPolling):
52 | """An empty async poller that returns the deserialized initial response.
53 | """
54 | async def run(self):
55 | """Empty run, no polling.
56 |
57 | Just override initial run to add "async"
58 | """
59 | pass
60 |
61 |
62 | async def async_poller(client, initial_response, deserialization_callback, polling_method):
63 | """Async Poller for long running operations.
64 |
65 | :param client: A msrest service client. Can be a SDK client and it will be casted to a ServiceClient.
66 | :type client: msrest.service_client.ServiceClient
67 | :param initial_response: The initial call response
68 | :type initial_response: msrest.universal_http.ClientResponse or msrest.pipeline.ClientRawResponse
69 | :param deserialization_callback: A callback that takes a Response and return a deserialized object. If a subclass of Model is given, this passes "deserialize" as callback.
70 | :type deserialization_callback: callable or msrest.serialization.Model
71 | :param polling_method: The polling strategy to adopt
72 | :type polling_method: msrest.polling.PollingMethod
73 | """
74 |
75 | try:
76 | client = client if isinstance(client, ServiceClientAsync) else client._client
77 | except AttributeError:
78 | raise ValueError("Poller client parameter must be a low-level msrest Service Client or a SDK client.")
79 | response = initial_response.response if isinstance(initial_response, ClientRawResponse) else initial_response
80 |
81 | if isinstance(deserialization_callback, type) and issubclass(deserialization_callback, Model):
82 | deserialization_callback = deserialization_callback.deserialize
83 |
84 | # Might raise a CloudError
85 | polling_method.initialize(client, response, deserialization_callback)
86 |
87 | await polling_method.run()
88 | return polling_method.resource()
89 |
--------------------------------------------------------------------------------
/msrest/py.typed:
--------------------------------------------------------------------------------
1 | # Marker file for PEP 561.
--------------------------------------------------------------------------------
/msrest/universal_http/aiohttp.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 | from typing import Any, Callable, AsyncIterator, Optional
27 |
28 | import aiohttp
29 | from multidict import CIMultiDict
30 |
31 | from . import AsyncHTTPSender, ClientRequest, AsyncClientResponse
32 |
33 | # Matching requests, because why not?
34 | CONTENT_CHUNK_SIZE = 10 * 1024
35 |
36 |
37 | class AioHTTPSender(AsyncHTTPSender):
38 | """AioHttp HTTP sender implementation.
39 | """
40 |
41 | def __init__(self, *, loop=None):
42 | self._session = aiohttp.ClientSession(loop=loop)
43 |
44 | async def __aenter__(self):
45 | await self._session.__aenter__()
46 | return self
47 |
48 | async def __aexit__(self, *exc_details): # pylint: disable=arguments-differ
49 | await self._session.__aexit__(*exc_details)
50 |
51 | async def send(self, request: ClientRequest, **config: Any) -> AsyncClientResponse:
52 | """Send the request using this HTTP sender.
53 |
54 | Will pre-load the body into memory to be available with a sync method.
55 | pass stream=True to avoid this behavior.
56 | """
57 | result = await self._session.request(
58 | request.method,
59 | request.url,
60 | **config
61 | )
62 | response = AioHttpClientResponse(request, result)
63 | if not config.get("stream", False):
64 | await response.load_body()
65 | return response
66 |
67 |
68 | class AioHttpClientResponse(AsyncClientResponse):
69 | def __init__(self, request: ClientRequest, aiohttp_response: aiohttp.ClientResponse) -> None:
70 | super(AioHttpClientResponse, self).__init__(request, aiohttp_response)
71 | # https://aiohttp.readthedocs.io/en/stable/client_reference.html#aiohttp.ClientResponse
72 | self.status_code = aiohttp_response.status
73 | self.headers = CIMultiDict(aiohttp_response.headers)
74 | self.reason = aiohttp_response.reason
75 | self._body = None
76 |
77 | def body(self) -> bytes:
78 | """Return the whole body as bytes in memory.
79 | """
80 | if not self._body:
81 | raise ValueError("Body is not available. Call async method load_body, or do your call with stream=False.")
82 | return self._body
83 |
84 | async def load_body(self) -> None:
85 | """Load in memory the body, so it could be accessible from sync methods."""
86 | self._body = await self.internal_response.read()
87 |
88 | def raise_for_status(self):
89 | self.internal_response.raise_for_status()
90 |
91 | def stream_download(self, chunk_size: Optional[int] = None, callback: Optional[Callable] = None) -> AsyncIterator[bytes]:
92 | """Generator for streaming request body data.
93 | """
94 | chunk_size = chunk_size or CONTENT_CHUNK_SIZE
95 | async def async_gen(resp):
96 | while True:
97 | chunk = await resp.content.read(chunk_size)
98 | if not chunk:
99 | break
100 | callback(chunk, resp)
101 | return async_gen(self.internal_response)
102 |
--------------------------------------------------------------------------------
/msrest/universal_http/async_abc.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 | import abc
27 |
28 | from typing import Any, List, Union, Callable, AsyncIterator, Optional
29 |
30 | try:
31 | from contextlib import AbstractAsyncContextManager # type: ignore
32 | except ImportError: # Python <= 3.7
33 | class AbstractAsyncContextManager(object): # type: ignore
34 | async def __aenter__(self):
35 | """Return `self` upon entering the runtime context."""
36 | return self
37 |
38 | @abc.abstractmethod
39 | async def __aexit__(self, exc_type, exc_value, traceback):
40 | """Raise any exception triggered within the runtime context."""
41 | return None
42 |
43 | from . import ClientRequest, HTTPClientResponse
44 |
45 |
46 | class AsyncClientResponse(HTTPClientResponse):
47 |
48 | def stream_download(self, chunk_size: Optional[int] = None, callback: Optional[Callable] = None) -> AsyncIterator[bytes]:
49 | """Generator for streaming request body data.
50 |
51 | Should be implemented by sub-classes if streaming download
52 | is supported.
53 |
54 | :param callback: Custom callback for monitoring progress.
55 | :param int chunk_size:
56 | """
57 | pass
58 |
59 |
60 | class AsyncHTTPSender(AbstractAsyncContextManager, abc.ABC):
61 | """An http sender ABC.
62 | """
63 |
64 | @abc.abstractmethod
65 | async def send(self, request: ClientRequest, **config: Any) -> AsyncClientResponse:
66 | """Send the request using this HTTP sender.
67 | """
68 | pass
69 |
70 | def build_context(self) -> Any:
71 | """Allow the sender to build a context that will be passed
72 | across the pipeline with the request.
73 |
74 | Return type has no constraints. Implementation is not
75 | required and None by default.
76 | """
77 | return None
78 |
79 | def __enter__(self):
80 | raise TypeError("Use 'async with' instead")
81 |
82 | def __exit__(self, exc_type, exc_val, exc_tb):
83 | # __exit__ should exist in pair with __enter__ but never executed
84 | pass # pragma: no cover
85 |
86 |
87 | __all__ = [
88 | 'AsyncHTTPSender',
89 | 'AsyncClientResponse'
90 | ]
--------------------------------------------------------------------------------
/msrest/version.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 |
27 | #: version of this package. Use msrest.__version__ instead
28 | msrest_version = "0.7.1"
29 |
--------------------------------------------------------------------------------
/pylintrc:
--------------------------------------------------------------------------------
1 | [MASTER]
2 | ignore-patterns=test_*
3 | reports=no
4 |
5 | [MESSAGES CONTROL]
6 | # For all codes, run 'pylint --list-msgs' or go to 'https://pylint.readthedocs.io/en/latest/reference_guide/features.html'
7 | # locally-disabled: Warning locally suppressed using disable-msg
8 | # cyclic-import: because of https://github.com/PyCQA/pylint/issues/850
9 | # too-many-arguments: Due to the nature of the CLI many commands have large arguments set which reflect in large arguments set in corresponding methods.
10 | disable=missing-docstring,locally-disabled,fixme,cyclic-import,too-many-arguments,invalid-name,duplicate-code
11 |
12 | [FORMAT]
13 | max-line-length=120
14 |
15 | [VARIABLES]
16 | # Tells whether we should check for unused import in __init__ files.
17 | init-import=yes
18 |
19 | [DESIGN]
20 | # Maximum number of locals for function / method body
21 | max-locals=25
22 | # Maximum number of branch for function / method body
23 | max-branches=20
24 |
25 | [SIMILARITIES]
26 | min-similarity-lines=10
27 |
28 | [BASIC]
29 | # Naming hints based on PEP 8 (https://www.python.org/dev/peps/pep-0008/#naming-conventions).
30 | # Consider these guidelines and not hard rules. Read PEP 8 for more details.
31 |
32 | # The invalid-name checker must be **enabled** for these hints to be used.
33 | include-naming-hint=yes
34 |
35 | module-name-hint=lowercase (keep short; underscores are discouraged)
36 | const-name-hint=UPPER_CASE_WITH_UNDERSCORES
37 | class-name-hint=CapitalizedWords
38 | class-attribute-name-hint=lower_case_with_underscores
39 | attr-name-hint=lower_case_with_underscores
40 | method-name-hint=lower_case_with_underscores
41 | function-name-hint=lower_case_with_underscores
42 | argument-name-hint=lower_case_with_underscores
43 | variable-name-hint=lower_case_with_underscores
44 | inlinevar-name-hint=lower_case_with_underscores (short is OK)
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [mypy]
2 | ignore_missing_imports = True
3 |
4 | [tool:pytest]
5 | addopts = --durations=10
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 |
27 | from setuptools import setup, find_packages
28 |
29 | setup(
30 | name='msrest',
31 | version='0.7.1',
32 | author='Microsoft Corporation',
33 | packages=find_packages(exclude=["tests", "tests.*"]),
34 | url="https://github.com/Azure/msrest-for-python",
35 | license='MIT License',
36 | description='AutoRest swagger generator Python client runtime.',
37 | long_description=open('README.rst').read(),
38 | classifiers=[
39 | 'Development Status :: 4 - Beta',
40 | 'Programming Language :: Python',
41 | 'Programming Language :: Python :: 3 :: Only',
42 | 'Programming Language :: Python :: 3',
43 | 'Programming Language :: Python :: 3.6',
44 | 'Programming Language :: Python :: 3.7',
45 | 'Programming Language :: Python :: 3.8',
46 | 'Programming Language :: Python :: 3.9',
47 | 'Programming Language :: Python :: 3.10',
48 | 'Programming Language :: Python :: 3.11',
49 | 'License :: OSI Approved :: MIT License',
50 | 'Topic :: Software Development'],
51 | python_requires=">=3.6",
52 | install_requires=[
53 | "requests~=2.16",
54 | "requests_oauthlib>=0.5.0",
55 | "isodate>=0.6.0",
56 | "certifi>=2017.4.17",
57 | "azure-core>=1.24.0",
58 | ],
59 | include_package_data=True,
60 | package_data={
61 | 'pytyped': ['py.typed'],
62 | },
63 | extras_require={
64 | "async:python_version>='3.5'": [
65 | 'aiohttp>=3.0',
66 | 'aiodns'
67 | ],
68 | }
69 | )
70 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 |
27 | import os
28 | from unittest import TestLoader, TextTestRunner
29 | import logging
30 | #logging.basicConfig(level=logging.DEBUG, filename="d:/log.txt")
31 |
32 | if __name__ == '__main__':
33 |
34 | runner = TextTestRunner(verbosity=2)
35 | test_dir = os.path.dirname(__file__)
36 |
37 | test_loader = TestLoader()
38 | suite = test_loader.discover(test_dir, pattern="unittest_*.py")
39 | runner.run(suite)
40 |
--------------------------------------------------------------------------------
/tests/asynctests/test_async_client.py:
--------------------------------------------------------------------------------
1 | #--------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to deal
9 | # in the Software without restriction, including without limitation the rights
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | # copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | # THE SOFTWARE.
24 | #
25 | #--------------------------------------------------------------------------
26 |
27 | import io
28 | import asyncio
29 | import json
30 | import unittest
31 | try:
32 | from unittest import mock
33 | except ImportError:
34 | import mock
35 | import sys
36 |
37 | import pytest
38 |
39 | import requests
40 | from requests.adapters import HTTPAdapter
41 | from oauthlib import oauth2
42 |
43 | from msrest.async_client import ServiceClientAsync
44 | from msrest.authentication import OAuthTokenAuthentication
45 | from msrest.configuration import Configuration
46 |
47 | from msrest import Configuration
48 | from msrest.exceptions import ClientRequestError, TokenExpiredError
49 | from msrest.universal_http import ClientRequest
50 | from msrest.universal_http.async_requests import AsyncRequestsClientResponse
51 |
52 |
53 | @unittest.skipIf(sys.version_info < (3, 5, 2), "Async tests only on 3.5.2 minimal")
54 | class TestServiceClient(object):
55 |
56 | @pytest.mark.asyncio
57 | async def test_client_send(self):
58 |
59 | cfg = Configuration("/")
60 | cfg.headers = {'Test': 'true'}
61 | cfg.credentials = mock.create_autospec(OAuthTokenAuthentication)
62 |
63 | client = ServiceClientAsync(cfg)
64 |
65 | req_response = requests.Response()
66 | req_response._content = br'{"real": true}' # Has to be valid bytes JSON
67 | req_response._content_consumed = True
68 | req_response.status_code = 200
69 |
70 | def side_effect(*args, **kwargs):
71 | return req_response
72 |
73 | session = mock.create_autospec(requests.Session)
74 | session.request.side_effect = side_effect
75 | session.adapters = {
76 | "http://": HTTPAdapter(),
77 | "https://": HTTPAdapter(),
78 | }
79 | # Be sure the mock does not trick me
80 | assert not hasattr(session.resolve_redirects, 'is_msrest_patched')
81 |
82 | client.config.pipeline._sender.driver.session = session
83 | client.config.credentials.signed_session.return_value = session
84 | client.config.credentials.refresh_session.return_value = session
85 |
86 | request = ClientRequest('GET', '/')
87 | await client.async_send(request, stream=False)
88 | session.request.call_count = 0
89 | session.request.assert_called_with(
90 | 'GET',
91 | '/',
92 | allow_redirects=True,
93 | cert=None,
94 | headers={
95 | 'User-Agent': cfg.user_agent,
96 | 'Test': 'true' # From global config
97 | },
98 | stream=False,
99 | timeout=100,
100 | verify=True
101 | )
102 | assert session.resolve_redirects.is_msrest_patched
103 |
104 | request = client.get('/', headers={'id':'1234'}, content={'Test':'Data'})
105 | await client.async_send(request, stream=False)
106 | session.request.assert_called_with(
107 | 'GET',
108 | '/',
109 | data='{"Test": "Data"}',
110 | allow_redirects=True,
111 | cert=None,
112 | headers={
113 | 'User-Agent': cfg.user_agent,
114 | 'Content-Length': '16',
115 | 'id':'1234',
116 | 'Accept': 'application/json',
117 | 'Test': 'true' # From global config
118 | },
119 | stream=False,
120 | timeout=100,
121 | verify=True
122 | )
123 | assert session.request.call_count == 1
124 | session.request.call_count = 0
125 | assert session.resolve_redirects.is_msrest_patched
126 |
127 | request = client.get('/', headers={'id':'1234'}, content={'Test':'Data'})
128 | session.request.side_effect = requests.RequestException("test")
129 | with pytest.raises(ClientRequestError):
130 | await client.async_send(request, test='value', stream=False)
131 | session.request.assert_called_with(
132 | 'GET',
133 | '/',
134 | data='{"Test": "Data"}',
135 | allow_redirects=True,
136 | cert=None,
137 | headers={
138 | 'User-Agent': cfg.user_agent,
139 | 'Content-Length': '16',
140 | 'id':'1234',
141 | 'Accept': 'application/json',
142 | 'Test': 'true' # From global config
143 | },
144 | stream=False,
145 | timeout=100,
146 | verify=True
147 | )
148 | assert session.request.call_count == 1
149 | session.request.call_count = 0
150 | assert session.resolve_redirects.is_msrest_patched
151 |
152 | session.request.side_effect = oauth2.rfc6749.errors.InvalidGrantError("test")
153 | with pytest.raises(TokenExpiredError):
154 | await client.async_send(request, headers={'id':'1234'}, content={'Test':'Data'}, test='value')
155 | assert session.request.call_count == 2
156 | session.request.call_count = 0
157 |
158 | session.request.side_effect = ValueError("test")
159 | with pytest.raises(ValueError):
160 | await client.async_send(request, headers={'id':'1234'}, content={'Test':'Data'}, test='value')
161 |
162 | @pytest.mark.asyncio
163 | async def test_client_stream_download(self):
164 |
165 | req_response = requests.Response()
166 | req_response._content = "abc"
167 | req_response._content_consumed = True
168 | req_response.status_code = 200
169 |
170 | client_response = AsyncRequestsClientResponse(
171 | None,
172 | req_response
173 | )
174 |
175 | def user_callback(chunk, response):
176 | assert response is req_response
177 | assert chunk in ["a", "b", "c"]
178 |
179 | async_iterator = client_response.stream_download(1, user_callback)
180 | result = ""
181 | async for value in async_iterator:
182 | result += value
183 | assert result == "abc"
184 |
185 |
186 | if __name__ == '__main__':
187 | unittest.main()
--------------------------------------------------------------------------------
/tests/asynctests/test_async_paging.py:
--------------------------------------------------------------------------------
1 | #--------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to deal
9 | # in the Software without restriction, including without limitation the rights
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | # copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | # THE SOFTWARE.
24 | #
25 | #--------------------------------------------------------------------------
26 | import sys
27 | import unittest
28 | import pytest
29 |
30 | from msrest.paging import Paged
31 |
32 | class FakePaged(Paged):
33 | _attribute_map = {
34 | 'next_link': {'key': 'nextLink', 'type': 'str'},
35 | 'current_page': {'key': 'value', 'type': '[str]'}
36 | }
37 |
38 | def __init__(self, *args, **kwargs):
39 | super(FakePaged, self).__init__(*args, **kwargs)
40 |
41 | class TestPaging(object):
42 |
43 | @pytest.mark.asyncio
44 | async def test_basic_paging(self):
45 |
46 | async def internal_paging(next_link=None, raw=False):
47 | if not next_link:
48 | return {
49 | 'nextLink': 'page2',
50 | 'value': ['value1.0', 'value1.1']
51 | }
52 | else:
53 | return {
54 | 'nextLink': None,
55 | 'value': ['value2.0', 'value2.1']
56 | }
57 |
58 | deserialized = FakePaged(None, {}, async_command=internal_paging)
59 |
60 | # 3.6 only : result_iterated = [obj async for obj in deserialized]
61 | result_iterated = []
62 | async for obj in deserialized:
63 | result_iterated.append(obj)
64 |
65 | assert ['value1.0', 'value1.1', 'value2.0', 'value2.1'] == result_iterated
66 |
67 | @pytest.mark.asyncio
68 | async def test_advance_paging(self):
69 |
70 | async def internal_paging(next_link=None, raw=False):
71 | if not next_link:
72 | return {
73 | 'nextLink': 'page2',
74 | 'value': ['value1.0', 'value1.1']
75 | }
76 | else:
77 | return {
78 | 'nextLink': None,
79 | 'value': ['value2.0', 'value2.1']
80 | }
81 |
82 | deserialized = FakePaged(None, {}, async_command=internal_paging)
83 | page1 = await deserialized.async_advance_page()
84 | assert ['value1.0', 'value1.1'] == page1
85 |
86 | page2 = await deserialized.async_advance_page()
87 | assert ['value2.0', 'value2.1'] == page2
88 |
89 | with pytest.raises(StopAsyncIteration):
90 | await deserialized.async_advance_page()
91 |
92 | @pytest.mark.asyncio
93 | async def test_get_paging(self):
94 |
95 | async def internal_paging(next_link=None, raw=False):
96 | if not next_link:
97 | return {
98 | 'nextLink': 'page2',
99 | 'value': ['value1.0', 'value1.1']
100 | }
101 | elif next_link == 'page2':
102 | return {
103 | 'nextLink': 'page3',
104 | 'value': ['value2.0', 'value2.1']
105 | }
106 | else:
107 | return {
108 | 'nextLink': None,
109 | 'value': ['value3.0', 'value3.1']
110 | }
111 |
112 | deserialized = FakePaged(None, {}, async_command=internal_paging)
113 | page2 = await deserialized.async_get('page2')
114 | assert ['value2.0', 'value2.1'] == page2
115 |
116 | page3 = await deserialized.async_get('page3')
117 | assert ['value3.0', 'value3.1'] == page3
118 |
119 | @pytest.mark.asyncio
120 | async def test_reset_paging(self):
121 |
122 | async def internal_paging(next_link=None, raw=False):
123 | if not next_link:
124 | return {
125 | 'nextLink': 'page2',
126 | 'value': ['value1.0', 'value1.1']
127 | }
128 | else:
129 | return {
130 | 'nextLink': None,
131 | 'value': ['value2.0', 'value2.1']
132 | }
133 |
134 | deserialized = FakePaged(None, {}, async_command=internal_paging)
135 | deserialized.reset()
136 |
137 | # 3.6 only : result_iterated = [obj async for obj in deserialized]
138 | result_iterated = []
139 | async for obj in deserialized:
140 | result_iterated.append(obj)
141 |
142 | assert ['value1.0', 'value1.1', 'value2.0', 'value2.1'] == result_iterated
143 |
144 | deserialized = FakePaged(None, {}, async_command=internal_paging)
145 | # Push the iterator to the last element
146 | async for element in deserialized:
147 | if element == "value2.0":
148 | break
149 | deserialized.reset()
150 |
151 | # 3.6 only : result_iterated = [obj async for obj in deserialized]
152 | result_iterated = []
153 | async for obj in deserialized:
154 | result_iterated.append(obj)
155 |
156 | assert ['value1.0', 'value1.1', 'value2.0', 'value2.1'] == result_iterated
157 |
158 | @pytest.mark.asyncio
159 | async def test_none_value(self):
160 | async def internal_paging(next_link=None, raw=False):
161 | return {
162 | 'nextLink': None,
163 | 'value': None
164 | }
165 |
166 | deserialized = FakePaged(None, {}, async_command=internal_paging)
167 |
168 | # 3.6 only : result_iterated = [obj async for obj in deserialized]
169 | result_iterated = []
170 | async for obj in deserialized:
171 | result_iterated.append(obj)
172 |
173 | assert len(result_iterated) == 0
174 |
--------------------------------------------------------------------------------
/tests/asynctests/test_pipeline.py:
--------------------------------------------------------------------------------
1 | #--------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to deal
9 | # in the Software without restriction, including without limitation the rights
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | # copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | # THE SOFTWARE.
24 | #
25 | #--------------------------------------------------------------------------
26 | import sys
27 |
28 | from msrest.universal_http import (
29 | ClientRequest,
30 | )
31 | from msrest.universal_http.async_requests import (
32 | AsyncRequestsHTTPSender,
33 | AsyncTrioRequestsHTTPSender,
34 | )
35 |
36 | from msrest.pipeline import (
37 | AsyncPipeline,
38 | AsyncHTTPSender,
39 | SansIOHTTPPolicy
40 | )
41 | from msrest.pipeline.async_requests import AsyncPipelineRequestsHTTPSender
42 | from msrest.pipeline.universal import UserAgentPolicy
43 | from msrest.pipeline.aiohttp import AioHTTPSender
44 |
45 | from msrest.configuration import Configuration
46 |
47 | import trio
48 |
49 | import pytest
50 |
51 |
52 | @pytest.mark.asyncio
53 | async def test_sans_io_exception():
54 | class BrokenSender(AsyncHTTPSender):
55 | async def send(self, request, **config):
56 | raise ValueError("Broken")
57 |
58 | async def __aexit__(self, exc_type, exc_value, traceback):
59 | """Raise any exception triggered within the runtime context."""
60 | return None
61 |
62 | pipeline = AsyncPipeline([SansIOHTTPPolicy()], BrokenSender())
63 |
64 | req = ClientRequest('GET', '/')
65 | with pytest.raises(ValueError):
66 | await pipeline.run(req)
67 |
68 | class SwapExec(SansIOHTTPPolicy):
69 | def on_exception(self, requests, **kwargs):
70 | exc_type, exc_value, exc_traceback = sys.exc_info()
71 | raise NotImplementedError(exc_value)
72 |
73 | pipeline = AsyncPipeline([SwapExec()], BrokenSender())
74 | with pytest.raises(NotImplementedError):
75 | await pipeline.run(req)
76 |
77 |
78 | @pytest.mark.asyncio
79 | async def test_basic_aiohttp():
80 |
81 | request = ClientRequest("GET", "http://bing.com")
82 | policies = [
83 | UserAgentPolicy("myusergant")
84 | ]
85 | async with AsyncPipeline(policies) as pipeline:
86 | response = await pipeline.run(request)
87 |
88 | assert pipeline._sender.driver._session.closed
89 | assert response.http_response.status_code == 200
90 |
91 | @pytest.mark.asyncio
92 | async def test_basic_async_requests():
93 |
94 | request = ClientRequest("GET", "http://bing.com")
95 | policies = [
96 | UserAgentPolicy("myusergant")
97 | ]
98 | async with AsyncPipeline(policies, AsyncPipelineRequestsHTTPSender()) as pipeline:
99 | response = await pipeline.run(request)
100 |
101 | assert response.http_response.status_code == 200
102 |
103 | @pytest.mark.asyncio
104 | async def test_conf_async_requests():
105 |
106 | conf = Configuration("http://bing.com/")
107 | request = ClientRequest("GET", "http://bing.com/")
108 | policies = [
109 | UserAgentPolicy("myusergant")
110 | ]
111 | async with AsyncPipeline(policies, AsyncPipelineRequestsHTTPSender(AsyncRequestsHTTPSender(conf))) as pipeline:
112 | response = await pipeline.run(request)
113 |
114 | assert response.http_response.status_code == 200
115 |
116 | def test_conf_async_trio_requests():
117 |
118 | async def do():
119 | conf = Configuration("http://bing.com/")
120 | request = ClientRequest("GET", "http://bing.com/")
121 | policies = [
122 | UserAgentPolicy("myusergant")
123 | ]
124 | async with AsyncPipeline(policies, AsyncPipelineRequestsHTTPSender(AsyncTrioRequestsHTTPSender(conf))) as pipeline:
125 | return await pipeline.run(request)
126 |
127 | response = trio.run(do)
128 | assert response.http_response.status_code == 200
--------------------------------------------------------------------------------
/tests/asynctests/test_polling.py:
--------------------------------------------------------------------------------
1 | #--------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to deal
9 | # in the Software without restriction, including without limitation the rights
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | # copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | # THE SOFTWARE.
24 | #
25 | #--------------------------------------------------------------------------
26 | import asyncio
27 | try:
28 | from unittest import mock
29 | except ImportError:
30 | import mock
31 |
32 | import pytest
33 |
34 | from msrest.polling.async_poller import *
35 | from msrest.async_client import ServiceClientAsync
36 | from msrest.serialization import Model
37 | from msrest.configuration import Configuration
38 |
39 |
40 | @pytest.mark.asyncio
41 | async def test_abc_polling():
42 | abc_polling = AsyncPollingMethod()
43 |
44 | with pytest.raises(NotImplementedError):
45 | abc_polling.initialize(None, None, None)
46 |
47 | with pytest.raises(NotImplementedError):
48 | await abc_polling.run()
49 |
50 | with pytest.raises(NotImplementedError):
51 | abc_polling.status()
52 |
53 | with pytest.raises(NotImplementedError):
54 | abc_polling.finished()
55 |
56 | with pytest.raises(NotImplementedError):
57 | abc_polling.resource()
58 |
59 |
60 | @pytest.mark.asyncio
61 | async def test_no_polling():
62 | no_polling = AsyncNoPolling()
63 |
64 | initial_response = "initial response"
65 | def deserialization_cb(response):
66 | assert response == initial_response
67 | return "Treated: "+response
68 |
69 | no_polling.initialize(None, initial_response, deserialization_cb)
70 | await no_polling.run() # Should no raise and do nothing
71 | assert no_polling.status() == "succeeded"
72 | assert no_polling.finished()
73 | assert no_polling.resource() == "Treated: "+initial_response
74 |
75 |
76 | class PollingTwoSteps(AsyncPollingMethod):
77 | """An empty poller that returns the deserialized initial response.
78 | """
79 | def __init__(self, sleep=0):
80 | self._initial_response = None
81 | self._deserialization_callback = None
82 | self._sleep = sleep
83 |
84 | def initialize(self, _, initial_response, deserialization_callback):
85 | self._initial_response = initial_response
86 | self._deserialization_callback = deserialization_callback
87 | self._finished = False
88 |
89 | async def run(self):
90 | """Empty run, no polling.
91 | """
92 | self._finished = True
93 | await asyncio.sleep(self._sleep) # Give me time to add callbacks!
94 |
95 | def status(self):
96 | """Return the current status as a string.
97 | :rtype: str
98 | """
99 | return "succeeded" if self._finished else "running"
100 |
101 | def finished(self):
102 | """Is this polling finished?
103 | :rtype: bool
104 | """
105 | return self._finished
106 |
107 | def resource(self):
108 | return self._deserialization_callback(self._initial_response)
109 |
110 | @pytest.fixture
111 | def client():
112 | # We need a ServiceClientAsync instance, but the poller itself don't use it, so we don't need
113 | # Something functional
114 | return ServiceClientAsync(Configuration("http://example.org"))
115 |
116 | @pytest.mark.asyncio
117 | async def test_poller(client):
118 |
119 | # Same the poller itself doesn't care about the initial_response, and there is no type constraint here
120 | initial_response = "Initial response"
121 |
122 | # Same for deserialization_callback, just pass to the polling_method
123 | def deserialization_callback(response):
124 | assert response == initial_response
125 | return "Treated: "+response
126 |
127 | method = AsyncNoPolling()
128 |
129 | result = await async_poller(client, initial_response, deserialization_callback, method)
130 | assert result == "Treated: "+initial_response
131 |
132 | # Test with a basic Model
133 | class MockedModel(Model):
134 | called = False
135 | @classmethod
136 | def deserialize(cls, data):
137 | assert data == initial_response
138 | cls.called = True
139 |
140 | result = await async_poller(client, initial_response, MockedModel, method)
141 | assert MockedModel.called
142 |
143 | # Test poller that method do a run
144 | method = PollingTwoSteps(sleep=2)
145 | result = await async_poller(client, initial_response, deserialization_callback, method)
146 |
147 | assert result == "Treated: "+initial_response
148 |
149 | @pytest.mark.asyncio
150 | async def test_broken_poller(client):
151 |
152 | with pytest.raises(ValueError):
153 | await async_poller(None, None, None, None)
154 |
155 | class NoPollingError(PollingTwoSteps):
156 | async def run(self):
157 | raise ValueError("Something bad happened")
158 |
159 | initial_response = "Initial response"
160 | def deserialization_callback(response):
161 | return "Treated: "+response
162 |
163 | method = NoPollingError()
164 |
165 | with pytest.raises(ValueError) as excinfo:
166 | await async_poller(client, initial_response, deserialization_callback, method)
167 | assert "Something bad happened" in str(excinfo.value)
168 |
--------------------------------------------------------------------------------
/tests/asynctests/test_universal_http.py:
--------------------------------------------------------------------------------
1 | #--------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to deal
9 | # in the Software without restriction, including without limitation the rights
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | # copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | # THE SOFTWARE.
24 | #
25 | #--------------------------------------------------------------------------
26 | import sys
27 |
28 | from msrest.universal_http import (
29 | ClientRequest,
30 | AsyncHTTPSender,
31 | )
32 | from msrest.universal_http.aiohttp import AioHTTPSender
33 | from msrest.universal_http.async_requests import (
34 | AsyncBasicRequestsHTTPSender,
35 | AsyncRequestsHTTPSender,
36 | AsyncTrioRequestsHTTPSender,
37 | )
38 |
39 | from msrest.configuration import Configuration
40 |
41 | import trio
42 |
43 | import pytest
44 |
45 |
46 | @pytest.mark.asyncio
47 | async def test_basic_aiohttp():
48 |
49 | request = ClientRequest("GET", "http://bing.com")
50 | async with AioHTTPSender() as sender:
51 | response = await sender.send(request)
52 | assert response.body() is not None
53 |
54 | assert sender._session.closed
55 | assert response.status_code == 200
56 |
57 | @pytest.mark.asyncio
58 | async def test_basic_async_requests():
59 |
60 | request = ClientRequest("GET", "http://bing.com")
61 | async with AsyncBasicRequestsHTTPSender() as sender:
62 | response = await sender.send(request)
63 | assert response.body() is not None
64 |
65 | assert response.status_code == 200
66 |
67 | @pytest.mark.asyncio
68 | async def test_conf_async_requests():
69 |
70 | conf = Configuration("http://bing.com/")
71 | request = ClientRequest("GET", "http://bing.com/")
72 | async with AsyncRequestsHTTPSender(conf) as sender:
73 | response = await sender.send(request)
74 | assert response.body() is not None
75 |
76 | assert response.status_code == 200
77 |
78 | def test_conf_async_trio_requests():
79 |
80 | async def do():
81 | conf = Configuration("http://bing.com/")
82 | request = ClientRequest("GET", "http://bing.com/")
83 | async with AsyncTrioRequestsHTTPSender(conf) as sender:
84 | return await sender.send(request)
85 | assert response.body() is not None
86 |
87 | response = trio.run(do)
88 | assert response.status_code == 200
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to
9 | # deal in the Software without restriction, including without limitation the
10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 | # sell copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 | # IN THE SOFTWARE.
24 | #
25 | # --------------------------------------------------------------------------
26 | import sys
27 |
28 | # Ignore collection of async tests for Python 2
29 | collect_ignore = []
30 | if sys.version_info < (3, 5):
31 | collect_ignore.append("asynctests")
32 |
--------------------------------------------------------------------------------
/tests/storage_models/__init__.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from .storage_account_check_name_availability_parameters import StorageAccountCheckNameAvailabilityParameters
13 | from .check_name_availability_result import CheckNameAvailabilityResult
14 | from .sku import Sku
15 | from .custom_domain import CustomDomain
16 | from .encryption_service import EncryptionService
17 | from .encryption_services import EncryptionServices
18 | from .encryption import Encryption
19 | from .storage_account_create_parameters import StorageAccountCreateParameters
20 | from .endpoints import Endpoints
21 | from .storage_account import StorageAccount
22 | from .storage_account_key import StorageAccountKey
23 | from .storage_account_list_keys_result import StorageAccountListKeysResult
24 | from .storage_account_regenerate_key_parameters import StorageAccountRegenerateKeyParameters
25 | from .storage_account_update_parameters import StorageAccountUpdateParameters
26 | from .usage_name import UsageName
27 | from .usage import Usage
28 | from .resource import Resource
29 | from .account_sas_parameters import AccountSasParameters
30 | from .list_account_sas_response import ListAccountSasResponse
31 | from .service_sas_parameters import ServiceSasParameters
32 | from .list_service_sas_response import ListServiceSasResponse
33 | from .storage_account_paged import StorageAccountPaged
34 | from .usage_paged import UsagePaged
35 | from .storage_management_client_enums import (
36 | Reason,
37 | SkuName,
38 | SkuTier,
39 | AccessTier,
40 | Kind,
41 | ProvisioningState,
42 | AccountStatus,
43 | KeyPermission,
44 | UsageUnit,
45 | HttpProtocol,
46 | )
47 |
48 | __all__ = [
49 | 'StorageAccountCheckNameAvailabilityParameters',
50 | 'CheckNameAvailabilityResult',
51 | 'Sku',
52 | 'CustomDomain',
53 | 'EncryptionService',
54 | 'EncryptionServices',
55 | 'Encryption',
56 | 'StorageAccountCreateParameters',
57 | 'Endpoints',
58 | 'StorageAccount',
59 | 'StorageAccountKey',
60 | 'StorageAccountListKeysResult',
61 | 'StorageAccountRegenerateKeyParameters',
62 | 'StorageAccountUpdateParameters',
63 | 'UsageName',
64 | 'Usage',
65 | 'Resource',
66 | 'AccountSasParameters',
67 | 'ListAccountSasResponse',
68 | 'ServiceSasParameters',
69 | 'ListServiceSasResponse',
70 | 'StorageAccountPaged',
71 | 'UsagePaged',
72 | 'Reason',
73 | 'SkuName',
74 | 'SkuTier',
75 | 'AccessTier',
76 | 'Kind',
77 | 'ProvisioningState',
78 | 'AccountStatus',
79 | 'KeyPermission',
80 | 'UsageUnit',
81 | 'HttpProtocol',
82 | ]
83 |
--------------------------------------------------------------------------------
/tests/storage_models/account_sas_parameters.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class AccountSasParameters(Model):
16 | """The parameters to list SAS credentials of a storage account.
17 |
18 | :param services: The signed services accessible with the account SAS.
19 | Possible values include: Blob (b), Queue (q), Table (t), File (f).
20 | Possible values include: 'b', 'q', 't', 'f'
21 | :type services: str or :class:`enum
22 | `
23 | :param resource_types: The signed resource types that are accessible with
24 | the account SAS. Service (s): Access to service-level APIs; Container (c):
25 | Access to container-level APIs; Object (o): Access to object-level APIs
26 | for blobs, queue messages, table entities, and files. Possible values
27 | include: 's', 'c', 'o'
28 | :type resource_types: str or :class:`enum
29 | `
30 | :param permissions: The signed permissions for the account SAS. Possible
31 | values include: Read (r), Write (w), Delete (d), List (l), Add (a), Create
32 | (c), Update (u) and Process (p). Possible values include: 'r', 'd', 'w',
33 | 'l', 'a', 'c', 'u', 'p'
34 | :type permissions: str or :class:`enum
35 | `
36 | :param ip_address_or_range: An IP address or a range of IP addresses from
37 | which to accept requests.
38 | :type ip_address_or_range: str
39 | :param protocols: The protocol permitted for a request made with the
40 | account SAS. Possible values include: 'https,http', 'https'
41 | :type protocols: str or :class:`HttpProtocol
42 | `
43 | :param shared_access_start_time: The time at which the SAS becomes valid.
44 | :type shared_access_start_time: datetime
45 | :param shared_access_expiry_time: The time at which the shared access
46 | signature becomes invalid.
47 | :type shared_access_expiry_time: datetime
48 | :param key_to_sign: The key to sign the account SAS token with.
49 | :type key_to_sign: str
50 | """
51 |
52 | _validation = {
53 | 'services': {'required': True},
54 | 'resource_types': {'required': True},
55 | 'permissions': {'required': True},
56 | 'shared_access_expiry_time': {'required': True},
57 | }
58 |
59 | _attribute_map = {
60 | 'services': {'key': 'signedServices', 'type': 'str'},
61 | 'resource_types': {'key': 'signedResourceTypes', 'type': 'str'},
62 | 'permissions': {'key': 'signedPermission', 'type': 'str'},
63 | 'ip_address_or_range': {'key': 'signedIp', 'type': 'str'},
64 | 'protocols': {'key': 'signedProtocol', 'type': 'HttpProtocol'},
65 | 'shared_access_start_time': {'key': 'signedStart', 'type': 'iso-8601'},
66 | 'shared_access_expiry_time': {'key': 'signedExpiry', 'type': 'iso-8601'},
67 | 'key_to_sign': {'key': 'keyToSign', 'type': 'str'},
68 | }
69 |
70 | def __init__(self, services, resource_types, permissions, shared_access_expiry_time, ip_address_or_range=None, protocols=None, shared_access_start_time=None, key_to_sign=None):
71 | self.services = services
72 | self.resource_types = resource_types
73 | self.permissions = permissions
74 | self.ip_address_or_range = ip_address_or_range
75 | self.protocols = protocols
76 | self.shared_access_start_time = shared_access_start_time
77 | self.shared_access_expiry_time = shared_access_expiry_time
78 | self.key_to_sign = key_to_sign
79 |
--------------------------------------------------------------------------------
/tests/storage_models/check_name_availability_result.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class CheckNameAvailabilityResult(Model):
16 | """The CheckNameAvailability operation response.
17 |
18 | Variables are only populated by the server, and will be ignored when
19 | sending a request.
20 |
21 | :ivar name_available: Gets a boolean value that indicates whether the name
22 | is available for you to use. If true, the name is available. If false, the
23 | name has already been taken or is invalid and cannot be used.
24 | :vartype name_available: bool
25 | :ivar reason: Gets the reason that a storage account name could not be
26 | used. The Reason element is only returned if NameAvailable is false.
27 | Possible values include: 'AccountNameInvalid', 'AlreadyExists'
28 | :vartype reason: str or :class:`Reason
29 | `
30 | :ivar message: Gets an error message explaining the Reason value in more
31 | detail.
32 | :vartype message: str
33 | """
34 |
35 | _validation = {
36 | 'name_available': {'readonly': True},
37 | 'reason': {'readonly': True},
38 | 'message': {'readonly': True},
39 | }
40 |
41 | _attribute_map = {
42 | 'name_available': {'key': 'nameAvailable', 'type': 'bool'},
43 | 'reason': {'key': 'reason', 'type': 'Reason'},
44 | 'message': {'key': 'message', 'type': 'str'},
45 | }
46 |
47 | def __init__(self):
48 | self.name_available = None
49 | self.reason = None
50 | self.message = None
51 |
--------------------------------------------------------------------------------
/tests/storage_models/custom_domain.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class CustomDomain(Model):
16 | """The custom domain assigned to this storage account. This can be set via
17 | Update.
18 |
19 | :param name: Gets or sets the custom domain name assigned to the storage
20 | account. Name is the CNAME source.
21 | :type name: str
22 | :param use_sub_domain: Indicates whether indirect CName validation is
23 | enabled. Default value is false. This should only be set on updates.
24 | :type use_sub_domain: bool
25 | """
26 |
27 | _validation = {
28 | 'name': {'required': True},
29 | }
30 |
31 | _attribute_map = {
32 | 'name': {'key': 'name', 'type': 'str'},
33 | 'use_sub_domain': {'key': 'useSubDomain', 'type': 'bool'},
34 | }
35 |
36 | def __init__(self, name, use_sub_domain=None):
37 | self.name = name
38 | self.use_sub_domain = use_sub_domain
39 |
--------------------------------------------------------------------------------
/tests/storage_models/encryption.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class Encryption(Model):
16 | """The encryption settings on the storage account.
17 |
18 | Variables are only populated by the server, and will be ignored when
19 | sending a request.
20 |
21 | :param services: List of services which support encryption.
22 | :type services: :class:`EncryptionServices
23 | `
24 | :ivar key_source: The encryption keySource (provider). Possible values
25 | (case-insensitive): Microsoft.Storage. Default value: "Microsoft.Storage"
26 | .
27 | :vartype key_source: str
28 | """
29 |
30 | _validation = {
31 | 'key_source': {'required': True, 'constant': True},
32 | }
33 |
34 | _attribute_map = {
35 | 'services': {'key': 'services', 'type': 'EncryptionServices'},
36 | 'key_source': {'key': 'keySource', 'type': 'str'},
37 | }
38 |
39 | key_source = "Microsoft.Storage"
40 |
41 | def __init__(self, services=None):
42 | self.services = services
43 |
--------------------------------------------------------------------------------
/tests/storage_models/encryption_service.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class EncryptionService(Model):
16 | """A service that allows server-side encryption to be used.
17 |
18 | Variables are only populated by the server, and will be ignored when
19 | sending a request.
20 |
21 | :param enabled: A boolean indicating whether or not the service encrypts
22 | the data as it is stored.
23 | :type enabled: bool
24 | :ivar last_enabled_time: Gets a rough estimate of the date/time when the
25 | encryption was last enabled by the user. Only returned when encryption is
26 | enabled. There might be some unencrypted blobs which were written after
27 | this time, as it is just a rough estimate.
28 | :vartype last_enabled_time: datetime
29 | """
30 |
31 | _validation = {
32 | 'last_enabled_time': {'readonly': True},
33 | }
34 |
35 | _attribute_map = {
36 | 'enabled': {'key': 'enabled', 'type': 'bool'},
37 | 'last_enabled_time': {'key': 'lastEnabledTime', 'type': 'iso-8601'},
38 | }
39 |
40 | def __init__(self, enabled=None):
41 | self.enabled = enabled
42 | self.last_enabled_time = None
43 |
--------------------------------------------------------------------------------
/tests/storage_models/encryption_services.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class EncryptionServices(Model):
16 | """A list of services that support encryption.
17 |
18 | Variables are only populated by the server, and will be ignored when
19 | sending a request.
20 |
21 | :param blob: The encryption function of the blob storage service.
22 | :type blob: :class:`EncryptionService
23 | `
24 | :param file: The encryption function of the file storage service.
25 | :type file: :class:`EncryptionService
26 | `
27 | :ivar table: The encryption function of the table storage service.
28 | :vartype table: :class:`EncryptionService
29 | `
30 | :ivar queue: The encryption function of the queue storage service.
31 | :vartype queue: :class:`EncryptionService
32 | `
33 | """
34 |
35 | _validation = {
36 | 'table': {'readonly': True},
37 | 'queue': {'readonly': True},
38 | }
39 |
40 | _attribute_map = {
41 | 'blob': {'key': 'blob', 'type': 'EncryptionService'},
42 | 'file': {'key': 'file', 'type': 'EncryptionService'},
43 | 'table': {'key': 'table', 'type': 'EncryptionService'},
44 | 'queue': {'key': 'queue', 'type': 'EncryptionService'},
45 | }
46 |
47 | def __init__(self, blob=None, file=None):
48 | self.blob = blob
49 | self.file = file
50 | self.table = None
51 | self.queue = None
52 |
--------------------------------------------------------------------------------
/tests/storage_models/endpoints.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class Endpoints(Model):
16 | """The URIs that are used to perform a retrieval of a public blob, queue, or
17 | table object.
18 |
19 | Variables are only populated by the server, and will be ignored when
20 | sending a request.
21 |
22 | :ivar blob: Gets the blob endpoint.
23 | :vartype blob: str
24 | :ivar queue: Gets the queue endpoint.
25 | :vartype queue: str
26 | :ivar table: Gets the table endpoint.
27 | :vartype table: str
28 | :ivar file: Gets the file endpoint.
29 | :vartype file: str
30 | """
31 |
32 | _validation = {
33 | 'blob': {'readonly': True},
34 | 'queue': {'readonly': True},
35 | 'table': {'readonly': True},
36 | 'file': {'readonly': True},
37 | }
38 |
39 | _attribute_map = {
40 | 'blob': {'key': 'blob', 'type': 'str'},
41 | 'queue': {'key': 'queue', 'type': 'str'},
42 | 'table': {'key': 'table', 'type': 'str'},
43 | 'file': {'key': 'file', 'type': 'str'},
44 | }
45 |
46 | def __init__(self):
47 | self.blob = None
48 | self.queue = None
49 | self.table = None
50 | self.file = None
51 |
--------------------------------------------------------------------------------
/tests/storage_models/list_account_sas_response.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class ListAccountSasResponse(Model):
16 | """The List SAS credentials operation response.
17 |
18 | Variables are only populated by the server, and will be ignored when
19 | sending a request.
20 |
21 | :ivar account_sas_token: List SAS credentials of storage account.
22 | :vartype account_sas_token: str
23 | """
24 |
25 | _validation = {
26 | 'account_sas_token': {'readonly': True},
27 | }
28 |
29 | _attribute_map = {
30 | 'account_sas_token': {'key': 'accountSasToken', 'type': 'str'},
31 | }
32 |
33 | def __init__(self):
34 | self.account_sas_token = None
35 |
--------------------------------------------------------------------------------
/tests/storage_models/list_service_sas_response.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class ListServiceSasResponse(Model):
16 | """The List service SAS credentials operation response.
17 |
18 | Variables are only populated by the server, and will be ignored when
19 | sending a request.
20 |
21 | :ivar service_sas_token: List service SAS credentials of specific
22 | resource.
23 | :vartype service_sas_token: str
24 | """
25 |
26 | _validation = {
27 | 'service_sas_token': {'readonly': True},
28 | }
29 |
30 | _attribute_map = {
31 | 'service_sas_token': {'key': 'serviceSasToken', 'type': 'str'},
32 | }
33 |
34 | def __init__(self):
35 | self.service_sas_token = None
36 |
--------------------------------------------------------------------------------
/tests/storage_models/resource.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class Resource(Model):
16 | """Describes a storage resource.
17 |
18 | Variables are only populated by the server, and will be ignored when
19 | sending a request.
20 |
21 | :ivar id: Resource Id
22 | :vartype id: str
23 | :ivar name: Resource name
24 | :vartype name: str
25 | :ivar type: Resource type
26 | :vartype type: str
27 | :param location: Resource location
28 | :type location: str
29 | :param tags: Tags assigned to a resource; can be used for viewing and
30 | grouping a resource (across resource groups).
31 | :type tags: dict
32 | """
33 |
34 | _validation = {
35 | 'id': {'readonly': True},
36 | 'name': {'readonly': True},
37 | 'type': {'readonly': True},
38 | }
39 |
40 | _attribute_map = {
41 | 'id': {'key': 'id', 'type': 'str'},
42 | 'name': {'key': 'name', 'type': 'str'},
43 | 'type': {'key': 'type', 'type': 'str'},
44 | 'location': {'key': 'location', 'type': 'str'},
45 | 'tags': {'key': 'tags', 'type': '{str}'},
46 | }
47 |
48 | def __init__(self, location=None, tags=None):
49 | self.id = None
50 | self.name = None
51 | self.type = None
52 | self.location = location
53 | self.tags = tags
54 |
--------------------------------------------------------------------------------
/tests/storage_models/service_sas_parameters.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class ServiceSasParameters(Model):
16 | """The parameters to list service SAS credentials of a specific resource.
17 |
18 | :param canonicalized_resource: The canonical path to the signed resource.
19 | :type canonicalized_resource: str
20 | :param resource: The signed services accessible with the service SAS.
21 | Possible values include: Blob (b), Container (c), File (f), Share (s).
22 | Possible values include: 'b', 'c', 'f', 's'
23 | :type resource: str or :class:`enum
24 | `
25 | :param permissions: The signed permissions for the service SAS. Possible
26 | values include: Read (r), Write (w), Delete (d), List (l), Add (a), Create
27 | (c), Update (u) and Process (p). Possible values include: 'r', 'd', 'w',
28 | 'l', 'a', 'c', 'u', 'p'
29 | :type permissions: str or :class:`enum
30 | `
31 | :param ip_address_or_range: An IP address or a range of IP addresses from
32 | which to accept requests.
33 | :type ip_address_or_range: str
34 | :param protocols: The protocol permitted for a request made with the
35 | account SAS. Possible values include: 'https,http', 'https'
36 | :type protocols: str or :class:`HttpProtocol
37 | `
38 | :param shared_access_start_time: The time at which the SAS becomes valid.
39 | :type shared_access_start_time: datetime
40 | :param shared_access_expiry_time: The time at which the shared access
41 | signature becomes invalid.
42 | :type shared_access_expiry_time: datetime
43 | :param identifier: A unique value up to 64 characters in length that
44 | correlates to an access policy specified for the container, queue, or
45 | table.
46 | :type identifier: str
47 | :param partition_key_start: The start of partition key.
48 | :type partition_key_start: str
49 | :param partition_key_end: The end of partition key.
50 | :type partition_key_end: str
51 | :param row_key_start: The start of row key.
52 | :type row_key_start: str
53 | :param row_key_end: The end of row key.
54 | :type row_key_end: str
55 | :param key_to_sign: The key to sign the account SAS token with.
56 | :type key_to_sign: str
57 | :param cache_control: The response header override for cache control.
58 | :type cache_control: str
59 | :param content_disposition: The response header override for content
60 | disposition.
61 | :type content_disposition: str
62 | :param content_encoding: The response header override for content
63 | encoding.
64 | :type content_encoding: str
65 | :param content_language: The response header override for content
66 | language.
67 | :type content_language: str
68 | :param content_type: The response header override for content type.
69 | :type content_type: str
70 | """
71 |
72 | _validation = {
73 | 'canonicalized_resource': {'required': True},
74 | 'resource': {'required': True},
75 | 'identifier': {'max_length': 64},
76 | }
77 |
78 | _attribute_map = {
79 | 'canonicalized_resource': {'key': 'canonicalizedResource', 'type': 'str'},
80 | 'resource': {'key': 'signedResource', 'type': 'str'},
81 | 'permissions': {'key': 'signedPermission', 'type': 'str'},
82 | 'ip_address_or_range': {'key': 'signedIp', 'type': 'str'},
83 | 'protocols': {'key': 'signedProtocol', 'type': 'HttpProtocol'},
84 | 'shared_access_start_time': {'key': 'signedStart', 'type': 'iso-8601'},
85 | 'shared_access_expiry_time': {'key': 'signedExpiry', 'type': 'iso-8601'},
86 | 'identifier': {'key': 'signedIdentifier', 'type': 'str'},
87 | 'partition_key_start': {'key': 'startPk', 'type': 'str'},
88 | 'partition_key_end': {'key': 'endPk', 'type': 'str'},
89 | 'row_key_start': {'key': 'startRk', 'type': 'str'},
90 | 'row_key_end': {'key': 'endRk', 'type': 'str'},
91 | 'key_to_sign': {'key': 'keyToSign', 'type': 'str'},
92 | 'cache_control': {'key': 'rscc', 'type': 'str'},
93 | 'content_disposition': {'key': 'rscd', 'type': 'str'},
94 | 'content_encoding': {'key': 'rsce', 'type': 'str'},
95 | 'content_language': {'key': 'rscl', 'type': 'str'},
96 | 'content_type': {'key': 'rsct', 'type': 'str'},
97 | }
98 |
99 | def __init__(self, canonicalized_resource, resource, permissions=None, ip_address_or_range=None, protocols=None, shared_access_start_time=None, shared_access_expiry_time=None, identifier=None, partition_key_start=None, partition_key_end=None, row_key_start=None, row_key_end=None, key_to_sign=None, cache_control=None, content_disposition=None, content_encoding=None, content_language=None, content_type=None):
100 | self.canonicalized_resource = canonicalized_resource
101 | self.resource = resource
102 | self.permissions = permissions
103 | self.ip_address_or_range = ip_address_or_range
104 | self.protocols = protocols
105 | self.shared_access_start_time = shared_access_start_time
106 | self.shared_access_expiry_time = shared_access_expiry_time
107 | self.identifier = identifier
108 | self.partition_key_start = partition_key_start
109 | self.partition_key_end = partition_key_end
110 | self.row_key_start = row_key_start
111 | self.row_key_end = row_key_end
112 | self.key_to_sign = key_to_sign
113 | self.cache_control = cache_control
114 | self.content_disposition = content_disposition
115 | self.content_encoding = content_encoding
116 | self.content_language = content_language
117 | self.content_type = content_type
118 |
--------------------------------------------------------------------------------
/tests/storage_models/sku.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class Sku(Model):
16 | """The SKU of the storage account.
17 |
18 | Variables are only populated by the server, and will be ignored when
19 | sending a request.
20 |
21 | :param name: Gets or sets the sku name. Required for account creation;
22 | optional for update. Note that in older versions, sku name was called
23 | accountType. Possible values include: 'Standard_LRS', 'Standard_GRS',
24 | 'Standard_RAGRS', 'Standard_ZRS', 'Premium_LRS'
25 | :type name: str or :class:`SkuName
26 | `
27 | :ivar tier: Gets the sku tier. This is based on the SKU name. Possible
28 | values include: 'Standard', 'Premium'
29 | :vartype tier: str or :class:`SkuTier
30 | `
31 | """
32 |
33 | _validation = {
34 | 'name': {'required': True},
35 | 'tier': {'readonly': True},
36 | }
37 |
38 | _attribute_map = {
39 | 'name': {'key': 'name', 'type': 'SkuName'},
40 | 'tier': {'key': 'tier', 'type': 'SkuTier'},
41 | }
42 |
43 | def __init__(self, name):
44 | self.name = name
45 | self.tier = None
46 |
--------------------------------------------------------------------------------
/tests/storage_models/storage_account.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from .resource import Resource
13 |
14 |
15 | class StorageAccount(Resource):
16 | """The storage account.
17 |
18 | Variables are only populated by the server, and will be ignored when
19 | sending a request.
20 |
21 | :ivar id: Resource Id
22 | :vartype id: str
23 | :ivar name: Resource name
24 | :vartype name: str
25 | :ivar type: Resource type
26 | :vartype type: str
27 | :param location: Resource location
28 | :type location: str
29 | :param tags: Tags assigned to a resource; can be used for viewing and
30 | grouping a resource (across resource groups).
31 | :type tags: dict
32 | :ivar sku: Gets the SKU.
33 | :vartype sku: :class:`Sku `
34 | :ivar kind: Gets the Kind. Possible values include: 'Storage',
35 | 'BlobStorage'
36 | :vartype kind: str or :class:`Kind
37 | `
38 | :ivar provisioning_state: Gets the status of the storage account at the
39 | time the operation was called. Possible values include: 'Creating',
40 | 'ResolvingDNS', 'Succeeded'
41 | :vartype provisioning_state: str or :class:`ProvisioningState
42 | `
43 | :ivar primary_endpoints: Gets the URLs that are used to perform a
44 | retrieval of a public blob, queue, or table object. Note that Standard_ZRS
45 | and Premium_LRS accounts only return the blob endpoint.
46 | :vartype primary_endpoints: :class:`Endpoints
47 | `
48 | :ivar primary_location: Gets the location of the primary data center for
49 | the storage account.
50 | :vartype primary_location: str
51 | :ivar status_of_primary: Gets the status indicating whether the primary
52 | location of the storage account is available or unavailable. Possible
53 | values include: 'available', 'unavailable'
54 | :vartype status_of_primary: str or :class:`AccountStatus
55 | `
56 | :ivar last_geo_failover_time: Gets the timestamp of the most recent
57 | instance of a failover to the secondary location. Only the most recent
58 | timestamp is retained. This element is not returned if there has never
59 | been a failover instance. Only available if the accountType is
60 | Standard_GRS or Standard_RAGRS.
61 | :vartype last_geo_failover_time: datetime
62 | :ivar secondary_location: Gets the location of the geo-replicated
63 | secondary for the storage account. Only available if the accountType is
64 | Standard_GRS or Standard_RAGRS.
65 | :vartype secondary_location: str
66 | :ivar status_of_secondary: Gets the status indicating whether the
67 | secondary location of the storage account is available or unavailable.
68 | Only available if the SKU name is Standard_GRS or Standard_RAGRS. Possible
69 | values include: 'available', 'unavailable'
70 | :vartype status_of_secondary: str or :class:`AccountStatus
71 | `
72 | :ivar creation_time: Gets the creation date and time of the storage
73 | account in UTC.
74 | :vartype creation_time: datetime
75 | :ivar custom_domain: Gets the custom domain the user assigned to this
76 | storage account.
77 | :vartype custom_domain: :class:`CustomDomain
78 | `
79 | :ivar secondary_endpoints: Gets the URLs that are used to perform a
80 | retrieval of a public blob, queue, or table object from the secondary
81 | location of the storage account. Only available if the SKU name is
82 | Standard_RAGRS.
83 | :vartype secondary_endpoints: :class:`Endpoints
84 | `
85 | :ivar encryption: Gets the encryption settings on the account. If
86 | unspecified, the account is unencrypted.
87 | :vartype encryption: :class:`Encryption
88 | `
89 | :ivar access_tier: Required for storage accounts where kind = BlobStorage.
90 | The access tier used for billing. Possible values include: 'Hot', 'Cool'
91 | :vartype access_tier: str or :class:`AccessTier
92 | `
93 | :param enable_https_traffic_only: Allows https traffic only to storage
94 | service if sets to true. Default value: False .
95 | :type enable_https_traffic_only: bool
96 | """
97 |
98 | _validation = {
99 | 'id': {'readonly': True},
100 | 'name': {'readonly': True},
101 | 'type': {'readonly': True},
102 | 'sku': {'readonly': True},
103 | 'kind': {'readonly': True},
104 | 'provisioning_state': {'readonly': True},
105 | 'primary_endpoints': {'readonly': True},
106 | 'primary_location': {'readonly': True},
107 | 'status_of_primary': {'readonly': True},
108 | 'last_geo_failover_time': {'readonly': True},
109 | 'secondary_location': {'readonly': True},
110 | 'status_of_secondary': {'readonly': True},
111 | 'creation_time': {'readonly': True},
112 | 'custom_domain': {'readonly': True},
113 | 'secondary_endpoints': {'readonly': True},
114 | 'encryption': {'readonly': True},
115 | 'access_tier': {'readonly': True},
116 | }
117 |
118 | _attribute_map = {
119 | 'id': {'key': 'id', 'type': 'str'},
120 | 'name': {'key': 'name', 'type': 'str'},
121 | 'type': {'key': 'type', 'type': 'str'},
122 | 'location': {'key': 'location', 'type': 'str'},
123 | 'tags': {'key': 'tags', 'type': '{str}'},
124 | 'sku': {'key': 'sku', 'type': 'Sku'},
125 | 'kind': {'key': 'kind', 'type': 'Kind'},
126 | 'provisioning_state': {'key': 'properties.provisioningState', 'type': 'ProvisioningState'},
127 | 'primary_endpoints': {'key': 'properties.primaryEndpoints', 'type': 'Endpoints'},
128 | 'primary_location': {'key': 'properties.primaryLocation', 'type': 'str'},
129 | 'status_of_primary': {'key': 'properties.statusOfPrimary', 'type': 'AccountStatus'},
130 | 'last_geo_failover_time': {'key': 'properties.lastGeoFailoverTime', 'type': 'iso-8601'},
131 | 'secondary_location': {'key': 'properties.secondaryLocation', 'type': 'str'},
132 | 'status_of_secondary': {'key': 'properties.statusOfSecondary', 'type': 'AccountStatus'},
133 | 'creation_time': {'key': 'properties.creationTime', 'type': 'iso-8601'},
134 | 'custom_domain': {'key': 'properties.customDomain', 'type': 'CustomDomain'},
135 | 'secondary_endpoints': {'key': 'properties.secondaryEndpoints', 'type': 'Endpoints'},
136 | 'encryption': {'key': 'properties.encryption', 'type': 'Encryption'},
137 | 'access_tier': {'key': 'properties.accessTier', 'type': 'AccessTier'},
138 | 'enable_https_traffic_only': {'key': 'properties.supportsHttpsTrafficOnly', 'type': 'bool'},
139 | }
140 |
141 | def __init__(self, location=None, tags=None, enable_https_traffic_only=False):
142 | super(StorageAccount, self).__init__(location=location, tags=tags)
143 | self.sku = None
144 | self.kind = None
145 | self.provisioning_state = None
146 | self.primary_endpoints = None
147 | self.primary_location = None
148 | self.status_of_primary = None
149 | self.last_geo_failover_time = None
150 | self.secondary_location = None
151 | self.status_of_secondary = None
152 | self.creation_time = None
153 | self.custom_domain = None
154 | self.secondary_endpoints = None
155 | self.encryption = None
156 | self.access_tier = None
157 | self.enable_https_traffic_only = enable_https_traffic_only
158 |
--------------------------------------------------------------------------------
/tests/storage_models/storage_account_check_name_availability_parameters.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class StorageAccountCheckNameAvailabilityParameters(Model):
16 | """The parameters used to check the availability of the storage account name.
17 |
18 | Variables are only populated by the server, and will be ignored when
19 | sending a request.
20 |
21 | :param name:
22 | :type name: str
23 | :ivar type: Default value: "Microsoft.Storage/storageAccounts" .
24 | :vartype type: str
25 | """
26 |
27 | _validation = {
28 | 'name': {'required': True},
29 | 'type': {'required': True, 'constant': True},
30 | }
31 |
32 | _attribute_map = {
33 | 'name': {'key': 'name', 'type': 'str'},
34 | 'type': {'key': 'type', 'type': 'str'},
35 | }
36 |
37 | type = "Microsoft.Storage/storageAccounts"
38 |
39 | def __init__(self, name):
40 | self.name = name
41 |
--------------------------------------------------------------------------------
/tests/storage_models/storage_account_create_parameters.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class StorageAccountCreateParameters(Model):
16 | """The parameters used when creating a storage account.
17 |
18 | :param sku: Required. Gets or sets the sku name.
19 | :type sku: :class:`Sku `
20 | :param kind: Required. Indicates the type of storage account. Possible
21 | values include: 'Storage', 'BlobStorage'
22 | :type kind: str or :class:`Kind
23 | `
24 | :param location: Required. Gets or sets the location of the resource. This
25 | will be one of the supported and registered Azure Geo Regions (e.g. West
26 | US, East US, Southeast Asia, etc.). The geo region of a resource cannot be
27 | changed once it is created, but if an identical geo region is specified on
28 | update, the request will succeed.
29 | :type location: str
30 | :param tags: Gets or sets a list of key value pairs that describe the
31 | resource. These tags can be used for viewing and grouping this resource
32 | (across resource groups). A maximum of 15 tags can be provided for a
33 | resource. Each tag must have a key with a length no greater than 128
34 | characters and a value with a length no greater than 256 characters.
35 | :type tags: dict
36 | :param custom_domain: User domain assigned to the storage account. Name is
37 | the CNAME source. Only one custom domain is supported per storage account
38 | at this time. To clear the existing custom domain, use an empty string for
39 | the custom domain name property.
40 | :type custom_domain: :class:`CustomDomain
41 | `
42 | :param encryption: Provides the encryption settings on the account. If
43 | left unspecified the account encryption settings will remain the same. The
44 | default setting is unencrypted.
45 | :type encryption: :class:`Encryption
46 | `
47 | :param access_tier: Required for storage accounts where kind =
48 | BlobStorage. The access tier used for billing. Possible values include:
49 | 'Hot', 'Cool'
50 | :type access_tier: str or :class:`AccessTier
51 | `
52 | :param enable_https_traffic_only: Allows https traffic only to storage
53 | service if sets to true. Default value: False .
54 | :type enable_https_traffic_only: bool
55 | """
56 |
57 | _validation = {
58 | 'sku': {'required': True},
59 | 'kind': {'required': True},
60 | 'location': {'required': True},
61 | }
62 |
63 | _attribute_map = {
64 | 'sku': {'key': 'sku', 'type': 'Sku'},
65 | 'kind': {'key': 'kind', 'type': 'Kind'},
66 | 'location': {'key': 'location', 'type': 'str'},
67 | 'tags': {'key': 'tags', 'type': '{str}'},
68 | 'custom_domain': {'key': 'properties.customDomain', 'type': 'CustomDomain'},
69 | 'encryption': {'key': 'properties.encryption', 'type': 'Encryption'},
70 | 'access_tier': {'key': 'properties.accessTier', 'type': 'AccessTier'},
71 | 'enable_https_traffic_only': {'key': 'properties.supportsHttpsTrafficOnly', 'type': 'bool'},
72 | }
73 |
74 | def __init__(self, sku, kind, location, tags=None, custom_domain=None, encryption=None, access_tier=None, enable_https_traffic_only=False):
75 | self.sku = sku
76 | self.kind = kind
77 | self.location = location
78 | self.tags = tags
79 | self.custom_domain = custom_domain
80 | self.encryption = encryption
81 | self.access_tier = access_tier
82 | self.enable_https_traffic_only = enable_https_traffic_only
83 |
--------------------------------------------------------------------------------
/tests/storage_models/storage_account_key.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class StorageAccountKey(Model):
16 | """An access key for the storage account.
17 |
18 | Variables are only populated by the server, and will be ignored when
19 | sending a request.
20 |
21 | :ivar key_name: Name of the key.
22 | :vartype key_name: str
23 | :ivar value: Base 64-encoded value of the key.
24 | :vartype value: str
25 | :ivar permissions: Permissions for the key -- read-only or full
26 | permissions. Possible values include: 'Read', 'Full'
27 | :vartype permissions: str or :class:`KeyPermission
28 | `
29 | """
30 |
31 | _validation = {
32 | 'key_name': {'readonly': True},
33 | 'value': {'readonly': True},
34 | 'permissions': {'readonly': True},
35 | }
36 |
37 | _attribute_map = {
38 | 'key_name': {'key': 'keyName', 'type': 'str'},
39 | 'value': {'key': 'value', 'type': 'str'},
40 | 'permissions': {'key': 'permissions', 'type': 'KeyPermission'},
41 | }
42 |
43 | def __init__(self):
44 | self.key_name = None
45 | self.value = None
46 | self.permissions = None
47 |
--------------------------------------------------------------------------------
/tests/storage_models/storage_account_list_keys_result.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class StorageAccountListKeysResult(Model):
16 | """The response from the ListKeys operation.
17 |
18 | Variables are only populated by the server, and will be ignored when
19 | sending a request.
20 |
21 | :ivar keys: Gets the list of storage account keys and their properties for
22 | the specified storage account.
23 | :vartype keys: list of :class:`StorageAccountKey
24 | `
25 | """
26 |
27 | _validation = {
28 | 'keys': {'readonly': True},
29 | }
30 |
31 | _attribute_map = {
32 | 'keys': {'key': 'keys', 'type': '[StorageAccountKey]'},
33 | }
34 |
35 | def __init__(self):
36 | self.keys = None
37 |
--------------------------------------------------------------------------------
/tests/storage_models/storage_account_paged.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.paging import Paged
13 |
14 |
15 | class StorageAccountPaged(Paged):
16 | """
17 | A paging container for iterating over a list of :class:`StorageAccount ` object
18 | """
19 |
20 | _attribute_map = {
21 | 'next_link': {'key': 'nextLink', 'type': 'str'},
22 | 'current_page': {'key': 'value', 'type': '[StorageAccount]'}
23 | }
24 |
25 | def __init__(self, *args, **kwargs):
26 |
27 | super(StorageAccountPaged, self).__init__(*args, **kwargs)
28 |
--------------------------------------------------------------------------------
/tests/storage_models/storage_account_regenerate_key_parameters.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class StorageAccountRegenerateKeyParameters(Model):
16 | """The parameters used to regenerate the storage account key.
17 |
18 | :param key_name:
19 | :type key_name: str
20 | """
21 |
22 | _validation = {
23 | 'key_name': {'required': True},
24 | }
25 |
26 | _attribute_map = {
27 | 'key_name': {'key': 'keyName', 'type': 'str'},
28 | }
29 |
30 | def __init__(self, key_name):
31 | self.key_name = key_name
32 |
--------------------------------------------------------------------------------
/tests/storage_models/storage_account_update_parameters.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class StorageAccountUpdateParameters(Model):
16 | """The parameters that can be provided when updating the storage account
17 | properties.
18 |
19 | :param sku: Gets or sets the SKU name. Note that the SKU name cannot be
20 | updated to Standard_ZRS or Premium_LRS, nor can accounts of those sku
21 | names be updated to any other value.
22 | :type sku: :class:`Sku `
23 | :param tags: Gets or sets a list of key value pairs that describe the
24 | resource. These tags can be used in viewing and grouping this resource
25 | (across resource groups). A maximum of 15 tags can be provided for a
26 | resource. Each tag must have a key no greater in length than 128
27 | characters and a value no greater in length than 256 characters.
28 | :type tags: dict
29 | :param custom_domain: Custom domain assigned to the storage account by the
30 | user. Name is the CNAME source. Only one custom domain is supported per
31 | storage account at this time. To clear the existing custom domain, use an
32 | empty string for the custom domain name property.
33 | :type custom_domain: :class:`CustomDomain
34 | `
35 | :param encryption: Provides the encryption settings on the account. The
36 | default setting is unencrypted.
37 | :type encryption: :class:`Encryption
38 | `
39 | :param access_tier: Required for storage accounts where kind =
40 | BlobStorage. The access tier used for billing. Possible values include:
41 | 'Hot', 'Cool'
42 | :type access_tier: str or :class:`AccessTier
43 | `
44 | :param enable_https_traffic_only: Allows https traffic only to storage
45 | service if sets to true. Default value: False .
46 | :type enable_https_traffic_only: bool
47 | """
48 |
49 | _attribute_map = {
50 | 'sku': {'key': 'sku', 'type': 'Sku'},
51 | 'tags': {'key': 'tags', 'type': '{str}'},
52 | 'custom_domain': {'key': 'properties.customDomain', 'type': 'CustomDomain'},
53 | 'encryption': {'key': 'properties.encryption', 'type': 'Encryption'},
54 | 'access_tier': {'key': 'properties.accessTier', 'type': 'AccessTier'},
55 | 'enable_https_traffic_only': {'key': 'properties.supportsHttpsTrafficOnly', 'type': 'bool'},
56 | }
57 |
58 | def __init__(self, sku=None, tags=None, custom_domain=None, encryption=None, access_tier=None, enable_https_traffic_only=False):
59 | self.sku = sku
60 | self.tags = tags
61 | self.custom_domain = custom_domain
62 | self.encryption = encryption
63 | self.access_tier = access_tier
64 | self.enable_https_traffic_only = enable_https_traffic_only
65 |
--------------------------------------------------------------------------------
/tests/storage_models/storage_management_client_enums.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from enum import Enum
13 |
14 |
15 | class Reason(Enum):
16 |
17 | account_name_invalid = "AccountNameInvalid"
18 | already_exists = "AlreadyExists"
19 |
20 |
21 | class SkuName(Enum):
22 |
23 | standard_lrs = "Standard_LRS"
24 | standard_grs = "Standard_GRS"
25 | standard_ragrs = "Standard_RAGRS"
26 | standard_zrs = "Standard_ZRS"
27 | premium_lrs = "Premium_LRS"
28 |
29 |
30 | class SkuTier(Enum):
31 |
32 | standard = "Standard"
33 | premium = "Premium"
34 |
35 |
36 | class AccessTier(Enum):
37 |
38 | hot = "Hot"
39 | cool = "Cool"
40 |
41 |
42 | class Kind(Enum):
43 |
44 | storage = "Storage"
45 | blob_storage = "BlobStorage"
46 |
47 |
48 | class ProvisioningState(Enum):
49 |
50 | creating = "Creating"
51 | resolving_dns = "ResolvingDNS"
52 | succeeded = "Succeeded"
53 |
54 |
55 | class AccountStatus(Enum):
56 |
57 | available = "available"
58 | unavailable = "unavailable"
59 |
60 |
61 | class KeyPermission(Enum):
62 |
63 | read = "Read"
64 | full = "Full"
65 |
66 |
67 | class UsageUnit(Enum):
68 |
69 | count = "Count"
70 | bytes = "Bytes"
71 | seconds = "Seconds"
72 | percent = "Percent"
73 | counts_per_second = "CountsPerSecond"
74 | bytes_per_second = "BytesPerSecond"
75 |
76 |
77 | class HttpProtocol(Enum):
78 |
79 | httpshttp = "https,http"
80 | https = "https"
81 |
--------------------------------------------------------------------------------
/tests/storage_models/usage.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class Usage(Model):
16 | """Describes Storage Resource Usage.
17 |
18 | Variables are only populated by the server, and will be ignored when
19 | sending a request.
20 |
21 | :ivar unit: Gets the unit of measurement. Possible values include:
22 | 'Count', 'Bytes', 'Seconds', 'Percent', 'CountsPerSecond',
23 | 'BytesPerSecond'
24 | :vartype unit: str or :class:`UsageUnit
25 | `
26 | :ivar current_value: Gets the current count of the allocated resources in
27 | the subscription.
28 | :vartype current_value: int
29 | :ivar limit: Gets the maximum count of the resources that can be allocated
30 | in the subscription.
31 | :vartype limit: int
32 | :ivar name: Gets the name of the type of usage.
33 | :vartype name: :class:`UsageName
34 | `
35 | """
36 |
37 | _validation = {
38 | 'unit': {'readonly': True},
39 | 'current_value': {'readonly': True},
40 | 'limit': {'readonly': True},
41 | 'name': {'readonly': True},
42 | }
43 |
44 | _attribute_map = {
45 | 'unit': {'key': 'unit', 'type': 'UsageUnit'},
46 | 'current_value': {'key': 'currentValue', 'type': 'int'},
47 | 'limit': {'key': 'limit', 'type': 'int'},
48 | 'name': {'key': 'name', 'type': 'UsageName'},
49 | }
50 |
51 | def __init__(self):
52 | self.unit = None
53 | self.current_value = None
54 | self.limit = None
55 | self.name = None
56 |
--------------------------------------------------------------------------------
/tests/storage_models/usage_name.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.serialization import Model
13 |
14 |
15 | class UsageName(Model):
16 | """The usage names that can be used; currently limited to StorageAccount.
17 |
18 | Variables are only populated by the server, and will be ignored when
19 | sending a request.
20 |
21 | :ivar value: Gets a string describing the resource name.
22 | :vartype value: str
23 | :ivar localized_value: Gets a localized string describing the resource
24 | name.
25 | :vartype localized_value: str
26 | """
27 |
28 | _validation = {
29 | 'value': {'readonly': True},
30 | 'localized_value': {'readonly': True},
31 | }
32 |
33 | _attribute_map = {
34 | 'value': {'key': 'value', 'type': 'str'},
35 | 'localized_value': {'key': 'localizedValue', 'type': 'str'},
36 | }
37 |
38 | def __init__(self):
39 | self.value = None
40 | self.localized_value = None
41 |
--------------------------------------------------------------------------------
/tests/storage_models/usage_paged.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # --------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | # Licensed under the MIT License. See License.txt in the project root for
5 | # license information.
6 | #
7 | # Code generated by Microsoft (R) AutoRest Code Generator.
8 | # Changes may cause incorrect behavior and will be lost if the code is
9 | # regenerated.
10 | # --------------------------------------------------------------------------
11 |
12 | from msrest.paging import Paged
13 |
14 |
15 | class UsagePaged(Paged):
16 | """
17 | A paging container for iterating over a list of :class:`Usage ` object
18 | """
19 |
20 | _attribute_map = {
21 | 'next_link': {'key': 'nextLink', 'type': 'str'},
22 | 'current_page': {'key': 'value', 'type': '[Usage]'}
23 | }
24 |
25 | def __init__(self, *args, **kwargs):
26 |
27 | super(UsagePaged, self).__init__(*args, **kwargs)
28 |
--------------------------------------------------------------------------------
/tests/test_auth.py:
--------------------------------------------------------------------------------
1 | #--------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to deal
9 | # in the Software without restriction, including without limitation the rights
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | # copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | # THE SOFTWARE.
24 | #
25 | #--------------------------------------------------------------------------
26 |
27 | import os
28 | import sys
29 | import json
30 | import isodate
31 | from datetime import datetime
32 | import base64
33 | from base64 import b64decode
34 | import unittest
35 | try:
36 | from unittest import mock
37 | except ImportError:
38 | import mock
39 |
40 | from msrest.authentication import (
41 | BasicAuthentication,
42 | BasicTokenAuthentication,
43 | OAuthTokenAuthentication,
44 | ApiKeyCredentials,
45 | CognitiveServicesCredentials,
46 | TopicCredentials,
47 | DomainCredentials
48 | )
49 |
50 | from requests import Request, PreparedRequest
51 |
52 |
53 | class TestAuthentication(unittest.TestCase):
54 |
55 | def setUp(self):
56 |
57 | self.request = mock.create_autospec(Request)
58 | self.request.headers = {}
59 | self.request.cookies = {}
60 | self.request.auth = None
61 | self.request.url = "http://my_endpoint.com"
62 | self.request.method = 'GET'
63 | self.request.files = None
64 | self.request.data = None
65 | self.request.json = None
66 | self.request.params = {}
67 | self.request.hooks = {}
68 |
69 | return super(TestAuthentication, self).setUp()
70 |
71 | def test_basic_auth(self):
72 |
73 | basic = BasicAuthentication("username", "password")
74 | session = basic.signed_session()
75 |
76 | req = session.auth(self.request)
77 | self.assertTrue('Authorization' in req.headers)
78 | self.assertTrue(req.headers['Authorization'].startswith('Basic '))
79 |
80 | def test_basic_token_auth(self):
81 |
82 | token = {
83 | 'access_token': '123456789'
84 | }
85 | basic = BasicTokenAuthentication(token)
86 | basic.set_token() # Just check that this does not raise
87 | session = basic.signed_session()
88 |
89 | req = session.prepare_request(self.request)
90 | assert 'Authorization' in req.headers
91 | assert req.headers['Authorization'] == 'Bearer 123456789'
92 |
93 | def test_token_auth(self):
94 |
95 | token = {
96 | 'access_token': '123456789'
97 | }
98 | auth = OAuthTokenAuthentication("client_id", token)
99 | session = auth.signed_session()
100 |
101 | request = PreparedRequest()
102 | request.prepare("GET", "https://example.org")
103 | session.auth(request)
104 | assert request.headers == {'Authorization': 'Bearer 123456789'}
105 |
106 | def test_apikey_auth(self):
107 | auth = ApiKeyCredentials(
108 | in_headers={
109 | 'testheader' : 'testheadervalue'
110 | }
111 | )
112 | session = auth.signed_session()
113 | prep_req = session.prepare_request(self.request)
114 | self.assertDictContainsSubset({'testheader' : 'testheadervalue'}, prep_req.headers)
115 |
116 | auth = ApiKeyCredentials(
117 | in_query={
118 | 'testquery' : 'testparamvalue'
119 | }
120 | )
121 | session = auth.signed_session()
122 | prep_req = session.prepare_request(self.request)
123 | assert "testquery=testparamvalue" in prep_req.path_url
124 |
125 | def test_cs_auth(self):
126 | auth = CognitiveServicesCredentials("mysubkey")
127 | session = auth.signed_session()
128 | prep_req = session.prepare_request(self.request)
129 | self.assertDictContainsSubset({'Ocp-Apim-Subscription-Key' : 'mysubkey'}, prep_req.headers)
130 |
131 | def test_eventgrid_auth(self):
132 | auth = TopicCredentials("mytopickey")
133 | session = auth.signed_session()
134 | prep_req = session.prepare_request(self.request)
135 | self.assertDictContainsSubset({'aeg-sas-key' : 'mytopickey'}, prep_req.headers)
136 |
137 | def test_eventgrid_domain_auth(self):
138 | auth = DomainCredentials("mydomainkey")
139 | session = auth.signed_session()
140 | prep_req = session.prepare_request(self.request)
141 | self.assertDictContainsSubset({'aeg-sas-key' : 'mydomainkey'}, prep_req.headers)
142 |
143 | if __name__ == '__main__':
144 | unittest.main()
145 |
146 |
--------------------------------------------------------------------------------
/tests/test_exceptions.py:
--------------------------------------------------------------------------------
1 | #--------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to deal
9 | # in the Software without restriction, including without limitation the rights
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | # copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | # THE SOFTWARE.
24 | #
25 | #--------------------------------------------------------------------------
26 | import json
27 | import unittest
28 | try:
29 | from unittest import mock
30 | except ImportError:
31 | import mock
32 |
33 | import requests
34 |
35 | from msrest.serialization import Model, Deserializer
36 | from msrest.exceptions import HttpOperationError
37 |
38 |
39 | class TestExceptions(unittest.TestCase):
40 |
41 | def test_request_exception(self):
42 | def raise_for_status():
43 | raise requests.RequestException()
44 |
45 | deserializer = Deserializer()
46 | response = mock.create_autospec(requests.Response)
47 | response.raise_for_status = raise_for_status
48 | response.reason = "TESTING"
49 |
50 | excep = HttpOperationError(deserializer, response)
51 |
52 | self.assertIn("TESTING", str(excep))
53 | self.assertIn("Operation returned an invalid status code", str(excep))
54 |
55 | def test_custom_exception(self):
56 |
57 | class ErrorResponse(Model):
58 | _attribute_map = {
59 | 'error': {'key': 'error', 'type': 'ErrorDetails'},
60 | }
61 | def __init__(self, error=None):
62 | self.error = error
63 |
64 |
65 | class ErrorResponseException(HttpOperationError):
66 | def __init__(self, deserialize, response, *args):
67 | super(ErrorResponseException, self).__init__(deserialize, response, 'ErrorResponse', *args)
68 |
69 | class ErrorDetails(Model):
70 | _validation = {
71 | 'code': {'readonly': True},
72 | 'message': {'readonly': True},
73 | 'target': {'readonly': True},
74 | }
75 |
76 | _attribute_map = {
77 | 'code': {'key': 'code', 'type': 'str'},
78 | 'message': {'key': 'message', 'type': 'str'},
79 | 'target': {'key': 'target', 'type': 'str'},
80 | }
81 |
82 | def __init__(self):
83 | self.code = None
84 | self.message = None
85 | self.target = None
86 |
87 | deserializer = Deserializer({
88 | 'ErrorResponse': ErrorResponse,
89 | 'ErrorDetails': ErrorDetails
90 | })
91 |
92 | response = requests.Response()
93 | response._content_consumed = True
94 | response._content = json.dumps(
95 | {
96 | "error": {
97 | "code": "NotOptedIn",
98 | "message": "You are not allowed to download invoices. Please contact your account administrator to turn on access in the management portal for allowing to download invoices through the API."
99 | }
100 | }
101 | ).encode('utf-8')
102 | response.headers = {"content-type": "application/json; charset=utf8"}
103 |
104 | excep = ErrorResponseException(deserializer, response)
105 |
106 | self.assertIn("NotOptedIn", str(excep))
107 | self.assertIn("You are not allowed to download invoices", str(excep))
108 |
--------------------------------------------------------------------------------
/tests/test_paging.py:
--------------------------------------------------------------------------------
1 | #--------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to deal
9 | # in the Software without restriction, including without limitation the rights
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | # copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | # THE SOFTWARE.
24 | #
25 | #--------------------------------------------------------------------------
26 |
27 | import unittest
28 |
29 | from msrest.paging import Paged
30 |
31 | class FakePaged(Paged):
32 | _attribute_map = {
33 | 'next_link': {'key': 'nextLink', 'type': 'str'},
34 | 'current_page': {'key': 'value', 'type': '[str]'}
35 | }
36 |
37 | def __init__(self, *args, **kwargs):
38 | super(FakePaged, self).__init__(*args, **kwargs)
39 |
40 | class TestPaging(unittest.TestCase):
41 |
42 | def test_basic_paging(self):
43 |
44 | def internal_paging(next_link=None, raw=False):
45 | if not next_link:
46 | return {
47 | 'nextLink': 'page2',
48 | 'value': ['value1.0', 'value1.1']
49 | }
50 | else:
51 | return {
52 | 'nextLink': None,
53 | 'value': ['value2.0', 'value2.1']
54 | }
55 |
56 | deserialized = FakePaged(internal_paging, {})
57 | result_iterated = list(deserialized)
58 | self.assertListEqual(
59 | ['value1.0', 'value1.1', 'value2.0', 'value2.1'],
60 | result_iterated
61 | )
62 |
63 | def test_advance_paging(self):
64 |
65 | def internal_paging(next_link=None, raw=False):
66 | if not next_link:
67 | return {
68 | 'nextLink': 'page2',
69 | 'value': ['value1.0', 'value1.1']
70 | }
71 | else:
72 | return {
73 | 'nextLink': None,
74 | 'value': ['value2.0', 'value2.1']
75 | }
76 |
77 | deserialized = FakePaged(internal_paging, {})
78 | page1 = deserialized.advance_page()
79 | self.assertListEqual(
80 | ['value1.0', 'value1.1'],
81 | page1
82 | )
83 | page2 = deserialized.advance_page()
84 | self.assertListEqual(
85 | ['value2.0', 'value2.1'],
86 | page2
87 | )
88 | with self.assertRaises(StopIteration):
89 | deserialized.advance_page()
90 |
91 | def test_get_paging(self):
92 |
93 | def internal_paging(next_link=None, raw=False):
94 | if not next_link:
95 | return {
96 | 'nextLink': 'page2',
97 | 'value': ['value1.0', 'value1.1']
98 | }
99 | elif next_link == 'page2':
100 | return {
101 | 'nextLink': 'page3',
102 | 'value': ['value2.0', 'value2.1']
103 | }
104 | else:
105 | return {
106 | 'nextLink': None,
107 | 'value': ['value3.0', 'value3.1']
108 | }
109 |
110 | deserialized = FakePaged(internal_paging, {})
111 | page2 = deserialized.get('page2')
112 | self.assertListEqual(
113 | ['value2.0', 'value2.1'],
114 | page2
115 | )
116 | page3 = deserialized.get('page3')
117 | self.assertListEqual(
118 | ['value3.0', 'value3.1'],
119 | page3
120 | )
121 |
122 | def test_reset_paging(self):
123 |
124 | def internal_paging(next_link=None, raw=False):
125 | if not next_link:
126 | return {
127 | 'nextLink': 'page2',
128 | 'value': ['value1.0', 'value1.1']
129 | }
130 | else:
131 | return {
132 | 'nextLink': None,
133 | 'value': ['value2.0', 'value2.1']
134 | }
135 |
136 | deserialized = FakePaged(internal_paging, {})
137 | deserialized.reset()
138 | result_iterated = list(deserialized)
139 | self.assertListEqual(
140 | ['value1.0', 'value1.1', 'value2.0', 'value2.1'],
141 | result_iterated
142 | )
143 |
144 | deserialized = FakePaged(internal_paging, {})
145 | # Push the iterator to the last element
146 | for element in deserialized:
147 | if element == "value2.0":
148 | break
149 | deserialized.reset()
150 | result_iterated = list(deserialized)
151 | self.assertListEqual(
152 | ['value1.0', 'value1.1', 'value2.0', 'value2.1'],
153 | result_iterated
154 | )
155 |
156 | def test_none_value(self):
157 | def internal_paging(next_link=None, raw=False):
158 | return {
159 | 'nextLink': None,
160 | 'value': None
161 | }
162 |
163 | deserialized = FakePaged(internal_paging, {})
164 | result_iterated = list(deserialized)
165 | self.assertEqual(len(result_iterated), 0)
166 |
--------------------------------------------------------------------------------
/tests/test_pipeline.py:
--------------------------------------------------------------------------------
1 | #--------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to deal
9 | # in the Software without restriction, including without limitation the rights
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | # copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | # THE SOFTWARE.
24 | #
25 | #--------------------------------------------------------------------------
26 |
27 | import json
28 | import requests
29 | import datetime
30 | from enum import Enum
31 | import unittest
32 | try:
33 | from unittest import mock
34 | except ImportError:
35 | import mock
36 | import xml.etree.ElementTree as ET
37 | import sys
38 |
39 | import pytest
40 |
41 | from msrest.universal_http import (
42 | ClientRequest,
43 | )
44 | from msrest.pipeline import (
45 | ClientRawResponse,
46 | SansIOHTTPPolicy,
47 | Pipeline,
48 | HTTPSender
49 | )
50 |
51 | from msrest import Configuration
52 |
53 |
54 | def test_sans_io_exception():
55 | class BrokenSender(HTTPSender):
56 | def send(self, request, **config):
57 | raise ValueError("Broken")
58 |
59 | def __exit__(self, exc_type, exc_value, traceback):
60 | """Raise any exception triggered within the runtime context."""
61 | return None
62 |
63 | pipeline = Pipeline([SansIOHTTPPolicy()], BrokenSender())
64 |
65 | req = ClientRequest('GET', '/')
66 | with pytest.raises(ValueError):
67 | pipeline.run(req)
68 |
69 | class SwapExec(SansIOHTTPPolicy):
70 | def on_exception(self, requests, **kwargs):
71 | exc_type, exc_value, exc_traceback = sys.exc_info()
72 | raise NotImplementedError(exc_value)
73 |
74 | pipeline = Pipeline([SwapExec()], BrokenSender())
75 | with pytest.raises(NotImplementedError):
76 | pipeline.run(req)
77 |
78 |
79 | class TestClientRequest(unittest.TestCase):
80 |
81 | def test_request_data(self):
82 |
83 | request = ClientRequest('GET', '/')
84 | data = "Lots of dataaaa"
85 | request.add_content(data)
86 |
87 | self.assertEqual(request.data, json.dumps(data))
88 | self.assertEqual(request.headers.get('Content-Length'), '17')
89 |
90 | def test_request_xml(self):
91 | request = ClientRequest('GET', '/')
92 | data = ET.Element("root")
93 | request.add_content(data)
94 |
95 | assert request.data == b"\n"
96 |
97 | def test_request_url_with_params(self):
98 |
99 | request = ClientRequest('GET', '/')
100 | request.url = "a/b/c?t=y"
101 | request.format_parameters({'g': 'h'})
102 |
103 | self.assertIn(request.url, [
104 | 'a/b/c?g=h&t=y',
105 | 'a/b/c?t=y&g=h'
106 | ])
107 |
108 | class TestClientResponse(unittest.TestCase):
109 |
110 | class Colors(Enum):
111 | red = 'red'
112 | blue = 'blue'
113 |
114 | def test_raw_response(self):
115 |
116 | response = mock.create_autospec(requests.Response)
117 | response.headers = {}
118 | response.headers["my-test"] = '1999-12-31T23:59:59-23:59'
119 | response.headers["colour"] = "red"
120 |
121 | raw = ClientRawResponse([], response)
122 |
123 | raw.add_headers({'my-test': 'iso-8601',
124 | 'another_header': 'str',
125 | 'colour': TestClientResponse.Colors})
126 | self.assertIsInstance(raw.headers['my-test'], datetime.datetime)
127 |
128 | if __name__ == '__main__':
129 | unittest.main()
130 |
--------------------------------------------------------------------------------
/tests/test_polling.py:
--------------------------------------------------------------------------------
1 | #--------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to deal
9 | # in the Software without restriction, including without limitation the rights
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | # copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | # THE SOFTWARE.
24 | #
25 | #--------------------------------------------------------------------------
26 | import time
27 | try:
28 | from unittest import mock
29 | except ImportError:
30 | import mock
31 |
32 | import pytest
33 |
34 | from msrest.polling import *
35 | from msrest.service_client import ServiceClient
36 | from msrest.serialization import Model
37 | from msrest.configuration import Configuration
38 |
39 |
40 | def test_abc_polling():
41 | abc_polling = PollingMethod()
42 |
43 | with pytest.raises(NotImplementedError):
44 | abc_polling.initialize(None, None, None)
45 |
46 | with pytest.raises(NotImplementedError):
47 | abc_polling.run()
48 |
49 | with pytest.raises(NotImplementedError):
50 | abc_polling.status()
51 |
52 | with pytest.raises(NotImplementedError):
53 | abc_polling.finished()
54 |
55 | with pytest.raises(NotImplementedError):
56 | abc_polling.resource()
57 |
58 | def test_no_polling():
59 | no_polling = NoPolling()
60 |
61 | initial_response = "initial response"
62 | def deserialization_cb(response):
63 | assert response == initial_response
64 | return "Treated: "+response
65 |
66 | no_polling.initialize(None, initial_response, deserialization_cb)
67 | no_polling.run() # Should no raise and do nothing
68 | assert no_polling.status() == "succeeded"
69 | assert no_polling.finished()
70 | assert no_polling.resource() == "Treated: "+initial_response
71 |
72 |
73 | class PollingTwoSteps(PollingMethod):
74 | """An empty poller that returns the deserialized initial response.
75 | """
76 | def __init__(self, sleep=0):
77 | self._initial_response = None
78 | self._deserialization_callback = None
79 | self._sleep = sleep
80 |
81 | def initialize(self, _, initial_response, deserialization_callback):
82 | self._initial_response = initial_response
83 | self._deserialization_callback = deserialization_callback
84 | self._finished = False
85 |
86 | def run(self):
87 | """Empty run, no polling.
88 | """
89 | self._finished = True
90 | time.sleep(self._sleep) # Give me time to add callbacks!
91 |
92 | def status(self):
93 | """Return the current status as a string.
94 | :rtype: str
95 | """
96 | return "succeeded" if self._finished else "running"
97 |
98 | def finished(self):
99 | """Is this polling finished?
100 | :rtype: bool
101 | """
102 | return self._finished
103 |
104 | def resource(self):
105 | return self._deserialization_callback(self._initial_response)
106 |
107 | @pytest.fixture
108 | def client():
109 | # We need a ServiceClient instance, but the poller itself don't use it, so we don't need
110 | # Something functional
111 | return ServiceClient(None, Configuration("http://example.org"))
112 |
113 | def test_poller(client):
114 |
115 | # Same the poller itself doesn't care about the initial_response, and there is no type constraint here
116 | initial_response = "Initial response"
117 |
118 | # Same for deserialization_callback, just pass to the polling_method
119 | def deserialization_callback(response):
120 | assert response == initial_response
121 | return "Treated: "+response
122 |
123 | method = NoPolling()
124 |
125 | poller = LROPoller(client, initial_response, deserialization_callback, method)
126 |
127 | done_cb = mock.MagicMock()
128 | poller.add_done_callback(done_cb)
129 |
130 | result = poller.result()
131 | assert poller.done()
132 | assert result == "Treated: "+initial_response
133 | assert poller.status() == "succeeded"
134 | done_cb.assert_called_once_with(method)
135 |
136 | # Test with a basic Model
137 | poller = LROPoller(client, initial_response, Model, method)
138 | assert poller._polling_method._deserialization_callback == Model.deserialize
139 |
140 | # Test poller that method do a run
141 | method = PollingTwoSteps(sleep=1)
142 | poller = LROPoller(client, initial_response, deserialization_callback, method)
143 |
144 | done_cb = mock.MagicMock()
145 | done_cb2 = mock.MagicMock()
146 | poller.add_done_callback(done_cb)
147 | poller.remove_done_callback(done_cb2)
148 |
149 | result = poller.result()
150 | assert result == "Treated: "+initial_response
151 | assert poller.status() == "succeeded"
152 | done_cb.assert_called_once_with(method)
153 | done_cb2.assert_not_called()
154 |
155 | with pytest.raises(ValueError) as excinfo:
156 | poller.remove_done_callback(done_cb)
157 | assert "Process is complete" in str(excinfo.value)
158 |
159 | def test_broken_poller(client):
160 |
161 | with pytest.raises(ValueError):
162 | LROPoller(None, None, None, None)
163 |
164 | class NoPollingError(PollingTwoSteps):
165 | def run(self):
166 | raise ValueError("Something bad happened")
167 |
168 | initial_response = "Initial response"
169 | def deserialization_callback(response):
170 | return "Treated: "+response
171 |
172 | method = NoPollingError()
173 | poller = LROPoller(client, initial_response, deserialization_callback, method)
174 |
175 | with pytest.raises(ValueError) as excinfo:
176 | poller.result()
177 | assert "Something bad happened" in str(excinfo.value)
178 |
--------------------------------------------------------------------------------
/tests/test_requests_universal.py:
--------------------------------------------------------------------------------
1 | #--------------------------------------------------------------------------
2 | #
3 | # Copyright (c) Microsoft Corporation. All rights reserved.
4 | #
5 | # The MIT License (MIT)
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the ""Software""), to deal
9 | # in the Software without restriction, including without limitation the rights
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | # copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in
15 | # all copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | # THE SOFTWARE.
24 | #
25 | #--------------------------------------------------------------------------
26 | import concurrent.futures
27 |
28 | from requests.adapters import HTTPAdapter
29 |
30 | from msrest.universal_http import (
31 | ClientRequest
32 | )
33 | from msrest.universal_http.requests import (
34 | BasicRequestsHTTPSender,
35 | RequestsHTTPSender,
36 | RequestHTTPSenderConfiguration
37 | )
38 |
39 | def test_session_callback():
40 |
41 | cfg = RequestHTTPSenderConfiguration()
42 | with RequestsHTTPSender(cfg) as driver:
43 |
44 | def callback(session, global_config, local_config, **kwargs):
45 | assert session is driver.session
46 | assert global_config is cfg
47 | assert local_config["test"]
48 | my_kwargs = kwargs.copy()
49 | my_kwargs.update({'used_callback': True})
50 | return my_kwargs
51 |
52 | cfg.session_configuration_callback = callback
53 |
54 | request = ClientRequest('GET', 'http://127.0.0.1/')
55 | output_kwargs = driver._configure_send(request, **{"test": True})
56 | assert output_kwargs['used_callback']
57 |
58 | def test_max_retries_on_default_adapter():
59 | # max_retries must be applied only on the default adapters of requests
60 | # If the user adds its own adapter, don't touch it
61 | cfg = RequestHTTPSenderConfiguration()
62 | max_retries = cfg.retry_policy()
63 |
64 | with RequestsHTTPSender(cfg) as driver:
65 | request = ClientRequest('GET', '/')
66 | driver.session.mount('"http://127.0.0.1/"', HTTPAdapter())
67 |
68 | driver._configure_send(request)
69 | assert driver.session.adapters["http://"].max_retries is max_retries
70 | assert driver.session.adapters["https://"].max_retries is max_retries
71 | assert driver.session.adapters['"http://127.0.0.1/"'].max_retries is not max_retries
72 |
73 |
74 | def test_threading_basic_requests():
75 | # Basic should have the session for all threads, it's why it's not recommended
76 | sender = BasicRequestsHTTPSender()
77 | main_thread_session = sender.session
78 |
79 | def thread_body(local_sender):
80 | # Should be the same session
81 | assert local_sender.session is main_thread_session
82 |
83 | return True
84 |
85 | with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
86 | future = executor.submit(thread_body, sender)
87 | assert future.result()
88 |
89 | def test_threading_cfg_requests():
90 | cfg = RequestHTTPSenderConfiguration()
91 |
92 | # The one with conf however, should have one session per thread automatically
93 | sender = RequestsHTTPSender(cfg)
94 | main_thread_session = sender.session
95 |
96 | # Check that this main session is patched
97 | assert main_thread_session.resolve_redirects.is_msrest_patched
98 |
99 | def thread_body(local_sender):
100 | # Should have it's own session
101 | assert local_sender.session is not main_thread_session
102 | # But should be patched as the main thread session
103 | assert local_sender.session.resolve_redirects.is_msrest_patched
104 | return True
105 |
106 | with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
107 | future = executor.submit(thread_body, sender)
108 | assert future.result()
109 |
--------------------------------------------------------------------------------
/tests/test_universal_pipeline.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #--------------------------------------------------------------------------
3 | #
4 | # Copyright (c) Microsoft Corporation. All rights reserved.
5 | #
6 | # The MIT License (MIT)
7 | #
8 | # Permission is hereby granted, free of charge, to any person obtaining a copy
9 | # of this software and associated documentation files (the ""Software""), to deal
10 | # in the Software without restriction, including without limitation the rights
11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | # copies of the Software, and to permit persons to whom the Software is
13 | # furnished to do so, subject to the following conditions:
14 | #
15 | # The above copyright notice and this permission notice shall be included in
16 | # all copies or substantial portions of the Software.
17 | #
18 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | # THE SOFTWARE.
25 | #
26 | #--------------------------------------------------------------------------
27 | try:
28 | from unittest import mock
29 | except ImportError:
30 | import mock
31 |
32 | import requests
33 |
34 | import pytest
35 |
36 | from msrest.exceptions import DeserializationError
37 | from msrest.universal_http import (
38 | ClientRequest,
39 | ClientResponse,
40 | HTTPClientResponse,
41 | )
42 | from msrest.universal_http.requests import RequestsClientResponse
43 |
44 | from msrest.pipeline import (
45 | Response,
46 | Request
47 | )
48 | from msrest.pipeline.universal import (
49 | HTTPLogger,
50 | RawDeserializer,
51 | UserAgentPolicy
52 | )
53 |
54 | def test_user_agent():
55 |
56 | with mock.patch.dict('os.environ', {'AZURE_HTTP_USER_AGENT': "mytools"}):
57 | policy = UserAgentPolicy()
58 | assert policy.user_agent.endswith("mytools")
59 |
60 | request = ClientRequest('GET', 'http://127.0.0.1/')
61 | policy.on_request(Request(request))
62 | assert request.headers["user-agent"].endswith("mytools")
63 |
64 | @mock.patch('msrest.http_logger._LOGGER')
65 | def test_no_log(mock_http_logger):
66 | universal_request = ClientRequest('GET', 'http://127.0.0.1/')
67 | request = Request(universal_request)
68 | http_logger = HTTPLogger()
69 | response = Response(request, ClientResponse(universal_request, None))
70 |
71 | # By default, no log handler for HTTP
72 | http_logger.on_request(request)
73 | mock_http_logger.debug.assert_not_called()
74 | http_logger.on_response(request, response)
75 | mock_http_logger.debug.assert_not_called()
76 | mock_http_logger.reset_mock()
77 |
78 | # I can enable it per request
79 | http_logger.on_request(request, **{"enable_http_logger": True})
80 | assert mock_http_logger.debug.call_count >= 1
81 | http_logger.on_response(request, response, **{"enable_http_logger": True})
82 | assert mock_http_logger.debug.call_count >= 1
83 | mock_http_logger.reset_mock()
84 |
85 | # I can enable it per request (bool value should be honored)
86 | http_logger.on_request(request, **{"enable_http_logger": False})
87 | mock_http_logger.debug.assert_not_called()
88 | http_logger.on_response(request, response, **{"enable_http_logger": False})
89 | mock_http_logger.debug.assert_not_called()
90 | mock_http_logger.reset_mock()
91 |
92 | # I can enable it globally
93 | http_logger.enable_http_logger = True
94 | http_logger.on_request(request)
95 | assert mock_http_logger.debug.call_count >= 1
96 | http_logger.on_response(request, response)
97 | assert mock_http_logger.debug.call_count >= 1
98 | mock_http_logger.reset_mock()
99 |
100 | # I can enable it globally and override it locally
101 | http_logger.enable_http_logger = True
102 | http_logger.on_request(request, **{"enable_http_logger": False})
103 | mock_http_logger.debug.assert_not_called()
104 | http_logger.on_response(request, response, **{"enable_http_logger": False})
105 | mock_http_logger.debug.assert_not_called()
106 | mock_http_logger.reset_mock()
107 |
108 |
109 | def test_raw_deserializer():
110 | raw_deserializer = RawDeserializer()
111 |
112 | def build_response(body, content_type=None):
113 | class MockResponse(HTTPClientResponse):
114 | def __init__(self, body, content_type):
115 | super(MockResponse, self).__init__(None, None)
116 | self._body = body
117 | if content_type:
118 | self.headers['content-type'] = content_type
119 |
120 | def body(self):
121 | return self._body
122 | return Response(None, MockResponse(body, content_type))
123 |
124 | # I deserialize XML
125 | response = build_response(b"", content_type="application/xml")
126 | raw_deserializer.on_response(None, response, stream=False)
127 | result = response.context["deserialized_data"]
128 | assert result.tag == "groot"
129 |
130 | # The basic deserializer works with unicode XML
131 | result = raw_deserializer.deserialize_from_text(u'', content_type="application/xml")
132 | assert result.attrib["language"] == u"français"
133 |
134 | # Catch some weird situation where content_type is XML, but content is JSON
135 | response = build_response(b'{"ugly": true}', content_type="application/xml")
136 | raw_deserializer.on_response(None, response, stream=False)
137 | result = response.context["deserialized_data"]
138 | assert result["ugly"] is True
139 |
140 | # Be sure I catch the correct exception if it's neither XML nor JSON
141 | with pytest.raises(DeserializationError):
142 | response = build_response(b'gibberish', content_type="application/xml")
143 | raw_deserializer.on_response(None, response, stream=False)
144 | with pytest.raises(DeserializationError):
145 | response = build_response(b'{{gibberish}}', content_type="application/xml")
146 | raw_deserializer.on_response(None, response, stream=False)
147 |
148 | # Simple JSON
149 | response = build_response(b'{"success": true}', content_type="application/json")
150 | raw_deserializer.on_response(None, response, stream=False)
151 | result = response.context["deserialized_data"]
152 | assert result["success"] is True
153 |
154 | # Simple JSON with complex content_type
155 | response = build_response(b'{"success": true}', content_type="application/vnd.microsoft.appconfig.kv+json")
156 | raw_deserializer.on_response(None, response, stream=False)
157 | result = response.context["deserialized_data"]
158 | assert result["success"] is True
159 |
160 | # JSON with UTF-8 BOM
161 | response = build_response(b'\xef\xbb\xbf{"success": true}', content_type="application/json; charset=utf-8")
162 | raw_deserializer.on_response(None, response, stream=False)
163 | result = response.context["deserialized_data"]
164 | assert result["success"] is True
165 |
166 | # For compat, if no content-type, decode JSON
167 | response = build_response(b'"data"')
168 | raw_deserializer.on_response(None, response, stream=False)
169 | result = response.context["deserialized_data"]
170 | assert result == "data"
171 |
172 | # Try with a mock of requests
173 |
174 | req_response = requests.Response()
175 | req_response.headers["content-type"] = "application/json"
176 | req_response._content = b'{"success": true}'
177 | req_response._content_consumed = True
178 | response = Response(None, RequestsClientResponse(None, req_response))
179 |
180 | raw_deserializer.on_response(None, response, stream=False)
181 | result = response.context["deserialized_data"]
182 | assert result["success"] is True
183 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist=py{36,37,38,39,310,311}
3 | skipsdist=True
4 |
5 | [testenv]
6 | setenv =
7 | PYTHONPATH = {toxinidir}:{toxinidir}/msrest
8 | PythonLogLevel=30
9 | deps=
10 | -rdev_requirements.txt
11 | commands_pre=
12 | autorest: bash ./autorest_setup.sh
13 | commands=
14 | pytest --cov=msrest tests/
15 | autorest: pytest --cov=msrest --cov-append autorest.python/test/vanilla/
16 | coverage report --fail-under=40
17 | coverage xml --ignore-errors # At this point, don't fail for "async" keyword in 2.7/3.4
18 |
--------------------------------------------------------------------------------