apytizer 0.0.1a0__py3-none-any.whl → 0.0.1b2__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 +460 -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.py +70 -12
- 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 +171 -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 +376 -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.1b2.dist-info/METADATA +41 -0
- apytizer-0.0.1b2.dist-info/RECORD +60 -0
- {apytizer-0.0.1a0.dist-info → apytizer-0.0.1b2.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/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.1b2.dist-info/licenses}/LICENSE +0 -0
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
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/base/model.py
|
|
3
|
+
"""Base model class.
|
|
4
|
+
|
|
5
|
+
This module defines the implementation of a base model class.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Standard Library Imports
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
from typing import Any
|
|
12
|
+
from typing import Generator
|
|
13
|
+
from typing import Hashable
|
|
14
|
+
from typing import Mapping
|
|
15
|
+
from typing import Tuple
|
|
16
|
+
|
|
17
|
+
# Local Imports
|
|
18
|
+
from .abstract_model import AbstractModel
|
|
19
|
+
from .. import states
|
|
20
|
+
|
|
21
|
+
__all__ = ["BaseModel"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class BaseModel(AbstractModel):
|
|
25
|
+
"""Implements a stateful model.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
**kwargs: Data with which to set model state.
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
reference: Hashable
|
|
33
|
+
|
|
34
|
+
def __init__(self, **kwargs: Any):
|
|
35
|
+
self._state = states.LocalState(kwargs)
|
|
36
|
+
|
|
37
|
+
def __contains__(self, key: str) -> bool:
|
|
38
|
+
return key in self._state
|
|
39
|
+
|
|
40
|
+
def __eq__(self, other: object) -> bool:
|
|
41
|
+
return (
|
|
42
|
+
other.reference == self.reference
|
|
43
|
+
if isinstance(other, BaseModel)
|
|
44
|
+
else False
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def __hash__(self) -> int:
|
|
48
|
+
return hash(self.reference)
|
|
49
|
+
|
|
50
|
+
def __getattr__(self, name: str) -> Any:
|
|
51
|
+
attr = self._state.get(name)
|
|
52
|
+
if not attr:
|
|
53
|
+
cls = self.__class__.__name__
|
|
54
|
+
message = f"type object '{cls!s}' has no attribute '{name!s}'"
|
|
55
|
+
raise AttributeError(message)
|
|
56
|
+
|
|
57
|
+
return attr
|
|
58
|
+
|
|
59
|
+
def __getitem__(self, key: str) -> Any:
|
|
60
|
+
value = self._state[key]
|
|
61
|
+
return value
|
|
62
|
+
|
|
63
|
+
def __iter__(self) -> Generator[Tuple[str, Any], None, None]:
|
|
64
|
+
yield from self._state.items()
|
|
65
|
+
|
|
66
|
+
def __repr__(self) -> str:
|
|
67
|
+
return self.__class__.__name__
|
|
68
|
+
|
|
69
|
+
def update(self, __m: Mapping[str, Any], **kwargs: Any) -> None:
|
|
70
|
+
"""Update local state with provided data.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
__m (optional): Mapping with which to update local state.
|
|
74
|
+
**kwargs: Data with which to update local state.
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
self._state.update(__m, **kwargs)
|
|
78
|
+
|
|
79
|
+
def rollback(self) -> None:
|
|
80
|
+
"""Rollback changes to local state."""
|
|
81
|
+
self._state.rollback()
|
|
82
|
+
|
|
83
|
+
def save(self) -> None:
|
|
84
|
+
"""Save changes to local state."""
|
|
85
|
+
self._state.save()
|
apytizer/protocols.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/protocols.py
|
|
3
|
+
|
|
4
|
+
# Standard Library Imports
|
|
5
|
+
import enum
|
|
6
|
+
import re
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
__all__ = ["Protocol", "get_protocol"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@enum.unique
|
|
13
|
+
class Protocol(str, enum.Enum):
|
|
14
|
+
"""Implements standard application layer protocols."""
|
|
15
|
+
|
|
16
|
+
HTTP = "http"
|
|
17
|
+
HTTPS = "https"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# ----------------------------------------------------------------------------
|
|
21
|
+
# Selectors
|
|
22
|
+
# ----------------------------------------------------------------------------
|
|
23
|
+
def get_protocol(
|
|
24
|
+
url: str, default: Optional[Protocol] = None
|
|
25
|
+
) -> Optional[Protocol]:
|
|
26
|
+
"""Get protocol from URL.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
url: URL from which to get protocol.
|
|
30
|
+
default (optional): Default protocol. Default ``None``.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Protocol.
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
match = re.match(r"\w+(?=://)", url, re.I)
|
|
37
|
+
result = Protocol(match.group(0)) if match else default
|
|
38
|
+
return result
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/repositories/abstract_repository.py
|
|
3
|
+
"""Abstract repository class interface.
|
|
4
|
+
|
|
5
|
+
This module defines an abstract repository class which provides an interface
|
|
6
|
+
for subclasses to implement.
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Standard Library Imports
|
|
11
|
+
import abc
|
|
12
|
+
from typing import Any
|
|
13
|
+
from typing import List
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
# Local Imports
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from ..models import AbstractModel
|
|
19
|
+
|
|
20
|
+
__all__ = ["AbstractRepository"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AbstractRepository(abc.ABC):
|
|
24
|
+
"""Represents an abstract repository."""
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def objects(self) -> List[AbstractModel]:
|
|
28
|
+
"""Objects in repository."""
|
|
29
|
+
raise NotImplementedError
|
|
30
|
+
|
|
31
|
+
@abc.abstractmethod
|
|
32
|
+
def add(self, obj: AbstractModel, /) -> None:
|
|
33
|
+
"""Abstract method to add an object to repository.
|
|
34
|
+
|
|
35
|
+
This method only adds an object to the local state of the repository.
|
|
36
|
+
New objects are not sent to the associated endpoint until the client
|
|
37
|
+
commits changes to the repository.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
obj: Instance of an abstract model subclass.
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
raise NotImplementedError
|
|
44
|
+
|
|
45
|
+
@abc.abstractmethod
|
|
46
|
+
def get(self, ref: Any, *args: Any, **kwargs: Any) -> AbstractModel:
|
|
47
|
+
"""Abstract method to get an object from the repository.
|
|
48
|
+
|
|
49
|
+
If the object is not found in local state, a request is send to the
|
|
50
|
+
associated endpoint.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
ref: Reference to object.
|
|
54
|
+
*args: Positional arguments.
|
|
55
|
+
**kwargs: Keyword arguments.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Instance of an abstract model subclass.
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
raise NotImplementedError
|
|
62
|
+
|
|
63
|
+
@abc.abstractmethod
|
|
64
|
+
def remove(self, obj: AbstractModel, /) -> None:
|
|
65
|
+
"""Abstract method to remove an object from repository.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
obj: Instance of an abstract model subclass.
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
raise NotImplementedError
|
|
72
|
+
|
|
73
|
+
@abc.abstractmethod
|
|
74
|
+
def commit(self) -> None:
|
|
75
|
+
"""Abstract method for committing changes to objects."""
|
|
76
|
+
raise NotImplementedError
|
|
77
|
+
|
|
78
|
+
@abc.abstractmethod
|
|
79
|
+
def rollback(self) -> None:
|
|
80
|
+
"""Abstract method for rolling back changes to objects."""
|
|
81
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/repositories/managed_repository.py
|
|
3
|
+
"""Managed Repository Class.
|
|
4
|
+
|
|
5
|
+
This module defines a managed repository class.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Standard Library Imports
|
|
10
|
+
from typing import Any
|
|
11
|
+
from typing import List
|
|
12
|
+
from typing import Optional
|
|
13
|
+
from typing import Set
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
# Local Imports
|
|
17
|
+
from .abstract_repository import AbstractRepository
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from ..managers import AbstractManager
|
|
21
|
+
from ..models import AbstractModel
|
|
22
|
+
|
|
23
|
+
__all__ = ["ManagedRepository"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ManagedRepository(AbstractRepository):
|
|
27
|
+
"""Implements a managed repository.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
manager: Manager.
|
|
31
|
+
objects (optional): Objects to include in repository.
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
manager: AbstractManager,
|
|
38
|
+
objects: Optional[List[AbstractModel]] = None,
|
|
39
|
+
) -> None:
|
|
40
|
+
self._manager = manager
|
|
41
|
+
self._objects: Set[AbstractModel] = set(objects or [])
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def objects(self) -> List[AbstractModel]:
|
|
45
|
+
"""Objects in repository."""
|
|
46
|
+
result = list(self._objects)
|
|
47
|
+
return result
|
|
48
|
+
|
|
49
|
+
def add(self, obj: AbstractModel) -> None:
|
|
50
|
+
"""Add an object to the repository.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
obj: Object to add.
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
self._objects.add(obj)
|
|
57
|
+
|
|
58
|
+
def get(self, ref: Any) -> AbstractModel:
|
|
59
|
+
"""Get an object from the repository.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
ref: Reference to object.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Object.
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
result = next(obj for obj in self.objects if obj.reference == ref)
|
|
70
|
+
except StopIteration:
|
|
71
|
+
result = self._manager.read(ref)
|
|
72
|
+
self._objects.add(result)
|
|
73
|
+
|
|
74
|
+
return result
|
|
75
|
+
|
|
76
|
+
def remove(self, obj: AbstractModel) -> None:
|
|
77
|
+
"""Remove an object from the repository.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
obj: Object to remove.
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
self._manager.delete(obj)
|
|
84
|
+
self._objects.discard(obj)
|
|
85
|
+
|
|
86
|
+
def commit(self) -> None:
|
|
87
|
+
"""Commit changes to objects in the repository."""
|
|
88
|
+
raise NotImplementedError
|
|
89
|
+
|
|
90
|
+
def rollback(self) -> None:
|
|
91
|
+
"""Rollback changes to objects in the repository."""
|
|
92
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/routes/abstract_route.py
|
|
3
|
+
|
|
4
|
+
# Standard Library Imports
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
import abc
|
|
7
|
+
|
|
8
|
+
__all__ = ["AbstractRoute"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AbstractRoute(abc.ABC):
|
|
12
|
+
"""Represents an abstract route."""
|
|
13
|
+
|
|
14
|
+
@abc.abstractmethod
|
|
15
|
+
def __add__(self, other: object) -> AbstractRoute:
|
|
16
|
+
raise NotImplementedError
|
|
17
|
+
|
|
18
|
+
@abc.abstractmethod
|
|
19
|
+
def __eq__(self, other: object) -> bool:
|
|
20
|
+
raise NotImplementedError
|
|
21
|
+
|
|
22
|
+
@abc.abstractmethod
|
|
23
|
+
def __hash__(self) -> int:
|
|
24
|
+
raise NotImplementedError
|
|
25
|
+
|
|
26
|
+
@abc.abstractmethod
|
|
27
|
+
def __str__(self) -> str:
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
|
|
30
|
+
@abc.abstractmethod
|
|
31
|
+
def __truediv__(self, other: object) -> AbstractRoute:
|
|
32
|
+
raise NotImplementedError
|