Skip to content

Commit 0ff0d74

Browse files
Regenerate SDK with Fern's latest Python generator (#199)
--------- Co-authored-by: Jay Vercellone <jay@pipedream.com>
1 parent c4faac0 commit 0ff0d74

File tree

10 files changed

+308
-44
lines changed

10 files changed

+308
-44
lines changed

poetry.lock

Lines changed: 47 additions & 37 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "pipedream"
33

44
[tool.poetry]
55
name = "pipedream"
6-
version = "1.0.9"
6+
version = "1.0.10"
77
description = ""
88
readme = "README.md"
99
authors = []

src/pipedream/core/client_wrapper.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ def __init__(
2727

2828
def get_headers(self) -> typing.Dict[str, str]:
2929
headers: typing.Dict[str, str] = {
30-
"User-Agent": "pipedream/1.0.9",
30+
"User-Agent": "pipedream/1.0.10",
3131
"X-Fern-Language": "Python",
3232
"X-Fern-SDK-Name": "pipedream",
33-
"X-Fern-SDK-Version": "1.0.9",
33+
"X-Fern-SDK-Version": "1.0.10",
3434
**(self.get_custom_headers() or {}),
3535
}
3636
if self._project_environment is not None:
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# This file was auto-generated by Fern from our API Definition.
2+
3+
# isort: skip_file
4+
5+
import typing
6+
from importlib import import_module
7+
8+
if typing.TYPE_CHECKING:
9+
from ._api import EventSource, aconnect_sse, connect_sse
10+
from ._exceptions import SSEError
11+
from ._models import ServerSentEvent
12+
_dynamic_imports: typing.Dict[str, str] = {
13+
"EventSource": "._api",
14+
"SSEError": "._exceptions",
15+
"ServerSentEvent": "._models",
16+
"aconnect_sse": "._api",
17+
"connect_sse": "._api",
18+
}
19+
20+
21+
def __getattr__(attr_name: str) -> typing.Any:
22+
module_name = _dynamic_imports.get(attr_name)
23+
if module_name is None:
24+
raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
25+
try:
26+
module = import_module(module_name, __package__)
27+
if module_name == f".{attr_name}":
28+
return module
29+
else:
30+
return getattr(module, attr_name)
31+
except ImportError as e:
32+
raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
33+
except AttributeError as e:
34+
raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
35+
36+
37+
def __dir__():
38+
lazy_attrs = list(_dynamic_imports.keys())
39+
return sorted(lazy_attrs)
40+
41+
42+
__all__ = ["EventSource", "SSEError", "ServerSentEvent", "aconnect_sse", "connect_sse"]
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# This file was auto-generated by Fern from our API Definition.
2+
3+
import re
4+
from contextlib import asynccontextmanager, contextmanager
5+
from typing import Any, AsyncGenerator, AsyncIterator, Iterator, cast
6+
7+
import httpx
8+
from ._decoders import SSEDecoder
9+
from ._exceptions import SSEError
10+
from ._models import ServerSentEvent
11+
12+
13+
class EventSource:
14+
def __init__(self, response: httpx.Response) -> None:
15+
self._response = response
16+
17+
def _check_content_type(self) -> None:
18+
content_type = self._response.headers.get("content-type", "").partition(";")[0]
19+
if "text/event-stream" not in content_type:
20+
raise SSEError(
21+
f"Expected response header Content-Type to contain 'text/event-stream', got {content_type!r}"
22+
)
23+
24+
def _get_charset(self) -> str:
25+
"""Extract charset from Content-Type header, fallback to UTF-8."""
26+
content_type = self._response.headers.get("content-type", "")
27+
28+
# Parse charset parameter using regex
29+
charset_match = re.search(r"charset=([^;\s]+)", content_type, re.IGNORECASE)
30+
if charset_match:
31+
charset = charset_match.group(1).strip("\"'")
32+
# Validate that it's a known encoding
33+
try:
34+
# Test if the charset is valid by trying to encode/decode
35+
"test".encode(charset).decode(charset)
36+
return charset
37+
except (LookupError, UnicodeError):
38+
# If charset is invalid, fall back to UTF-8
39+
pass
40+
41+
# Default to UTF-8 if no charset specified or invalid charset
42+
return "utf-8"
43+
44+
@property
45+
def response(self) -> httpx.Response:
46+
return self._response
47+
48+
def iter_sse(self) -> Iterator[ServerSentEvent]:
49+
self._check_content_type()
50+
decoder = SSEDecoder()
51+
charset = self._get_charset()
52+
53+
buffer = ""
54+
for chunk in self._response.iter_bytes():
55+
# Decode chunk using detected charset
56+
text_chunk = chunk.decode(charset, errors="replace")
57+
buffer += text_chunk
58+
59+
# Process complete lines
60+
while "\n" in buffer:
61+
line, buffer = buffer.split("\n", 1)
62+
line = line.rstrip("\r")
63+
sse = decoder.decode(line)
64+
# when we reach a "\n\n" => line = ''
65+
# => decoder will attempt to return an SSE Event
66+
if sse is not None:
67+
yield sse
68+
69+
# Process any remaining data in buffer
70+
if buffer.strip():
71+
line = buffer.rstrip("\r")
72+
sse = decoder.decode(line)
73+
if sse is not None:
74+
yield sse
75+
76+
async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]:
77+
self._check_content_type()
78+
decoder = SSEDecoder()
79+
lines = cast(AsyncGenerator[str, None], self._response.aiter_lines())
80+
try:
81+
async for line in lines:
82+
line = line.rstrip("\n")
83+
sse = decoder.decode(line)
84+
if sse is not None:
85+
yield sse
86+
finally:
87+
await lines.aclose()
88+
89+
90+
@contextmanager
91+
def connect_sse(client: httpx.Client, method: str, url: str, **kwargs: Any) -> Iterator[EventSource]:
92+
headers = kwargs.pop("headers", {})
93+
headers["Accept"] = "text/event-stream"
94+
headers["Cache-Control"] = "no-store"
95+
96+
with client.stream(method, url, headers=headers, **kwargs) as response:
97+
yield EventSource(response)
98+
99+
100+
@asynccontextmanager
101+
async def aconnect_sse(
102+
client: httpx.AsyncClient,
103+
method: str,
104+
url: str,
105+
**kwargs: Any,
106+
) -> AsyncIterator[EventSource]:
107+
headers = kwargs.pop("headers", {})
108+
headers["Accept"] = "text/event-stream"
109+
headers["Cache-Control"] = "no-store"
110+
111+
async with client.stream(method, url, headers=headers, **kwargs) as response:
112+
yield EventSource(response)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# This file was auto-generated by Fern from our API Definition.
2+
3+
from typing import List, Optional
4+
5+
from ._models import ServerSentEvent
6+
7+
8+
class SSEDecoder:
9+
def __init__(self) -> None:
10+
self._event = ""
11+
self._data: List[str] = []
12+
self._last_event_id = ""
13+
self._retry: Optional[int] = None
14+
15+
def decode(self, line: str) -> Optional[ServerSentEvent]:
16+
# See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501
17+
18+
if not line:
19+
if not self._event and not self._data and not self._last_event_id and self._retry is None:
20+
return None
21+
22+
sse = ServerSentEvent(
23+
event=self._event,
24+
data="\n".join(self._data),
25+
id=self._last_event_id,
26+
retry=self._retry,
27+
)
28+
29+
# NOTE: as per the SSE spec, do not reset last_event_id.
30+
self._event = ""
31+
self._data = []
32+
self._retry = None
33+
34+
return sse
35+
36+
if line.startswith(":"):
37+
return None
38+
39+
fieldname, _, value = line.partition(":")
40+
41+
if value.startswith(" "):
42+
value = value[1:]
43+
44+
if fieldname == "event":
45+
self._event = value
46+
elif fieldname == "data":
47+
self._data.append(value)
48+
elif fieldname == "id":
49+
if "\0" in value:
50+
pass
51+
else:
52+
self._last_event_id = value
53+
elif fieldname == "retry":
54+
try:
55+
self._retry = int(value)
56+
except (TypeError, ValueError):
57+
pass
58+
else:
59+
pass # Field is ignored.
60+
61+
return None
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# This file was auto-generated by Fern from our API Definition.
2+
3+
import httpx
4+
5+
6+
class SSEError(httpx.TransportError):
7+
pass

0 commit comments

Comments
 (0)