--- title: Python | Stainless description: Generate production-ready Python SDKs from your OpenAPI specification --- Python is fully supported. The Stainless Python SDK generator creates idiomatic, type-safe Python client libraries from your OpenAPI specification. **Example repositories:** - [openai/openai-python](https://github.com/openai/openai-python) - [anthropics/anthropic-sdk-python](https://github.com/anthropics/anthropic-sdk-python) - [Metronome-Industries/metronome-python](https://github.com/Metronome-Industries/metronome-python) - [browserbase/stagehand-python](https://github.com/browserbase/stagehand-python) - [ComposioHQ/composio-base-py](https://github.com/ComposioHQ/composio-base-py) ## Configuration To generate a Python SDK, add the `python` target to your Stainless configuration file: ``` targets: python: edition: python.2025-11-20 package_name: my_company_client ``` ### Common configuration options ``` targets: python: # Specify the edition edition: python.2025-11-20 # package_name is used for imports (e.g., "import my_company_client") package_name: my_company_client # project_name is the PyPI distribution name (e.g., "pip install my-company-client") # If not specified, defaults to package_name project_name: my-company-client # Configure publishing publish: pypi: true ``` For a complete list of configuration options, see the [Python target reference](/docs/reference/config#python/index.md). ## Editions Editions allow Stainless to make improvements to SDKs that aren’t backwards-compatible. You can explicitly opt in to new editions when you’re ready. See the [SDK and config editions reference](/docs/reference/editions/index.md) for more information. #### python.2025-11-20 - Changed default package manager from Rye to uv - To revert to Rye, set `options.use_uv: false` #### python.2025-10-08 - Initial edition for Python (used by default if no edition is specified) ## Publishing to PyPI Package your Python SDK for distribution on [PyPI](https://pypi.org/), making it easy for users to install with `pip install`. Before publishing, you need to [link a production repository](/docs/sdks/publish#link-production-repos/index.md) where Stainless will push your SDK code. - [Trusted Publishing (OIDC) \[Recommended\]](#tab-panel-27) - [API Tokens](#tab-panel-28) Trusted publishing uses OpenID Connect (OIDC) to authenticate with PyPI without requiring long-lived API tokens. This is the recommended approach for security. (Option 1): Configure a new PyPI project 1. Navigate to the [publishing settings](https://pypi.org/manage/account/publishing/) in your PyPI account, and scroll down to the `Add a new pending publisher` section. 2. Enter the **PyPI project name** for your SDK. This should match the `targets.python.package_name` value in your Stainless config. 3. Enter the **Owner** and the **Repository name** to match the GitHub repository’s location. 4. Enter `publish-pypi.yml` as the **Workflow name**. 5. **\[Recommended]** If you utilize a specific [GitHub Actions environment](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments) for releases, enter the **Environment name**. This will make it so only release workflows running in that environment can publish. The release environment is set in your [Stainless config](/docs/reference/config/#codeflow-config/index.md) like so: ``` codeflow: release_environment: # ... ``` 6. Click **Add**. (Option 2): Configure an existing PyPI project 1. Navigate to your project settings at `pypi.org/manage/project//settings/publishing`, and find the `Add a new publisher` section. 2. Enter the **Owner** and the **Repository name** to match the GitHub repository’s location. 3. Enter `publish-pypi.yml` as the **Workflow name**. 4. **\[Recommended]** If you utilize a specific [GitHub Actions environment](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments) for releases, enter the **Environment name**. This will make it so only release workflows running in that environment can publish. The release environment is set in your [Stainless config](/docs/reference/config/#codeflow-config/index.md) like so: ``` codeflow: release_environment: # ... ``` 5. Click **Add**. ### Update your Stainless config Update the [Stainless config](/docs/reference/config#python/index.md) to specify OIDC authentication and save. ``` targets: python: package_name: publish: pypi: auth_method: oidc ``` ### Troubleshooting Trusted publishing is only supported with the `uv` package manager. If you’re using `rye` you may get a `No access token found` error. Ensure your config is on the `python.2025-11-20` [edition](/docs/reference/editions/index.md) or higher: ``` targets: python: edition: python.2025-11-20 ``` Use API tokens to authenticate with PyPI. Note that these tokens may need to be rotated periodically for security. Get an API token 1. Log in or sign up at [PyPI](https://pypi.org/). 2. Select your profile picture on the top right to open a dropdown. 3. Navigate to **Access settings > API tokens > Add API token**. You may have to verify your email address and set up 2FA if you haven’t already done so. *Note:* If you haven’t created a PyPI package yet, you can create an API token scoped to your whole account, or you can create an empty PyPI package to use as a starting point. Add the token to your production repo 1. In your production repo, navigate to **Secrets and variables** > **Actions** > **New repository secret**. The URL should look like `https://github.com///settings/secrets/actions/new`. 2. Add a new secret named `PYPI_TOKEN` with your API token. ### Choose a package name and update your Stainless config 1. Choose an available package name. Suggested names are: - `` - `-client` You can check whether the name is available by testing the link at `https://pypi.org/project/`. 2. Update the [Stainless config](/docs/reference/config#python/index.md) with your package name and save. ``` targets: python: package_name: publish: pypi: true ``` ## Design decisions ### Why we don’t use Python’s `Enum` class for enums Stainless generates Python enums using `Literal` types instead of Python’s built-in `Enum` class. For example, instead of: ``` from enum import Enum class Status(str, Enum): PENDING = "pending" COMPLETED = "completed" FAILED = "failed" ``` We generate: ``` from typing_extensions import Literal Status = Literal["pending", "completed", "failed"] ``` This is an intentional design decision based on several considerations: **Forward compatibility**: `Literal` types ensure forward compatibility when new enum values are added to your API. If your server starts returning a new value like `"cancelled"` that wasn’t present when the SDK was generated, `Literal` types allow it to pass through gracefully. With `Enum` classes, you’d need to update the SDK and release a new version to handle new values. **Lightweight and developer-friendly**: `Literal` types require no runtime overhead and less boilerplate. They provide excellent type checking and auto-completion in modern IDEs while keeping the generated code simple and readable. **Consistent philosophy**: Across all our SDKs, we believe it’s better to let unexpected enum values pass through rather than breaking when new values are introduced. This approach prioritizes SDK stability and reduces the need for frequent breaking changes. For more context on our design philosophy around forward compatibility, check out our [blog post on Java enum compatibility](https://www.stainless.com/blog/forwards-compatible-enums-in-java) — while Java doesn’t have literals like Python, it illustrates similar design considerations.