Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
strategy:
matrix:
operating-system: [ ubuntu-latest ]
python-version: [ '3.6', '3.7', '3.8', '3.9', '3.10' ]
python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11' ]
steps:
- name: Checkout
uses: actions/checkout@v2
Expand All @@ -32,4 +32,4 @@ jobs:
- run: coverage xml

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v2
24 changes: 15 additions & 9 deletions decorest/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def __init__(self, func: typing.Callable[...,
self.rest_client = args[0]

args_dict = dict_from_args(func, *args)
self.req_path = render_path(self.path_template, args_dict)
self.req_path = render_path(self.path_template, args_dict, kwargs)
self.session = None
if '__session' in self.kwargs:
self.session = self.kwargs['__session']
Expand All @@ -85,12 +85,13 @@ def __init__(self, func: typing.Callable[...,
# Merge query parameters from common values for all method
# invocations with arguments provided in the method
# arguments
query_parameters = self._merge_args(args_dict, func, 'query')
form_parameters = self._merge_args(args_dict, func, 'form')
multipart_parameters = self._merge_args(args_dict, func, 'multipart')
query_parameters = self._merge_args(args_dict, kwargs, func, 'query')
form_parameters = self._merge_args(args_dict, kwargs, func, 'form')
multipart_parameters = self._merge_args(args_dict, kwargs, func,
'multipart')
header_parameters = CaseInsensitiveDict(
merge_dicts(get_header_decor(self.rest_client.__class__),
self._merge_args(args_dict, func, 'header')))
self._merge_args(args_dict, kwargs, func, 'header')))

# Merge header parameters with default values, treat header
# decorators with 2 params as default values only if they
Expand Down Expand Up @@ -374,7 +375,7 @@ def _validate_decor(self, decor: str, kwargs: ArgsDict,
raise TypeError("{} value must be an instance of {}".format(
decor, cls.__name__))

def _merge_args(self, args_dict: ArgsDict,
def _merge_args(self, args_dict: ArgsDict, kwargs: ArgsDict,
func: typing.Callable[..., typing.Any], decor: str) \
-> ArgsDict:
"""
Expand All @@ -392,9 +393,14 @@ def _merge_args(self, args_dict: ArgsDict,
parameters = {}
if args_decor:
for arg, value in args_decor.items():
if (isinstance(value, str)) \
and arg in args_dict.keys():
parameters[value] = args_dict[arg]
if (isinstance(value, str)):
if arg in args_dict.keys():
parameters[value] = args_dict[arg]
if arg in kwargs.keys():
parameters[value] = kwargs[arg]
# Delete from kwargs so that we don't
# pass them on to requests/httpx
del kwargs[arg]
else:
parameters[arg] = value
return parameters
19 changes: 14 additions & 5 deletions decorest/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,26 @@ def iteritems_lower(self) \
for (lkey, keyval) in self.__original.items())


def render_path(path: str, args: ArgsDict) -> str:
def render_path(path: str, args: ArgsDict, kwargs: ArgsDict) -> str:
"""Render REST path from *args."""
LOG.debug('RENDERING PATH FROM: %s, %s', path, args)
LOG.debug('RENDERING PATH FROM: %s, %s', path, args, kwargs)
result = path
matches = re.search(r'{([^}.]*)}', result)
used_kwargs = []
while matches:
path_token = matches.group(1)
if path_token not in args:
if path_token in kwargs:
result = re.sub('{%s}' % path_token, str(kwargs[path_token]),
result)
matches = re.search(r'{([^}.]*)}', result)
used_kwargs.append(path_token)
elif path_token in args:
result = re.sub('{%s}' % path_token, str(args[path_token]), result)
matches = re.search(r'{([^}.]*)}', result)
else:
raise ValueError("Missing argument %s in REST call." % path_token)
result = re.sub('{%s}' % path_token, str(args[path_token]), result)
matches = re.search(r'{([^}.]*)}', result)
for used_kwarg in used_kwargs:
del kwargs[used_kwarg]
return result


Expand Down
46 changes: 46 additions & 0 deletions tests/petstore_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,49 @@ def test_user_methods(client):

with pytest.raises(HTTPErrorWrapper) as e:
client.get_user('swagger')


@pytest.mark.parametrize("client", pytest_params)
def test_user_methods_with_kwargs_params(client):

res = client.create_user({
'username': 'swagger',
'firstName': 'Swagger',
'lastName': 'Petstore',
'email': 'swagger@example.com',
'password': 'guess',
'phone': '001-111-CALL-ME',
"userStatus": 0
})

assert res is True

# Mixed usage
res = client.login('swagger', password='petstore')

assert res.decode("utf-8").startswith('logged in user session:')

res = client.get_user(username='swagger')

assert res['phone'] == '001-111-CALL-ME'

client.update_user(
'swagger', {
'username': 'swagger',
'firstName': 'Swagger',
'lastName': 'Petstore',
'email': 'swagger@example.com',
'password': 'guess',
'phone': '001-111-CALL-ME',
"userStatus": 0
})

res = client.get_user(username='swagger')

assert res['email'] == 'swagger@example.com'
assert res['password'] == 'guess'

client.delete_user(username='swagger')

with pytest.raises(HTTPErrorWrapper) as e:
client.get_user(username='swagger')
42 changes: 42 additions & 0 deletions tests/petstore_test_with_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,45 @@ def test_user_methods(client: PetstoreClientWithTyping) -> None:
assert res['password'] == 'guess'

client.delete_user('swagger')


def test_user_methods_with_kwargs_params(
client: PetstoreClientWithTyping) -> None:

res = client.create_user({
'username': 'swagger',
'firstName': 'Swagger',
'lastName': 'Petstore',
'email': 'swagger@example.com',
'password': 'guess',
'phone': '001-111-CALL-ME',
"userStatus": 0
})

assert res is True

res = client.login('swagger', password='petstore')

assert res.decode("utf-8").startswith('logged in user session:')

res = client.get_user(username='swagger')

assert res['phone'] == '001-111-CALL-ME'

client.update_user(
123, {
'username': 'swagger',
'firstName': 'Swagger',
'lastName': 'Petstore',
'email': 'swagger@example.com',
'password': 'guess',
'phone': '001-111-CALL-ME',
"userStatus": 0
})

res = client.get_user(username='swagger')

assert res['email'] == 'swagger@example.com'
assert res['password'] == 'guess'

client.delete_user(username='swagger')