openapi-python-client

repository·main·Indexed 24 days ago

https://github.com/openapi-generators/openapi-python-client

A generator that creates modern, type-annotated Python client libraries from OpenAPI 3.0 and 3.1 specifications. It utilizes Python dataclasses and Jinja2 templates to provide a developer-friendly experience, supporting both synchronous and asynchronous API calls via sync, sync_detailed, asyncio, and asyncio_detailed methods. The tool includes support for token-based authentication through AuthenticatedClient and manages dependencies and packaging using Poetry.

Tokens
35.2K
Snippets
88
Records
151
Agent score
80%

What's inside openapi-python-client

  1. How the `functional_tests` module works

    main

    The functional_tests module contains end-to-end tests that treat both the generator and the generated code as black boxes. Instead of testing low-level implementation details or comparing code against 'golden records', these tests verify the actual behavior of the generated client at runtime.

    There are two primary submodules:

    1. generated_code_execution: Uses valid API specs to run the generator, then imports and executes the resulting code to ensure it works correctly.
    2. generator_failure_cases: Uses invalid API specs to verify that the generator correctly produces warnings for bad schemas or fatal errors for invalid specifications.
  2. Use Runtime Expressions in Link and Callback Objects

    main

    Runtime expressions allow you to define values based on information available during an actual HTTP call. These are primarily used within Link Objects and Callback Objects to dynamically drive operations.

    Key rules:

    • Expressions preserve the type of the referenced value.
    • Expressions can be embedded into string values by surrounding them with {} curly braces.
    • If a runtime expression fails to evaluate, no parameter value is passed to the target operation.
    • Request parameters (like headers or path params) MUST be declared in the parameters section of the parent operation to be evaluatable.
    links:
      address:
        operationId: getUserAddressByUUID
        parameters:
          # get the `uuid` field from the `uuid` field in the response body
          userUuid: $response.body#/uuid
  3. Configure XML Arrays (Wrapped vs Unwrapped)

    main

    When defining arrays in XML, you can control whether they are wrapped in a container element or appear as a sequence of elements.

    • Unwrapped (Default): Elements appear one after another. To give them a specific name, use xml.name inside the items object.
    • Wrapped: Elements are contained within a parent element. To control the parent element name, use xml.name on the array itself. To control the child element name, use xml.name inside the items object.

    Example of a wrapped array with custom names:

    animals:
      type: array
      items:
        type: string
        xml:
          name: animal
      xml:
        name: aliens
        wrapped: true

    Resulting XML: <aliens><animal>value</animal></aliens>

  4. Understand Parameter Locations

    main

    In an OpenAPI specification, a parameter's location is defined by the in field. There are four possible values:

    • path: The parameter is part of the operation's URL (e.g., /items/{itemId}). If in is path, the required property MUST be true.
    • query: Parameters appended to the URL (e.g., /items?id=123).
    • header: Custom HTTP headers. Note that Accept, Content-Type, and Authorization headers should be ignored if defined here.
    • cookie: Used to pass specific cookie values to the API.
  5. Understanding the OpenAPI Specification (OAS)

    main

    The OpenAPI Specification (OAS) is a language-agnostic standard for defining HTTP APIs. It allows both humans and machines to discover and understand an API's capabilities (endpoints, parameters, request/response formats) without needing access to the source code.

    When using openapi-python-client, you provide an OpenAPI definition (typically a YAML or JSON file) which the tool then uses to generate a type-safe Python client. The quality and completeness of your generated client depend on how well your OpenAPI definition adheres to the specification.

  6. Instantiating non-pydantic schema types

    main

    Some OpenAPI schema types cannot be implemented as standard Pydantic models due to Python's typing constraints regarding dynamic field names. These types are implemented as type aliases for Dict.

    When you need to create instances of these types, do not attempt to use a class constructor; instead, use a standard Python dict.

    Supported types implemented as Dict:

    • Callback: Dict[str, PathItem]
    • Paths: Dict[str, PathItem]
    • Responses: Dict[str, Union[Response, Reference]]
    • SecurityRequirement: Dict[str, List[str]]
  7. Define an API Responses Object

    main

    The Responses Object is a container that maps HTTP response codes to their expected Response Object.

    • Specific Status Codes: Use the HTTP status code (e.g., "200") as the property name. Status codes MUST be enclosed in quotation marks in JSON.
    • Wildcards: You can define ranges using uppercase X (e.g., 2XX covers 200-299).
    • Default Response: Use the default field to define a response for any HTTP code not explicitly covered by other declarations.

    An operation MUST contain at least one response code, and it SHOULD include the successful operation response.

    {
      "200": {
        "description": "a pet to be returned",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Pet"
            }
          }
        }
      },
      "default": {
        "description": "Unexpected error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorModel"
            }
          }
        }
      }
    }
  8. How API endpoints and functions are structured

    main

    The generator maps every path/method combination to a Python module.

    Module Location:

    • If an endpoint has tags, the first tag is used as the module name (e.g., my_enum_api_client.api.my_tag).
    • If an endpoint has no tags, it is located in my_enum_api_client.api.default.

    Available Functions per Module: Each endpoint module provides four distinct functions:

    1. sync: A blocking request that returns the parsed data model if successful, or None if it fails.
    2. sync_detailed: A blocking request that returns a Response[Model] object, which includes the parsed data and metadata like status_code.
    3. asyncio: An asynchronous version of sync.
    4. asyncio_detailed: An asynchronous version of sync_detailed.

    All path parameters, query parameters, and request bodies are exposed as arguments to these functions.

  9. Use Runtime Expressions in Callbacks and Links

    main

    Runtime expressions allow you to define values dynamically based on information available during an HTTP request or response. These are primarily used in Callback Objects (to define webhook URLs) and Link Objects (to define relationships between operations).

    Commonly used expressions include:

    • $url: The full URL of the request.
    • $method: The HTTP method (e.g., POST).
    • $request.path.{name}: A path parameter value.
    • $request.query.{name}: A query parameter value.
    • $request.header.{name}: A header value.
    • $request.body#{json-pointer}: A value from the request body using a JSON Pointer.
    • $response.header.{name}: A header value from the response.
    • $response.body#{json-pointer}: A value from the response body using a JSON Pointer.
    Expression | Value 
    ---|:---
    $url | http://example.org/subscribe/myevent?queryUrl=http://clientdomain.com/stillrunning
    $method | POST
    $request.path.eventType | myevent
    $request.query.queryUrl | http://clientdomain.com/stillrunning
    $request.header.content-Type | application/json
    $request.body#/failedUrl | http://clientdomain.com/failed
    $request.body#/successUrls/2 | http://clientdomain.com/medium
    $response.header.Location | http://example.org/subscription/1
  10. Use runtime expressions in OpenAPI

    main

    OpenAPI allows the use of runtime expressions to reference dynamic values during operation execution. Expressions can be embedded into string values by surrounding them with {} curly braces. Note that runtime expressions preserve the type of the referenced value.

    Common expression patterns include:

    • HTTP Method: $method (the allowable values are the HTTP operation methods).
    • Requested media type: $request.header.accept.
    • Request parameter: $request.path.id (Note: parameters MUST be declared in the parameters section of the parent operation to be evaluated).
    • Request body property: $request.body#/user/uuid (references portions of the requestBody).
    • Request URL: $url.
    • Response value: $response.body#/status (references portions of the response body).
    • Response header: $response.header.Server (only single header values are available).
  11. Implement Polymorphism with the discriminator object

    main

    When a payload can be one of several different schemas (using oneOf, anyOf, or allOf), use a discriminator object to aid in serialization, deserialization, and validation. The discriminator identifies which property in the payload determines the specific schema to use.

    Requirements:

    • The discriminator field MUST be a required field.
    • The propertyName within the discriminator object is REQUIRED.
    • Inline schema definitions (schemas without an ID) cannot be used in polymorphism.
    • The discriminator object is only legal when using oneOf, anyOf, or allOf.

    Example of Polymorphic Pets:

    Pet:
      type: object
      discriminator:
        propertyName: petType
      properties:
        name:
          type: string
        petType:
          type: string
      required:
      - name
      - petType
    
    Cat:
      allOf:
      - $ref: '#/components/schemas/Pet'
      - type: object
        properties:
          huntingSkill:
            type: string
            enum:
            - clueless
            - lazy
            - adventurous
            - aggressive
        required:
        - huntingSkill
    components:
      schemas:
        Pet:
          type: object
          discriminator:
            propertyName: petType
          properties:
            name:
              type: string
            petType:
              type: string
          required:
          - name
          - petType
        Cat:
          description: A representation of a cat
          allOf:
          - $ref: '#/components/schemas/Pet'
          - type: object
            properties:
              huntingSkill:
                type: string
                description: The measured skill for hunting
                enum:
                - clueless
                - lazy
                - adventurous
                - aggressive
            required:
            - huntingSkill
        Dog:
          description: A representation of a dog
          allOf:
          - $ref: '#/components/schemas/Pet'
          - type: object
            properties:
              packSize:
                type: integer
                format: int32
                description: the size of the pack the dog is from
                default: 0
                minimum: 0
            required:
            - packSize