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.
Files changed (75) hide show
  1. apytizer/__init__.py +2 -12
  2. apytizer/adapters/__init__.py +2 -3
  3. apytizer/adapters/transport_adapter.py +91 -0
  4. apytizer/apis/__init__.py +6 -0
  5. apytizer/apis/abstract_api.py +36 -0
  6. apytizer/apis/web_api.py +460 -0
  7. apytizer/connections/__init__.py +6 -0
  8. apytizer/connections/abstract_connection.py +28 -0
  9. apytizer/connections/http_connection.py +431 -0
  10. apytizer/decorators/__init__.py +5 -5
  11. apytizer/decorators/caching.py +60 -9
  12. apytizer/decorators/chunking.py +105 -0
  13. apytizer/decorators/connection.py +55 -20
  14. apytizer/decorators/json.py +70 -12
  15. apytizer/decorators/pagination.py +50 -32
  16. apytizer/endpoints/__init__.py +6 -0
  17. apytizer/endpoints/abstract_endpoint.py +38 -0
  18. apytizer/endpoints/web_endpoint.py +519 -0
  19. apytizer/engines/__init__.py +6 -0
  20. apytizer/engines/abstract_engine.py +45 -0
  21. apytizer/engines/http_engine.py +171 -0
  22. apytizer/errors.py +34 -0
  23. apytizer/factories/__init__.py +5 -0
  24. apytizer/factories/abstract_factory.py +17 -0
  25. apytizer/http_methods.py +34 -0
  26. apytizer/managers/__init__.py +12 -0
  27. apytizer/managers/abstract_manager.py +80 -0
  28. apytizer/managers/base_manager.py +116 -0
  29. apytizer/mappers/__init__.py +6 -0
  30. apytizer/mappers/abstract_mapper.py +48 -0
  31. apytizer/mappers/base_mapper.py +78 -0
  32. apytizer/media_types.py +118 -0
  33. apytizer/models/__init__.py +6 -0
  34. apytizer/models/abstract_model.py +119 -0
  35. apytizer/models/base_model.py +85 -0
  36. apytizer/protocols.py +38 -0
  37. apytizer/repositories/__init__.py +6 -0
  38. apytizer/repositories/abstract_repository.py +81 -0
  39. apytizer/repositories/managed_repository.py +92 -0
  40. apytizer/routes/__init__.py +6 -0
  41. apytizer/routes/abstract_route.py +32 -0
  42. apytizer/routes/base_route.py +138 -0
  43. apytizer/sessions/__init__.py +33 -0
  44. apytizer/sessions/abstract_session.py +63 -0
  45. apytizer/sessions/requests_session.py +125 -0
  46. apytizer/states/__init__.py +6 -0
  47. apytizer/states/abstract_state.py +71 -0
  48. apytizer/states/local_state.py +99 -0
  49. apytizer/utils/__init__.py +9 -4
  50. apytizer/utils/caching.py +39 -0
  51. apytizer/utils/dictionaries.py +376 -0
  52. apytizer/utils/errors.py +104 -0
  53. apytizer/utils/iterables.py +91 -0
  54. apytizer/utils/objects.py +145 -0
  55. apytizer/utils/strings.py +69 -0
  56. apytizer/utils/typing.py +29 -0
  57. apytizer-0.0.1b2.dist-info/METADATA +41 -0
  58. apytizer-0.0.1b2.dist-info/RECORD +60 -0
  59. {apytizer-0.0.1a0.dist-info → apytizer-0.0.1b2.dist-info}/WHEEL +1 -2
  60. apytizer/abstracts/__init__.py +0 -8
  61. apytizer/abstracts/api.py +0 -147
  62. apytizer/abstracts/endpoint.py +0 -177
  63. apytizer/abstracts/model.py +0 -50
  64. apytizer/abstracts/session.py +0 -39
  65. apytizer/adapters/transport.py +0 -40
  66. apytizer/base/__init__.py +0 -8
  67. apytizer/base/api.py +0 -510
  68. apytizer/base/endpoint.py +0 -443
  69. apytizer/base/model.py +0 -119
  70. apytizer/utils/generate_key.py +0 -18
  71. apytizer/utils/merge.py +0 -19
  72. apytizer-0.0.1a0.dist-info/METADATA +0 -27
  73. apytizer-0.0.1a0.dist-info/RECORD +0 -25
  74. apytizer-0.0.1a0.dist-info/top_level.txt +0 -1
  75. {apytizer-0.0.1a0.dist-info → apytizer-0.0.1b2.dist-info/licenses}/LICENSE +0 -0
@@ -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,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/models/__init__.py
3
+
4
+ # Local Imports
5
+ from .abstract_model import *
6
+ from .base_model import *
@@ -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,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/repositories/__init__.py
3
+
4
+ # Local Imports
5
+ from .abstract_repository import *
6
+ from .managed_repository import *
@@ -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,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/routes/__init__.py
3
+
4
+ # Local Imports
5
+ from .abstract_route import *
6
+ from .base_route import *
@@ -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