Coverage for src/gentrie/validation.py: 100%

19 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-08-20 09:30 -0700

1# -*- coding: utf-8 -*- 

2 

3"""Validation functions for the gentrie package.""" 

4 

5from collections.abc import Sequence 

6from typing import Any 

7from warnings import warn 

8 

9from .protocols import TrieKeyToken 

10 

11 

12def is_triekeytoken(token: Any) -> bool: 

13 """Tests token for whether it is a valid :class:`TrieKeyToken`. 

14 

15 A valid :class:`TrieKeyToken` is a hashable object (implements both ``__eq__()`` and ``__hash__()`` methods). 

16 

17 Examples: 

18 :class:`bool`, :class:`bytes`, :class:`float`, :class:`frozenset`, 

19 :class:`int`, :class:`str`, :class:`None`, :class:`tuple`. 

20 

21 Args: 

22 token (Any): Object for testing. 

23 

24 Returns: 

25 :class:`bool`: ``True`` if a valid :class:`TrieKeyToken`, ``False`` otherwise. 

26 """ 

27 return isinstance(token, TrieKeyToken) 

28 

29 

30def is_hashable(token: Any) -> bool: 

31 """is_hashable is deprecated and will be removed in a future version. 

32 

33 This function is a wrapper for :func:`is_triekeytoken` and is only provided for backward compatibility. 

34 

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) 

43 

44 

45def is_generalizedkey(key: Any) -> bool: 

46 """Tests key for whether it is a valid `GeneralizedKey`. 

47 

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. 

51 

52 Parameters: 

53 key (Any): Key for testing. 

54 

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. 

61 

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 

65 

66 # General check: Must be a sequence. 

67 if not isinstance(key, Sequence): 

68 return False 

69 

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 

74 

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 

80 

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]