Skip to content

Commit 826ab21

Browse files
authored
Merge pull request #13 from steel-dev/release-please--branches--main--changes--next
release: 0.1.0-beta.2
2 parents 3d24e4e + eab7bc1 commit 826ab21

File tree

15 files changed

+143
-76
lines changed

15 files changed

+143
-76
lines changed

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "0.1.0-beta.1"
2+
".": "0.1.0-beta.2"
33
}

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
# Changelog
22

3+
## 0.1.0-beta.2 (2024-11-28)
4+
5+
Full Changelog: [v0.1.0-beta.1...v0.1.0-beta.2](https://github.com/steel-dev/steel-python/compare/v0.1.0-beta.1...v0.1.0-beta.2)
6+
7+
### Bug Fixes
8+
9+
* **client:** compat with new httpx 0.28.0 release ([#21](https://github.com/steel-dev/steel-python/issues/21)) ([91f80cc](https://github.com/steel-dev/steel-python/commit/91f80cc77f003c87b89e521ba56d96dc795fa072))
10+
11+
12+
### Chores
13+
14+
* **internal:** codegen related update ([#18](https://github.com/steel-dev/steel-python/issues/18)) ([264d442](https://github.com/steel-dev/steel-python/commit/264d4425bf0171dcc492cc33a8fdc99d5d380c74))
15+
* **internal:** exclude mypy from running on tests ([#20](https://github.com/steel-dev/steel-python/issues/20)) ([2d4a38d](https://github.com/steel-dev/steel-python/commit/2d4a38d2616354f58237d78e667eb20a907a85b5))
16+
* rebuild project due to codegen change ([#12](https://github.com/steel-dev/steel-python/issues/12)) ([c49b406](https://github.com/steel-dev/steel-python/commit/c49b406ba78c6fb4b17b015f5d294963608e6313))
17+
* rebuild project due to codegen change ([#14](https://github.com/steel-dev/steel-python/issues/14)) ([1ab28ec](https://github.com/steel-dev/steel-python/commit/1ab28ec28acc64043324e900af87b6b5ee558f64))
18+
* rebuild project due to codegen change ([#15](https://github.com/steel-dev/steel-python/issues/15)) ([60da75c](https://github.com/steel-dev/steel-python/commit/60da75c9b364810fe905a63de28b6259e3e767bd))
19+
* rebuild project due to codegen change ([#16](https://github.com/steel-dev/steel-python/issues/16)) ([f0f9386](https://github.com/steel-dev/steel-python/commit/f0f9386b4f2e0611a976b89abdc039beae7c96e5))
20+
* rebuild project due to codegen change ([#17](https://github.com/steel-dev/steel-python/issues/17)) ([dcfc0ea](https://github.com/steel-dev/steel-python/commit/dcfc0eaa856de36448880ff079bf24a73ce12637))
21+
* remove now unused `cached-property` dep ([#19](https://github.com/steel-dev/steel-python/issues/19)) ([c65914b](https://github.com/steel-dev/steel-python/commit/c65914babf7987c2cc2ba6e809f70f70163b3f26))
22+
323
## 0.1.0-beta.1 (2024-11-07)
424

525
Full Changelog: [v0.0.1-beta.5...v0.1.0-beta.1](https://github.com/steel-dev/steel-python/compare/v0.0.1-beta.5...v0.1.0-beta.1)

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ import os
2828
from steel import Steel
2929

3030
client = Steel(
31-
# This is the default and can be omitted
32-
steel_api_key=os.environ.get("STEEL_API_KEY"),
31+
steel_api_key=os.environ.get("STEEL_API_KEY"), # This is the default and can be omitted
3332
)
3433

3534
session = client.sessions.create(
@@ -54,8 +53,7 @@ import asyncio
5453
from steel import AsyncSteel
5554

5655
client = AsyncSteel(
57-
# This is the default and can be omitted
58-
steel_api_key=os.environ.get("STEEL_API_KEY"),
56+
steel_api_key=os.environ.get("STEEL_API_KEY"), # This is the default and can be omitted
5957
)
6058

6159

@@ -247,12 +245,14 @@ Note that requests that time out are [retried twice by default](#retries).
247245

248246
We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module.
249247

250-
You can enable logging by setting the environment variable `STEEL_LOG` to `debug`.
248+
You can enable logging by setting the environment variable `STEEL_LOG` to `info`.
251249

252250
```shell
253-
$ export STEEL_LOG=debug
251+
$ export STEEL_LOG=info
254252
```
255253

254+
Or to `debug` for more verbose logging.
255+
256256
### How to tell whether `None` means `null` or missing
257257

258258
In an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`:

mypy.ini

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ show_error_codes = True
55
# Exclude _files.py because mypy isn't smart enough to apply
66
# the correct type narrowing and as this is an internal module
77
# it's fine to just use Pyright.
8-
exclude = ^(src/steel/_files\.py|_dev/.*\.py)$
8+
#
9+
# We also exclude our `tests` as mypy doesn't always infer
10+
# types correctly and Pyright will still catch any type errors.
11+
exclude = ^(src/steel/_files\.py|_dev/.*\.py|tests/.*)$
912

1013
strict_equality = True
1114
implicit_reexport = True

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "steel-sdk"
3-
version = "0.1.0-beta.1"
3+
version = "0.1.0-beta.2"
44
description = "The official Python library for the steel API"
55
dynamic = ["readme"]
66
license = "Apache-2.0"
@@ -14,7 +14,6 @@ dependencies = [
1414
"anyio>=3.5.0, <5",
1515
"distro>=1.7.0, <2",
1616
"sniffio",
17-
"cached-property; python_version < '3.8'",
1817
]
1918
requires-python = ">= 3.8"
2019
classifiers = [
@@ -55,6 +54,7 @@ dev-dependencies = [
5554
"dirty-equals>=0.6.0",
5655
"importlib-metadata>=6.7.0",
5756
"rich>=13.7.1",
57+
"nest_asyncio==1.6.0"
5858
]
5959

6060
[tool.rye.scripts]

requirements-dev.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ mdurl==0.1.2
5151
mypy==1.13.0
5252
mypy-extensions==1.0.0
5353
# via mypy
54+
nest-asyncio==1.6.0
5455
nodeenv==1.8.0
5556
# via pyright
5657
nox==2023.4.22

src/steel/_base_client.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,7 @@ def __init__(
792792
custom_query: Mapping[str, object] | None = None,
793793
_strict_response_validation: bool,
794794
) -> None:
795+
kwargs: dict[str, Any] = {}
795796
if limits is not None:
796797
warnings.warn(
797798
"The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead",
@@ -804,6 +805,7 @@ def __init__(
804805
limits = DEFAULT_CONNECTION_LIMITS
805806

806807
if transport is not None:
808+
kwargs["transport"] = transport
807809
warnings.warn(
808810
"The `transport` argument is deprecated. The `http_client` argument should be passed instead",
809811
category=DeprecationWarning,
@@ -813,6 +815,7 @@ def __init__(
813815
raise ValueError("The `http_client` argument is mutually exclusive with `transport`")
814816

815817
if proxies is not None:
818+
kwargs["proxies"] = proxies
816819
warnings.warn(
817820
"The `proxies` argument is deprecated. The `http_client` argument should be passed instead",
818821
category=DeprecationWarning,
@@ -856,10 +859,9 @@ def __init__(
856859
base_url=base_url,
857860
# cast to a valid type because mypy doesn't understand our type narrowing
858861
timeout=cast(Timeout, timeout),
859-
proxies=proxies,
860-
transport=transport,
861862
limits=limits,
862863
follow_redirects=True,
864+
**kwargs, # type: ignore
863865
)
864866

865867
def is_closed(self) -> bool:
@@ -1358,6 +1360,7 @@ def __init__(
13581360
custom_headers: Mapping[str, str] | None = None,
13591361
custom_query: Mapping[str, object] | None = None,
13601362
) -> None:
1363+
kwargs: dict[str, Any] = {}
13611364
if limits is not None:
13621365
warnings.warn(
13631366
"The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead",
@@ -1370,6 +1373,7 @@ def __init__(
13701373
limits = DEFAULT_CONNECTION_LIMITS
13711374

13721375
if transport is not None:
1376+
kwargs["transport"] = transport
13731377
warnings.warn(
13741378
"The `transport` argument is deprecated. The `http_client` argument should be passed instead",
13751379
category=DeprecationWarning,
@@ -1379,6 +1383,7 @@ def __init__(
13791383
raise ValueError("The `http_client` argument is mutually exclusive with `transport`")
13801384

13811385
if proxies is not None:
1386+
kwargs["proxies"] = proxies
13821387
warnings.warn(
13831388
"The `proxies` argument is deprecated. The `http_client` argument should be passed instead",
13841389
category=DeprecationWarning,
@@ -1422,10 +1427,9 @@ def __init__(
14221427
base_url=base_url,
14231428
# cast to a valid type because mypy doesn't understand our type narrowing
14241429
timeout=cast(Timeout, timeout),
1425-
proxies=proxies,
1426-
transport=transport,
14271430
limits=limits,
14281431
follow_redirects=True,
1432+
**kwargs, # type: ignore
14291433
)
14301434

14311435
def is_closed(self) -> bool:

src/steel/_compat.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ def model_dump(
145145
exclude=exclude,
146146
exclude_unset=exclude_unset,
147147
exclude_defaults=exclude_defaults,
148-
warnings=warnings,
148+
# warnings are not supported in Pydantic v1
149+
warnings=warnings if PYDANTIC_V2 else True,
149150
)
150151
return cast(
151152
"dict[str, Any]",
@@ -213,9 +214,6 @@ def __set_name__(self, owner: type[Any], name: str) -> None: ...
213214
# __set__ is not defined at runtime, but @cached_property is designed to be settable
214215
def __set__(self, instance: object, value: _T) -> None: ...
215216
else:
216-
try:
217-
from functools import cached_property as cached_property
218-
except ImportError:
219-
from cached_property import cached_property as cached_property
217+
from functools import cached_property as cached_property
220218

221219
typed_cached_property = cached_property

src/steel/_utils/_sync.py

Lines changed: 40 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,62 @@
11
from __future__ import annotations
22

3+
import sys
4+
import asyncio
35
import functools
4-
from typing import TypeVar, Callable, Awaitable
6+
import contextvars
7+
from typing import Any, TypeVar, Callable, Awaitable
58
from typing_extensions import ParamSpec
69

7-
import anyio
8-
import anyio.to_thread
9-
10-
from ._reflection import function_has_argument
11-
1210
T_Retval = TypeVar("T_Retval")
1311
T_ParamSpec = ParamSpec("T_ParamSpec")
1412

1513

16-
# copied from `asyncer`, https://github.com/tiangolo/asyncer
17-
def asyncify(
18-
function: Callable[T_ParamSpec, T_Retval],
19-
*,
20-
cancellable: bool = False,
21-
limiter: anyio.CapacityLimiter | None = None,
22-
) -> Callable[T_ParamSpec, Awaitable[T_Retval]]:
14+
if sys.version_info >= (3, 9):
15+
to_thread = asyncio.to_thread
16+
else:
17+
# backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread
18+
# for Python 3.8 support
19+
async def to_thread(
20+
func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
21+
) -> Any:
22+
"""Asynchronously run function *func* in a separate thread.
23+
24+
Any *args and **kwargs supplied for this function are directly passed
25+
to *func*. Also, the current :class:`contextvars.Context` is propagated,
26+
allowing context variables from the main thread to be accessed in the
27+
separate thread.
28+
29+
Returns a coroutine that can be awaited to get the eventual result of *func*.
30+
"""
31+
loop = asyncio.events.get_running_loop()
32+
ctx = contextvars.copy_context()
33+
func_call = functools.partial(ctx.run, func, *args, **kwargs)
34+
return await loop.run_in_executor(None, func_call)
35+
36+
37+
# inspired by `asyncer`, https://github.com/tiangolo/asyncer
38+
def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]:
2339
"""
2440
Take a blocking function and create an async one that receives the same
25-
positional and keyword arguments, and that when called, calls the original function
26-
in a worker thread using `anyio.to_thread.run_sync()`. Internally,
27-
`asyncer.asyncify()` uses the same `anyio.to_thread.run_sync()`, but it supports
28-
keyword arguments additional to positional arguments and it adds better support for
29-
autocompletion and inline errors for the arguments of the function called and the
30-
return value.
31-
32-
If the `cancellable` option is enabled and the task waiting for its completion is
33-
cancelled, the thread will still run its course but its return value (or any raised
34-
exception) will be ignored.
41+
positional and keyword arguments. For python version 3.9 and above, it uses
42+
asyncio.to_thread to run the function in a separate thread. For python version
43+
3.8, it uses locally defined copy of the asyncio.to_thread function which was
44+
introduced in python 3.9.
3545
36-
Use it like this:
46+
Usage:
3747
38-
```Python
39-
def do_work(arg1, arg2, kwarg1="", kwarg2="") -> str:
40-
# Do work
41-
return "Some result"
48+
```python
49+
def blocking_func(arg1, arg2, kwarg1=None):
50+
# blocking code
51+
return result
4252
4353
44-
result = await to_thread.asyncify(do_work)("spam", "ham", kwarg1="a", kwarg2="b")
45-
print(result)
54+
result = asyncify(blocking_function)(arg1, arg2, kwarg1=value1)
4655
```
4756
4857
## Arguments
4958
5059
`function`: a blocking regular callable (e.g. a function)
51-
`cancellable`: `True` to allow cancellation of the operation
52-
`limiter`: capacity limiter to use to limit the total amount of threads running
53-
(if omitted, the default limiter is used)
5460
5561
## Return
5662
@@ -60,22 +66,6 @@ def do_work(arg1, arg2, kwarg1="", kwarg2="") -> str:
6066
"""
6167

6268
async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval:
63-
partial_f = functools.partial(function, *args, **kwargs)
64-
65-
# In `v4.1.0` anyio added the `abandon_on_cancel` argument and deprecated the old
66-
# `cancellable` argument, so we need to use the new `abandon_on_cancel` to avoid
67-
# surfacing deprecation warnings.
68-
if function_has_argument(anyio.to_thread.run_sync, "abandon_on_cancel"):
69-
return await anyio.to_thread.run_sync(
70-
partial_f,
71-
abandon_on_cancel=cancellable,
72-
limiter=limiter,
73-
)
74-
75-
return await anyio.to_thread.run_sync(
76-
partial_f,
77-
cancellable=cancellable,
78-
limiter=limiter,
79-
)
69+
return await to_thread(function, *args, **kwargs)
8070

8171
return wrapper

src/steel/_utils/_transform.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,11 @@ async def _async_transform_recursive(
316316
# Iterable[T]
317317
or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))
318318
):
319+
# dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually
320+
# intended as an iterable, so we don't transform it.
321+
if isinstance(data, dict):
322+
return cast(object, data)
323+
319324
inner_type = extract_type_arg(stripped_type, 0)
320325
return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]
321326

0 commit comments

Comments
 (0)