forked from youtype/mypy_boto3_builder
/
import_string.py
137 lines (104 loc) · 3.15 KB
/
import_string.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
"""
Wrapper for Python import strings.
"""
import functools
from typing import TypeVar
_R = TypeVar("_R", bound="ImportString")
@functools.total_ordering
class ImportString:
"""
Wrapper for Python import strings.
Arguments:
master -- Master module name
parts -- Other import parts
Examples::
import_string = ImportString("my", "name")
str(import_string)
'my.name'
import_string.render()
'my.name'
import_string.parts.append('test')
import_string.render()
'my.name.test'
"""
def __init__(self, master_name: str, *parts: str) -> None:
self.parts: list[str] = []
all_parts = [master_name, *parts]
for part in all_parts:
if not part or "." in part:
raise ValueError(f"Invalid ImportString parts: {parts} - {part}")
self.parts.append(part)
@classmethod
def from_str(cls: type[_R], import_string: str) -> _R:
"""
Create from string.
"""
return cls(*import_string.split("."))
@classmethod
def empty(cls: type[_R]) -> _R:
"""
Create an empty ImportString.
"""
result = cls("fake")
result.parts.clear()
return result
@classmethod
def parent(cls: type[_R]) -> _R:
"""
Get parent ImportString.
"""
result = cls.empty()
result.parts.append("")
return result
def __bool__(self) -> bool:
return bool(self.parts)
def __str__(self) -> str:
return self.render()
def __hash__(self) -> int:
return hash(str(self))
def __eq__(self, other: object) -> bool:
return str(self) == str(other)
def __gt__(self, other: object) -> bool:
return str(self) > str(other)
def __add__(self: _R, other: _R) -> _R:
result = self.__class__.empty()
result.parts = self.parts + other.parts
return result
def startswith(self: _R, other: _R) -> bool:
"""
Check if import string starts with `other`.
Examples::
ImportString('my', 'name').startswith(ImportString('my'))
True
ImportString('my_module', 'name').startswith(ImportString('my'))
False
ImportString('my', 'name').startswith(ImportString('my, 'name'))
True
ImportString('my', 'name').startswith(ImportString.empty())
True
Arguments:
other -- Other import string.
"""
for part_index, part in enumerate(other.parts):
try:
self_part = self.parts[part_index]
except IndexError:
return False
if self_part != part:
return False
return True
def render(self) -> str:
"""
Render to string.
Returns:
Ready to use import string.
"""
return ".".join(self.parts)
@property
def master_name(self) -> str:
"""
Get first import string part or `builtins`.
"""
if not self.parts:
return "builtins"
return self.parts[0]