From 2d4a30edc2f84fc7effc9b7719aae8ed51646338 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 21 May 2025 15:13:12 +0000
Subject: [PATCH 01/29] chore(docs): grammar improvements
---
SECURITY.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/SECURITY.md b/SECURITY.md
index b6499508..1e432e1e 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -16,11 +16,11 @@ before making any information public.
## Reporting Non-SDK Related Security Issues
If you encounter security issues that are not directly related to SDKs but pertain to the services
-or products provided by Finch please follow the respective company's security reporting guidelines.
+or products provided by Finch, please follow the respective company's security reporting guidelines.
### Finch Terms and Policies
-Please contact founders@tryfinch.com for any questions or concerns regarding security of our services.
+Please contact founders@tryfinch.com for any questions or concerns regarding the security of our services.
---
From b95ac1971a10372d70fc7ccdb3cf63f4411e9d2e Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 22 May 2025 14:04:45 +0000
Subject: [PATCH 02/29] feat(api): api update
---
.stats.yml | 4 ++--
src/finch/resources/connect/sessions.py | 8 ++++----
src/finch/types/connect/session_new_params.py | 4 ++--
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index eecdfce3..fca506d9 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 46
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-944a0f9d35f3b8ec2ba62fa12e551cf89f0b845f8ed1e3c7f67a9fb80b32d96f.yml
-openapi_spec_hash: 37c849e7b5dd941c011385b49467e077
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-4fcae6958da081ca0e96ef6b3ce8f0b6e50994faaed8ecd6e94aa40ce1a93521.yml
+openapi_spec_hash: a825b1120bb26f04505e3bc9ab1e48f3
config_hash: 53778a0b839c4f6ad34fbba051f5e8a6
diff --git a/src/finch/resources/connect/sessions.py b/src/finch/resources/connect/sessions.py
index 80c13640..c88c3a67 100644
--- a/src/finch/resources/connect/sessions.py
+++ b/src/finch/resources/connect/sessions.py
@@ -77,8 +77,8 @@ def new(
Create a new connect session for an employer
Args:
- minutes_to_expire: The number of minutes until the session expires (defaults to 43,200, which is 30
- days)
+ minutes_to_expire: The number of minutes until the session expires (defaults to 129,600, which is
+ 90 days)
extra_headers: Send extra headers
@@ -235,8 +235,8 @@ async def new(
Create a new connect session for an employer
Args:
- minutes_to_expire: The number of minutes until the session expires (defaults to 43,200, which is 30
- days)
+ minutes_to_expire: The number of minutes until the session expires (defaults to 129,600, which is
+ 90 days)
extra_headers: Send extra headers
diff --git a/src/finch/types/connect/session_new_params.py b/src/finch/types/connect/session_new_params.py
index 5434176a..f49dc752 100644
--- a/src/finch/types/connect/session_new_params.py
+++ b/src/finch/types/connect/session_new_params.py
@@ -38,8 +38,8 @@ class SessionNewParams(TypedDict, total=False):
minutes_to_expire: Optional[float]
"""
- The number of minutes until the session expires (defaults to 43,200, which is 30
- days)
+ The number of minutes until the session expires (defaults to 129,600, which is
+ 90 days)
"""
redirect_uri: Optional[str]
From a31325e6eeb04cfa725ceee5637b869dd02be1a8 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 22 May 2025 19:37:34 +0000
Subject: [PATCH 03/29] feat(api): api update
---
.stats.yml | 4 +-
api.md | 2 +-
src/finch/types/hris/__init__.py | 1 -
src/finch/types/hris/benefit_type.py | 20 +-
.../pay_statement_item_list_response.py | 18 +-
src/finch/types/hris/employment_data.py | 32 +--
src/finch/types/hris/pay_statement.py | 198 ---------------
.../types/hris/pay_statement_response.py | 8 +-
.../types/hris/pay_statement_response_body.py | 240 +++++++++++++++++-
src/finch/types/hris/payment.py | 12 +-
src/finch/types/money.py | 2 +-
src/finch/types/money_param.py | 6 +-
.../types/payroll/pay_group_list_response.py | 24 +-
.../payroll/pay_group_retrieve_response.py | 2 +-
.../types/sandbox/payment_create_params.py | 130 +++++-----
tests/api_resources/hris/test_benefits.py | 4 +-
tests/api_resources/sandbox/test_payment.py | 52 ++--
17 files changed, 385 insertions(+), 370 deletions(-)
delete mode 100644 src/finch/types/hris/pay_statement.py
diff --git a/.stats.yml b/.stats.yml
index fca506d9..f17b73c9 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 46
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-4fcae6958da081ca0e96ef6b3ce8f0b6e50994faaed8ecd6e94aa40ce1a93521.yml
-openapi_spec_hash: a825b1120bb26f04505e3bc9ab1e48f3
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-5b7ee4cddd08558fb0cc5010d36899c0bbe5d6a8d39c8171f65b70b68927df3c.yml
+openapi_spec_hash: c01482dc89e9bf7ae4c9dad0fb547e62
config_hash: 53778a0b839c4f6ad34fbba051f5e8a6
diff --git a/api.md b/api.md
index 1e750c7c..2ba59c1f 100644
--- a/api.md
+++ b/api.md
@@ -128,7 +128,7 @@ Methods:
Types:
```python
-from finch.types.hris import PayStatement, PayStatementResponse, PayStatementResponseBody
+from finch.types.hris import PayStatementResponse, PayStatementResponseBody
```
Methods:
diff --git a/src/finch/types/hris/__init__.py b/src/finch/types/hris/__init__.py
index 4f9edc30..82434d01 100644
--- a/src/finch/types/hris/__init__.py
+++ b/src/finch/types/hris/__init__.py
@@ -8,7 +8,6 @@
from .payment import Payment as Payment
from .individual import Individual as Individual
from .benefit_type import BenefitType as BenefitType
-from .pay_statement import PayStatement as PayStatement
from .company_benefit import CompanyBenefit as CompanyBenefit
from .employment_data import EmploymentData as EmploymentData
from .benefits_support import BenefitsSupport as BenefitsSupport
diff --git a/src/finch/types/hris/benefit_type.py b/src/finch/types/hris/benefit_type.py
index 2c8ad7f4..9f5b4132 100644
--- a/src/finch/types/hris/benefit_type.py
+++ b/src/finch/types/hris/benefit_type.py
@@ -7,24 +7,24 @@
BenefitType: TypeAlias = Optional[
Literal[
+ "457",
"401k",
"401k_roth",
"401k_loan",
"403b",
"403b_roth",
- "457",
"457_roth",
- "s125_medical",
- "s125_dental",
- "s125_vision",
- "hsa_pre",
- "hsa_post",
- "fsa_medical",
- "fsa_dependent_care",
- "simple_ira",
- "simple",
"commuter",
"custom_post_tax",
"custom_pre_tax",
+ "fsa_dependent_care",
+ "fsa_medical",
+ "hsa_post",
+ "hsa_pre",
+ "s125_dental",
+ "s125_medical",
+ "s125_vision",
+ "simple",
+ "simple_ira",
]
]
diff --git a/src/finch/types/hris/company/pay_statement_item_list_response.py b/src/finch/types/hris/company/pay_statement_item_list_response.py
index 31e9cf16..e2ca25b0 100644
--- a/src/finch/types/hris/company/pay_statement_item_list_response.py
+++ b/src/finch/types/hris/company/pay_statement_item_list_response.py
@@ -9,18 +9,18 @@
class Attributes(BaseModel):
- employer: Optional[bool] = None
- """`true` if the amount is paid by the employers.
-
- This field is only available for taxes.
- """
-
metadata: Optional[Dict[str, Optional[object]]] = None
"""The metadata of the pay statement item derived by the rules engine if available.
Each attribute will be a key-value pair defined by a rule.
"""
+ employer: Optional[bool] = None
+ """`true` if the amount is paid by the employers.
+
+ This field is only available for taxes.
+ """
+
pre_tax: Optional[bool] = None
"""`true` if the pay statement item is pre-tax.
@@ -32,11 +32,11 @@ class Attributes(BaseModel):
class PayStatementItemListResponse(BaseModel):
- attributes: Optional[Attributes] = None
+ attributes: Attributes
"""The attributes of the pay statement item."""
- category: Optional[Literal["earnings", "taxes", "employee_deductions", "employer_contributions"]] = None
+ category: Literal["earnings", "taxes", "employee_deductions", "employer_contributions"]
"""The category of the pay statement item."""
- name: Optional[str] = None
+ name: str
"""The name of the pay statement item."""
diff --git a/src/finch/types/hris/employment_data.py b/src/finch/types/hris/employment_data.py
index dab5f5f6..0804a2a0 100644
--- a/src/finch/types/hris/employment_data.py
+++ b/src/finch/types/hris/employment_data.py
@@ -10,20 +10,14 @@
__all__ = [
"EmploymentData",
"UnionMember0",
- "UnionMember0CustomField",
"UnionMember0Department",
"UnionMember0Employment",
"UnionMember0Manager",
+ "UnionMember0CustomField",
"BatchError",
]
-class UnionMember0CustomField(BaseModel):
- name: Optional[str] = None
-
- value: Union[Optional[str], Optional[List[object]], Optional[float], Optional[bool], Optional[object], None] = None
-
-
class UnionMember0Department(BaseModel):
name: Optional[str] = None
"""The name of the department associated with the individual."""
@@ -46,6 +40,12 @@ class UnionMember0Manager(BaseModel):
"""A stable Finch `id` (UUID v4) for an individual in the company."""
+class UnionMember0CustomField(BaseModel):
+ name: Optional[str] = None
+
+ value: Union[Optional[str], Optional[List[object]], Optional[float], Optional[bool], Optional[object], None] = None
+
+
class UnionMember0(BaseModel):
id: str
"""A stable Finch `id` (UUID v4) for an individual in the company."""
@@ -53,13 +53,6 @@ class UnionMember0(BaseModel):
class_code: Optional[str] = None
"""Worker's compensation classification code for this employee"""
- custom_fields: Optional[List[UnionMember0CustomField]] = None
- """Custom fields for the individual.
-
- These are fields which are defined by the employer in the system. Custom fields
- are not currently supported for assisted connections.
- """
-
department: Optional[UnionMember0Department] = None
"""The department object."""
@@ -101,8 +94,12 @@ class UnionMember0(BaseModel):
title: Optional[str] = None
"""The current title of the individual."""
- work_id: Optional[str] = None
- """This field is deprecated in favour of `source_id`"""
+ custom_fields: Optional[List[UnionMember0CustomField]] = None
+ """Custom fields for the individual.
+
+ These are fields which are defined by the employer in the system. Custom fields
+ are not currently supported for assisted connections.
+ """
income_history: Optional[List[Optional[Income]]] = None
"""The array of income history."""
@@ -117,6 +114,9 @@ class UnionMember0(BaseModel):
source_id: Optional[str] = None
"""The source system's unique employment identifier for this individual"""
+ work_id: Optional[str] = None
+ """This field is deprecated in favour of `source_id`"""
+
class BatchError(BaseModel):
code: float
diff --git a/src/finch/types/hris/pay_statement.py b/src/finch/types/hris/pay_statement.py
deleted file mode 100644
index 7c6f63b6..00000000
--- a/src/finch/types/hris/pay_statement.py
+++ /dev/null
@@ -1,198 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import Dict, List, Optional
-from typing_extensions import Literal
-
-from ..money import Money
-from ..._models import BaseModel
-from .benefit_type import BenefitType
-
-__all__ = [
- "PayStatement",
- "Earning",
- "EarningAttributes",
- "EarningAttributesMetadata",
- "EmployeeDeduction",
- "EmployeeDeductionAttributes",
- "EmployeeDeductionAttributesMetadata",
- "EmployerContribution",
- "EmployerContributionAttributes",
- "EmployerContributionAttributesMetadata",
- "Tax",
- "TaxAttributes",
- "TaxAttributesMetadata",
-]
-
-
-class EarningAttributesMetadata(BaseModel):
- metadata: Optional[Dict[str, Optional[object]]] = None
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class EarningAttributes(BaseModel):
- metadata: Optional[EarningAttributesMetadata] = None
-
-
-class Earning(BaseModel):
- amount: Optional[int] = None
- """The earnings amount in cents."""
-
- attributes: Optional[EarningAttributes] = None
-
- currency: Optional[str] = None
- """The earnings currency code."""
-
- hours: Optional[float] = None
- """The number of hours associated with this earning.
-
- (For salaried employees, this could be hours per pay period, `0` or `null`,
- depending on the provider).
- """
-
- name: Optional[str] = None
- """The exact name of the deduction from the pay statement."""
-
- type: Optional[
- Literal[
- "salary",
- "wage",
- "reimbursement",
- "overtime",
- "severance",
- "double_overtime",
- "pto",
- "sick",
- "bonus",
- "commission",
- "tips",
- "1099",
- "other",
- ]
- ] = None
- """The type of earning."""
-
-
-class EmployeeDeductionAttributesMetadata(BaseModel):
- metadata: Optional[Dict[str, Optional[object]]] = None
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class EmployeeDeductionAttributes(BaseModel):
- metadata: Optional[EmployeeDeductionAttributesMetadata] = None
-
-
-class EmployeeDeduction(BaseModel):
- amount: Optional[int] = None
- """The deduction amount in cents."""
-
- attributes: Optional[EmployeeDeductionAttributes] = None
-
- currency: Optional[str] = None
- """The deduction currency."""
-
- name: Optional[str] = None
- """The deduction name from the pay statement."""
-
- pre_tax: Optional[bool] = None
- """Boolean indicating if the deduction is pre-tax."""
-
- type: Optional[BenefitType] = None
- """Type of benefit."""
-
-
-class EmployerContributionAttributesMetadata(BaseModel):
- metadata: Optional[Dict[str, Optional[object]]] = None
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class EmployerContributionAttributes(BaseModel):
- metadata: Optional[EmployerContributionAttributesMetadata] = None
-
-
-class EmployerContribution(BaseModel):
- amount: Optional[int] = None
- """The contribution amount in cents."""
-
- attributes: Optional[EmployerContributionAttributes] = None
-
- currency: Optional[str] = None
- """The contribution currency."""
-
- name: Optional[str] = None
- """The contribution name from the pay statement."""
-
- type: Optional[BenefitType] = None
- """Type of benefit."""
-
-
-class TaxAttributesMetadata(BaseModel):
- metadata: Optional[Dict[str, Optional[object]]] = None
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class TaxAttributes(BaseModel):
- metadata: Optional[TaxAttributesMetadata] = None
-
-
-class Tax(BaseModel):
- amount: Optional[int] = None
- """The tax amount in cents."""
-
- attributes: Optional[TaxAttributes] = None
-
- currency: Optional[str] = None
- """The currency code."""
-
- employer: Optional[bool] = None
- """`true` if the amount is paid by the employers."""
-
- name: Optional[str] = None
- """The exact name of tax from the pay statement."""
-
- type: Optional[Literal["state", "federal", "local", "fica"]] = None
- """The type of taxes."""
-
-
-class PayStatement(BaseModel):
- earnings: Optional[List[Optional[Earning]]] = None
- """The array of earnings objects associated with this pay statement"""
-
- employee_deductions: Optional[List[Optional[EmployeeDeduction]]] = None
- """The array of deductions objects associated with this pay statement."""
-
- employer_contributions: Optional[List[Optional[EmployerContribution]]] = None
-
- gross_pay: Optional[Money] = None
-
- individual_id: Optional[str] = None
- """A stable Finch `id` (UUID v4) for an individual in the company"""
-
- net_pay: Optional[Money] = None
-
- payment_method: Optional[Literal["check", "direct_deposit", "other"]] = None
- """The payment method."""
-
- taxes: Optional[List[Optional[Tax]]] = None
- """The array of taxes objects associated with this pay statement."""
-
- total_hours: Optional[float] = None
- """The number of hours worked for this pay period"""
-
- type: Optional[Literal["regular_payroll", "off_cycle_payroll", "one_time_payment"]] = None
- """The type of the payment associated with the pay statement."""
diff --git a/src/finch/types/hris/pay_statement_response.py b/src/finch/types/hris/pay_statement_response.py
index 35c33990..c89ae0fc 100644
--- a/src/finch/types/hris/pay_statement_response.py
+++ b/src/finch/types/hris/pay_statement_response.py
@@ -1,7 +1,5 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import Optional
-
from ..._models import BaseModel
from .pay_statement_response_body import PayStatementResponseBody
@@ -9,8 +7,8 @@
class PayStatementResponse(BaseModel):
- body: Optional[PayStatementResponseBody] = None
+ body: PayStatementResponseBody
- code: Optional[int] = None
+ code: int
- payment_id: Optional[str] = None
+ payment_id: str
diff --git a/src/finch/types/hris/pay_statement_response_body.py b/src/finch/types/hris/pay_statement_response_body.py
index 51ef1584..2255563f 100644
--- a/src/finch/types/hris/pay_statement_response_body.py
+++ b/src/finch/types/hris/pay_statement_response_body.py
@@ -1,16 +1,240 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import List, Optional
+from typing import Dict, List, Union, Optional
+from typing_extensions import Literal, TypeAlias
+from ..money import Money
from ..._models import BaseModel
-from .pay_statement import PayStatement
-from ..shared.paging import Paging
+from .benefit_type import BenefitType
-__all__ = ["PayStatementResponseBody"]
+__all__ = [
+ "PayStatementResponseBody",
+ "UnionMember0",
+ "UnionMember0Paging",
+ "UnionMember0PayStatement",
+ "UnionMember0PayStatementEarning",
+ "UnionMember0PayStatementEarningAttributes",
+ "UnionMember0PayStatementEarningAttributesMetadata",
+ "UnionMember0PayStatementEmployeeDeduction",
+ "UnionMember0PayStatementEmployeeDeductionAttributes",
+ "UnionMember0PayStatementEmployeeDeductionAttributesMetadata",
+ "UnionMember0PayStatementEmployerContribution",
+ "UnionMember0PayStatementEmployerContributionAttributes",
+ "UnionMember0PayStatementEmployerContributionAttributesMetadata",
+ "UnionMember0PayStatementTax",
+ "UnionMember0PayStatementTaxAttributes",
+ "UnionMember0PayStatementTaxAttributesMetadata",
+ "BatchError",
+ "UnionMember2",
+]
-class PayStatementResponseBody(BaseModel):
- paging: Optional[Paging] = None
+class UnionMember0Paging(BaseModel):
+ offset: int
+ """The current start index of the returned list of elements"""
- pay_statements: Optional[List[PayStatement]] = None
- """The array of pay statements for the current payment."""
+ count: Optional[int] = None
+ """The total number of elements for the entire query (not just the given page)"""
+
+
+class UnionMember0PayStatementEarningAttributesMetadata(BaseModel):
+ metadata: Dict[str, Optional[object]]
+ """The metadata to be attached to the entity by existing rules.
+
+ It is a key-value pairs where the values can be of any type (string, number,
+ boolean, object, array, etc.).
+ """
+
+
+class UnionMember0PayStatementEarningAttributes(BaseModel):
+ metadata: UnionMember0PayStatementEarningAttributesMetadata
+
+
+class UnionMember0PayStatementEarning(BaseModel):
+ amount: Optional[int] = None
+ """The earnings amount in cents."""
+
+ currency: Optional[str] = None
+ """The earnings currency code."""
+
+ hours: Optional[float] = None
+ """The number of hours associated with this earning.
+
+ (For salaried employees, this could be hours per pay period, `0` or `null`,
+ depending on the provider).
+ """
+
+ name: Optional[str] = None
+ """The exact name of the deduction from the pay statement."""
+
+ type: Optional[
+ Literal[
+ "salary",
+ "wage",
+ "reimbursement",
+ "overtime",
+ "severance",
+ "double_overtime",
+ "pto",
+ "sick",
+ "bonus",
+ "commission",
+ "tips",
+ "1099",
+ "other",
+ ]
+ ] = None
+ """The type of earning."""
+
+ attributes: Optional[UnionMember0PayStatementEarningAttributes] = None
+
+
+class UnionMember0PayStatementEmployeeDeductionAttributesMetadata(BaseModel):
+ metadata: Dict[str, Optional[object]]
+ """The metadata to be attached to the entity by existing rules.
+
+ It is a key-value pairs where the values can be of any type (string, number,
+ boolean, object, array, etc.).
+ """
+
+
+class UnionMember0PayStatementEmployeeDeductionAttributes(BaseModel):
+ metadata: UnionMember0PayStatementEmployeeDeductionAttributesMetadata
+
+
+class UnionMember0PayStatementEmployeeDeduction(BaseModel):
+ amount: Optional[int] = None
+ """The deduction amount in cents."""
+
+ currency: Optional[str] = None
+ """The deduction currency."""
+
+ name: Optional[str] = None
+ """The deduction name from the pay statement."""
+
+ pre_tax: Optional[bool] = None
+ """Boolean indicating if the deduction is pre-tax."""
+
+ type: Optional[BenefitType] = None
+ """Type of benefit."""
+
+ attributes: Optional[UnionMember0PayStatementEmployeeDeductionAttributes] = None
+
+
+class UnionMember0PayStatementEmployerContributionAttributesMetadata(BaseModel):
+ metadata: Dict[str, Optional[object]]
+ """The metadata to be attached to the entity by existing rules.
+
+ It is a key-value pairs where the values can be of any type (string, number,
+ boolean, object, array, etc.).
+ """
+
+
+class UnionMember0PayStatementEmployerContributionAttributes(BaseModel):
+ metadata: UnionMember0PayStatementEmployerContributionAttributesMetadata
+
+
+class UnionMember0PayStatementEmployerContribution(BaseModel):
+ currency: Optional[str] = None
+ """The contribution currency."""
+
+ name: Optional[str] = None
+ """The contribution name from the pay statement."""
+
+ type: Optional[BenefitType] = None
+ """Type of benefit."""
+
+ amount: Optional[int] = None
+ """The contribution amount in cents."""
+
+ attributes: Optional[UnionMember0PayStatementEmployerContributionAttributes] = None
+
+
+class UnionMember0PayStatementTaxAttributesMetadata(BaseModel):
+ metadata: Dict[str, Optional[object]]
+ """The metadata to be attached to the entity by existing rules.
+
+ It is a key-value pairs where the values can be of any type (string, number,
+ boolean, object, array, etc.).
+ """
+
+
+class UnionMember0PayStatementTaxAttributes(BaseModel):
+ metadata: UnionMember0PayStatementTaxAttributesMetadata
+
+
+class UnionMember0PayStatementTax(BaseModel):
+ currency: Optional[str] = None
+ """The currency code."""
+
+ employer: Optional[bool] = None
+ """`true` if the amount is paid by the employers."""
+
+ name: Optional[str] = None
+ """The exact name of tax from the pay statement."""
+
+ type: Optional[Literal["state", "federal", "local", "fica"]] = None
+ """The type of taxes."""
+
+ amount: Optional[int] = None
+ """The tax amount in cents."""
+
+ attributes: Optional[UnionMember0PayStatementTaxAttributes] = None
+
+
+class UnionMember0PayStatement(BaseModel):
+ earnings: Optional[List[Optional[UnionMember0PayStatementEarning]]] = None
+ """The array of earnings objects associated with this pay statement"""
+
+ employee_deductions: Optional[List[Optional[UnionMember0PayStatementEmployeeDeduction]]] = None
+ """The array of deductions objects associated with this pay statement."""
+
+ employer_contributions: Optional[List[Optional[UnionMember0PayStatementEmployerContribution]]] = None
+
+ gross_pay: Optional[Money] = None
+
+ individual_id: str
+ """A stable Finch `id` (UUID v4) for an individual in the company"""
+
+ net_pay: Optional[Money] = None
+
+ payment_method: Optional[Literal["check", "direct_deposit", "other"]] = None
+ """The payment method."""
+
+ taxes: Optional[List[Optional[UnionMember0PayStatementTax]]] = None
+ """The array of taxes objects associated with this pay statement."""
+
+ total_hours: Optional[float] = None
+ """The number of hours worked for this pay period"""
+
+ type: Optional[Literal["off_cycle_payroll", "one_time_payment", "regular_payroll"]] = None
+ """The type of the payment associated with the pay statement."""
+
+
+class UnionMember0(BaseModel):
+ paging: UnionMember0Paging
+
+ pay_statements: List[UnionMember0PayStatement]
+
+
+class BatchError(BaseModel):
+ code: float
+
+ message: str
+
+ name: str
+
+ finch_code: Optional[str] = None
+
+
+class UnionMember2(BaseModel):
+ code: Literal[202]
+
+ finch_code: Literal["data_sync_in_progress"]
+
+ message: Literal["The pay statements for this payment are being fetched. Please check back later."]
+
+ name: Literal["accepted"]
+
+
+PayStatementResponseBody: TypeAlias = Union[UnionMember0, BatchError, UnionMember2]
diff --git a/src/finch/types/hris/payment.py b/src/finch/types/hris/payment.py
index d227f162..992006f1 100644
--- a/src/finch/types/hris/payment.py
+++ b/src/finch/types/hris/payment.py
@@ -16,7 +16,7 @@ class PayPeriod(BaseModel):
class Payment(BaseModel):
- id: Optional[str] = None
+ id: str
"""The unique id for the payment."""
company_debit: Optional[Money] = None
@@ -40,14 +40,14 @@ class Payment(BaseModel):
List[
Literal[
"annually",
- "semi_annually",
- "quarterly",
- "monthly",
- "semi_monthly",
"bi_weekly",
- "weekly",
"daily",
+ "monthly",
"other",
+ "quarterly",
+ "semi_annually",
+ "semi_monthly",
+ "weekly",
]
]
] = None
diff --git a/src/finch/types/money.py b/src/finch/types/money.py
index 21ebb963..86c433ad 100644
--- a/src/finch/types/money.py
+++ b/src/finch/types/money.py
@@ -11,4 +11,4 @@ class Money(BaseModel):
amount: Optional[int] = None
"""Amount for money object (in cents)"""
- currency: Optional[str] = None
+ currency: str
diff --git a/src/finch/types/money_param.py b/src/finch/types/money_param.py
index 180e8687..03e202e6 100644
--- a/src/finch/types/money_param.py
+++ b/src/finch/types/money_param.py
@@ -3,13 +3,13 @@
from __future__ import annotations
from typing import Optional
-from typing_extensions import TypedDict
+from typing_extensions import Required, TypedDict
__all__ = ["MoneyParam"]
class MoneyParam(TypedDict, total=False):
- amount: Optional[int]
+ amount: Required[Optional[int]]
"""Amount for money object (in cents)"""
- currency: str
+ currency: Required[str]
diff --git a/src/finch/types/payroll/pay_group_list_response.py b/src/finch/types/payroll/pay_group_list_response.py
index cd25dfcc..a1fb9826 100644
--- a/src/finch/types/payroll/pay_group_list_response.py
+++ b/src/finch/types/payroll/pay_group_list_response.py
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import List, Optional
+from typing import List
from typing_extensions import Literal
from ..._models import BaseModel
@@ -9,25 +9,15 @@
class PayGroupListResponse(BaseModel):
- id: Optional[str] = None
+ id: str
"""Finch id (uuidv4) for the pay group"""
- name: Optional[str] = None
+ name: str
"""Name of the pay group"""
- pay_frequencies: Optional[
- List[
- Literal[
- "annually",
- "semi_annually",
- "quarterly",
- "monthly",
- "semi_monthly",
- "bi_weekly",
- "weekly",
- "daily",
- "other",
- ]
+ pay_frequencies: List[
+ Literal[
+ "annually", "bi_weekly", "daily", "monthly", "other", "quarterly", "semi_annually", "semi_monthly", "weekly"
]
- ] = None
+ ]
"""List of pay frequencies associated with this pay group"""
diff --git a/src/finch/types/payroll/pay_group_retrieve_response.py b/src/finch/types/payroll/pay_group_retrieve_response.py
index b07ec41c..b3ff7092 100644
--- a/src/finch/types/payroll/pay_group_retrieve_response.py
+++ b/src/finch/types/payroll/pay_group_retrieve_response.py
@@ -19,7 +19,7 @@ class PayGroupRetrieveResponse(BaseModel):
pay_frequencies: List[
Literal[
- "annually", "semi_annually", "quarterly", "monthly", "semi_monthly", "bi_weekly", "weekly", "daily", "other"
+ "annually", "bi_weekly", "daily", "monthly", "other", "quarterly", "semi_annually", "semi_monthly", "weekly"
]
]
"""List of pay frequencies associated with this pay group"""
diff --git a/src/finch/types/sandbox/payment_create_params.py b/src/finch/types/sandbox/payment_create_params.py
index d35089b0..d17c1dd2 100644
--- a/src/finch/types/sandbox/payment_create_params.py
+++ b/src/finch/types/sandbox/payment_create_params.py
@@ -3,7 +3,7 @@
from __future__ import annotations
from typing import Dict, Iterable, Optional
-from typing_extensions import Literal, TypedDict
+from typing_extensions import Literal, Required, TypedDict
from ..money_param import MoneyParam
from ..hris.benefit_type import BenefitType
@@ -35,7 +35,7 @@ class PaymentCreateParams(TypedDict, total=False):
class PayStatementEarningAttributesMetadata(TypedDict, total=False):
- metadata: Dict[str, Optional[object]]
+ metadata: Required[Dict[str, Optional[object]]]
"""The metadata to be attached to the entity by existing rules.
It is a key-value pairs where the values can be of any type (string, number,
@@ -44,50 +44,52 @@ class PayStatementEarningAttributesMetadata(TypedDict, total=False):
class PayStatementEarningAttributes(TypedDict, total=False):
- metadata: PayStatementEarningAttributesMetadata
+ metadata: Required[PayStatementEarningAttributesMetadata]
class PayStatementEarning(TypedDict, total=False):
- amount: Optional[int]
+ amount: Required[Optional[int]]
"""The earnings amount in cents."""
- attributes: Optional[PayStatementEarningAttributes]
-
- currency: Optional[str]
+ currency: Required[Optional[str]]
"""The earnings currency code."""
- hours: Optional[float]
+ hours: Required[Optional[float]]
"""The number of hours associated with this earning.
(For salaried employees, this could be hours per pay period, `0` or `null`,
depending on the provider).
"""
- name: Optional[str]
+ name: Required[Optional[str]]
"""The exact name of the deduction from the pay statement."""
- type: Optional[
- Literal[
- "salary",
- "wage",
- "reimbursement",
- "overtime",
- "severance",
- "double_overtime",
- "pto",
- "sick",
- "bonus",
- "commission",
- "tips",
- "1099",
- "other",
+ type: Required[
+ Optional[
+ Literal[
+ "salary",
+ "wage",
+ "reimbursement",
+ "overtime",
+ "severance",
+ "double_overtime",
+ "pto",
+ "sick",
+ "bonus",
+ "commission",
+ "tips",
+ "1099",
+ "other",
+ ]
]
]
"""The type of earning."""
+ attributes: Optional[PayStatementEarningAttributes]
+
class PayStatementEmployeeDeductionAttributesMetadata(TypedDict, total=False):
- metadata: Dict[str, Optional[object]]
+ metadata: Required[Dict[str, Optional[object]]]
"""The metadata to be attached to the entity by existing rules.
It is a key-value pairs where the values can be of any type (string, number,
@@ -96,30 +98,30 @@ class PayStatementEmployeeDeductionAttributesMetadata(TypedDict, total=False):
class PayStatementEmployeeDeductionAttributes(TypedDict, total=False):
- metadata: PayStatementEmployeeDeductionAttributesMetadata
+ metadata: Required[PayStatementEmployeeDeductionAttributesMetadata]
class PayStatementEmployeeDeduction(TypedDict, total=False):
- amount: Optional[int]
+ amount: Required[Optional[int]]
"""The deduction amount in cents."""
- attributes: Optional[PayStatementEmployeeDeductionAttributes]
-
- currency: Optional[str]
+ currency: Required[Optional[str]]
"""The deduction currency."""
- name: Optional[str]
+ name: Required[Optional[str]]
"""The deduction name from the pay statement."""
- pre_tax: Optional[bool]
+ pre_tax: Required[Optional[bool]]
"""Boolean indicating if the deduction is pre-tax."""
- type: Optional[BenefitType]
+ type: Required[Optional[BenefitType]]
"""Type of benefit."""
+ attributes: Optional[PayStatementEmployeeDeductionAttributes]
+
class PayStatementEmployerContributionAttributesMetadata(TypedDict, total=False):
- metadata: Dict[str, Optional[object]]
+ metadata: Required[Dict[str, Optional[object]]]
"""The metadata to be attached to the entity by existing rules.
It is a key-value pairs where the values can be of any type (string, number,
@@ -128,27 +130,27 @@ class PayStatementEmployerContributionAttributesMetadata(TypedDict, total=False)
class PayStatementEmployerContributionAttributes(TypedDict, total=False):
- metadata: PayStatementEmployerContributionAttributesMetadata
+ metadata: Required[PayStatementEmployerContributionAttributesMetadata]
class PayStatementEmployerContribution(TypedDict, total=False):
- amount: Optional[int]
- """The contribution amount in cents."""
-
- attributes: Optional[PayStatementEmployerContributionAttributes]
-
- currency: Optional[str]
+ currency: Required[Optional[str]]
"""The contribution currency."""
- name: Optional[str]
+ name: Required[Optional[str]]
"""The contribution name from the pay statement."""
- type: Optional[BenefitType]
+ type: Required[Optional[BenefitType]]
"""Type of benefit."""
+ amount: Optional[int]
+ """The contribution amount in cents."""
+
+ attributes: Optional[PayStatementEmployerContributionAttributes]
+
class PayStatementTaxAttributesMetadata(TypedDict, total=False):
- metadata: Dict[str, Optional[object]]
+ metadata: Required[Dict[str, Optional[object]]]
"""The metadata to be attached to the entity by existing rules.
It is a key-value pairs where the values can be of any type (string, number,
@@ -157,52 +159,52 @@ class PayStatementTaxAttributesMetadata(TypedDict, total=False):
class PayStatementTaxAttributes(TypedDict, total=False):
- metadata: PayStatementTaxAttributesMetadata
+ metadata: Required[PayStatementTaxAttributesMetadata]
class PayStatementTax(TypedDict, total=False):
- amount: Optional[int]
- """The tax amount in cents."""
-
- attributes: Optional[PayStatementTaxAttributes]
-
- currency: Optional[str]
+ currency: Required[Optional[str]]
"""The currency code."""
- employer: Optional[bool]
+ employer: Required[Optional[bool]]
"""`true` if the amount is paid by the employers."""
- name: Optional[str]
+ name: Required[Optional[str]]
"""The exact name of tax from the pay statement."""
- type: Optional[Literal["state", "federal", "local", "fica"]]
+ type: Required[Optional[Literal["state", "federal", "local", "fica"]]]
"""The type of taxes."""
+ amount: Optional[int]
+ """The tax amount in cents."""
+
+ attributes: Optional[PayStatementTaxAttributes]
+
class PayStatement(TypedDict, total=False):
- earnings: Optional[Iterable[Optional[PayStatementEarning]]]
+ earnings: Required[Optional[Iterable[Optional[PayStatementEarning]]]]
"""The array of earnings objects associated with this pay statement"""
- employee_deductions: Optional[Iterable[Optional[PayStatementEmployeeDeduction]]]
+ employee_deductions: Required[Optional[Iterable[Optional[PayStatementEmployeeDeduction]]]]
"""The array of deductions objects associated with this pay statement."""
- employer_contributions: Optional[Iterable[Optional[PayStatementEmployerContribution]]]
+ employer_contributions: Required[Optional[Iterable[Optional[PayStatementEmployerContribution]]]]
- gross_pay: Optional[MoneyParam]
+ gross_pay: Required[Optional[MoneyParam]]
- individual_id: str
+ individual_id: Required[str]
"""A stable Finch `id` (UUID v4) for an individual in the company"""
- net_pay: Optional[MoneyParam]
+ net_pay: Required[Optional[MoneyParam]]
- payment_method: Optional[Literal["check", "direct_deposit", "other"]]
+ payment_method: Required[Optional[Literal["check", "direct_deposit", "other"]]]
"""The payment method."""
- taxes: Optional[Iterable[Optional[PayStatementTax]]]
+ taxes: Required[Optional[Iterable[Optional[PayStatementTax]]]]
"""The array of taxes objects associated with this pay statement."""
- total_hours: Optional[float]
+ total_hours: Required[Optional[float]]
"""The number of hours worked for this pay period"""
- type: Optional[Literal["regular_payroll", "off_cycle_payroll", "one_time_payment"]]
+ type: Required[Optional[Literal["off_cycle_payroll", "one_time_payment", "regular_payroll"]]]
"""The type of the payment associated with the pay statement."""
diff --git a/tests/api_resources/hris/test_benefits.py b/tests/api_resources/hris/test_benefits.py
index 9a55c13c..23869d81 100644
--- a/tests/api_resources/hris/test_benefits.py
+++ b/tests/api_resources/hris/test_benefits.py
@@ -42,7 +42,7 @@ def test_method_create_with_all_params(self, client: Finch) -> None:
},
description="description",
frequency="one_time",
- type="401k",
+ type="457",
)
assert_matches_type(CreateCompanyBenefitsResponse, benefit, path=["response"])
@@ -223,7 +223,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncFinch) ->
},
description="description",
frequency="one_time",
- type="401k",
+ type="457",
)
assert_matches_type(CreateCompanyBenefitsResponse, benefit, path=["response"])
diff --git a/tests/api_resources/sandbox/test_payment.py b/tests/api_resources/sandbox/test_payment.py
index 49482a0f..5ef35cc5 100644
--- a/tests/api_resources/sandbox/test_payment.py
+++ b/tests/api_resources/sandbox/test_payment.py
@@ -31,37 +31,37 @@ def test_method_create_with_all_params(self, client: Finch) -> None:
"earnings": [
{
"amount": 0,
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
"currency": "currency",
"hours": 0,
"name": "name",
"type": "salary",
+ "attributes": {"metadata": {"metadata": {"foo": {}}}},
}
],
"employee_deductions": [
{
- "amount": 2000,
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
- "currency": "usd",
- "name": "401k test",
+ "amount": 0,
+ "currency": "currency",
+ "name": "name",
"pre_tax": True,
- "type": "401k",
+ "type": "457",
+ "attributes": {"metadata": {"metadata": {"foo": {}}}},
}
],
"employer_contributions": [
{
- "amount": 0,
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
"currency": "currency",
"name": "name",
- "type": "401k",
+ "type": "457",
+ "amount": 0,
+ "attributes": {"metadata": {"metadata": {"foo": {}}}},
}
],
"gross_pay": {
"amount": 0,
"currency": "currency",
},
- "individual_id": "b2338cfb-472f-4f72-9faa-e028c083144a",
+ "individual_id": "individual_id",
"net_pay": {
"amount": 0,
"currency": "currency",
@@ -69,16 +69,16 @@ def test_method_create_with_all_params(self, client: Finch) -> None:
"payment_method": "check",
"taxes": [
{
- "amount": 0,
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
"currency": "currency",
"employer": True,
"name": "name",
"type": "state",
+ "amount": 0,
+ "attributes": {"metadata": {"metadata": {"foo": {}}}},
}
],
"total_hours": 0,
- "type": "regular_payroll",
+ "type": "off_cycle_payroll",
}
],
start_date="start_date",
@@ -123,37 +123,37 @@ async def test_method_create_with_all_params(self, async_client: AsyncFinch) ->
"earnings": [
{
"amount": 0,
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
"currency": "currency",
"hours": 0,
"name": "name",
"type": "salary",
+ "attributes": {"metadata": {"metadata": {"foo": {}}}},
}
],
"employee_deductions": [
{
- "amount": 2000,
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
- "currency": "usd",
- "name": "401k test",
+ "amount": 0,
+ "currency": "currency",
+ "name": "name",
"pre_tax": True,
- "type": "401k",
+ "type": "457",
+ "attributes": {"metadata": {"metadata": {"foo": {}}}},
}
],
"employer_contributions": [
{
- "amount": 0,
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
"currency": "currency",
"name": "name",
- "type": "401k",
+ "type": "457",
+ "amount": 0,
+ "attributes": {"metadata": {"metadata": {"foo": {}}}},
}
],
"gross_pay": {
"amount": 0,
"currency": "currency",
},
- "individual_id": "b2338cfb-472f-4f72-9faa-e028c083144a",
+ "individual_id": "individual_id",
"net_pay": {
"amount": 0,
"currency": "currency",
@@ -161,16 +161,16 @@ async def test_method_create_with_all_params(self, async_client: AsyncFinch) ->
"payment_method": "check",
"taxes": [
{
- "amount": 0,
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
"currency": "currency",
"employer": True,
"name": "name",
"type": "state",
+ "amount": 0,
+ "attributes": {"metadata": {"metadata": {"foo": {}}}},
}
],
"total_hours": 0,
- "type": "regular_payroll",
+ "type": "off_cycle_payroll",
}
],
start_date="start_date",
From 8cb6d58d5caf6e4efb2367be8b7fb5de40c7a7d5 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 23 May 2025 12:25:53 +0000
Subject: [PATCH 04/29] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index f17b73c9..c3c58b6d 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 46
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-5b7ee4cddd08558fb0cc5010d36899c0bbe5d6a8d39c8171f65b70b68927df3c.yml
-openapi_spec_hash: c01482dc89e9bf7ae4c9dad0fb547e62
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-bee51b1baafbccc6806b5165e0ed78ecf537826794fe15ceb0141897c73d8b8a.yml
+openapi_spec_hash: 12440ed643b40a8690740b6b16ad410a
config_hash: 53778a0b839c4f6ad34fbba051f5e8a6
From 0fce3dc7b1e625f23e36c6ef0e65b6eaab550301 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 23 May 2025 22:16:10 +0000
Subject: [PATCH 05/29] codegen metadata
---
.stats.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.stats.yml b/.stats.yml
index c3c58b6d..bfb3faad 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 46
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-bee51b1baafbccc6806b5165e0ed78ecf537826794fe15ceb0141897c73d8b8a.yml
-openapi_spec_hash: 12440ed643b40a8690740b6b16ad410a
+openapi_spec_hash: 5017bc383c3a779ccbc2d9b6d93b26b4
config_hash: 53778a0b839c4f6ad34fbba051f5e8a6
From 4dfa0fe34952e308ea3192193ff9725eb1ba1482 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 27 May 2025 13:24:05 +0000
Subject: [PATCH 06/29] codegen metadata
---
.stats.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.stats.yml b/.stats.yml
index bfb3faad..34192d01 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 46
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-bee51b1baafbccc6806b5165e0ed78ecf537826794fe15ceb0141897c73d8b8a.yml
openapi_spec_hash: 5017bc383c3a779ccbc2d9b6d93b26b4
-config_hash: 53778a0b839c4f6ad34fbba051f5e8a6
+config_hash: acf5ff659c4400f8c6ee5a2488a83904
From 4ad8fe4df8a6a803fc8750cf03e2ec2d47ce0f49 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 27 May 2025 16:08:27 +0000
Subject: [PATCH 07/29] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 34192d01..b69fc900 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 46
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-bee51b1baafbccc6806b5165e0ed78ecf537826794fe15ceb0141897c73d8b8a.yml
-openapi_spec_hash: 5017bc383c3a779ccbc2d9b6d93b26b4
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-fb9ba13b8189c4223972e38677ea301c3121358e88fbfc9e4ec95ea5cc92f530.yml
+openapi_spec_hash: 7bd02ce73505e51c5fd78608fed55c62
config_hash: acf5ff659c4400f8c6ee5a2488a83904
From 9548dd59525baf33a8d1977b132e93b2fe1d7f96 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 28 May 2025 15:56:18 +0000
Subject: [PATCH 08/29] chore(tests): skip endpoints with basic auth
---
.stats.yml | 4 ++--
tests/api_resources/connect/test_sessions.py | 16 ++++++++++++++++
.../sandbox/connections/test_accounts.py | 8 ++++++++
tests/api_resources/sandbox/test_connections.py | 8 ++++++++
4 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index b69fc900..5ee5e805 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 46
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-fb9ba13b8189c4223972e38677ea301c3121358e88fbfc9e4ec95ea5cc92f530.yml
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-1a82d3230c420c8562600b0ad45133d79ab68ffd21d524ab26eef11e163dba09.yml
openapi_spec_hash: 7bd02ce73505e51c5fd78608fed55c62
-config_hash: acf5ff659c4400f8c6ee5a2488a83904
+config_hash: 81eb5297df860cf29f8c5bdbf9f89608
diff --git a/tests/api_resources/connect/test_sessions.py b/tests/api_resources/connect/test_sessions.py
index cc53a9b7..bdc6339c 100644
--- a/tests/api_resources/connect/test_sessions.py
+++ b/tests/api_resources/connect/test_sessions.py
@@ -20,6 +20,7 @@
class TestSessions:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_method_new(self, client: Finch) -> None:
session = client.connect.sessions.new(
@@ -29,6 +30,7 @@ def test_method_new(self, client: Finch) -> None:
)
assert_matches_type(SessionNewResponse, session, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_method_new_with_all_params(self, client: Finch) -> None:
session = client.connect.sessions.new(
@@ -47,6 +49,7 @@ def test_method_new_with_all_params(self, client: Finch) -> None:
)
assert_matches_type(SessionNewResponse, session, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_raw_response_new(self, client: Finch) -> None:
response = client.connect.sessions.with_raw_response.new(
@@ -60,6 +63,7 @@ def test_raw_response_new(self, client: Finch) -> None:
session = response.parse()
assert_matches_type(SessionNewResponse, session, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_streaming_response_new(self, client: Finch) -> None:
with client.connect.sessions.with_streaming_response.new(
@@ -75,6 +79,7 @@ def test_streaming_response_new(self, client: Finch) -> None:
assert cast(Any, response.is_closed) is True
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_method_reauthenticate(self, client: Finch) -> None:
session = client.connect.sessions.reauthenticate(
@@ -82,6 +87,7 @@ def test_method_reauthenticate(self, client: Finch) -> None:
)
assert_matches_type(SessionReauthenticateResponse, session, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_method_reauthenticate_with_all_params(self, client: Finch) -> None:
session = client.connect.sessions.reauthenticate(
@@ -92,6 +98,7 @@ def test_method_reauthenticate_with_all_params(self, client: Finch) -> None:
)
assert_matches_type(SessionReauthenticateResponse, session, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_raw_response_reauthenticate(self, client: Finch) -> None:
response = client.connect.sessions.with_raw_response.reauthenticate(
@@ -103,6 +110,7 @@ def test_raw_response_reauthenticate(self, client: Finch) -> None:
session = response.parse()
assert_matches_type(SessionReauthenticateResponse, session, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_streaming_response_reauthenticate(self, client: Finch) -> None:
with client.connect.sessions.with_streaming_response.reauthenticate(
@@ -120,6 +128,7 @@ def test_streaming_response_reauthenticate(self, client: Finch) -> None:
class TestAsyncSessions:
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_method_new(self, async_client: AsyncFinch) -> None:
session = await async_client.connect.sessions.new(
@@ -129,6 +138,7 @@ async def test_method_new(self, async_client: AsyncFinch) -> None:
)
assert_matches_type(SessionNewResponse, session, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_method_new_with_all_params(self, async_client: AsyncFinch) -> None:
session = await async_client.connect.sessions.new(
@@ -147,6 +157,7 @@ async def test_method_new_with_all_params(self, async_client: AsyncFinch) -> Non
)
assert_matches_type(SessionNewResponse, session, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_raw_response_new(self, async_client: AsyncFinch) -> None:
response = await async_client.connect.sessions.with_raw_response.new(
@@ -160,6 +171,7 @@ async def test_raw_response_new(self, async_client: AsyncFinch) -> None:
session = response.parse()
assert_matches_type(SessionNewResponse, session, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_streaming_response_new(self, async_client: AsyncFinch) -> None:
async with async_client.connect.sessions.with_streaming_response.new(
@@ -175,6 +187,7 @@ async def test_streaming_response_new(self, async_client: AsyncFinch) -> None:
assert cast(Any, response.is_closed) is True
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_method_reauthenticate(self, async_client: AsyncFinch) -> None:
session = await async_client.connect.sessions.reauthenticate(
@@ -182,6 +195,7 @@ async def test_method_reauthenticate(self, async_client: AsyncFinch) -> None:
)
assert_matches_type(SessionReauthenticateResponse, session, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_method_reauthenticate_with_all_params(self, async_client: AsyncFinch) -> None:
session = await async_client.connect.sessions.reauthenticate(
@@ -192,6 +206,7 @@ async def test_method_reauthenticate_with_all_params(self, async_client: AsyncFi
)
assert_matches_type(SessionReauthenticateResponse, session, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_raw_response_reauthenticate(self, async_client: AsyncFinch) -> None:
response = await async_client.connect.sessions.with_raw_response.reauthenticate(
@@ -203,6 +218,7 @@ async def test_raw_response_reauthenticate(self, async_client: AsyncFinch) -> No
session = response.parse()
assert_matches_type(SessionReauthenticateResponse, session, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_streaming_response_reauthenticate(self, async_client: AsyncFinch) -> None:
async with async_client.connect.sessions.with_streaming_response.reauthenticate(
diff --git a/tests/api_resources/sandbox/connections/test_accounts.py b/tests/api_resources/sandbox/connections/test_accounts.py
index 5ff83d69..5115364c 100644
--- a/tests/api_resources/sandbox/connections/test_accounts.py
+++ b/tests/api_resources/sandbox/connections/test_accounts.py
@@ -20,6 +20,7 @@
class TestAccounts:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_method_create(self, client: Finch) -> None:
account = client.sandbox.connections.accounts.create(
@@ -28,6 +29,7 @@ def test_method_create(self, client: Finch) -> None:
)
assert_matches_type(AccountCreateResponse, account, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_method_create_with_all_params(self, client: Finch) -> None:
account = client.sandbox.connections.accounts.create(
@@ -38,6 +40,7 @@ def test_method_create_with_all_params(self, client: Finch) -> None:
)
assert_matches_type(AccountCreateResponse, account, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_raw_response_create(self, client: Finch) -> None:
response = client.sandbox.connections.accounts.with_raw_response.create(
@@ -50,6 +53,7 @@ def test_raw_response_create(self, client: Finch) -> None:
account = response.parse()
assert_matches_type(AccountCreateResponse, account, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_streaming_response_create(self, client: Finch) -> None:
with client.sandbox.connections.accounts.with_streaming_response.create(
@@ -100,6 +104,7 @@ def test_streaming_response_update(self, client: Finch) -> None:
class TestAsyncAccounts:
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_method_create(self, async_client: AsyncFinch) -> None:
account = await async_client.sandbox.connections.accounts.create(
@@ -108,6 +113,7 @@ async def test_method_create(self, async_client: AsyncFinch) -> None:
)
assert_matches_type(AccountCreateResponse, account, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_method_create_with_all_params(self, async_client: AsyncFinch) -> None:
account = await async_client.sandbox.connections.accounts.create(
@@ -118,6 +124,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncFinch) ->
)
assert_matches_type(AccountCreateResponse, account, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_raw_response_create(self, async_client: AsyncFinch) -> None:
response = await async_client.sandbox.connections.accounts.with_raw_response.create(
@@ -130,6 +137,7 @@ async def test_raw_response_create(self, async_client: AsyncFinch) -> None:
account = response.parse()
assert_matches_type(AccountCreateResponse, account, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_streaming_response_create(self, async_client: AsyncFinch) -> None:
async with async_client.sandbox.connections.accounts.with_streaming_response.create(
diff --git a/tests/api_resources/sandbox/test_connections.py b/tests/api_resources/sandbox/test_connections.py
index 1a31e49b..c4fed5da 100644
--- a/tests/api_resources/sandbox/test_connections.py
+++ b/tests/api_resources/sandbox/test_connections.py
@@ -17,6 +17,7 @@
class TestConnections:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_method_create(self, client: Finch) -> None:
connection = client.sandbox.connections.create(
@@ -24,6 +25,7 @@ def test_method_create(self, client: Finch) -> None:
)
assert_matches_type(ConnectionCreateResponse, connection, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_method_create_with_all_params(self, client: Finch) -> None:
connection = client.sandbox.connections.create(
@@ -34,6 +36,7 @@ def test_method_create_with_all_params(self, client: Finch) -> None:
)
assert_matches_type(ConnectionCreateResponse, connection, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_raw_response_create(self, client: Finch) -> None:
response = client.sandbox.connections.with_raw_response.create(
@@ -45,6 +48,7 @@ def test_raw_response_create(self, client: Finch) -> None:
connection = response.parse()
assert_matches_type(ConnectionCreateResponse, connection, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
def test_streaming_response_create(self, client: Finch) -> None:
with client.sandbox.connections.with_streaming_response.create(
@@ -62,6 +66,7 @@ def test_streaming_response_create(self, client: Finch) -> None:
class TestAsyncConnections:
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_method_create(self, async_client: AsyncFinch) -> None:
connection = await async_client.sandbox.connections.create(
@@ -69,6 +74,7 @@ async def test_method_create(self, async_client: AsyncFinch) -> None:
)
assert_matches_type(ConnectionCreateResponse, connection, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_method_create_with_all_params(self, async_client: AsyncFinch) -> None:
connection = await async_client.sandbox.connections.create(
@@ -79,6 +85,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncFinch) ->
)
assert_matches_type(ConnectionCreateResponse, connection, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_raw_response_create(self, async_client: AsyncFinch) -> None:
response = await async_client.sandbox.connections.with_raw_response.create(
@@ -90,6 +97,7 @@ async def test_raw_response_create(self, async_client: AsyncFinch) -> None:
connection = response.parse()
assert_matches_type(ConnectionCreateResponse, connection, path=["response"])
+ @pytest.mark.skip(reason="prism tests are broken")
@parametrize
async def test_streaming_response_create(self, async_client: AsyncFinch) -> None:
async with async_client.sandbox.connections.with_streaming_response.create(
From 2b6929f8eca16bff688b19907f38e05ad2d2b092 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 2 Jun 2025 10:12:35 +0000
Subject: [PATCH 09/29] chore(docs): remove reference to rye shell
---
CONTRIBUTING.md | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b9879f76..21f44701 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -17,8 +17,7 @@ $ rye sync --all-features
You can then run scripts using `rye run python script.py` or by activating the virtual environment:
```sh
-$ rye shell
-# or manually activate - https://docs.python.org/3/library/venv.html#how-venvs-work
+# Activate the virtual environment - https://docs.python.org/3/library/venv.html#how-venvs-work
$ source .venv/bin/activate
# now you can omit the `rye run` prefix
From f9bdda77ef1fcb1460594a16f1b8726bbaad2b52 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 2 Jun 2025 17:33:46 +0000
Subject: [PATCH 10/29] feat(client): add follow_redirects request option
---
src/finch/_base_client.py | 6 +++++
src/finch/_models.py | 2 ++
src/finch/_types.py | 2 ++
tests/test_client.py | 56 ++++++++++++++++++++++++++++++++++++++-
4 files changed, 65 insertions(+), 1 deletion(-)
diff --git a/src/finch/_base_client.py b/src/finch/_base_client.py
index c46eaebc..1196bcc0 100644
--- a/src/finch/_base_client.py
+++ b/src/finch/_base_client.py
@@ -961,6 +961,9 @@ def request(
if self.custom_auth is not None:
kwargs["auth"] = self.custom_auth
+ if options.follow_redirects is not None:
+ kwargs["follow_redirects"] = options.follow_redirects
+
log.debug("Sending HTTP Request: %s %s", request.method, request.url)
response = None
@@ -1475,6 +1478,9 @@ async def request(
if self.custom_auth is not None:
kwargs["auth"] = self.custom_auth
+ if options.follow_redirects is not None:
+ kwargs["follow_redirects"] = options.follow_redirects
+
log.debug("Sending HTTP Request: %s %s", request.method, request.url)
response = None
diff --git a/src/finch/_models.py b/src/finch/_models.py
index 798956f1..4f214980 100644
--- a/src/finch/_models.py
+++ b/src/finch/_models.py
@@ -737,6 +737,7 @@ class FinalRequestOptionsInput(TypedDict, total=False):
idempotency_key: str
json_data: Body
extra_json: AnyMapping
+ follow_redirects: bool
@final
@@ -750,6 +751,7 @@ class FinalRequestOptions(pydantic.BaseModel):
files: Union[HttpxRequestFiles, None] = None
idempotency_key: Union[str, None] = None
post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven()
+ follow_redirects: Union[bool, None] = None
# It should be noted that we cannot use `json` here as that would override
# a BaseModel method in an incompatible fashion.
diff --git a/src/finch/_types.py b/src/finch/_types.py
index 643bfe5e..202fb730 100644
--- a/src/finch/_types.py
+++ b/src/finch/_types.py
@@ -101,6 +101,7 @@ class RequestOptions(TypedDict, total=False):
params: Query
extra_json: AnyMapping
idempotency_key: str
+ follow_redirects: bool
# Sentinel class used until PEP 0661 is accepted
@@ -217,3 +218,4 @@ class _GenericAlias(Protocol):
class HttpxSendArgs(TypedDict, total=False):
auth: httpx.Auth
+ follow_redirects: bool
diff --git a/tests/test_client.py b/tests/test_client.py
index 309fcbac..1a2cd599 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -24,7 +24,7 @@
from finch import Finch, AsyncFinch, APIResponseValidationError
from finch._types import Omit
from finch._models import BaseModel, FinalRequestOptions
-from finch._exceptions import APIResponseValidationError
+from finch._exceptions import APIStatusError, APIResponseValidationError
from finch._base_client import DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, make_request_options
from .utils import update_env
@@ -829,6 +829,33 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
assert response.retries_taken == failures_before_success
assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
+ @pytest.mark.respx(base_url=base_url)
+ def test_follow_redirects(self, respx_mock: MockRouter) -> None:
+ # Test that the default follow_redirects=True allows following redirects
+ respx_mock.post("/redirect").mock(
+ return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ )
+ respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"}))
+
+ response = self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
+ assert response.status_code == 200
+ assert response.json() == {"status": "ok"}
+
+ @pytest.mark.respx(base_url=base_url)
+ def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None:
+ # Test that follow_redirects=False prevents following redirects
+ respx_mock.post("/redirect").mock(
+ return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ )
+
+ with pytest.raises(APIStatusError) as exc_info:
+ self.client.post(
+ "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response
+ )
+
+ assert exc_info.value.response.status_code == 302
+ assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected"
+
class TestAsyncFinch:
client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True)
@@ -1672,3 +1699,30 @@ async def test_main() -> None:
raise AssertionError("calling get_platform using asyncify resulted in a hung process")
time.sleep(0.1)
+
+ @pytest.mark.respx(base_url=base_url)
+ async def test_follow_redirects(self, respx_mock: MockRouter) -> None:
+ # Test that the default follow_redirects=True allows following redirects
+ respx_mock.post("/redirect").mock(
+ return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ )
+ respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"}))
+
+ response = await self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
+ assert response.status_code == 200
+ assert response.json() == {"status": "ok"}
+
+ @pytest.mark.respx(base_url=base_url)
+ async def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None:
+ # Test that follow_redirects=False prevents following redirects
+ respx_mock.post("/redirect").mock(
+ return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ )
+
+ with pytest.raises(APIStatusError) as exc_info:
+ await self.client.post(
+ "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response
+ )
+
+ assert exc_info.value.response.status_code == 302
+ assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected"
From edcea6c7ac55661d3e46aec1c42576988b510dfc Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 4 Jun 2025 15:28:27 +0000
Subject: [PATCH 11/29] fix(client): fix PayStatementResponse body
---
.stats.yml | 2 +-
api.md | 7 +-
src/finch/resources/sandbox/payment.py | 5 +-
src/finch/types/hris/__init__.py | 3 +
src/finch/types/hris/pay_statement.py | 198 +++++++++++++++
.../pay_statement_data_sync_in_progress.py | 17 ++
src/finch/types/hris/pay_statement_param.py | 201 +++++++++++++++
.../types/hris/pay_statement_response.py | 21 +-
.../types/hris/pay_statement_response_body.py | 232 +-----------------
.../types/sandbox/payment_create_params.py | 202 +--------------
10 files changed, 460 insertions(+), 428 deletions(-)
create mode 100644 src/finch/types/hris/pay_statement.py
create mode 100644 src/finch/types/hris/pay_statement_data_sync_in_progress.py
create mode 100644 src/finch/types/hris/pay_statement_param.py
diff --git a/.stats.yml b/.stats.yml
index 5ee5e805..8d6b9198 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 46
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-1a82d3230c420c8562600b0ad45133d79ab68ffd21d524ab26eef11e163dba09.yml
openapi_spec_hash: 7bd02ce73505e51c5fd78608fed55c62
-config_hash: 81eb5297df860cf29f8c5bdbf9f89608
+config_hash: bb896000dc7e790e5c9ddda2549fb7ac
diff --git a/api.md b/api.md
index 2ba59c1f..3a65985b 100644
--- a/api.md
+++ b/api.md
@@ -128,7 +128,12 @@ Methods:
Types:
```python
-from finch.types.hris import PayStatementResponse, PayStatementResponseBody
+from finch.types.hris import (
+ PayStatement,
+ PayStatementDataSyncInProgress,
+ PayStatementResponse,
+ PayStatementResponseBody,
+)
```
Methods:
diff --git a/src/finch/resources/sandbox/payment.py b/src/finch/resources/sandbox/payment.py
index 8092fe60..3f0a6397 100644
--- a/src/finch/resources/sandbox/payment.py
+++ b/src/finch/resources/sandbox/payment.py
@@ -14,6 +14,7 @@
from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
from ..._base_client import make_request_options
from ...types.sandbox import payment_create_params
+from ...types.hris.pay_statement_param import PayStatementParam
from ...types.sandbox.payment_create_response import PaymentCreateResponse
__all__ = ["Payment", "AsyncPayment"]
@@ -43,7 +44,7 @@ def create(
self,
*,
end_date: str | NotGiven = NOT_GIVEN,
- pay_statements: Iterable[payment_create_params.PayStatement] | NotGiven = NOT_GIVEN,
+ pay_statements: Iterable[PayStatementParam] | NotGiven = NOT_GIVEN,
start_date: str | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -105,7 +106,7 @@ async def create(
self,
*,
end_date: str | NotGiven = NOT_GIVEN,
- pay_statements: Iterable[payment_create_params.PayStatement] | NotGiven = NOT_GIVEN,
+ pay_statements: Iterable[PayStatementParam] | NotGiven = NOT_GIVEN,
start_date: str | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
diff --git a/src/finch/types/hris/__init__.py b/src/finch/types/hris/__init__.py
index 82434d01..277f19a8 100644
--- a/src/finch/types/hris/__init__.py
+++ b/src/finch/types/hris/__init__.py
@@ -8,6 +8,7 @@
from .payment import Payment as Payment
from .individual import Individual as Individual
from .benefit_type import BenefitType as BenefitType
+from .pay_statement import PayStatement as PayStatement
from .company_benefit import CompanyBenefit as CompanyBenefit
from .employment_data import EmploymentData as EmploymentData
from .benefits_support import BenefitsSupport as BenefitsSupport
@@ -15,6 +16,7 @@
from .document_response import DocumentResponse as DocumentResponse
from .benfit_contribution import BenfitContribution as BenfitContribution
from .individual_response import IndividualResponse as IndividualResponse
+from .pay_statement_param import PayStatementParam as PayStatementParam
from .payment_list_params import PaymentListParams as PaymentListParams
from .benefit_contribution import BenefitContribution as BenefitContribution
from .document_list_params import DocumentListParams as DocumentListParams
@@ -35,6 +37,7 @@
from .create_company_benefits_response import CreateCompanyBenefitsResponse as CreateCompanyBenefitsResponse
from .directory_list_individuals_params import DirectoryListIndividualsParams as DirectoryListIndividualsParams
from .pay_statement_retrieve_many_params import PayStatementRetrieveManyParams as PayStatementRetrieveManyParams
+from .pay_statement_data_sync_in_progress import PayStatementDataSyncInProgress as PayStatementDataSyncInProgress
from .benefit_list_supported_benefits_response import (
BenefitListSupportedBenefitsResponse as BenefitListSupportedBenefitsResponse,
)
diff --git a/src/finch/types/hris/pay_statement.py b/src/finch/types/hris/pay_statement.py
new file mode 100644
index 00000000..1c874672
--- /dev/null
+++ b/src/finch/types/hris/pay_statement.py
@@ -0,0 +1,198 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from typing_extensions import Literal
+
+from ..money import Money
+from ..._models import BaseModel
+from .benefit_type import BenefitType
+
+__all__ = [
+ "PayStatement",
+ "Earning",
+ "EarningAttributes",
+ "EarningAttributesMetadata",
+ "EmployeeDeduction",
+ "EmployeeDeductionAttributes",
+ "EmployeeDeductionAttributesMetadata",
+ "EmployerContribution",
+ "EmployerContributionAttributes",
+ "EmployerContributionAttributesMetadata",
+ "Tax",
+ "TaxAttributes",
+ "TaxAttributesMetadata",
+]
+
+
+class EarningAttributesMetadata(BaseModel):
+ metadata: Dict[str, Optional[object]]
+ """The metadata to be attached to the entity by existing rules.
+
+ It is a key-value pairs where the values can be of any type (string, number,
+ boolean, object, array, etc.).
+ """
+
+
+class EarningAttributes(BaseModel):
+ metadata: EarningAttributesMetadata
+
+
+class Earning(BaseModel):
+ amount: Optional[int] = None
+ """The earnings amount in cents."""
+
+ currency: Optional[str] = None
+ """The earnings currency code."""
+
+ hours: Optional[float] = None
+ """The number of hours associated with this earning.
+
+ (For salaried employees, this could be hours per pay period, `0` or `null`,
+ depending on the provider).
+ """
+
+ name: Optional[str] = None
+ """The exact name of the deduction from the pay statement."""
+
+ type: Optional[
+ Literal[
+ "salary",
+ "wage",
+ "reimbursement",
+ "overtime",
+ "severance",
+ "double_overtime",
+ "pto",
+ "sick",
+ "bonus",
+ "commission",
+ "tips",
+ "1099",
+ "other",
+ ]
+ ] = None
+ """The type of earning."""
+
+ attributes: Optional[EarningAttributes] = None
+
+
+class EmployeeDeductionAttributesMetadata(BaseModel):
+ metadata: Dict[str, Optional[object]]
+ """The metadata to be attached to the entity by existing rules.
+
+ It is a key-value pairs where the values can be of any type (string, number,
+ boolean, object, array, etc.).
+ """
+
+
+class EmployeeDeductionAttributes(BaseModel):
+ metadata: EmployeeDeductionAttributesMetadata
+
+
+class EmployeeDeduction(BaseModel):
+ amount: Optional[int] = None
+ """The deduction amount in cents."""
+
+ currency: Optional[str] = None
+ """The deduction currency."""
+
+ name: Optional[str] = None
+ """The deduction name from the pay statement."""
+
+ pre_tax: Optional[bool] = None
+ """Boolean indicating if the deduction is pre-tax."""
+
+ type: Optional[BenefitType] = None
+ """Type of benefit."""
+
+ attributes: Optional[EmployeeDeductionAttributes] = None
+
+
+class EmployerContributionAttributesMetadata(BaseModel):
+ metadata: Dict[str, Optional[object]]
+ """The metadata to be attached to the entity by existing rules.
+
+ It is a key-value pairs where the values can be of any type (string, number,
+ boolean, object, array, etc.).
+ """
+
+
+class EmployerContributionAttributes(BaseModel):
+ metadata: EmployerContributionAttributesMetadata
+
+
+class EmployerContribution(BaseModel):
+ currency: Optional[str] = None
+ """The contribution currency."""
+
+ name: Optional[str] = None
+ """The contribution name from the pay statement."""
+
+ type: Optional[BenefitType] = None
+ """Type of benefit."""
+
+ amount: Optional[int] = None
+ """The contribution amount in cents."""
+
+ attributes: Optional[EmployerContributionAttributes] = None
+
+
+class TaxAttributesMetadata(BaseModel):
+ metadata: Dict[str, Optional[object]]
+ """The metadata to be attached to the entity by existing rules.
+
+ It is a key-value pairs where the values can be of any type (string, number,
+ boolean, object, array, etc.).
+ """
+
+
+class TaxAttributes(BaseModel):
+ metadata: TaxAttributesMetadata
+
+
+class Tax(BaseModel):
+ currency: Optional[str] = None
+ """The currency code."""
+
+ employer: Optional[bool] = None
+ """`true` if the amount is paid by the employers."""
+
+ name: Optional[str] = None
+ """The exact name of tax from the pay statement."""
+
+ type: Optional[Literal["state", "federal", "local", "fica"]] = None
+ """The type of taxes."""
+
+ amount: Optional[int] = None
+ """The tax amount in cents."""
+
+ attributes: Optional[TaxAttributes] = None
+
+
+class PayStatement(BaseModel):
+ earnings: Optional[List[Optional[Earning]]] = None
+ """The array of earnings objects associated with this pay statement"""
+
+ employee_deductions: Optional[List[Optional[EmployeeDeduction]]] = None
+ """The array of deductions objects associated with this pay statement."""
+
+ employer_contributions: Optional[List[Optional[EmployerContribution]]] = None
+
+ gross_pay: Optional[Money] = None
+
+ individual_id: str
+ """A stable Finch `id` (UUID v4) for an individual in the company"""
+
+ net_pay: Optional[Money] = None
+
+ payment_method: Optional[Literal["check", "direct_deposit", "other"]] = None
+ """The payment method."""
+
+ taxes: Optional[List[Optional[Tax]]] = None
+ """The array of taxes objects associated with this pay statement."""
+
+ total_hours: Optional[float] = None
+ """The number of hours worked for this pay period"""
+
+ type: Optional[Literal["off_cycle_payroll", "one_time_payment", "regular_payroll"]] = None
+ """The type of the payment associated with the pay statement."""
diff --git a/src/finch/types/hris/pay_statement_data_sync_in_progress.py b/src/finch/types/hris/pay_statement_data_sync_in_progress.py
new file mode 100644
index 00000000..c9561566
--- /dev/null
+++ b/src/finch/types/hris/pay_statement_data_sync_in_progress.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["PayStatementDataSyncInProgress"]
+
+
+class PayStatementDataSyncInProgress(BaseModel):
+ code: Literal[202]
+
+ finch_code: Literal["data_sync_in_progress"]
+
+ message: Literal["The pay statements for this payment are being fetched. Please check back later."]
+
+ name: Literal["accepted"]
diff --git a/src/finch/types/hris/pay_statement_param.py b/src/finch/types/hris/pay_statement_param.py
new file mode 100644
index 00000000..b5e70561
--- /dev/null
+++ b/src/finch/types/hris/pay_statement_param.py
@@ -0,0 +1,201 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Iterable, Optional
+from typing_extensions import Literal, Required, TypedDict
+
+from ..money_param import MoneyParam
+from .benefit_type import BenefitType
+
+__all__ = [
+ "PayStatementParam",
+ "Earning",
+ "EarningAttributes",
+ "EarningAttributesMetadata",
+ "EmployeeDeduction",
+ "EmployeeDeductionAttributes",
+ "EmployeeDeductionAttributesMetadata",
+ "EmployerContribution",
+ "EmployerContributionAttributes",
+ "EmployerContributionAttributesMetadata",
+ "Tax",
+ "TaxAttributes",
+ "TaxAttributesMetadata",
+]
+
+
+class EarningAttributesMetadata(TypedDict, total=False):
+ metadata: Required[Dict[str, Optional[object]]]
+ """The metadata to be attached to the entity by existing rules.
+
+ It is a key-value pairs where the values can be of any type (string, number,
+ boolean, object, array, etc.).
+ """
+
+
+class EarningAttributes(TypedDict, total=False):
+ metadata: Required[EarningAttributesMetadata]
+
+
+class Earning(TypedDict, total=False):
+ amount: Required[Optional[int]]
+ """The earnings amount in cents."""
+
+ currency: Required[Optional[str]]
+ """The earnings currency code."""
+
+ hours: Required[Optional[float]]
+ """The number of hours associated with this earning.
+
+ (For salaried employees, this could be hours per pay period, `0` or `null`,
+ depending on the provider).
+ """
+
+ name: Required[Optional[str]]
+ """The exact name of the deduction from the pay statement."""
+
+ type: Required[
+ Optional[
+ Literal[
+ "salary",
+ "wage",
+ "reimbursement",
+ "overtime",
+ "severance",
+ "double_overtime",
+ "pto",
+ "sick",
+ "bonus",
+ "commission",
+ "tips",
+ "1099",
+ "other",
+ ]
+ ]
+ ]
+ """The type of earning."""
+
+ attributes: Optional[EarningAttributes]
+
+
+class EmployeeDeductionAttributesMetadata(TypedDict, total=False):
+ metadata: Required[Dict[str, Optional[object]]]
+ """The metadata to be attached to the entity by existing rules.
+
+ It is a key-value pairs where the values can be of any type (string, number,
+ boolean, object, array, etc.).
+ """
+
+
+class EmployeeDeductionAttributes(TypedDict, total=False):
+ metadata: Required[EmployeeDeductionAttributesMetadata]
+
+
+class EmployeeDeduction(TypedDict, total=False):
+ amount: Required[Optional[int]]
+ """The deduction amount in cents."""
+
+ currency: Required[Optional[str]]
+ """The deduction currency."""
+
+ name: Required[Optional[str]]
+ """The deduction name from the pay statement."""
+
+ pre_tax: Required[Optional[bool]]
+ """Boolean indicating if the deduction is pre-tax."""
+
+ type: Required[Optional[BenefitType]]
+ """Type of benefit."""
+
+ attributes: Optional[EmployeeDeductionAttributes]
+
+
+class EmployerContributionAttributesMetadata(TypedDict, total=False):
+ metadata: Required[Dict[str, Optional[object]]]
+ """The metadata to be attached to the entity by existing rules.
+
+ It is a key-value pairs where the values can be of any type (string, number,
+ boolean, object, array, etc.).
+ """
+
+
+class EmployerContributionAttributes(TypedDict, total=False):
+ metadata: Required[EmployerContributionAttributesMetadata]
+
+
+class EmployerContribution(TypedDict, total=False):
+ currency: Required[Optional[str]]
+ """The contribution currency."""
+
+ name: Required[Optional[str]]
+ """The contribution name from the pay statement."""
+
+ type: Required[Optional[BenefitType]]
+ """Type of benefit."""
+
+ amount: Optional[int]
+ """The contribution amount in cents."""
+
+ attributes: Optional[EmployerContributionAttributes]
+
+
+class TaxAttributesMetadata(TypedDict, total=False):
+ metadata: Required[Dict[str, Optional[object]]]
+ """The metadata to be attached to the entity by existing rules.
+
+ It is a key-value pairs where the values can be of any type (string, number,
+ boolean, object, array, etc.).
+ """
+
+
+class TaxAttributes(TypedDict, total=False):
+ metadata: Required[TaxAttributesMetadata]
+
+
+class Tax(TypedDict, total=False):
+ currency: Required[Optional[str]]
+ """The currency code."""
+
+ employer: Required[Optional[bool]]
+ """`true` if the amount is paid by the employers."""
+
+ name: Required[Optional[str]]
+ """The exact name of tax from the pay statement."""
+
+ type: Required[Optional[Literal["state", "federal", "local", "fica"]]]
+ """The type of taxes."""
+
+ amount: Optional[int]
+ """The tax amount in cents."""
+
+ attributes: Optional[TaxAttributes]
+
+
+class PayStatementParam(TypedDict, total=False):
+ earnings: Required[Optional[Iterable[Optional[Earning]]]]
+ """The array of earnings objects associated with this pay statement"""
+
+ employee_deductions: Required[Optional[Iterable[Optional[EmployeeDeduction]]]]
+ """The array of deductions objects associated with this pay statement."""
+
+ employer_contributions: Required[Optional[Iterable[Optional[EmployerContribution]]]]
+
+ gross_pay: Required[Optional[MoneyParam]]
+
+ individual_id: Required[str]
+ """A stable Finch `id` (UUID v4) for an individual in the company"""
+
+ net_pay: Required[Optional[MoneyParam]]
+
+ payment_method: Required[Optional[Literal["check", "direct_deposit", "other"]]]
+ """The payment method."""
+
+ taxes: Required[Optional[Iterable[Optional[Tax]]]]
+ """The array of taxes objects associated with this pay statement."""
+
+ total_hours: Required[Optional[float]]
+ """The number of hours worked for this pay period"""
+
+ type: Required[Optional[Literal["off_cycle_payroll", "one_time_payment", "regular_payroll"]]]
+ """The type of the payment associated with the pay statement."""
diff --git a/src/finch/types/hris/pay_statement_response.py b/src/finch/types/hris/pay_statement_response.py
index c89ae0fc..56825783 100644
--- a/src/finch/types/hris/pay_statement_response.py
+++ b/src/finch/types/hris/pay_statement_response.py
@@ -1,13 +1,30 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+from typing import Union, Optional
+from typing_extensions import TypeAlias
+
from ..._models import BaseModel
from .pay_statement_response_body import PayStatementResponseBody
+from .pay_statement_data_sync_in_progress import PayStatementDataSyncInProgress
+
+__all__ = ["PayStatementResponse", "Body", "BodyBatchError"]
+
+
+class BodyBatchError(BaseModel):
+ code: float
+
+ message: str
+
+ name: str
+
+ finch_code: Optional[str] = None
+
-__all__ = ["PayStatementResponse"]
+Body: TypeAlias = Union[PayStatementResponseBody, BodyBatchError, PayStatementDataSyncInProgress]
class PayStatementResponse(BaseModel):
- body: PayStatementResponseBody
+ body: Body
code: int
diff --git a/src/finch/types/hris/pay_statement_response_body.py b/src/finch/types/hris/pay_statement_response_body.py
index 2255563f..454a807c 100644
--- a/src/finch/types/hris/pay_statement_response_body.py
+++ b/src/finch/types/hris/pay_statement_response_body.py
@@ -1,35 +1,14 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import Dict, List, Union, Optional
-from typing_extensions import Literal, TypeAlias
+from typing import List, Optional
-from ..money import Money
from ..._models import BaseModel
-from .benefit_type import BenefitType
+from .pay_statement import PayStatement
-__all__ = [
- "PayStatementResponseBody",
- "UnionMember0",
- "UnionMember0Paging",
- "UnionMember0PayStatement",
- "UnionMember0PayStatementEarning",
- "UnionMember0PayStatementEarningAttributes",
- "UnionMember0PayStatementEarningAttributesMetadata",
- "UnionMember0PayStatementEmployeeDeduction",
- "UnionMember0PayStatementEmployeeDeductionAttributes",
- "UnionMember0PayStatementEmployeeDeductionAttributesMetadata",
- "UnionMember0PayStatementEmployerContribution",
- "UnionMember0PayStatementEmployerContributionAttributes",
- "UnionMember0PayStatementEmployerContributionAttributesMetadata",
- "UnionMember0PayStatementTax",
- "UnionMember0PayStatementTaxAttributes",
- "UnionMember0PayStatementTaxAttributesMetadata",
- "BatchError",
- "UnionMember2",
-]
+__all__ = ["PayStatementResponseBody", "Paging"]
-class UnionMember0Paging(BaseModel):
+class Paging(BaseModel):
offset: int
"""The current start index of the returned list of elements"""
@@ -37,204 +16,7 @@ class UnionMember0Paging(BaseModel):
"""The total number of elements for the entire query (not just the given page)"""
-class UnionMember0PayStatementEarningAttributesMetadata(BaseModel):
- metadata: Dict[str, Optional[object]]
- """The metadata to be attached to the entity by existing rules.
+class PayStatementResponseBody(BaseModel):
+ paging: Paging
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class UnionMember0PayStatementEarningAttributes(BaseModel):
- metadata: UnionMember0PayStatementEarningAttributesMetadata
-
-
-class UnionMember0PayStatementEarning(BaseModel):
- amount: Optional[int] = None
- """The earnings amount in cents."""
-
- currency: Optional[str] = None
- """The earnings currency code."""
-
- hours: Optional[float] = None
- """The number of hours associated with this earning.
-
- (For salaried employees, this could be hours per pay period, `0` or `null`,
- depending on the provider).
- """
-
- name: Optional[str] = None
- """The exact name of the deduction from the pay statement."""
-
- type: Optional[
- Literal[
- "salary",
- "wage",
- "reimbursement",
- "overtime",
- "severance",
- "double_overtime",
- "pto",
- "sick",
- "bonus",
- "commission",
- "tips",
- "1099",
- "other",
- ]
- ] = None
- """The type of earning."""
-
- attributes: Optional[UnionMember0PayStatementEarningAttributes] = None
-
-
-class UnionMember0PayStatementEmployeeDeductionAttributesMetadata(BaseModel):
- metadata: Dict[str, Optional[object]]
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class UnionMember0PayStatementEmployeeDeductionAttributes(BaseModel):
- metadata: UnionMember0PayStatementEmployeeDeductionAttributesMetadata
-
-
-class UnionMember0PayStatementEmployeeDeduction(BaseModel):
- amount: Optional[int] = None
- """The deduction amount in cents."""
-
- currency: Optional[str] = None
- """The deduction currency."""
-
- name: Optional[str] = None
- """The deduction name from the pay statement."""
-
- pre_tax: Optional[bool] = None
- """Boolean indicating if the deduction is pre-tax."""
-
- type: Optional[BenefitType] = None
- """Type of benefit."""
-
- attributes: Optional[UnionMember0PayStatementEmployeeDeductionAttributes] = None
-
-
-class UnionMember0PayStatementEmployerContributionAttributesMetadata(BaseModel):
- metadata: Dict[str, Optional[object]]
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class UnionMember0PayStatementEmployerContributionAttributes(BaseModel):
- metadata: UnionMember0PayStatementEmployerContributionAttributesMetadata
-
-
-class UnionMember0PayStatementEmployerContribution(BaseModel):
- currency: Optional[str] = None
- """The contribution currency."""
-
- name: Optional[str] = None
- """The contribution name from the pay statement."""
-
- type: Optional[BenefitType] = None
- """Type of benefit."""
-
- amount: Optional[int] = None
- """The contribution amount in cents."""
-
- attributes: Optional[UnionMember0PayStatementEmployerContributionAttributes] = None
-
-
-class UnionMember0PayStatementTaxAttributesMetadata(BaseModel):
- metadata: Dict[str, Optional[object]]
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class UnionMember0PayStatementTaxAttributes(BaseModel):
- metadata: UnionMember0PayStatementTaxAttributesMetadata
-
-
-class UnionMember0PayStatementTax(BaseModel):
- currency: Optional[str] = None
- """The currency code."""
-
- employer: Optional[bool] = None
- """`true` if the amount is paid by the employers."""
-
- name: Optional[str] = None
- """The exact name of tax from the pay statement."""
-
- type: Optional[Literal["state", "federal", "local", "fica"]] = None
- """The type of taxes."""
-
- amount: Optional[int] = None
- """The tax amount in cents."""
-
- attributes: Optional[UnionMember0PayStatementTaxAttributes] = None
-
-
-class UnionMember0PayStatement(BaseModel):
- earnings: Optional[List[Optional[UnionMember0PayStatementEarning]]] = None
- """The array of earnings objects associated with this pay statement"""
-
- employee_deductions: Optional[List[Optional[UnionMember0PayStatementEmployeeDeduction]]] = None
- """The array of deductions objects associated with this pay statement."""
-
- employer_contributions: Optional[List[Optional[UnionMember0PayStatementEmployerContribution]]] = None
-
- gross_pay: Optional[Money] = None
-
- individual_id: str
- """A stable Finch `id` (UUID v4) for an individual in the company"""
-
- net_pay: Optional[Money] = None
-
- payment_method: Optional[Literal["check", "direct_deposit", "other"]] = None
- """The payment method."""
-
- taxes: Optional[List[Optional[UnionMember0PayStatementTax]]] = None
- """The array of taxes objects associated with this pay statement."""
-
- total_hours: Optional[float] = None
- """The number of hours worked for this pay period"""
-
- type: Optional[Literal["off_cycle_payroll", "one_time_payment", "regular_payroll"]] = None
- """The type of the payment associated with the pay statement."""
-
-
-class UnionMember0(BaseModel):
- paging: UnionMember0Paging
-
- pay_statements: List[UnionMember0PayStatement]
-
-
-class BatchError(BaseModel):
- code: float
-
- message: str
-
- name: str
-
- finch_code: Optional[str] = None
-
-
-class UnionMember2(BaseModel):
- code: Literal[202]
-
- finch_code: Literal["data_sync_in_progress"]
-
- message: Literal["The pay statements for this payment are being fetched. Please check back later."]
-
- name: Literal["accepted"]
-
-
-PayStatementResponseBody: TypeAlias = Union[UnionMember0, BatchError, UnionMember2]
+ pay_statements: List[PayStatement]
diff --git a/src/finch/types/sandbox/payment_create_params.py b/src/finch/types/sandbox/payment_create_params.py
index d17c1dd2..2bff98b7 100644
--- a/src/finch/types/sandbox/payment_create_params.py
+++ b/src/finch/types/sandbox/payment_create_params.py
@@ -2,209 +2,17 @@
from __future__ import annotations
-from typing import Dict, Iterable, Optional
-from typing_extensions import Literal, Required, TypedDict
+from typing import Iterable
+from typing_extensions import TypedDict
-from ..money_param import MoneyParam
-from ..hris.benefit_type import BenefitType
+from ..hris.pay_statement_param import PayStatementParam
-__all__ = [
- "PaymentCreateParams",
- "PayStatement",
- "PayStatementEarning",
- "PayStatementEarningAttributes",
- "PayStatementEarningAttributesMetadata",
- "PayStatementEmployeeDeduction",
- "PayStatementEmployeeDeductionAttributes",
- "PayStatementEmployeeDeductionAttributesMetadata",
- "PayStatementEmployerContribution",
- "PayStatementEmployerContributionAttributes",
- "PayStatementEmployerContributionAttributesMetadata",
- "PayStatementTax",
- "PayStatementTaxAttributes",
- "PayStatementTaxAttributesMetadata",
-]
+__all__ = ["PaymentCreateParams"]
class PaymentCreateParams(TypedDict, total=False):
end_date: str
- pay_statements: Iterable[PayStatement]
+ pay_statements: Iterable[PayStatementParam]
start_date: str
-
-
-class PayStatementEarningAttributesMetadata(TypedDict, total=False):
- metadata: Required[Dict[str, Optional[object]]]
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class PayStatementEarningAttributes(TypedDict, total=False):
- metadata: Required[PayStatementEarningAttributesMetadata]
-
-
-class PayStatementEarning(TypedDict, total=False):
- amount: Required[Optional[int]]
- """The earnings amount in cents."""
-
- currency: Required[Optional[str]]
- """The earnings currency code."""
-
- hours: Required[Optional[float]]
- """The number of hours associated with this earning.
-
- (For salaried employees, this could be hours per pay period, `0` or `null`,
- depending on the provider).
- """
-
- name: Required[Optional[str]]
- """The exact name of the deduction from the pay statement."""
-
- type: Required[
- Optional[
- Literal[
- "salary",
- "wage",
- "reimbursement",
- "overtime",
- "severance",
- "double_overtime",
- "pto",
- "sick",
- "bonus",
- "commission",
- "tips",
- "1099",
- "other",
- ]
- ]
- ]
- """The type of earning."""
-
- attributes: Optional[PayStatementEarningAttributes]
-
-
-class PayStatementEmployeeDeductionAttributesMetadata(TypedDict, total=False):
- metadata: Required[Dict[str, Optional[object]]]
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class PayStatementEmployeeDeductionAttributes(TypedDict, total=False):
- metadata: Required[PayStatementEmployeeDeductionAttributesMetadata]
-
-
-class PayStatementEmployeeDeduction(TypedDict, total=False):
- amount: Required[Optional[int]]
- """The deduction amount in cents."""
-
- currency: Required[Optional[str]]
- """The deduction currency."""
-
- name: Required[Optional[str]]
- """The deduction name from the pay statement."""
-
- pre_tax: Required[Optional[bool]]
- """Boolean indicating if the deduction is pre-tax."""
-
- type: Required[Optional[BenefitType]]
- """Type of benefit."""
-
- attributes: Optional[PayStatementEmployeeDeductionAttributes]
-
-
-class PayStatementEmployerContributionAttributesMetadata(TypedDict, total=False):
- metadata: Required[Dict[str, Optional[object]]]
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class PayStatementEmployerContributionAttributes(TypedDict, total=False):
- metadata: Required[PayStatementEmployerContributionAttributesMetadata]
-
-
-class PayStatementEmployerContribution(TypedDict, total=False):
- currency: Required[Optional[str]]
- """The contribution currency."""
-
- name: Required[Optional[str]]
- """The contribution name from the pay statement."""
-
- type: Required[Optional[BenefitType]]
- """Type of benefit."""
-
- amount: Optional[int]
- """The contribution amount in cents."""
-
- attributes: Optional[PayStatementEmployerContributionAttributes]
-
-
-class PayStatementTaxAttributesMetadata(TypedDict, total=False):
- metadata: Required[Dict[str, Optional[object]]]
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class PayStatementTaxAttributes(TypedDict, total=False):
- metadata: Required[PayStatementTaxAttributesMetadata]
-
-
-class PayStatementTax(TypedDict, total=False):
- currency: Required[Optional[str]]
- """The currency code."""
-
- employer: Required[Optional[bool]]
- """`true` if the amount is paid by the employers."""
-
- name: Required[Optional[str]]
- """The exact name of tax from the pay statement."""
-
- type: Required[Optional[Literal["state", "federal", "local", "fica"]]]
- """The type of taxes."""
-
- amount: Optional[int]
- """The tax amount in cents."""
-
- attributes: Optional[PayStatementTaxAttributes]
-
-
-class PayStatement(TypedDict, total=False):
- earnings: Required[Optional[Iterable[Optional[PayStatementEarning]]]]
- """The array of earnings objects associated with this pay statement"""
-
- employee_deductions: Required[Optional[Iterable[Optional[PayStatementEmployeeDeduction]]]]
- """The array of deductions objects associated with this pay statement."""
-
- employer_contributions: Required[Optional[Iterable[Optional[PayStatementEmployerContribution]]]]
-
- gross_pay: Required[Optional[MoneyParam]]
-
- individual_id: Required[str]
- """A stable Finch `id` (UUID v4) for an individual in the company"""
-
- net_pay: Required[Optional[MoneyParam]]
-
- payment_method: Required[Optional[Literal["check", "direct_deposit", "other"]]]
- """The payment method."""
-
- taxes: Required[Optional[Iterable[Optional[PayStatementTax]]]]
- """The array of taxes objects associated with this pay statement."""
-
- total_hours: Required[Optional[float]]
- """The number of hours worked for this pay period"""
-
- type: Required[Optional[Literal["off_cycle_payroll", "one_time_payment", "regular_payroll"]]]
- """The type of the payment associated with the pay statement."""
From d1dc8a4aa3df7a0199ab04958a766b56b3be0dc5 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 4 Jun 2025 15:45:13 +0000
Subject: [PATCH 12/29] fix(client): manual fix
---
.stats.yml | 2 +-
api.md | 3 +-
src/finch/resources/hris/benefits/benefits.py | 14 +++---
src/finch/types/hris/__init__.py | 4 +-
.../hris/benefit_features_and_operations.py | 44 ++-----------------
...efits_response.py => supported_benefit.py} | 4 +-
tests/api_resources/hris/test_benefits.py | 14 +++---
7 files changed, 23 insertions(+), 62 deletions(-)
rename src/finch/types/hris/{benefit_list_supported_benefits_response.py => supported_benefit.py} (92%)
diff --git a/.stats.yml b/.stats.yml
index 8d6b9198..a5a080cf 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 46
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-1a82d3230c420c8562600b0ad45133d79ab68ffd21d524ab26eef11e163dba09.yml
openapi_spec_hash: 7bd02ce73505e51c5fd78608fed55c62
-config_hash: bb896000dc7e790e5c9ddda2549fb7ac
+config_hash: 5146b12344dae76238940989dac1e8a0
diff --git a/api.md b/api.md
index 3a65985b..f6de6000 100644
--- a/api.md
+++ b/api.md
@@ -175,7 +175,6 @@ from finch.types.hris import (
SupportPerBenefitType,
SupportedBenefit,
UpdateCompanyBenefitResponse,
- BenefitListSupportedBenefitsResponse,
BenfitContribution,
)
```
@@ -186,7 +185,7 @@ Methods:
- client.hris.benefits.retrieve(benefit_id) -> CompanyBenefit
- client.hris.benefits.update(benefit_id, \*\*params) -> UpdateCompanyBenefitResponse
- client.hris.benefits.list() -> SyncSinglePage[CompanyBenefit]
-- client.hris.benefits.list_supported_benefits() -> SyncSinglePage[BenefitListSupportedBenefitsResponse]
+- client.hris.benefits.list_supported_benefits() -> SyncSinglePage[SupportedBenefit]
### Individuals
diff --git a/src/finch/resources/hris/benefits/benefits.py b/src/finch/resources/hris/benefits/benefits.py
index ed4dbe08..eecb7b7f 100644
--- a/src/finch/resources/hris/benefits/benefits.py
+++ b/src/finch/resources/hris/benefits/benefits.py
@@ -26,9 +26,9 @@
from ....types.hris.benefit_type import BenefitType
from ....types.hris.company_benefit import CompanyBenefit
from ....types.hris.benefit_frequency import BenefitFrequency
+from ....types.hris.supported_benefit import SupportedBenefit
from ....types.hris.update_company_benefit_response import UpdateCompanyBenefitResponse
from ....types.hris.create_company_benefits_response import CreateCompanyBenefitsResponse
-from ....types.hris.benefit_list_supported_benefits_response import BenefitListSupportedBenefitsResponse
__all__ = ["Benefits", "AsyncBenefits"]
@@ -211,15 +211,15 @@ def list_supported_benefits(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> SyncSinglePage[BenefitListSupportedBenefitsResponse]:
+ ) -> SyncSinglePage[SupportedBenefit]:
"""Get deductions metadata"""
return self._get_api_list(
"/employer/benefits/meta",
- page=SyncSinglePage[BenefitListSupportedBenefitsResponse],
+ page=SyncSinglePage[SupportedBenefit],
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- model=BenefitListSupportedBenefitsResponse,
+ model=SupportedBenefit,
)
@@ -401,15 +401,15 @@ def list_supported_benefits(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> AsyncPaginator[BenefitListSupportedBenefitsResponse, AsyncSinglePage[BenefitListSupportedBenefitsResponse]]:
+ ) -> AsyncPaginator[SupportedBenefit, AsyncSinglePage[SupportedBenefit]]:
"""Get deductions metadata"""
return self._get_api_list(
"/employer/benefits/meta",
- page=AsyncSinglePage[BenefitListSupportedBenefitsResponse],
+ page=AsyncSinglePage[SupportedBenefit],
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- model=BenefitListSupportedBenefitsResponse,
+ model=SupportedBenefit,
)
diff --git a/src/finch/types/hris/__init__.py b/src/finch/types/hris/__init__.py
index 277f19a8..dccd7885 100644
--- a/src/finch/types/hris/__init__.py
+++ b/src/finch/types/hris/__init__.py
@@ -14,6 +14,7 @@
from .benefits_support import BenefitsSupport as BenefitsSupport
from .benefit_frequency import BenefitFrequency as BenefitFrequency
from .document_response import DocumentResponse as DocumentResponse
+from .supported_benefit import SupportedBenefit as SupportedBenefit
from .benfit_contribution import BenfitContribution as BenfitContribution
from .individual_response import IndividualResponse as IndividualResponse
from .pay_statement_param import PayStatementParam as PayStatementParam
@@ -38,6 +39,3 @@
from .directory_list_individuals_params import DirectoryListIndividualsParams as DirectoryListIndividualsParams
from .pay_statement_retrieve_many_params import PayStatementRetrieveManyParams as PayStatementRetrieveManyParams
from .pay_statement_data_sync_in_progress import PayStatementDataSyncInProgress as PayStatementDataSyncInProgress
-from .benefit_list_supported_benefits_response import (
- BenefitListSupportedBenefitsResponse as BenefitListSupportedBenefitsResponse,
-)
diff --git a/src/finch/types/hris/benefit_features_and_operations.py b/src/finch/types/hris/benefit_features_and_operations.py
index b357f72f..89f9be7a 100644
--- a/src/finch/types/hris/benefit_features_and_operations.py
+++ b/src/finch/types/hris/benefit_features_and_operations.py
@@ -1,51 +1,15 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import List, Optional
-from typing_extensions import Literal
+from typing import Optional
from ..._models import BaseModel
-from .benefit_frequency import BenefitFrequency
+from .supported_benefit import SupportedBenefit
from .support_per_benefit_type import SupportPerBenefitType
-__all__ = ["BenefitFeaturesAndOperations", "SupportedFeatures"]
-
-
-class SupportedFeatures(BaseModel):
- annual_maximum: Optional[bool] = None
- """Whether the provider supports an annual maximum for this benefit."""
-
- catch_up: Optional[bool] = None
- """Whether the provider supports catch up for this benefit.
-
- This field will only be true for retirement benefits.
- """
-
- company_contribution: Optional[List[Optional[Literal["fixed", "percent"]]]] = None
- """Supported contribution types.
-
- An empty array indicates contributions are not supported.
- """
-
- description: Optional[str] = None
-
- employee_deduction: Optional[List[Optional[Literal["fixed", "percent"]]]] = None
- """Supported deduction types.
-
- An empty array indicates deductions are not supported.
- """
-
- frequencies: Optional[List[Optional[BenefitFrequency]]] = None
- """The list of frequencies supported by the provider for this benefit"""
-
- hsa_contribution_limit: Optional[List[Optional[Literal["individual", "family"]]]] = None
- """Whether the provider supports HSA contribution limits.
-
- Empty if this feature is not supported for the benefit. This array only has
- values for HSA benefits.
- """
+__all__ = ["BenefitFeaturesAndOperations"]
class BenefitFeaturesAndOperations(BaseModel):
- supported_features: Optional[SupportedFeatures] = None
+ supported_features: Optional[SupportedBenefit] = None
supported_operations: Optional[SupportPerBenefitType] = None
diff --git a/src/finch/types/hris/benefit_list_supported_benefits_response.py b/src/finch/types/hris/supported_benefit.py
similarity index 92%
rename from src/finch/types/hris/benefit_list_supported_benefits_response.py
rename to src/finch/types/hris/supported_benefit.py
index e29b2b7f..08d94399 100644
--- a/src/finch/types/hris/benefit_list_supported_benefits_response.py
+++ b/src/finch/types/hris/supported_benefit.py
@@ -6,10 +6,10 @@
from ..._models import BaseModel
from .benefit_frequency import BenefitFrequency
-__all__ = ["BenefitListSupportedBenefitsResponse"]
+__all__ = ["SupportedBenefit"]
-class BenefitListSupportedBenefitsResponse(BaseModel):
+class SupportedBenefit(BaseModel):
annual_maximum: Optional[bool] = None
"""Whether the provider supports an annual maximum for this benefit."""
diff --git a/tests/api_resources/hris/test_benefits.py b/tests/api_resources/hris/test_benefits.py
index 23869d81..05b9348f 100644
--- a/tests/api_resources/hris/test_benefits.py
+++ b/tests/api_resources/hris/test_benefits.py
@@ -12,9 +12,9 @@
from finch.pagination import SyncSinglePage, AsyncSinglePage
from finch.types.hris import (
CompanyBenefit,
+ SupportedBenefit,
UpdateCompanyBenefitResponse,
CreateCompanyBenefitsResponse,
- BenefitListSupportedBenefitsResponse,
)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -178,7 +178,7 @@ def test_streaming_response_list(self, client: Finch) -> None:
@parametrize
def test_method_list_supported_benefits(self, client: Finch) -> None:
benefit = client.hris.benefits.list_supported_benefits()
- assert_matches_type(SyncSinglePage[BenefitListSupportedBenefitsResponse], benefit, path=["response"])
+ assert_matches_type(SyncSinglePage[SupportedBenefit], benefit, path=["response"])
@parametrize
def test_raw_response_list_supported_benefits(self, client: Finch) -> None:
@@ -187,7 +187,7 @@ def test_raw_response_list_supported_benefits(self, client: Finch) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
benefit = response.parse()
- assert_matches_type(SyncSinglePage[BenefitListSupportedBenefitsResponse], benefit, path=["response"])
+ assert_matches_type(SyncSinglePage[SupportedBenefit], benefit, path=["response"])
@parametrize
def test_streaming_response_list_supported_benefits(self, client: Finch) -> None:
@@ -196,7 +196,7 @@ def test_streaming_response_list_supported_benefits(self, client: Finch) -> None
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
benefit = response.parse()
- assert_matches_type(SyncSinglePage[BenefitListSupportedBenefitsResponse], benefit, path=["response"])
+ assert_matches_type(SyncSinglePage[SupportedBenefit], benefit, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -359,7 +359,7 @@ async def test_streaming_response_list(self, async_client: AsyncFinch) -> None:
@parametrize
async def test_method_list_supported_benefits(self, async_client: AsyncFinch) -> None:
benefit = await async_client.hris.benefits.list_supported_benefits()
- assert_matches_type(AsyncSinglePage[BenefitListSupportedBenefitsResponse], benefit, path=["response"])
+ assert_matches_type(AsyncSinglePage[SupportedBenefit], benefit, path=["response"])
@parametrize
async def test_raw_response_list_supported_benefits(self, async_client: AsyncFinch) -> None:
@@ -368,7 +368,7 @@ async def test_raw_response_list_supported_benefits(self, async_client: AsyncFin
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
benefit = response.parse()
- assert_matches_type(AsyncSinglePage[BenefitListSupportedBenefitsResponse], benefit, path=["response"])
+ assert_matches_type(AsyncSinglePage[SupportedBenefit], benefit, path=["response"])
@parametrize
async def test_streaming_response_list_supported_benefits(self, async_client: AsyncFinch) -> None:
@@ -377,6 +377,6 @@ async def test_streaming_response_list_supported_benefits(self, async_client: As
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
benefit = await response.parse()
- assert_matches_type(AsyncSinglePage[BenefitListSupportedBenefitsResponse], benefit, path=["response"])
+ assert_matches_type(AsyncSinglePage[SupportedBenefit], benefit, path=["response"])
assert cast(Any, response.is_closed) is True
From 68ed0de62a49c340a1ee7dc71d941d71b6d780c4 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 11 Jun 2025 13:50:55 +0000
Subject: [PATCH 13/29] feat(api): api update
---
.stats.yml | 4 ++--
src/finch/types/income.py | 3 ++-
src/finch/types/income_param.py | 9 ++++++---
tests/api_resources/sandbox/test_directory.py | 9 +++++----
tests/api_resources/sandbox/test_employment.py | 9 +++++----
5 files changed, 20 insertions(+), 14 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index a5a080cf..2d5b94d6 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 46
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-1a82d3230c420c8562600b0ad45133d79ab68ffd21d524ab26eef11e163dba09.yml
-openapi_spec_hash: 7bd02ce73505e51c5fd78608fed55c62
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-dd2cdf274497a0f36aaa493457a1144f51f6c28e14b4521acca9715747688b50.yml
+openapi_spec_hash: 8bbc0ac9dae255055b69c1d040c43c4b
config_hash: 5146b12344dae76238940989dac1e8a0
diff --git a/src/finch/types/income.py b/src/finch/types/income.py
index d01ba0e9..2b1f36b2 100644
--- a/src/finch/types/income.py
+++ b/src/finch/types/income.py
@@ -1,6 +1,7 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
from typing import Optional
+from datetime import date
from typing_extensions import Literal
from .._models import BaseModel
@@ -15,7 +16,7 @@ class Income(BaseModel):
currency: Optional[str] = None
"""The currency code."""
- effective_date: Optional[str] = None
+ effective_date: Optional[date] = None
"""The date the income amount went into effect."""
unit: Optional[
diff --git a/src/finch/types/income_param.py b/src/finch/types/income_param.py
index d27b1f8b..ed0bb0c2 100644
--- a/src/finch/types/income_param.py
+++ b/src/finch/types/income_param.py
@@ -2,8 +2,11 @@
from __future__ import annotations
-from typing import Optional
-from typing_extensions import Literal, Required, TypedDict
+from typing import Union, Optional
+from datetime import date
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from .._utils import PropertyInfo
__all__ = ["IncomeParam"]
@@ -15,7 +18,7 @@ class IncomeParam(TypedDict, total=False):
currency: Required[Optional[str]]
"""The currency code."""
- effective_date: Required[Optional[str]]
+ effective_date: Required[Annotated[Union[str, date, None], PropertyInfo(format="iso8601")]]
"""The date the income amount went into effect."""
unit: Required[
diff --git a/tests/api_resources/sandbox/test_directory.py b/tests/api_resources/sandbox/test_directory.py
index 20154dec..448904f7 100644
--- a/tests/api_resources/sandbox/test_directory.py
+++ b/tests/api_resources/sandbox/test_directory.py
@@ -9,6 +9,7 @@
from finch import Finch, AsyncFinch
from tests.utils import assert_matches_type
+from finch._utils import parse_date
from finch.types.sandbox import DirectoryCreateResponse
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -55,14 +56,14 @@ def test_method_create_with_all_params(self, client: Finch) -> None:
"income": {
"amount": 0,
"currency": "currency",
- "effective_date": "effective_date",
+ "effective_date": parse_date("2019-12-27"),
"unit": "yearly",
},
"income_history": [
{
"amount": 0,
"currency": "currency",
- "effective_date": "effective_date",
+ "effective_date": parse_date("2019-12-27"),
"unit": "yearly",
}
],
@@ -169,14 +170,14 @@ async def test_method_create_with_all_params(self, async_client: AsyncFinch) ->
"income": {
"amount": 0,
"currency": "currency",
- "effective_date": "effective_date",
+ "effective_date": parse_date("2019-12-27"),
"unit": "yearly",
},
"income_history": [
{
"amount": 0,
"currency": "currency",
- "effective_date": "effective_date",
+ "effective_date": parse_date("2019-12-27"),
"unit": "yearly",
}
],
diff --git a/tests/api_resources/sandbox/test_employment.py b/tests/api_resources/sandbox/test_employment.py
index 40e5ffc5..04f47254 100644
--- a/tests/api_resources/sandbox/test_employment.py
+++ b/tests/api_resources/sandbox/test_employment.py
@@ -9,6 +9,7 @@
from finch import Finch, AsyncFinch
from tests.utils import assert_matches_type
+from finch._utils import parse_date
from finch.types.sandbox import EmploymentUpdateResponse
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -46,14 +47,14 @@ def test_method_update_with_all_params(self, client: Finch) -> None:
income={
"amount": 0,
"currency": "currency",
- "effective_date": "effective_date",
+ "effective_date": parse_date("2019-12-27"),
"unit": "yearly",
},
income_history=[
{
"amount": 0,
"currency": "currency",
- "effective_date": "effective_date",
+ "effective_date": parse_date("2019-12-27"),
"unit": "yearly",
}
],
@@ -142,14 +143,14 @@ async def test_method_update_with_all_params(self, async_client: AsyncFinch) ->
income={
"amount": 0,
"currency": "currency",
- "effective_date": "effective_date",
+ "effective_date": parse_date("2019-12-27"),
"unit": "yearly",
},
income_history=[
{
"amount": 0,
"currency": "currency",
- "effective_date": "effective_date",
+ "effective_date": parse_date("2019-12-27"),
"unit": "yearly",
}
],
From 97ef62d11c21650cb6f34a69f3309cdbf034df86 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 11 Jun 2025 14:56:58 +0000
Subject: [PATCH 14/29] feat(api): api update
---
.stats.yml | 4 +-
src/finch/resources/sandbox/payment.py | 20 +-
src/finch/types/__init__.py | 1 -
src/finch/types/hris/__init__.py | 1 -
src/finch/types/hris/pay_statement_param.py | 201 ------------------
src/finch/types/money_param.py | 15 --
.../types/sandbox/payment_create_params.py | 137 +++++++++++-
tests/api_resources/sandbox/test_payment.py | 85 +++-----
8 files changed, 173 insertions(+), 291 deletions(-)
delete mode 100644 src/finch/types/hris/pay_statement_param.py
delete mode 100644 src/finch/types/money_param.py
diff --git a/.stats.yml b/.stats.yml
index 2d5b94d6..f1c7d32b 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 46
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-dd2cdf274497a0f36aaa493457a1144f51f6c28e14b4521acca9715747688b50.yml
-openapi_spec_hash: 8bbc0ac9dae255055b69c1d040c43c4b
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-f7e741bc6e0175fd96a9db5348092b90a77b0985154c0814bb681ad5dccdf19a.yml
+openapi_spec_hash: b348a9ef407a8e91dd770fcb219d4ac5
config_hash: 5146b12344dae76238940989dac1e8a0
diff --git a/src/finch/resources/sandbox/payment.py b/src/finch/resources/sandbox/payment.py
index 3f0a6397..dc2ec15e 100644
--- a/src/finch/resources/sandbox/payment.py
+++ b/src/finch/resources/sandbox/payment.py
@@ -2,7 +2,8 @@
from __future__ import annotations
-from typing import Iterable
+from typing import Union, Iterable
+from datetime import date
import httpx
@@ -14,7 +15,6 @@
from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
from ..._base_client import make_request_options
from ...types.sandbox import payment_create_params
-from ...types.hris.pay_statement_param import PayStatementParam
from ...types.sandbox.payment_create_response import PaymentCreateResponse
__all__ = ["Payment", "AsyncPayment"]
@@ -43,9 +43,9 @@ def with_streaming_response(self) -> PaymentWithStreamingResponse:
def create(
self,
*,
- end_date: str | NotGiven = NOT_GIVEN,
- pay_statements: Iterable[PayStatementParam] | NotGiven = NOT_GIVEN,
- start_date: str | NotGiven = NOT_GIVEN,
+ end_date: Union[str, date] | NotGiven = NOT_GIVEN,
+ pay_statements: Iterable[payment_create_params.PayStatement] | NotGiven = NOT_GIVEN,
+ start_date: Union[str, date] | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -57,6 +57,8 @@ def create(
Add a new sandbox payment
Args:
+ pay_statements: Array of pay statements to include in the payment.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -105,9 +107,9 @@ def with_streaming_response(self) -> AsyncPaymentWithStreamingResponse:
async def create(
self,
*,
- end_date: str | NotGiven = NOT_GIVEN,
- pay_statements: Iterable[PayStatementParam] | NotGiven = NOT_GIVEN,
- start_date: str | NotGiven = NOT_GIVEN,
+ end_date: Union[str, date] | NotGiven = NOT_GIVEN,
+ pay_statements: Iterable[payment_create_params.PayStatement] | NotGiven = NOT_GIVEN,
+ start_date: Union[str, date] | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -119,6 +121,8 @@ async def create(
Add a new sandbox payment
Args:
+ pay_statements: Array of pay statements to include in the payment.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
diff --git a/src/finch/types/__init__.py b/src/finch/types/__init__.py
index 4c2b5d4e..14bf231d 100644
--- a/src/finch/types/__init__.py
+++ b/src/finch/types/__init__.py
@@ -12,7 +12,6 @@
)
from .location import Location as Location
from .provider import Provider as Provider
-from .money_param import MoneyParam as MoneyParam
from .income_param import IncomeParam as IncomeParam
from .company_event import CompanyEvent as CompanyEvent
from .introspection import Introspection as Introspection
diff --git a/src/finch/types/hris/__init__.py b/src/finch/types/hris/__init__.py
index dccd7885..cbb3ebd9 100644
--- a/src/finch/types/hris/__init__.py
+++ b/src/finch/types/hris/__init__.py
@@ -17,7 +17,6 @@
from .supported_benefit import SupportedBenefit as SupportedBenefit
from .benfit_contribution import BenfitContribution as BenfitContribution
from .individual_response import IndividualResponse as IndividualResponse
-from .pay_statement_param import PayStatementParam as PayStatementParam
from .payment_list_params import PaymentListParams as PaymentListParams
from .benefit_contribution import BenefitContribution as BenefitContribution
from .document_list_params import DocumentListParams as DocumentListParams
diff --git a/src/finch/types/hris/pay_statement_param.py b/src/finch/types/hris/pay_statement_param.py
deleted file mode 100644
index b5e70561..00000000
--- a/src/finch/types/hris/pay_statement_param.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing import Dict, Iterable, Optional
-from typing_extensions import Literal, Required, TypedDict
-
-from ..money_param import MoneyParam
-from .benefit_type import BenefitType
-
-__all__ = [
- "PayStatementParam",
- "Earning",
- "EarningAttributes",
- "EarningAttributesMetadata",
- "EmployeeDeduction",
- "EmployeeDeductionAttributes",
- "EmployeeDeductionAttributesMetadata",
- "EmployerContribution",
- "EmployerContributionAttributes",
- "EmployerContributionAttributesMetadata",
- "Tax",
- "TaxAttributes",
- "TaxAttributesMetadata",
-]
-
-
-class EarningAttributesMetadata(TypedDict, total=False):
- metadata: Required[Dict[str, Optional[object]]]
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class EarningAttributes(TypedDict, total=False):
- metadata: Required[EarningAttributesMetadata]
-
-
-class Earning(TypedDict, total=False):
- amount: Required[Optional[int]]
- """The earnings amount in cents."""
-
- currency: Required[Optional[str]]
- """The earnings currency code."""
-
- hours: Required[Optional[float]]
- """The number of hours associated with this earning.
-
- (For salaried employees, this could be hours per pay period, `0` or `null`,
- depending on the provider).
- """
-
- name: Required[Optional[str]]
- """The exact name of the deduction from the pay statement."""
-
- type: Required[
- Optional[
- Literal[
- "salary",
- "wage",
- "reimbursement",
- "overtime",
- "severance",
- "double_overtime",
- "pto",
- "sick",
- "bonus",
- "commission",
- "tips",
- "1099",
- "other",
- ]
- ]
- ]
- """The type of earning."""
-
- attributes: Optional[EarningAttributes]
-
-
-class EmployeeDeductionAttributesMetadata(TypedDict, total=False):
- metadata: Required[Dict[str, Optional[object]]]
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class EmployeeDeductionAttributes(TypedDict, total=False):
- metadata: Required[EmployeeDeductionAttributesMetadata]
-
-
-class EmployeeDeduction(TypedDict, total=False):
- amount: Required[Optional[int]]
- """The deduction amount in cents."""
-
- currency: Required[Optional[str]]
- """The deduction currency."""
-
- name: Required[Optional[str]]
- """The deduction name from the pay statement."""
-
- pre_tax: Required[Optional[bool]]
- """Boolean indicating if the deduction is pre-tax."""
-
- type: Required[Optional[BenefitType]]
- """Type of benefit."""
-
- attributes: Optional[EmployeeDeductionAttributes]
-
-
-class EmployerContributionAttributesMetadata(TypedDict, total=False):
- metadata: Required[Dict[str, Optional[object]]]
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class EmployerContributionAttributes(TypedDict, total=False):
- metadata: Required[EmployerContributionAttributesMetadata]
-
-
-class EmployerContribution(TypedDict, total=False):
- currency: Required[Optional[str]]
- """The contribution currency."""
-
- name: Required[Optional[str]]
- """The contribution name from the pay statement."""
-
- type: Required[Optional[BenefitType]]
- """Type of benefit."""
-
- amount: Optional[int]
- """The contribution amount in cents."""
-
- attributes: Optional[EmployerContributionAttributes]
-
-
-class TaxAttributesMetadata(TypedDict, total=False):
- metadata: Required[Dict[str, Optional[object]]]
- """The metadata to be attached to the entity by existing rules.
-
- It is a key-value pairs where the values can be of any type (string, number,
- boolean, object, array, etc.).
- """
-
-
-class TaxAttributes(TypedDict, total=False):
- metadata: Required[TaxAttributesMetadata]
-
-
-class Tax(TypedDict, total=False):
- currency: Required[Optional[str]]
- """The currency code."""
-
- employer: Required[Optional[bool]]
- """`true` if the amount is paid by the employers."""
-
- name: Required[Optional[str]]
- """The exact name of tax from the pay statement."""
-
- type: Required[Optional[Literal["state", "federal", "local", "fica"]]]
- """The type of taxes."""
-
- amount: Optional[int]
- """The tax amount in cents."""
-
- attributes: Optional[TaxAttributes]
-
-
-class PayStatementParam(TypedDict, total=False):
- earnings: Required[Optional[Iterable[Optional[Earning]]]]
- """The array of earnings objects associated with this pay statement"""
-
- employee_deductions: Required[Optional[Iterable[Optional[EmployeeDeduction]]]]
- """The array of deductions objects associated with this pay statement."""
-
- employer_contributions: Required[Optional[Iterable[Optional[EmployerContribution]]]]
-
- gross_pay: Required[Optional[MoneyParam]]
-
- individual_id: Required[str]
- """A stable Finch `id` (UUID v4) for an individual in the company"""
-
- net_pay: Required[Optional[MoneyParam]]
-
- payment_method: Required[Optional[Literal["check", "direct_deposit", "other"]]]
- """The payment method."""
-
- taxes: Required[Optional[Iterable[Optional[Tax]]]]
- """The array of taxes objects associated with this pay statement."""
-
- total_hours: Required[Optional[float]]
- """The number of hours worked for this pay period"""
-
- type: Required[Optional[Literal["off_cycle_payroll", "one_time_payment", "regular_payroll"]]]
- """The type of the payment associated with the pay statement."""
diff --git a/src/finch/types/money_param.py b/src/finch/types/money_param.py
deleted file mode 100644
index 03e202e6..00000000
--- a/src/finch/types/money_param.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing import Optional
-from typing_extensions import Required, TypedDict
-
-__all__ = ["MoneyParam"]
-
-
-class MoneyParam(TypedDict, total=False):
- amount: Required[Optional[int]]
- """Amount for money object (in cents)"""
-
- currency: Required[str]
diff --git a/src/finch/types/sandbox/payment_create_params.py b/src/finch/types/sandbox/payment_create_params.py
index 2bff98b7..1f74fb81 100644
--- a/src/finch/types/sandbox/payment_create_params.py
+++ b/src/finch/types/sandbox/payment_create_params.py
@@ -2,17 +2,140 @@
from __future__ import annotations
-from typing import Iterable
-from typing_extensions import TypedDict
+from typing import Union, Iterable, Optional
+from datetime import date
+from typing_extensions import Literal, Required, Annotated, TypedDict
-from ..hris.pay_statement_param import PayStatementParam
+from ..._utils import PropertyInfo
-__all__ = ["PaymentCreateParams"]
+__all__ = [
+ "PaymentCreateParams",
+ "PayStatement",
+ "PayStatementEarning",
+ "PayStatementEmployeeDeduction",
+ "PayStatementEmployerContribution",
+ "PayStatementTax",
+]
class PaymentCreateParams(TypedDict, total=False):
- end_date: str
+ end_date: Annotated[Union[str, date], PropertyInfo(format="iso8601")]
- pay_statements: Iterable[PayStatementParam]
+ pay_statements: Iterable[PayStatement]
+ """Array of pay statements to include in the payment."""
- start_date: str
+ start_date: Annotated[Union[str, date], PropertyInfo(format="iso8601")]
+
+
+class PayStatementEarning(TypedDict, total=False):
+ amount: int
+
+ hours: float
+
+ name: str
+
+ type: Literal[
+ "bonus",
+ "commission",
+ "double_overtime",
+ "other",
+ "overtime",
+ "pto",
+ "reimbursement",
+ "salary",
+ "severance",
+ "sick",
+ "tips",
+ "wage",
+ "1099",
+ ]
+
+
+class PayStatementEmployeeDeduction(TypedDict, total=False):
+ amount: int
+
+ name: str
+
+ pre_tax: bool
+
+ type: Literal[
+ "457",
+ "401k",
+ "401k_roth",
+ "401k_loan",
+ "403b",
+ "403b_roth",
+ "457_roth",
+ "commuter",
+ "custom_post_tax",
+ "custom_pre_tax",
+ "fsa_dependent_care",
+ "fsa_medical",
+ "hsa_post",
+ "hsa_pre",
+ "s125_dental",
+ "s125_medical",
+ "s125_vision",
+ "simple",
+ "simple_ira",
+ ]
+
+
+class PayStatementEmployerContribution(TypedDict, total=False):
+ amount: int
+
+ name: str
+
+ type: Literal[
+ "457",
+ "401k",
+ "401k_roth",
+ "401k_loan",
+ "403b",
+ "403b_roth",
+ "457_roth",
+ "commuter",
+ "custom_post_tax",
+ "custom_pre_tax",
+ "fsa_dependent_care",
+ "fsa_medical",
+ "hsa_post",
+ "hsa_pre",
+ "s125_dental",
+ "s125_medical",
+ "s125_vision",
+ "simple",
+ "simple_ira",
+ ]
+
+
+class PayStatementTax(TypedDict, total=False):
+ amount: int
+
+ employer: bool
+
+ name: str
+
+ type: Literal["federal", "fica", "local", "state"]
+
+
+class PayStatement(TypedDict, total=False):
+ individual_id: Required[str]
+
+ earnings: Iterable[PayStatementEarning]
+
+ employee_deductions: Iterable[PayStatementEmployeeDeduction]
+
+ employer_contributions: Iterable[PayStatementEmployerContribution]
+
+ gross_pay: int
+
+ net_pay: int
+
+ payment_method: Optional[Literal["check", "direct_deposit", "other"]]
+
+ taxes: Iterable[PayStatementTax]
+
+ total_hours: float
+
+ type: Optional[Literal["off_cycle_payroll", "one_time_payment", "regular_payroll"]]
diff --git a/tests/api_resources/sandbox/test_payment.py b/tests/api_resources/sandbox/test_payment.py
index 5ef35cc5..7f5f4bda 100644
--- a/tests/api_resources/sandbox/test_payment.py
+++ b/tests/api_resources/sandbox/test_payment.py
@@ -9,6 +9,7 @@
from finch import Finch, AsyncFinch
from tests.utils import assert_matches_type
+from finch._utils import parse_date
from finch.types.sandbox import PaymentCreateResponse
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -25,63 +26,49 @@ def test_method_create(self, client: Finch) -> None:
@parametrize
def test_method_create_with_all_params(self, client: Finch) -> None:
payment = client.sandbox.payment.create(
- end_date="end_date",
+ end_date=parse_date("2019-12-27"),
pay_statements=[
{
+ "individual_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
"earnings": [
{
"amount": 0,
- "currency": "currency",
"hours": 0,
- "name": "name",
- "type": "salary",
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
+ "name": "x",
+ "type": "bonus",
}
],
"employee_deductions": [
{
"amount": 0,
- "currency": "currency",
- "name": "name",
+ "name": "x",
"pre_tax": True,
"type": "457",
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
}
],
"employer_contributions": [
{
- "currency": "currency",
- "name": "name",
- "type": "457",
"amount": 0,
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
+ "name": "x",
+ "type": "457",
}
],
- "gross_pay": {
- "amount": 0,
- "currency": "currency",
- },
- "individual_id": "individual_id",
- "net_pay": {
- "amount": 0,
- "currency": "currency",
- },
+ "gross_pay": 1,
+ "net_pay": 9007199254740991,
"payment_method": "check",
"taxes": [
{
- "currency": "currency",
- "employer": True,
- "name": "name",
- "type": "state",
"amount": 0,
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
+ "employer": True,
+ "name": "x",
+ "type": "federal",
}
],
- "total_hours": 0,
+ "total_hours": 1,
"type": "off_cycle_payroll",
}
],
- start_date="start_date",
+ start_date=parse_date("2019-12-27"),
)
assert_matches_type(PaymentCreateResponse, payment, path=["response"])
@@ -117,63 +104,49 @@ async def test_method_create(self, async_client: AsyncFinch) -> None:
@parametrize
async def test_method_create_with_all_params(self, async_client: AsyncFinch) -> None:
payment = await async_client.sandbox.payment.create(
- end_date="end_date",
+ end_date=parse_date("2019-12-27"),
pay_statements=[
{
+ "individual_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
"earnings": [
{
"amount": 0,
- "currency": "currency",
"hours": 0,
- "name": "name",
- "type": "salary",
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
+ "name": "x",
+ "type": "bonus",
}
],
"employee_deductions": [
{
"amount": 0,
- "currency": "currency",
- "name": "name",
+ "name": "x",
"pre_tax": True,
"type": "457",
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
}
],
"employer_contributions": [
{
- "currency": "currency",
- "name": "name",
- "type": "457",
"amount": 0,
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
+ "name": "x",
+ "type": "457",
}
],
- "gross_pay": {
- "amount": 0,
- "currency": "currency",
- },
- "individual_id": "individual_id",
- "net_pay": {
- "amount": 0,
- "currency": "currency",
- },
+ "gross_pay": 1,
+ "net_pay": 9007199254740991,
"payment_method": "check",
"taxes": [
{
- "currency": "currency",
- "employer": True,
- "name": "name",
- "type": "state",
"amount": 0,
- "attributes": {"metadata": {"metadata": {"foo": {}}}},
+ "employer": True,
+ "name": "x",
+ "type": "federal",
}
],
- "total_hours": 0,
+ "total_hours": 1,
"type": "off_cycle_payroll",
}
],
- start_date="start_date",
+ start_date=parse_date("2019-12-27"),
)
assert_matches_type(PaymentCreateResponse, payment, path=["response"])
From 25017c43233e726dd80d1e7ca41eb02ec9456871 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 12 Jun 2025 10:28:34 +0000
Subject: [PATCH 15/29] chore(tests): run tests in parallel
---
pyproject.toml | 3 ++-
requirements-dev.lock | 4 ++++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
index 4b64a1e0..de5a4b98 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -54,6 +54,7 @@ dev-dependencies = [
"importlib-metadata>=6.7.0",
"rich>=13.7.1",
"nest_asyncio==1.6.0",
+ "pytest-xdist>=3.6.1",
]
[tool.rye.scripts]
@@ -125,7 +126,7 @@ replacement = '[\1](https://github.com/Finch-API/finch-api-python/tree/main/\g<2
[tool.pytest.ini_options]
testpaths = ["tests"]
-addopts = "--tb=short"
+addopts = "--tb=short -n auto"
xfail_strict = true
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "session"
diff --git a/requirements-dev.lock b/requirements-dev.lock
index 6ef50771..ddc2481e 100644
--- a/requirements-dev.lock
+++ b/requirements-dev.lock
@@ -30,6 +30,8 @@ distro==1.8.0
exceptiongroup==1.2.2
# via anyio
# via pytest
+execnet==2.1.1
+ # via pytest-xdist
filelock==3.12.4
# via virtualenv
h11==0.14.0
@@ -72,7 +74,9 @@ pygments==2.18.0
pyright==1.1.399
pytest==8.3.3
# via pytest-asyncio
+ # via pytest-xdist
pytest-asyncio==0.24.0
+pytest-xdist==3.7.0
python-dateutil==2.8.2
# via time-machine
pytz==2023.3.post1
From 260c761024d36f0bb4efc3cf5a5e0e3c22b5f83e Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 12 Jun 2025 16:43:46 +0000
Subject: [PATCH 16/29] fix(client): correctly parse binary response | stream
---
src/finch/_base_client.py | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/src/finch/_base_client.py b/src/finch/_base_client.py
index 1196bcc0..df5ea511 100644
--- a/src/finch/_base_client.py
+++ b/src/finch/_base_client.py
@@ -1086,7 +1086,14 @@ def _process_response(
origin = get_origin(cast_to) or cast_to
- if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse):
+ if (
+ inspect.isclass(origin)
+ and issubclass(origin, BaseAPIResponse)
+ # we only want to actually return the custom BaseAPIResponse class if we're
+ # returning the raw response, or if we're not streaming SSE, as if we're streaming
+ # SSE then `cast_to` doesn't actively reflect the type we need to parse into
+ and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER)))
+ ):
if not issubclass(origin, APIResponse):
raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}")
@@ -1603,7 +1610,14 @@ async def _process_response(
origin = get_origin(cast_to) or cast_to
- if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse):
+ if (
+ inspect.isclass(origin)
+ and issubclass(origin, BaseAPIResponse)
+ # we only want to actually return the custom BaseAPIResponse class if we're
+ # returning the raw response, or if we're not streaming SSE, as if we're streaming
+ # SSE then `cast_to` doesn't actively reflect the type we need to parse into
+ and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER)))
+ ):
if not issubclass(origin, AsyncAPIResponse):
raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}")
From e51c8f78bb45e0c3dbce5dc67401390078a2fd33 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 16 Jun 2025 14:20:05 +0000
Subject: [PATCH 17/29] chore(tests): add tests for httpx client instantiation
& proxies
---
tests/test_client.py | 53 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)
diff --git a/tests/test_client.py b/tests/test_client.py
index 1a2cd599..3adf40f0 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -25,7 +25,14 @@
from finch._types import Omit
from finch._models import BaseModel, FinalRequestOptions
from finch._exceptions import APIStatusError, APIResponseValidationError
-from finch._base_client import DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, make_request_options
+from finch._base_client import (
+ DEFAULT_TIMEOUT,
+ HTTPX_DEFAULT_TIMEOUT,
+ BaseClient,
+ DefaultHttpxClient,
+ DefaultAsyncHttpxClient,
+ make_request_options,
+)
from .utils import update_env
@@ -829,6 +836,28 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
assert response.retries_taken == failures_before_success
assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
+ def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None:
+ # Test that the proxy environment variables are set correctly
+ monkeypatch.setenv("HTTPS_PROXY", "https://example.org")
+
+ client = DefaultHttpxClient()
+
+ mounts = tuple(client._mounts.items())
+ assert len(mounts) == 1
+ assert mounts[0][0].pattern == "https://"
+
+ @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning")
+ def test_default_client_creation(self) -> None:
+ # Ensure that the client can be initialized without any exceptions
+ DefaultHttpxClient(
+ verify=True,
+ cert=None,
+ trust_env=True,
+ http1=True,
+ http2=False,
+ limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
+ )
+
@pytest.mark.respx(base_url=base_url)
def test_follow_redirects(self, respx_mock: MockRouter) -> None:
# Test that the default follow_redirects=True allows following redirects
@@ -1700,6 +1729,28 @@ async def test_main() -> None:
time.sleep(0.1)
+ async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None:
+ # Test that the proxy environment variables are set correctly
+ monkeypatch.setenv("HTTPS_PROXY", "https://example.org")
+
+ client = DefaultAsyncHttpxClient()
+
+ mounts = tuple(client._mounts.items())
+ assert len(mounts) == 1
+ assert mounts[0][0].pattern == "https://"
+
+ @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning")
+ async def test_default_client_creation(self) -> None:
+ # Ensure that the client can be initialized without any exceptions
+ DefaultAsyncHttpxClient(
+ verify=True,
+ cert=None,
+ trust_env=True,
+ http1=True,
+ http2=False,
+ limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
+ )
+
@pytest.mark.respx(base_url=base_url)
async def test_follow_redirects(self, respx_mock: MockRouter) -> None:
# Test that the default follow_redirects=True allows following redirects
From ed2f82d6ecc8662e4717c9f7b75d912226c5df25 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 16 Jun 2025 18:58:26 +0000
Subject: [PATCH 18/29] chore(internal): update conftest.py
---
tests/conftest.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tests/conftest.py b/tests/conftest.py
index 2aaf6585..f08a4aa2 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,3 +1,5 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
from __future__ import annotations
import os
From 3a05f2a17a2f0067486b6c8427b2d50c18a79a80 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 17 Jun 2025 00:10:42 +0000
Subject: [PATCH 19/29] chore(ci): enable for pull requests
---
.github/workflows/ci.yml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c2b98114..99700f34 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -7,6 +7,10 @@ on:
- 'integrated/**'
- 'stl-preview-head/**'
- 'stl-preview-base/**'
+ pull_request:
+ branches-ignore:
+ - 'stl-preview-head/**'
+ - 'stl-preview-base/**'
jobs:
lint:
From e27f6d80e9b9bdbb23fa3a6c9cc84ecbbf790638 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 17 Jun 2025 14:52:06 +0000
Subject: [PATCH 20/29] chore(readme): update badges
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index dd9945f6..3829859e 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Finch Python API library
-[](https://pypi.org/project/finch-api/)
+[![PyPI version]()](https://pypi.org/project/finch-api/)
The Finch Python library provides convenient access to the Finch REST API from any Python 3.8+
application. The library includes type definitions for all request params and response fields,
From c17f0817149ae17fe866bc79f8613cbccc0ded72 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 18 Jun 2025 12:41:01 +0000
Subject: [PATCH 21/29] docs(client): fix httpx.Timeout documentation reference
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 3829859e..e1e9203b 100644
--- a/README.md
+++ b/README.md
@@ -219,7 +219,7 @@ client.with_options(max_retries=5).hris.directory.list()
### Timeouts
By default requests time out after 1 minute. You can configure this with a `timeout` option,
-which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/#fine-tuning-the-configuration) object:
+which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:
```python
from finch import Finch
From 9529a960260e47e147e10a9fec012f279089628b Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 20 Jun 2025 18:01:22 +0000
Subject: [PATCH 22/29] feat(client): add support for aiohttp
---
README.md | 31 +++++++++++++
pyproject.toml | 2 +
requirements-dev.lock | 27 ++++++++++++
requirements.lock | 27 ++++++++++++
src/finch/__init__.py | 3 +-
src/finch/_base_client.py | 22 ++++++++++
tests/api_resources/connect/test_sessions.py | 4 +-
.../hris/benefits/test_individuals.py | 4 +-
.../company/pay_statement_item/test_rules.py | 4 +-
.../hris/company/test_pay_statement_item.py | 4 +-
tests/api_resources/hris/test_benefits.py | 4 +-
tests/api_resources/hris/test_company.py | 4 +-
tests/api_resources/hris/test_directory.py | 4 +-
tests/api_resources/hris/test_documents.py | 4 +-
tests/api_resources/hris/test_employments.py | 4 +-
tests/api_resources/hris/test_individuals.py | 4 +-
.../api_resources/hris/test_pay_statements.py | 4 +-
tests/api_resources/hris/test_payments.py | 4 +-
tests/api_resources/jobs/test_automated.py | 4 +-
tests/api_resources/jobs/test_manual.py | 4 +-
.../api_resources/payroll/test_pay_groups.py | 4 +-
.../sandbox/connections/test_accounts.py | 4 +-
.../sandbox/jobs/test_configuration.py | 4 +-
tests/api_resources/sandbox/test_company.py | 4 +-
.../api_resources/sandbox/test_connections.py | 4 +-
tests/api_resources/sandbox/test_directory.py | 4 +-
.../api_resources/sandbox/test_employment.py | 4 +-
.../api_resources/sandbox/test_individual.py | 4 +-
tests/api_resources/sandbox/test_jobs.py | 4 +-
tests/api_resources/sandbox/test_payment.py | 4 +-
tests/api_resources/test_access_tokens.py | 4 +-
tests/api_resources/test_account.py | 4 +-
tests/api_resources/test_providers.py | 4 +-
.../api_resources/test_request_forwarding.py | 4 +-
tests/conftest.py | 43 ++++++++++++++++---
35 files changed, 232 insertions(+), 35 deletions(-)
diff --git a/README.md b/README.md
index e1e9203b..f9395885 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,37 @@ asyncio.run(main())
Functionality between the synchronous and asynchronous clients is otherwise identical.
+### With aiohttp
+
+By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.
+
+You can enable this by installing `aiohttp`:
+
+```sh
+# install from PyPI
+pip install finch-api[aiohttp]
+```
+
+Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
+
+```python
+import asyncio
+from finch import DefaultAioHttpClient
+from finch import AsyncFinch
+
+
+async def main() -> None:
+ async with AsyncFinch(
+ access_token="My Access Token",
+ http_client=DefaultAioHttpClient(),
+ ) as client:
+ page = await client.hris.directory.list()
+ print(page.individuals)
+
+
+asyncio.run(main())
+```
+
## Using types
Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like:
diff --git a/pyproject.toml b/pyproject.toml
index de5a4b98..63ea5499 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -37,6 +37,8 @@ classifiers = [
Homepage = "https://github.com/Finch-API/finch-api-python"
Repository = "https://github.com/Finch-API/finch-api-python"
+[project.optional-dependencies]
+aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.6"]
[tool.rye]
managed = true
diff --git a/requirements-dev.lock b/requirements-dev.lock
index ddc2481e..56aeed96 100644
--- a/requirements-dev.lock
+++ b/requirements-dev.lock
@@ -10,6 +10,13 @@
# universal: false
-e file:.
+aiohappyeyeballs==2.6.1
+ # via aiohttp
+aiohttp==3.12.8
+ # via finch-api
+ # via httpx-aiohttp
+aiosignal==1.3.2
+ # via aiohttp
annotated-types==0.6.0
# via pydantic
anyio==4.4.0
@@ -17,6 +24,10 @@ anyio==4.4.0
# via httpx
argcomplete==3.1.2
# via nox
+async-timeout==5.0.1
+ # via aiohttp
+attrs==25.3.0
+ # via aiohttp
certifi==2023.7.22
# via httpcore
# via httpx
@@ -34,16 +45,23 @@ execnet==2.1.1
# via pytest-xdist
filelock==3.12.4
# via virtualenv
+frozenlist==1.6.2
+ # via aiohttp
+ # via aiosignal
h11==0.14.0
# via httpcore
httpcore==1.0.2
# via httpx
httpx==0.28.1
# via finch-api
+ # via httpx-aiohttp
# via respx
+httpx-aiohttp==0.1.6
+ # via finch-api
idna==3.4
# via anyio
# via httpx
+ # via yarl
importlib-metadata==7.0.0
iniconfig==2.0.0
# via pytest
@@ -51,6 +69,9 @@ markdown-it-py==3.0.0
# via rich
mdurl==0.1.2
# via markdown-it-py
+multidict==6.4.4
+ # via aiohttp
+ # via yarl
mypy==1.14.1
mypy-extensions==1.0.0
# via mypy
@@ -65,6 +86,9 @@ platformdirs==3.11.0
# via virtualenv
pluggy==1.5.0
# via pytest
+propcache==0.3.1
+ # via aiohttp
+ # via yarl
pydantic==2.10.3
# via finch-api
pydantic-core==2.27.1
@@ -98,11 +122,14 @@ tomli==2.0.2
typing-extensions==4.12.2
# via anyio
# via finch-api
+ # via multidict
# via mypy
# via pydantic
# via pydantic-core
# via pyright
virtualenv==20.24.5
# via nox
+yarl==1.20.0
+ # via aiohttp
zipp==3.17.0
# via importlib-metadata
diff --git a/requirements.lock b/requirements.lock
index d9942c73..9261dbc2 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -10,11 +10,22 @@
# universal: false
-e file:.
+aiohappyeyeballs==2.6.1
+ # via aiohttp
+aiohttp==3.12.8
+ # via finch-api
+ # via httpx-aiohttp
+aiosignal==1.3.2
+ # via aiohttp
annotated-types==0.6.0
# via pydantic
anyio==4.4.0
# via finch-api
# via httpx
+async-timeout==5.0.1
+ # via aiohttp
+attrs==25.3.0
+ # via aiohttp
certifi==2023.7.22
# via httpcore
# via httpx
@@ -22,15 +33,28 @@ distro==1.8.0
# via finch-api
exceptiongroup==1.2.2
# via anyio
+frozenlist==1.6.2
+ # via aiohttp
+ # via aiosignal
h11==0.14.0
# via httpcore
httpcore==1.0.2
# via httpx
httpx==0.28.1
# via finch-api
+ # via httpx-aiohttp
+httpx-aiohttp==0.1.6
+ # via finch-api
idna==3.4
# via anyio
# via httpx
+ # via yarl
+multidict==6.4.4
+ # via aiohttp
+ # via yarl
+propcache==0.3.1
+ # via aiohttp
+ # via yarl
pydantic==2.10.3
# via finch-api
pydantic-core==2.27.1
@@ -41,5 +65,8 @@ sniffio==1.3.0
typing-extensions==4.12.2
# via anyio
# via finch-api
+ # via multidict
# via pydantic
# via pydantic-core
+yarl==1.20.0
+ # via aiohttp
diff --git a/src/finch/__init__.py b/src/finch/__init__.py
index 181d238f..41c80a92 100644
--- a/src/finch/__init__.py
+++ b/src/finch/__init__.py
@@ -26,7 +26,7 @@
UnprocessableEntityError,
APIResponseValidationError,
)
-from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient
+from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient
from ._utils._logs import setup_logging as _setup_logging
__all__ = [
@@ -68,6 +68,7 @@
"DEFAULT_CONNECTION_LIMITS",
"DefaultHttpxClient",
"DefaultAsyncHttpxClient",
+ "DefaultAioHttpClient",
]
if not _t.TYPE_CHECKING:
diff --git a/src/finch/_base_client.py b/src/finch/_base_client.py
index df5ea511..1caaa8d7 100644
--- a/src/finch/_base_client.py
+++ b/src/finch/_base_client.py
@@ -1304,6 +1304,24 @@ def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
+try:
+ import httpx_aiohttp
+except ImportError:
+
+ class _DefaultAioHttpClient(httpx.AsyncClient):
+ def __init__(self, **_kwargs: Any) -> None:
+ raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra")
+else:
+
+ class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore
+ def __init__(self, **kwargs: Any) -> None:
+ kwargs.setdefault("timeout", DEFAULT_TIMEOUT)
+ kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS)
+ kwargs.setdefault("follow_redirects", True)
+
+ super().__init__(**kwargs)
+
+
if TYPE_CHECKING:
DefaultAsyncHttpxClient = httpx.AsyncClient
"""An alias to `httpx.AsyncClient` that provides the same defaults that this SDK
@@ -1312,8 +1330,12 @@ def __init__(self, **kwargs: Any) -> None:
This is useful because overriding the `http_client` with your own instance of
`httpx.AsyncClient` will result in httpx's defaults being used, not ours.
"""
+
+ DefaultAioHttpClient = httpx.AsyncClient
+ """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`."""
else:
DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient
+ DefaultAioHttpClient = _DefaultAioHttpClient
class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient):
diff --git a/tests/api_resources/connect/test_sessions.py b/tests/api_resources/connect/test_sessions.py
index bdc6339c..f437d7e6 100644
--- a/tests/api_resources/connect/test_sessions.py
+++ b/tests/api_resources/connect/test_sessions.py
@@ -126,7 +126,9 @@ def test_streaming_response_reauthenticate(self, client: Finch) -> None:
class TestAsyncSessions:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@pytest.mark.skip(reason="prism tests are broken")
@parametrize
diff --git a/tests/api_resources/hris/benefits/test_individuals.py b/tests/api_resources/hris/benefits/test_individuals.py
index 865bfff0..13ee6a10 100644
--- a/tests/api_resources/hris/benefits/test_individuals.py
+++ b/tests/api_resources/hris/benefits/test_individuals.py
@@ -220,7 +220,9 @@ def test_path_params_unenroll_many(self, client: Finch) -> None:
class TestAsyncIndividuals:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_enroll_many(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/hris/company/pay_statement_item/test_rules.py b/tests/api_resources/hris/company/pay_statement_item/test_rules.py
index 5cf919d9..340a7d99 100644
--- a/tests/api_resources/hris/company/pay_statement_item/test_rules.py
+++ b/tests/api_resources/hris/company/pay_statement_item/test_rules.py
@@ -176,7 +176,9 @@ def test_path_params_delete(self, client: Finch) -> None:
class TestAsyncRules:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/hris/company/test_pay_statement_item.py b/tests/api_resources/hris/company/test_pay_statement_item.py
index 35f0aa08..c270ce8e 100644
--- a/tests/api_resources/hris/company/test_pay_statement_item.py
+++ b/tests/api_resources/hris/company/test_pay_statement_item.py
@@ -57,7 +57,9 @@ def test_streaming_response_list(self, client: Finch) -> None:
class TestAsyncPayStatementItem:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_list(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/hris/test_benefits.py b/tests/api_resources/hris/test_benefits.py
index 05b9348f..7961a951 100644
--- a/tests/api_resources/hris/test_benefits.py
+++ b/tests/api_resources/hris/test_benefits.py
@@ -202,7 +202,9 @@ def test_streaming_response_list_supported_benefits(self, client: Finch) -> None
class TestAsyncBenefits:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/hris/test_company.py b/tests/api_resources/hris/test_company.py
index 8ea9dba0..fb206fda 100644
--- a/tests/api_resources/hris/test_company.py
+++ b/tests/api_resources/hris/test_company.py
@@ -44,7 +44,9 @@ def test_streaming_response_retrieve(self, client: Finch) -> None:
class TestAsyncCompany:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_retrieve(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/hris/test_directory.py b/tests/api_resources/hris/test_directory.py
index 5822d710..37505168 100644
--- a/tests/api_resources/hris/test_directory.py
+++ b/tests/api_resources/hris/test_directory.py
@@ -94,7 +94,9 @@ def test_streaming_response_list_individuals(self, client: Finch) -> None:
class TestAsyncDirectory:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_list(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/hris/test_documents.py b/tests/api_resources/hris/test_documents.py
index 7833d4f3..2b9d7077 100644
--- a/tests/api_resources/hris/test_documents.py
+++ b/tests/api_resources/hris/test_documents.py
@@ -92,7 +92,9 @@ def test_path_params_retreive(self, client: Finch) -> None:
class TestAsyncDocuments:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_list(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/hris/test_employments.py b/tests/api_resources/hris/test_employments.py
index 6ce52e42..f6514da2 100644
--- a/tests/api_resources/hris/test_employments.py
+++ b/tests/api_resources/hris/test_employments.py
@@ -51,7 +51,9 @@ def test_streaming_response_retrieve_many(self, client: Finch) -> None:
class TestAsyncEmployments:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_retrieve_many(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/hris/test_individuals.py b/tests/api_resources/hris/test_individuals.py
index 99a6f081..9b16cad2 100644
--- a/tests/api_resources/hris/test_individuals.py
+++ b/tests/api_resources/hris/test_individuals.py
@@ -53,7 +53,9 @@ def test_streaming_response_retrieve_many(self, client: Finch) -> None:
class TestAsyncIndividuals:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_retrieve_many(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/hris/test_pay_statements.py b/tests/api_resources/hris/test_pay_statements.py
index 961bdba3..1631e42c 100644
--- a/tests/api_resources/hris/test_pay_statements.py
+++ b/tests/api_resources/hris/test_pay_statements.py
@@ -51,7 +51,9 @@ def test_streaming_response_retrieve_many(self, client: Finch) -> None:
class TestAsyncPayStatements:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_retrieve_many(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/hris/test_payments.py b/tests/api_resources/hris/test_payments.py
index 70b183ea..8a2ba820 100644
--- a/tests/api_resources/hris/test_payments.py
+++ b/tests/api_resources/hris/test_payments.py
@@ -55,7 +55,9 @@ def test_streaming_response_list(self, client: Finch) -> None:
class TestAsyncPayments:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_list(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/jobs/test_automated.py b/tests/api_resources/jobs/test_automated.py
index c0dc5e49..746efc27 100644
--- a/tests/api_resources/jobs/test_automated.py
+++ b/tests/api_resources/jobs/test_automated.py
@@ -159,7 +159,9 @@ def test_streaming_response_list(self, client: Finch) -> None:
class TestAsyncAutomated:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create_overload_1(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/jobs/test_manual.py b/tests/api_resources/jobs/test_manual.py
index df63695c..1cd30215 100644
--- a/tests/api_resources/jobs/test_manual.py
+++ b/tests/api_resources/jobs/test_manual.py
@@ -57,7 +57,9 @@ def test_path_params_retrieve(self, client: Finch) -> None:
class TestAsyncManual:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_retrieve(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/payroll/test_pay_groups.py b/tests/api_resources/payroll/test_pay_groups.py
index 2800c44f..ebf7dbd4 100644
--- a/tests/api_resources/payroll/test_pay_groups.py
+++ b/tests/api_resources/payroll/test_pay_groups.py
@@ -91,7 +91,9 @@ def test_streaming_response_list(self, client: Finch) -> None:
class TestAsyncPayGroups:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_retrieve(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/sandbox/connections/test_accounts.py b/tests/api_resources/sandbox/connections/test_accounts.py
index 5115364c..c10e33a2 100644
--- a/tests/api_resources/sandbox/connections/test_accounts.py
+++ b/tests/api_resources/sandbox/connections/test_accounts.py
@@ -102,7 +102,9 @@ def test_streaming_response_update(self, client: Finch) -> None:
class TestAsyncAccounts:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@pytest.mark.skip(reason="prism tests are broken")
@parametrize
diff --git a/tests/api_resources/sandbox/jobs/test_configuration.py b/tests/api_resources/sandbox/jobs/test_configuration.py
index b7d8ac2c..c2f21d0f 100644
--- a/tests/api_resources/sandbox/jobs/test_configuration.py
+++ b/tests/api_resources/sandbox/jobs/test_configuration.py
@@ -78,7 +78,9 @@ def test_streaming_response_update(self, client: Finch) -> None:
class TestAsyncConfiguration:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_retrieve(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/sandbox/test_company.py b/tests/api_resources/sandbox/test_company.py
index 12b02136..5485bf01 100644
--- a/tests/api_resources/sandbox/test_company.py
+++ b/tests/api_resources/sandbox/test_company.py
@@ -139,7 +139,9 @@ def test_streaming_response_update(self, client: Finch) -> None:
class TestAsyncCompany:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_update(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/sandbox/test_connections.py b/tests/api_resources/sandbox/test_connections.py
index c4fed5da..d240af9f 100644
--- a/tests/api_resources/sandbox/test_connections.py
+++ b/tests/api_resources/sandbox/test_connections.py
@@ -64,7 +64,9 @@ def test_streaming_response_create(self, client: Finch) -> None:
class TestAsyncConnections:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@pytest.mark.skip(reason="prism tests are broken")
@parametrize
diff --git a/tests/api_resources/sandbox/test_directory.py b/tests/api_resources/sandbox/test_directory.py
index 448904f7..ffc78bec 100644
--- a/tests/api_resources/sandbox/test_directory.py
+++ b/tests/api_resources/sandbox/test_directory.py
@@ -130,7 +130,9 @@ def test_streaming_response_create(self, client: Finch) -> None:
class TestAsyncDirectory:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/sandbox/test_employment.py b/tests/api_resources/sandbox/test_employment.py
index 04f47254..c9aecbc0 100644
--- a/tests/api_resources/sandbox/test_employment.py
+++ b/tests/api_resources/sandbox/test_employment.py
@@ -112,7 +112,9 @@ def test_path_params_update(self, client: Finch) -> None:
class TestAsyncEmployment:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_update(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/sandbox/test_individual.py b/tests/api_resources/sandbox/test_individual.py
index 31b04012..524a08a3 100644
--- a/tests/api_resources/sandbox/test_individual.py
+++ b/tests/api_resources/sandbox/test_individual.py
@@ -95,7 +95,9 @@ def test_path_params_update(self, client: Finch) -> None:
class TestAsyncIndividual:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_update(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/sandbox/test_jobs.py b/tests/api_resources/sandbox/test_jobs.py
index fd990987..b8fa4c0a 100644
--- a/tests/api_resources/sandbox/test_jobs.py
+++ b/tests/api_resources/sandbox/test_jobs.py
@@ -50,7 +50,9 @@ def test_streaming_response_create(self, client: Finch) -> None:
class TestAsyncJobs:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/sandbox/test_payment.py b/tests/api_resources/sandbox/test_payment.py
index 7f5f4bda..9b563814 100644
--- a/tests/api_resources/sandbox/test_payment.py
+++ b/tests/api_resources/sandbox/test_payment.py
@@ -94,7 +94,9 @@ def test_streaming_response_create(self, client: Finch) -> None:
class TestAsyncPayment:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/test_access_tokens.py b/tests/api_resources/test_access_tokens.py
index 1a4cd3fe..323c5d81 100644
--- a/tests/api_resources/test_access_tokens.py
+++ b/tests/api_resources/test_access_tokens.py
@@ -89,7 +89,9 @@ def test_streaming_response_create(self, client: Finch) -> None:
class TestAsyncAccessTokens:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/test_account.py b/tests/api_resources/test_account.py
index 2464a527..9280254f 100644
--- a/tests/api_resources/test_account.py
+++ b/tests/api_resources/test_account.py
@@ -69,7 +69,9 @@ def test_streaming_response_introspect(self, client: Finch) -> None:
class TestAsyncAccount:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_disconnect(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/test_providers.py b/tests/api_resources/test_providers.py
index 380fdf9c..7228af21 100644
--- a/tests/api_resources/test_providers.py
+++ b/tests/api_resources/test_providers.py
@@ -45,7 +45,9 @@ def test_streaming_response_list(self, client: Finch) -> None:
class TestAsyncProviders:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_list(self, async_client: AsyncFinch) -> None:
diff --git a/tests/api_resources/test_request_forwarding.py b/tests/api_resources/test_request_forwarding.py
index ea0c6319..be7ec45f 100644
--- a/tests/api_resources/test_request_forwarding.py
+++ b/tests/api_resources/test_request_forwarding.py
@@ -67,7 +67,9 @@ def test_streaming_response_forward(self, client: Finch) -> None:
class TestAsyncRequestForwarding:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_forward(self, async_client: AsyncFinch) -> None:
diff --git a/tests/conftest.py b/tests/conftest.py
index f08a4aa2..6631269d 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -6,10 +6,12 @@
import logging
from typing import TYPE_CHECKING, Iterator, AsyncIterator
+import httpx
import pytest
from pytest_asyncio import is_async_test
-from finch import Finch, AsyncFinch
+from finch import Finch, AsyncFinch, DefaultAioHttpClient
+from finch._utils import is_dict
if TYPE_CHECKING:
from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage]
@@ -27,6 +29,19 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None:
for async_test in pytest_asyncio_tests:
async_test.add_marker(session_scope_marker, append=False)
+ # We skip tests that use both the aiohttp client and respx_mock as respx_mock
+ # doesn't support custom transports.
+ for item in items:
+ if "async_client" not in item.fixturenames or "respx_mock" not in item.fixturenames:
+ continue
+
+ if not hasattr(item, "callspec"):
+ continue
+
+ async_client_param = item.callspec.params.get("async_client")
+ if is_dict(async_client_param) and async_client_param.get("http_client") == "aiohttp":
+ item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx_mock"))
+
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -45,9 +60,25 @@ def client(request: FixtureRequest) -> Iterator[Finch]:
@pytest.fixture(scope="session")
async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncFinch]:
- strict = getattr(request, "param", True)
- if not isinstance(strict, bool):
- raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}")
-
- async with AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=strict) as client:
+ param = getattr(request, "param", True)
+
+ # defaults
+ strict = True
+ http_client: None | httpx.AsyncClient = None
+
+ if isinstance(param, bool):
+ strict = param
+ elif is_dict(param):
+ strict = param.get("strict", True)
+ assert isinstance(strict, bool)
+
+ http_client_type = param.get("http_client", "httpx")
+ if http_client_type == "aiohttp":
+ http_client = DefaultAioHttpClient()
+ else:
+ raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict")
+
+ async with AsyncFinch(
+ base_url=base_url, access_token=access_token, _strict_response_validation=strict, http_client=http_client
+ ) as client:
yield client
From 668de928c9cd69af00755b2a201f3e26a24f9630 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 23 Jun 2025 18:49:35 +0000
Subject: [PATCH 23/29] chore(tests): skip some failing tests on the latest
python versions
---
tests/test_client.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tests/test_client.py b/tests/test_client.py
index 3adf40f0..24a87f3f 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -186,6 +186,7 @@ def test_copy_signature(self) -> None:
copy_param = copy_signature.parameters.get(name)
assert copy_param is not None, f"copy() signature is missing the {name} param"
+ @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12")
def test_copy_build_request(self) -> None:
options = FinalRequestOptions(method="get", url="/foo")
@@ -1024,6 +1025,7 @@ def test_copy_signature(self) -> None:
copy_param = copy_signature.parameters.get(name)
assert copy_param is not None, f"copy() signature is missing the {name} param"
+ @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12")
def test_copy_build_request(self) -> None:
options = FinalRequestOptions(method="get", url="/foo")
From d424f44c05c5c05bf31b43e1da74acea40324553 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 26 Jun 2025 14:05:15 +0000
Subject: [PATCH 24/29] =?UTF-8?q?fix(ci):=20release-doctor=20=E2=80=94=20r?=
=?UTF-8?q?eport=20correct=20token=20name?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
bin/check-release-environment | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bin/check-release-environment b/bin/check-release-environment
index 49f294c8..b845b0f4 100644
--- a/bin/check-release-environment
+++ b/bin/check-release-environment
@@ -3,7 +3,7 @@
errors=()
if [ -z "${PYPI_TOKEN}" ]; then
- errors+=("The FINCH_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.")
+ errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.")
fi
lenErrors=${#errors[@]}
From 398de612e40086b2814a79c2fcdd0d73bba289fc Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 27 Jun 2025 22:38:00 +0000
Subject: [PATCH 25/29] chore(ci): only run for pushes and fork pull requests
---
.github/workflows/ci.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 99700f34..fc0d5a6a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -17,6 +17,7 @@ jobs:
timeout-minutes: 10
name: lint
runs-on: ${{ github.repository == 'stainless-sdks/finch-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+ if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@v4
@@ -42,6 +43,7 @@ jobs:
contents: read
id-token: write
runs-on: depot-ubuntu-24.04
+ if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@v4
@@ -62,6 +64,7 @@ jobs:
timeout-minutes: 10
name: test
runs-on: ${{ github.repository == 'stainless-sdks/finch-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+ if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@v4
From 5bd88a7228b013e66794a9731e3d3669d1a43321 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sun, 29 Jun 2025 06:18:13 +0000
Subject: [PATCH 26/29] fix(ci): correct conditional
---
.github/workflows/ci.yml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fc0d5a6a..4cc640ab 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -36,14 +36,13 @@ jobs:
run: ./scripts/lint
upload:
- if: github.repository == 'stainless-sdks/finch-python'
+ if: github.repository == 'stainless-sdks/finch-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork)
timeout-minutes: 10
name: upload
permissions:
contents: read
id-token: write
runs-on: depot-ubuntu-24.04
- if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@v4
From 2d63eeda7540306cf29e14ac475c6f5cc740d768 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 1 Jul 2025 23:35:16 +0000
Subject: [PATCH 27/29] chore(ci): change upload type
---
.github/workflows/ci.yml | 18 ++++++++++++++++--
scripts/utils/upload-artifact.sh | 12 +++++++-----
2 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4cc640ab..187c937b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,10 +35,10 @@ jobs:
- name: Run lints
run: ./scripts/lint
- upload:
+ build:
if: github.repository == 'stainless-sdks/finch-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
@@ -46,6 +46,20 @@ jobs:
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
diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh
index d89a9ed0..bb0a7a76 100755
--- a/scripts/utils/upload-artifact.sh
+++ b/scripts/utils/upload-artifact.sh
@@ -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")
@@ -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/finch-python/$SHA'\033[0m"
+ echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/finch-python/$SHA/$FILENAME'\033[0m"
else
echo -e "\033[31mFailed to upload artifact.\033[0m"
exit 1
From 8411cf6e5185b4f2dd70ac030d30d7c9808e55b5 Mon Sep 17 00:00:00 2001
From: Tomer Aberbach
Date: Wed, 2 Jul 2025 15:46:45 -0400
Subject: [PATCH 28/29] chore: fix access tokens test
---
tests/api_resources/test_access_tokens.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/tests/api_resources/test_access_tokens.py b/tests/api_resources/test_access_tokens.py
index 323c5d81..1a4cd3fe 100644
--- a/tests/api_resources/test_access_tokens.py
+++ b/tests/api_resources/test_access_tokens.py
@@ -89,9 +89,7 @@ def test_streaming_response_create(self, client: Finch) -> None:
class TestAsyncAccessTokens:
- parametrize = pytest.mark.parametrize(
- "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
- )
+ parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
@parametrize
async def test_method_create(self, async_client: AsyncFinch) -> None:
From 5ff5f5dbeb0883c5db62c2b7d6fd3b21293dc474 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 2 Jul 2025 19:47:09 +0000
Subject: [PATCH 29/29] release: 1.30.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 43 +++++++++++++++++++++++++++++++++++
pyproject.toml | 2 +-
src/finch/_version.py | 2 +-
4 files changed, 46 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index b8af36c3..35c30adc 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "1.29.0"
+ ".": "1.30.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b45e750e..a8c2c54b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,48 @@
# Changelog
+## 1.30.0 (2025-07-02)
+
+Full Changelog: [v1.29.0...v1.30.0](https://github.com/Finch-API/finch-api-python/compare/v1.29.0...v1.30.0)
+
+### Features
+
+* **api:** api update ([97ef62d](https://github.com/Finch-API/finch-api-python/commit/97ef62d11c21650cb6f34a69f3309cdbf034df86))
+* **api:** api update ([68ed0de](https://github.com/Finch-API/finch-api-python/commit/68ed0de62a49c340a1ee7dc71d941d71b6d780c4))
+* **api:** api update ([a31325e](https://github.com/Finch-API/finch-api-python/commit/a31325e6eeb04cfa725ceee5637b869dd02be1a8))
+* **api:** api update ([b95ac19](https://github.com/Finch-API/finch-api-python/commit/b95ac1971a10372d70fc7ccdb3cf63f4411e9d2e))
+* **client:** add follow_redirects request option ([f9bdda7](https://github.com/Finch-API/finch-api-python/commit/f9bdda77ef1fcb1460594a16f1b8726bbaad2b52))
+* **client:** add support for aiohttp ([9529a96](https://github.com/Finch-API/finch-api-python/commit/9529a960260e47e147e10a9fec012f279089628b))
+
+
+### Bug Fixes
+
+* **ci:** correct conditional ([5bd88a7](https://github.com/Finch-API/finch-api-python/commit/5bd88a7228b013e66794a9731e3d3669d1a43321))
+* **ci:** release-doctor — report correct token name ([d424f44](https://github.com/Finch-API/finch-api-python/commit/d424f44c05c5c05bf31b43e1da74acea40324553))
+* **client:** correctly parse binary response | stream ([260c761](https://github.com/Finch-API/finch-api-python/commit/260c761024d36f0bb4efc3cf5a5e0e3c22b5f83e))
+* **client:** fix PayStatementResponse body ([edcea6c](https://github.com/Finch-API/finch-api-python/commit/edcea6c7ac55661d3e46aec1c42576988b510dfc))
+* **client:** manual fix ([d1dc8a4](https://github.com/Finch-API/finch-api-python/commit/d1dc8a4aa3df7a0199ab04958a766b56b3be0dc5))
+
+
+### Chores
+
+* **ci:** change upload type ([2d63eed](https://github.com/Finch-API/finch-api-python/commit/2d63eeda7540306cf29e14ac475c6f5cc740d768))
+* **ci:** enable for pull requests ([3a05f2a](https://github.com/Finch-API/finch-api-python/commit/3a05f2a17a2f0067486b6c8427b2d50c18a79a80))
+* **ci:** only run for pushes and fork pull requests ([398de61](https://github.com/Finch-API/finch-api-python/commit/398de612e40086b2814a79c2fcdd0d73bba289fc))
+* **docs:** grammar improvements ([2d4a30e](https://github.com/Finch-API/finch-api-python/commit/2d4a30edc2f84fc7effc9b7719aae8ed51646338))
+* **docs:** remove reference to rye shell ([2b6929f](https://github.com/Finch-API/finch-api-python/commit/2b6929f8eca16bff688b19907f38e05ad2d2b092))
+* fix access tokens test ([8411cf6](https://github.com/Finch-API/finch-api-python/commit/8411cf6e5185b4f2dd70ac030d30d7c9808e55b5))
+* **internal:** update conftest.py ([ed2f82d](https://github.com/Finch-API/finch-api-python/commit/ed2f82d6ecc8662e4717c9f7b75d912226c5df25))
+* **readme:** update badges ([e27f6d8](https://github.com/Finch-API/finch-api-python/commit/e27f6d80e9b9bdbb23fa3a6c9cc84ecbbf790638))
+* **tests:** add tests for httpx client instantiation & proxies ([e51c8f7](https://github.com/Finch-API/finch-api-python/commit/e51c8f78bb45e0c3dbce5dc67401390078a2fd33))
+* **tests:** run tests in parallel ([25017c4](https://github.com/Finch-API/finch-api-python/commit/25017c43233e726dd80d1e7ca41eb02ec9456871))
+* **tests:** skip endpoints with basic auth ([9548dd5](https://github.com/Finch-API/finch-api-python/commit/9548dd59525baf33a8d1977b132e93b2fe1d7f96))
+* **tests:** skip some failing tests on the latest python versions ([668de92](https://github.com/Finch-API/finch-api-python/commit/668de928c9cd69af00755b2a201f3e26a24f9630))
+
+
+### Documentation
+
+* **client:** fix httpx.Timeout documentation reference ([c17f081](https://github.com/Finch-API/finch-api-python/commit/c17f0817149ae17fe866bc79f8613cbccc0ded72))
+
## 1.29.0 (2025-05-16)
Full Changelog: [v1.28.0...v1.29.0](https://github.com/Finch-API/finch-api-python/compare/v1.28.0...v1.29.0)
diff --git a/pyproject.toml b/pyproject.toml
index 63ea5499..6b804c9b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "finch-api"
-version = "1.29.0"
+version = "1.30.0"
description = "The official Python library for the Finch API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/finch/_version.py b/src/finch/_version.py
index dc7a3a54..89726bc8 100644
--- a/src/finch/_version.py
+++ b/src/finch/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "finch"
-__version__ = "1.29.0" # x-release-please-version
+__version__ = "1.30.0" # x-release-please-version