Skip to main content

Test that type annotations match click parameter types

Project description

click-type-test

PyPI - Version PyPI - Python Version


Table of Contents

Overview

click-type-test allows you to test that your click options and arguments match your type annotations.

It is named by the trio of things which it supports:

  • click: build a CLI
  • type: add type annotations
  • test: test that the first two match

Installation

click-type-test only works on Python 3.10+ .

On supported python versions, it should be installed with

pip install click-type-test

Usage

Install the package and then use click_type_test to extract annotation data and check it against your commands.

from click_type_test import check_param_annotations
from foopkg import mycommand

def test_mycommand_annotations():
    check_param_annotations(mycommand)

check_param_annotations raises a click_type_test.BadAnnotationError if an annotation does not match.

Version Guarding

Just because click-type-test only works on certain pythons does not mean that your application, tested with it, is also restricted to those python versions.

You can guard your usage like so (under pytest):

import pytest
import sys
from foopkg import mycommand

@pytest.mark.skipif(
    sys.version_info < (3, 10),
    reason="click-type-test requires py3.10+",
)
def test_mycommand_annotations():
    from click_type_test import check_param_annotations
    check_param_annotations(mycommand)

Type Determination Logic

click-type-test makes its best effort to determine what type will be passed to the command at runtime, based on the available information.

  1. If the parameter implements AnnotatedParameter and has an explicit annotation, that value will always be used.

    This makes this value the highest precedence override.

  2. If there is a callback function with a return type annotation, the return type of the callback will be used. This makes the callback return type the second highest precedence value.

  3. After this, click-type-test must inspect the parameter type which has been used, any default value which has been set, multiple=True for options and nargs=-1 for arguments, and generally try to infer the type correctly.

For parameter type evaluation, users can control the behavior of custom types in two significant ways:

  • annotate convert with a return type annotation (this will be used if available)

  • implement AnnotatedOption and define a type annotation (this is used as the highest precedence value for a parameter type, so it can be used as an override)

As a simple example, click-type-test is able to handle the following comma delimited list type definition correctly correctly:

class CommaDelimitedList(click.ParamType):
    def get_metavar(self, param: click.Parameter) -> str:
        return "TEXT,TEXT,..."

    def convert(
        self, value: str, param: click.Parameter | None, ctx: click.Context | None
    ) -> list[str]:
        value = super().convert(value, param, ctx)
        return value.split(",") if value else []

Extending And Adjusting With AnnotatedParamType and AnnotatedOption

The type deductions made by click-type-test are not always going to be correct. But rather than skipping options or parameters, it's better to pass along the information needed if possible.

Custom parameter types can support usage with click-type-test by implementing the AnnotatedParamType protocol.

Similarly, custom click.Option subclasses can support specialized usage by implementing the AnnotatedOption protocol.

The path you take will depend a bit on the precise needs of your CLI, but it generally should be possible to tune click-type-test to correctly understand your CLI.

AnnotatedParamType

If you have a custom ParamType, extend it to implement the AnnotatedParamType protocol and it will have first-class support in click-type-test.

This requires that there be a method, get_type_annotation, which takes the click.Parameter which was used and returns the type which should be expected as an annotation.

You can check that a ParamType implements AnnotatedParamType with a simple isinstance check:

import click_type_test

isinstance(myparamtype, click_type_test.AnnotatedParamType)

AnnotatedParameter

If you have a subclass of Option or Argument which produces specialized values, it may be necessary to provide type information from that class. To handle this case, just have your parameter class implement the AnnotatedParameter protocol.

This requires a method, has_explicit_annotation, and a property type_annotation. has_explicit_annotation takes no arguments and returns a bool. type_annotation returns a type.

See examples/explicitly_annotation_option.py for an example.

You can check that a Parameter implements AnnotatedParameter with a simple isinstance check:

import click_type_test

isinstance(myparam, click_type_test.AnnotatedParameter)

API

The following values are the consumable API for click-type-test. No other values are part of the interface.

  • AnnotatedParamType: a protocol for click.ParamType subclasses which provide explicit type information. It is runtime checkable.

  • AnnotatedParameter: a protocol for click.Parameter subclasses which provide explicit type information. It is runtime checkable.

  • deduce_type_from_parameter: a function which takes a click.Parameter and returns a type. Used internally but useful for unit testing custom classes.

  • check_param_annotations: a function which takes a click.Command and checks the type annotations on its callback against its parameter list.

  • BadAnnotationError: the error type raised if checking annotations fails.

License

click-type-test is distributed under the terms of the MIT license.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

click_type_test-1.3.0.tar.gz (9.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

click_type_test-1.3.0-py3-none-any.whl (9.3 kB view details)

Uploaded Python 3

File details

Details for the file click_type_test-1.3.0.tar.gz.

File metadata

  • Download URL: click_type_test-1.3.0.tar.gz
  • Upload date:
  • Size: 9.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for click_type_test-1.3.0.tar.gz
Algorithm Hash digest
SHA256 c907e00a4bf0b6f1f56440550fa80d04e1aeb028309f990e1f2438252702c8f3
MD5 81e2bd556acaa7683ebff0da79999801
BLAKE2b-256 20f0bc1c9a8b2e11d9ee8c0524655a84ea45061aa55cdfa8c5d485dbcf7ea7d5

See more details on using hashes here.

Provenance

The following attestation bundles were made for click_type_test-1.3.0.tar.gz:

Publisher: publish.yaml on sirosen/click-type-test

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file click_type_test-1.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for click_type_test-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1a79725875b3abf06af5611480d9fab01d752bf67449e7811903c55aa3922d29
MD5 5476c91eaa5395320cbdeb4f6c0c3c5d
BLAKE2b-256 11eaa1f495b685971bfb1e55c9cb733985c9f04d22c0753107511b07bb41ff01

See more details on using hashes here.

Provenance

The following attestation bundles were made for click_type_test-1.3.0-py3-none-any.whl:

Publisher: publish.yaml on sirosen/click-type-test

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page