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
« 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
5import dataclasses
6import enum
7import re
10class FormatType(enum.Enum):
11 default = None
12 compact = 'c'
14 def __str__(self) -> str:
15 match self:
16 case FormatType.default:
17 return ''
18 case FormatType.compact:
19 return 'c'
22class Selection(enum.Enum):
23 edges = '^'
24 begin = '<'
25 end = '>'
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 '>'
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]
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='')
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)
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)
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}'
74_FORMAT_PATTERN = re.compile(
75 '^(?P<selection>[><^])?'
76 r'(?:#(?P<length>\d+))?'
77 '(?P<type>[c])?'
78 '(?::(?P<nested>.*))?$'
79)
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}'")
87 return FormatSpec(
88 format_type=FormatType(match['type']),
89 _selection=match['selection'],
90 _length=match['length'],
91 _nested=match['nested'],
92 )