Skip to content

release: 1.10.0 #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
18 changes: 16 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,31 @@ jobs:
- name: Run lints
run: ./scripts/lint

upload:
build:
if: github.repository == 'stainless-sdks/knock-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork)
timeout-minutes: 10
name: upload
name: build
permissions:
contents: read
id-token: write
runs-on: depot-ubuntu-24.04
steps:
- uses: actions/checkout@v4

- name: Install Rye
run: |
curl -sSf https://rye.astral.sh/get | bash
echo "$HOME/.rye/shims" >> $GITHUB_PATH
env:
RYE_VERSION: '0.44.0'
RYE_INSTALL_OPTION: '--yes'

- name: Install dependencies
run: rye sync --all-features

- name: Run build
run: rye build

- name: Get GitHub OIDC Token
id: github-oidc
uses: actions/github-script@v6
Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "1.9.0"
".": "1.10.0"
}
6 changes: 3 additions & 3 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 89
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/knock%2Fknock-fa68e7353b6d2eb51af35279f2f591a7d6168c5ade69c3cf40b5aae119ebcce1.yml
openapi_spec_hash: f9315c4f1d89624aa2447499dbfe734c
config_hash: b4c547c1d4c8cd0834bc793ddf5388ee
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/knock%2Fknock-592610bb4a05ea45115eba7544acd8efbab327749d0f78bd83e164aa305dc0a7.yml
openapi_spec_hash: 6a65b9127625d9479ba6bff2ba3f8d37
config_hash: c835f0912492c3f1189f78f876c7c90c
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# Changelog

## 1.10.0 (2025-07-15)

Full Changelog: [v1.9.0...v1.10.0](https://github.com/knocklabs/knock-python/compare/v1.9.0...v1.10.0)

### Features

* **api:** api update ([7673085](https://github.com/knocklabs/knock-python/commit/7673085795939d4ce7e23cf0f2926fa8c6a08978))
* **api:** api update ([b9f028b](https://github.com/knocklabs/knock-python/commit/b9f028b69089460aa522b023a272182803b6143c))
* clean up environment call outs ([fb2f0d5](https://github.com/knocklabs/knock-python/commit/fb2f0d522f82069c40d931fe26722fc52be70967))


### Bug Fixes

* **client:** don't send Content-Type header on GET requests ([64799d2](https://github.com/knocklabs/knock-python/commit/64799d2f0f8288c23e86ed7df45eeb211866ac0b))
* **parsing:** correctly handle nested discriminated unions ([e8e1932](https://github.com/knocklabs/knock-python/commit/e8e1932417093bf612a0705cc7fada4a980f3a39))


### Chores

* **ci:** change upload type ([e71a7c5](https://github.com/knocklabs/knock-python/commit/e71a7c512b98a9910e1596462504105cf3e9d58d))
* **internal:** bump pinned h11 dep ([f2b5336](https://github.com/knocklabs/knock-python/commit/f2b53364b5dd9cfcc15bea2204dc42bc6e49d0f6))
* **package:** mark python 3.13 as supported ([ee5b244](https://github.com/knocklabs/knock-python/commit/ee5b244a23c584a6ccd61e85c56bb9e3e85dcb8d))
* **readme:** fix version rendering on pypi ([a2fae26](https://github.com/knocklabs/knock-python/commit/a2fae26e5dfa3c003f3f1cf2b45bf55fe47db724))

## 1.9.0 (2025-06-30)

Full Changelog: [v1.8.1...v1.9.0](https://github.com/knocklabs/knock-python/compare/v1.8.1...v1.9.0)
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Knock Python API library

[![PyPI version](<https://img.shields.io/pypi/v/knockapi.svg?label=pypi%20(stable)>)](https://pypi.org/project/knockapi/)
<!-- prettier-ignore -->
[![PyPI version](https://img.shields.io/pypi/v/knockapi.svg?label=pypi%20(stable))](https://pypi.org/project/knockapi/)

The Knock Python library provides convenient access to the Knock REST API from any Python 3.8+
application. The library includes type definitions for all request params and response fields,
Expand Down Expand Up @@ -86,15 +87,14 @@ pip install knockapi[aiohttp]
Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:

```python
import os
import asyncio
from knockapi import DefaultAioHttpClient
from knockapi import AsyncKnock


async def main() -> None:
async with AsyncKnock(
api_key=os.environ.get("KNOCK_API_KEY"), # This is the default and can be omitted
api_key="My API Key",
http_client=DefaultAioHttpClient(),
) as client:
response = await client.workflows.trigger(
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "knockapi"
version = "1.9.0"
version = "1.10.0"
description = "The official Python library for the knock API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand All @@ -24,6 +24,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Operating System :: OS Independent",
"Operating System :: POSIX",
"Operating System :: MacOS",
Expand All @@ -38,7 +39,7 @@ Homepage = "https://github.com/knocklabs/knock-python"
Repository = "https://github.com/knocklabs/knock-python"

[project.optional-dependencies]
aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.6"]
aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.8"]

[tool.rye]
managed = true
Expand Down
6 changes: 3 additions & 3 deletions requirements-dev.lock
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ filelock==3.12.4
frozenlist==1.6.2
# via aiohttp
# via aiosignal
h11==0.14.0
h11==0.16.0
# via httpcore
httpcore==1.0.2
httpcore==1.0.9
# via httpx
httpx==0.28.1
# via httpx-aiohttp
# via knockapi
# via respx
httpx-aiohttp==0.1.6
httpx-aiohttp==0.1.8
# via knockapi
idna==3.4
# via anyio
Expand Down
6 changes: 3 additions & 3 deletions requirements.lock
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ exceptiongroup==1.2.2
frozenlist==1.6.2
# via aiohttp
# via aiosignal
h11==0.14.0
h11==0.16.0
# via httpcore
httpcore==1.0.2
httpcore==1.0.9
# via httpx
httpx==0.28.1
# via httpx-aiohttp
# via knockapi
httpx-aiohttp==0.1.6
httpx-aiohttp==0.1.8
# via knockapi
idna==3.4
# via anyio
Expand Down
12 changes: 7 additions & 5 deletions scripts/utils/upload-artifact.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#!/usr/bin/env bash
set -exuo pipefail

RESPONSE=$(curl -X POST "$URL" \
FILENAME=$(basename dist/*.whl)

RESPONSE=$(curl -X POST "$URL?filename=$FILENAME" \
-H "Authorization: Bearer $AUTH" \
-H "Content-Type: application/json")

Expand All @@ -12,13 +14,13 @@ if [[ "$SIGNED_URL" == "null" ]]; then
exit 1
fi

UPLOAD_RESPONSE=$(tar -cz . | curl -v -X PUT \
-H "Content-Type: application/gzip" \
--data-binary @- "$SIGNED_URL" 2>&1)
UPLOAD_RESPONSE=$(curl -v -X PUT \
-H "Content-Type: binary/octet-stream" \
--data-binary "@dist/$FILENAME" "$SIGNED_URL" 2>&1)

if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then
echo -e "\033[32mUploaded build to Stainless storage.\033[0m"
echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/knock-python/$SHA'\033[0m"
echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/knock-python/$SHA/$FILENAME'\033[0m"
else
echo -e "\033[31mFailed to upload artifact.\033[0m"
exit 1
Expand Down
11 changes: 9 additions & 2 deletions src/knockapi/_base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,15 @@ def _build_request(
# work around https://github.com/encode/httpx/discussions/2880
kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")}

is_body_allowed = options.method.lower() != "get"

if is_body_allowed:
kwargs["json"] = json_data if is_given(json_data) else None
kwargs["files"] = files
else:
headers.pop("Content-Type", None)
kwargs.pop("data", None)

# TODO: report this error to httpx
return self._client.build_request( # pyright: ignore[reportUnknownMemberType]
headers=headers,
Expand All @@ -540,8 +549,6 @@ def _build_request(
# so that passing a `TypedDict` doesn't cause an error.
# https://github.com/microsoft/pyright/issues/3526#event-6715453066
params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None,
json=json_data if is_given(json_data) else None,
files=files,
**kwargs,
)

Expand Down
13 changes: 8 additions & 5 deletions src/knockapi/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import os
import inspect
from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, cast
from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast
from datetime import date, datetime
from typing_extensions import (
List,
Unpack,
Literal,
ClassVar,
Expand Down Expand Up @@ -366,7 +367,7 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object:
if type_ is None:
raise RuntimeError(f"Unexpected field type is None for {key}")

return construct_type(value=value, type_=type_)
return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None))


def is_basemodel(type_: type) -> bool:
Expand Down Expand Up @@ -420,7 +421,7 @@ def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T:
return cast(_T, construct_type(value=value, type_=type_))


def construct_type(*, value: object, type_: object) -> object:
def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]] = None) -> object:
"""Loose coercion to the expected type with construction of nested values.

If the given value does not match the expected type then it is returned as-is.
Expand All @@ -438,8 +439,10 @@ def construct_type(*, value: object, type_: object) -> object:
type_ = type_.__value__ # type: ignore[unreachable]

# unwrap `Annotated[T, ...]` -> `T`
if is_annotated_type(type_):
meta: tuple[Any, ...] = get_args(type_)[1:]
if metadata is not None:
meta: tuple[Any, ...] = tuple(metadata)
elif is_annotated_type(type_):
meta = get_args(type_)[1:]
type_ = extract_type_arg(type_, 0)
else:
meta = tuple()
Expand Down
2 changes: 1 addition & 1 deletion src/knockapi/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "knockapi"
__version__ = "1.9.0" # x-release-please-version
__version__ = "1.10.0" # x-release-please-version
32 changes: 16 additions & 16 deletions src/knockapi/resources/schedules/schedules.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ def create(
(string), an inline user request (object), or an inline object request, which is
determined by the presence of a `collection` property.

data: An optional map of data to pass into the workflow execution. There is a 1024
byte limit on the size of any single string value (with the exception of
[email attachments](/integrations/email/attachments)), and a 10MB limit on the
size of the full `data` payload.
data: An optional map of data to pass into the workflow execution. There is a 10MB
limit on the size of the full `data` payload. Any individual string value
greater than 1024 bytes in length will be
[truncated](/developer-tools/api-logs#log-truncation) in your logs.

ending_at: The ending date and time for the schedule.

Expand Down Expand Up @@ -175,10 +175,10 @@ def update(
actor: A reference to a recipient, either a user identifier (string) or an object
reference (ID, collection).

data: An optional map of data to pass into the workflow execution. There is a 1024
byte limit on the size of any single string value (with the exception of
[email attachments](/integrations/email/attachments)), and a 10MB limit on the
size of the full `data` payload.
data: An optional map of data to pass into the workflow execution. There is a 10MB
limit on the size of the full `data` payload. Any individual string value
greater than 1024 bytes in length will be
[truncated](/developer-tools/api-logs#log-truncation) in your logs.

ending_at: The ending date and time for the schedule.

Expand Down Expand Up @@ -374,10 +374,10 @@ async def create(
(string), an inline user request (object), or an inline object request, which is
determined by the presence of a `collection` property.

data: An optional map of data to pass into the workflow execution. There is a 1024
byte limit on the size of any single string value (with the exception of
[email attachments](/integrations/email/attachments)), and a 10MB limit on the
size of the full `data` payload.
data: An optional map of data to pass into the workflow execution. There is a 10MB
limit on the size of the full `data` payload. Any individual string value
greater than 1024 bytes in length will be
[truncated](/developer-tools/api-logs#log-truncation) in your logs.

ending_at: The ending date and time for the schedule.

Expand Down Expand Up @@ -446,10 +446,10 @@ async def update(
actor: A reference to a recipient, either a user identifier (string) or an object
reference (ID, collection).

data: An optional map of data to pass into the workflow execution. There is a 1024
byte limit on the size of any single string value (with the exception of
[email attachments](/integrations/email/attachments)), and a 10MB limit on the
size of the full `data` payload.
data: An optional map of data to pass into the workflow execution. There is a 10MB
limit on the size of the full `data` payload. Any individual string value
greater than 1024 bytes in length will be
[truncated](/developer-tools/api-logs#log-truncation) in your logs.

ending_at: The ending date and time for the schedule.

Expand Down
16 changes: 8 additions & 8 deletions src/knockapi/resources/workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ def trigger(
subsequent cancellation. Should be unique across trigger requests to avoid
unintentional cancellations.

data: An optional map of data to pass into the workflow execution. There is a 1024
byte limit on the size of any single string value (with the exception of
[email attachments](/integrations/email/attachments)), and a 10MB limit on the
size of the full `data` payload.
data: An optional map of data to pass into the workflow execution. There is a 10MB
limit on the size of the full `data` payload. Any individual string value
greater than 1024 bytes in length will be
[truncated](/developer-tools/api-logs#log-truncation) in your logs.

tenant: An request to set a tenant inline.

Expand Down Expand Up @@ -282,10 +282,10 @@ async def trigger(
subsequent cancellation. Should be unique across trigger requests to avoid
unintentional cancellations.

data: An optional map of data to pass into the workflow execution. There is a 1024
byte limit on the size of any single string value (with the exception of
[email attachments](/integrations/email/attachments)), and a 10MB limit on the
size of the full `data` payload.
data: An optional map of data to pass into the workflow execution. There is a 10MB
limit on the size of the full `data` payload. Any individual string value
greater than 1024 bytes in length will be
[truncated](/developer-tools/api-logs#log-truncation) in your logs.

tenant: An request to set a tenant inline.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@ class QueryOptions(TypedDict, total=False):
"""
Mix and match channel types by providing a comma-separated list of any
combination of public_channel, private_channel, mpim, im. Defaults to
`"public_channel,private_channel"`.
`"public_channel,private_channel"`. If the user's Slack ID is unavailable, this
option is ignored and only public channels are returned.
"""
6 changes: 3 additions & 3 deletions src/knockapi/types/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ class Schedule(BaseModel):
data: Optional[Dict[str, object]] = None
"""An optional map of data to pass into the workflow execution.

There is a 1024 byte limit on the size of any single string value (with the
exception of [email attachments](/integrations/email/attachments)), and a 10MB
limit on the size of the full `data` payload.
There is a 10MB limit on the size of the full `data` payload. Any individual
string value greater than 1024 bytes in length will be
[truncated](/developer-tools/api-logs#log-truncation) in your logs.
"""

last_occurrence_at: Optional[datetime] = None
Expand Down
6 changes: 3 additions & 3 deletions src/knockapi/types/schedule_create_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ class ScheduleCreateParams(TypedDict, total=False):
data: Optional[Dict[str, object]]
"""An optional map of data to pass into the workflow execution.

There is a 1024 byte limit on the size of any single string value (with the
exception of [email attachments](/integrations/email/attachments)), and a 10MB
limit on the size of the full `data` payload.
There is a 10MB limit on the size of the full `data` payload. Any individual
string value greater than 1024 bytes in length will be
[truncated](/developer-tools/api-logs#log-truncation) in your logs.
"""

ending_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")]
Expand Down
Loading