Skip to content

Commit

Permalink
Omit Header with None value from request (#169)
Browse files Browse the repository at this point in the history
Fixes #167.
  • Loading branch information
prkumar authored Aug 10, 2019
1 parent 0a68434 commit 66e3a5a
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 16 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file.
The format is based on `Keep a Changelog`_, and this project adheres to the
`Semantic Versioning`_ scheme.

# Unreleased_
=============
Changed
-------
- Omit ``Header`` argument from request when its value is ``None``.
(`#167`_, `#169`_)

0.9.0_ - 2019-06-05
===================
Added
Expand All @@ -29,7 +36,7 @@ Fixed

Changed
-------
- Renamed ``uplink.retry.stop.DISABLE`` to ``uplink.retry.stop.NEVER``
- Rename ``uplink.retry.stop.DISABLE`` to ``uplink.retry.stop.NEVER``

0.8.0_ - 2019-02-16
===================
Expand Down Expand Up @@ -294,6 +301,7 @@ Added
.. _`Semantic Versioning`: https://packaging.python.org/tutorials/distributing-packages/#semantic-versioning-preferred

.. Releases
.. _Unreleased: https://github.com/prkumar/uplink/compare/v0.9.0...HEAD
.. _0.9.0: https://github.com/prkumar/uplink/compare/v0.8.0...v0.9.0
.. _0.8.0: https://github.com/prkumar/uplink/compare/v0.7.0...v0.8.0
.. _0.7.0: https://github.com/prkumar/uplink/compare/v0.6.1...v0.7.0
Expand Down
4 changes: 4 additions & 0 deletions tests/unit/test_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@ def test_modify_request(self, request_builder):
arguments.Header("hello").modify_request(request_builder, "world")
assert request_builder.info["headers"] == {"hello": "world"}

def test_skip_none(self, request_builder):
arguments.Header("hello").modify_request(request_builder, None)
assert request_builder.info["headers"] == {}


class TestHeaderMap(ArgumentTestCase, FuncDecoratorTestCase):
type_cls = arguments.HeaderMap
Expand Down
49 changes: 34 additions & 15 deletions uplink/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,23 @@ def converter_key(self): # pragma: no cover
raise NotImplementedError


class EncodeNoneMixin(object):
#: Identifies how a `None` value should be encoded in the request.
_encode_none = None # type: str

def _modify_request(self, request_builder, value): # pragma: no cover
raise NotImplementedError

def modify_request(self, request_builder, value):
if value is None:
if self._encode_none is None:
# ignore value if it is None and shouldn't be encoded
return
else:
value = self._encode_none
super(EncodeNoneMixin, self).modify_request(request_builder, value)


class FuncDecoratorMixin(object):
@classmethod
def _is_static_call(cls, *args_, **kwargs):
Expand Down Expand Up @@ -304,7 +321,7 @@ def _modify_request(self, request_builder, value):
request_builder.url.set_variable({self.name: value})


class Query(FuncDecoratorMixin, NamedArgument):
class Query(FuncDecoratorMixin, EncodeNoneMixin, NamedArgument):
"""
Set a dynamic query parameter.
Expand Down Expand Up @@ -392,14 +409,6 @@ def _modify_request(self, request_builder, value):
request_builder.info, {self.name: value}, self._encoded
)

def modify_request(self, request_builder, value):
if value is None:
# ignore value if it is None and shouldn't be encoded
if self._encode_none is not None:
self._modify_request(request_builder, self._encode_none)
else:
super(Query, self).modify_request(request_builder, value)


class QueryMap(FuncDecoratorMixin, TypedArgument):
"""
Expand Down Expand Up @@ -438,20 +447,30 @@ def _modify_request(self, request_builder, value):
Query.update_params(request_builder.info, value, self._encoded)


class Header(FuncDecoratorMixin, NamedArgument):
class Header(FuncDecoratorMixin, EncodeNoneMixin, NamedArgument):
"""
Pass a header as a method argument at runtime.
While :py:class:`uplink.headers` attaches static headers
that define all requests sent from a consumer method, this
class turns a method argument into a dynamic header value.
While :py:class:`uplink.headers` attaches request headers values
that are static across all requests sent from the decorated
consumer method, this annotation turns a method argument into a
dynamic request header.
Example:
.. code-block:: python
@get("/user")
def (self, session_id: Header("Authorization")):
\"""Get the authenticated user\"""
def me(self, session_id: Header("Authorization")):
\"""Get the authenticated user.\"""
To define an optional header, use the default value of `None`:
@get("/repositories")
def fetch_repos(self, auth: Header("Authorization") = None):
\"""List all public repositories.\"""
When the argument is not used, the header will not be added
to the request.
"""

@property
Expand Down

0 comments on commit 66e3a5a

Please sign in to comment.