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

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 

6 

7from pydantic import field_validator, ValidationInfo 

8 

9from nskit.common.configuration import BaseConfiguration 

10 

11if sys.version_info.major <= 3 and sys.version_info.minor <= 11: 

12 from typing_extensions import TypeAliasType 

13else: 

14 from typing import TypeAliasType 

15 

16REPO_SEPARATOR = '-' 

17_DELIMITERS = ['.', ',', '-'] 

18 

19NamespaceOptionsType = TypeAliasType('NamespaceOptionsType', List[Union[str, Dict[str, 'NamespaceOptionsType']]]) 

20 

21 

22class ValidationEnum(Enum): 

23 """Enum for validation level.""" 

24 strict = '2' 

25 warn = '1' 

26 none = '0' 

27 

28 

29class NamespaceValidator(BaseConfiguration): 

30 """Namespace Validator object.""" 

31 options: Optional[NamespaceOptionsType] 

32 repo_separator: str = REPO_SEPARATOR 

33 delimiters: List[str] = _DELIMITERS 

34 

35 __delimiters_regexp = None 

36 # Validate delimiters to add repo_separator 

37 

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 

44 

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 

50 

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] 

56 

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)) 

60 

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 

71 

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}'