diff --git a/modules/swagger-codegen/src/main/resources/python/asyncio/rest.mustache b/modules/swagger-codegen/src/main/resources/python/asyncio/rest.mustache index 986902f56d0..39003abfbfd 100644 --- a/modules/swagger-codegen/src/main/resources/python/asyncio/rest.mustache +++ b/modules/swagger-codegen/src/main/resources/python/asyncio/rest.mustache @@ -47,8 +47,7 @@ class RESTClientObject(object): # if not set certificate file, use Mozilla's root certificates. ca_certs = certifi.where() - ssl_context = ssl.SSLContext() - ssl_context.load_verify_locations(cafile=ca_certs) + ssl_context = ssl.create_default_context(cafile=ca_certs) if configuration.cert_file: ssl_context.load_cert_chain( configuration.cert_file, keyfile=configuration.key_file @@ -113,21 +112,34 @@ class RESTClientObject(object): "timeout": timeout, "headers": headers } + + if query_params: + args["url"] += '?' + urlencode(query_params) + # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']: - if query_params: - url += '?' + urlencode(query_params) if re.search('json', headers['Content-Type'], re.IGNORECASE): if body is not None: body = json.dumps(body) args["data"] = body elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501 - data = aiohttp.FormData() - for k, v in post_params.items(): - data.add_field(k, v) - args["data"] = data + args["data"] = aiohttp.FormData(post_params) elif headers['Content-Type'] == 'multipart/form-data': - args["data"] = post_params + # must del headers['Content-Type'], or the correct + # Content-Type which generated by aiohttp + del headers['Content-Type'] + data = aiohttp.FormData() + for param in post_params: + k, v = param + if isinstance(v, tuple) and len(v) == 3: + data.add_field(k, + value=v[1], + filename=v[0], + content_type=v[2]) + else: + data.add_field(k, v) + args["data"] = data + # Pass a `bytes` parameter directly in the body to support # other content types than Json when `body` argument is provided # in serialized form @@ -139,8 +151,6 @@ class RESTClientObject(object): arguments. Please check that your arguments match declared content type.""" raise ApiException(status=0, reason=msg) - else: - args["data"] = query_params async with self.pool_manager.request(**args) as r: data = await r.text() diff --git a/modules/swagger-codegen/src/main/resources/python/test-requirements.mustache b/modules/swagger-codegen/src/main/resources/python/test-requirements.mustache index 2702246c0e6..31f8d94d99f 100644 --- a/modules/swagger-codegen/src/main/resources/python/test-requirements.mustache +++ b/modules/swagger-codegen/src/main/resources/python/test-requirements.mustache @@ -1,5 +1,11 @@ +{{^asyncio}} coverage>=4.0.3 nose>=1.3.7 +{{/asyncio}} +{{#asyncio}} +pytest>=3.3.1 +pytest-cov>=2.5.1 +{{/asyncio}} pluggy>=0.3.1 py>=1.4.31 randomize>=0.13 diff --git a/modules/swagger-codegen/src/main/resources/python/tox.mustache b/modules/swagger-codegen/src/main/resources/python/tox.mustache index 1cf0829dc93..63d12fdeaea 100644 --- a/modules/swagger-codegen/src/main/resources/python/tox.mustache +++ b/modules/swagger-codegen/src/main/resources/python/tox.mustache @@ -1,10 +1,20 @@ [tox] +{{^asyncio}} envlist = py27, py3 +{{/asyncio}} +{{#asyncio}} +envlist = py3 +{{/asyncio}} [testenv] deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt - + commands= +{{^asyncio}} nosetests \ - [] \ No newline at end of file + [] +{{/asyncio}} +{{#asyncio}} + pytest -v --cov petstore_api +{{/asyncio}} diff --git a/pom.xml b/pom.xml index f9d861bbf9d..ee1488a3b67 100644 --- a/pom.xml +++ b/pom.xml @@ -846,6 +846,7 @@ samples/client/petstore/javascript samples/client/petstore/python samples/client/petstore/python-tornado + samples/client/petstore/python-asyncio samples/client/petstore/typescript-fetch/builds/default samples/client/petstore/typescript-fetch/builds/es6-target samples/client/petstore/typescript-fetch/builds/with-npm-version diff --git a/samples/client/petstore/python-asyncio/Makefile b/samples/client/petstore/python-asyncio/Makefile new file mode 100644 index 00000000000..8d0afd4974d --- /dev/null +++ b/samples/client/petstore/python-asyncio/Makefile @@ -0,0 +1,18 @@ + #!/bin/bash + +REQUIREMENTS_FILE=dev-requirements.txt +REQUIREMENTS_OUT=dev-requirements.txt.log +SETUP_OUT=*.egg-info +VENV=.venv + +clean: + rm -rf $(REQUIREMENTS_OUT) + rm -rf $(SETUP_OUT) + rm -rf $(VENV) + rm -rf .tox + rm -rf .coverage + find . -name "*.py[oc]" -delete + find . -name "__pycache__" -delete + +test-all: clean + bash ./test_python3.sh diff --git a/samples/client/petstore/python-asyncio/dev-requirements.txt b/samples/client/petstore/python-asyncio/dev-requirements.txt new file mode 100644 index 00000000000..49182426e20 --- /dev/null +++ b/samples/client/petstore/python-asyncio/dev-requirements.txt @@ -0,0 +1,4 @@ +tox +coverage +randomize +flake8 diff --git a/samples/client/petstore/python-asyncio/docs/FakeApi.md b/samples/client/petstore/python-asyncio/docs/FakeApi.md index 1da84500bfe..b910ba2b464 100644 --- a/samples/client/petstore/python-asyncio/docs/FakeApi.md +++ b/samples/client/petstore/python-asyncio/docs/FakeApi.md @@ -273,10 +273,10 @@ configuration.password = 'YOUR_PASSWORD' # create an instance of the API class api_instance = petstore_api.FakeApi(petstore_api.ApiClient(configuration)) -number = 8.14 # float | None +number = 3.4 # float | None double = 1.2 # float | None pattern_without_delimiter = 'pattern_without_delimiter_example' # str | None -byte = 'B' # str | None +byte = 'byte_example' # str | None integer = 56 # int | None (optional) int32 = 56 # int | None (optional) int64 = 789 # int | None (optional) diff --git a/samples/client/petstore/python-asyncio/git_push.sh b/samples/client/petstore/python-asyncio/git_push.sh index ed374619b13..ae01b182ae9 100644 --- a/samples/client/petstore/python-asyncio/git_push.sh +++ b/samples/client/petstore/python-asyncio/git_push.sh @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/samples/client/petstore/python-asyncio/petstore_api/rest.py b/samples/client/petstore/python-asyncio/petstore_api/rest.py index 497882aaa9f..69d87b282c6 100644 --- a/samples/client/petstore/python-asyncio/petstore_api/rest.py +++ b/samples/client/petstore/python-asyncio/petstore_api/rest.py @@ -56,8 +56,7 @@ class RESTClientObject(object): # if not set certificate file, use Mozilla's root certificates. ca_certs = certifi.where() - ssl_context = ssl.SSLContext() - ssl_context.load_verify_locations(cafile=ca_certs) + ssl_context = ssl.create_default_context(cafile=ca_certs) if configuration.cert_file: ssl_context.load_cert_chain( configuration.cert_file, keyfile=configuration.key_file @@ -122,21 +121,34 @@ class RESTClientObject(object): "timeout": timeout, "headers": headers } + + if query_params: + args["url"] += '?' + urlencode(query_params) + # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']: - if query_params: - url += '?' + urlencode(query_params) if re.search('json', headers['Content-Type'], re.IGNORECASE): if body is not None: body = json.dumps(body) args["data"] = body elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501 - data = aiohttp.FormData() - for k, v in post_params.items(): - data.add_field(k, v) - args["data"] = data + args["data"] = aiohttp.FormData(post_params) elif headers['Content-Type'] == 'multipart/form-data': - args["data"] = post_params + # must del headers['Content-Type'], or the correct + # Content-Type which generated by aiohttp + del headers['Content-Type'] + data = aiohttp.FormData() + for param in post_params: + k, v = param + if isinstance(v, tuple) and len(v) == 3: + data.add_field(k, + value=v[1], + filename=v[0], + content_type=v[2]) + else: + data.add_field(k, v) + args["data"] = data + # Pass a `bytes` parameter directly in the body to support # other content types than Json when `body` argument is provided # in serialized form @@ -148,8 +160,6 @@ class RESTClientObject(object): arguments. Please check that your arguments match declared content type.""" raise ApiException(status=0, reason=msg) - else: - args["data"] = query_params async with self.pool_manager.request(**args) as r: data = await r.text() diff --git a/samples/client/petstore/python-asyncio/pom.xml b/samples/client/petstore/python-asyncio/pom.xml new file mode 100644 index 00000000000..b370bd02469 --- /dev/null +++ b/samples/client/petstore/python-asyncio/pom.xml @@ -0,0 +1,46 @@ + + 4.0.0 + io.swagger + PythonAsyncioClientTests + pom + 1.0-SNAPSHOT + Python Asyncio Petstore Client + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory} + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + pytest-test + integration-test + + exec + + + make + + test-all + + + + + + + + diff --git a/samples/client/petstore/python-asyncio/test-requirements.txt b/samples/client/petstore/python-asyncio/test-requirements.txt index 2702246c0e6..1c20b09b865 100644 --- a/samples/client/petstore/python-asyncio/test-requirements.txt +++ b/samples/client/petstore/python-asyncio/test-requirements.txt @@ -1,5 +1,5 @@ -coverage>=4.0.3 -nose>=1.3.7 +pytest>=3.3.1 +pytest-cov>=2.5.1 pluggy>=0.3.1 py>=1.4.31 randomize>=0.13 diff --git a/samples/client/petstore/python-asyncio/test_python3.sh b/samples/client/petstore/python-asyncio/test_python3.sh new file mode 100755 index 00000000000..01abf0b35b1 --- /dev/null +++ b/samples/client/petstore/python-asyncio/test_python3.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +REQUIREMENTS_FILE=dev-requirements.txt +REQUIREMENTS_OUT=dev-requirements.txt.log +SETUP_OUT=*.egg-info +VENV=.venv +DEACTIVE=false + +export LC_ALL=en_US.UTF-8 +export LANG=en_US.UTF-8 + +### set virtualenv +if [ -z "$VIRTUAL_ENV" ]; then + virtualenv $VENV --no-site-packages --always-copy --python python3 + source $VENV/bin/activate + DEACTIVE=true +fi + +### install dependencies +pip install -r $REQUIREMENTS_FILE | tee -a $REQUIREMENTS_OUT +python setup.py develop + +### run tests +tox || exit 1 + +### static analysis of code +flake8 --show-source petstore_api/ + +### deactivate virtualenv +#if [ $DEACTIVE == true ]; then +# deactivate +#fi diff --git a/samples/client/petstore/python-asyncio/testfiles/foo.png b/samples/client/petstore/python-asyncio/testfiles/foo.png new file mode 100644 index 00000000000..a9b12cf5927 Binary files /dev/null and b/samples/client/petstore/python-asyncio/testfiles/foo.png differ diff --git a/samples/client/petstore/python-asyncio/tests/__init__.py b/samples/client/petstore/python-asyncio/tests/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/samples/client/petstore/python-asyncio/tests/test_pet_api.py b/samples/client/petstore/python-asyncio/tests/test_pet_api.py new file mode 100644 index 00000000000..ad143e246fa --- /dev/null +++ b/samples/client/petstore/python-asyncio/tests/test_pet_api.py @@ -0,0 +1,208 @@ +# coding: utf-8 + +# flake8: noqa + +""" +Run the tests. +$ docker pull swaggerapi/petstore +$ docker run -d -e SWAGGER_HOST=http://petstore.swagger.io -e SWAGGER_BASE_PATH=/v2 -p 80:8080 swaggerapi/petstore +$ pytest -vv +""" + +import os +import unittest +import asyncio +import pytest + +import petstore_api +from petstore_api import Configuration +from petstore_api.rest import ApiException + +from .util import id_gen + +import json + +import urllib3 + +HOST = 'http://localhost:80/v2' + + +def async_test(f): + def wrapper(*args, **kwargs): + coro = asyncio.coroutine(f) + future = coro(*args, **kwargs) + loop = asyncio.get_event_loop() + loop.run_until_complete(future) + return wrapper + + +class TestPetApiTests(unittest.TestCase): + + def setUp(self): + config = Configuration() + config.host = HOST + + self.api_client = petstore_api.ApiClient(config) + self.pet_api = petstore_api.PetApi(self.api_client) + self.setUpModels() + self.setUpFiles() + + def setUpModels(self): + self.category = petstore_api.Category() + self.category.id = id_gen() + self.category.name = "dog" + self.tag = petstore_api.Tag() + self.tag.id = id_gen() + self.tag.name = "swagger-codegen-python-pet-tag" + self.pet = petstore_api.Pet(name="hello kity", photo_urls=["http://foo.bar.com/1", "http://foo.bar.com/2"]) + self.pet.id = id_gen() + self.pet.status = "sold" + self.pet.category = self.category + self.pet.tags = [self.tag] + + def setUpFiles(self): + self.test_file_dir = os.path.join(os.path.dirname(__file__), "..", "testfiles") + self.test_file_dir = os.path.realpath(self.test_file_dir) + self.foo = os.path.join(self.test_file_dir, "foo.png") + + def test_separate_default_client_instances(self): + pet_api = petstore_api.PetApi() + pet_api2 = petstore_api.PetApi() + self.assertNotEqual(pet_api.api_client, pet_api2.api_client) + + pet_api.api_client.user_agent = 'api client 3' + pet_api2.api_client.user_agent = 'api client 4' + + self.assertNotEqual(pet_api.api_client.user_agent, pet_api2.api_client.user_agent) + + def test_separate_default_config_instances(self): + pet_api = petstore_api.PetApi() + pet_api2 = petstore_api.PetApi() + self.assertNotEqual(pet_api.api_client.configuration, pet_api2.api_client.configuration) + + pet_api.api_client.configuration.host = 'somehost' + pet_api2.api_client.configuration.host = 'someotherhost' + self.assertNotEqual(pet_api.api_client.configuration.host, pet_api2.api_client.configuration.host) + + @async_test + async def test_async_with_result(self): + await self.pet_api.add_pet(body=self.pet) + + calls = [self.pet_api.get_pet_by_id(self.pet.id), + self.pet_api.get_pet_by_id(self.pet.id)] + + responses, _ = await asyncio.wait(calls) + for response in responses: + self.assertEqual(response.result().id, self.pet.id) + self.assertEqual(len(responses), 2) + + @async_test + async def test_exception(self): + await self.pet_api.add_pet(body=self.pet) + + try: + await self.pet_api.get_pet_by_id("-9999999999999") + except ApiException as e: + exception = e + + self.assertIsInstance(exception, ApiException) + self.assertEqual(exception.status, 404) + + @async_test + async def test_add_pet_and_get_pet_by_id(self): + await self.pet_api.add_pet(body=self.pet) + + fetched = await self.pet_api.get_pet_by_id(pet_id=self.pet.id) + self.assertIsNotNone(fetched) + self.assertEqual(self.pet.id, fetched.id) + self.assertIsNotNone(fetched.category) + self.assertEqual(self.pet.category.name, fetched.category.name) + + @async_test + async def test_add_pet_and_get_pet_by_id_with_http_info(self): + await self.pet_api.add_pet(body=self.pet) + + fetched = await self.pet_api.get_pet_by_id_with_http_info(pet_id=self.pet.id) + self.assertIsNotNone(fetched) + self.assertEqual(self.pet.id, fetched[0].id) + self.assertIsNotNone(fetched[0].category) + self.assertEqual(self.pet.category.name, fetched[0].category.name) + + @async_test + async def test_update_pet(self): + self.pet.name = "hello kity with updated" + await self.pet_api.update_pet(body=self.pet) + + fetched = await self.pet_api.get_pet_by_id(pet_id=self.pet.id) + self.assertIsNotNone(fetched) + self.assertEqual(self.pet.id, fetched.id) + self.assertEqual(self.pet.name, fetched.name) + self.assertIsNotNone(fetched.category) + self.assertEqual(fetched.category.name, self.pet.category.name) + + @async_test + async def test_find_pets_by_status(self): + await self.pet_api.add_pet(body=self.pet) + pets = await self.pet_api.find_pets_by_status(status=[self.pet.status]) + self.assertIn( + self.pet.id, + list(map(lambda x: getattr(x, 'id'), pets)) + ) + + @async_test + async def test_find_pets_by_tags(self): + await self.pet_api.add_pet(body=self.pet) + pets = await self.pet_api.find_pets_by_tags(tags=[self.tag.name]) + self.assertIn( + self.pet.id, + list(map(lambda x: getattr(x, 'id'), pets)) + ) + + @async_test + async def test_update_pet_with_form(self): + await self.pet_api.add_pet(body=self.pet) + + name = "hello kity with form updated" + status = "pending" + await self.pet_api.update_pet_with_form(pet_id=self.pet.id, name=name, status=status) + + fetched = await self.pet_api.get_pet_by_id(pet_id=self.pet.id) + self.assertEqual(self.pet.id, fetched.id) + self.assertEqual(name, fetched.name) + self.assertEqual(status, fetched.status) + + @async_test + async def test_upload_file(self): + # upload file with form parameter + try: + additional_metadata = "special" + await self.pet_api.upload_file( + pet_id=self.pet.id, + additional_metadata=additional_metadata, + file=self.foo + ) + except ApiException as e: + self.fail("upload_file() raised {0} unexpectedly".format(type(e))) + + # upload only file + try: + await self.pet_api.upload_file(pet_id=self.pet.id, file=self.foo) + except ApiException as e: + self.fail("upload_file() raised {0} unexpectedly".format(type(e))) + + @async_test + async def test_delete_pet(self): + await self.pet_api.add_pet(body=self.pet) + await self.pet_api.delete_pet(pet_id=self.pet.id, api_key="special-key") + + try: + await self.pet_api.get_pet_by_id(pet_id=self.pet.id) + raise Exception("expected an error") + except ApiException as e: + self.assertEqual(404, e.status) + + +if __name__ == '__main__': + import logging + logging.basicConfig(level=logging.DEBUG) + unittest.main() diff --git a/samples/client/petstore/python-asyncio/tests/util.py b/samples/client/petstore/python-asyncio/tests/util.py new file mode 100644 index 00000000000..39fba1514b3 --- /dev/null +++ b/samples/client/petstore/python-asyncio/tests/util.py @@ -0,0 +1,11 @@ +# flake8: noqa + +import random + + +def id_gen(bits=32): + """ Returns a n-bit randomly generated int """ + return int(random.getrandbits(bits)) + + + diff --git a/samples/client/petstore/python-asyncio/tox.ini b/samples/client/petstore/python-asyncio/tox.ini index 1cf0829dc93..65f5351ddcc 100644 --- a/samples/client/petstore/python-asyncio/tox.ini +++ b/samples/client/petstore/python-asyncio/tox.ini @@ -1,10 +1,9 @@ [tox] -envlist = py27, py3 +envlist = py3 [testenv] deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt - + commands= - nosetests \ - [] \ No newline at end of file + pytest -v --cov petstore_api diff --git a/samples/client/petstore/python-tornado/tox.ini b/samples/client/petstore/python-tornado/tox.ini index 1cf0829dc93..3d0be613cfc 100644 --- a/samples/client/petstore/python-tornado/tox.ini +++ b/samples/client/petstore/python-tornado/tox.ini @@ -4,7 +4,7 @@ envlist = py27, py3 [testenv] deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt - + commands= nosetests \ - [] \ No newline at end of file + [] diff --git a/samples/client/petstore/python/docs/FakeApi.md b/samples/client/petstore/python/docs/FakeApi.md index 1da84500bfe..b910ba2b464 100644 --- a/samples/client/petstore/python/docs/FakeApi.md +++ b/samples/client/petstore/python/docs/FakeApi.md @@ -273,10 +273,10 @@ configuration.password = 'YOUR_PASSWORD' # create an instance of the API class api_instance = petstore_api.FakeApi(petstore_api.ApiClient(configuration)) -number = 8.14 # float | None +number = 3.4 # float | None double = 1.2 # float | None pattern_without_delimiter = 'pattern_without_delimiter_example' # str | None -byte = 'B' # str | None +byte = 'byte_example' # str | None integer = 56 # int | None (optional) int32 = 56 # int | None (optional) int64 = 789 # int | None (optional) diff --git a/samples/client/petstore/python/tox.ini b/samples/client/petstore/python/tox.ini index 1cf0829dc93..3d0be613cfc 100644 --- a/samples/client/petstore/python/tox.ini +++ b/samples/client/petstore/python/tox.ini @@ -4,7 +4,7 @@ envlist = py27, py3 [testenv] deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt - + commands= nosetests \ - [] \ No newline at end of file + []