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,171 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/engines/http_engine.py
3
+ """HTTP Engine Class.
4
+
5
+ This module defines the HTTP engine class implementation.
6
+
7
+ """
8
+
9
+ # Standard Library Imports
10
+ from collections import ChainMap
11
+ from typing import Any
12
+ from typing import MutableMapping
13
+ from typing import Optional
14
+ from typing import Tuple
15
+ from typing import Type
16
+ from typing import TypeVar
17
+ from typing import Union
18
+
19
+ # Third-Party Imports
20
+ from requests.auth import AuthBase
21
+ from requests.adapters import HTTPAdapter
22
+
23
+ # Local Imports
24
+ from .abstract_engine import AbstractEngine
25
+ from ..connections import HttpConnection
26
+ from ..protocols import Protocol
27
+ from ..protocols import get_protocol
28
+
29
+ __all__ = ["HTTPEngine"]
30
+
31
+
32
+ # Custom types:
33
+ T = TypeVar("T")
34
+
35
+
36
+ class HTTPEngine(AbstractEngine):
37
+ """Implements an HTTP engine.
38
+
39
+ Args:
40
+ url: Base URL.
41
+ adapters (optional): Connection adapters. Default ``None``.
42
+ auth (optional): Authentication header. default ``None``.
43
+ cert (optional): Client certificate. Default ``None``.
44
+ headers (optional): Headers to set globally. Default ``None``.
45
+ params (optional): Parameters to set globally. Default ``None``.
46
+ proxies (optional): Protocols mapped to proxy URLs. Default ``None``.
47
+ stream (optional): Whether to stream response content. Default ``False``.
48
+ timeout (optional): How long to wait before timing out. Default ``None``.
49
+ verify (optional): Whether to verify certificate. Default ``True``.
50
+
51
+ Raises:
52
+ TypeError: when URL of type other than `str`.
53
+
54
+ """
55
+
56
+ _connection_cls: Type[HttpConnection] = HttpConnection
57
+
58
+ def __init__(
59
+ self,
60
+ url: str,
61
+ *,
62
+ adapters: Optional[MutableMapping[Protocol, HTTPAdapter]] = None,
63
+ auth: Optional[Union[AuthBase, Tuple[str, str]]] = None,
64
+ cert: Optional[Union[str, Tuple[str, str]]] = None,
65
+ headers: Optional[MutableMapping[str, str]] = None,
66
+ params: Optional[MutableMapping[str, Any]] = None,
67
+ proxies: Optional[MutableMapping[str, str]] = None,
68
+ stream: Optional[bool] = False,
69
+ timeout: Optional[Union[float, Tuple[float, float]]] = None,
70
+ verify: Optional[bool] = True,
71
+ ) -> None:
72
+ self._url = standardize_url(url)
73
+ self._adapters = ChainMap(adapters or {})
74
+ self._auth = auth
75
+ self._cert = cert
76
+ self._headers = ChainMap(headers or {})
77
+ self._params = ChainMap(params or {})
78
+ self._proxies = ChainMap(proxies or {})
79
+ self._stream = stream or False
80
+ self._timeout = timeout
81
+ self._verify = verify
82
+
83
+ @property
84
+ def protocol(self) -> Optional[Protocol]:
85
+ """Protocol."""
86
+ result = get_protocol(self.url)
87
+ return result
88
+
89
+ @property
90
+ def url(self) -> str:
91
+ """Base URL."""
92
+ return self._url
93
+
94
+ @property
95
+ def adapters(self) -> ChainMap[Protocol, HTTPAdapter]:
96
+ """Connection adapters."""
97
+ return self._adapters
98
+
99
+ @property
100
+ def auth(self) -> Optional[Union[AuthBase, Tuple[str, str]]]:
101
+ """Authentication header."""
102
+ return self._auth
103
+
104
+ @property
105
+ def cert(self) -> Optional[Union[str, Tuple[str, str]]]:
106
+ """Certificate."""
107
+ return self._cert
108
+
109
+ @property
110
+ def headers(self) -> ChainMap[str, str]:
111
+ """Headers."""
112
+ return self._headers
113
+
114
+ @property
115
+ def params(self) -> ChainMap[str, Any]:
116
+ """Parameters."""
117
+ return self._params
118
+
119
+ @property
120
+ def proxies(self) -> ChainMap[str, str]:
121
+ """Proxies."""
122
+ return self._proxies
123
+
124
+ @property
125
+ def stream(self) -> bool:
126
+ """Whether response content will be streamed."""
127
+ return self._stream
128
+
129
+ @property
130
+ def timeout(self) -> Optional[Union[float, Tuple[float, float]]]:
131
+ """How long before request times out."""
132
+ return self._timeout
133
+
134
+ @property
135
+ def verify(self) -> Optional[Union[float, Tuple[float, float]]]:
136
+ """Whether certificate is verified."""
137
+ return self._verify
138
+
139
+ def __repr__(self) -> str:
140
+ return f"Engine({self.url!r})"
141
+
142
+ def connect(self) -> HttpConnection:
143
+ """Establish connection.
144
+
145
+ Returns:
146
+ Connection instance.
147
+
148
+ """
149
+ result = self._connection_cls(self)
150
+ return result
151
+
152
+
153
+ # ----------------------------------------------------------------------------
154
+ # Helpers
155
+ # ----------------------------------------------------------------------------
156
+ def standardize_url(__url: Any, /) -> str:
157
+ """Standardize URL.
158
+
159
+ Args:
160
+ __url: URL.
161
+
162
+ Returns:
163
+ URL.
164
+
165
+ """
166
+ if not isinstance(__url, str):
167
+ message = f"expected type 'str', got {type(__url)} instead"
168
+ raise TypeError(message)
169
+
170
+ result = __url if __url.endswith("/") else __url + "/"
171
+ return result
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,5 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/factories/__init__.py
3
+
4
+ # Local Imports
5
+ from .abstract_factory import *
@@ -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."""
@@ -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,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/mappers/__init__.py
3
+ """Mappers."""
4
+
5
+ # Local Imports
6
+ from .abstract_mapper import *
@@ -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