Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.109.1"
".": "0.110.0"
}
8 changes: 4 additions & 4 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 168
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-6432801dcf017d9234739b9f6153a8cfecd91255af4765f24ba5209f3e320b5c.yml
openapi_spec_hash: c2cfee2f044ba15d15b5bfdbd37280c0
config_hash: 8ac73ccdb428816c89c84802c794a3f8
configured_endpoints: 167
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-c3f6c23018d70fd1b259b21cfd377b5d905872f0d3c5ce2cdb8013f4b6daa338.yml
openapi_spec_hash: b551344da9d29eb4c5374874ed84a9b0
config_hash: 012f050e575d1bbfe8db56a9eeaa5fcd
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
# Changelog

## 0.110.0 (2025-11-11)

Full Changelog: [v0.109.1...v0.110.0](https://github.com/lithic-com/lithic-python/compare/v0.109.1...v0.110.0)

### Features

* **api:** add payment_details ([59653f5](https://github.com/lithic-com/lithic-python/commit/59653f5db08fe87d8c8f97b34c1edac575996e19))
* **api:** deprecate auth rule apply endpoint and fix several schemas ([2ccbc8e](https://github.com/lithic-com/lithic-python/commit/2ccbc8e1defce005fcf32d22aa68b8d98af68222))
* **api:** new fields in Statements APIs ([79aa803](https://github.com/lithic-com/lithic-python/commit/79aa803f7e6a30a3d6383f5bca21d7c4c955b6e9))
* **api:** re-add rules metadata to tokenization events ([7e4b9a7](https://github.com/lithic-com/lithic-python/commit/7e4b9a750be5431be81a74a4b44c7a1f872ebf5d))
* **api:** remove deprecated fields from Cardholder Authentication object ([7e4b9a7](https://github.com/lithic-com/lithic-python/commit/7e4b9a750be5431be81a74a4b44c7a1f872ebf5d))


### Bug Fixes

* **api:** adjust Auth Rules spec for better SDK structure ([7e4b9a7](https://github.com/lithic-com/lithic-python/commit/7e4b9a750be5431be81a74a4b44c7a1f872ebf5d))
* **api:** fix oneOf -> anyOf in account holder update ([59653f5](https://github.com/lithic-com/lithic-python/commit/59653f5db08fe87d8c8f97b34c1edac575996e19))
* **api:** fixing spec for Tokenizations and Enhanced data ([79aa803](https://github.com/lithic-com/lithic-python/commit/79aa803f7e6a30a3d6383f5bca21d7c4c955b6e9))
* compat with Python 3.14 ([b56a588](https://github.com/lithic-com/lithic-python/commit/b56a588a807420f848c39e445ea6e39034c2f88f))


### Chores

* **api:** adds support for new ACH_RECEIPT_RELEASED event ([30908f3](https://github.com/lithic-com/lithic-python/commit/30908f3f1d72c38d57239415d2a093dbdaee4f96))
* **examples:** fix type checking ([6a138be](https://github.com/lithic-com/lithic-python/commit/6a138beaf9ec36f3db3cb89e8b653ef2e34f4de4))
* **internal/tests:** avoid race condition with implicit client cleanup ([e90a25d](https://github.com/lithic-com/lithic-python/commit/e90a25da2bb81f93978555a8f671866fbe696235))
* **internal:** grammar fix (it's -> its) ([091851d](https://github.com/lithic-com/lithic-python/commit/091851d9a940925c83d30cb98470ff22b7bb9de2))
* **package:** drop Python 3.8 support ([80a0e1f](https://github.com/lithic-com/lithic-python/commit/80a0e1f29373041cc63d7acffa37d91a5c1a66b0))

## 0.109.1 (2025-10-29)

Full Changelog: [v0.109.0...v0.109.1](https://github.com/lithic-com/lithic-python/compare/v0.109.0...v0.109.1)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<!-- prettier-ignore -->
[![PyPI version](https://img.shields.io/pypi/v/lithic.svg?label=pypi%20(stable))](https://pypi.org/project/lithic/)

The Lithic Python library provides convenient access to the Lithic REST API from any Python 3.8+
The Lithic Python library provides convenient access to the Lithic REST API from any Python 3.9+
application. The library includes type definitions for all request params and response fields,
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).

Expand Down Expand Up @@ -481,7 +481,7 @@ print(lithic.__version__)

## Requirements

Python 3.8 or higher.
Python 3.9 or higher.

## Contributing

Expand Down
25 changes: 16 additions & 9 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,16 @@ from lithic.types.auth_rules import (
AuthRuleCondition,
Conditional3DSActionParameters,
ConditionalAttribute,
ConditionalAuthorizationActionParameters,
ConditionalBlockParameters,
MerchantLockParameters,
RuleStats,
VelocityLimitParams,
VelocityLimitParamsPeriodWindow,
VelocityLimitPeriod,
V2CreateResponse,
V2RetrieveResponse,
V2UpdateResponse,
V2ListResponse,
V2ApplyResponse,
V2DraftResponse,
V2PromoteResponse,
V2RetrieveFeaturesResponse,
Expand All @@ -107,7 +107,6 @@ Methods:
- <code title="patch /v2/auth_rules/{auth_rule_token}">client.auth_rules.v2.<a href="./src/lithic/resources/auth_rules/v2/v2.py">update</a>(auth_rule_token, \*\*<a href="src/lithic/types/auth_rules/v2_update_params.py">params</a>) -> <a href="./src/lithic/types/auth_rules/v2_update_response.py">V2UpdateResponse</a></code>
- <code title="get /v2/auth_rules">client.auth_rules.v2.<a href="./src/lithic/resources/auth_rules/v2/v2.py">list</a>(\*\*<a href="src/lithic/types/auth_rules/v2_list_params.py">params</a>) -> <a href="./src/lithic/types/auth_rules/v2_list_response.py">SyncCursorPage[V2ListResponse]</a></code>
- <code title="delete /v2/auth_rules/{auth_rule_token}">client.auth_rules.v2.<a href="./src/lithic/resources/auth_rules/v2/v2.py">delete</a>(auth_rule_token) -> None</code>
- <code title="post /v2/auth_rules/{auth_rule_token}/apply">client.auth_rules.v2.<a href="./src/lithic/resources/auth_rules/v2/v2.py">apply</a>(auth_rule_token, \*\*<a href="src/lithic/types/auth_rules/v2_apply_params.py">params</a>) -> <a href="./src/lithic/types/auth_rules/v2_apply_response.py">V2ApplyResponse</a></code>
- <code title="post /v2/auth_rules/{auth_rule_token}/draft">client.auth_rules.v2.<a href="./src/lithic/resources/auth_rules/v2/v2.py">draft</a>(auth_rule_token, \*\*<a href="src/lithic/types/auth_rules/v2_draft_params.py">params</a>) -> <a href="./src/lithic/types/auth_rules/v2_draft_response.py">V2DraftResponse</a></code>
- <code title="post /v2/auth_rules/{auth_rule_token}/promote">client.auth_rules.v2.<a href="./src/lithic/resources/auth_rules/v2/v2.py">promote</a>(auth_rule_token) -> <a href="./src/lithic/types/auth_rules/v2_promote_response.py">V2PromoteResponse</a></code>
- <code title="get /v2/auth_rules/{auth_rule_token}/features">client.auth_rules.v2.<a href="./src/lithic/resources/auth_rules/v2/v2.py">retrieve_features</a>(auth_rule_token, \*\*<a href="src/lithic/types/auth_rules/v2_retrieve_features_params.py">params</a>) -> <a href="./src/lithic/types/auth_rules/v2_retrieve_features_response.py">V2RetrieveFeaturesResponse</a></code>
Expand Down Expand Up @@ -270,16 +269,24 @@ Methods:
Types:

```python
from lithic.types import Dispute, DisputeEvidence
from lithic.types import (
Dispute,
DisputeEvidence,
DisputeCreateResponse,
DisputeRetrieveResponse,
DisputeUpdateResponse,
DisputeListResponse,
DisputeDeleteResponse,
)
```

Methods:

- <code title="post /v1/disputes">client.disputes.<a href="./src/lithic/resources/disputes.py">create</a>(\*\*<a href="src/lithic/types/dispute_create_params.py">params</a>) -> <a href="./src/lithic/types/dispute.py">Dispute</a></code>
- <code title="get /v1/disputes/{dispute_token}">client.disputes.<a href="./src/lithic/resources/disputes.py">retrieve</a>(dispute_token) -> <a href="./src/lithic/types/dispute.py">Dispute</a></code>
- <code title="patch /v1/disputes/{dispute_token}">client.disputes.<a href="./src/lithic/resources/disputes.py">update</a>(dispute_token, \*\*<a href="src/lithic/types/dispute_update_params.py">params</a>) -> <a href="./src/lithic/types/dispute.py">Dispute</a></code>
- <code title="get /v1/disputes">client.disputes.<a href="./src/lithic/resources/disputes.py">list</a>(\*\*<a href="src/lithic/types/dispute_list_params.py">params</a>) -> <a href="./src/lithic/types/dispute.py">SyncCursorPage[Dispute]</a></code>
- <code title="delete /v1/disputes/{dispute_token}">client.disputes.<a href="./src/lithic/resources/disputes.py">delete</a>(dispute_token) -> <a href="./src/lithic/types/dispute.py">Dispute</a></code>
- <code title="post /v1/disputes">client.disputes.<a href="./src/lithic/resources/disputes.py">create</a>(\*\*<a href="src/lithic/types/dispute_create_params.py">params</a>) -> <a href="./src/lithic/types/dispute_create_response.py">DisputeCreateResponse</a></code>
- <code title="get /v1/disputes/{dispute_token}">client.disputes.<a href="./src/lithic/resources/disputes.py">retrieve</a>(dispute_token) -> <a href="./src/lithic/types/dispute_retrieve_response.py">DisputeRetrieveResponse</a></code>
- <code title="patch /v1/disputes/{dispute_token}">client.disputes.<a href="./src/lithic/resources/disputes.py">update</a>(dispute_token, \*\*<a href="src/lithic/types/dispute_update_params.py">params</a>) -> <a href="./src/lithic/types/dispute_update_response.py">DisputeUpdateResponse</a></code>
- <code title="get /v1/disputes">client.disputes.<a href="./src/lithic/resources/disputes.py">list</a>(\*\*<a href="src/lithic/types/dispute_list_params.py">params</a>) -> <a href="./src/lithic/types/dispute_list_response.py">SyncCursorPage[DisputeListResponse]</a></code>
- <code title="delete /v1/disputes/{dispute_token}">client.disputes.<a href="./src/lithic/resources/disputes.py">delete</a>(dispute_token) -> <a href="./src/lithic/types/dispute_delete_response.py">DisputeDeleteResponse</a></code>
- <code title="delete /v1/disputes/{dispute_token}/evidences/{evidence_token}">client.disputes.<a href="./src/lithic/resources/disputes.py">delete_evidence</a>(evidence_token, \*, dispute_token) -> <a href="./src/lithic/types/dispute_evidence.py">DisputeEvidence</a></code>
- <code title="post /v1/disputes/{dispute_token}/evidences">client.disputes.<a href="./src/lithic/resources/disputes.py">initiate_evidence_upload</a>(dispute_token, \*\*<a href="src/lithic/types/dispute_initiate_evidence_upload_params.py">params</a>) -> <a href="./src/lithic/types/dispute_evidence.py">DisputeEvidence</a></code>
- <code title="get /v1/disputes/{dispute_token}/evidences">client.disputes.<a href="./src/lithic/resources/disputes.py">list_evidences</a>(dispute_token, \*\*<a href="src/lithic/types/dispute_list_evidences_params.py">params</a>) -> <a href="./src/lithic/types/dispute_evidence.py">SyncCursorPage[DisputeEvidence]</a></code>
Expand Down
7 changes: 4 additions & 3 deletions examples/upload_evidence.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#!/usr/bin/env -S rye run python

from __future__ import annotations

# To run this example locally
# 1. Install Rye and setup a Python virtual environment: ./scripts/bootstrap
# 2. Run the example: LITHIC_API_KEY=<your_api_key> rye run python examples/upload_evidence.py


from lithic import Lithic, file_from_path
from lithic.types import DisputeListResponse, DisputeCreateResponse

client = Lithic(environment="sandbox")

Expand All @@ -16,7 +17,7 @@
assert transaction.token, "Transaction must have a token"

disputes_page = client.disputes.list()
dispute = disputes_page.data[0]
dispute: DisputeCreateResponse | DisputeListResponse | None = disputes_page.data[0] if disputes_page.data else None
if not dispute:
dispute = client.disputes.create(
amount=42,
Expand Down
7 changes: 3 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "lithic"
version = "0.109.1"
version = "0.110.0"
description = "The official Python library for the lithic API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand All @@ -15,11 +15,10 @@ dependencies = [
"distro>=1.7.0, <2",
"sniffio",
]
requires-python = ">= 3.8"
requires-python = ">= 3.9"
classifiers = [
"Typing :: Typed",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand Down Expand Up @@ -141,7 +140,7 @@ filterwarnings = [
# there are a couple of flags that are still disabled by
# default in strict mode as they are experimental and niche.
typeCheckingMode = "strict"
pythonVersion = "3.8"
pythonVersion = "3.9"

exclude = [
"_dev",
Expand Down
11 changes: 8 additions & 3 deletions src/lithic/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import os
import inspect
import weakref
from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast
from datetime import date, datetime
from typing_extensions import (
Expand Down Expand Up @@ -573,6 +574,9 @@ class CachedDiscriminatorType(Protocol):
__discriminator__: DiscriminatorDetails


DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary()


class DiscriminatorDetails:
field_name: str
"""The name of the discriminator field in the variant class, e.g.
Expand Down Expand Up @@ -615,8 +619,9 @@ def __init__(


def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None:
if isinstance(union, CachedDiscriminatorType):
return union.__discriminator__
cached = DISCRIMINATOR_CACHE.get(union)
if cached is not None:
return cached

discriminator_field_name: str | None = None

Expand Down Expand Up @@ -669,7 +674,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any,
discriminator_field=discriminator_field_name,
discriminator_alias=discriminator_alias,
)
cast(CachedDiscriminatorType, union).__discriminator__ = details
DISCRIMINATOR_CACHE.setdefault(union, details)
return details


Expand Down
34 changes: 3 additions & 31 deletions src/lithic/_utils/_sync.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from __future__ import annotations

import sys
import asyncio
import functools
import contextvars
from typing import Any, TypeVar, Callable, Awaitable
from typing import TypeVar, Callable, Awaitable
from typing_extensions import ParamSpec

import anyio
Expand All @@ -15,34 +13,11 @@
T_ParamSpec = ParamSpec("T_ParamSpec")


if sys.version_info >= (3, 9):
_asyncio_to_thread = asyncio.to_thread
else:
# backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread
# for Python 3.8 support
async def _asyncio_to_thread(
func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
) -> Any:
"""Asynchronously run function *func* in a separate thread.

Any *args and **kwargs supplied for this function are directly passed
to *func*. Also, the current :class:`contextvars.Context` is propagated,
allowing context variables from the main thread to be accessed in the
separate thread.

Returns a coroutine that can be awaited to get the eventual result of *func*.
"""
loop = asyncio.events.get_running_loop()
ctx = contextvars.copy_context()
func_call = functools.partial(ctx.run, func, *args, **kwargs)
return await loop.run_in_executor(None, func_call)


async def to_thread(
func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
) -> T_Retval:
if sniffio.current_async_library() == "asyncio":
return await _asyncio_to_thread(func, *args, **kwargs)
return await asyncio.to_thread(func, *args, **kwargs)

return await anyio.to_thread.run_sync(
functools.partial(func, *args, **kwargs),
Expand All @@ -53,10 +28,7 @@ async def to_thread(
def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]:
"""
Take a blocking function and create an async one that receives the same
positional and keyword arguments. For python version 3.9 and above, it uses
asyncio.to_thread to run the function in a separate thread. For python version
3.8, it uses locally defined copy of the asyncio.to_thread function which was
introduced in python 3.9.
positional and keyword arguments.

Usage:

Expand Down
2 changes: 1 addition & 1 deletion src/lithic/_utils/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]:
# Type safe methods for narrowing types with TypeVars.
# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown],
# however this cause Pyright to rightfully report errors. As we know we don't
# care about the contained types we can safely use `object` in it's place.
# care about the contained types we can safely use `object` in its place.
#
# There are two separate functions defined, `is_*` and `is_*_t` for different use cases.
# `is_*` is for when you're dealing with an unknown input
Expand Down
2 changes: 1 addition & 1 deletion src/lithic/_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__ = "lithic"
__version__ = "0.109.1" # x-release-please-version
__version__ = "0.110.0" # x-release-please-version
Loading