Skip to content

Commit

Permalink
httpx without subclassing. retaining ability to close connections if …
Browse files Browse the repository at this point in the history
…required.
  • Loading branch information
Schalk1e committed May 16, 2024
1 parent 6570d65 commit 340b995
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 66 deletions.
4 changes: 2 additions & 2 deletions src/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ class MissingConfig(Exception):


def config_from_env(key: str) -> str:
"""Checks whether key exists in global environment and returns it if it
does. Else it raises a MissingConfig Exception.
"""Fetches a config value from the global environment, raising
MissingConfig if it isn't there.
"""
if not (value := os.environ.get(key, None)):
Expand Down
80 changes: 34 additions & 46 deletions src/api/aaq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,38 @@
BASE_URL = config_from_env("AAQ_API_BASE_URL")


class BaseClient(Client):
"""To be used in various endpoint specific requests.
Need to subclass here in order to overload `paginate_get` which is useful
for each of the different endpoint specific request in this submodule.
This also allows calls like pyAAQ.inbounds.base_client.close(), which is
recommended for use of Client outside of a context manager.
"""

def __init__(self, *args, **kwargs) -> Client:
"""We expect a user to pass a base_url kwarg here to pass to Client."""
super().__init__(*args, **kwargs)

def paginate_get(self, url: str, limit: int = 100, **kwargs) -> list[dict]:
"""Paginate over pages in an AAQ endpoint up to a limit."""
params = {**kwargs}

params["offset"] = 0
if "limit" not in params:
params["limit"] = 100

response_list = []

while True:
print(
"Retrieving results for offsets: ",
params["offset"],
"to",
params["offset"] + params["limit"],
sep=" ",
)
response = self.get(url, params=params)
response.raise_for_status()
result = response.json()["result"]
response_list.append(result)
if len(result) == params["limit"]:
params["offset"] += params["limit"]
elif len(result) < params["limit"]:
break

response_list = sum(response_list, [])

return response_list
def paginate_get(
httpx_client: Client, url: str, limit: int = 100, **kwargs
) -> list[dict]:
"""Paginate over pages in an AAQ endpoint up to a limit."""

params = {**kwargs}

params["offset"] = 0
if "limit" not in params:
params["limit"] = 100

response_list = []
while True:
print(
"Retrieving results for offsets: ",
params["offset"],
"to",
params["offset"] + params["limit"],
sep=" ",
)
response = httpx_client.get(url, params=params)
response.raise_for_status()
result = response.json()["result"]
response_list.append(result)
if len(result) == params["limit"]:
params["offset"] += params["limit"]
elif len(result) < params["limit"]:
break

response_list = sum(response_list, [])

return response_list


headers = {
Expand All @@ -59,6 +46,7 @@ def paginate_get(self, url: str, limit: int = 100, **kwargs) -> list[dict]:
"Content-Type": "application/json",
}

base_client = BaseClient(base_url=BASE_URL, headers=headers)

httpx_client = Client(base_url=BASE_URL, headers=headers)

from .main import pyAAQ as pyAAQ
19 changes: 14 additions & 5 deletions src/api/aaq/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,22 @@
from api.aaq.requests.inbounds_ud import InboundsUD
from api.aaq.requests.urgency_rules import UrgencyRules

from . import base_client
from . import httpx_client


class pyAAQ:
"""A wrapper class for the various AAQ endpoints."""

inbounds = Inbounds(base_client)
faqmatches = FAQMatches(base_client)
inbounds_ud = InboundsUD(base_client)
urgency_rules = UrgencyRules(base_client)
inbounds = Inbounds(httpx_client)
faqmatches = FAQMatches(httpx_client)
inbounds_ud = InboundsUD(httpx_client)
urgency_rules = UrgencyRules(httpx_client)

def httpx_close():
"""Exposing the ability to close the client here.
The client is being used outside of a context manager
and httpx recommends closing these connections in this case.
"""
httpx_client.close()
9 changes: 6 additions & 3 deletions src/api/aaq/requests/faqmatches.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from attrs import define
from httpx import Client
from pandas import DataFrame, concat

from .. import BaseClient
from .. import paginate_get


@define
class FAQMatches:
"""Dedicated to the faqmatches endpoint of the AAQ Data Export API."""

base_client: type[BaseClient]
httpx_client: Client

def get_faqmatches(self, **kwargs) -> DataFrame:
"""Get a pandas DataFrame of faqmatches.
Expand All @@ -21,7 +22,9 @@ def get_faqmatches(self, **kwargs) -> DataFrame:

url = "faqmatches"

response_list = self.base_client.paginate_get(url, **kwargs)
response_list = paginate_get(
httpx_client=self.httpx_client, url=url, **kwargs
)

response_list = [
{key: str(d[key]) for key in d} for d in response_list
Expand Down
13 changes: 9 additions & 4 deletions src/api/aaq/requests/inbounds.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from ast import literal_eval

from attrs import define
from httpx import Client
from pandas import DataFrame, concat

from .. import BaseClient
from .. import paginate_get


@define
Expand All @@ -16,7 +17,7 @@ class Inbounds:
"""

base_client: type[BaseClient]
httpx_client: Client

def get_inbounds(self, **kwargs) -> DataFrame:
"""Get a pandas DataFrame of inbound messages.
Expand All @@ -32,7 +33,9 @@ def get_inbounds(self, **kwargs) -> DataFrame:
"""
url = "inbounds"

response_list = self.base_client.paginate_get(url, **kwargs)
response_list = paginate_get(
httpx_client=self.httpx_client, url=url, **kwargs
)

response_list = [
{key: str(d[key]) for key in d} for d in response_list
Expand Down Expand Up @@ -62,7 +65,9 @@ def get_faqranks(self, **kwargs) -> DataFrame:
"""
url = "inbounds"

response_list = self.base_client.paginate_get(url, **kwargs)
response_list = paginate_get(
httpx_client=self.httpx_client, url=url, **kwargs
)

response_list = [
{key: str(d[key]) for key in d} for d in response_list
Expand Down
9 changes: 6 additions & 3 deletions src/api/aaq/requests/inbounds_ud.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from attrs import define
from httpx import Client
from pandas import DataFrame, concat

from .. import BaseClient
from .. import paginate_get


@define
Expand All @@ -13,7 +14,7 @@ class InboundsUD:
"""

base_session: type[BaseClient]
httpx_client: Client

def get_inbounds_ud(self, **kwargs) -> DataFrame:
"""Get inbounds from the urgency detection endpoint.
Expand All @@ -30,7 +31,9 @@ def get_inbounds_ud(self, **kwargs) -> DataFrame:

url = "inbounds_ud"

response_list = self.base_session.paginate_get(url, **kwargs)
response_list = paginate_get(
httpx_client=self.httpx_client, url=url, **kwargs
)

response_list = [
{key: str(d[key]) for key in d} for d in response_list
Expand Down
9 changes: 6 additions & 3 deletions src/api/aaq/requests/urgency_rules.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from attrs import define
from httpx import Client
from pandas import DataFrame, concat

from .. import BaseClient
from .. import paginate_get


@define
Expand All @@ -13,13 +14,15 @@ class UrgencyRules:
"""

base_session: type[BaseClient]
httpx_client: Client

def get_urgency_rules(self, **kwargs) -> DataFrame:
"""Get a pandas DataFrame of urgency rules."""
url = "urgency_rules"

response_list = self.base_session.paginate_get(url, **kwargs)
response_list = paginate_get(
httpx_client=self.httpx_client, url=url, **kwargs
)

response_list = [
{key: str(d[key]) for key in d} for d in response_list
Expand Down

0 comments on commit 340b995

Please sign in to comment.