Skip to content

Commit fd7d377

Browse files
SDK regeneration
1 parent c4faac0 commit fd7d377

File tree

11 files changed

+310
-46
lines changed

11 files changed

+310
-46
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/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import typing
77

88
import httpx
9-
from .types.project_environment import ProjectEnvironment
9+
from ._.types.project_environment import ProjectEnvironment
1010
from .core.api_error import ApiError
1111
from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
1212
from .core.oauth_token_provider import OAuthTokenProvider

src/pipedream/core/client_wrapper.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import typing
44

55
import httpx
6-
from ..types.project_environment import ProjectEnvironment
6+
from .._.types.project_environment import ProjectEnvironment
77
from .http_client import AsyncHttpClient, HttpClient
88

99

@@ -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)

0 commit comments

Comments
 (0)