Skip to content

release: 0.2.0 #124

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions .github/workflows/create-releases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,18 @@ jobs:
repo: ${{ github.event.repository.full_name }}
stainless-api-key: ${{ secrets.STAINLESS_API_KEY }}

- name: Set up Python
- name: Install Rye
if: ${{ steps.release.outputs.releases_created }}
uses: actions/setup-python@v4
with:
python-version: '3.7'
run: |
curl -sSf https://rye-up.com/get | bash
echo "$HOME/.rye/shims" >> $GITHUB_PATH
env:
RYE_VERSION: 0.15.2
RYE_INSTALL_OPTION: "--yes"

- name: Publish to PyPI
if: ${{ steps.release.outputs.releases_created }}
run: |
pipx install poetry
bash ./bin/publish-pypi
env:
PYPI_TOKEN: ${{ secrets.FINCH_PYPI_TOKEN }}
12 changes: 7 additions & 5 deletions .github/workflows/publish-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ jobs:
steps:
- uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.7'
- name: Install Rye
run: |
curl -sSf https://rye-up.com/get | bash
echo "$HOME/.rye/shims" >> $GITHUB_PATH
env:
RYE_VERSION: 0.15.2
RYE_INSTALL_OPTION: "--yes"

- name: Publish to PyPI
run: |
pipx install poetry
bash ./bin/publish-pypi
env:
PYPI_TOKEN: ${{ secrets.FINCH_PYPI_TOKEN }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ __pycache__

dist

.venv

.env
codegen.log
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.1.5"
".": "0.2.0"
}
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,42 @@
# Changelog

## 0.2.0 (2023-10-17)

Full Changelog: [v0.1.5...v0.2.0](https://github.com/Finch-API/finch-api-python/compare/v0.1.5...v0.2.0)

### Features

* **client:** add logging setup ([#127](https://github.com/Finch-API/finch-api-python/issues/127)) ([71aab4b](https://github.com/Finch-API/finch-api-python/commit/71aab4bab20f465005c6300bdedf46862d7fdb82))
* **client:** add support for passing in a httpx client ([#123](https://github.com/Finch-API/finch-api-python/issues/123)) ([623f5bc](https://github.com/Finch-API/finch-api-python/commit/623f5bc8c460e8e880ebc932c8a0f4a4ba780396))
* **client:** support passing httpx.URL instances to base_url ([#138](https://github.com/Finch-API/finch-api-python/issues/138)) ([0aad4df](https://github.com/Finch-API/finch-api-python/commit/0aad4df2c3a86f60ff9a28fc7b28ea2012541257))
* make webhook headers case insensitive ([#130](https://github.com/Finch-API/finch-api-python/issues/130)) ([3af34f9](https://github.com/Finch-API/finch-api-python/commit/3af34f9a71dde0fa60ac7c2aa75e1273e8431015))


### Bug Fixes

* **client:** accept io.IOBase instances in file params ([#134](https://github.com/Finch-API/finch-api-python/issues/134)) ([1297832](https://github.com/Finch-API/finch-api-python/commit/1297832b447ed973ab39b93a329550151747a899))
* **client:** correctly handle arguments with env vars ([#128](https://github.com/Finch-API/finch-api-python/issues/128)) ([e502b18](https://github.com/Finch-API/finch-api-python/commit/e502b18337da39095aee811ea3d69576085602a4))
* correct benfits to benefits ([#125](https://github.com/Finch-API/finch-api-python/issues/125)) ([9e2c02a](https://github.com/Finch-API/finch-api-python/commit/9e2c02ad1f53d94ed523e340751b39f10ebbe483))


### Chores

* **internal:** cleanup some redundant code ([#133](https://github.com/Finch-API/finch-api-python/issues/133)) ([88e49bc](https://github.com/Finch-API/finch-api-python/commit/88e49bcc474195cde09d127d028e3a427b13e9a2))
* **internal:** enable lint rule ([#132](https://github.com/Finch-API/finch-api-python/issues/132)) ([8e266e8](https://github.com/Finch-API/finch-api-python/commit/8e266e82511822cfefc7d877300382a05ca2f25e))
* **internal:** improve publish script ([#137](https://github.com/Finch-API/finch-api-python/issues/137)) ([78cd8ba](https://github.com/Finch-API/finch-api-python/commit/78cd8bad7146ab40a16342d9d0c838d36c041c23))
* **internal:** migrate from Poetry to Rye ([#136](https://github.com/Finch-API/finch-api-python/issues/136)) ([ac49d7c](https://github.com/Finch-API/finch-api-python/commit/ac49d7cd8721c46ad2a1ed77c5f5b75eca7ad4f9))
* update comment ([#131](https://github.com/Finch-API/finch-api-python/issues/131)) ([20aa00a](https://github.com/Finch-API/finch-api-python/commit/20aa00ac06e4104b74c047cc75720b2aa77c2f4a))


### Documentation

* organisation -> organization (UK to US English) ([#135](https://github.com/Finch-API/finch-api-python/issues/135)) ([2e278bd](https://github.com/Finch-API/finch-api-python/commit/2e278bd482dc7407689142037df9fd3bfa15f05e))


### Refactors

* **test:** refactor authentication tests ([#126](https://github.com/Finch-API/finch-api-python/issues/126)) ([88b79b5](https://github.com/Finch-API/finch-api-python/commit/88b79b5a98bda98ae1b6562b385db6ca53105707))

## 0.1.5 (2023-10-09)

Full Changelog: [v0.1.4...v0.1.5](https://github.com/Finch-API/finch-api-python/compare/v0.1.4...v0.1.5)
Expand Down
89 changes: 51 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Finch Python API Library
# Finch Python API library

[![PyPI version](https://img.shields.io/pypi/v/finch-api.svg)](https://pypi.org/project/finch-api/)

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

## Documentation

The API documentation can be found [here](https://developer.tryfinch.com/).
The API documentation can be found [in the Finch Documentation Center](https://developer.tryfinch.com/).

## Installation

Expand All @@ -24,30 +24,30 @@ The full API of this library can be found in [api.md](https://www.github.com/Fin
from finch import Finch

client = Finch(
access_token="my access token",
access_token="My Access Token",
)

page = client.hris.directory.list_individuals(
page = client.hris.directory.list(
candidate_id="<candidate id>",
)
directory = page.individuals[0]
print(directory.first_name)
```

## Async Usage
## Async usage

Simply import `AsyncFinch` instead of `Finch` and use `await` with each API call:

```python
from finch import AsyncFinch

client = AsyncFinch(
access_token="my access token",
access_token="My Access Token",
)


async def main():
page = await client.hris.directory.list_individuals(
page = await client.hris.directory.list(
candidate_id="<candidate id>",
)
print(page.individuals[0].first_name)
Expand All @@ -58,11 +58,11 @@ asyncio.run(main())

Functionality between the synchronous and asynchronous clients is otherwise identical.

## Using Types
## 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 provide helper methods for things like serializing back into json ([v1](https://docs.pydantic.dev/1.10/usage/models/), [v2](https://docs.pydantic.dev/latest/usage/serialization/)). To get a dictionary, you can call `dict(model)`.
Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev), which provide helper methods for things like serializing back into JSON ([v1](https://docs.pydantic.dev/1.10/usage/models/), [v2](https://docs.pydantic.dev/latest/usage/serialization/)). To get a dictionary, call `dict(model)`.

This helps provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `"basic"`.
Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`.

## Pagination

Expand All @@ -77,7 +77,7 @@ client = Finch()

all_directories = []
# Automatically fetches more pages as needed.
for directory in client.hris.directory.list_individuals():
for directory in client.hris.directory.list():
# Do something with directory here
all_directories.append(directory)
print(all_directories)
Expand All @@ -95,7 +95,7 @@ client = AsyncFinch()
async def main() -> None:
all_directories = []
# Iterate through items across all pages, issuing requests as needed.
async for directory in client.hris.directory.list_individuals():
async for directory in client.hris.directory.list():
all_directories.append(directory)
print(all_directories)

Expand All @@ -106,7 +106,7 @@ asyncio.run(main())
Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages:

```python
first_page = await client.hris.directory.list_individuals()
first_page = await client.hris.directory.list()
if first_page.has_next_page():
print(f"will fetch next page using these details: {first_page.next_page_info()}")
next_page = await first_page.get_next_page()
Expand All @@ -118,7 +118,7 @@ if first_page.has_next_page():
Or just work directly with the returned data:

```python
first_page = await client.hris.directory.list_individuals()
first_page = await client.hris.directory.list()

print(
f"the current start offset for this page: {first_page.paging.offset}"
Expand All @@ -138,7 +138,7 @@ from finch import Finch

client = Finch()

client.hris.directory.list_individuals(
client.hris.directory.list(
path_params=[],
params={},
)
Expand Down Expand Up @@ -169,10 +169,10 @@ async def handler(request: Request):

## Handling errors

When the library is unable to connect to the API (e.g., due to network connection problems or a timeout), a subclass of `finch.APIConnectionError` is raised.
When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `finch.APIConnectionError` is raised.

When the API returns a non-success status code (i.e., 4xx or 5xx
response), a subclass of `finch.APIStatusError` will be raised, containing `status_code` and `response` properties.
When the API returns a non-success status code (that is, 4xx or 5xx
response), a subclass of `finch.APIStatusError` is raised, containing `status_code` and `response` properties.

All errors inherit from `finch.APIError`.

Expand All @@ -183,7 +183,7 @@ from finch import Finch
client = Finch()

try:
client.hris.directory.list_individuals()
client.hris.directory.list()
except finch.APIConnectionError as e:
print("The server could not be reached")
print(e.__cause__) # an underlying Exception, likely raised within httpx.
Expand All @@ -210,11 +210,11 @@ Error codes are as followed:

### Retries

Certain errors will be automatically retried 2 times by default, with a short exponential backoff.
Certain errors are automatically retried 2 times by default, with a short exponential backoff.
Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,
429 Rate Limit, and >=500 Internal errors will all be retried by default.
429 Rate Limit, and >=500 Internal errors are all retried by default.

You can use the `max_retries` option to configure or disable this:
You can use the `max_retries` option to configure or disable retry settings:

```python
from finch import Finch
Expand All @@ -226,13 +226,13 @@ client = Finch(
)

# Or, configure per-request:
client.with_options(max_retries=5).hris.directory.list_individuals()
client.with_options(max_retries=5).hris.directory.list()
```

### Timeouts

Requests time out after 1 minute by default. 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):
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:

```python
from finch import Finch
Expand All @@ -249,12 +249,12 @@ client = Finch(
)

# Override per-request:
client.with_options(timeout=5 * 1000).hris.directory.list_individuals()
client.with_options(timeout=5 * 1000).hris.directory.list()
```

On timeout, an `APITimeoutError` is thrown.

Note that requests which time out will be [retried twice by default](#retries).
Note that requests that time out are [retried twice by default](#retries).

## Default Headers

Expand All @@ -274,9 +274,19 @@ client = Finch(

## Advanced

### Logging

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

You can enable logging by setting the environment variable `FINCH_LOG` to `debug`.

```shell
$ export FINCH_LOG=debug
```

### How to tell whether `None` means `null` or missing

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

```py
if response.my_field is None:
Expand All @@ -286,31 +296,34 @@ if response.my_field is None:
print('Got json like {"my_field": null}.')
```

### Configuring custom URLs, proxies, and transports
### Configuring the HTTP client

You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including:

You can configure the following keyword arguments when instantiating the client:
- Support for proxies
- Custom transports
- Additional [advanced](https://www.python-httpx.org/advanced/#client-instances) functionality

```python
import httpx
from finch import Finch

client = Finch(
# Use a custom base URL
base_url="http://my.test.server.example.com:8083",
proxies="http://my.test.proxy.example.com",
transport=httpx.HTTPTransport(local_address="0.0.0.0"),
http_client=httpx.Client(
proxies="http://my.test.proxy.example.com",
transport=httpx.HTTPTransport(local_address="0.0.0.0"),
),
)
```

See the httpx documentation for information about the [`proxies`](https://www.python-httpx.org/advanced/#http-proxying) and [`transport`](https://www.python-httpx.org/advanced/#custom-transports) keyword arguments.

### Managing HTTP resources

By default we will close the underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__) is called but you can also manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting.
By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting.

## Versioning

This package generally attempts to follow [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:

1. Changes that only affect static types, without breaking runtime behavior.
2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals)_.
Expand Down
3 changes: 2 additions & 1 deletion api.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ from finch.types.hris import IndividualInDirectory

Methods:

- <code title="get /employer/directory">client.hris.directory.<a href="./src/finch/resources/hris/directory.py">list_individuals</a>(\*\*<a href="src/finch/types/hris/directory_list_individuals_params.py">params</a>) -> <a href="./src/finch/types/hris/individual_in_directory.py">SyncIndividualsPage[IndividualInDirectory]</a></code>
- <code title="get /employer/directory">client.hris.directory.<a href="./src/finch/resources/hris/directory.py">list</a>(\*\*<a href="src/finch/types/hris/directory_list_params.py">params</a>) -> <a href="./src/finch/types/hris/individual_in_directory.py">SyncIndividualsPage[IndividualInDirectory]</a></code>

## Individuals

Expand Down Expand Up @@ -95,6 +95,7 @@ Types:

```python
from finch.types.hris import (
BenefitContribution,
BenefitFrequency,
BenefitType,
BenfitContribution,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a part of these changes but

Suggested change
BenfitContribution,
BenefitContribution,

Expand Down
4 changes: 2 additions & 2 deletions bin/check-release-environment
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
errors=()

if [ -z "${STAINLESS_API_KEY}" ]; then
errors+=("The STAINLESS_API_KEY secret has not been set. Please contact Stainless for an API key & set it in your organisation secrets on GitHub.")
errors+=("The STAINLESS_API_KEY secret has not been set. Please contact Stainless for an API key & set it in your organization secrets on GitHub.")
fi

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 organisation secrets.")
errors+=("The FINCH_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.")
fi

len=${#errors[@]}
Expand Down
5 changes: 3 additions & 2 deletions bin/publish-pypi
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env bash

set -eux
poetry config pypi-token.pypi $PYPI_TOKEN
poetry publish --build
mkdir -p dist
rye build --clean
rye publish --yes --token=$PYPI_TOKEN
2 changes: 1 addition & 1 deletion bin/test
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/usr/bin/env bash

bin/check-test-server && poetry run pytest "$@"
bin/check-test-server && rye run pytest "$@"
Loading