python_api.py
Overview
The [python_api.py](/projects/286/67218) file provides a sophisticated utility to perform approximate comparisons between numeric values, sequences, mappings, and numpy arrays. Its main feature is the [approx()](/projects/286/67332) function, which enables intuitive and flexible tolerance-based equality checks, primarily useful in testing scenarios where floating-point precision issues occur. This utility supports:
Scalar numeric comparisons with configurable relative and absolute tolerances.
Recursive approximate comparisons for sequences (like lists and tuples) and mappings (like dictionaries).
Specialized handling for numpy arrays (including shape validation and element-wise comparison).
Support for
Decimaltypes with appropriate tolerances.Graceful fallback to strict equality for non-numeric types.
The module is designed to integrate seamlessly with testing frameworks (such as pytest) to simplify assertions involving floating-point and numeric data structures.
Class and Function Documentation
1. approx(expected, rel=None, abs=None, nan_ok=False) -> ApproxBase
**Purpose:** Factory function that returns an appropriate `ApproxBase` subclass instance to compare the `expected` value to an actual value approximately within specified tolerances.
**Parameters:**
expected(any): The reference value or structure (scalar, sequence, mapping, numpy array) against which to compare.rel(float | Decimal | None): Optional relative tolerance (fractional).abs(float | Decimal | None): Optional absolute tolerance.nan_ok(bool): Whether NaN values should compare equal to each other.
**Returns:**
An instance of a subclass of
ApproxBasesuited for the type ofexpected.
**Usage Example:**
from python_api import approx
assert 0.1 + 0.2 == approx(0.3)
assert [0.1, 0.2] == approx([0.1, 0.2])
assert {'a': 1.0} == approx({'a': 1.0})
2. Class ApproxBase
**Purpose:** Abstract base class providing the foundational interface and utilities for approximate comparisons. It is subclassed to handle various data types (scalars, sequences, mappings, numpy arrays).
**Key Attributes:**
expected: The expected value or structure.rel: Relative tolerance.abs: Absolute tolerance.nan_ok: Whether NaNs are considered equal.
**Key Methods:**
__eq__(self, actual) -> bool: Returns whetheractualapproximately equalsexpected.__ne__(self, actual) -> bool: Logical negation of__eq__._approx_scalar(x): Convertsxto anApproxScalarorApproxDecimalfor scalar comparison._yield_comparisons(actual): Abstract method; yields pairs of values to compare element-wise._check_type(): Validatesexpectedtype; overridden by subclasses.__bool__(): Disabled; raises an error if used in a boolean context to avoid misuse.
**Notes:**
Numpy's ufuncs are disabled on this class to avoid unwanted behavior (
__array_ufunc__ = None).Hashing is disabled (
__hash__ = None).
3. Class ApproxScalar(ApproxBase)
**Purpose:** Handles approximate comparison of single numeric scalar values.
**Key Attributes:**
DEFAULT_ABSOLUTE_TOLERANCE= 1e-12DEFAULT_RELATIVE_TOLERANCE= 1e-6
**Key Methods:**
__repr__(): Returns a string like"value ± tolerance", describing the expected value and tolerance.__eq__(actual): Implements approximate equality logic, including special handling for NaN, infinity, and booleans.tolerance: Property that calculates the effective tolerance based onrelandabs.
**Usage Example:**
a = ApproxScalar(1.0)
print(a) # Output: "1.0 ± 1e-06"
assert a == 1.0000005
4. Class ApproxDecimal(ApproxScalar)
**Purpose:** Specialized subclass of `ApproxScalar` for `decimal.Decimal` types, using decimal-based tolerances.
**Key Differences:**
Uses
Decimal("1e-12")andDecimal("1e-6")as default tolerances.Custom
__repr__to format tolerances correctly as Decimals.
5. Class ApproxSequenceLike(ApproxBase)
**Purpose:** Handles approximate comparison for ordered sequences (lists, tuples, or sequence-like objects).
**Key Methods:**
__repr__(): Displays the expected sequence with each element wrapped in an approx scalar._yield_comparisons(actual): Yields pairs of corresponding elements for comparison._repr_compare(other_side): Provides detailed comparison failure message including mismatched indices and differences.
**Type Checking:** Raises a `TypeError` if nested sequences are detected (nested data structures not supported).
6. Class ApproxMapping(ApproxBase)
**Purpose:** Handles approximate comparison of mappings/dictionaries where values are numeric.
**Key Methods:**
__repr__(): Displays the expected mapping with values wrapped in approx scalars._yield_comparisons(actual): Yields pairs of values matching keys in expected._repr_compare(other_side): Provides detailed failure message listing differing keys and difference metrics.
**Type Checking:** Raises a `TypeError` if nested dictionaries are detected.
7. Class ApproxNumpy(ApproxBase)
**Purpose:** Specialized handler for numpy arrays, performing element-wise approximate comparisons with shape validation.
**Key Methods:**
__repr__(): Shows the numpy array as nested approx scalars._yield_comparisons(actual): Yields element pairs from both arrays._repr_compare(other_side): Constructs detailed failure messages including max absolute and relative differences, and lists indices of mismatched elements.
**Implementation Detail:**
Converts inputs to numpy arrays when possible.
Compares shape before element-wise comparison.
8. Helper Functions
_compare_approx(full_object, message_data, number_of_elements, different_ids, max_abs_diff, max_rel_diff) -> list[str]
Builds a formatted list of strings describing the comparison failure, detailing mismatches and max differences._recursive_sequence_map(f, x)
Recursively applies functionfto each element in a nested sequence._is_sequence_like(expected)
Checks if an object behaves like a sequence (has__getitem__, is sized, and is not str/bytes)._is_numpy_array(obj)
ReturnsTrueifobjis convertible to a numpy ndarray and numpy is imported._as_numpy_array(obj)
Attempts to convertobjto a numpy ndarray if possible; otherwise returnsNone.
Important Implementation Details and Algorithms
Tolerance Calculation:
The scalar comparison uses a hybrid tolerance that takes the maximum of the relative tolerance (scaled by the expected value) and the absolute tolerance, ensuring meaningful comparisons near zero and for very large values.NaN and Infinity Handling:
NaN values compare equal only ifnan_ok=Trueis set. Infinite values only equal themselves.Type Safety in Comparisons:
The module enforces type checks to avoid comparing incompatible or nested unsupported structures, raisingTypeErrorwhere appropriate.Numpy Integration:
The module supports numpy arrays gracefully, validating shape compatibility and providing detailed failure messages with indices of mismatched elements.No Boolean Context:
TheApproxBaseclass disables boolean conversion to prevent misuse and confusing errors, encouraging explicit equality assertions.
Interaction with Other System Components
This module is designed as a utility for testing frameworks such as pytest, to allow expressive approximate equality assertions.
It can be imported and used in any Python testing code that needs to compare floating-point results with tolerance.
The module optionally integrates with numpy if it is imported in the environment, enhancing support for scientific computing use cases.
It uses standard Python collections and type hinting to maintain broad compatibility.
Visual Diagram
classDiagram
class ApproxBase {
- expected
- rel
- abs
- nan_ok
+ __eq__(actual)
+ __ne__(actual)
+ __bool__()
+ _approx_scalar(x)
+ _yield_comparisons(actual)
+ _check_type()
+ _repr_compare(other_side)
}
class ApproxScalar {
+ DEFAULT_ABSOLUTE_TOLERANCE = 1e-12
+ DEFAULT_RELATIVE_TOLERANCE = 1e-6
+ tolerance
+ __repr__()
+ __eq__(actual)
}
class ApproxDecimal {
+ DEFAULT_ABSOLUTE_TOLERANCE = Decimal("1e-12")
+ DEFAULT_RELATIVE_TOLERANCE = Decimal("1e-6")
+ __repr__()
}
class ApproxSequenceLike {
+ __repr__()
+ _repr_compare(other_side)
+ _yield_comparisons(actual)
+ _check_type()
}
class ApproxMapping {
+ __repr__()
+ _repr_compare(other_side)
+ _yield_comparisons(actual)
+ _check_type()
}
class ApproxNumpy {
+ __repr__()
+ _repr_compare(other_side)
+ _yield_comparisons(actual)
+ __eq__(actual)
}
ApproxScalar --|> ApproxBase
ApproxDecimal --|> ApproxScalar
ApproxSequenceLike --|> ApproxBase
ApproxMapping --|> ApproxBase
ApproxNumpy --|> ApproxBase
Summary
The [python_api.py](/projects/286/67218) file provides the `approx` mechanism for approximate equality checks essential in floating-point testing. It supports a variety of data types and structures, with configurable tolerances and detailed diagnostic messages for mismatches. Its design ensures extensibility, numpy compatibility, and safe usage patterns, making it a robust tool for numerical software testing.