apytizer 0.0.1a0__py3-none-any.whl → 0.0.1b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- apytizer/__init__.py +2 -12
- apytizer/adapters/__init__.py +2 -3
- apytizer/adapters/transport_adapter.py +91 -0
- apytizer/apis/__init__.py +6 -0
- apytizer/apis/abstract_api.py +36 -0
- apytizer/apis/web_api.py +461 -0
- apytizer/connections/__init__.py +6 -0
- apytizer/connections/abstract_connection.py +28 -0
- apytizer/connections/http_connection.py +431 -0
- apytizer/decorators/__init__.py +5 -5
- apytizer/decorators/caching.py +60 -9
- apytizer/decorators/chunking.py +105 -0
- apytizer/decorators/connection.py +55 -20
- apytizer/decorators/json_response.py +93 -0
- apytizer/decorators/pagination.py +50 -32
- apytizer/endpoints/__init__.py +6 -0
- apytizer/endpoints/abstract_endpoint.py +38 -0
- apytizer/endpoints/web_endpoint.py +519 -0
- apytizer/engines/__init__.py +6 -0
- apytizer/engines/abstract_engine.py +45 -0
- apytizer/engines/http_engine.py +129 -0
- apytizer/errors.py +34 -0
- apytizer/factories/__init__.py +5 -0
- apytizer/factories/abstract_factory.py +17 -0
- apytizer/http_methods.py +34 -0
- apytizer/managers/__init__.py +12 -0
- apytizer/managers/abstract_manager.py +80 -0
- apytizer/managers/base_manager.py +116 -0
- apytizer/mappers/__init__.py +6 -0
- apytizer/mappers/abstract_mapper.py +48 -0
- apytizer/mappers/base_mapper.py +78 -0
- apytizer/media_types.py +118 -0
- apytizer/models/__init__.py +6 -0
- apytizer/models/abstract_model.py +119 -0
- apytizer/models/base_model.py +85 -0
- apytizer/protocols.py +38 -0
- apytizer/repositories/__init__.py +6 -0
- apytizer/repositories/abstract_repository.py +81 -0
- apytizer/repositories/managed_repository.py +92 -0
- apytizer/routes/__init__.py +6 -0
- apytizer/routes/abstract_route.py +32 -0
- apytizer/routes/base_route.py +138 -0
- apytizer/sessions/__init__.py +33 -0
- apytizer/sessions/abstract_session.py +63 -0
- apytizer/sessions/requests_session.py +125 -0
- apytizer/states/__init__.py +6 -0
- apytizer/states/abstract_state.py +71 -0
- apytizer/states/local_state.py +99 -0
- apytizer/utils/__init__.py +9 -4
- apytizer/utils/caching.py +39 -0
- apytizer/utils/dictionaries.py +375 -0
- apytizer/utils/errors.py +104 -0
- apytizer/utils/iterables.py +91 -0
- apytizer/utils/objects.py +145 -0
- apytizer/utils/strings.py +69 -0
- apytizer/utils/typing.py +29 -0
- apytizer-0.0.1b1.dist-info/METADATA +41 -0
- apytizer-0.0.1b1.dist-info/RECORD +60 -0
- {apytizer-0.0.1a0.dist-info → apytizer-0.0.1b1.dist-info}/WHEEL +1 -2
- apytizer/abstracts/__init__.py +0 -8
- apytizer/abstracts/api.py +0 -147
- apytizer/abstracts/endpoint.py +0 -177
- apytizer/abstracts/model.py +0 -50
- apytizer/abstracts/session.py +0 -39
- apytizer/adapters/transport.py +0 -40
- apytizer/base/__init__.py +0 -8
- apytizer/base/api.py +0 -510
- apytizer/base/endpoint.py +0 -443
- apytizer/base/model.py +0 -119
- apytizer/decorators/json.py +0 -35
- apytizer/utils/generate_key.py +0 -18
- apytizer/utils/merge.py +0 -19
- apytizer-0.0.1a0.dist-info/METADATA +0 -27
- apytizer-0.0.1a0.dist-info/RECORD +0 -25
- apytizer-0.0.1a0.dist-info/top_level.txt +0 -1
- {apytizer-0.0.1a0.dist-info → apytizer-0.0.1b1.dist-info/licenses}/LICENSE +0 -0
apytizer/errors.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/errors.py
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ConnectionError(Exception):
|
|
6
|
+
"""Base class for connection errors."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ConnectionNotStarted(ConnectionError):
|
|
10
|
+
"""Error raised when connection not started."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EndpointError(Exception):
|
|
14
|
+
"""Base class for endpoint errors."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class EndpointNotFound(Exception):
|
|
18
|
+
"""Error raised when endpoint not found."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class RequestError(Exception):
|
|
22
|
+
"""Base class for request errors."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MethodNotAllowed(RequestError):
|
|
26
|
+
"""Error raised when request method not allowed."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SessionError(Exception):
|
|
30
|
+
"""Base class for session errors."""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class SessionNotStarted(SessionError):
|
|
34
|
+
"""Error raised when session not started."""
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/factories/abstract_factory.py
|
|
3
|
+
"""Abstract factory class interface.
|
|
4
|
+
|
|
5
|
+
This module defines an abstract factory class which provides an interface for
|
|
6
|
+
subclasses to implement.
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Standard Library Imports
|
|
11
|
+
import abc
|
|
12
|
+
|
|
13
|
+
__all__ = ["AbstractFactory"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AbstractFactory(abc.ABC):
|
|
17
|
+
"""Represents an abstract factory."""
|
apytizer/http_methods.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/http_methods.py
|
|
3
|
+
|
|
4
|
+
# Standard Library Imports
|
|
5
|
+
import enum
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@enum.unique
|
|
10
|
+
class HTTPMethod(str, enum.Enum):
|
|
11
|
+
"""Implements standard HTTP methods."""
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def _missing_(cls, value: object) -> Optional[enum.Enum]:
|
|
15
|
+
result = (
|
|
16
|
+
HTTPMethod(value.upper())
|
|
17
|
+
if (
|
|
18
|
+
isinstance(value, str)
|
|
19
|
+
and not value.isupper()
|
|
20
|
+
and value.upper() in HTTPMethod
|
|
21
|
+
)
|
|
22
|
+
else None
|
|
23
|
+
)
|
|
24
|
+
return result
|
|
25
|
+
|
|
26
|
+
CONNECT = "CONNECT"
|
|
27
|
+
HEAD = "HEAD"
|
|
28
|
+
GET = "GET"
|
|
29
|
+
POST = "POST"
|
|
30
|
+
PUT = "PUT"
|
|
31
|
+
PATCH = "PATCH"
|
|
32
|
+
DELETE = "DELETE"
|
|
33
|
+
OPTIONS = "OPTIONS"
|
|
34
|
+
TRACE = "TRACE"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/managers/__init__.py
|
|
3
|
+
"""Managers.
|
|
4
|
+
|
|
5
|
+
Managers are abstractions around endpoints. Managers act as a bridge between
|
|
6
|
+
their respective endpoints and the repositories and models that rely on them.
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Local Imports
|
|
11
|
+
from .abstract_manager import *
|
|
12
|
+
from .base_manager import *
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/managers/abstract_manager.py
|
|
3
|
+
"""Abstract manager class interface.
|
|
4
|
+
|
|
5
|
+
This module defines an abstract manager class which provides an interface for
|
|
6
|
+
subclasses to implement. Each of the abstract methods represents a standard
|
|
7
|
+
create, read, update or delete (CRUD) operation.
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# Standard Library Imports
|
|
12
|
+
import abc
|
|
13
|
+
from typing import Any
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
# Local Imports
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from ..models import AbstractModel
|
|
19
|
+
|
|
20
|
+
__all__ = ["AbstractManager"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AbstractManager(abc.ABC):
|
|
24
|
+
"""Represents an abstract model manager."""
|
|
25
|
+
|
|
26
|
+
@abc.abstractmethod
|
|
27
|
+
def create(self, obj: AbstractModel, /) -> None:
|
|
28
|
+
"""Abstract method to create an object.
|
|
29
|
+
|
|
30
|
+
This method must call the `post` method on an associated endpoint to
|
|
31
|
+
create the object.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
obj: Instance of an abstract model subclass.
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
raise NotImplementedError
|
|
38
|
+
|
|
39
|
+
@abc.abstractmethod
|
|
40
|
+
def read(self, ref: Any, /) -> AbstractModel:
|
|
41
|
+
"""Abstract method to 'read' an object.
|
|
42
|
+
|
|
43
|
+
This method must call the `get` method on an associated endpoint to
|
|
44
|
+
retrieve object data. The response is used to instantiate an instance
|
|
45
|
+
of the managed object class.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
ref: Reference to object on endpoint.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Instance of an abstract model subclass.
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
raise NotImplementedError
|
|
55
|
+
|
|
56
|
+
@abc.abstractmethod
|
|
57
|
+
def update(self, obj: AbstractModel, /) -> None:
|
|
58
|
+
"""Abstract method to update an object.
|
|
59
|
+
|
|
60
|
+
This method must call the `put` method on an associated endpoint to
|
|
61
|
+
update object data.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
obj: Instance of an abstract model subclass.
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
raise NotImplementedError
|
|
68
|
+
|
|
69
|
+
@abc.abstractmethod
|
|
70
|
+
def delete(self, obj: AbstractModel, /) -> None:
|
|
71
|
+
"""Abstract method to delete an object.
|
|
72
|
+
|
|
73
|
+
This method must call the `delete` method on an associated endpoint
|
|
74
|
+
to delete the object.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
obj: Instance of an abstract model subclass.
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/managers/base_manager.py
|
|
3
|
+
"""Base manager class.
|
|
4
|
+
|
|
5
|
+
This module defines a base implementation of a manager class.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Standard Library Imports
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
import logging
|
|
12
|
+
from typing import Any
|
|
13
|
+
from typing import Type
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
# Local Imports
|
|
17
|
+
from .abstract_manager import AbstractManager
|
|
18
|
+
from ..apis import AbstractAPI
|
|
19
|
+
from ..mappers import AbstractMapper
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from ..models import AbstractModel
|
|
23
|
+
|
|
24
|
+
__all__ = ["BaseManager"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Initialize logger.
|
|
28
|
+
log = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BaseManager(AbstractManager):
|
|
32
|
+
"""Base class from which all manager implementations are derived.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
__api: API instance.
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
TypeError: when argument is not type 'Endpoint'.
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __new__(
|
|
43
|
+
cls: Type[BaseManager],
|
|
44
|
+
__api: AbstractAPI,
|
|
45
|
+
__mapper: AbstractMapper,
|
|
46
|
+
/,
|
|
47
|
+
) -> BaseManager:
|
|
48
|
+
if not isinstance(__api, AbstractAPI): # type: ignore
|
|
49
|
+
message = f"expected type 'WebAPI', got {type(__api)} instead"
|
|
50
|
+
raise TypeError(message)
|
|
51
|
+
|
|
52
|
+
if not isinstance(__mapper, AbstractMapper): # type: ignore
|
|
53
|
+
message = f"expected type 'Mapper', got {type(__mapper)} instead"
|
|
54
|
+
raise TypeError(message)
|
|
55
|
+
|
|
56
|
+
instance = super().__new__(cls) # type: ignore
|
|
57
|
+
return instance
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
__api: AbstractAPI,
|
|
62
|
+
__mapper: AbstractMapper,
|
|
63
|
+
/,
|
|
64
|
+
) -> None:
|
|
65
|
+
self._api = __api
|
|
66
|
+
self._mapper = __mapper
|
|
67
|
+
|
|
68
|
+
def __repr__(self) -> str:
|
|
69
|
+
result = "<{cls!s}>".format(cls=self.__class__.__name__)
|
|
70
|
+
return result
|
|
71
|
+
|
|
72
|
+
def create(self, obj: "AbstractModel", /) -> None:
|
|
73
|
+
"""Create an object.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
obj: Object to create.
|
|
77
|
+
|
|
78
|
+
"""
|
|
79
|
+
raise NotImplementedError
|
|
80
|
+
|
|
81
|
+
def read(self, ref: Any, /) -> "AbstractModel":
|
|
82
|
+
"""Read an object.
|
|
83
|
+
|
|
84
|
+
Calls `get` method on associated endpoint to retrieve object data.
|
|
85
|
+
Uses response to instantiate an instance of the managed object class.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
ref: Reference to object on endpoint.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Object instance.
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
raise NotImplementedError
|
|
95
|
+
|
|
96
|
+
def update(self, obj: "AbstractModel", /) -> None:
|
|
97
|
+
"""Update an object.
|
|
98
|
+
|
|
99
|
+
Calls `put` method on associated endpoint to update object data.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
obj: Object to update.
|
|
103
|
+
|
|
104
|
+
"""
|
|
105
|
+
raise NotImplementedError
|
|
106
|
+
|
|
107
|
+
def delete(self, obj: "AbstractModel", /) -> None:
|
|
108
|
+
"""Delete an object.
|
|
109
|
+
|
|
110
|
+
Calls `delete` method on associated endpoint to delete the object.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
obj: Object to delete.
|
|
114
|
+
|
|
115
|
+
"""
|
|
116
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/mappers/abstract_mapper.py
|
|
3
|
+
"""Abstract mapper class interface.
|
|
4
|
+
|
|
5
|
+
This module defines an abstract mapper class which provides an interface for
|
|
6
|
+
subclasses to implement.
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Standard Library Imports
|
|
11
|
+
import abc
|
|
12
|
+
from typing import Mapping
|
|
13
|
+
from typing import Type
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
# Local Imports
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from ..models import AbstractModel
|
|
19
|
+
from ..routes import AbstractRoute
|
|
20
|
+
|
|
21
|
+
__all__ = ["AbstractMapper"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AbstractMapper(abc.ABC):
|
|
25
|
+
"""Represents an abstract mapper.
|
|
26
|
+
|
|
27
|
+
Each mapper is an association between a Python class and an API endpoint,
|
|
28
|
+
which allows ORM operations against the class.
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
@abc.abstractmethod
|
|
34
|
+
def model(self) -> Type["AbstractModel"]:
|
|
35
|
+
"""Model class."""
|
|
36
|
+
raise NotImplementedError
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
@abc.abstractmethod
|
|
40
|
+
def route(self) -> "AbstractRoute":
|
|
41
|
+
"""Routes."""
|
|
42
|
+
raise NotImplementedError
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
@abc.abstractmethod
|
|
46
|
+
def properties(self) -> Mapping[str, str]:
|
|
47
|
+
"""Properties."""
|
|
48
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/mappers/base_mapper.py
|
|
3
|
+
"""Base Mapper Class.
|
|
4
|
+
|
|
5
|
+
This module defines the base mapper class implementation.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Standard Library Imports
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
from types import MappingProxyType
|
|
12
|
+
from typing import Mapping
|
|
13
|
+
from typing import Type
|
|
14
|
+
|
|
15
|
+
# Local Imports
|
|
16
|
+
from .abstract_mapper import AbstractMapper
|
|
17
|
+
from ..models import AbstractModel
|
|
18
|
+
from ..routes import AbstractRoute
|
|
19
|
+
|
|
20
|
+
__all__ = ["Mapper"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Mapper(AbstractMapper):
|
|
24
|
+
"""Implements a mapper.
|
|
25
|
+
|
|
26
|
+
Each mapper is an association between a Python class and an API endpoint,
|
|
27
|
+
which allows ORM operations against the class.
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __new__(
|
|
32
|
+
cls: Type[Mapper],
|
|
33
|
+
__class: Type[AbstractModel],
|
|
34
|
+
__route: AbstractRoute,
|
|
35
|
+
__properties: Mapping[str, str],
|
|
36
|
+
/,
|
|
37
|
+
) -> Mapper:
|
|
38
|
+
if not issubclass(__class, AbstractModel): # type: ignore
|
|
39
|
+
message = f"{__class} is not a subclass of 'AbstractModel'"
|
|
40
|
+
raise TypeError(message)
|
|
41
|
+
|
|
42
|
+
if not isinstance(__route, AbstractRoute): # type: ignore
|
|
43
|
+
message = f"expected type 'Route', got {type(__route)} instead"
|
|
44
|
+
raise TypeError(message)
|
|
45
|
+
|
|
46
|
+
if not isinstance(__properties, Mapping): # type: ignore
|
|
47
|
+
expected = "expected type 'Mapping'"
|
|
48
|
+
actual = f"got {type(__properties)} instead"
|
|
49
|
+
message = ", ".join([expected, actual])
|
|
50
|
+
raise TypeError(message)
|
|
51
|
+
|
|
52
|
+
instance = super().__new__(cls) # type: ignore
|
|
53
|
+
return instance
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
__class: Type[AbstractModel],
|
|
58
|
+
__route: AbstractRoute,
|
|
59
|
+
__properties: Mapping[str, str],
|
|
60
|
+
) -> None:
|
|
61
|
+
self._class = __class
|
|
62
|
+
self._route = __route
|
|
63
|
+
self._properties = MappingProxyType(__properties)
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def model(self) -> Type[AbstractModel]:
|
|
67
|
+
"""Model."""
|
|
68
|
+
raise NotImplementedError
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def route(self) -> AbstractRoute:
|
|
72
|
+
"""Route."""
|
|
73
|
+
raise NotImplementedError
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def properties(self) -> Mapping[str, str]:
|
|
77
|
+
"""Properties."""
|
|
78
|
+
raise NotImplementedError
|
apytizer/media_types.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/media_types.py
|
|
3
|
+
"""Media types (previously MIME types) and subtypes.
|
|
4
|
+
|
|
5
|
+
.. _IANA:
|
|
6
|
+
https://www.iana.org/assignments/media-types/media-types.xhtml
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Standard Library Imports
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
import enum
|
|
13
|
+
import mimetypes
|
|
14
|
+
import re
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@enum.unique
|
|
19
|
+
class MediaType(str, enum.Enum):
|
|
20
|
+
"""Implements media types."""
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def type(self) -> Optional[str]:
|
|
24
|
+
"""Content type."""
|
|
25
|
+
match = re.match(r"[a-z]+(?=/)", self._value_)
|
|
26
|
+
result = match.group(0) if match else None
|
|
27
|
+
return result
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def subtype(self) -> Optional[str]:
|
|
31
|
+
"""Content subtype."""
|
|
32
|
+
match = re.search(r"(?<=/)[a-z+-]+", self._value_)
|
|
33
|
+
result = match.group(0) if match else None
|
|
34
|
+
return result
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def extension(self) -> Optional[str]:
|
|
38
|
+
"""File extension."""
|
|
39
|
+
result = mimetypes.guess_extension(self._value_)
|
|
40
|
+
return result
|
|
41
|
+
|
|
42
|
+
# application
|
|
43
|
+
APPLICATION_JAVASCRIPT = "application/javascript"
|
|
44
|
+
APPLICATION_JSON = "application/json"
|
|
45
|
+
APPLICATION_MANIFEST = "application/manifest+json"
|
|
46
|
+
APPLICATION_MSWORD = "application/msword"
|
|
47
|
+
APPLICATION_OCTET_STREAM = "application/octet-stream"
|
|
48
|
+
APPLICATION_ODA = "application/oda"
|
|
49
|
+
APPLICATION_PDF = "application/pdf"
|
|
50
|
+
APPLICATION_PKCS7_MIME = "application/pkcs7-mime"
|
|
51
|
+
APPLICATION_POSTSCRIPT = "application/postscript"
|
|
52
|
+
APPLICATION_RTF = "application/rtf"
|
|
53
|
+
APPLICATION_RTX = "application/rtx"
|
|
54
|
+
APPLICATION_SQL = "application/sql"
|
|
55
|
+
APPLICATION_TAR = "application/x-tar"
|
|
56
|
+
APPLICATION_XML = "application/xml"
|
|
57
|
+
APPLICATION_ZIP = "application/zip"
|
|
58
|
+
|
|
59
|
+
# audio
|
|
60
|
+
AUDIO_AIFF = "audio/x-aiff"
|
|
61
|
+
AUDIO_BASIC = "audio/basic"
|
|
62
|
+
AUDIO_MP4 = "audio/mp4"
|
|
63
|
+
AUDIO_MPEG = "audio/mpeg"
|
|
64
|
+
AUDIO_MIDI = "audio/midi"
|
|
65
|
+
AUDIO_REALAUDIO = "audio/x-pn-realaudio"
|
|
66
|
+
AUDIO_WAV = "audio/x-wav"
|
|
67
|
+
|
|
68
|
+
# image
|
|
69
|
+
IMAGE_BMP = "image/bmp"
|
|
70
|
+
IMAGE_GIF = "image/gif"
|
|
71
|
+
IMAGE_ICON = "image/vnd.microsoft.icon"
|
|
72
|
+
IMAGE_IEF = "image/ief"
|
|
73
|
+
IMAGE_JPEG = "image/jpeg"
|
|
74
|
+
IMAGE_PICT = "image/pict"
|
|
75
|
+
IMAGE_PNG = "image/png"
|
|
76
|
+
IMAGE_RGB = "image/x-rgb"
|
|
77
|
+
IMAGE_SVG = "image/svg+xml"
|
|
78
|
+
IMAGE_TIFF = "image/tiff"
|
|
79
|
+
|
|
80
|
+
# message
|
|
81
|
+
MESSAGE_PARTIAL = "message/partial"
|
|
82
|
+
MESSAGE_RFC822 = "message/rfc822"
|
|
83
|
+
|
|
84
|
+
# multipart
|
|
85
|
+
MULTIPART_BYTERANGES = "multipart/byteranges"
|
|
86
|
+
MULTIPART_ENCRYPTED = "multipart/encrypted"
|
|
87
|
+
MULTIPART_FORM_DATA = "multipart/form-data"
|
|
88
|
+
MULTIPART_HEADER_SET = "multipart/header-set"
|
|
89
|
+
MULTIPART_MULTILINGUAL = "multipart/multilingual"
|
|
90
|
+
MULTIPART_SIGNED = "multipart/signed"
|
|
91
|
+
|
|
92
|
+
# text
|
|
93
|
+
TEXT_CSS = "text/css"
|
|
94
|
+
TEXT_CSV = "text/csv"
|
|
95
|
+
TEXT_DNS = "text/dns"
|
|
96
|
+
TEXT_HTML = "text/html"
|
|
97
|
+
TEXT_JAVASCRIPT = "text/javascript"
|
|
98
|
+
TEXT_MARKDOWN = "text/markdown"
|
|
99
|
+
TEXT_PLAIN = "text/plain"
|
|
100
|
+
TEXT_PYTHON = "text/x-python"
|
|
101
|
+
TEXT_RTF = "text/rtf"
|
|
102
|
+
TEXT_RTX = "text/rtx"
|
|
103
|
+
TEXT_SETEXT = "text/x-setext"
|
|
104
|
+
TEXT_SGML = "text/x-sgml"
|
|
105
|
+
TEXT_STRINGS = "text/strings"
|
|
106
|
+
TEXT_TSV = "text/tab-separated-values"
|
|
107
|
+
TEXT_VCARD = "text/x-vcard"
|
|
108
|
+
TEXT_XML = "text/xml"
|
|
109
|
+
|
|
110
|
+
# video
|
|
111
|
+
VIDEO_H264 = "video/H264"
|
|
112
|
+
VIDEO_MP4 = "video/mp4"
|
|
113
|
+
VIDEO_MPEG = "video/mpeg"
|
|
114
|
+
VIDEO_MSVIDEO = "video/x-msvideo"
|
|
115
|
+
VIDEO_QUICKTIME = "video/quicktime"
|
|
116
|
+
VIDEO_RAW = "video/raw"
|
|
117
|
+
VIDEO_SGI_MOVIE = "video/x-sgi-movie"
|
|
118
|
+
VIDEO_WEBM = "video/webm"
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/models/abstract_model.py
|
|
3
|
+
"""Abstract model class interface.
|
|
4
|
+
|
|
5
|
+
This module defines an abstract model class which provides an interface
|
|
6
|
+
for subclasses to implement.
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Standard Library Imports
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
import abc
|
|
13
|
+
from typing import Any
|
|
14
|
+
from typing import Mapping
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
# Local Imports
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from ..managers import AbstractManager
|
|
20
|
+
from ..states import AbstractState
|
|
21
|
+
|
|
22
|
+
__all__ = ["AbstractModel"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AbstractModel(abc.ABC):
|
|
26
|
+
"""Represents an abstract model."""
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def manager(self) -> AbstractManager:
|
|
30
|
+
"""Manager for model."""
|
|
31
|
+
return getattr(self, "__manager__")
|
|
32
|
+
|
|
33
|
+
@manager.setter
|
|
34
|
+
def manager(self, manager: AbstractManager, /) -> None:
|
|
35
|
+
setattr(self, "__manager__", manager)
|
|
36
|
+
|
|
37
|
+
@manager.deleter
|
|
38
|
+
def manager(self) -> None:
|
|
39
|
+
delattr(self, "__manager__")
|
|
40
|
+
|
|
41
|
+
@abc.abstractmethod
|
|
42
|
+
def __eq__(self, other: object) -> bool:
|
|
43
|
+
"""Abstract method for determining whether model is equal to another.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Whether models are equal.
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
raise NotImplementedError
|
|
50
|
+
|
|
51
|
+
@abc.abstractmethod
|
|
52
|
+
def __hash__(self) -> int:
|
|
53
|
+
"""Abstract method for returning the hash value of a model.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Hash value.
|
|
57
|
+
|
|
58
|
+
"""
|
|
59
|
+
raise NotImplementedError
|
|
60
|
+
|
|
61
|
+
@abc.abstractmethod
|
|
62
|
+
def __repr__(self) -> str:
|
|
63
|
+
"""Abstract method for returning string representation of model."""
|
|
64
|
+
raise NotImplementedError
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class AbstractStatefulModel(AbstractModel):
|
|
68
|
+
"""Class represents an abstract stateful model."""
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
@abc.abstractmethod
|
|
72
|
+
def state(self) -> AbstractState:
|
|
73
|
+
"""State."""
|
|
74
|
+
raise NotImplementedError
|
|
75
|
+
|
|
76
|
+
@abc.abstractmethod
|
|
77
|
+
def __contains__(self, other: object) -> bool:
|
|
78
|
+
"""Abstract method for determining whether object in state.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Whether object in state.
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
raise NotImplementedError
|
|
85
|
+
|
|
86
|
+
@abc.abstractmethod
|
|
87
|
+
def __getattr__(self, name: str) -> Any:
|
|
88
|
+
"""Abstract method for getting an attribute from state.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Value of attribute.
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
raise NotImplementedError
|
|
95
|
+
|
|
96
|
+
@abc.abstractmethod
|
|
97
|
+
def __getitem__(self, name: str) -> Any:
|
|
98
|
+
"""Abstract method for getting an item from state.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Value of item.
|
|
102
|
+
|
|
103
|
+
"""
|
|
104
|
+
raise NotImplementedError
|
|
105
|
+
|
|
106
|
+
@abc.abstractmethod
|
|
107
|
+
def update(self, __m: Mapping[str, Any], **kwargs: Any) -> None:
|
|
108
|
+
"""Abstract method for updating state of model."""
|
|
109
|
+
raise NotImplementedError
|
|
110
|
+
|
|
111
|
+
@abc.abstractmethod
|
|
112
|
+
def rollback(self) -> None:
|
|
113
|
+
"""Abstract method for rolling back changes to state of model."""
|
|
114
|
+
raise NotImplementedError
|
|
115
|
+
|
|
116
|
+
@abc.abstractmethod
|
|
117
|
+
def save(self) -> None:
|
|
118
|
+
"""Abstract method for saving changes to state of model."""
|
|
119
|
+
raise NotImplementedError
|