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,138 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/routes/base_route.py
3
+
4
+ # Standard Library Imports
5
+ from typing import List
6
+
7
+ # Local Imports
8
+ from .abstract_route import AbstractRoute
9
+
10
+ __all__ = ["Route"]
11
+
12
+
13
+ class Route(AbstractRoute):
14
+ """Implements a route.
15
+
16
+ Args:
17
+ __value: Value.
18
+
19
+ """
20
+
21
+ def __init__(self, __value: str = "/", /) -> None:
22
+ if not isinstance(__value, (AbstractRoute, str)): # type: ignore
23
+ message = f"expected type 'str', got {type(__value)} instead"
24
+ raise TypeError(message)
25
+
26
+ self._value = str(__value).strip("/") + "/"
27
+
28
+ def __eq__(self, other: object) -> bool:
29
+ if not isinstance(other, (AbstractRoute, str)):
30
+ return False
31
+
32
+ other_segments = get_segments(str(other))
33
+ self_segments = get_segments(self._value)
34
+ result = segments_are_equal(other_segments, self_segments)
35
+ return result
36
+
37
+ def __hash__(self) -> int:
38
+ return hash(self._value.lower())
39
+
40
+ def __len__(self) -> int:
41
+ return len(get_segments(self._value))
42
+
43
+ def __str__(self) -> str:
44
+ return self._value
45
+
46
+ def __gt__(self, other: object) -> bool:
47
+ if not isinstance(other, (AbstractRoute, str)):
48
+ return False
49
+
50
+ self_segments = get_segments(self._value)
51
+ other_segments = get_segments(str(other))
52
+ result = tuple(self_segments) > tuple(other_segments)
53
+ return result
54
+
55
+ def __lt__(self, other: object) -> bool:
56
+ if not isinstance(other, (AbstractRoute, str)):
57
+ return False
58
+
59
+ self_segments = get_segments(self._value)
60
+ other_segments = get_segments(str(other))
61
+ result = tuple(self_segments) < tuple(other_segments)
62
+ return result
63
+
64
+ def __add__(self, other: object) -> AbstractRoute:
65
+ if not isinstance(other, str):
66
+ message = f"expected type 'str', got {type(other)} instead"
67
+ raise TypeError(message)
68
+
69
+ result = Route(self._value + other.strip("/"))
70
+ return result
71
+
72
+ def __truediv__(self, other: object) -> AbstractRoute:
73
+ if not isinstance(other, str):
74
+ message = f"expected type 'str', got {type(other)} instead"
75
+ raise TypeError(message)
76
+
77
+ result = Route(self._value + other.strip("/"))
78
+ return result
79
+
80
+
81
+ # ----------------------------------------------------------------------------
82
+ # Helpers
83
+ # ----------------------------------------------------------------------------
84
+ def count_segments(__value: str, /) -> int:
85
+ """Get number of segments.
86
+
87
+ Args:
88
+ __value: Value for which to get number of segments.
89
+
90
+ Returns:
91
+ Number of segments.
92
+
93
+ """
94
+ result = len(get_segments(__value))
95
+ return result
96
+
97
+
98
+ def get_segments(__value: str, /) -> List[str]:
99
+ """Get segments of string seperated by slashes.
100
+
101
+ Args:
102
+ __value: Value for which to get segments.
103
+
104
+ Returns:
105
+ Segments.
106
+
107
+ """
108
+ if not isinstance(__value, str): # type: ignore
109
+ message = f"expected type 'str', got {type(__value)} instead"
110
+ raise TypeError(message)
111
+
112
+ result = __value.strip("/").split("/")
113
+ return result
114
+
115
+
116
+ def segments_are_equal(
117
+ first: List[str], second: List[str], placeholder: str = r"{}"
118
+ ) -> bool:
119
+ """Check whether segments are equal.
120
+
121
+ Args:
122
+ first: First list of segments.
123
+ second: Second list of segments.
124
+ placeholder: Placeholder. Default ``{}``.
125
+
126
+ Returns:
127
+ Whether segments are equal.
128
+
129
+ """
130
+ if len(first) != len(second):
131
+ return False
132
+
133
+ result = all(
134
+ left.lower() == right.lower()
135
+ for left, right in zip(first, second)
136
+ if right != placeholder
137
+ )
138
+ return result
@@ -0,0 +1,33 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/sessions/__init__.py
3
+
4
+ # Standard Library Imports
5
+ from typing import Type
6
+ from typing import TYPE_CHECKING
7
+
8
+ # Local Imports
9
+ from .abstract_session import *
10
+ from .requests_session import *
11
+
12
+ if TYPE_CHECKING:
13
+ from ..engines import AbstractEngine
14
+
15
+
16
+ class sessionmaker:
17
+ """Implements a sessionmaker."""
18
+
19
+ def __init__(
20
+ self, class_: Type[RequestsSession] = RequestsSession
21
+ ) -> None:
22
+ self._session_type = class_
23
+
24
+ def __call__(self, __engine: "AbstractEngine", /) -> RequestsSession:
25
+ result = self._session_type(
26
+ adapters=getattr(__engine, "adapters", None),
27
+ auth=getattr(__engine, "auth", None),
28
+ cert=getattr(__engine, "cert", None),
29
+ proxies=getattr(__engine, "proxies", None),
30
+ stream=getattr(__engine, "stream", False),
31
+ verify=getattr(__engine, "verify", True),
32
+ )
33
+ return result
@@ -0,0 +1,63 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/sessions/abstract_session.py
3
+ """Abstract Session Class.
4
+
5
+ This module defines an abstract session 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 TYPE_CHECKING
14
+
15
+ # Third-Party Imports
16
+ import requests
17
+ from requests.adapters import HTTPAdapter
18
+
19
+ if TYPE_CHECKING:
20
+ from ..protocols import Protocol
21
+
22
+ __all__ = ["AbstractSession"]
23
+
24
+
25
+ class AbstractSession(abc.ABC):
26
+ """Represents an abstract session."""
27
+
28
+ @abc.abstractmethod
29
+ def start(self) -> None:
30
+ """Starts session."""
31
+ raise NotImplementedError
32
+
33
+ @abc.abstractmethod
34
+ def close(self) -> None:
35
+ """Destroys session."""
36
+ raise NotImplementedError
37
+
38
+ @abc.abstractmethod
39
+ def mount(self, protocol: "Protocol", adapter: HTTPAdapter) -> None:
40
+ """Registers a connection adapter to a protocol prefix.
41
+
42
+ Args:
43
+ protocol: Protocol prefix on which to mount adapter.
44
+ adapter: HTTP adapter to apply to connection.
45
+
46
+ """
47
+ raise NotImplementedError
48
+
49
+ @abc.abstractmethod
50
+ def send(
51
+ self, __request: requests.Request, /, **kwargs: Any
52
+ ) -> requests.Response:
53
+ """Send an HTTP request.
54
+
55
+ Args:
56
+ __request: Request to send.
57
+ **kwargs: Keyword arguments.
58
+
59
+ Returns:
60
+ Response.
61
+
62
+ """
63
+ raise NotImplementedError
@@ -0,0 +1,125 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/sessions/requests_session.py
3
+ """Requests Session Class.
4
+
5
+ This module defines the requests session class implementation.
6
+
7
+ """
8
+
9
+ # Standard Library Imports
10
+ import logging
11
+ from typing import Any
12
+ from typing import Dict
13
+ from typing import MutableMapping
14
+ from typing import Optional
15
+ from typing import Tuple
16
+ from typing import Union
17
+ from typing import final
18
+
19
+ # Third-Party Imports
20
+ import requests
21
+ from requests.auth import AuthBase
22
+ from requests.adapters import HTTPAdapter
23
+
24
+ # Local Imports
25
+ from .abstract_session import AbstractSession
26
+ from ..protocols import Protocol
27
+
28
+ __all__ = ["RequestsSession"]
29
+
30
+
31
+ log = logging.getLogger("apytizer")
32
+
33
+
34
+ class RequestsSession(AbstractSession):
35
+ """Implements a requests session.
36
+
37
+ The `RequestsSession` class provides `start` and `close` methods for manual
38
+ control of the session.
39
+
40
+ A session instance can also be used as a context manager.
41
+
42
+ Args:
43
+ adapters (optional): Connection adapters. Default ``None``.
44
+ auth (optional): Authentication header. default ``None``.
45
+ cert (optional): Client certificate. Default ``None``.
46
+ proxies (optional): Protocols mapped to proxy URLs. Default ``None``.
47
+ stream (optional): Whether to stream response content. Default ``False``.
48
+ verify (optional): Whether to verify certificate. Default ``True``.
49
+
50
+ .. Requests Documentation:
51
+ https://docs.python-requests.org/en/latest/api/#request-sessions
52
+
53
+ """
54
+
55
+ def __init__(
56
+ self,
57
+ *,
58
+ adapters: Optional[Dict[Protocol, HTTPAdapter]] = None,
59
+ auth: Optional[Union[AuthBase, Tuple[str, str]]] = None,
60
+ cert: Optional[Union[str, Tuple[str, str]]] = None,
61
+ proxies: Optional[MutableMapping[str, str]] = None,
62
+ stream: Optional[bool] = False,
63
+ verify: Optional[bool] = True,
64
+ ) -> None:
65
+ self._session = requests.Session()
66
+ self._session.auth = auth
67
+ self._session.cert = cert
68
+ self._session.proxies = proxies # type: ignore
69
+ self._session.stream = stream # type: ignore
70
+ self._session.verify = verify # type: ignore
71
+
72
+ if adapters is not None:
73
+ for protocol, adapter in adapters.items():
74
+ self._session.mount(protocol, adapter)
75
+
76
+ @final
77
+ def __enter__(self) -> AbstractSession:
78
+ """Starts session as context manager."""
79
+ self.start()
80
+ return self
81
+
82
+ @final
83
+ def __exit__(self, *_) -> None:
84
+ """Stops session as context manager."""
85
+ self.close()
86
+
87
+ def start(self) -> None:
88
+ """Starts session."""
89
+ log.debug("Starting session...")
90
+
91
+ def close(self, *_: Any) -> None:
92
+ """Destroys session."""
93
+ self._session.close()
94
+ log.debug("Session closed")
95
+
96
+ def mount(self, protocol: Protocol, adapter: HTTPAdapter) -> None:
97
+ """Registers connection adapter to protocol prefix.
98
+
99
+ Args:
100
+ protocol: Protocol on which to mount adapter.
101
+ adapter: HTTP adapter to apply to connection.
102
+
103
+ """
104
+ self._session.mount(f"{protocol.value!s}://", adapter)
105
+ log.debug(
106
+ "Mounted %(adapter)s adapter to %(protocol)s protocol",
107
+ {"adapter": adapter, "protocol": protocol.value},
108
+ )
109
+
110
+ def send(
111
+ self, __request: requests.Request, /, **kwargs: Any
112
+ ) -> requests.Response:
113
+ """Send an HTTP request.
114
+
115
+ Args:
116
+ __request: Request to send.
117
+ **kwargs (optional): Keyword arguments.
118
+
119
+ Returns:
120
+ Response object.
121
+
122
+ """
123
+ request = self._session.prepare_request(__request)
124
+ response = self._session.send(request, **kwargs)
125
+ return response
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/states/__init__.py
3
+
4
+ # Local Imports
5
+ from .abstract_state import *
6
+ from .local_state import *
@@ -0,0 +1,71 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/states/abstract_state.py
3
+ """Abstract State Class Interface.
4
+
5
+ This module defines an abstract state 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 Generator
15
+ from typing import Mapping
16
+ from typing import Optional
17
+
18
+ __all__ = ["AbstractState"]
19
+
20
+
21
+ class AbstractState(abc.ABC):
22
+ """Represents an abstract state."""
23
+
24
+ @abc.abstractmethod
25
+ def __contains__(self, key: str) -> bool:
26
+ raise NotImplementedError
27
+
28
+ @abc.abstractmethod
29
+ def __eq__(self, other: object) -> bool:
30
+ raise NotImplementedError
31
+
32
+ @abc.abstractmethod
33
+ def __getitem__(self, key: str) -> Any:
34
+ raise NotImplementedError
35
+
36
+ @abc.abstractmethod
37
+ def __setitem__(self, key: str, value: Any) -> None:
38
+ raise NotImplementedError
39
+
40
+ @abc.abstractmethod
41
+ def __iter__(self) -> Generator[Any, None, None]:
42
+ raise NotImplementedError
43
+
44
+ @abc.abstractmethod
45
+ def get(self, key: str) -> Any:
46
+ """Abstract method for getting an item from state."""
47
+ raise NotImplementedError
48
+
49
+ @abc.abstractmethod
50
+ def items(self) -> Any:
51
+ """Abstract method for getting items from state."""
52
+ raise NotImplementedError
53
+
54
+ @abc.abstractmethod
55
+ def update(
56
+ self,
57
+ __m: Optional[Mapping[str, Any]] = None,
58
+ **kwargs: Any,
59
+ ) -> None:
60
+ """Abstract method for updating state."""
61
+ raise NotImplementedError
62
+
63
+ @abc.abstractmethod
64
+ def rollback(self) -> None:
65
+ """Abstract method for rolling back changes to state."""
66
+ raise NotImplementedError
67
+
68
+ @abc.abstractmethod
69
+ def save(self) -> None:
70
+ """Abstract method for saving changes to state."""
71
+ raise NotImplementedError
@@ -0,0 +1,99 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/states/base_state.py
3
+ """Base State Class.
4
+
5
+ This module defines the implementation of a base state class.
6
+
7
+ """
8
+
9
+ # Standard Library Imports
10
+ from __future__ import annotations
11
+ import collections
12
+ from typing import Any
13
+ from typing import Dict
14
+ from typing import Generator
15
+ from typing import Mapping
16
+ from typing import Optional
17
+ from typing import Tuple
18
+
19
+ # Local Imports
20
+ from . import AbstractState
21
+ from .. import utils
22
+
23
+ __all__ = ["LocalState"]
24
+
25
+
26
+ class LocalState(AbstractState):
27
+ """Class implements a local state."""
28
+
29
+ def __init__(
30
+ self,
31
+ base: Optional[Dict[str, Any]] = None,
32
+ default: Optional[Dict[str, Any]] = None,
33
+ ) -> None:
34
+ self._state = collections.ChainMap(base or {}, default or {})
35
+
36
+ def __contains__(self, key: str) -> bool:
37
+ return key in self._state
38
+
39
+ def __eq__(self, other: object) -> bool:
40
+ return (
41
+ dict(other) == dict(self)
42
+ if isinstance(other, AbstractState)
43
+ else False
44
+ )
45
+
46
+ def __getitem__(self, key: str) -> Any:
47
+ result = self.get(key)
48
+ return result
49
+
50
+ def __setitem__(self, key: str, value: Any) -> None:
51
+ self._state = utils.deep_set(self._state, key, value)
52
+
53
+ def __iter__(self) -> Generator[Tuple[str, Any], None, None]:
54
+ yield from self._state.items()
55
+
56
+ def get(self, key: str) -> Any:
57
+ """Get an item from state.
58
+
59
+ Args:
60
+ key: Key.
61
+
62
+ Returns:
63
+ Value of key in state.
64
+
65
+ """
66
+ if not isinstance(key, str): # type: ignore
67
+ message = f"expected type 'str', got {type(key)} instead"
68
+ raise TypeError(message)
69
+
70
+ result = utils.deep_get(self._state, key)
71
+ return result
72
+
73
+ def items(self) -> Any:
74
+ """Get items from state."""
75
+ results = self._state.items()
76
+ return results
77
+
78
+ def update(
79
+ self,
80
+ __m: Optional[Mapping[str, Any]] = None,
81
+ **kwargs: Any,
82
+ ) -> None:
83
+ """Update state.
84
+
85
+ Args:
86
+ __m (optional): Mapping. Default ``None``.
87
+ **kwargs: Keyword arguments.
88
+
89
+ """
90
+ self._state.update(__m or {}, **kwargs)
91
+
92
+ def rollback(self) -> None:
93
+ """Roll back changes to state."""
94
+ self._state.clear()
95
+
96
+ def save(self) -> None:
97
+ """Save changes to state."""
98
+ if self._state.maps[0]: # type: ignore
99
+ self._state = self._state.new_child() # type: ignore
@@ -1,6 +1,11 @@
1
1
  # -*- coding: utf-8 -*-
2
+ # src/apytizer/utils/__init__.py
2
3
 
3
- # pylint: skip-file
4
-
5
- from .generate_key import generate_key
6
- from .merge import merge
4
+ # Local Imports
5
+ from .caching import *
6
+ from .dictionaries import *
7
+ from .errors import *
8
+ from .iterables import *
9
+ from .objects import *
10
+ from .strings import *
11
+ from .typing import *
@@ -0,0 +1,39 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Standard Library Import
4
+ from typing import Any
5
+ from typing import Callable
6
+ from typing import Hashable
7
+ from typing import Tuple
8
+
9
+ # Third-Party Imports
10
+ from cachetools.keys import hashkey
11
+
12
+ __all__ = ["generate_key"]
13
+
14
+
15
+ def generate_key(*tags: str) -> Callable[..., Tuple[Hashable, ...]]:
16
+ """Generates a hashable key for caching values.
17
+
18
+ Args:
19
+ *tags: Tags.
20
+
21
+ """
22
+
23
+ def hash_parameters(*args: Any, **kwargs: Any) -> Tuple[Hashable, ...]:
24
+ """Hashes function parameters.
25
+
26
+ Args:
27
+ *args: Positional arguments.
28
+ **kwargs: Keyword arguments.
29
+
30
+ Return:
31
+ Cache Key.
32
+
33
+ """
34
+ result = hashkey(
35
+ *tags, *args, *[f"{k!s}={v!s}" for k, v in sorted(kwargs.items())]
36
+ )
37
+ return result
38
+
39
+ return hash_parameters