File: containers.py

package info (click to toggle)
python-noseofyeti 2.4.9-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 360 kB
  • sloc: python: 2,581; sh: 31; makefile: 12
file content (215 lines) | stat: -rw-r--r-- 6,447 bytes parent folder | download
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
import re
from tokenize import COMMENT, NAME

regexes = {
    "joins": re.compile(r"[- /]"),
    "repeated_underscore": re.compile(r"_{2,}"),
    "invalid_variable_name_start": re.compile(r"^[^a-zA-Z_]"),
    "invalid_variable_name_characters": re.compile(r"[^0-9a-zA-Z_]"),
}


def acceptable(name, capitalize=False):
    """Convert a string into something that can be used as a valid python variable name"""
    # Convert space and dashes into underscores
    name = regexes["joins"].sub("_", name)

    # Remove invalid characters
    name = regexes["invalid_variable_name_characters"].sub("", name)

    # Remove leading characters until we find a letter or underscore
    name = regexes["invalid_variable_name_start"].sub("", name)

    # Clean up irregularities in underscores.
    name = regexes["repeated_underscore"].sub("_", name.strip("_"))

    if capitalize:
        # We don't use python's built in capitalize method here because it
        # turns all upper chars into lower chars if not at the start of
        # the string and we only want to change the first character.
        name_parts = []
        for word in name.split("_"):
            name_parts.append(word[0].upper())
            if len(word) > 1:
                name_parts.append(word[1:])
        name = "".join(name_parts)

    return name


class TokenDetails:
    """Container for current token"""

    def __init__(self, tokenum=None, value=None, srow=0, scol=0):
        self.set(tokenum, value, srow, scol)

    def set(self, tokenum, value, srow, scol):
        self.srow = srow
        self.scol = scol
        self.value = value
        self.tokenum = tokenum

    def transfer(self, details):
        details.set(*self.values())

    def values(self):
        return self.tokenum, self.value, self.srow, self.scol


class Single:
    """Container for a single block (i.e. it or ignore block)"""

    def __init__(self, srow, scol, group, typ=None, indent=0):
        self.typ = typ
        self.group = group
        self.indent = indent

        self.args = []
        self.comments = []
        self.return_type = None

        self._name = None
        self.english = None
        self.skipped = False

        self.started_arg = False
        self.starting_arg = False

        if not self.group.root:
            self.args.append((NAME, "self", srow, scol))
        else:
            self.args.append((None, None, srow, scol))

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = acceptable(value)
        self.english = value

    @property
    def python_name(self):
        if self.typ == "it":
            return f"test_{self.name}"
        else:
            return f"ignore__{self.name}"

    @property
    def identifier(self):
        if self.group.root:
            return self.python_name
        else:
            return f"{self.group.kls_name}.{self.python_name}"

    def add_to_arg(self, tokenum, value, srow, scol):
        if value == "," and len(self.args) == 1 and self.args[0][0] is None:
            return

        if value == "->" and self.return_type is None:
            self.return_type = [(tokenum, value, srow, scol)]
            return

        if self.comments or tokenum is COMMENT:
            self.comments.append((tokenum, value))
        elif self.return_type is not None:
            self.return_type.append((tokenum, value, srow, scol))
        else:
            self.args.append((tokenum, value, srow, scol))


class Group:
    """Container for describe blocks"""

    def __init__(self, name=None, root=False, parent=None, level=0, typ=None):
        self.kls = None
        self.typ = typ
        self.name = name
        self.root = root
        self.empty = True
        self.level = level
        self.parent = parent
        self.singles = []
        self.has_after_each = False
        self.async_after_each = False
        self.has_before_each = False
        self.async_before_each = False

        # Default whether this group is starting anything
        self.starting_single = False
        if root:
            # Root technically isn't a group, so it doesn't have a signature to start
            self.starting_group = False
        else:
            # Group is created before we have all information
            # Hence it's signature is being created
            self.starting_group = True

    def __repr__(self):
        if self.root:
            return "<Root Group>"
        else:
            return f"<Group {self.kls_name}:{self.super_kls}({self.parent})>"

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value
        self.english = value
        if value:
            self._name = acceptable(value, True)

    @property
    def starting_signature(self):
        """Determine if this group is starting itself or anything belonging to it"""
        return self.starting_group or self.starting_single

    @property
    def kls_name(self):
        """Determine python name for group"""
        # Determine kls for group
        if not self.parent or not self.parent.name:
            return f"Test{self.name}"
        else:
            use = self.parent.kls_name
            if use.startswith("Test"):
                use = use[4:]

            return f"Test{use}_{self.name}"

    @property
    def super_kls(self):
        """
        Determine what kls this group inherits from
        If default kls should be used, then None is returned
        """
        if not self.kls and self.parent and self.parent.name:
            return self.parent.kls_name
        return self.kls

    def start_group(self, scol, typ):
        """Start a new group"""
        return Group(parent=self, level=scol, typ=typ)

    def start_single(self, typ, srow, scol):
        """Start a new single"""
        self.starting_single = True
        single = self.single = Single(srow, scol, self, typ=typ, indent=(scol - self.level))
        self.singles.append(single)
        return single

    def finish_signature(self):
        """Tell group it isn't starting anything anymore"""
        self.starting_group = False
        self.starting_single = False

    def modify_kls(self, name):
        """Add a part to what will end up being the kls' superclass"""
        if self.kls is None:
            self.kls = name
        else:
            self.kls += name