Skip to content

Commit 5da25ac

Browse files
zeryxdanielfrg
andauthored
Exception enhancements (#90)
* added the dummy flag to swap over to localhost * Moving from fork * added fastAPI and uvicorn support for fastAPI service; added basic algorithm_HTTP response failure handling (for 5xx errors). * make sure that fastAPI is actually installed during testing * migrated all testing logic out of the Client * Moving from fork * added fastAPI and uvicorn support for fastAPI service; added basic algorithm_HTTP response failure handling (for 5xx errors). * make sure that fastAPI is actually installed during testing * cleaned up dependencies * removed dummy from client in init * remove all changes made by Daniels PR * more adaptions to separate Daniels PR * cleaning up blank space and correcting changes * added blankspaces to simplify changelog * fixed typo in exception test * ensured we were importing the right app into our test case * pytest is run from root, not relative test dir * added a newline Co-authored-by: Daniel Rodriguez <daniel@danielfrg.com>
1 parent 041fa5b commit 5da25ac

File tree

7 files changed

+66
-14
lines changed

7 files changed

+66
-14
lines changed

Algorithmia/CLI.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def auth(self, apikey, apiaddress, cacert="", profile="default"):
2222
config['profiles'][profile]['api_key'] = apikey
2323
config['profiles'][profile]['api_server'] = apiaddress
2424
config['profiles'][profile]['ca_cert'] = cacert
25-
25+
2626
else:
2727
config['profiles'][profile] = {'api_key':apikey,'api_server':apiaddress,'ca_cert':cacert}
2828
else:
@@ -299,7 +299,7 @@ def getconfigfile(self):
299299
#%LOCALAPPDATA%\Algorithmia\config
300300
#create the api key file if it does not exist
301301
keyPath = os.path.expandvars("%LOCALAPPDATA%\\Algorithmia\\")
302-
302+
303303
keyFile = "config"
304304

305305
if(not os.path.exists(keyPath)):

Algorithmia/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def main():
124124
if len(APIkey) == 28 and APIkey.startswith("sim"):
125125
if APIaddress == "" or not APIaddress.startswith("https://api."):
126126
APIaddress = "https://api.algorithmia.com"
127-
127+
128128
CLI().auth(apikey=APIkey, apiaddress=APIaddress, cacert=CACert, profile=args.profile)
129129
else:
130130
print("invalid api key")

Algorithmia/algo_response.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,21 @@ def __repr__(self):
1818
return self.__unicode__().encode('utf-8')
1919

2020
@staticmethod
21-
def create_algo_response(responseJson):
22-
# Parse response JSON
23-
if 'error' in responseJson:
21+
def create_algo_response(response):
22+
# Parse response JSON, if it's indeed JSON
23+
if 'error' in response or 'metadata' not in response:
2424
# Failure
25-
raise raiseAlgoApiError(responseJson)
25+
raise raiseAlgoApiError(response)
2626
else:
27-
metadata = Metadata(responseJson['metadata'])
27+
metadata = Metadata(response['metadata'])
2828
# Success, check content_type
29-
if responseJson['metadata']['content_type'] == 'binary':
29+
if response['metadata']['content_type'] == 'binary':
3030
# Decode Base64 encoded binary file
31-
return AlgoResponse(base64.b64decode(responseJson['result']), metadata)
32-
elif responseJson['metadata']['content_type'] == 'void':
31+
return AlgoResponse(base64.b64decode(response['result']), metadata)
32+
elif response['metadata']['content_type'] == 'void':
3333
return AlgoResponse(None, metadata)
3434
else:
35-
return AlgoResponse(responseJson['result'], metadata)
35+
return AlgoResponse(response['result'], metadata)
3636

3737
class Metadata(object):
3838
def __init__(self, metadata):

Algorithmia/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def __init__(self, apiKey = None, apiAddress = None, caCert = None):
4141
self.catCerts(caCert)
4242
self.requestSession.verify = self.ca_cert
4343
elif caCert is not None and 'REQUESTS_CA_BUNDLE' in os.environ:
44-
#if both are available, use the one supplied in the constructor. I assume that a user supplying a cert in initialization wants to use that one.
44+
#if both are available, use the one supplied in the constructor. I assume that a user supplying a cert in initialization wants to use that one.
4545
self.catCerts(caCert)
4646
self.requestSession.verify = self.ca_cert
4747

@@ -141,7 +141,7 @@ def postJsonHelper(self, url, input_object, parse_response_as_json=True, **query
141141

142142
response = self.requestSession.post(self.apiAddress + url, data=input_json, headers=headers, params=query_parameters)
143143

144-
if parse_response_as_json:
144+
if parse_response_as_json and response.status_code == 200:
145145
return response.json()
146146
return response
147147

Test/algo_failure_test.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import sys
2+
from multiprocessing import Process
3+
# look in ../ BEFORE trying to import Algorithmia. If you append to the
4+
# you will load the version installed on the computer.
5+
sys.path = ['../'] + sys.path
6+
7+
import unittest
8+
import Algorithmia
9+
import uvicorn
10+
import time
11+
from requests import Response
12+
from Test.api import app
13+
14+
def start_webserver():
15+
uvicorn.run(app, host="127.0.0.1", port=8080, log_level="debug")
16+
17+
class AlgoTest(unittest.TestCase):
18+
error_500 = Response()
19+
error_500.status_code = 500
20+
21+
def setUp(self):
22+
self.client = Algorithmia.client(api_address="http://localhost:8080")
23+
self.uvi_p = Process(target=start_webserver)
24+
self.uvi_p.start()
25+
time.sleep(1)
26+
def tearDown(self):
27+
self.uvi_p.terminate()
28+
def test_throw_500_error_HTTP_response_on_algo_request(self):
29+
try:
30+
result = self.client.algo('util/Echo').pipe(bytearray('foo','utf-8'))
31+
except Exception as e:
32+
result = e
33+
pass
34+
self.assertEqual(str(self.error_500), str(result))

Test/api/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import importlib
2+
from fastapi import FastAPI, Response
3+
4+
app = FastAPI()
5+
6+
@app.post("/v1/{username}/{algoname}/{version}")
7+
async def throw_error(username, algoname, version):
8+
return Response("Internal Server Error", status_code=500)
9+
10+
11+
def create_endpoint(algoname):
12+
module = importlib.import_module(algoname)
13+
@app.get("/invocations")
14+
def invocations(data):
15+
return module.apply(data)
16+

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ argparse
66
algorithmia-api-client>=1.3,<1.4
77
algorithmia-adk>=1.0.2,<1.1
88
numpy<2
9+
uvicorn==0.14.0
10+
fastapi==0.65.2

0 commit comments

Comments
 (0)