-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Try getting the error message in response_handler #640
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
Closed
Closed
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
9011489
Try getting the error message in response_handler
alexandresobolevski bd801dd
Did not mean to commit this
alexandresobolevski e706aba
Always raise PlotlyRequestError
alexandresobolevski 20d8b3b
Change expected exception
alexandresobolevski c8092f8
Factor out `get_config` and `get_credentials`.
theengineear 2615218
Generalize our `PlotlyRequestError`.
theengineear 3a247c0
Create organized api modules :tada:!
theengineear 3ad476b
Use the new api definitions in all api calls.
theengineear fd38bda
Use `api.v2.plot_schema` to get GRAPH_REFERENCE.
theengineear 62d90d0
:wrench: Allow `proxy_*` in `sign_in`.
theengineear 4cd52bd
Update default schema.
theengineear 5886831
Use requests.compat.json instead of plain json.
theengineear File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
from base64 import b64encode | ||
|
||
from requests.compat import builtin_str, is_py2 | ||
|
||
|
||
def _to_native_string(string, encoding): | ||
if isinstance(string, builtin_str): | ||
return string | ||
if is_py2: | ||
return string.encode(encoding) | ||
return string.decode(encoding) | ||
|
||
|
||
def to_native_utf8_string(string): | ||
return _to_native_string(string, 'utf-8') | ||
|
||
|
||
def to_native_ascii_string(string): | ||
return _to_native_string(string, 'ascii') | ||
|
||
|
||
def basic_auth(username, password): | ||
""" | ||
Creates the basic auth value to be used in an authorization header. | ||
|
||
This is mostly copied from the requests library. | ||
|
||
:param (str) username: A Plotly username. | ||
:param (str) password: The password for the given Plotly username. | ||
:returns: (str) An 'authorization' header for use in a request header. | ||
|
||
""" | ||
if isinstance(username, str): | ||
username = username.encode('latin1') | ||
|
||
if isinstance(password, str): | ||
password = password.encode('latin1') | ||
|
||
return 'Basic ' + to_native_ascii_string( | ||
b64encode(b':'.join((username, password))).strip() | ||
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from __future__ import absolute_import | ||
|
||
from plotly.api.v1.clientresp import clientresp |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
"""Interface to deprecated /clientresp API. Subject to deletion.""" | ||
from __future__ import absolute_import | ||
|
||
import warnings | ||
|
||
from requests.compat import json as _json | ||
|
||
from plotly import config, utils, version | ||
from plotly.api.v1.utils import request | ||
|
||
|
||
def clientresp(data, **kwargs): | ||
""" | ||
Deprecated endpoint, still used because it can parse data out of a plot. | ||
|
||
When we get around to forcing users to create grids and then create plots, | ||
we can finally get rid of this. | ||
|
||
:param (list) data: The data array from a figure. | ||
|
||
""" | ||
creds = config.get_credentials() | ||
cfg = config.get_config() | ||
|
||
dumps_kwargs = {'sort_keys': True, 'cls': utils.PlotlyJSONEncoder} | ||
|
||
payload = { | ||
'platform': 'python', 'version': version.__version__, | ||
'args': _json.dumps(data, **dumps_kwargs), | ||
'un': creds['username'], 'key': creds['api_key'], 'origin': 'plot', | ||
'kwargs': _json.dumps(kwargs, **dumps_kwargs) | ||
} | ||
|
||
url = '{plotly_domain}/clientresp'.format(**cfg) | ||
response = request('post', url, data=payload) | ||
|
||
# Old functionality, just keeping it around. | ||
parsed_content = response.json() | ||
if parsed_content.get('warning'): | ||
warnings.warn(parsed_content['warning']) | ||
if parsed_content.get('message'): | ||
print(parsed_content['message']) | ||
|
||
return response |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
from __future__ import absolute_import | ||
|
||
import requests | ||
from requests.exceptions import RequestException | ||
|
||
from plotly import config, exceptions | ||
from plotly.api.utils import basic_auth | ||
|
||
|
||
def validate_response(response): | ||
""" | ||
Raise a helpful PlotlyRequestError for failed requests. | ||
|
||
:param (requests.Response) response: A Response object from an api request. | ||
:raises: (PlotlyRequestError) If the request failed for any reason. | ||
:returns: (None) | ||
|
||
""" | ||
content = response.content | ||
status_code = response.status_code | ||
try: | ||
parsed_content = response.json() | ||
except ValueError: | ||
message = content if content else 'No Content' | ||
raise exceptions.PlotlyRequestError(message, status_code, content) | ||
|
||
message = '' | ||
if isinstance(parsed_content, dict): | ||
error = parsed_content.get('error') | ||
if error: | ||
message = error | ||
else: | ||
if response.ok: | ||
return | ||
if not message: | ||
message = content if content else 'No Content' | ||
|
||
raise exceptions.PlotlyRequestError(message, status_code, content) | ||
|
||
|
||
def get_headers(): | ||
""" | ||
Using session credentials/config, get headers for a v1 API request. | ||
|
||
Users may have their own proxy layer and so we free up the `authorization` | ||
header for this purpose (instead adding the user authorization in a new | ||
`plotly-authorization` header). See pull #239. | ||
|
||
:returns: (dict) Headers to add to a requests.request call. | ||
|
||
""" | ||
headers = {} | ||
creds = config.get_credentials() | ||
proxy_auth = basic_auth(creds['proxy_username'], creds['proxy_password']) | ||
|
||
if config.get_config()['plotly_proxy_authorization']: | ||
headers['authorization'] = proxy_auth | ||
|
||
return headers | ||
|
||
|
||
def request(method, url, **kwargs): | ||
""" | ||
Central place to make any v1 api request. | ||
|
||
:param (str) method: The request method ('get', 'put', 'delete', ...). | ||
:param (str) url: The full api url to make the request to. | ||
:param kwargs: These are passed along to requests. | ||
:return: (requests.Response) The response directly from requests. | ||
|
||
""" | ||
if kwargs.get('json', None) is not None: | ||
# See plotly.api.v2.utils.request for examples on how to do this. | ||
raise exceptions.PlotlyError('V1 API does not handle arbitrary json.') | ||
kwargs['headers'] = dict(kwargs.get('headers', {}), **get_headers()) | ||
kwargs['verify'] = config.get_config()['plotly_ssl_verification'] | ||
try: | ||
response = requests.request(method, url, **kwargs) | ||
except RequestException as e: | ||
# The message can be an exception. E.g., MaxRetryError. | ||
message = str(getattr(e, 'message', 'No message')) | ||
response = getattr(e, 'response', None) | ||
status_code = response.status_code if response else None | ||
content = response.content if response else 'No content' | ||
raise exceptions.PlotlyRequestError(message, status_code, content) | ||
validate_response(response) | ||
return response |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from __future__ import absolute_import | ||
|
||
from plotly.api.v2 import files, folders, grids, images, plot_schema, plots |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
"""Interface to Plotly's /v2/files endpoints.""" | ||
from __future__ import absolute_import | ||
|
||
from plotly.api.v2.utils import build_url, make_params, request | ||
|
||
RESOURCE = 'files' | ||
|
||
|
||
def retrieve(fid, share_key=None): | ||
""" | ||
Retrieve a general file from Plotly. | ||
|
||
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`. | ||
:param (str) share_key: The secret key granting 'read' access if private. | ||
:returns: (requests.Response) Returns response directly from requests. | ||
|
||
""" | ||
url = build_url(RESOURCE, id=fid) | ||
params = make_params(share_key=share_key) | ||
return request('get', url, params=params) | ||
|
||
|
||
def update(fid, body): | ||
""" | ||
Update a general file from Plotly. | ||
|
||
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`. | ||
:param (dict) body: A mapping of body param names to values. | ||
:returns: (requests.Response) Returns response directly from requests. | ||
|
||
""" | ||
url = build_url(RESOURCE, id=fid) | ||
return request('put', url, json=body) | ||
|
||
|
||
def trash(fid): | ||
""" | ||
Soft-delete a general file from Plotly. (Can be undone with 'restore'). | ||
|
||
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`. | ||
:returns: (requests.Response) Returns response directly from requests. | ||
|
||
""" | ||
url = build_url(RESOURCE, id=fid, route='trash') | ||
return request('post', url) | ||
|
||
|
||
def restore(fid): | ||
""" | ||
Restore a trashed, general file from Plotly. See 'trash'. | ||
|
||
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`. | ||
:returns: (requests.Response) Returns response directly from requests. | ||
|
||
""" | ||
url = build_url(RESOURCE, id=fid, route='restore') | ||
return request('post', url) | ||
|
||
|
||
def permanent_delete(fid): | ||
""" | ||
Permanently delete a trashed, general file from Plotly. See 'trash'. | ||
|
||
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`. | ||
:returns: (requests.Response) Returns response directly from requests. | ||
|
||
""" | ||
url = build_url(RESOURCE, id=fid, route='permanent_delete') | ||
return request('delete', url) | ||
|
||
|
||
def lookup(path, parent=None, user=None, exists=None): | ||
""" | ||
Retrieve a general file from Plotly without needing a fid. | ||
|
||
:param (str) path: The '/'-delimited path specifying the file location. | ||
:param (int) parent: Parent id, an integer, which the path is relative to. | ||
:param (str) user: The username to target files for. Defaults to requestor. | ||
:param (bool) exists: If True, don't return the full file, just a flag. | ||
:returns: (requests.Response) Returns response directly from requests. | ||
|
||
""" | ||
url = build_url(RESOURCE, route='lookup') | ||
params = make_params(path=path, parent=parent, user=user, exists=exists) | ||
return request('get', url, params=params) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice, this all looks really good |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
"""Interface to Plotly's /v2/folders endpoints.""" | ||
from __future__ import absolute_import | ||
|
||
from plotly.api.v2.utils import build_url, make_params, request | ||
|
||
RESOURCE = 'folders' | ||
|
||
|
||
def create(body): | ||
""" | ||
Create a new folder. | ||
|
||
:param (dict) body: A mapping of body param names to values. | ||
:returns: (requests.Response) Returns response directly from requests. | ||
|
||
""" | ||
url = build_url(RESOURCE) | ||
return request('post', url, json=body) | ||
|
||
|
||
def retrieve(fid, share_key=None): | ||
""" | ||
Retrieve a folder from Plotly. | ||
|
||
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`. | ||
:param (str) share_key: The secret key granting 'read' access if private. | ||
:returns: (requests.Response) Returns response directly from requests. | ||
|
||
""" | ||
url = build_url(RESOURCE, id=fid) | ||
params = make_params(share_key=share_key) | ||
return request('get', url, params=params) | ||
|
||
|
||
def update(fid, body): | ||
""" | ||
Update a folder from Plotly. | ||
|
||
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`. | ||
:param (dict) body: A mapping of body param names to values. | ||
:returns: (requests.Response) Returns response directly from requests. | ||
|
||
""" | ||
url = build_url(RESOURCE, id=fid) | ||
return request('put', url, json=body) | ||
|
||
|
||
def trash(fid): | ||
""" | ||
Soft-delete a folder from Plotly. (Can be undone with 'restore'). | ||
|
||
This action is recursively done on files inside the folder. | ||
|
||
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`. | ||
:returns: (requests.Response) Returns response directly from requests. | ||
|
||
""" | ||
url = build_url(RESOURCE, id=fid, route='trash') | ||
return request('post', url) | ||
|
||
|
||
def restore(fid): | ||
""" | ||
Restore a trashed folder from Plotly. See 'trash'. | ||
|
||
This action is recursively done on files inside the folder. | ||
|
||
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`. | ||
:returns: (requests.Response) Returns response directly from requests. | ||
|
||
""" | ||
url = build_url(RESOURCE, id=fid, route='restore') | ||
return request('post', url) | ||
|
||
|
||
def permanent_delete(fid): | ||
""" | ||
Permanently delete a trashed folder file from Plotly. See 'trash'. | ||
|
||
This action is recursively done on files inside the folder. | ||
|
||
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`. | ||
:returns: (requests.Response) Returns response directly from requests. | ||
|
||
""" | ||
url = build_url(RESOURCE, id=fid, route='permanent_delete') | ||
return request('delete', url) | ||
|
||
|
||
def lookup(path, parent=None, user=None, exists=None): | ||
""" | ||
Retrieve a folder file from Plotly without needing a fid. | ||
|
||
:param (str) path: The '/'-delimited path specifying the file location. | ||
:param (int) parent: Parent id, an integer, which the path is relative to. | ||
:param (str) user: The username to target files for. Defaults to requestor. | ||
:param (bool) exists: If True, don't return the full file, just a flag. | ||
:returns: (requests.Response) Returns response directly from requests. | ||
|
||
""" | ||
url = build_url(RESOURCE, route='lookup') | ||
params = make_params(path=path, parent=parent, user=user, exists=exists) | ||
return request('get', url, params=params) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why
latin1
and notutf-8
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'd have to ask Kenneth Reitz. It's just something I pulled from
requests.HTTPBasicAuth
. My guess is that it doesn't matter, but I'd rather just leave it as was written there.