mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-24 07:17:17 +00:00
refactor: replace enum with intenum and strenum
This commit is contained in:
parent
b116e6fa07
commit
f51b8e2358
@ -26,7 +26,7 @@ In order to resolve all difficult cases the `autopep8` is used. You can perform
|
|||||||
Again, the most checks can be performed by `make check` command, though some additional guidelines must be applied:
|
Again, the most checks can be performed by `make check` command, though some additional guidelines must be applied:
|
||||||
|
|
||||||
* Every class, every function (including private and protected), every attribute must be documented. The project follows [Google style documentation](https://google.github.io/styleguide/pyguide.html). The only exception is local functions.
|
* Every class, every function (including private and protected), every attribute must be documented. The project follows [Google style documentation](https://google.github.io/styleguide/pyguide.html). The only exception is local functions.
|
||||||
* Correct way to document function, if section is empty, e.g. no notes or there are no args, it should be omitted:
|
* Correct way to document function (if a section is empty, e.g. no notes or there are no args, it should be omitted) is the following:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def foo(argument: str, *, flag: bool = False) -> int:
|
def foo(argument: str, *, flag: bool = False) -> int:
|
||||||
@ -64,7 +64,7 @@ Again, the most checks can be performed by `make check` command, though some add
|
|||||||
```python
|
```python
|
||||||
class Clazz(BaseClazz):
|
class Clazz(BaseClazz):
|
||||||
"""
|
"""
|
||||||
brand-new implementation of ``BaseClazz``
|
brand-new implementation of :class:`BaseClazz`
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
CLAZZ_ATTRIBUTE(int): (class attribute) a brand-new class attribute
|
CLAZZ_ATTRIBUTE(int): (class attribute) a brand-new class attribute
|
||||||
@ -92,7 +92,7 @@ Again, the most checks can be performed by `make check` command, though some add
|
|||||||
|
|
||||||
* Type annotations are the must, even for local functions. For the function argument `self` (for instance methods) and `cls` (for class methods) should not be annotated.
|
* Type annotations are the must, even for local functions. For the function argument `self` (for instance methods) and `cls` (for class methods) should not be annotated.
|
||||||
* For collection types built-in classes must be used if possible (e.g. `dict` instead of `typing.Dict`, `tuple` instead of `typing.Tuple`). In case if built-in type is not available, but `collections.abc` provides interface, it must be used (e.g. `collections.abc.Awaitable` instead of `typing.Awaitable`, `collections.abc.Iterable` instead of `typing.Iterable`). For union classes, the bar operator (`|`) must be used (e.g. `float | int` instead of `typing.Union[float, int]`), which also includes `typinng.Optional` (e.g. `str | None` instead of `Optional[str]`).
|
* For collection types built-in classes must be used if possible (e.g. `dict` instead of `typing.Dict`, `tuple` instead of `typing.Tuple`). In case if built-in type is not available, but `collections.abc` provides interface, it must be used (e.g. `collections.abc.Awaitable` instead of `typing.Awaitable`, `collections.abc.Iterable` instead of `typing.Iterable`). For union classes, the bar operator (`|`) must be used (e.g. `float | int` instead of `typing.Union[float, int]`), which also includes `typinng.Optional` (e.g. `str | None` instead of `Optional[str]`).
|
||||||
* `classmethod` should always return `Self`. In case of mypy warning (e.g. if there is a branch in which function doesn't return the instance of `cls`) consider using `staticmethod` instead.
|
* `classmethod` should (almost) always return `Self`. In case of mypy warning (e.g. if there is a branch in which function doesn't return the instance of `cls`) consider using `staticmethod` instead.
|
||||||
* Recommended order of function definitions in class:
|
* Recommended order of function definitions in class:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@ -121,10 +121,12 @@ Again, the most checks can be performed by `make check` command, though some add
|
|||||||
def __hash__(self) -> int: ... # basically any magic (or look-alike) method
|
def __hash__(self) -> int: ... # basically any magic (or look-alike) method
|
||||||
```
|
```
|
||||||
|
|
||||||
Methods inside one group should be ordered alphabetically, the only exception is `__init__` method (`__post__init__` for dataclasses) which should be defined first. For test methods it is recommended to follow the order in which functions are defined.
|
Methods inside one group should be ordered alphabetically, the only exceptions are `__init__` (`__post_init__` for dataclasses) and `__new__` methods which should be defined first. For test methods it is recommended to follow the order in which functions are defined.
|
||||||
|
|
||||||
Though, we would like to highlight abstract methods (i.e. ones which raise `NotImplementedError`), we still keep in global order at the moment.
|
Though, we would like to highlight abstract methods (i.e. ones which raise `NotImplementedError`), we still keep in global order at the moment.
|
||||||
|
|
||||||
|
For the most cases there is custom `pylint` plugin, which performs checks automatically.
|
||||||
|
|
||||||
* Abstract methods must raise `NotImplementedError` instead of using `abc.abstractmethod`. The reason behind this restriction is the fact that we have class/static abstract methods for those we need to define their attribute first making the code harder to read.
|
* Abstract methods must raise `NotImplementedError` instead of using `abc.abstractmethod`. The reason behind this restriction is the fact that we have class/static abstract methods for those we need to define their attribute first making the code harder to read.
|
||||||
* For any path interactions `pathlib.Path` must be used.
|
* For any path interactions `pathlib.Path` must be used.
|
||||||
* Configuration interactions must go through `ahriman.core.configuration.Configuration` class instance.
|
* Configuration interactions must go through `ahriman.core.configuration.Configuration` class instance.
|
||||||
@ -167,7 +169,7 @@ Again, the most checks can be performed by `make check` command, though some add
|
|||||||
* No global variable is allowed outside of `ahriman` module. `ahriman.core.context` is also special case.
|
* No global variable is allowed outside of `ahriman` module. `ahriman.core.context` is also special case.
|
||||||
* Single quotes are not allowed. The reason behind this restriction is the fact that docstrings must be written by using double quotes only, and we would like to make style consistent.
|
* Single quotes are not allowed. The reason behind this restriction is the fact that docstrings must be written by using double quotes only, and we would like to make style consistent.
|
||||||
* If your class writes anything to log, the `ahriman.core.log.LazyLogging` trait must be used.
|
* If your class writes anything to log, the `ahriman.core.log.LazyLogging` trait must be used.
|
||||||
* Web API methods must be documented by using `aiohttp_apispec` library. Schema testing mostly should be implemented in related view class tests. Recommended example for documentation (excluding comments):
|
* Web API methods must be documented by using `aiohttp_apispec` library. The schema testing mostly should be implemented in related view class tests. Recommended example for documentation (excluding comments):
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import aiohttp_apispec
|
import aiohttp_apispec
|
||||||
@ -191,6 +193,7 @@ Again, the most checks can be performed by `make check` command, though some add
|
|||||||
class Foo(BaseView):
|
class Foo(BaseView):
|
||||||
|
|
||||||
POST_PERMISSION = ...
|
POST_PERMISSION = ...
|
||||||
|
ROUTES = ...
|
||||||
|
|
||||||
@aiohttp_apispec.docs(
|
@aiohttp_apispec.docs(
|
||||||
tags=["Tag"],
|
tags=["Tag"],
|
||||||
@ -216,6 +219,7 @@ Again, the most checks can be performed by `make check` command, though some add
|
|||||||
|
|
||||||
* It is allowed to change web API to add new fields or remove optional ones. However, in case of model changes, new API version must be introduced.
|
* It is allowed to change web API to add new fields or remove optional ones. However, in case of model changes, new API version must be introduced.
|
||||||
* On the other hand, it is allowed to change method signatures, however, it is recommended to add new parameters as optional if possible. Deprecated API can be dropped during major release.
|
* On the other hand, it is allowed to change method signatures, however, it is recommended to add new parameters as optional if possible. Deprecated API can be dropped during major release.
|
||||||
|
* Enumerations (`Enum` classes) are allowed and recommended. However, it is recommended to use `StrEnum` class if there are from/to string conversions and `IntEnum` otherwise.
|
||||||
|
|
||||||
### Other checks
|
### Other checks
|
||||||
|
|
||||||
|
@ -19,33 +19,33 @@
|
|||||||
#
|
#
|
||||||
from astroid import nodes
|
from astroid import nodes
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
from pylint.checkers import BaseRawFileChecker
|
from pylint.checkers import BaseRawFileChecker
|
||||||
from pylint.lint import PyLinter
|
from pylint.lint import PyLinter
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
class MethodTypeOrder(int, Enum):
|
class MethodTypeOrder(StrEnum):
|
||||||
"""
|
"""
|
||||||
method type enumeration
|
method type enumeration
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
New(MethodTypeOrder): (class attribute) constructor method
|
|
||||||
Init(MethodTypeOrder): (class attribute) initialization method
|
|
||||||
Property(MethodTypeOrder): (class attribute) property method
|
|
||||||
Class(MethodTypeOrder): (class attribute) class method
|
Class(MethodTypeOrder): (class attribute) class method
|
||||||
Static(MethodTypeOrder): (class attribute) static method
|
Init(MethodTypeOrder): (class attribute) initialization method
|
||||||
Normal(MethodTypeOrder): (class attribute) usual method
|
|
||||||
Magic(MethodTypeOrder): (class attribute) other magical methods
|
Magic(MethodTypeOrder): (class attribute) other magical methods
|
||||||
|
New(MethodTypeOrder): (class attribute) constructor method
|
||||||
|
Normal(MethodTypeOrder): (class attribute) usual method
|
||||||
|
Property(MethodTypeOrder): (class attribute) property method
|
||||||
|
Static(MethodTypeOrder): (class attribute) static method
|
||||||
"""
|
"""
|
||||||
|
|
||||||
New = 0
|
Class = "classmethod"
|
||||||
Init = 1
|
Init = "init"
|
||||||
Property = 2
|
Magic = "magic"
|
||||||
Class = 3
|
New = "new"
|
||||||
Static = 4
|
Normal = "regular"
|
||||||
Normal = 5
|
Property = "property"
|
||||||
Magic = 6
|
Static = "staticmethod"
|
||||||
|
|
||||||
|
|
||||||
class DefinitionOrder(BaseRawFileChecker):
|
class DefinitionOrder(BaseRawFileChecker):
|
||||||
@ -65,45 +65,31 @@ class DefinitionOrder(BaseRawFileChecker):
|
|||||||
|
|
||||||
name = "method-ordering"
|
name = "method-ordering"
|
||||||
msgs = {
|
msgs = {
|
||||||
"W0001": (
|
"W6001": (
|
||||||
"Invalid method order %s, expected %s",
|
"Invalid method order %s, expected %s",
|
||||||
"methods-out-of-order",
|
"methods-out-of-order",
|
||||||
"Methods are defined out of recommended order.",
|
"Methods are defined out of recommended order.",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
options = ()
|
options = (
|
||||||
|
(
|
||||||
@staticmethod
|
"method-type-order",
|
||||||
def comparator(function: nodes.FunctionDef) -> tuple[int, str]:
|
{
|
||||||
"""
|
"default": [
|
||||||
compare key for function node
|
"new",
|
||||||
|
"init",
|
||||||
Args:
|
"property",
|
||||||
function(nodes.FunctionDef): function definition
|
"classmethod",
|
||||||
|
"staticmethod",
|
||||||
Returns:
|
"regular",
|
||||||
tuple[int, str]: comparison key
|
"magic",
|
||||||
"""
|
],
|
||||||
# init methods
|
"type": "csv",
|
||||||
if function.name in ("__new__",):
|
"metavar": "<comma-separated types>",
|
||||||
return MethodTypeOrder.New, function.name
|
"help": "Method types order to check.",
|
||||||
if function.name in ("__init__", "__post_init__"):
|
},
|
||||||
return MethodTypeOrder.Init, function.name
|
),
|
||||||
|
)
|
||||||
# decorated methods
|
|
||||||
decorators = []
|
|
||||||
if function.decorators is not None:
|
|
||||||
decorators = [getattr(decorator, "name", None) for decorator in function.decorators.get_children()]
|
|
||||||
for decorator in decorators:
|
|
||||||
if decorator in DefinitionOrder.DECORATED_METHODS_ORDER:
|
|
||||||
return DefinitionOrder.DECORATED_METHODS_ORDER[decorator], function.name
|
|
||||||
|
|
||||||
# magic methods
|
|
||||||
if function.name.startswith("__") and function.name.endswith("__"):
|
|
||||||
return MethodTypeOrder.Magic, function.name
|
|
||||||
|
|
||||||
# normal method
|
|
||||||
return MethodTypeOrder.Normal, function.name
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def methods(source: Iterable[Any], start_lineno: int = 0) -> list[nodes.FunctionDef]:
|
def methods(source: Iterable[Any], start_lineno: int = 0) -> list[nodes.FunctionDef]:
|
||||||
@ -124,6 +110,38 @@ class DefinitionOrder(BaseRawFileChecker):
|
|||||||
|
|
||||||
return list(filter(is_defined_function, source))
|
return list(filter(is_defined_function, source))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_type(function: nodes.FunctionDef) -> MethodTypeOrder:
|
||||||
|
"""
|
||||||
|
resolve type of the function
|
||||||
|
|
||||||
|
Args:
|
||||||
|
function(nodes.FunctionDef): function definition
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
MethodTypeOrder: resolved function type
|
||||||
|
"""
|
||||||
|
# init methods
|
||||||
|
if function.name in ("__new__",):
|
||||||
|
return MethodTypeOrder.New
|
||||||
|
if function.name in ("__init__", "__post_init__"):
|
||||||
|
return MethodTypeOrder.Init
|
||||||
|
|
||||||
|
# decorated methods
|
||||||
|
decorators = []
|
||||||
|
if function.decorators is not None:
|
||||||
|
decorators = [getattr(decorator, "name", None) for decorator in function.decorators.get_children()]
|
||||||
|
for decorator in decorators:
|
||||||
|
if decorator in DefinitionOrder.DECORATED_METHODS_ORDER:
|
||||||
|
return DefinitionOrder.DECORATED_METHODS_ORDER[decorator]
|
||||||
|
|
||||||
|
# magic methods
|
||||||
|
if function.name.startswith("__") and function.name.endswith("__"):
|
||||||
|
return MethodTypeOrder.Magic
|
||||||
|
|
||||||
|
# normal method
|
||||||
|
return MethodTypeOrder.Normal
|
||||||
|
|
||||||
def check_class(self, clazz: nodes.ClassDef) -> None:
|
def check_class(self, clazz: nodes.ClassDef) -> None:
|
||||||
"""
|
"""
|
||||||
check class functions ordering
|
check class functions ordering
|
||||||
@ -131,7 +149,7 @@ class DefinitionOrder(BaseRawFileChecker):
|
|||||||
Args:
|
Args:
|
||||||
clazz(nodes.ClassDef): class definition
|
clazz(nodes.ClassDef): class definition
|
||||||
"""
|
"""
|
||||||
methods = DefinitionOrder.methods(clazz.values(), clazz.lineno)
|
methods = self.methods(clazz.values(), clazz.lineno)
|
||||||
self.check_functions(methods)
|
self.check_functions(methods)
|
||||||
|
|
||||||
def check_functions(self, functions: list[nodes.FunctionDef]) -> None:
|
def check_functions(self, functions: list[nodes.FunctionDef]) -> None:
|
||||||
@ -141,12 +159,30 @@ class DefinitionOrder(BaseRawFileChecker):
|
|||||||
Args:
|
Args:
|
||||||
functions(list[nodes.FunctionDef]): list of functions in their defined order
|
functions(list[nodes.FunctionDef]): list of functions in their defined order
|
||||||
"""
|
"""
|
||||||
for real, expected in zip(functions, sorted(functions, key=DefinitionOrder.comparator)):
|
for real, expected in zip(functions, sorted(functions, key=self.comparator)):
|
||||||
if real == expected:
|
if real == expected:
|
||||||
continue
|
continue
|
||||||
self.add_message("methods-out-of-order", line=real.lineno, args=(real.name, expected.name))
|
self.add_message("methods-out-of-order", line=real.lineno, args=(real.name, expected.name))
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def comparator(self, function: nodes.FunctionDef) -> tuple[int, str]:
|
||||||
|
"""
|
||||||
|
compare key for sorting function
|
||||||
|
|
||||||
|
Args:
|
||||||
|
function(nodes.FunctionDef): function definition
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple[int, str]: comparison key for the specified function definition
|
||||||
|
"""
|
||||||
|
function_type = self.resolve_type(function)
|
||||||
|
try:
|
||||||
|
function_type_index = self.linter.config.method_type_order.index(function_type)
|
||||||
|
except ValueError:
|
||||||
|
function_type_index = 10 # not in the list
|
||||||
|
|
||||||
|
return function_type_index, function.name
|
||||||
|
|
||||||
def process_module(self, node: nodes.Module) -> None:
|
def process_module(self, node: nodes.Module) -> None:
|
||||||
"""
|
"""
|
||||||
process module
|
process module
|
||||||
@ -155,7 +191,7 @@ class DefinitionOrder(BaseRawFileChecker):
|
|||||||
node(nodes.Module): module node to check
|
node(nodes.Module): module node to check
|
||||||
"""
|
"""
|
||||||
# check global methods
|
# check global methods
|
||||||
self.check_functions(DefinitionOrder.methods(node.values()))
|
self.check_functions(self.methods(node.values()))
|
||||||
# check class definitions
|
# check class definitions
|
||||||
for clazz in filter(lambda method: isinstance(method, nodes.ClassDef), node.values()):
|
for clazz in filter(lambda method: isinstance(method, nodes.ClassDef), node.values()):
|
||||||
self.check_class(clazz)
|
self.check_class(clazz)
|
||||||
|
@ -17,10 +17,10 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class Action(str, Enum):
|
class Action(StrEnum):
|
||||||
"""
|
"""
|
||||||
base action enumeration
|
base action enumeration
|
||||||
|
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
#
|
#
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class AuthSettings(str, Enum):
|
class AuthSettings(StrEnum):
|
||||||
"""
|
"""
|
||||||
web authorization type
|
web authorization type
|
||||||
|
|
||||||
|
@ -18,13 +18,13 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from dataclasses import dataclass, field, fields
|
from dataclasses import dataclass, field, fields
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
from typing import Any, Self
|
from typing import Any, Self
|
||||||
|
|
||||||
from ahriman.core.util import filter_json, pretty_datetime, utcnow
|
from ahriman.core.util import filter_json, pretty_datetime, utcnow
|
||||||
|
|
||||||
|
|
||||||
class BuildStatusEnum(str, Enum):
|
class BuildStatusEnum(StrEnum):
|
||||||
"""
|
"""
|
||||||
build status enumeration
|
build status enumeration
|
||||||
|
|
||||||
|
@ -17,10 +17,10 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class LogHandler(str, Enum):
|
class LogHandler(StrEnum):
|
||||||
"""
|
"""
|
||||||
log handler as described by default configuration
|
log handler as described by default configuration
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#
|
#
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ from ahriman.core.util import package_like
|
|||||||
from ahriman.models.repository_paths import RepositoryPaths
|
from ahriman.models.repository_paths import RepositoryPaths
|
||||||
|
|
||||||
|
|
||||||
class PackageSource(str, Enum):
|
class PackageSource(StrEnum):
|
||||||
"""
|
"""
|
||||||
package source for addition enumeration
|
package source for addition enumeration
|
||||||
|
|
||||||
|
@ -17,10 +17,10 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from enum import Enum
|
from enum import IntEnum
|
||||||
|
|
||||||
|
|
||||||
class PacmanSynchronization(int, Enum):
|
class PacmanSynchronization(IntEnum):
|
||||||
"""
|
"""
|
||||||
pacman database synchronization flag
|
pacman database synchronization flag
|
||||||
|
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
#
|
#
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class ReportSettings(str, Enum):
|
class ReportSettings(StrEnum):
|
||||||
"""
|
"""
|
||||||
report targets enumeration
|
report targets enumeration
|
||||||
|
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
#
|
#
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class SignSettings(str, Enum):
|
class SignSettings(StrEnum):
|
||||||
"""
|
"""
|
||||||
sign targets enumeration
|
sign targets enumeration
|
||||||
|
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
#
|
#
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class SmtpSSLSettings(str, Enum):
|
class SmtpSSLSettings(StrEnum):
|
||||||
"""
|
"""
|
||||||
SMTP SSL mode enumeration
|
SMTP SSL mode enumeration
|
||||||
|
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
#
|
#
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class UploadSettings(str, Enum):
|
class UploadSettings(StrEnum):
|
||||||
"""
|
"""
|
||||||
remote synchronization targets enumeration
|
remote synchronization targets enumeration
|
||||||
|
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
#
|
#
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class UserAccess(str, Enum):
|
class UserAccess(StrEnum):
|
||||||
"""
|
"""
|
||||||
web user access enumeration
|
web user access enumeration
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user