All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Fix: Cursor-based pagination also assumes
""
is a terminal signal for pagination, same as if the next cursor wereNone
.
- Fix: Upgrade intermediate representation dependency to safely parse null unknown types.
- Improvement:
.dict
performance is increased, consolidating to a single call to Pydantic's.dict
instead of 2 in attempts to remove unset optional values.
- Fix: Query encoding now appropriately takes arrays of deep objects into account.
- Fix: Unions with utils now update forward refs again, a regression that was introduced in version 3.7.0
- Fix: If there are no autogenerated examples present, the Python SDK generator no longer fails.
-
Feat: Adds a new flag to generate forward compatible Python enums, as opposed to leveraging raw string enums as literals. This works through addding an "_UNKNOWN" member to your enum set, the value of which is the raw value of the unrecognized enum.
generators: - name: fernapi/fern-python-sdk config: pydantic_config: enum_type: "forward_compatible_python_enums" # Other valid option is: "literals" and "python_enums"
- Feat: A new configuration is introduced to make discriminated union member naming more Pythonic. With V1 union naming, member names change from
<UnionName>_<DiscriminantValue>
to<DiscriminantValue><UnionName>
. Concretely, union members previously namedChat_User
will now be namedUserChat
under the new configuration.generators: - name: fernapi/fern-python-sdk config: pydantic_config: union_naming: "v1" # Other valid option is: "v0"
- Improvement: generated SDKs now use ruff for linting and formatting, instead of Black.
- Improvement: Python circular referencing types are more robust.
-
Feat: The generator now respects returning nested properties, these can be specified via:
In OpenAPI below, we'd like to only return the property
jobId
from theJob
object we get back from our server to our SDK users:my/endpoint: get: x-fern-sdk-return-value: jobId response: Job
For a similar situation using the Fern definition:
endpoints: getJob: method: GET path: my/endpoint response: type: Job property: jobId
-
Fix: The underlying content no longer sends empty JSON bodies, instead it'll pass a
None
value to httpx
- Fix: The root type for unions with visitors now has it's parent typed correctly. This allows auto-complete to work once again on the union when it's nested within other pydantic models.
-
Improvement: The generated SDK now respects the pydantic version flag, generating V1 only code and V2 only code if specified. If not, the SDK is generated as it is today, with compatibility for BOTH Pydantic versions. This cleans up the generated code, and brings back features liked wrapped aliases for V1-only SDKs.
Pydantic compatibility can be specified through the config below:
generators: - name: fernapi/fern-python-sdk config: pydantic_config: version: "v1" # Other valid options include: "v2" and "both"
- Fix: The Python generator now instantiates
Any
types asOptional[Any]
to be able to meet some breaks in Pydantic V2.
- Fix: Literal templates are generated if they are union members
- Internal: The SDK generator has now been upgraded to use Pydantic V2 internally. Note that
there is no change to the generated code, however by leveraging Pydantic V2 you should notice
an improvement in
fern generate
times.
- Improvement: Aliased literals are also defaulted within Pydantic models, whereas previously only direct literals were defaulted.
- Improvement: Snippets now provide optional literals in functions and models.
- Fix: Generated tests that expect an empty result when they are of type
text
(not JSON) now appropriately expect an empty string instead ofNone
.
- Fix: The generator now allows you to extend aliased types (as long as they're objects).
- Fix: regression in readme generation introduced in 3.3.1
- Fix: Generated READMEs now reference RequestOptions as TypedDicts correctly.
- Fix: TypedDict snippets now include literals where available.
- internal: Upgrade to IR 53.1.0
- chore: The Python generator now creates snippet templates for undiscriminated unions.
- Fix: The generated README now imports
ApiError
as if it were from outside the module.
-
Improvement: The Python SDK can now be generated such that inputs to requests are TypedDicts, instead of Pydantic models. This allows for consumers of the SDK to continue to have type hinting and autocomplete, but not need to import new object types when creating requests.
With the following config:
generators: - name: fernapi/fern-python-sdk config: pydantic_config: use_typeddict_requests: true
The usage will change from:
client\ .imdb\ .create_movie( request=CreateMovieRequest(title="title", rating=4.3), )
to:
client\ .imdb\ .create_movie( request={"title": "title", "rating": 4.3}, )
- Improvement: The root client users interact with is now exported from the main
__init__.py
, this allows users to access the client viafrom my_sdk import my_sdk_client
as opposed tofrom my_sdk.client import my_sdk_client
.- Note this comes with an edge-case break. In the unlikely event you have a type that conflicts in naming with the exported root client, that type model is post-fixed with "Model". e.g. a type
Merge
in an SDK exporting a clientMerge
becomesMergeModel
.
- Note this comes with an edge-case break. In the unlikely event you have a type that conflicts in naming with the exported root client, that type model is post-fixed with "Model". e.g. a type
- Fix:
update_forward_refs
no longer raises errors, preserving original behavior, pre-3.x.
- Fix: Sometimes mypy will error on the typing of
expected_types
within our test suite, despite them being labeled astyping.Any
. This updates the types for tuples totyping.Tuple[tying.Any, typing.Any]
to appease mypy.
- Break: The generated SDK now supports Pydantic V2 outright, it no longer uses
pydantic.v1
models. This change introduces additional breaks:- Wrapped aliases have been removed
- Custom root validators for models with a root type have been removed (e.g. only unions with utils still leverages root models)
- Public fields previously prefixed with
_
are now prefixed withf_
(Pydantic V2 does not allow for_
prefixes on public fields and Python does not allow for a numeric prefix)
-
Improvement: The generated SDK now allows for specifying whether or not to generate
streaming
functions as overloaded functions or separate functions. Concretely, you can now specify whether or not you'd prefer the generation of your streaming functions as:def chat(...) -> ChatResponse: ... # and def chat_stream(...) -> typing.Iterator[StreamedChatResponse]: ...
or:
@overload def chat(...) -> ChatResponse: ... # and @overload def chat(..., stream=True) -> typing.Iterator[StreamedChatResponse]: ...
The latter is configurable by specifying
stream-condition
andresponse-stream
on your request within your Fern definition:service: auth: false base-path: / endpoints: listUsers: method: GET path: /users stream-condition: $request.stream request: name: ListUsersRequest query-parameters: page: integer body: properties: stream: optional<boolean> response: ListUsersResponse response-stream: User
- Fix: The generated python SDK now requires an environment be specified if a default is not provided.
- Fix: The generated python SDK Oauth client now no longer checks for an expiry when getting the access token if an expiry field is not configured.
- Fix: The generated python SDK now serializes bytes within JSON as a utf-8 encoded string.
- Fix: The generated python SDK no longer runs into a recursion error during snippet generation.
- Fix: The generated python SDK no longer treats
set
as a reserved word for method names.
- Fix: The unchecked base model no longer coerces None to a type.
- Fix: The http client appropriately defaults empty fields within RequestOptions.
- Improvement: The generated Python SDK can now respect configured defaults from the API spec.
generators:
- name: fernapi/fern-python-sdk
config:
pydantic_config:
use_provided_defaults: true
- Fix: Sync and AsyncPage now pass through the generic type to BasePage, allowing the use of
.items
, etc. to be appropriately typed within your type checking system.
- Fix: offset page now allows for the usage of 0 as a page start, previously the use of
page or 1
made Python coerce booleans and become 1, ignoring the user-provided 0.
- Improvement: Generated readmes now include an "advanced" section, outlining usage of retries, timeouts, error handling and usage of a custom client.
- Improvement: Async snippets now run the async function leveraging asyncio.run to be more copy-pastable.
- Fix: the fix from 2.5.2 is now case-insensitive
Recap of 2.5.2:
Fix: Support
listSDK method names instead of defaulting to
list_.
- Feat: the Python SDK now generates an accompanying SDK reference (
reference.md
) for users to review the SDK methods at a glance within the SDK's GitHub repository.
- Fix: the Python SDK now does not send additional properties via JSON or data if the request is leveraging the other field.
- Improvement: the Python SDK now copies unit tests over to the generated SDK for additional unit testing (separate from wire-format testing).
- Upgrade: The Python SDK generator is now upgraded to IR V49.
-
Feature: README generation now supports a section dedicated to streaming usage, as well as one for paginated endpoints.
-
Improvement: Paginated endpoint snippets now show using an iterator:
Before:
from seed.client import SeedPagination client = SeedPagination( token="YOUR_TOKEN", base_url="https://yourhost.com/path/to/api", ) client.users.list_with_cursor_pagination( page=1, per_page=1, order="asc", starting_after="string", )
After:
from seed.client import SeedPagination client = SeedPagination( token="YOUR_TOKEN", base_url="https://yourhost.com/path/to/api", ) response = client.users.list_with_cursor_pagination( page=1, per_page=1, order="asc", starting_after="string", ) for item in response: yield item # alternatively, you can paginate page-by-page for page in response.iter_pages(): yield page
- Improvement: The SDK now produces templates for the root clients within snippet-template.json. This allows users of the Templates API to pass in data for the auth variables present within the root client.
- Fix: The SDK now handles stream termination sequences like
[DONE]
. This is a typical way for LLM providers to communicate when the stream has ended.
- Fix: Improve the SDK to not leak
JSONDecodeError
to SDK users. Instead, anApiError
will be thrown with the text content of the response.
- Feature: Add support for higher quality
README.md
generation.
- Fix: the generator now only specifies the readme location within pyproject.toml if one was successfully created.
- Internal: The generator now consumes IRv46.
- Fix: The python generator only adds a publish step in github actions if credentials are specified.
- Fix: The unchecked base model stops special casing defaults and pydantic v2.
- Fix: Offset based pagination is now 1-based, as opposed to 0 based
- Fix: The HTTP client now passes in additional body properties from the request options, even if the body is empty (regression from the client migration in 2.8.0)
- Fix: Unions with elements that specify no properties are generated correctly.
- Fix: Unions with a single type now have a valid type alias (rather than an invalid
typing.Union
).
- Fix: The unchecked base model now handles pulling the discriminant from a dict, not just a model/object.
- Fix: Snippet templates for discriminated unions now specify the
template_input
property which is required to actually see snippets of instantiating discriminated unions.
- Fix: downgrades mypy so we can run it over all our files without concern for their pydantic bug
- Fix: adds typehint to the response variable
- Fix: The SDK removes unset query parameters from requests (regression from the client migration in 2.8.0)
- Fix: The SDK fixes it's type for
files
parameters to the http client (regression from the client migration in 2.8.0)
- Fix: Snippets preserve trailing slashes
- Fix: The new http client abstraction ensures a slash is postfixed to the baseurl
- Fix: Snippets preserve trailing slashes
- Improvement: The Python generator now runs custom unit tests in CI if configured.
- Fix: The none-filtering function now supports mypy's invariance check.
- Fix: The parameter comment/documentation for timeouts on the root client now reflects the custom timeout passed through within configuration.
- Improvement: Endpoint function request logic has been abstracted into the request function of the wrapped httpx client.
- Improvement: The generator now outputs an
exampleId
alongside each generated snippet so that we can correlate snippets with the relevant examples. This is useful for retrieving examples from Fern's API and making sure that you can show multiple snippets in the generated docs.
- Fix: this adds a back door token getter function to OAuth clients to better test the functionality.
-
Improvement: Support adding optional dependencies and extras to your generated
pyproject.toml
. To use this configuration, please add the following:extra_dependencies: boto3: 1.28.57 langchain: version: "^0.1.20" optional: true extras: telemetry: ["langchain", "boto3"]
- Fix: tests now carry a type annotation for
expected_types
variable.
- Improvement: literal values are now all defaulted such that users are not required to plug in a redundant value.
- Fix: Optional lists returned from pagination endpoints are now appropriately flattened such that the
Pager
return types are correctlyPager[ListItem]
as opposed toPager[List[ListItem]]
.
- Fix: Add typing library for dateutils in testing lib to satisfy mypy errors.
- Fix: Stop specifying custom licenses manually, let poetry handle adding them.
- Fix: Support
list
SDK method names instead of defaulting tolist_
.
- Fix: Literal parameters are added back to the request body.
- Fix: Do not attempt to run
fern test
in CI until the command is more widely rolled out.
- Fix: Address
propogate
->propagate
typo in python codegen.
- Fix: This version addresses issues in unit test generation and reenables the creation of unit tests.
- Fix: The Python SDK generator now uses safe names wherever string concat is not used (like in client generation naming), so this will update module and parameter names.
- Fix: Snippets and unit tests now correctly write optional request bodies when
inline_request_params
is set toTrue
. Previously the generator wrote snippets that inlined these parameters, which does not match the generated SDK itself.
- Fix: Inlined body parameters now deconflict in naming with header and query parameters by prefixing the request objects name.
-
Fix: The
pyproject.toml
generator now writes authors in a valid format fortool.poetry
, not justproject
-
Fix: The query encoder now correctly handles none values
- Fix: The
pyproject.toml
generator now includes project URLs when specified.
- Improvement: Users can now specify information that will appear in their pypi record.
generators:
- name: fernapi/fern-python-sdk
metadata:
description: this is the desc for my package
keywords:
- science
- data analysis
documentationLink: "https://buildwithfern.com/learn"
homepageLink: "https://buildwithfern.com/"
authors:
- email: support@buildwithfern.com
name: Armando
- Fix: Inline request parameters now deconflict in naming with the unnamed path parameter arguments. Previously, when inlining request parameters into the method signature, we would not deconflict naming with the unnamed args preceding them. Now, conflicting unnamed parameters are post-fixed with an "_".
Before:
def method_name(id: str, *, id: str) -> None:
...
After:
def method_name(id_: str, *, id: str) -> None:
...
- Internal: The generator now uses the latest FDR SDK.
- Improvement: The generated SDK will now correctly encode deep object query parameters.
For example, if you have an object
{"test": {"nested": "object"}}
as a query parameter, we will now encode it astest[nested]=object
.
-
Chore: add enhanced snippet support for streaming endpoints.
Before:
from seed.client import SeedStreaming client = SeedStreaming( base_url="https://yourhost.com/path/to/api", ) client.dummy.generate_stream( num_events=1, )
After:
from seed.client import SeedStreaming client = SeedStreaming( base_url="https://yourhost.com/path/to/api", ) response = client.dummy.generate_stream( num_events=1, ) for chunk in response: yield chunk
-
Feature: Add support for cursor and offset pagination.
For example, consider the following endpoint
/users
endpoint:types: User: properties: name: string ListUserResponse: properties: next: optional<string> data: list<User> service: auth: false base-path: /users endpoints: list: path: "" method: GET pagination: cursor: $request.starting_after next_cursor: $response.next results: $response.data request: name: ListUsersRequest query-parameters: starting_after: optional<string> response: ListUsersResponse
The generated
client.Users.List
can then be used as aUser
generator (effectively the "default"):for user in client.users.list(...): print user
a page-by-page generator:
for page in client.users.list(...).iter_pages(): print(page.items)
or statically calling
next_page
to perform the pagination manually:pager = client.users.list(...) # First page print(pager.items) # Second page pager = pager.next_page() print(pager.items)
- Fix: the python generator previously used
exclude_unset
on pydantic models, however this would remove defaulted values. This change updates this to only exclude none fields that were not required.
-
Break: the python SDK is now on major version 2, there are no substantial logic changes, however default configuration has changed. To take this upgrade without any breaks, please add the below configuration to your
generators.yml
file:generators: - name: fernapi/fern-python-sdk config: inline_request_params: false
-
Improvement: you can now declare a new python version range for your
pyproject.toml
, which will declare a new version range for your pip package.generators: - name: fernapi/fern-python-sdk config: pyproject_python_version: ^3.8.1
-
Improvement: you can now specify dev dependencies from your
generators.yml
file:generators: - name: fernapi/fern-python-sdk config: extra_dev_dependencies: requests_mock: 1.12.1 boto3: 1.28.57
These will then populate your
pyproject.toml
automatically:... [tool.poetry.dev-dependencies] requests_mock = "1.12.1" boto3 = "1.28.57" ...
- Fix: the unchecked basemodel no longer tries to dereference an object if it's null.
- Improvement: The python generator now produces sync snippet templates, as opposed to just async templates as it was before
- Fix: Snippet templates now generate the correct imports for object types.
- Fix: The SDK now generates discriminated union snippet templates correctly.
- Improvement: Union types leverage the fern aware base model to include JSON and Dict function overrides.
- Fix: The vanilla pydantic base model now respects the
require_optional_fields
, this became a regression in 1.5.1-rc0 when we started to inline union properties which leverages the vanilla base model.
- Fix: Address formatting issues with snippet templates, we now strip newlines off OG snippets as well as plumb through indentation metadata to places that were previously missing it.
-
Fix: Discriminated union variants that are objects now have inlined properties instead of extending a base type.
Circle_Shape(pydantic_v1.BaseModel): type: typing.Literal["circle"] radius: integer Square_Shape(pydantic_v1.BaseModel): type: typing.Literal["circle"] side: integer
instead of
Circle_Shape(Circle): type: typing.Literal["circle"] Square_Shape(Square): type: typing.Literal["circle"]
-
Feat: The generator now supports inlining top-level request parameters instead of requiring users create a request object.
Config:
generators: - name: fernapi/fern-python-sdk config: inline_request_params: true
Before:
def get_and_return_with_optional_field( self, *, request: ObjectWithOptionalField, request_options: typing.Optional[RequestOptions] = None ) -> ObjectWithOptionalField: ...
After:
def get_and_return_with_optional_field( self, *, string: typing.Optional[str] = OMIT, integer: typing.Optional[int] = OMIT, long: typing.Optional[int] = OMIT, double: typing.Optional[float] = OMIT, bool: typing.Optional[bool] = OMIT, request_options: typing.Optional[RequestOptions] = None ) -> ObjectWithOptionalField: ...
- Improvement: keyword arguments are now ordered such that required params are ordered before optional params. Note that since these are kwargs, this is a non-breaking change.
- Improvement: docstrings now match numpydoc/PEP257 format
- Fix: Set
mypy
dev dependency in generatedpyproject.toml
to1.9.0
. This prevents upstreammypy
bugs from affecting user builds. Note that this is only a dev dependency, so it does not affect the behavior of the SDK. - Fix: Temporarily disable unit test generation.
- Improvement: Use named parameters for all
httpx
request params.
- Fix: Initialize the OAuth token provider member variables to their default values before they are set.
- Feature: The python SDK generator now supports OAuth client generation for the client-credentials flow.
-
Chore: default generated clients to follow redirects by default, this effectively flips the
follow_redirects_by_default
flag toTrue
and can be reverted with the following configuration:generators: - name: fernapi/fern-python-sdk config: follow_redirects_by_default: false
- Fix: the python SDK generator now checks to make sure a header is not null before casting it to a string.
- Internal: add logging for python snippet template generation.
-
Beta, feature: The generator now registers snippet templates which can be used for dynamic SDK code snippet generation.
Note: You must be on the enterprise tier to enable this mode.
- Fix: The generator now correctly imports
json
when deserializing server sent events.
- Feature: The generator now depends on v38 of Intermediate Representation which requires the latest
CLI. As part of this, the generator now supports server sent events using
httpx-sse
.
- Fix: There are a number of fixes to the skip validation code as well as tests to reflect those updates.
- Fix: The generator now writes the skipped-validation
cast
with a suffixing new line so that the code compiles.
- Fix: The generator no longer attempts to create a version file if Fern does not own generating the full package (e.g. in local generation). It's too confusing for to make the relevant changes to the package set up, and is also arguably not even needed in local generation.
-
[EXPERIMENTAL] Feature: The python SDK now includes a configuration option to skip pydantic validation. This ensures that Pydantic does not immediately fail if the model being returned from an API does not exactly match the Pydantic model. This is meant to add flexibility, should your SDK fall behind your API, but should be used sparingly, as the type-hinting for users will still reflect the Pydantic model exactly.
generators: - name: fernapi/fern-python-sdk ... config: pydantic_config: skip_validation: true
- Fix: Pydantic introduced a "break" to their 1.x libs by adding in a .v1 submodule that does not mirror the one that comes with pydantic v2. To get around this we now force the usage of the v1 submodule only if the pydantic version is v2.
-
Break: The python SDK now defaults new (breaking configuration) to introduce general improvements.
In order to revert to the previous configuration flags and avoid the break, please leverage the below configuration:
generators: - name: fernapi/fern-python-sdk config: improved_imports: false pydantic_config: require_optional_fields: true use_str_enums: false extra_fields: "forbid"
-
Improvement: The python SDK now supports specifying whether or not to follow redirects in requests by default, and exposes an option to override that functionality for consumers.
generators: - name: fernapi/fern-python-sdk config: follow_redirects_by_default: true
which then just instantiates the client like so:
client = SeedExhaustive( token="YOUR_TOKEN", follow_redirects=True # This is defaulted to the value passed into follow_redirects_by_default, and ignored if not specified )
- Fix: revert the change from 0.13.2, the stream call returns a context manager, which is not awaited. The issue that this was meant to solve was actually fixed in version
0.12.2
.
- Fix: Github workflows for publishing now work again (previously the trigger was incorrect).
-
Fix: Asynchronous calls to
httpx.stream
are now awaited. This is applicable to any file download or JSON streaming (chat completion) endpoints.# Before async with httpx.stream # After async with await httpx.stream
-
Improvement: discriminant values in unions are now defaulted such that callers no longer need to specify the discriminant:
# Before shape = Circle(discriminant="circle", radius=2.0) # After shape = Circle(radius=2.0)
- Improvement: the python SDK now exposes it's version through
__version__
to match module standards and expectations.
import seed
print(seed.__version__) # prints 0.0.1 or whatever version is contained within the pyproject.toml
- Fix: the python SDK uses the timeout provided to the top level client as the default per-request, previously if there was no timeout override in the RequestOptions, we'd default to 60s, even if a timeout was provided at the client level.
-
Improvement: Allow full forward compat with enums while keeping intellisense by unioning enum literals with
typing.AnyStr
.Before:
Operand = typing.Union[typing.AnyStr, typing.Literal[">", "=", "less_than"]]
After:
Operand = typing.Literal[">", "=", "less_than"]
- Improvement: Allow bytes requests to take in iterators of bytes, mirroring the types allowed by HTTPX.
- Fix: Fix the returned type and value contained within the retrying wrapper for the HTTPX client (http_client.py).
- Improves example generation and snippets for union types, as well as multi-url environments.
- Fix: Stringifies header arguments, HTTPX was previously hard failing for certain types
- Beta, feature: The SDK now generates tests leveraging auto-generated data to test typing, as well as wire-formatting (e.g. the SDKs are sending and receiving data as expected). This comes out of the box within the generated github workflow, as well as through the fern cli:
fern test --command "your test command"
. Note: You must be on the enterprise tier to enable this mode.
-
feature: Expose a feature flag to pass through additional properties not specified within your pydantic model from your SDK. This allows for easier forward compatibility should your SDK drift behind your spec.
Config:
generators: - name: fernapi/fern-python-sdk ... config: pydantic_config: extra_fields: "allow"
Example generated code:
# my_object.py class MyObject(pydantic.BaseModel): string: typing.Optional[str] = None ... # main.py o = pydantic.parse_obj_as(MyObject, {"string": "string", "my_new_property": "custom_value"}) print(o.my_new_property) # <--- "custom_value"
-
chore: Use docstrings instead of Pydantic field descriptions. This is meant to be a cleanliness change. This goes from:
field: Optional[str] = pydantic.Field(default=None, description="This is the description.")
to:
field: Optional[str] = pydantic.Field(default=None) """This is the description."""
-
Beta, Improvement: Introduces a
max_retries
parameter to the RequestOptions dict accepted by all requests. This parameter will retry requests automatically, with exponential backoff and a jitter. The client will automatically retry requests of a 5XX status code, or certain 4XX codes (429, 408, 409).client\ .imdb\ .create_movie( request=CreateMovieRequest(title="title", rating=4.3), request_options={ "max_retries": 5, } )
-
Beta: Introduce a
client
custom config that allows you to specify class_name and filename for the client. This configuration can be used in several ways:-
Rename your client class:
config: client: class_name: Imdb
-
Add custom functions to your generated SDK:
config: client: class_name: BaseImdb filename: base_client.py exported_class_name: Imdb exported_filename: client.py
Often times you may want to add additional methods or utilities to the generated client. The easiest way to do this is to configure Fern to write the autogenerated client in another file and extend it on your own.
With the configuration above, Fern's Python SDK generator will create a class called
BaseImdb
andAsyncBaseImdb
and put them in a file calledbase_client.py
. As a user, you can extend both these classes with custom utilities.To make sure the code snippets in the generated SDK are accurate you can specify
exported_class_name
andexported_filename
.
-
-
Improvement: Introduces a flag
use_str_enums
to swap from using proper Enum classes to using Literals to represent enums. This change allows for forward compatibility of enums, since the user will receive the string back.config: pydantic_config: use_str_enums: true
generates enums as:
Operand = typing.Literal[">", "=", "less_than"]
-
Improvement: You can now specify envvars to scan for headers, not just auth scheme headers.
# OpenAPI x-fern-global-headers: - header: x-api-key name: api_key optional: true env: MY_API_KEY
... or ...
# Fern Definition getAllUsers: method: GET path: /all request: name: GetAllUsersRequest headers: X-API-KEY: string env: MY_API_KEY
the generated client will look like
import os class Client: def __init__(self, *, apiKey: str = os.getenv("MY_API_KEY"))
- Fix: Fix the usage of ApiError when leveraging auth envvars, when the schema for ApiError was changed, this usage was missed in the update.
- Fix: We now grab enum values appropriately when enums are within unions.
-
Fix: Transition from lists to sequences within function calls, this is a fix as a result of how mypy handles type variance. This fix is only for function calls as testing shows that we do not hit the same issue within mypy with list[union[*]] fields on pydantic objects. This issue outlines it well: https://stackoverflow.com/questions/76138438/incompatible-types-in-assignment-expression-has-type-liststr-variable-has
-
Improvement: The Python SDK generator now defaults to
require_optional_fields = False
. This means that any requests that have optional fields no longer require a user to input data (or aNone
value) in. Example:# Previously: def my_function(my_parameter: typing.Optional[str]): pass my_function() # <--- This fails mypy my_function(None) # <--- This is necessary my_function("I work in both cases!") ... # Now: def my_function(): pass my_function() # <--- I no longer fail mypy my_function(None) # <--- I still work my_function("I work in both cases!")
-
Improvement (Beta): The Python generator now supports a configuration option called
improved_imports
. To enable this configuration, just add the following to your generators.ymlgenerators: - name: fernapi/fern-python-sdk ... config: improved_imports: true
Enabling improved imports will remove the verbose
resources
directory in the SDK and make the imports shorter. This will also improve the imports from Pylance and Pyright that are automatically generated# Before from sdk.resources.fhir import Patient # After from sdk.fhir import Patient
-
Improvement: Python now supports specifying files to auto-export from the root
__init__.py
file, this means you can export custom classes and functions from your package for users to access like so:from my_package import custom_function
the configuration for this is:
# generators.yml python-sdk: generators: - name: fernapi/fern-python-sdk version: 0.11.1 config: additional_init_exports: - from: file_with_custom_function imports: - custom_function
-
Chore: Add a docstring for base clients to explain usage, example:
class SeedTest: """ Use this class to access the different functions within the SDK. You can instantiate any number of clients with different configuration that will propogate to these functions. --- from seed.client import SeedTest client = SeedTest( token="YOUR_TOKEN", base_url="https://yourhost.com/path/to/api", ) """
-
Improvement: Python now supports a wider range of types for file upload, mirroring the
httpx
library used under the hood, these are grouped under a new typeFile
:# core/file.py FileContent = typing.Union[typing.IO[bytes], bytes, str] File = typing.Union[ # file (or bytes) FileContent, # (filename, file (or bytes)) typing.Tuple[typing.Optional[str], FileContent], # (filename, file (or bytes), content_type) typing.Tuple[typing.Optional[str], FileContent, typing.Optional[str]], # (filename, file (or bytes), content_type, headers) typing.Tuple[typing.Optional[str], FileContent, typing.Optional[str], typing.Mapping[str, str]], ] ... # service.py def post( self, *, file: core.File, request_options: typing.Optional[RequestOptions] = None, ) -> None: """ Parameters: - file: core.File. See core.File for more documentation - request_options: typing.Optional[RequestOptions]. Request-specific configuration. """ ... # main.py f = open('report.xls', 'rb') service.post(file=f) # Or leveraging a tuple with open('largefile.zip', 'rb') as f: service.post(file=('largefile.zip', f)) ...
-
Fix: Python now supports API specifications that leverage lists for file upload. Previously, Fern incorrectly made all
list<file>
type requests simplyfile
.# service.py def post( self, *, file_list: typing.List[core.File], request_options: typing.Optional[RequestOptions] = None, ) -> None: """ Parameters: - file_list: typing.List[core.File]. See core.File for more documentation - request_options: typing.Optional[RequestOptions]. Request-specific configuration. """ ... # main.py f1 = open('report.xls', 'rb') f2 = open('page.docx', 'rb') service.post(file_list=[f1, f2])
-
Fix: Several bugfixes were made to related to literal properties. If a literal is used as a query parameeter, header, path parameter, or request parameter, the user no longer has to explicitly pass it in.
For example, the following endpoint
endpoints: chat_stream: request: name: ListUsersRequest headers: X_API_VERSION: literal<"2022-02-02"> body: properties: stream: literal<true> query: string
would generate the following signature in Python
class Client: # The user does not have to pass in api version or stream since # they are literals and always the same def chat_stream(self, *, query: str) -> None:
-
Fix: The SDK always sends the enum wire value instead of the name of the enum. For example, for the following enum,
class Operand(str, enum.Enum): GREATER_THAN = ">" EQUAL_TO = "="
the SDK should always be sending
>
and=
when making a request.This affected enums used in path parameters, query parameters and any request body parameters at the first level. To fix, the SDK sends the
.value
attribute of the enum. -
Fix: Revert #2719 which introduced additional issues with circular references within our Python types.
-
Improvement: Add support for a RequestOptions object for each generated function within Python SDKs. This parameter is an optional final parameter that allows for configuring timeout, as well as pass in arbitrary data through to the request. RequestOptions is a TypedDict, with optional fields, so there's no need to instantiate an object, just pass in the relevant keys within a dict!
-
timeout_in_seconds
overrides the timeout for this specific request -
additional_body_parameters
are expanded into the JSON request body -
additional_query_parameters
are expanded into the JSON query parameters map -
additional_headers
are expanded into the request's header map -
Here's an example:
client\ .imdb\ .create_movie( request=CreateMovieRequest(title="title", rating=4.3), request_options={ "timeout_in_seconds": 99, "additional_body_parameters": {"another": "body parameter"}, "additional_headers": {"another": "header"}, } )
-
- Improvement: Remove support for Python 3.7. In order to support newer versions of libraries we depend on (such as typing and typing-extensions), we must move on to Python 3.8. With this change we are also able to:
- Remove the
backports
dependency, ascached_property
is now included withinfunctools
- Remove the upper bound dependency on Pydantic which had dropped support for Python 3.7
- Remove the
-
Fix: Remove literals from SDK function signatures, as they are not modifiable for end users.
Before:
def get_options(self, *, dry_run: typing_extensions.Literal[True]) -> Options: ... json=jsonable_encoder({"dryRun": dry_run}), ...
After:
def get_options(self, *) -> Options: ... json=jsonable_encoder({"dryRun": "true"}),
-
Fix: Acknowledge the optionality of a
File
property, previously we were requiring allFile
type inputs, even if they were specified as optional within the OpenAPI or Fern definition. Now, we check if the parameter is required and make the parameter optional if it is not.
-
Feature: The SDK generator now supports whitelabelling. When this is turned on, there will be no mention of Fern in the generated code.
Note: You must be on the enterprise tier to enable this mode.
- Fix: Increase recursion depth to allow for highly nested and complex examples, this is a temporary solution while the example datamodel is further refined.
- Fix: The Python SDK better handles cyclical references. In particular, cyclical references are tracked for undiscriminated unions, and update_forward_refs is always called with object references.
-
Feature: If the auth scheme has environment variables specified, the generated python client will scan those environment variables.
For example, for the following Fern Definition
auth: APIKey auth-schemes: APIKey: header: X-FERN-API-KEY type: string env: FERN_API_KEY
the generated client will look like
import os class Client: def __init__(self, *, apiKey: str = os.getenv("FERN_API_KEY"))
-
Fix: Enums in inlined requests send the appropriate value.
class Operand(str, Enum): greater_than = ">" equal_to = "=" # Previously the SDK would just send the operand directly def endpoint(self, *, operand: Operand): httpx.post(json={"operand": operand}) # Now, the SDK will send the value of the enum def endpoint(self, *, operand: Operand): httpx.post(json={"operand": operand.value})
- Chore: Initialize this changelog