lsrestclient 0.3.0__tar.gz → 0.6.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lsrestclient
3
- Version: 0.3.0
3
+ Version: 0.6.0
4
4
  Summary: REST Api Client
5
5
  Author: mba
6
6
  Author-email: bartel@electronic-shop.lu
@@ -0,0 +1,3 @@
1
+ from .exceptions import *
2
+ from .response import *
3
+ from .client import *
@@ -0,0 +1,203 @@
1
+ import os
2
+ from typing import Optional, Dict, Any
3
+
4
+ import lsjsonclasses
5
+ import requests
6
+ from requests import Session
7
+
8
+ import re
9
+
10
+ from lsrestclient import LsRestClientResponse, exceptions
11
+
12
+ find_parameters_regex = re.compile('{(.*?)}')
13
+
14
+
15
+ class LsRestClient(Session):
16
+ _clients = {}
17
+
18
+ @classmethod
19
+ def from_env(cls, env_name: str, name: Optional[str] = None, required: bool = True):
20
+ """
21
+ Create an instance of the LsRestClient class using environment variables.
22
+ It gets globally saved under the given name, so that it can be reused.
23
+
24
+ :param env_name: The name of the environment variable that holds the base URL.
25
+ :param name: An optional name for the client instance.
26
+ :param required: A flag indicating whether the environment variable is required. Defaults to True.
27
+ :return: An instance of the LsRestClient class.
28
+ """
29
+ base_url = os.environ.get(env_name, "")
30
+ if base_url == "" and required:
31
+ raise EnvironmentError(f"Environment variable '{env_name}' needs to be set")
32
+
33
+ return cls(
34
+ base_url=base_url,
35
+ name=name
36
+ )
37
+
38
+ @classmethod
39
+ def client(cls, name: str):
40
+ """
41
+ Retrieves the LsRestClient instance with the specified name. If a client with the given name does not exist, an exception is raised.
42
+
43
+ :param name: The name of the LsRestClient to be retrieved.
44
+ :return: The LsRestClient instance with the given name.
45
+ """
46
+ try:
47
+ return cls._clients[name]
48
+ except KeyError:
49
+ raise Exception(f"LsRestClient with name '{name}' not initialized.")
50
+
51
+ def __init__(self, base_url: str = None, name: str = "default") -> None:
52
+ """Class representing a REST client for JSON API."""
53
+
54
+ self.base_url = base_url
55
+ self.base_headers = {'content-type': 'application/json'}
56
+ self.name = name
57
+ super().__init__()
58
+ self._clients[name] = self
59
+
60
+ def full_url(self, url: str, params: Optional[dict] = None) -> str:
61
+ """
62
+ Builds a full url from the base_url with url parameters replaced.
63
+
64
+ :param url: The relative URL to be used to build the full URL.
65
+ :param params: An optional dictionary that contains the parameters to be used in formatting the URL.
66
+ Default is None. Used parameters get removed from the dictionary.
67
+ :return: The full URL with the parameters replaced.
68
+ """
69
+ if params is None:
70
+ params = {}
71
+
72
+ full_url = f"{self.base_url}{url}"
73
+ found = find_parameters_regex.findall(full_url)
74
+ url_params = {p: params[p] for p in found}
75
+ for p in found:
76
+ del params[p]
77
+ return full_url.format(**url_params)
78
+
79
+ def request(
80
+ self,
81
+ method: str,
82
+ url: str,
83
+ *args,
84
+ params: Optional[Dict[str, Any]] = None,
85
+ body: Optional[Dict[str, Any]] = None,
86
+ **kwargs
87
+ ) -> LsRestClientResponse: # pragma: no cover
88
+ """
89
+ :param method: The HTTP method to be used for the request.
90
+ :param url: The URL endpoint for the request.
91
+ :param args: Additional arguments for the request.
92
+ :param params: Optional query parameters for the request.
93
+ :param body: Optional request body in JSON format.
94
+ :param kwargs: Additional keyword arguments for the request.
95
+ :return: An instance of LsRestClientResponse.
96
+
97
+ This method sends an HTTP request using the provided method, URL, parameters, and body. It returns an instance of LsRestClientResponse which represents the response received from the server.
98
+
99
+ The `method` parameter specifies the HTTP method to be used for the request, e.g., 'GET', 'POST', 'PUT', 'DELETE', etc.
100
+
101
+ The `url` parameter specifies the URL endpoint for the request.
102
+
103
+ The `args` parameter is used to pass additional arguments to the underlying `Session.request` method. These arguments are passed directly to the `requests.request` function.
104
+
105
+ The `params` parameter is an optional dictionary of query parameters to be included in the request URL. This can be used to include query parameters in the URL, e.g., '/endpoint?key=value'.
106
+
107
+ The `body` parameter is an optional dictionary representing the request body in JSON format. This can be used to send data in the request body for methods like 'POST' or 'PUT'.
108
+
109
+ The `kwargs` parameter is used to pass additional keyword arguments to the underlying `Session.request` method. These keyword arguments are passed directly to the `requests.request` function.
110
+
111
+ The returned value is an instance of LsRestClientResponse, which represents the response received from the server. This object provides various properties and methods to access the response data.
112
+
113
+ Note: This method raises a `ConnectionError` if a connection error occurs during the request.
114
+ """
115
+ # apply base_headers
116
+ headers = self.base_headers | kwargs.get("headers", {})
117
+ kwargs |= dict(headers=headers)
118
+
119
+ # params
120
+ if params is None:
121
+ params = {}
122
+ if body is not None:
123
+ kwargs['data'] = lsjsonclasses.LSoftJSONEncoder.dumps(body).encode("utf8")
124
+
125
+ full_url = self.full_url(url, params)
126
+
127
+ try:
128
+ requests_response = requests.request(method.upper(), full_url, *args, params=params, **kwargs)
129
+ response = LsRestClientResponse.from_requests_response(requests_response)
130
+ return response
131
+
132
+ except requests.ConnectionError:
133
+ raise exceptions.ConnectionError(url=full_url)
134
+
135
+ def get(self, *args, **kwargs) -> LsRestClientResponse:
136
+ """
137
+ Send a GET request to the specified URL.
138
+
139
+ :param args: Positional arguments used to construct the URL.
140
+ :param kwargs: Additional keyword arguments for the request.
141
+ :return: The response object of type LsRestClientResponse.
142
+ """
143
+ return self.request('GET', *args, **kwargs)
144
+
145
+ def post(self, *args, **kwargs) -> LsRestClientResponse:
146
+ """
147
+ This method is used to send a POST request using the LSRestClient class.
148
+
149
+ :param args: The positional arguments for the POST request, including the URL and any additional parameters.
150
+ :param kwargs: The keyword arguments for the POST request, including headers, body, and any other parameters.
151
+ :return: An instance of LsRestClientResponse, representing the response from the POST request.
152
+ """
153
+ return self.request('POST', *args, **kwargs)
154
+
155
+ def put(self, *args, **kwargs) -> LsRestClientResponse:
156
+ """
157
+ This method is used to send a PUT request using the LSRestClient class.
158
+
159
+ :param args: The arguments to be passed to the request.
160
+ :param kwargs: The keyword arguments to be passed to the request.
161
+ :return: An instance of LsRestClientResponse representing the response from the server.
162
+ """
163
+ return self.request('PUT', *args, **kwargs)
164
+
165
+ def patch(self, *args, **kwargs) -> LsRestClientResponse:
166
+ """
167
+ Send a PATCH request to the specified URL with the provided arguments.
168
+
169
+ :param args: The arguments for the PATCH request.
170
+ :param kwargs: The keyword arguments for the PATCH request.
171
+ :return: The response from the PATCH request as an `LsRestClientResponse` object.
172
+ """
173
+ return self.request('PATCH', *args, **kwargs)
174
+
175
+ def delete(self, *args, **kwargs) -> LsRestClientResponse:
176
+ """
177
+ Deletes a resource using the DELETE method.
178
+
179
+ :param args: Variable length argument list.
180
+ :param kwargs: Arbitrary keyword arguments.
181
+ :return: An instance of LsRestClientResponse.
182
+ """
183
+ return self.request('DELETE', *args, **kwargs)
184
+
185
+ def options(self, *args, **kwargs) -> LsRestClientResponse:
186
+ """
187
+ Send an OPTIONS request using the LsRestClient.
188
+
189
+ :param args: Additional positional arguments to be passed to the request.
190
+ :param kwargs: Additional keyword arguments to be passed to the request.
191
+ :return: LsRestClientResponse object containing the response of the OPTIONS request.
192
+ """
193
+ return self.request('OPTIONS', *args, **kwargs)
194
+
195
+ def head(self, *args: object, **kwargs: object) -> object:
196
+ """
197
+ Send a HEAD request to the specified URL.
198
+
199
+ :param args: Positional arguments passed to the `request` method.
200
+ :param kwargs: Keyword arguments passed to the `request` method.
201
+ :return: The response object from the request.
202
+ """
203
+ return self.request('HEAD', *args, **kwargs)
@@ -0,0 +1,21 @@
1
+ from typing import Optional
2
+
3
+
4
+ class ConnectionError(Exception):
5
+ """Exception class for connection errors.
6
+
7
+ Args:
8
+ url (Optional[str]): The URL that the connection could not be established to.
9
+
10
+ Attributes:
11
+ url (Optional[str]): The URL that the connection could not be established to.
12
+
13
+ Raises:
14
+ ConnectionError: If a connection could not be established to the given URL.
15
+
16
+ """
17
+ url: str
18
+
19
+ def __init__(self, url: Optional[str] = None) -> None:
20
+ self.url = url
21
+ super().__init__(f"Connection could not be established to '{url}'")
@@ -0,0 +1,64 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+
4
+ import lsjsonclasses
5
+ from requests import Response
6
+ from requests.structures import CaseInsensitiveDict
7
+
8
+
9
+ @dataclass
10
+ class LsRestClientResponse:
11
+ """
12
+ Represents a response from the LsRestClient.
13
+ """
14
+
15
+ status_code: int
16
+ content: str
17
+ headers: CaseInsensitiveDict
18
+
19
+ _json: Optional[dict] = None
20
+
21
+ def json(self):
22
+ if self._json is None:
23
+ self._json = lsjsonclasses.LSoftJSONDecoder.loads(self.content)
24
+ return self._json
25
+
26
+ @classmethod
27
+ def from_requests_response(cls, response: Response):
28
+ """
29
+ Create an instance of LsRestClientResponse from a requests Response object.
30
+
31
+ :param response: The requests Response object.
32
+ :type response: Response
33
+ :return: An instance of LsRestClientResponse representing the response.
34
+ :rtype: LsRestClientResponse
35
+ """
36
+ return cls(
37
+ status_code=response.status_code,
38
+ content=response.content.decode("utf8"),
39
+ headers=response.headers,
40
+ )
41
+
42
+ @classmethod
43
+ def from_dict(cls, status_code: int = 200, data: dict = None, headers: CaseInsensitiveDict = None):
44
+ """
45
+ Converts a dictionary into an instance of the LsRestClientResponse class.
46
+
47
+ :param status_code: The HTTP status code. Defaults to 200.
48
+ :param data: The data dictionary. Defaults to None.
49
+ :param headers: The headers dictionary. Defaults to None.
50
+ :return: An instance of the LsRestClientResponse class.
51
+
52
+ """
53
+ if data is None:
54
+ data = {}
55
+ if headers is None:
56
+ headers = {}
57
+
58
+ content = lsjsonclasses.LSoftJSONEncoder.dumps(data)
59
+
60
+ return cls(
61
+ status_code=status_code,
62
+ content=content,
63
+ headers=headers
64
+ )
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "lsrestclient"
3
- version = "0.3.0"
3
+ version = "0.6.0"
4
4
  description = "REST Api Client"
5
5
  authors = ["mba <bartel@electronic-shop.lu>"]
6
6
  readme = "README.md"
@@ -1,114 +0,0 @@
1
- import re
2
- from dataclasses import dataclass
3
- from typing import Optional, Dict, Any
4
-
5
- import lsjsonclasses
6
- import requests
7
- from requests import Session, Response
8
- from requests.structures import CaseInsensitiveDict
9
-
10
- from lsrestclient import exceptions
11
-
12
- find_parameters_regex = re.compile('{(.*?)}')
13
-
14
-
15
- @dataclass
16
- class LsRestClientResponse:
17
- status_code: int
18
- content: str
19
- headers: CaseInsensitiveDict
20
-
21
- _json: Optional[dict] = None
22
-
23
- def json(self):
24
- if self._json is None:
25
- self._json = lsjsonclasses.LSoftJSONDecoder.loads(self.content)
26
- return self._json
27
-
28
- @classmethod
29
- def from_requests_response(cls, response: Response):
30
- return cls(
31
- status_code=response.status_code,
32
- content=response.content.decode("utf8"),
33
- headers=response.headers,
34
- )
35
-
36
-
37
- class LsRestClient(Session):
38
- _clients = {}
39
-
40
- @classmethod
41
- def client(cls, name):
42
- try:
43
- return cls._clients[name]
44
- except KeyError:
45
- raise Exception(f"LsRestClient with name '{name}' not initialized.")
46
-
47
- def __init__(self, base_url: str = None, name: str = "default") -> None:
48
- self.base_url = base_url
49
- self.base_headers = {'content-type': 'application/json'}
50
- self.name = name
51
- super().__init__()
52
- self._clients[name] = self
53
-
54
- def full_url(self, url: str, params: Optional[dict] = None) -> str:
55
- if params is None:
56
- params = {}
57
-
58
- full_url = f"{self.base_url}{url}"
59
- found = find_parameters_regex.findall(full_url)
60
- url_params = {p: params[p] for p in found}
61
- for p in found:
62
- del params[p]
63
- return full_url.format(**url_params)
64
-
65
- def request(
66
- self,
67
- method: str,
68
- url: str,
69
- *args,
70
- params: Optional[Dict[str, Any]] = None,
71
- body: Optional[Dict[str, Any]] = None,
72
- **kwargs
73
- ) -> LsRestClientResponse: # pragma: no cover
74
-
75
- # apply base_headers
76
- headers = self.base_headers | kwargs.get("headers", {})
77
- kwargs |= dict(headers=headers)
78
-
79
- # params
80
- if params is None:
81
- params = {}
82
- if body is not None:
83
- kwargs['data'] = lsjsonclasses.LSoftJSONEncoder.dumps(body).encode("utf8")
84
-
85
- full_url = self.full_url(url, params)
86
-
87
- try:
88
- requests_response = requests.request(method.upper(), full_url, *args, params=params, **kwargs)
89
- response = LsRestClientResponse.from_requests_response(requests_response)
90
- return response
91
-
92
- except requests.ConnectionError:
93
- raise exceptions.ConnectionError(url=full_url)
94
-
95
- def get(self, *args, **kwargs) -> LsRestClientResponse:
96
- return self.request('GET', *args, **kwargs)
97
-
98
- def post(self, *args, **kwargs) -> LsRestClientResponse:
99
- return self.request('POST', *args, **kwargs)
100
-
101
- def put(self, *args, **kwargs) -> LsRestClientResponse:
102
- return self.request('PUT', *args, **kwargs)
103
-
104
- def patch(self, *args, **kwargs) -> LsRestClientResponse:
105
- return self.request('PATCH', *args, **kwargs)
106
-
107
- def delete(self, *args, **kwargs) -> LsRestClientResponse:
108
- return self.request('DELETE', *args, **kwargs)
109
-
110
- def options(self, *args, **kwargs) -> LsRestClientResponse:
111
- return self.request('OPTIONS', *args, **kwargs)
112
-
113
- def head(self, *args, **kwargs):
114
- return self.request('HEAD', *args, **kwargs)
@@ -1,5 +0,0 @@
1
- class ConnectionError(Exception):
2
-
3
- def __init__(self, *args: object, url: str | None = None) -> None:
4
- self.url = url
5
- super().__init__(f"Connection could not be established to '{url}'", *args)
File without changes