Variants

Variants define a named group of image versions, each with its own transformation. Pass a mapping of names to Transform objects to variants() to generate a dictionary of URLs. Pass a dataclass class to return a typed instance with URL attributes.

Tip

Use the local Docker server on port 8080 for the examples in this chapter.

Generate variants

Define a complete transformation for each name, then generate URLs for a source image:

from pprint import pprint

from imgproxykit import Client, Transform

client = Client('http://localhost:8080')
source_url = 'https://picsum.photos/id/237/1600/1200'

products = {  # (1)!
    'small': Transform(width=320),
    'medium': Transform(width=640),
    'large': Transform(width=1280),
}

product_urls = client.variants(source=source_url, variants=products)  # (2)!

pprint(product_urls, sort_dicts=False)
  1. Choose names for your application and a complete transformation for each variant.

  2. Generate one URL per variant using the same source and client settings.

Show Output
{'small': 'http://localhost:8080/unsafe/width:320/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzIzNy8xNjAwLzEyMDA',
 'medium': 'http://localhost:8080/unsafe/width:640/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzIzNy8xNjAwLzEyMDA',
 'large': 'http://localhost:8080/unsafe/width:1280/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzIzNy8xNjAwLzEyMDA'}

Access URLs by name, such as product_urls['small'], or pass the dictionary to a template or API response.

Reuse a variant set

Define a set once and reuse it across source images. Keep shared settings in a base transformation and use extend() to add a width for each variant:

from pprint import pprint

from imgproxykit import Client, Transform

client = Client('http://localhost:8080')
dog_source = 'https://picsum.photos/id/237/1600/1200'
pug_source = 'https://picsum.photos/id/1025/1600/1200'

base = Transform(quality=85, format='webp')  # (1)!
sizes = {
    'small': base.extend(width=320),
    'medium': base.extend(width=640),
    'large': base.extend(width=1280),
}

dog_urls = client.variants(source=dog_source, variants=sizes)  # (2)!
pug_urls = client.variants(source=pug_source, variants=sizes)

pprint({'Dog': dog_urls}, sort_dicts=False)
pprint({'Pug': pug_urls}, sort_dicts=False)
  1. Share quality and format, then use extend() to add each width.

  2. Reuse sizes with different sources. You can keep the mapping at module level and reuse it across requests.

Show Output
{'Dog': {'small': 'http://localhost:8080/unsafe/quality:85/format:webp/width:320/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzIzNy8xNjAwLzEyMDA',
         'medium': 'http://localhost:8080/unsafe/quality:85/format:webp/width:640/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzIzNy8xNjAwLzEyMDA',
         'large': 'http://localhost:8080/unsafe/quality:85/format:webp/width:1280/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzIzNy8xNjAwLzEyMDA'}}
{'Pug': {'small': 'http://localhost:8080/unsafe/quality:85/format:webp/width:320/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzEwMjUvMTYwMC8xMjAw',
         'medium': 'http://localhost:8080/unsafe/quality:85/format:webp/width:640/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzEwMjUvMTYwMC8xMjAw',
         'large': 'http://localhost:8080/unsafe/quality:85/format:webp/width:1280/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzEwMjUvMTYwMC8xMjAw'}}

Both dictionaries have the same variant names and transformations, with URLs pointing to their respective sources.

Type variant names

Annotate the input mapping with a Literal union to check variant names when accessing the generated URLs:

from pprint import pprint
from typing import Literal, assert_type

from imgproxykit import Client, Transform

client = Client('http://localhost:8080')
source_url = 'https://picsum.photos/id/237/1600/1200'

type ProductKeys = Literal['small', 'medium', 'large']  # (1)!

products: dict[ProductKeys, Transform] = {
    'small': Transform(width=320),
    'medium': Transform(width=640),
    'large': Transform(width=1280),
}

product_urls = client.variants(source=source_url, variants=products)
assert_type(product_urls, dict[ProductKeys, str])  # (2)!

pprint(product_urls, sort_dicts=False)
  1. Limit variant names to a Literal union and use it as the mapping’s key type.

  2. The returned dictionary preserves that key type. A type checker accepts product_urls['small'] and rejects product_urls['profile'].

Show Output
{'small': 'http://localhost:8080/unsafe/width:320/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzIzNy8xNjAwLzEyMDA',
 'medium': 'http://localhost:8080/unsafe/width:640/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzIzNy8xNjAwLzEyMDA',
 'large': 'http://localhost:8080/unsafe/width:1280/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzIzNy8xNjAwLzEyMDA'}

Without an explicit key annotation, a regular string-keyed mapping typically produces dict[str, str].

Return a dataclass

Declare a standard-library dataclass when you want a named result type for an API response or application code. Each field combines its result type, str, with a transformation in Annotated:

from dataclasses import dataclass
from pprint import pprint
from typing import Annotated, assert_type

from imgproxykit import Client, Transform

base = Transform(quality=85, format='webp')


@dataclass(frozen=True, slots=True)
class ProductImages:
    """Declare the result fields and their transformations together."""

    small: Annotated[str, base.extend(width=320)]  # (1)!
    medium: Annotated[str, base.extend(width=640)]
    large: Annotated[str, base.extend(width=1280)]


client = Client('http://localhost:8080')
source_url = 'https://picsum.photos/id/237/1600/1200'

product_urls = client.variants(source_url, variants=ProductImages)  # (2)!

assert_type(product_urls, ProductImages)
assert_type(product_urls.small, str)

pprint(product_urls)
  1. Declare each URL field as Annotated[str, Transform(...)].

  2. Pass the class to return a ProductImages instance containing URLs.

Show Output
ProductImages(small='http://localhost:8080/unsafe/quality:85/format:webp/width:320/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzIzNy8xNjAwLzEyMDA',
              medium='http://localhost:8080/unsafe/quality:85/format:webp/width:640/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzIzNy8xNjAwLzEyMDA',
              large='http://localhost:8080/unsafe/quality:85/format:webp/width:1280/aHR0cHM6Ly9waWNzdW0ucGhvdG9zL2lkLzIzNy8xNjAwLzEyMDA')

Access URLs as attributes, such as product_urls.small. Reuse the class with other sources or clients.

Note

Each field must declare exactly one Transform in Annotated[str, Transform(...)]. Use Transform() for a URL without processing options. InitVar and init=False fields are unsupported.

Use ProductImages as a field type in a Pydantic model or msgspec Struct, and pass the result of client.variants() to that field. See the FastAPI example.

Next steps

See the Client API reference for the complete variants() signature and return type.