12.3.3. Homework#
Use these questions after class to check your understanding of the chapter concepts and coding skills.
Sign in from the account menu before you start working on the homework.
Complete all true/false and coding questions before you submit the homework.
Launch Live Code (the rocket) to write and run code in the coding cells.
Multiple submissions are allowed. The highest score is kept.
1. HTTP Methods
A client can use GET to request data and POST to send a new JSON payload when an API supports those methods.
2. Status Codes
A 404 response usually means the requested API resource was not found.
3. Query Parameters
API query parameters should normally be pasted into a URL string by hand instead of passed through a structured parameter dictionary.
4. JSON Payloads
After response.json() returns a dictionary, Python code can navigate nested data with keys and list indexes.
5. API Keys
It is good practice to hard-code a private API key directly in a textbook notebook before committing it.
### 6. Build query parameters
# In your own code, urllib.parse.urlencode(params) builds and percent-encodes a query
# string, and requests.get(url, params=params) does it for you. This grading sandbox
# cannot import urllib, so encode_value() below stands in for the encoding step: it
# escapes %, &, =, and + and turns a space into +, the same way urlencode() does.
# Pass each key and value in params through encode_value(), join each pair with "=",
# join the pairs with "&", and add the result after "/search?".
# Print exactly this line:
# # /search?q=fish+%26+chips&page=2&limit=5
params = {"q": "fish & chips", "page": 2, "limit": 5}
def encode_value(value):
text = str(value)
for char, code in [("%", "%25"), ("&", "%26"), ("=", "%3D"), ("+", "%2B"), (" ", "+")]:
text = text.replace(char, code)
return text
### Your code starts here.
### Your code ends here.
/search?q=fish+%26+chips&page=2&limit=5
### 7. Check a status code
# Use a conditional to label the response. Print exactly this line:
# # Status: retry
response = {"status_code": 503, "body": {"message": "busy"}}
### Your code starts here.
### Your code ends here.
Status: retry
### 8. Retry with exponential backoff
# FlakySource raises TransientError a set number of times before it succeeds.
# Write retry_call(call_fn, retries=4, base_delay=0.5) that calls call_fn() and,
# after each TransientError, records the backoff delay base_delay * 2 ** (attempt - 1)
# in a delays list instead of calling time.sleep(). On success, return a tuple of
# the result and delays. If the last attempt also fails, re-raise the error.
# Call it on FlakySource(3).get and print the result and the delays. Then call it
# on FlakySource(5).get, catch the TransientError, and print "Gave up:" with the
# error. Print exactly these three lines:
# # Result: {'status': 'ok', 'calls': 4}
# # Delays: [0.5, 1.0, 2.0]
# # Gave up: temporary timeout
class TransientError(Exception):
pass
class FlakySource:
def __init__(self, failures_before_success):
self.failures_before_success = failures_before_success
self.calls = 0
def get(self):
self.calls += 1
if self.calls <= self.failures_before_success:
raise TransientError("temporary timeout")
return {"status": "ok", "calls": self.calls}
### Your code starts here.
### Your code ends here.
Result: {'status': 'ok', 'calls': 4}
Delays: [0.5, 1.0, 2.0]
Gave up: temporary timeout
### 9. Prepare a POST body
# Create a JSON-like body dictionary. Print exactly this line:
# # Body: {'name': 'Ana', 'role': 'analyst'}
name = "Ana"
role = "analyst"
### Your code starts here.
### Your code ends here.
Body: {'name': 'Ana', 'role': 'analyst'}
### 10. Validate a response contract
# contract maps each required key to its expected type.
# Write validate_payload(payload, contract) that raises ValueError with the message
# "Missing fields: [...]" (a list of missing keys in contract order) when keys are
# missing, raises ValueError with "Wrong type for field: <key>" for the first value
# whose type does not match, and otherwise returns a new dict with only the contract keys.
# Call it on good, missing, and bad_type in that order. Print the returned dict, or
# print "Error:" with the message when it raises. Print exactly these three lines:
# # {'city': 'Austin', 'temp': 31.5, 'humidity': 40}
# # Error: Missing fields: ['temp', 'humidity']
# # Error: Wrong type for field: temp
contract = {"city": str, "temp": float, "humidity": int}
good = {"city": "Austin", "temp": 31.5, "humidity": 40, "source": "sensor-7"}
missing = {"city": "Austin"}
bad_type = {"city": "Austin", "temp": "hot", "humidity": 40}
### Your code starts here.
### Your code ends here.
{'city': 'Austin', 'temp': 31.5, 'humidity': 40}
Error: Missing fields: ['temp', 'humidity']
Error: Wrong type for field: temp