lsrestclient 0.3.0__tar.gz → 0.6.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -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