From d46deb27ad829a088b248356efde05e03fae9109 Mon Sep 17 00:00:00 2001 From: DJDevon3 <49322231+DJDevon3@users.noreply.github.com> Date: Thu, 11 Apr 2024 10:58:24 -0400 Subject: [PATCH 1/5] add context manager vs explicit close() examples This should be handy to point beginners at for recommended syntax to use with API requests. --- .../requests_wifi_context_manager_basics.py | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 examples/wifi/requests_wifi_context_manager_basics.py diff --git a/examples/wifi/requests_wifi_context_manager_basics.py b/examples/wifi/requests_wifi_context_manager_basics.py new file mode 100644 index 0000000..2d4a4de --- /dev/null +++ b/examples/wifi/requests_wifi_context_manager_basics.py @@ -0,0 +1,87 @@ +# SPDX-FileCopyrightText: 2024 DJDevon3 +# SPDX-License-Identifier: MIT +# Updated for Circuit Python 9.0 +""" WiFi Context Manager Basics Example """ + +import os + +import adafruit_connection_manager +import wifi + +import adafruit_requests + +# Get WiFi details, ensure these are setup in settings.toml +ssid = os.getenv("CIRCUITPY_WIFI_SSID") +password = os.getenv("CIRCUITPY_WIFI_PASSWORD") + +# Initalize Wifi, Socket Pool, Request Session +pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio) +requests = adafruit_requests.Session(pool, ssl_context) +rssi = wifi.radio.ap_info.rssi + +JSON_GET_URL = "https://httpbin.org/get" + +print(f"\nConnecting to {ssid}...") +print(f"Signal Strength: {rssi}") +try: + # Connect to the Wi-Fi network + wifi.radio.connect(ssid, password) +except OSError as e: + print(f"❌ OSError: {e}") +print("✅ Wifi!\n") + +print("-" * 40) +# This method requires an explicit close +print("Explicit Close Example") +response = requests.get(JSON_GET_URL) +json_data = response.json() +if response.status_code == 200: + print(f" | 🆗 Status Code: {response.status_code}") +else: + print(f" | | Status Code: {response.status_code}") +headers = json_data["headers"] +date = response.headers.get("date", "") +print(f" | | Response Timestamp: {date}") + +# Close socket manually (prone to mid-disconnect socket errors, out of retries) +response.close() +print(f" | ✂️ Disconnected from {JSON_GET_URL}") + +# Buffer data is still available from json_data after socket close +content_type = response.headers.get("content-type", "") +print(f" | Content-Type: {content_type}") + +print("\nversus\n") + +print("-" * 40) +# Closing socket is included automatically using "with" +print("Context Manager WITH Example") +response = requests.get(JSON_GET_URL) +# Wrap a request using a with statement +with requests.get(JSON_GET_URL) as response: + date = response.headers.get("date", "") + json_data = response.json() + if response.status_code == 200: + print(f" | 🆗 Status Code: {response.status_code}") + else: + print(f" | | Status Code: {response.status_code}") + headers = json_data["headers"] + print(f" | | Response Timestamp: {date}") + +# Notice there is no response.close() +# It's handled automatically in a with statement +# This is the better way. +print(f" | ✂️ Disconnected from {JSON_GET_URL}") + +# JSON data still available outside of with. +content_type = response.headers.get("content-type", "") +print(f" | Content-Type: {content_type}") + + +print("\nBoth examples are functionally identical") +print( + "However, a with statement is more robust against disconnections mid-request " + + "and automatically closes the socket." +) +print("Using with statements for requests is recommended\n\n") From e72d22a857fbf001de9b19de50804d7c0640e206 Mon Sep 17 00:00:00 2001 From: DJDevon3 <49322231+DJDevon3@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:08:59 -0400 Subject: [PATCH 2/5] reworded socket to response per Anecdata suggestion --- examples/wifi/requests_wifi_context_manager_basics.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/wifi/requests_wifi_context_manager_basics.py b/examples/wifi/requests_wifi_context_manager_basics.py index 2d4a4de..25a610c 100644 --- a/examples/wifi/requests_wifi_context_manager_basics.py +++ b/examples/wifi/requests_wifi_context_manager_basics.py @@ -44,18 +44,18 @@ date = response.headers.get("date", "") print(f" | | Response Timestamp: {date}") -# Close socket manually (prone to mid-disconnect socket errors, out of retries) +# Close response manually (prone to mid-disconnect socket errors, out of retries) response.close() print(f" | ✂️ Disconnected from {JSON_GET_URL}") -# Buffer data is still available from json_data after socket close +# Buffer data is still available from json_data after response close content_type = response.headers.get("content-type", "") print(f" | Content-Type: {content_type}") print("\nversus\n") print("-" * 40) -# Closing socket is included automatically using "with" +# Closing response is included automatically using "with" print("Context Manager WITH Example") response = requests.get(JSON_GET_URL) # Wrap a request using a with statement @@ -69,7 +69,7 @@ headers = json_data["headers"] print(f" | | Response Timestamp: {date}") -# Notice there is no response.close() +# Notice there is no response.close() here # It's handled automatically in a with statement # This is the better way. print(f" | ✂️ Disconnected from {JSON_GET_URL}") @@ -82,6 +82,6 @@ print("\nBoth examples are functionally identical") print( "However, a with statement is more robust against disconnections mid-request " - + "and automatically closes the socket." + + "and automatically closes the response." ) print("Using with statements for requests is recommended\n\n") From 067773cf0ad207c2769414f880468d0ebd053136 Mon Sep 17 00:00:00 2001 From: DJDevon3 <49322231+DJDevon3@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:25:17 -0400 Subject: [PATCH 3/5] reword comment for data still available after response closed suggested per Justin --- examples/wifi/requests_wifi_context_manager_basics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/wifi/requests_wifi_context_manager_basics.py b/examples/wifi/requests_wifi_context_manager_basics.py index 25a610c..c6ef9b0 100644 --- a/examples/wifi/requests_wifi_context_manager_basics.py +++ b/examples/wifi/requests_wifi_context_manager_basics.py @@ -48,7 +48,7 @@ response.close() print(f" | ✂️ Disconnected from {JSON_GET_URL}") -# Buffer data is still available from json_data after response close +# This example shows json_data still available after response close content_type = response.headers.get("content-type", "") print(f" | Content-Type: {content_type}") @@ -74,7 +74,7 @@ # This is the better way. print(f" | ✂️ Disconnected from {JSON_GET_URL}") -# JSON data still available outside of with. +# This example shows json_data still available outside of with. content_type = response.headers.get("content-type", "") print(f" | Content-Type: {content_type}") From 88299d5c274059c732d3952b8e48a0b40d7cdf68 Mon Sep 17 00:00:00 2001 From: DJDevon3 <49322231+DJDevon3@users.noreply.github.com> Date: Thu, 11 Apr 2024 13:32:14 -0400 Subject: [PATCH 4/5] added Socket in Use count to show disconnection is real Thanks to Justin for the example! --- .../wifi/requests_wifi_context_manager_basics.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/examples/wifi/requests_wifi_context_manager_basics.py b/examples/wifi/requests_wifi_context_manager_basics.py index c6ef9b0..ab2ca03 100644 --- a/examples/wifi/requests_wifi_context_manager_basics.py +++ b/examples/wifi/requests_wifi_context_manager_basics.py @@ -32,9 +32,12 @@ print("✅ Wifi!\n") print("-" * 40) + # This method requires an explicit close -print("Explicit Close Example") +print("Explicit Close() Example") response = requests.get(JSON_GET_URL) +print(" | ✅ Connected to JSON") + json_data = response.json() if response.status_code == 200: print(f" | 🆗 Status Code: {response.status_code}") @@ -50,14 +53,18 @@ # This example shows json_data still available after response close content_type = response.headers.get("content-type", "") -print(f" | Content-Type: {content_type}") +print(f" | Content-Type: {content_type}") +print("-" * 40) print("\nversus\n") print("-" * 40) + # Closing response is included automatically using "with" print("Context Manager WITH Example") response = requests.get(JSON_GET_URL) +print(" | ✅ Connected to JSON") + # Wrap a request using a with statement with requests.get(JSON_GET_URL) as response: date = response.headers.get("date", "") @@ -76,8 +83,8 @@ # This example shows json_data still available outside of with. content_type = response.headers.get("content-type", "") -print(f" | Content-Type: {content_type}") - +print(f" | Content-Type: {content_type}") +print("-" * 40) print("\nBoth examples are functionally identical") print( From 0aa7e0d90c9380641a70839a58fc30db553fb5f4 Mon Sep 17 00:00:00 2001 From: DJDevon3 <49322231+DJDevon3@users.noreply.github.com> Date: Fri, 12 Apr 2024 00:15:46 -0400 Subject: [PATCH 5/5] keep it simple, removed additional fluff --- examples/wifi/requests_wifi_context_manager_basics.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/examples/wifi/requests_wifi_context_manager_basics.py b/examples/wifi/requests_wifi_context_manager_basics.py index ab2ca03..9b5c68e 100644 --- a/examples/wifi/requests_wifi_context_manager_basics.py +++ b/examples/wifi/requests_wifi_context_manager_basics.py @@ -50,13 +50,9 @@ # Close response manually (prone to mid-disconnect socket errors, out of retries) response.close() print(f" | ✂️ Disconnected from {JSON_GET_URL}") - -# This example shows json_data still available after response close -content_type = response.headers.get("content-type", "") -print(f" | Content-Type: {content_type}") print("-" * 40) -print("\nversus\n") +print("versus") print("-" * 40) @@ -81,9 +77,6 @@ # This is the better way. print(f" | ✂️ Disconnected from {JSON_GET_URL}") -# This example shows json_data still available outside of with. -content_type = response.headers.get("content-type", "") -print(f" | Content-Type: {content_type}") print("-" * 40) print("\nBoth examples are functionally identical")