Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.refactron.dev/llms.txt

Use this file to discover all available pages before exploring further.

Transform ID: manual_typecheck_to_hints Language: Python

What it does

Detects isinstance(x, T1) / elif isinstance(x, T2) / ... chains where every branch discriminates the same parameter x, then annotates the parameter with Union[T1, T2, ...] and adds from typing import Union if needed. The chain itself is preserved (the runtime check still runs); only the parameter signature is enriched.

Detector pattern

The detector at src/analyze/detectors/python/manual-typecheck.ts walks If / Elif chains looking for isinstance(<param>, <type>) shapes, groups them by parameter name, and surfaces a finding only when the discriminated parameter has no existing annotation — so every finding maps to a change the transform will actually make.

Preconditions

  1. The function has at least one parameter without an existing type annotation.
  2. Every isinstance branch in the chain discriminates the same single parameter.
  3. The chain effectively dispatches the function body — i.e. the bare-else is either absent or raises.
  4. The parameter is not already annotated.
  5. The chain references at least two distinct types (a single isinstance is not informative enough to be worth annotating).

Before / after

import math


class Circle:
    def __init__(self, radius):
        self.radius = radius


class Square:
    def __init__(self, side):
        self.side = side


def area(shape):
    if isinstance(shape, Circle):
        return math.pi * shape.radius * shape.radius
    elif isinstance(shape, Square):
        return shape.side * shape.side
    else:
        raise TypeError("unknown shape: %r" % shape)

Edge cases handled

  • Adds from typing import Union when not already imported.
  • Preserves the runtime isinstance chain — purely additive in semantics.
  • Records satisfied preconditions as annotated:<fn>:<param> for traceability.

Edge cases NOT handled (skip via precondition)

  • Parameter is already annotated (def handle(x: int)).
  • Chain discriminates more than one parameter (e.g. isinstance(x, int) then isinstance(y, str)).
  • Chain contains a single type only.
  • The discriminated parameter doesn’t exist in the function signature (defensive check).