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.
- osc_sdk_python/VERSION +1 -0
- osc_sdk_python/__init__.py +31 -0
- osc_sdk_python/authentication.py +176 -0
- osc_sdk_python/call.py +96 -0
- osc_sdk_python/credentials.py +186 -0
- osc_sdk_python/limiter.py +29 -0
- osc_sdk_python/outscale_gateway.py +322 -0
- osc_sdk_python/problem.py +100 -0
- osc_sdk_python/requester.py +29 -0
- osc_sdk_python/resources/gateway_errors.yaml +1220 -0
- osc_sdk_python/resources/outscale.yaml +25166 -0
- osc_sdk_python/retry.py +138 -0
- osc_sdk_python/version.py +7 -0
- osc_sdk_python-0.39.2.dist-info/METADATA +259 -0
- osc_sdk_python-0.39.2.dist-info/RECORD +17 -0
- osc_sdk_python-0.39.2.dist-info/WHEEL +4 -0
- osc_sdk_python-0.39.2.dist-info/licenses/LICENSE +29 -0
osc_sdk_python/retry.py
ADDED
|
@@ -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,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
|
+
[](https://docs.outscale.com/en/userguide/Open-Source-Projects.html) [](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,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.
|