mirror of
https://github.com/Kludex/awesome-fastapi-projects.git
synced 2025-05-14 21:27:04 +00:00
Add a working prototype of SourceGraph client
This commit is contained in:
parent
61967ab207
commit
dae524ab0c
1
app/scraper/__init__.py
Normal file
1
app/scraper/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Scraping module for the application."""
|
85
app/scraper/client.py
Normal file
85
app/scraper/client.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
"""The client for the SourceGraph API."""
|
||||||
|
from collections.abc import AsyncGenerator, Mapping
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from datetime import timedelta
|
||||||
|
from typing import Any, AnyStr, Final, Self
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
import stamina
|
||||||
|
from httpx_sse import EventSource, ServerSentEvent, aconnect_sse
|
||||||
|
from pydantic import HttpUrl
|
||||||
|
|
||||||
|
#: The URL of the SourceGraph SSE API.
|
||||||
|
SOURCE_GRAPH_STREAM_API_URL: Final[
|
||||||
|
HttpUrl
|
||||||
|
] = "https://sourcegraph.com/.api/search/stream"
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncSourceGraphSSEClient:
|
||||||
|
"""A client for the SourceGraph SSE API."""
|
||||||
|
|
||||||
|
def __init__(self: Self) -> None:
|
||||||
|
"""Initialize the client."""
|
||||||
|
self._last_event_id: str | None = None
|
||||||
|
self._reconnection_delay: float = 0.0
|
||||||
|
self._aclient: httpx.AsyncClient = httpx.AsyncClient()
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def _aconnect_sse(
|
||||||
|
self: Self, **kwargs: Mapping[AnyStr, Any]
|
||||||
|
) -> AsyncGenerator[EventSource, None]:
|
||||||
|
"""Connect to the SourceGraph SSE API."""
|
||||||
|
headers = kwargs.pop("headers", {})
|
||||||
|
if self._last_event_id is not None:
|
||||||
|
headers["Last-Event-ID"] = self._last_event_id
|
||||||
|
async with aconnect_sse(
|
||||||
|
client=self._aclient,
|
||||||
|
url=SOURCE_GRAPH_STREAM_API_URL,
|
||||||
|
method="GET",
|
||||||
|
headers=headers,
|
||||||
|
**kwargs,
|
||||||
|
) as event_source:
|
||||||
|
yield event_source
|
||||||
|
|
||||||
|
async def _aiter_sse(
|
||||||
|
self: Self, **kwargs: Mapping[AnyStr, Any]
|
||||||
|
) -> AsyncGenerator[ServerSentEvent, None]:
|
||||||
|
"""Iterate over the SourceGraph SSE API."""
|
||||||
|
async with self._aconnect_sse(**kwargs) as event_source:
|
||||||
|
async for event in event_source.aiter_sse():
|
||||||
|
yield event
|
||||||
|
|
||||||
|
async def _aiter_sse_with_retries(
|
||||||
|
self: Self, **kwargs: Mapping[AnyStr, Any]
|
||||||
|
) -> AsyncGenerator[ServerSentEvent, None]:
|
||||||
|
"""Iterate over the SourceGraph SSE API with retries."""
|
||||||
|
async for attempt in stamina.retry_context(
|
||||||
|
on=httpx.ReadError, wait_initial=max(self._reconnection_delay, 0.1)
|
||||||
|
):
|
||||||
|
with attempt:
|
||||||
|
async for event in self._aiter_sse(**kwargs):
|
||||||
|
self._last_event_id = event.id
|
||||||
|
if event.retry is not None:
|
||||||
|
self._reconnection_delay = timedelta(
|
||||||
|
milliseconds=event.retry
|
||||||
|
).total_seconds()
|
||||||
|
yield event
|
||||||
|
|
||||||
|
async def aiter_fastapi_repos(self: Self) -> AsyncGenerator[ServerSentEvent, None]:
|
||||||
|
"""Iterate over the SourceGraph SSE API with retries."""
|
||||||
|
params = {
|
||||||
|
"q": quote(
|
||||||
|
" ".join(
|
||||||
|
[
|
||||||
|
"repo:has.content(from fastapi import FastApi)",
|
||||||
|
"type:repo",
|
||||||
|
"visibility:public",
|
||||||
|
"archived:no",
|
||||||
|
"fork:no",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
async for event in self._aiter_sse_with_retries(params=params):
|
||||||
|
yield event
|
@ -14,11 +14,14 @@ dependencies = [
|
|||||||
"aiosqlite",
|
"aiosqlite",
|
||||||
"alembic",
|
"alembic",
|
||||||
"fastapi[all]",
|
"fastapi[all]",
|
||||||
|
"httpx-sse",
|
||||||
"sqlalchemy[asyncio]",
|
"sqlalchemy[asyncio]",
|
||||||
|
"stamina",
|
||||||
]
|
]
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
"black",
|
"black",
|
||||||
|
"ipython",
|
||||||
"isort",
|
"isort",
|
||||||
"pip-tools",
|
"pip-tools",
|
||||||
"pre-commit",
|
"pre-commit",
|
||||||
|
@ -39,6 +39,8 @@ httptools==0.6.0
|
|||||||
# via uvicorn
|
# via uvicorn
|
||||||
httpx==0.24.1
|
httpx==0.24.1
|
||||||
# via fastapi
|
# via fastapi
|
||||||
|
httpx-sse==0.3.1
|
||||||
|
# via awesome-fastapi-projects (pyproject.toml)
|
||||||
idna==3.4
|
idna==3.4
|
||||||
# via
|
# via
|
||||||
# anyio
|
# anyio
|
||||||
@ -86,8 +88,12 @@ sqlalchemy[asyncio]==2.0.19
|
|||||||
# via
|
# via
|
||||||
# alembic
|
# alembic
|
||||||
# awesome-fastapi-projects (pyproject.toml)
|
# awesome-fastapi-projects (pyproject.toml)
|
||||||
|
stamina==23.1.0
|
||||||
|
# via awesome-fastapi-projects (pyproject.toml)
|
||||||
starlette==0.27.0
|
starlette==0.27.0
|
||||||
# via fastapi
|
# via fastapi
|
||||||
|
tenacity==8.2.2
|
||||||
|
# via stamina
|
||||||
typing-extensions==4.7.1
|
typing-extensions==4.7.1
|
||||||
# via
|
# via
|
||||||
# alembic
|
# alembic
|
||||||
|
@ -15,6 +15,10 @@ anyio==3.7.1
|
|||||||
# httpcore
|
# httpcore
|
||||||
# starlette
|
# starlette
|
||||||
# watchfiles
|
# watchfiles
|
||||||
|
asttokens==2.2.1
|
||||||
|
# via stack-data
|
||||||
|
backcall==0.2.0
|
||||||
|
# via ipython
|
||||||
black==23.7.0
|
black==23.7.0
|
||||||
# via awesome-fastapi-projects (pyproject.toml)
|
# via awesome-fastapi-projects (pyproject.toml)
|
||||||
build==0.10.0
|
build==0.10.0
|
||||||
@ -30,12 +34,16 @@ click==8.1.6
|
|||||||
# black
|
# black
|
||||||
# pip-tools
|
# pip-tools
|
||||||
# uvicorn
|
# uvicorn
|
||||||
|
decorator==5.1.1
|
||||||
|
# via ipython
|
||||||
distlib==0.3.7
|
distlib==0.3.7
|
||||||
# via virtualenv
|
# via virtualenv
|
||||||
dnspython==2.4.1
|
dnspython==2.4.1
|
||||||
# via email-validator
|
# via email-validator
|
||||||
email-validator==2.0.0.post2
|
email-validator==2.0.0.post2
|
||||||
# via fastapi
|
# via fastapi
|
||||||
|
executing==1.2.0
|
||||||
|
# via stack-data
|
||||||
fastapi[all]==0.100.0
|
fastapi[all]==0.100.0
|
||||||
# via awesome-fastapi-projects (pyproject.toml)
|
# via awesome-fastapi-projects (pyproject.toml)
|
||||||
filelock==3.12.2
|
filelock==3.12.2
|
||||||
@ -52,6 +60,8 @@ httptools==0.6.0
|
|||||||
# via uvicorn
|
# via uvicorn
|
||||||
httpx==0.24.1
|
httpx==0.24.1
|
||||||
# via fastapi
|
# via fastapi
|
||||||
|
httpx-sse==0.3.1
|
||||||
|
# via awesome-fastapi-projects (pyproject.toml)
|
||||||
identify==2.5.26
|
identify==2.5.26
|
||||||
# via pre-commit
|
# via pre-commit
|
||||||
idna==3.4
|
idna==3.4
|
||||||
@ -59,10 +69,14 @@ idna==3.4
|
|||||||
# anyio
|
# anyio
|
||||||
# email-validator
|
# email-validator
|
||||||
# httpx
|
# httpx
|
||||||
|
ipython==8.14.0
|
||||||
|
# via awesome-fastapi-projects (pyproject.toml)
|
||||||
isort==5.12.0
|
isort==5.12.0
|
||||||
# via awesome-fastapi-projects (pyproject.toml)
|
# via awesome-fastapi-projects (pyproject.toml)
|
||||||
itsdangerous==2.1.2
|
itsdangerous==2.1.2
|
||||||
# via fastapi
|
# via fastapi
|
||||||
|
jedi==0.19.0
|
||||||
|
# via ipython
|
||||||
jinja2==3.1.2
|
jinja2==3.1.2
|
||||||
# via fastapi
|
# via fastapi
|
||||||
mako==1.2.4
|
mako==1.2.4
|
||||||
@ -71,6 +85,8 @@ markupsafe==2.1.3
|
|||||||
# via
|
# via
|
||||||
# jinja2
|
# jinja2
|
||||||
# mako
|
# mako
|
||||||
|
matplotlib-inline==0.1.6
|
||||||
|
# via ipython
|
||||||
mypy-extensions==1.0.0
|
mypy-extensions==1.0.0
|
||||||
# via black
|
# via black
|
||||||
natsort==8.4.0
|
natsort==8.4.0
|
||||||
@ -84,8 +100,14 @@ packaging==23.1
|
|||||||
# black
|
# black
|
||||||
# build
|
# build
|
||||||
# pyproject-fmt
|
# pyproject-fmt
|
||||||
|
parso==0.8.3
|
||||||
|
# via jedi
|
||||||
pathspec==0.11.1
|
pathspec==0.11.1
|
||||||
# via black
|
# via black
|
||||||
|
pexpect==4.8.0
|
||||||
|
# via ipython
|
||||||
|
pickleshare==0.7.5
|
||||||
|
# via ipython
|
||||||
pip-tools==7.1.0
|
pip-tools==7.1.0
|
||||||
# via awesome-fastapi-projects (pyproject.toml)
|
# via awesome-fastapi-projects (pyproject.toml)
|
||||||
platformdirs==3.9.1
|
platformdirs==3.9.1
|
||||||
@ -94,6 +116,12 @@ platformdirs==3.9.1
|
|||||||
# virtualenv
|
# virtualenv
|
||||||
pre-commit==3.3.3
|
pre-commit==3.3.3
|
||||||
# via awesome-fastapi-projects (pyproject.toml)
|
# via awesome-fastapi-projects (pyproject.toml)
|
||||||
|
prompt-toolkit==3.0.39
|
||||||
|
# via ipython
|
||||||
|
ptyprocess==0.7.0
|
||||||
|
# via pexpect
|
||||||
|
pure-eval==0.2.2
|
||||||
|
# via stack-data
|
||||||
pydantic==2.1.1
|
pydantic==2.1.1
|
||||||
# via
|
# via
|
||||||
# fastapi
|
# fastapi
|
||||||
@ -105,6 +133,8 @@ pydantic-extra-types==2.0.0
|
|||||||
# via fastapi
|
# via fastapi
|
||||||
pydantic-settings==2.0.2
|
pydantic-settings==2.0.2
|
||||||
# via fastapi
|
# via fastapi
|
||||||
|
pygments==2.15.1
|
||||||
|
# via ipython
|
||||||
pyproject-fmt==0.13.0
|
pyproject-fmt==0.13.0
|
||||||
# via awesome-fastapi-projects (pyproject.toml)
|
# via awesome-fastapi-projects (pyproject.toml)
|
||||||
pyproject-hooks==1.0.0
|
pyproject-hooks==1.0.0
|
||||||
@ -124,6 +154,8 @@ pyyaml==6.0.1
|
|||||||
# uvicorn
|
# uvicorn
|
||||||
ruff==0.0.280
|
ruff==0.0.280
|
||||||
# via awesome-fastapi-projects (pyproject.toml)
|
# via awesome-fastapi-projects (pyproject.toml)
|
||||||
|
six==1.16.0
|
||||||
|
# via asttokens
|
||||||
sniffio==1.3.0
|
sniffio==1.3.0
|
||||||
# via
|
# via
|
||||||
# anyio
|
# anyio
|
||||||
@ -133,12 +165,22 @@ sqlalchemy[asyncio]==2.0.19
|
|||||||
# via
|
# via
|
||||||
# alembic
|
# alembic
|
||||||
# awesome-fastapi-projects (pyproject.toml)
|
# awesome-fastapi-projects (pyproject.toml)
|
||||||
|
stack-data==0.6.2
|
||||||
|
# via ipython
|
||||||
|
stamina==23.1.0
|
||||||
|
# via awesome-fastapi-projects (pyproject.toml)
|
||||||
starlette==0.27.0
|
starlette==0.27.0
|
||||||
# via fastapi
|
# via fastapi
|
||||||
|
tenacity==8.2.2
|
||||||
|
# via stamina
|
||||||
tokenize-rt==5.1.0
|
tokenize-rt==5.1.0
|
||||||
# via pyupgrade
|
# via pyupgrade
|
||||||
tomlkit==0.12.1
|
tomlkit==0.12.1
|
||||||
# via pyproject-fmt
|
# via pyproject-fmt
|
||||||
|
traitlets==5.9.0
|
||||||
|
# via
|
||||||
|
# ipython
|
||||||
|
# matplotlib-inline
|
||||||
typing-extensions==4.7.1
|
typing-extensions==4.7.1
|
||||||
# via
|
# via
|
||||||
# alembic
|
# alembic
|
||||||
@ -156,6 +198,8 @@ virtualenv==20.24.2
|
|||||||
# via pre-commit
|
# via pre-commit
|
||||||
watchfiles==0.19.0
|
watchfiles==0.19.0
|
||||||
# via uvicorn
|
# via uvicorn
|
||||||
|
wcwidth==0.2.6
|
||||||
|
# via prompt-toolkit
|
||||||
websockets==11.0.3
|
websockets==11.0.3
|
||||||
# via uvicorn
|
# via uvicorn
|
||||||
wheel==0.41.0
|
wheel==0.41.0
|
||||||
|
@ -46,6 +46,8 @@ httptools==0.6.0
|
|||||||
# via uvicorn
|
# via uvicorn
|
||||||
httpx==0.24.1
|
httpx==0.24.1
|
||||||
# via fastapi
|
# via fastapi
|
||||||
|
httpx-sse==0.3.1
|
||||||
|
# via awesome-fastapi-projects (pyproject.toml)
|
||||||
idna==3.4
|
idna==3.4
|
||||||
# via
|
# via
|
||||||
# anyio
|
# anyio
|
||||||
@ -119,8 +121,12 @@ sqlalchemy[asyncio]==2.0.19
|
|||||||
# via
|
# via
|
||||||
# alembic
|
# alembic
|
||||||
# awesome-fastapi-projects (pyproject.toml)
|
# awesome-fastapi-projects (pyproject.toml)
|
||||||
|
stamina==23.1.0
|
||||||
|
# via awesome-fastapi-projects (pyproject.toml)
|
||||||
starlette==0.27.0
|
starlette==0.27.0
|
||||||
# via fastapi
|
# via fastapi
|
||||||
|
tenacity==8.2.2
|
||||||
|
# via stamina
|
||||||
typing-extensions==4.7.1
|
typing-extensions==4.7.1
|
||||||
# via
|
# via
|
||||||
# alembic
|
# alembic
|
||||||
|
Loading…
x
Reference in New Issue
Block a user