Coverage for fpdf2_textindex/interface/label_path.py: 68.75%

36 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-01 14:22 +0000

1"""Label Path.""" 

2 

3from __future__ import annotations 

4 

5from collections.abc import Iterable 

6from typing import SupportsIndex, TypeAlias, overload 

7 

8from typing_extensions import Self 

9 

10from fpdf2_textindex import constants as const 

11from fpdf2_textindex.utils import remove_quotes 

12 

13 

14class LabelPath(tuple[str, ...]): 

15 """Label Path.""" 

16 

17 def __new__(cls, labels: Self | Iterable[str] | str = ()) -> Self: 

18 """Initializes the `LabelPath`. 

19 

20 Args: 

21 labels: The labels composing the label path. 

22 

23 Raises: 

24 TypeError: If `labels` does not have type `LabelPath`, 

25 `Iterable[str]` or `str`. 

26 """ 

27 if isinstance(labels, cls): 

28 return labels 

29 elif isinstance(labels, Iterable): 29 ↛ 31line 29 didn't jump to line 31 because the condition on line 29 was always true

30 labels_ = tuple(labels) 

31 elif isinstance(labels, str): 

32 labels_ = (labels,) 

33 elif labels is None: 

34 labels_ = tuple() 

35 else: 

36 msg = f"invalid type of `labels`: {type(labels).__name__:s}" 

37 raise TypeError(msg) 

38 if not all(isinstance(la, str) for la in labels_): 38 ↛ 39line 38 didn't jump to line 39 because the condition on line 38 was never true

39 msg = "invalid type of `labels`-elements: all must be `str`" 

40 raise TypeError(msg) 

41 return super().__new__(cls, (remove_quotes(la) for la in labels_)) 

42 

43 @overload 

44 def __getitem__(self, i: SupportsIndex) -> str: ... 

45 

46 @overload 

47 def __getitem__(self, i: slice) -> Self: ... 

48 

49 def __getitem__(self, i: SupportsIndex | slice) -> Self | str: 

50 result = super().__getitem__(i) 

51 if isinstance(result, tuple): 

52 return type(self)(result) 

53 return result 

54 

55 def __repr__(self) -> str: 

56 return f"{type(self).__name__:s}({super().__repr__()[1:-1]:s})" 

57 

58 def __str__(self) -> str: 

59 return self.join() 

60 

61 def join(self) -> str: 

62 """Returns the joined label path. 

63 

64 Returns: 

65 The label path, joined by the path delimiter. 

66 """ 

67 return f" {const.PATH_DELIMITER:s} ".join(f'"{la:s}"' for la in self) 

68 

69 @classmethod 

70 def split_str(cls, label_path_str: str) -> Self: 

71 """Splits a label path string by the path delimiter, removes quotes 

72 from its elements and returns the corresponding label path. 

73 

74 Args: 

75 label_path_str: The label path string to split. 

76 

77 Returns: 

78 The corresponding label path. 

79 """ 

80 return cls(label_path_str.split(const.PATH_DELIMITER)) 

81 

82 

83LabelPathT: TypeAlias = LabelPath | Iterable[str] | str 

84"""Label Path Type."""