Skip to content

Commit 080fd62

Browse files
committed
Create organized api modules 🎉!
This was driving me nuts. We basically manually handle creating and validating *each* api response inside each calling function. Even worse, we *sometimes* raise a `PlotlyRequestError` and *sometimes* just bubble up the `requests.exceptions.HTTPError` ;__;. This does the following: * Define an `api.v1` module that only includes `clientresp` (the only old api method we still *need* to cling to) * Define an `api.v2` module that includes all the new functionality of our v2 api. * Both `v1` and `v2` raise the same `PlotlyRequestError`, so that users only need to catch *one* exception class in scripts.
1 parent 2615218 commit 080fd62

File tree

11 files changed

+714
-0
lines changed

11 files changed

+714
-0
lines changed

plotly/api/__init__.py

Whitespace-only changes.

plotly/api/v1/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from __future__ import absolute_import
2+
3+
from plotly.api.v1.clientresp import clientresp

plotly/api/v1/clientresp.py

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""Interface to deprecated /clientresp API. Subject to deletion."""
2+
from __future__ import absolute_import
3+
4+
import json
5+
import warnings
6+
7+
from plotly import config, utils, version
8+
from plotly.api.v1.utils import request
9+
10+
11+
def clientresp(data, **kwargs):
12+
"""
13+
Deprecated endpoint, still used because it can parse data out of a plot.
14+
15+
When we get around to forcing users to create grids and then create plots,
16+
we can finally get rid of this.
17+
18+
:param (list) data: The data array from a figure.
19+
20+
"""
21+
creds = config.get_credentials()
22+
cfg = config.get_config()
23+
24+
payload = {
25+
'platform': 'python', 'version': version.__version__,
26+
'args': json.dumps(data, cls=utils.PlotlyJSONEncoder),
27+
'un': creds['username'], 'key': creds['api_key'], 'origin': 'plot',
28+
'kwargs': json.dumps(kwargs, cls=utils.PlotlyJSONEncoder)
29+
}
30+
31+
url = '{plotly_domain}/clientresp'.format(**cfg)
32+
response = request('post', url, data=payload,
33+
verify=cfg['plotly_ssl_verification'])
34+
35+
# Old functionality, just keeping it around.
36+
parsed_content = response.json()
37+
if parsed_content.get('warning'):
38+
warnings.warn(parsed_content['warning'])
39+
if parsed_content.get('message'):
40+
print(parsed_content['message'])
41+
42+
return response

plotly/api/v1/utils.py

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from __future__ import absolute_import
2+
3+
import requests
4+
5+
from plotly import exceptions
6+
7+
8+
def validate_response(response):
9+
"""
10+
Raise a helpful PlotlyRequestError for failed requests.
11+
12+
:param (requests.Response) response: A Response object from an api request.
13+
:raises: (PlotlyRequestError) If the request failed for any reason.
14+
:returns: (None)
15+
16+
"""
17+
if response.ok:
18+
return
19+
20+
content = response.content
21+
status_code = response.status_code
22+
try:
23+
parsed_content = response.json()
24+
except ValueError:
25+
message = content if content else 'No Content'
26+
raise exceptions.PlotlyRequestError(message, status_code, content)
27+
28+
message = ''
29+
if isinstance(parsed_content, dict):
30+
error = parsed_content.get('error')
31+
if error:
32+
message = error
33+
if not message:
34+
message = content if content else 'No Content'
35+
36+
raise exceptions.PlotlyRequestError(message, status_code, content)
37+
38+
39+
def request(method, url, **kwargs):
40+
"""
41+
Central place to make any api request.
42+
43+
:param (str) method: The request method ('get', 'put', 'delete', ...).
44+
:param (str) url: The full api url to make the request to.
45+
:param kwargs: These are passed along to requests.
46+
:return: (requests.Response) The response directly from requests.
47+
48+
"""
49+
response = requests.request(method, url, **kwargs)
50+
validate_response(response)
51+
return response

plotly/api/v2/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from __future__ import absolute_import
2+
3+
from plotly.api.v2 import files, folders, grids, images, plots

plotly/api/v2/files.py

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"""Interface to Plotly's /v2/files endpoints."""
2+
from __future__ import absolute_import
3+
4+
from plotly.api.v2.utils import build_url, make_params, request
5+
6+
RESOURCE = 'files'
7+
8+
9+
def retrieve(fid, share_key=None):
10+
"""
11+
Retrieve a general file from Plotly.
12+
13+
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`.
14+
:param (str) share_key: The secret key granting 'read' access if private.
15+
:returns: (requests.Response) Returns response directly from requests.
16+
17+
"""
18+
url = build_url(RESOURCE, id=fid)
19+
params = make_params(share_key=share_key)
20+
return request('get', url, params=params)
21+
22+
23+
def update(fid, body):
24+
"""
25+
Update a general file from Plotly.
26+
27+
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`.
28+
:param (dict) body: A mapping of body param names to values.
29+
:returns: (requests.Response) Returns response directly from requests.
30+
31+
"""
32+
url = build_url(RESOURCE, id=fid)
33+
return request('put', url, json=body)
34+
35+
36+
def trash(fid):
37+
"""
38+
Soft-delete a general file from Plotly. (Can be undone with 'restore').
39+
40+
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`.
41+
:returns: (requests.Response) Returns response directly from requests.
42+
43+
"""
44+
url = build_url(RESOURCE, id=fid, route='trash')
45+
return request('post', url)
46+
47+
48+
def restore(fid):
49+
"""
50+
Restore a trashed, general file from Plotly. See 'trash'.
51+
52+
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`.
53+
:returns: (requests.Response) Returns response directly from requests.
54+
55+
"""
56+
url = build_url(RESOURCE, id=fid, route='restore')
57+
return request('post', url)
58+
59+
60+
def permanent_delete(fid):
61+
"""
62+
Permanently delete a trashed, general file from Plotly. See 'trash'.
63+
64+
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`.
65+
:returns: (requests.Response) Returns response directly from requests.
66+
67+
"""
68+
url = build_url(RESOURCE, id=fid, route='permanent_delete')
69+
return request('delete', url)
70+
71+
72+
def lookup(path, parent=None, user=None, exists=None):
73+
"""
74+
Retrieve a general file from Plotly without needing a fid.
75+
76+
:param (str) path: The '/'-delimited path specifying the file location.
77+
:param (int) parent: Parent id, an integer, which the path is relative to.
78+
:param (str) user: The username to target files for. Defaults to requestor.
79+
:param (bool) exists: If True, don't return the full file, just a flag.
80+
:returns: (requests.Response) Returns response directly from requests.
81+
82+
"""
83+
url = build_url(RESOURCE, route='lookup')
84+
params = make_params(path=path, parent=parent, user=user, exists=exists)
85+
return request('get', url, params=params)

plotly/api/v2/folders.py

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
"""Interface to Plotly's /v2/folders endpoints."""
2+
from __future__ import absolute_import
3+
4+
from plotly.api.v2.utils import build_url, make_params, request
5+
6+
RESOURCE = 'folders'
7+
8+
9+
def create(body):
10+
"""
11+
Create a new folder.
12+
13+
:param (dict) body: A mapping of body param names to values.
14+
:returns: (requests.Response) Returns response directly from requests.
15+
16+
"""
17+
url = build_url(RESOURCE)
18+
return request('post', url, json=body)
19+
20+
21+
def retrieve(fid, share_key=None):
22+
"""
23+
Retrieve a folder from Plotly.
24+
25+
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`.
26+
:param (str) share_key: The secret key granting 'read' access if private.
27+
:returns: (requests.Response) Returns response directly from requests.
28+
29+
"""
30+
url = build_url(RESOURCE, id=fid)
31+
params = make_params(share_key=share_key)
32+
return request('get', url, params=params)
33+
34+
35+
def update(fid, body):
36+
"""
37+
Update a folder from Plotly.
38+
39+
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`.
40+
:param (dict) body: A mapping of body param names to values.
41+
:returns: (requests.Response) Returns response directly from requests.
42+
43+
"""
44+
url = build_url(RESOURCE, id=fid)
45+
return request('put', url, json=body)
46+
47+
48+
def trash(fid):
49+
"""
50+
Soft-delete a folder from Plotly. (Can be undone with 'restore').
51+
52+
This action is recursively done on files inside the folder.
53+
54+
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`.
55+
:returns: (requests.Response) Returns response directly from requests.
56+
57+
"""
58+
url = build_url(RESOURCE, id=fid, route='trash')
59+
return request('post', url)
60+
61+
62+
def restore(fid):
63+
"""
64+
Restore a trashed folder from Plotly. See 'trash'.
65+
66+
This action is recursively done on files inside the folder.
67+
68+
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`.
69+
:returns: (requests.Response) Returns response directly from requests.
70+
71+
"""
72+
url = build_url(RESOURCE, id=fid, route='restore')
73+
return request('post', url)
74+
75+
76+
def permanent_delete(fid):
77+
"""
78+
Permanently delete a trashed folder file from Plotly. See 'trash'.
79+
80+
This action is recursively done on files inside the folder.
81+
82+
:param (str) fid: The `{username}:{idlocal}` identifier. E.g. `foo:88`.
83+
:returns: (requests.Response) Returns response directly from requests.
84+
85+
"""
86+
url = build_url(RESOURCE, id=fid, route='permanent_delete')
87+
return request('delete', url)

0 commit comments

Comments
 (0)