tyrannosaurus.enums.Toml.__contains__()   A
last analyzed

Complexity

Conditions 5

Size

Total Lines 11
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 11
nop 2
dl 0
loc 11
rs 9.3333
c 0
b 0
f 0
1
"""
2
Supporting enums.
3
4
Original source: https://github.com/dmyersturnbull/tyrannosaurus
5
Copyright 2020–2021 Douglas Myers-Turnbull
6
Licensed under the Apache License, Version 2.0 (the "License");
7
you may not use this file except in compliance with the License.
8
You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0
9
"""
10
11
from __future__ import annotations
12
13
import enum
14
from pathlib import Path, PurePath
15
from typing import Any, Mapping, Optional, Union
16
17
import requests
18
import tomlkit
19
20
21
class DevStatus(str, enum.Enum):
22
    planning = "planning"
23
    pre_alpha = "pre-alpha"
24
    alpha = "alpha"
25
    beta = "beta"
26
    production = "production"
27
    mature = "mature"
28
    inactive = "inactive"
29
30
    @property
31
    def true_name(self) -> str:
32
        """
33
        A nice name, like "pre-alpha".
34
        """
35
        return self.name.replace("_", "-")
36
37
    @property
38
    def true_value(self) -> int:
39
        """
40
        1 for planning, 2 for pre-alpha, ... .
41
        Same as for PyPi classifiers.
42
        """
43
        return {
44
            "planning": 1,
45
            "pre_alpha": 2,
46
            "alpha": 3,
47
            "beta": 4,
48
            "production": 5,
49
            "mature": 6,
50
            "inactive": 7,
51
        }[self.name]
52
53
    @property
54
    def description(self) -> str:
55
        """
56
        A fragment like "a production state" or "an alpha state".
57
        """
58
        name = self.true_name
59
        article = "an" if name[0] in ["a", "e", "i", "o", "u", "h"] else "a"
60
        return f"{article} {name} state"
61
62
    @property
63
    def pypi(self) -> str:
64
        """
65
        A string that is recognized as a PyPi classifier.
66
        """
67
        name = self.name.replace("_", " ").title().replace(" ", "-")
68
        return f"{self.true_value} - {name}"
69
70
    @property
71
    def dunder(self) -> str:
72
        """
73
        A string that works for ``__status__``.
74
        """
75
        return "Production" if self.true_value >= 5 else "Development"
76
77
    @classmethod
78
    def guess_from_version(cls, version: str) -> DevStatus:
79
        """
80
        Makes a really rough guess for the status from a semantic version string.
81
82
        Behavior::
83
84
            - Guesses planning for 0.0.x (these are not semantic versions).
85
            - Guesses alpha for pre-1.0
86
            - Guesses production for 1.0+
87
88
        Arguments:
89
            version: A semantic version like "0.1.x"; can also start with 0.0.
90
        """
91
        if version.startswith("v"):
92
            version = version[1:]
93
        if version.startswith("0.0."):
94
            return DevStatus.planning
95
        elif version.startswith("1."):
96
            return DevStatus.production
97
        return DevStatus.alpha
98
99
100
class Toml:
101
    """A thin wrapper around toml to make getting values easier."""
102
103
    @classmethod
104
    def read(cls, path: Union[PurePath, str]) -> Toml:
105
        return Toml(tomlkit.loads(Path(path).read_text(encoding="utf8")))
106
107
    def __init__(self, x: Mapping[str, Any]) -> None:
108
        self.x = x
109
110
    def __getitem__(self, items: str):
111
        if not isinstance(items, str):
112
            raise AssertionError(f"Failed with '{items}'")
113
        if "." not in items:
114
            return self.x[items]
115
        at = self.x
116
        for item in items.split("."):
117
            at = at[item]
118
        if isinstance(at, dict):
119
            return Toml(at)
120
        return at
121
122
    def __contains__(self, items):
123
        if not isinstance(items, str):
124
            raise AssertionError(f"Failed with '{items}'")
125
        if "." not in items:
126
            return items in self.x
127
        at = self.x
128
        for item in items.split("."):
129
            if item not in at:
130
                return False
131
            at = at[item]
132
        return True
133
134
    def get(self, items: str, default: Any = None) -> Optional[Any]:
135
        try:
136
            return self[items]
137
        except KeyError:
138
            return default
139
140
    def items(self):
141
        return self.x.items()
142
143
    def keys(self):
144
        return self.x.keys()
145
146
    def __str__(self):
147
        return f"{self.__class__.__name__} ({repr(self.x)})"
148
149
    def __repr__(self):
150
        return f"{self.__class__.__name__} ({repr(self.x)})"
151
152
    def __eq__(self, other):
153
        return isinstance(other, Toml) and self.x == other.x
154
155
156
class TomlBuilder:
157
    def __init__(self):
158
        self._dict = {}
159
160
    def add(self, key: str, value: Any):
161
        at = self._dict
162
        if "." in key:
163
            for k in key.split(".")[:-1]:
164
                if k not in at:
165
                    at[k] = {}
166
                at = at[k]
167
        at[key.split(".")[-1]] = value
168
        return self
169
170
    def build(self) -> Toml:
171
        return Toml(self._dict)
172
173
174
class License(str, enum.Enum):
175
    agpl3 = "agpl3"
176
    apache2 = "apache2"
177
    cc0 = "cc0"
178
    ccby = "ccby"
179
    ccbync = "ccbync"
180
    gpl3 = "gpl3"
181
    lgpl3 = "lgpl3"
182
    mit = "mit"
183
    mpl2 = "mpl2"
184
185
    @classmethod
186
    def of(cls, value: Union[str, License]) -> License:
187
        if isinstance(value, License):
188
            return value
189
        for v in list(License):
190
            if v.spdx == value:
191
                return v
192
            if v.name == value:
193
                return v
194
            if v.full_name == value:
195
                return v
196
        raise LookupError(f"Could not find {value}")
197
198
    @property
199
    def url(self) -> str:
200
        return f"https://spdx.org/licenses/{self.spdx}.html"
201
202
    @property
203
    def spdx(self) -> str:
204
        return {
205
            "agpl3": "AGPL-3.0-or-later",
206
            "apache2": "Apache-2.0",
207
            "cc0": "CC0-1.0",
208
            "ccby": "CC-BY-4.0",
209
            "ccbync": "CC-BY-NC-4.0",
210
            "gpl2": "GPL-2.0-or-later",
211
            "gpl3": "GPL-3.0-or-later",
212
            "lgpl3": "LGPL-3.0-or-later",
213
            "mit": "MIT",
214
            "mpl2": "MPL-2.0",
215
        }[self.name]
216
217
    @property
218
    def full_name(self) -> str:
219
        return {
220
            "apache2": "Apache License 2.0",
221
            "cc0": "CC0 1.0",
222
            "ccby": "CC BY 4.0",
223
            "ccbync": "CC BY NC 4.0",
224
            "gpl2": "GNU General Public License 2.0",
225
            "gpl3": "GNU General Public License 3.0",
226
            "lgpl3": "GNU Lesser General Public License 3.0",
227
            "mit": "MIT License",
228
            "mpl2": "Mozilla Public License 2.0",
229
            "agpl3": "GNU Affero General Public License 3.0",
230
        }[self.name]
231
232
    @property
233
    def family(self) -> str:
234
        return {
235
            "apache2": "Apache",
236
            "cc0": "CC",
237
            "ccby": "CC",
238
            "ccbync": "CC",
239
            "gpl2": "GPL",
240
            "gpl3": "GPL",
241
            "lgpl3": "GPL",
242
            "mit": "MIT",
243
            "mpl2": "Mozilla",
244
            "agpl3": "GPL",
245
        }[self.name]
246
247
    def download_license(self) -> str:
248
        return self._read_url(self.license_url)
249
250
    def download_header(self) -> str:
251
        if self is License.mit:
252
            return ""
253
        return self._read_url(self.header_url)
254
255
    def _read_url(self, url: str) -> str:
256
        response = requests.get(url)
257
        if response.status_code > 400:
258
            raise ValueError(f"Status code {response.status_code} for url {url}")
259
        return response.text
260
261
    @property
262
    def license_url(self) -> str:
263
        return self.header_url.replace("-header", "")
264
265
    @property
266
    def header_url(self) -> str:
267
        name = {
268
            "apache2": "apache",
269
            "ccby": "cc_by",
270
            "ccbync": "cc_by_nc",
271
            "gpl3": "gpl3",
272
            "lgpl3": "lgpl",
273
            "mit": "mit",
274
            "mpl2": "mpl",
275
            "cc0": "cc0",
276
            "agpl3": "agpl3",
277
        }[self.name]
278
        return f"https://raw.githubusercontent.com/licenses/license-templates/master/templates/{name}-header.txt"
279
280
281
__all__ = ["DevStatus", "License", "Toml", "TomlBuilder"]
282