Skip to content

Commit

Permalink
Merge pull request #93 from timothycrosley/feature/response-aware-tra…
Browse files Browse the repository at this point in the history
…nsformers

Feature/response aware transformers
  • Loading branch information
timothycrosley committed Nov 4, 2015
2 parents 5c52ca7 + 318f203 commit 4755156
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 6 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Changelog
### 1.6.5
- Fixed a small spelling error on the `smart_boolean` type

### 1.6.7
### 1.7.0
- Auto supply `response` and `request` to output transformations and formats when they are taken as arguments
- Improved the `smart_boolean` type even further, to allow 0, 1, t, f strings as input
- Enabled normal boolean type to easily work with cli apps, by having it interact via 'store_true'
39 changes: 34 additions & 5 deletions hug/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
from hug.format import underscore
from hug.run import INTRO, server

AUTO_INCLUDE = {'request', 'response'}


class HugAPI(object):
'''Stores the information necessary to expose API calls within this module externally'''
Expand Down Expand Up @@ -242,6 +244,9 @@ def _create_interface(module, api_function, parameters=None, defaults={}, output

takes_kwargs = bool(api_function.__code__.co_flags & 0x08)
function_output = output or module.__hug__.output_format
function_output_args = (AUTO_INCLUDE.intersection(function_output.__code__.co_varnames) if
hasattr(function_output, '__code__') else ())
default_kwargs = {}
directives = module.__hug__.directives()
use_directives = set(accepted_parameters).intersection(directives.keys())
if transform is None and not isinstance(api_function.__annotations__.get('return', None), (str, type(None))):
Expand All @@ -253,6 +258,9 @@ def _create_interface(module, api_function, parameters=None, defaults={}, output
else:
output_type = transform or api_function.__annotations__.get('return', None)

if transform and hasattr(transform, '__code__'):
transform_args = AUTO_INCLUDE.intersection(transform.__code__.co_varnames)

is_method = False
if 'method' in api_function.__class__.__name__:
is_method = True
Expand All @@ -278,13 +286,25 @@ def interface(request, response, api_version=None, **kwargs):
if set_status:
response.status = set_status

if function_output_args:
function_output_kwargs = {}
if 'response' in function_output_args:
function_output_kwargs['response'] = response
if 'request' in function_output_args:
function_output_kwargs['request'] = request
else:
function_output_kwargs = default_kwargs

api_version = int(api_version) if api_version is not None else api_version
response.content_type = function_output.content_type
if callable(function_output.content_type):
response.content_type = function_output.content_type(request=request, response=response)
else:
response.content_type = function_output.content_type
for requirement in requires:
conclusion = requirement(response=response, request=request, module=module, api_version=api_version)
if conclusion is not True:
if conclusion:
response.data = function_output(conclusion)
response.data = function_output(conclusion, **function_output_kwargs)
return

input_parameters = kwargs
Expand Down Expand Up @@ -321,7 +341,7 @@ def interface(request, response, api_version=None, **kwargs):
if not require in input_parameters:
errors[require] = "Required parameter not supplied"
if errors:
response.data = function_output({"errors": errors})
response.data = function_output({"errors": errors}, **function_output_kwargs)
response.status = HTTP_BAD_REQUEST
return

Expand All @@ -330,9 +350,18 @@ def interface(request, response, api_version=None, **kwargs):

to_return = api_function(**input_parameters)
if transform and not (isinstance(transform, type) and isinstance(to_return, transform)):
to_return = transform(to_return)
if transform_args:
extra_kwargs = {}
if 'response' in transform_args:
extra_kwargs['response'] = response
if 'request' in transform_args:
extra_kwargs['request'] = request
to_return = transform(to_return, **extra_kwargs)
else:
to_return = transform(to_return)

to_return = function_output(to_return) if not hasattr(to_return, 'read') else to_return
to_return = (function_output(to_return, **function_output_kwargs) if
not hasattr(to_return, 'read') else to_return)
if hasattr(to_return, 'read'):
response.stream = to_return
if hasattr(to_return, 'name') and os.path.isfile(to_return.name):
Expand Down
27 changes: 27 additions & 0 deletions tests/test_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,16 @@ def hello() -> lambda data: "Hello {0}!".format(data):
assert hug.test.get(api, 'hello').data == "world"
assert hello() == 'world'

def transform_with_request_data(data, request, response):
return (data, request and True, response and True)

@hug.get(transform=transform_with_request_data)
def hello():
return "world"

response = hug.test.get(api, 'hello')
assert response.data == ['world', True, True]


def test_marshmallow_support():
'''Ensure that you can use Marshmallow style objects to control input and output validation and transformation'''
Expand Down Expand Up @@ -412,6 +422,23 @@ def test():
assert 'hug' in hug.test.get(api, 'test').data


def test_smart_outputter():
'''Test to ensure that the output formatter can accept request and response arguments'''
def smart_output_type(response, request):
if response and request:
return 'application/json'

@hug.format.content_type(smart_output_type)
def output_formatter(data, request, response):
return hug.output_format.json((data, request and True, response and True))

@hug.get(output=output_formatter)
def test():
return True

assert hug.test.get(api, 'test').data == [True, True, True]


def test_output_format():
'''Test to ensure it's possible to quickly change the default hug output format'''
old_formatter = api.__hug__.output_format
Expand Down

0 comments on commit 4755156

Please sign in to comment.