Coverage for .nox/test-3-9/lib/python3.9/site-packages/nskit/vcs/namespace_validator.py: 98%
61 statements
« prev ^ index » next coverage.py v7.4.2, created at 2024-02-25 17:38 +0000
« prev ^ index » next coverage.py v7.4.2, created at 2024-02-25 17:38 +0000
1"""Namespace Validator for validating if a name is in a namespace."""
2from enum import Enum
3import re
4import sys
5from typing import Dict, List, Optional, Union
7from pydantic import field_validator, ValidationInfo
9from nskit.common.configuration import BaseConfiguration
11if sys.version_info.major <= 3 and sys.version_info.minor <= 11:
12 from typing_extensions import TypeAliasType
13else:
14 from typing import TypeAliasType
16REPO_SEPARATOR = '-'
17_DELIMITERS = ['.', ',', '-']
19NamespaceOptionsType = TypeAliasType('NamespaceOptionsType', List[Union[str, Dict[str, 'NamespaceOptionsType']]])
22class ValidationEnum(Enum):
23 """Enum for validation level."""
24 strict = '2'
25 warn = '1'
26 none = '0'
29class NamespaceValidator(BaseConfiguration):
30 """Namespace Validator object."""
31 options: Optional[NamespaceOptionsType]
32 repo_separator: str = REPO_SEPARATOR
33 delimiters: List[str] = _DELIMITERS
35 __delimiters_regexp = None
36 # Validate delimiters to add repo_separator
38 @field_validator('delimiters', mode='after')
39 @classmethod
40 def _validate_repo_separator_in_delimiters(cls, v: List[str], info: ValidationInfo):
41 if info.data['repo_separator'] not in v:
42 v.append(info.data['repo_separator'])
43 return v
45 @property
46 def _delimiters_regexp(self):
47 if self.__delimiters_regexp is None:
48 self.__delimiters_regexp = '|'.join(map(re.escape, self.delimiters))
49 return self.__delimiters_regexp
51 def to_parts(self, name: str):
52 """Break the name into the namespace parts."""
53 if self.options:
54 return re.split(self._delimiters_regexp, name)
55 return [name]
57 def to_repo_name(self, name: str):
58 """Convert the name to the appropriate name with a given repo separator."""
59 return self.repo_separator.join(self.to_parts(name))
61 def validate_name(self, proposed_name: str):
62 """Validate a proposed name."""
63 name_parts = self.to_parts(proposed_name)
64 if self.options:
65 result, message = self._validate_level(name_parts, self.options)
66 message = message.format(key='<root>')
67 else:
68 result = True
69 message = 'no constraints set'
70 return result, message
72 def _validate_level(
73 self,
74 name_parts: List[str],
75 partial_namespace: List[Union[str, Dict]]):
76 not_matched = []
77 for key in partial_namespace:
78 # If it is a dict, then there are mappings of <section>: [<subsection 1>, <subsection 2>]
79 if isinstance(key, dict):
80 for sub_key, new_partial_namespace in key.items():
81 if sub_key == name_parts[0]:
82 # This maps to a section with subsections, so we need to validate those
83 result, message = self._validate_level(name_parts[1:], new_partial_namespace)
84 if not result:
85 message = message.format(key=sub_key)
86 return result, message
87 not_matched.append(sub_key)
88 # Otherwise it is a string
89 elif key == name_parts[0]:
90 return True, 'ok'
91 else:
92 not_matched.append(key)
93 return False, f'Does not match valid names for {{key}}: {", ".join(not_matched)}, with delimiters: {self.delimiters}'