Coverage for install/scipp/format/_parse.py: 48%

58 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-11-17 01:51 +0000

1# SPDX-License-Identifier: BSD-3-Clause 

2# Copyright (c) 2023 Scipp contributors (https://github.com/scipp) 

3# @author Jan-Lukas Wynen 

4 

5import dataclasses 

6import enum 

7import re 

8 

9 

10class FormatType(enum.Enum): 

11 default = None 

12 compact = 'c' 

13 

14 def __str__(self) -> str: 

15 match self: 

16 case FormatType.default: 

17 return '' 

18 case FormatType.compact: 

19 return 'c' 

20 

21 

22class Selection(enum.Enum): 

23 edges = '^' 

24 begin = '<' 

25 end = '>' 

26 

27 def __str__(self) -> str: 

28 match self: 

29 case Selection.edges: 

30 return '^' 

31 case Selection.begin: 

32 return '<' 

33 case Selection.end: 

34 return '>' 

35 

36 

37@dataclasses.dataclass(frozen=True, slots=True) 

38class FormatSpec: 

39 format_type: FormatType 

40 _selection: dataclasses.InitVar[str | None] 

41 _length: dataclasses.InitVar[str | None] 

42 _nested: dataclasses.InitVar[str | None] 

43 

44 selection: Selection = dataclasses.field(init=False, default=Selection.edges) 

45 length: int = dataclasses.field(init=False, default=4) 

46 nested: str = dataclasses.field(init=False, default='') 

47 

48 # Track whether the user specified a value or whether the default is used. 

49 has_selection: bool = dataclasses.field(init=False, default=False) 

50 has_length: bool = dataclasses.field(init=False, default=False) 

51 has_nested: bool = dataclasses.field(init=False, default=False) 

52 

53 def __post_init__( 

54 self, _selection: str | None, _length: str | None, _nested: str | None 

55 ) -> None: 

56 if _selection is not None: 

57 object.__setattr__(self, 'selection', Selection(_selection)) 

58 object.__setattr__(self, 'has_selection', True) 

59 if _length is not None: 

60 object.__setattr__(self, 'length', int(_length)) 

61 object.__setattr__(self, 'has_length', True) 

62 if _nested is not None: 

63 object.__setattr__(self, 'nested', _nested) 

64 object.__setattr__(self, 'has_nested', True) 

65 

66 def __str__(self) -> str: 

67 sel = str(self.selection) if self.has_selection else '' 

68 length = f'#{self.length}' if self.has_length else '' 

69 typ = str(self.format_type) if self.format_type != FormatType.default else '' 

70 nested = f':{self.nested}' if self.has_nested else '' 

71 return f'{sel}{length}{typ}{nested}' 

72 

73 

74_FORMAT_PATTERN = re.compile( 

75 '^(?P<selection>[><^])?' 

76 r'(?:#(?P<length>\d+))?' 

77 '(?P<type>[c])?' 

78 '(?::(?P<nested>.*))?$' 

79) 

80 

81 

82def parse(raw_spec: str, cls: type) -> FormatSpec: 

83 match = _FORMAT_PATTERN.match(raw_spec) 

84 if match is None: 

85 raise ValueError(f"Invalid format spec '{raw_spec}' for type '{cls}'") 

86 

87 return FormatSpec( 

88 format_type=FormatType(match['type']), 

89 _selection=match['selection'], 

90 _length=match['length'], 

91 _nested=match['nested'], 

92 )