Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 86 additions & 10 deletions fast_flights/fetcher.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import overload
from typing import Literal, overload

from primp import Client

Expand All @@ -10,7 +10,13 @@


@overload
def get_flights(q: str, /, *, proxy: str | None = None) -> MetaList:
def get_flights(
q: str,
/,
*,
proxy: str | None = None,
return_url: Literal[False] = False,
) -> MetaList:
"""Get flights using a str query.

Examples:
Expand All @@ -19,7 +25,28 @@ def get_flights(q: str, /, *, proxy: str | None = None) -> MetaList:


@overload
def get_flights(q: Query, /, *, proxy: str | None = None) -> MetaList:
def get_flights(
q: str,
/,
*,
proxy: str | None = None,
return_url: Literal[True],
) -> tuple[MetaList, str]:
"""Get flights using a str query, returning both flights and URL.

Examples:
- *Flights from TPE to MYJ on 2025-12-22 one way economy class*
"""


@overload
def get_flights(
q: Query,
/,
*,
proxy: str | None = None,
return_url: Literal[False] = False,
) -> MetaList:
"""Get flights using a structured query.

Example:
Expand All @@ -44,21 +71,65 @@ def get_flights(q: Query, /, *, proxy: str | None = None) -> MetaList:
"""


@overload
def get_flights(
q: Query,
/,
*,
proxy: str | None = None,
return_url: Literal[True],
) -> tuple[MetaList, str]:
"""Get flights using a structured query, returning both flights and URL.

Example:
```python
get_flights(
query(
flights=[
FlightQuery(
date="2025-12-22",
from_airport="TPE",
to_airport="MYJ",
)
],
seat="economy",
trip="one-way",
passengers=Passengers(adults=1),
language="en-US",
currency="",
),
return_url=True
)
```
"""


def get_flights(
q: Query | str,
/,
*,
proxy: str | None = None,
integration: Integration | None = None,
) -> MetaList:
return_url: bool = False,
) -> MetaList | tuple[MetaList, str]:
"""Get flights.

Args:
q: The query.
proxy (str, optional): Proxy.
return_url (bool, optional): If True, returns a tuple of (MetaList, url).
If False (default), returns only MetaList for backward compatibility.

Returns:
MetaList if return_url is False (default), or
tuple of (MetaList, url) if return_url is True.
"""
html = fetch_flights_html(q, proxy=proxy, integration=integration)
return parse(html)
html, url = fetch_flights_html(q, proxy=proxy, integration=integration)
flights = parse(html)

if return_url:
return flights, url
return flights


def fetch_flights_html(
Expand All @@ -67,12 +138,16 @@ def fetch_flights_html(
*,
proxy: str | None = None,
integration: Integration | None = None,
) -> str:
"""Fetch flights and get the **HTML**.
) -> tuple[str, str]:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if fetch_flights_html is exported publicly and used externally

# Check __init__.py exports
echo "=== Checking public exports ==="
cat fast_flights/__init__.py 2>/dev/null || echo "__init__.py not found"

# Search for external usage patterns of fetch_flights_html
echo -e "\n=== Searching for fetch_flights_html usage ==="
rg -n "fetch_flights_html" --type py -C2

Repository: AWeirdDev/flights

Length of output: 1515


🏁 Script executed:

#!/bin/bash

# Get the full function signature and implementation
echo "=== Full fetch_flights_html function ==="
sed -n '135,168p' fast_flights/fetcher.py

# Check for docstrings
echo -e "\n=== Checking for docstrings ==="
sed -n '135,155p' fast_flights/fetcher.py | grep -A 20 '"""'

# Search for tests related to fetch_flights_html
echo -e "\n=== Searching for tests ==="
find . -type f -name "*test*.py" -o -name "test_*" | head -20
rg -n "fetch_flights_html" --type py tests/ 2>/dev/null || echo "No tests found"

# Check for any changelog or documentation about this change
echo -e "\n=== Checking changelog/docs ==="
fd -e md -e rst -e txt | xargs grep -l "fetch_flights_html" 2>/dev/null || echo "No docs mention found"

Repository: AWeirdDev/flights

Length of output: 1478


Breaking change: fetch_flights_html return type changed from str to tuple[str, str].

The function is publicly exported and now returns a tuple of (html, url) instead of just the HTML string. External callers expecting a string will break:

# Code that worked before will now fail:
html = fetch_flights_html(query)  # Now returns tuple, not string

The return type is documented in the function's docstring, but this breaking change lacks a release note or migration guide. Consider either:

  1. Adding a return_url parameter to maintain backward compatibility, or
  2. Documenting this as a breaking change in release notes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fast_flights/fetcher.py` at line 141, fetch_flights_html was changed to
return (html, url) breaking callers that expect a single string; restore
backward compatibility by adding an optional parameter (e.g., return_url: bool =
False) to fetch_flights_html and branch the return: when return_url is False
return the html string (old behavior) and when True return the (html, url)
tuple, update the function signature/type hints and docstring to describe the
parameter and both return shapes (or provide typed overloads), and update
tests/docs; alternatively, if you choose to keep the new tuple-only API, add a
clear release note/migration guide calling out fetch_flights_html change so
callers can adapt.

"""Fetch flights and get the **HTML** and **URL**.

Args:
q: The query.
proxy (str, optional): Proxy.

Returns:
A tuple of (html, url) where html is the response text
and url is the final URL of the request.
"""
if integration is None:
client = Client(
Expand All @@ -90,7 +165,8 @@ def fetch_flights_html(
params = {"q": q}

res = client.get(URL, params=params)
return res.text
return res.text, str(res.url)

else:
return integration.fetch_html(q)
html = integration.fetch_html(q)
return html, URL
Comment on lines 170 to +172
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Integration branch returns incomplete URL without query parameters.

The direct fetch path (line 168) returns str(res.url) which includes the full URL with query parameters. However, the integration branch returns only the base URL constant ("https://www.google.com/travel/flights"), omitting query parameters entirely.

This inconsistency undermines the debugging/sharing purpose of returning the URL. Users would expect the full request URL, not just the base endpoint.

Suggested fix
     else:
         html = integration.fetch_html(q)
-        return html, URL
+        if isinstance(q, Query):
+            return html, q.url()
+        else:
+            return html, f"{URL}?q={q}"

This mirrors how bright_data.py constructs URLs internally (per relevant snippet at fast_flights/integrations/bright_data.py:38-48).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
else:
return integration.fetch_html(q)
html = integration.fetch_html(q)
return html, URL
else:
html = integration.fetch_html(q)
if isinstance(q, Query):
return html, q.url()
else:
return html, f"{URL}?q={q}"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fast_flights/fetcher.py` around lines 170 - 172, The integration branch
currently returns only the base URL constant (URL) which omits query parameters;
update the integration path in the fetch logic so it returns the full request
URL (including query string) along with the fetched HTML — either by changing
integration.fetch_html(q) to return a tuple (html, full_url) or by constructing
the URL the same way bright_data does (see bright_data.build/construct logic)
and return that full URL instead of the bare URL constant; ensure the calling
code that handles the other branch (which expects (html, URL)) still receives
the same (html, full_url) shape.