Coverage for src/gentrie/validation.py: 89%
19 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-08-17 11:24 -0700
« prev ^ index » next coverage.py v7.6.10, created at 2025-08-17 11:24 -0700
1# -*- coding: utf-8 -*-
3"""Validation functions for the gentrie package."""
5from collections.abc import Sequence
6from typing import Any
7from warnings import warn
9from .protocols import TrieKeyToken
12def is_triekeytoken(token: Any) -> bool:
13 """Tests token for whether it is a valid :class:`TrieKeyToken`.
15 A valid :class:`TrieKeyToken` is a hashable object (implements both ``__eq__()`` and ``__hash__()`` methods).
17 Examples:
18 :class:`bool`, :class:`bytes`, :class:`float`, :class:`frozenset`,
19 :class:`int`, :class:`str`, :class:`None`, :class:`tuple`.
21 Args:
22 token (Any): Object for testing.
24 Returns:
25 :class:`bool`: ``True`` if a valid :class:`TrieKeyToken`, ``False`` otherwise.
26 """
27 return isinstance(token, TrieKeyToken)
30def is_hashable(token: Any) -> bool:
31 """is_hashable is deprecated and will be removed in a future version.
33 This function is a wrapper for :func:`is_triekeytoken` and is only provided for backward compatibility.
35 Use :func:`is_triekeytoken` instead.
36 """
37 warn(
38 "is_hashable is deprecated and will be removed in a future version. Use is_triekeytoken instead.",
39 DeprecationWarning,
40 stacklevel=2
41 )
42 return is_triekeytoken(token)
45def is_generalizedkey(key: Any) -> bool:
46 """Tests key for whether it is a valid `GeneralizedKey`.
48 A valid :class:`GeneralizedKey` is a :class:`Sequence` that returns
49 :class:`TrieKeyToken` protocol conformant objects when
50 iterated. It must have at least one token.
52 Parameters:
53 key (Any): Key for testing.
55 Returns:
56 :class:`bool`: ``True`` if a valid :class:`GeneralizedKey`, ``False`` otherwise.
57 """
58 # This complex logic makes this hotpath code MUCH faster (as much as 50 or 60 times) by performing
59 # very fast dedicated checks for common types before performing much slower per-token generalized
60 # protocol based checks.
62 # Fast path 1: A non-empty string or bytes is a valid key.
63 if isinstance(key, (str, bytes)) and key:
64 return True
66 # General check: Must be a sequence.
67 if not isinstance(key, Sequence):
68 return False
70 # Now that we know it's a sequence, we can safely check its length.
71 # An empty sequence is not a valid key.
72 if not key:
73 return False
75 # Fast path 2: Check for sequences of common, simple built-in types.
76 # This is much faster than the general protocol check.
77 if all(isinstance(t, (int, float, complex, frozenset, tuple, bool, str, bytes)
78 ) for t in key): # pyright: ignore[reportUnknownVariableType]
79 return True
81 # Fallback/Cold path: Perform the slower, general protocol check.
82 # This only runs if the fast path fails.
83 return all(isinstance(t, TrieKeyToken) for t in key) # pyright: ignore[reportUnknownVariableType]