osc_sdk_python 0.39.2__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.
@@ -0,0 +1,138 @@
1
+ import requests
2
+ import time
3
+ import random
4
+ from requests.exceptions import JSONDecodeError
5
+ from .problem import ProblemDecoder, LegacyProblemDecoder, LegacyProblem, Problem
6
+
7
+ MAX_RETRIES = 3
8
+ RETRY_BACKOFF_FACTOR = 1.0
9
+ RETRY_BACKOFF_JITTER = 3.0
10
+ RETRY_BACKOFF_MAX = 30.0
11
+
12
+
13
+ class Retry:
14
+ """
15
+ Hold a request attempt and try to execute it
16
+ """
17
+
18
+ def __init__(self, session: requests.Session, method: str, url: str, **kwargs):
19
+ self.session = session
20
+ self.method: str = method
21
+ self.url: str = url
22
+ self.request_kwargs = kwargs
23
+
24
+ # Extract all retry parameters
25
+ self.attempt: int = int(self.request_kwargs.pop("attempt", 0))
26
+ self.max_retries: int = int(
27
+ self.request_kwargs.get("max_retries", MAX_RETRIES)
28
+ )
29
+ self.backoff_factor: float = float(
30
+ self.request_kwargs.get("backoff_factor", RETRY_BACKOFF_FACTOR)
31
+ )
32
+ self.backoff_jitter: float = float(
33
+ self.request_kwargs.get("backoff_jitter", RETRY_BACKOFF_JITTER)
34
+ )
35
+ self.backoff_max: float = float(
36
+ self.request_kwargs.get("backoff_max", RETRY_BACKOFF_MAX)
37
+ )
38
+
39
+ def execute_once(self) -> requests.Response:
40
+ """
41
+ Execute the request without retry
42
+ """
43
+ return self.session.request(self.method, self.url, **self.request_kwargs)
44
+
45
+ def increment(self) -> "Retry":
46
+ """
47
+ Return a copy of the retry with an incremented attempt count
48
+ """
49
+ new_kwargs = self.request_kwargs.copy()
50
+ new_kwargs["attempt"] = self.attempt + 1
51
+ return Retry(self.session, self.method, self.url, **new_kwargs)
52
+
53
+ def should_retry(self, e: requests.exceptions.RequestException) -> bool:
54
+ if isinstance(e, requests.exceptions.TooManyRedirects):
55
+ return False
56
+
57
+ if isinstance(e, requests.exceptions.URLRequired):
58
+ return False
59
+
60
+ if isinstance(e, ValueError):
61
+ # can be raised on bogus request
62
+ return False
63
+
64
+ if e.response is not None:
65
+ if 400 <= e.response.status_code < 500 and e.response.status_code != 429:
66
+ return False
67
+
68
+ return self.attempt < self.max_retries
69
+
70
+ def get_backoff_time(self) -> float:
71
+ """
72
+ {backoff factor} * (2 ** ({number of previous retries}))
73
+ random.uniform(0, {backoff jitter})
74
+ """
75
+
76
+ backoff: float = self.backoff_factor * (2**self.attempt)
77
+ backoff += random.uniform(0, self.backoff_jitter)
78
+ return min(backoff, self.backoff_max)
79
+
80
+ def execute(self) -> requests.Response:
81
+ try:
82
+ res = self.execute_once()
83
+ raise_for_status(res)
84
+ return res
85
+ except requests.exceptions.RequestException as e:
86
+ if self.should_retry(e):
87
+ sleep_time = self.get_backoff_time()
88
+ time.sleep(sleep_time)
89
+ return self.increment().execute()
90
+ else:
91
+ raise e
92
+
93
+
94
+ def raise_for_status(response: requests.Response):
95
+ http_error_msg = ""
96
+ problem = None
97
+ reason = get_default_reason(response)
98
+
99
+ try:
100
+ ct = response.headers.get("content-type") or ""
101
+ if "application/json" in ct:
102
+ problem = response.json(cls=LegacyProblemDecoder)
103
+ problem.status = problem.status or str(response.status_code)
104
+ problem.url = response.url
105
+ elif "application/problem+json" in ct:
106
+ problem = response.json(cls=ProblemDecoder)
107
+ problem.status = problem.status or str(response.status_code)
108
+ except JSONDecodeError:
109
+ pass
110
+ else:
111
+ if 400 <= response.status_code < 500:
112
+ if isinstance(problem, LegacyProblem) or isinstance(problem, Problem):
113
+ http_error_msg = f"Client Error --> {problem.msg()}"
114
+ else:
115
+ http_error_msg = f"{response.status_code} Client Error: {reason} for url: {response.url}"
116
+
117
+ elif 500 <= response.status_code < 600:
118
+ if isinstance(problem, LegacyProblem) or isinstance(problem, Problem):
119
+ http_error_msg = f"Server Error --> {problem.msg()}"
120
+ else:
121
+ http_error_msg = f"{response.status_code} Server Error: {reason} for url: {response.url}"
122
+
123
+ if http_error_msg:
124
+ raise requests.HTTPError(http_error_msg, response=response)
125
+
126
+
127
+ def get_default_reason(response):
128
+ if isinstance(response.reason, bytes):
129
+ # We attempt to decode utf-8 first because some servers
130
+ # choose to localize their reason strings. If the string
131
+ # isn't utf-8, we fall back to iso-8859-1 for all other
132
+ # encodings. (See PR #3538)
133
+ try:
134
+ return response.reason.decode("utf-8")
135
+ except UnicodeDecodeError:
136
+ return response.reason.decode("iso-8859-1")
137
+ else:
138
+ return response.reason
@@ -0,0 +1,7 @@
1
+ import os
2
+
3
+
4
+ def get_version() -> str:
5
+ osc_sdk_python_path = os.path.dirname(os.path.abspath(__file__))
6
+ with open(os.path.join(osc_sdk_python_path, "VERSION"), "r") as fd:
7
+ return fd.read().strip()
@@ -0,0 +1,259 @@
1
+ Metadata-Version: 2.4
2
+ Name: osc_sdk_python
3
+ Version: 0.39.2
4
+ Summary: Outscale Gateway python SDK
5
+ Project-URL: Homepage, https://github.com/outscale/osc-sdk-python
6
+ Author-email: Outscal SAS <opensource@outscale.com>
7
+ License: BSD
8
+ License-File: LICENSE
9
+ Classifier: License :: OSI Approved :: BSD License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: requests>=2.20.0
19
+ Requires-Dist: ruamel-yaml==0.19.1
20
+ Requires-Dist: urllib3>=2.6.3
21
+ Description-Content-Type: text/markdown
22
+
23
+ [![Project Graduated](https://docs.outscale.com/fr/userguide/_images/Project-Graduated-green.svg)](https://docs.outscale.com/en/userguide/Open-Source-Projects.html) [![](https://dcbadge.limes.pink/api/server/HUVtY5gT6s?style=flat&theme=default-inverted)](https://discord.gg/HUVtY5gT6s)
24
+
25
+ # Outscale Python SDK
26
+
27
+ <p align="center">
28
+ <img alt="Outscale Python SDK" src="https://img.icons8.com/?size=100&id=2866&format=png&color=000000" width="100px">
29
+ </p>
30
+
31
+ ---
32
+
33
+ ## 🌐 Links
34
+
35
+ - Documentation: <https://docs.outscale.com/api.html?python#3ds-outscale-api>
36
+ - Project repository: <https://github.com/outscale/osc-sdk-python>
37
+ - Outscale website: <https://outscale.com/>
38
+ - Join our community on [Discord](https://discord.gg/HUVtY5gT6s)
39
+
40
+ ---
41
+
42
+ ## 📄 Table of Contents
43
+
44
+ - [Overview](#-overview)
45
+ - [Requirements](#-requirements)
46
+ - [Installation](#-installation)
47
+ - [Configuration](#-configuration)
48
+ - [Usage](#-usage)
49
+ - [Examples](#-examples)
50
+ - [Known Issues & Troubleshooting](#-known-issues--troubleshooting)
51
+ - [License](#-license)
52
+ - [Contributing](#-contributing)
53
+
54
+ ---
55
+
56
+ ## 🧭 Overview
57
+
58
+ **Outscale Python SDK** (`osc-sdk-python`) is the official Python SDK to interact with the [OUTSCALE API](https://docs.outscale.com/api.html?python#3ds-outscale-api).
59
+
60
+ It allows you to:
61
+
62
+ - Configure multiple profiles through environment variables or credential files.
63
+ - Customize retry and rate-limit behavior.
64
+ - Enable detailed logging of requests and responses.
65
+
66
+ You will need an Outscale account and API credentials. If you do not have one yet, please visit the [Outscale website](https://outscale.com/).
67
+
68
+ ---
69
+
70
+ ## ✅ Requirements
71
+
72
+ - Python 3.x
73
+ - `pip` (Python package manager)
74
+ - Access to the OUTSCALE API (valid access key / secret key or basic auth)
75
+ - Network access to the Outscale API endpoints
76
+
77
+ ---
78
+
79
+ ## ⚙ Installation
80
+
81
+ ### Option 1: Install from PyPI
82
+
83
+ Install the pre-built Python package:
84
+
85
+ ```bash
86
+ pip install osc-sdk-python
87
+ ````
88
+
89
+ ### Option 2: Install from source
90
+
91
+ Clone the repository and build the package:
92
+
93
+ ```bash
94
+ git clone https://github.com/outscale/osc-sdk-python.git
95
+ cd osc-sdk-python
96
+ make package
97
+ ```
98
+
99
+ Then install the built wheel:
100
+
101
+ ```bash
102
+ pip install dist/osc_sdk_python-0.39.2-py3-none-any.whl
103
+ ```
104
+
105
+ ---
106
+
107
+ ## 🛠 Configuration
108
+
109
+ When you use the SDK, you can choose a **profile**. Profiles can be defined via **environment variables** or in a **credentials file**.
110
+ Environment variables take precedence over files.
111
+
112
+ In the credentials file, you can set a default profile named `default`. It will be used if you do not explicitly specify a profile.
113
+
114
+ ### Environment variables
115
+
116
+ The SDK understands the following environment variables:
117
+
118
+ ```bash
119
+ export OSC_PROFILE=<PROFILE> # default: "default"
120
+
121
+ # or explicit credentials:
122
+ export OSC_ACCESS_KEY=<ACCESS_KEY>
123
+ export OSC_SECRET_KEY=<SECRET_KEY>
124
+
125
+ # optional:
126
+ export OSC_REGION=<REGION> # default: eu-west-2
127
+ export OSC_MAX_RETRIES=<INT> # default: 3
128
+ export OSC_RETRY_BACKOFF_FACTOR=<FLOAT> # default: 1.0
129
+ export OSC_RETRY_BACKOFF_JITTER=<FLOAT> # default: 3.0
130
+ export OSC_RETRY_BACKOFF_MAX=<FLOAT> # default: 30
131
+ ```
132
+
133
+ ### Credentials files
134
+
135
+ By default, the SDK looks for a JSON configuration file at `~/.osc/config.json`.
136
+
137
+ Example:
138
+
139
+ ```json
140
+ {
141
+ "default": {
142
+ "access_key": "<ACCESS_KEY>",
143
+ "secret_key": "<SECRET_KEY>",
144
+ "region": "<REGION>"
145
+ },
146
+ "profile_1": {
147
+ "access_key": "<ACCESS_KEY>",
148
+ "secret_key": "<SECRET_KEY>",
149
+ "region": "<REGION>"
150
+ },
151
+ "profile_2": {
152
+ "access_key": "<ACCESS_KEY>",
153
+ "secret_key": "<SECRET_KEY>",
154
+ "region": "<REGION>"
155
+ }
156
+ }
157
+ ```
158
+
159
+ Notes:
160
+
161
+ * If `~/.osc/config.json` is not found, `~/.oapi_credentials` will be used as a fallback.
162
+ * Environment variables have priority over credentials files.
163
+
164
+ ### Basic Authentication
165
+
166
+ You can also use `osc-sdk-python` with **basic authentication** using your account email and password.
167
+ Note that some API calls may be blocked with this method. See the [authentication documentation](https://docs.outscale.com/api#authentication) for more details.
168
+
169
+ Example:
170
+
171
+ ```python
172
+ from osc_sdk_python import Gateway
173
+
174
+ with Gateway(email="your@email.com", password="yourAccountPassword") as gw:
175
+ keys = gw.ReadAccessKeys()
176
+ ```
177
+
178
+ ### Retry Options
179
+
180
+ The following options can be provided when initializing the `Gateway` to customize the retry behavior of the SDK:
181
+
182
+ * `max_retries` (integer, default `3`)
183
+ * `retry_backoff_factor` (float, default `1.0`)
184
+ * `retry_backoff_jitter` (float, default `3.0`)
185
+ * `retry_backoff_max` (float, default `30`)
186
+
187
+ These correspond to their counterparts in [`urllib3.util.Retry`](https://urllib3.readthedocs.io/en/stable/reference/urllib3.util.html#urllib3.util.Retry).
188
+
189
+ Example:
190
+
191
+ ```python
192
+ from osc_sdk_python import Gateway
193
+
194
+ gw = Gateway(
195
+ max_retries=5,
196
+ retry_backoff_factor=0.5,
197
+ retry_backoff_jitter=1.0,
198
+ retry_backoff_max=120,
199
+ )
200
+ ```
201
+
202
+ ### Rate Limit Options
203
+
204
+ You can also configure rate limiting when initializing the `Gateway`:
205
+
206
+ * `limiter_max_requests` (integer, default `5`)
207
+ * `limiter_window` (integer, default `1`)
208
+
209
+ Example:
210
+
211
+ ```python
212
+ from osc_sdk_python import Gateway
213
+
214
+ gw = Gateway(
215
+ limiter_max_requests=20,
216
+ limiter_window=5,
217
+ )
218
+ ```
219
+
220
+ More usage patterns and logging examples are documented in:
221
+
222
+ * [docs/examples.md](docs/examples.md)
223
+
224
+ ---
225
+
226
+ ## 🧪 Examples
227
+
228
+ Some example topics covered in `docs/examples.md`:
229
+
230
+ * Listing VMs and volumes
231
+ * Using profiles and regions
232
+ * Raw calls with `gw.raw("ActionName", **params)`
233
+ * Enabling and reading logs
234
+
235
+ ---
236
+
237
+ ## 🧩 Known Issues & Troubleshooting
238
+
239
+ Common issues (such as UTF-8 / locale errors when reading the API spec) and their workarounds are documented in:
240
+
241
+ * [docs/troubleshooting.md](docs/troubleshooting.md)
242
+
243
+ ---
244
+
245
+ ## 📜 License
246
+
247
+ **Outscale Python SDK** is released under the BSD-3-Clause.
248
+
249
+ © 2025 OUTSCALE SAS
250
+
251
+ See [LICENSE](./LICENSE) for full details.
252
+
253
+ ---
254
+
255
+ ## 🤝 Contributing
256
+
257
+ We welcome contributions!
258
+
259
+ Please read our [Contributing Guidelines](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) before submitting a pull request.
@@ -0,0 +1,17 @@
1
+ osc_sdk_python/VERSION,sha256=HK3l2Xm_A7QsVN4d-o0_r11-a8uYN5Dy9Cfyem1B3BQ,7
2
+ osc_sdk_python/__init__.py,sha256=kAIzZAGD6A--uVJObIdSlFRG3MdCt_30SmE-bMt1uyk,766
3
+ osc_sdk_python/authentication.py,sha256=FKcLo9GPjFPGswqFkxBUDbjxTGaHQFPImLiUj0PJK4U,6571
4
+ osc_sdk_python/call.py,sha256=Ed0ROJC1ORxz-lOgiZFCkBzEs7i0ov6GYGDrr2PVsOY,3290
5
+ osc_sdk_python/credentials.py,sha256=98Y0WD81jPOzYRg6WurMO-iyk-dBET5tFNzEFV9sb3s,7202
6
+ osc_sdk_python/limiter.py,sha256=udIFlBeXFGabF7YkWLiJsDMmyQfYlglJo_-EEwQm-GQ,913
7
+ osc_sdk_python/outscale_gateway.py,sha256=xF324XYX2s3b1YrN1_PO5iypJn26fHXMiuKwN5J3lx8,9971
8
+ osc_sdk_python/problem.py,sha256=ScgRAK79yrhZFb1WUIVnt9c6byygnQyqZTp5S5heYO0,3095
9
+ osc_sdk_python/requester.py,sha256=7LPCNzw0qq-alJJVHw-07C27jGw3XO9Ioq2hwaNgbm8,914
10
+ osc_sdk_python/retry.py,sha256=5y1sK8kSjylh0yoMZrhL-aqNTikf1J_isSkyHs4lgL4,4893
11
+ osc_sdk_python/version.py,sha256=R1dZvsRZ0qORIvX1l2RMZjrwCSF9MuM1nzvVCZb8aU0,212
12
+ osc_sdk_python/resources/gateway_errors.yaml,sha256=7RbhfR_u_VCN9gln-_OxIoOve3nI31M94glycMb8b3M,24496
13
+ osc_sdk_python/resources/outscale.yaml,sha256=hlBUZMTJEWCgdQsCkxi_vJUNzdFIaOZ6Gt8dABa42Zg,962417
14
+ osc_sdk_python-0.39.2.dist-info/METADATA,sha256=QOxGUuYHmm3YWlDlIpZYnqjXn7SuCiJAtIp5Bo-7IQA,6835
15
+ osc_sdk_python-0.39.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
+ osc_sdk_python-0.39.2.dist-info/licenses/LICENSE,sha256=eAtKefmWuj4G96ORsmNbY1PqGZj2sx5SkgiNYL5LocU,1512
17
+ osc_sdk_python-0.39.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2019, Outscale SAS
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ * Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.