Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XMLAdapterMixin implementation with xmltodict #87

Merged
merged 4 commits into from
May 24, 2017

Conversation

willplaehn
Copy link
Contributor

An XMLAdapterMixin that leverages xmltodict for input and output. #78

It can accept either an XML string, or a dictionary formatted per xmltodict's documentation.

This implementation truncates the output of xmltodict.unparse (removes the XML declaration and newline character) due to incompatibility with the Google Sheets API I was testing with.

Considerations:

  • The declaration tag may need to be configurable by Tapioca wrapper developers.
  • Encodes request data as utf-8.

if 'headers' not in arguments:
# allows user to override for formats like 'application/atom+xml'
arguments['headers'] = {}
arguments['headers']['Content-Type'] = 'application/xml'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be one level of indentation down? I user needs to override it may call super and then alter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, that is a better solution. I've made the change.

Before the fix, with line 135 inside the if statement, headers could be overridden at runtime (if no super calls set the headers) - this was the use case I was going after:

ipython console:

a = api.manage_worksheets(key='my_key').post(
    data=x, headers={'Content-Type': 'application/atom+xml'})

After the fix, it's up to the wrapper developer to ensure that get_request_kwargs correctly defines the headers, and the user of the api wrapper won't have to think about it:

tapioca_gheets.py/GsheetsClientAdapter/get_request_kwargs:

params['headers'] = {'Content-Type': 'application/atom+xml'}

@willplaehn
Copy link
Contributor Author

95f8fbc Adds a new feature for wrapper developers to pass keyword arguments into xmltodict.parse and xmltodict.unparse in XMLAdapterMixin without hardcoding tapioca for individual options coupled to the xmltodict package. This will need some documentation.

XMLAdapterMixin looks for specifically prefixed keywords, removes them from the keyword dictionary that is passed to requests, and passes the suffixes and values directly to xmltodict.parse / unparse as keyword arguments. There are two special prefixes "xmltodict_parse__" and "xmltodict_unparse__". These prefixes could be anything we choose, I added the module name and double underscore for readability and to be explicit.

The wrapper developer can set these parameters inside of their get_request_kwargs like this example that removes the XML declaration tag from POST output:

tapioca_gsheets.py/GsheetsClientAdapter/get_request_kwargs:

kwargs['xmltodict_unparse__full_document'] = False

This approach allows the wrapper developer to control input and output by passing any parameter they need into xmltodict.parse or unparse without limitation. See xmltodict source for options


class XMLAdapterMixin(object):

def __init__(self, serializer_class=None, *args, **kwargs):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be removed safely.

@filipeximenes
Copy link
Contributor

Took me a while but i got the idea of the xmltodict_unparse__ and xmltodict_parse__ params.
I'm a bit afraid that wrappers developers change this arguments resulting in unpredictable behaviours. Always knowing what to expect from wrappers is one of the key features of tapioca.

Changing this arguments would provoke changes in the way the developer interact with the wrapper?
Would some cases break in case we do not allow passing this arguments?

@willplaehn
Copy link
Contributor Author

@filipeximenes - I thought about this and I think we can add a small number of parameters (and only one, if it's acceptable to force utf-8) instead of using this catch-all approach to address your concern.

After reviewing, we probably won't need too much beyond full_document and possibly encoding. As written, this pr converts to utf-8 anyway as a catch-all for Python 2 vs 3 issues.


The story behind xmltodict_unparse__:

Opting to give the developer full control of parse() & unparse() (instead of supplying a boolean mapped to the full_document param of unparse()) was a choice to eliminate a potential blocker for wrapper developers. I'm not sure about the full range of XML requirements, but assume that xmltodict has relevant options.

The original requirement I encountered was to remove the XML declaration (example: <?xml version="1.0" encoding="UTF-8" standalone="no" ?>) of requests to the API I was testing with. Since different APIs will have different requirements, we shouldn't hard-code it.

@filipeximenes
Copy link
Contributor

My only concern here is about simplicity:

  1. The way developers interact with XML wrappers should not differer from one to another. This means that developers should always know what to expect after a XML response is returned.
  2. Final users of wrapper should not have to worry about configuring extra things before making a call. If xmltodict_unparse__ and xmltodict_parse__ will only be used by the developer of the wrapper for configuring specific service behaviour, that's ok. It's only a matter of properly documenting/explaining this parameters and pointing them to xmltodict documentation.

If those 2 things are being respected, I'm ok with it. Does this make sense?

@filipeximenes filipeximenes merged commit 0cc768f into vintasoftware:master May 24, 2017
filipeximenes added a commit that referenced this pull request May 24, 2017
@filipeximenes
Copy link
Contributor

@willplaehn happy PR anniversary! hahaha
Please thank PyCon sprints for this. Very sorry for taking so long.

@willplaehn willplaehn deleted the xmladapter branch July 30, 2017 23:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants