Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2@file
3@brief Helpers to submit REST API requests.
4"""
5import os
6import requests
7from requests.auth import HTTPBasicAuth
8from requests.exceptions import HTTPError
9from ..args import zip_dict, bytes2string
10from ..tools import json_loads, json_dumps
13def json_upload_model(name, pyfile, data=None):
14 """
15 Builds a REST request to upload a machine learned models to a REST API defined by
16 @see cl MLStoragePost.
18 :param name: name of the model, should be unique and not already used
19 :param pyfile: python file which computes the prediction,
20 the file must follows the specification defined in
21 :ref:`l-template-ml`
22 :param data: binary file, usually everything the models pickled
23 :return: dictionary ready to be jsonified
24 """
25 if name is None or name == '':
26 raise ValueError("name cannot be empty.")
27 if not os.path.exists(pyfile):
28 raise FileNotFoundError("Unable to find '{0}'.".format(pyfile))
29 if data is None:
30 data = []
31 if not isinstance(data, list):
32 data = [data]
33 for d in data:
34 if not os.path.exists(d):
35 raise FileNotFoundError("Unable to find '{0}'.".format(d))
37 # model file
38 last_file = os.path.split(pyfile)[-1]
39 with open(pyfile, "rb") as f:
40 code = f.read()
42 if code == '':
43 raise ValueError("File '{0}' cannot be empty.".format(pyfile))
44 model_data = {last_file: code}
46 # model data
47 if data:
48 for d in data:
49 ld = os.path.split(d)[-1]
50 with open(d, "rb") as f:
51 content = f.read()
52 model_data[ld] = content
54 # zip data
55 zipped = zip_dict(model_data)
56 request = dict(name=name, cmd='upload',
57 zip=bytes2string(zipped))
58 return request
61def json_predict_model(name, data, format='json'): # pylint: disable=W0622
62 """
63 Builds a REST request to compute the prediction of a machine learning model
64 upload with @see fn json_upload_model.
65 See also @see cl MLStoragePost.
67 :param name: name of the model, should be unique and not already used
68 :param data: any kind of data the model request
69 :param format: ``'json'`` or ``'image'``
70 :return: dictionary ready to be jsonified
71 """
72 if name is None or name == '':
73 raise ValueError("name cannot be empty.")
75 js = json_dumps(data)
76 obs = dict(cmd='predict', name=name, input=js, format=format)
77 return obs
80def submit_rest_request(request, login=None, pwd=None, url='http://127.0.0.1:8081/',
81 timeout=50, fLOG=None):
82 """
83 Submits a request to a REST API defined by
84 @see cl MLStoragePost.
86 :param login: login
87 :param pwd: password
88 :param request: request as a dictionary
89 :param url: url
90 :param timeout: timeout
91 :param fLOG: logging function
92 :return: request results as dictionary
93 """
94 if login:
95 if fLOG:
96 fLOG("[submit_rest_request] submit authentified request as '{0}' to '{1}'".format(
97 login, url))
98 auth = HTTPBasicAuth(login, pwd)
99 jsonified = json_dumps(request)
100 response = requests.post(
101 url, auth=auth, data=jsonified, timeout=timeout)
102 else:
103 if fLOG:
104 fLOG("[submit_rest_request] submit request to '{0}'".format(url))
105 jsonified = json_dumps(request)
106 response = requests.post(url, data=jsonified, timeout=timeout)
108 if response.ok:
109 return json_loads(response.content)
110 else:
111 content = None
112 if hasattr(response, 'content'):
113 content = response.content
114 elif hasattr(response, '_content'):
115 content = response._content # pylint: disable=W0212
116 if content:
117 if isinstance(content, bytes):
118 http_error_msg = content.decode('ascii')
119 try:
120 val = json_loads(http_error_msg)
121 http_error_msg = val
122 except ValueError:
123 pass
124 finally:
125 pass
126 else:
127 http_error_msg = json_loads(content)
128 if isinstance(http_error_msg, dict):
129 http_error_msg = "\n".join(
130 ["{0}: {1}".format(k, v) for k, v in sorted(http_error_msg.items())])
131 else:
132 http_error_msg = "ERROR"
133 raise HTTPError(http_error_msg, response=response)