unlyx 1.0.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.
- unlyx-1.0.0/LICENSE +21 -0
- unlyx-1.0.0/PKG-INFO +182 -0
- unlyx-1.0.0/README.md +153 -0
- unlyx-1.0.0/pyproject.toml +45 -0
- unlyx-1.0.0/setup.cfg +4 -0
- unlyx-1.0.0/tests/test_client.py +303 -0
- unlyx-1.0.0/tests/test_webhook.py +62 -0
- unlyx-1.0.0/unlyx/__init__.py +34 -0
- unlyx-1.0.0/unlyx/_version.py +1 -0
- unlyx-1.0.0/unlyx/client.py +411 -0
- unlyx-1.0.0/unlyx/errors.py +45 -0
- unlyx-1.0.0/unlyx/models.py +293 -0
- unlyx-1.0.0/unlyx/py.typed +0 -0
- unlyx-1.0.0/unlyx/transport.py +91 -0
- unlyx-1.0.0/unlyx/webhook.py +127 -0
- unlyx-1.0.0/unlyx.egg-info/PKG-INFO +182 -0
- unlyx-1.0.0/unlyx.egg-info/SOURCES.txt +18 -0
- unlyx-1.0.0/unlyx.egg-info/dependency_links.txt +1 -0
- unlyx-1.0.0/unlyx.egg-info/requires.txt +3 -0
- unlyx-1.0.0/unlyx.egg-info/top_level.txt +1 -0
unlyx-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 UNIVERSALHOMEANDTECH LTD (Unlyx)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
unlyx-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: unlyx
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Official Python SDK for the Unlyx REST API v1 (account, services, orders, webhooks, usage).
|
|
5
|
+
Author: Unlyx
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://unlyx.com/developers
|
|
8
|
+
Project-URL: Source, https://github.com/AktherHossion/unlyx-python
|
|
9
|
+
Project-URL: Issues, https://github.com/AktherHossion/unlyx-python/issues
|
|
10
|
+
Keywords: unlyx,imei,unlock,reseller,gsm,api,sdk,webhooks
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Provides-Extra: test
|
|
27
|
+
Requires-Dist: pytest>=7; extra == "test"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# unlyx (Python)
|
|
31
|
+
|
|
32
|
+
Official Python SDK for the **Unlyx REST API v1** — balance, services, orders (single and bulk), cancel, webhooks and usage for any Unlyx shop.
|
|
33
|
+
|
|
34
|
+
- Python 3.9+, standard library only. Fully typed (`py.typed`).
|
|
35
|
+
- HTTPS only, TLS verified, redirects never followed.
|
|
36
|
+
- Every POST carries an `Idempotency-Key`; retries re-send the same key, so a network hiccup can never charge an order twice.
|
|
37
|
+
- One exception type: `unlyx.UnlyxError`.
|
|
38
|
+
- `unlyx.verify_webhook()` checks signed webhook deliveries.
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install unlyx
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Until the package is on PyPI, install it from a copy of this folder: `pip install ./sdks/python`.
|
|
47
|
+
|
|
48
|
+
## Quick start
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
import os
|
|
52
|
+
import unlyx
|
|
53
|
+
|
|
54
|
+
client = unlyx.Client("https://yourshop.com", os.environ["UNLYX_API_KEY"])
|
|
55
|
+
|
|
56
|
+
account = client.account()
|
|
57
|
+
print(account.balance, account.currency)
|
|
58
|
+
|
|
59
|
+
for service in client.services("imei"):
|
|
60
|
+
print(service.id, service.name, service.price, service.currency)
|
|
61
|
+
|
|
62
|
+
# Use your own order id as the idempotency key: running the same code twice
|
|
63
|
+
# returns the first order, never a second charge.
|
|
64
|
+
order = client.place_order(42, "356938035643809", idempotency_key="my-order-1001")
|
|
65
|
+
print(order.reference, order.status)
|
|
66
|
+
|
|
67
|
+
order = client.order(order.reference)
|
|
68
|
+
if order.is_success:
|
|
69
|
+
print("Result:", order.reply)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Get a key in the shop: **Account → API**. Tick *Sandbox* to build without spending money — sandbox orders get a `SANDBOX-` reference and never reach a supplier.
|
|
73
|
+
|
|
74
|
+
## All methods
|
|
75
|
+
|
|
76
|
+
| Method | Endpoint | Returns |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `account()` | `GET /account` | `Account` |
|
|
79
|
+
| `usage(days=7)` | `GET /usage` | `Usage` |
|
|
80
|
+
| `services(type=None)` | `GET /services` | `list[Service]` |
|
|
81
|
+
| `service(id)` | `GET /services/{id}` | `Service` |
|
|
82
|
+
| `place_order(service, imei=None, *, quantity, fields, note, file, idempotency_key)` | `POST /orders` | `Order` (`replayed`, `balance` set) |
|
|
83
|
+
| `place_orders([...])` | `POST /orders/bulk` | `BulkResult` |
|
|
84
|
+
| `orders(*, status, since, imei, page, per_page)` | `GET /orders` | `OrderPage` |
|
|
85
|
+
| `order(reference)` | `GET /orders/{reference}` | `Order` |
|
|
86
|
+
| `cancel_order(reference)` | `POST /orders/{reference}/cancel` | `Order` |
|
|
87
|
+
| `webhooks()` | `GET /webhooks` | `list[WebhookEndpoint]` |
|
|
88
|
+
| `create_webhook(url, events=None, name=None)` | `POST /webhooks` | `WebhookEndpoint` (`secret` set once) |
|
|
89
|
+
| `delete_webhook(id)` | `DELETE /webhooks/{id}` | `bool` |
|
|
90
|
+
| `request(method, path, query=None, body=None, headers=None)` | anything under `/api/v1` | decoded JSON envelope |
|
|
91
|
+
|
|
92
|
+
Money (`balance`, `price`) is kept as an exact decimal string — use `decimal.Decimal(order.price)` for maths.
|
|
93
|
+
|
|
94
|
+
Bulk: up to 100 lines; each succeeds or fails on its own. A line's `reference` is its idempotency key; lines without one get a random reference so the SDK's retries are safe.
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
result = client.place_orders([
|
|
98
|
+
{"reference": "batch7-1", "service": 42, "imei": "356938035643809"},
|
|
99
|
+
{"reference": "batch7-2", "service": 42, "imei": "359070071234567"},
|
|
100
|
+
])
|
|
101
|
+
for line in result.failed:
|
|
102
|
+
print(line.reference, line.error_message)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Options
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
client = unlyx.Client(
|
|
109
|
+
"https://yourshop.com", key,
|
|
110
|
+
timeout=30, connect_timeout=10,
|
|
111
|
+
max_retries=2, # 0 turns retries off
|
|
112
|
+
max_retry_wait=60, # never sleep longer than this for a Retry-After
|
|
113
|
+
signing_key_id=None, # optional request signing, see below
|
|
114
|
+
signing_secret=None,
|
|
115
|
+
ca_bundle=None, # path to a CA bundle if the system has none
|
|
116
|
+
user_agent="my-panel/2.3",
|
|
117
|
+
)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### When the SDK retries
|
|
121
|
+
|
|
122
|
+
| Situation | GET | POST |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| Connection failed before anything was sent (DNS, refused, TLS) | retried | retried, same Idempotency-Key |
|
|
125
|
+
| HTTP 429 with `Retry-After` (≤ `max_retry_wait`) | retried after that wait | retried after that wait, same key |
|
|
126
|
+
| HTTP 5xx | retried with backoff | **not retried** (except below) |
|
|
127
|
+
| HTTP 503 with error code `temporary` (shop database busy, nothing placed) | retried | retried, same key |
|
|
128
|
+
| Timeout / connection lost after sending | retried | **not retried** — check the order, or repeat with the same key |
|
|
129
|
+
|
|
130
|
+
### Request signing (optional)
|
|
131
|
+
|
|
132
|
+
If the shop turned on signed requests for your key, pass `signing_key_id` and `signing_secret`. Every request then carries `X-Key-Id`, `X-Timestamp` (unix seconds) and `X-Signature` = hex HMAC-SHA256 of `"{METHOD}\n{path}\n{timestamp}\n{rawBody}"` (path without the query string, e.g. `/api/v1/orders`; empty body for GET).
|
|
133
|
+
|
|
134
|
+
## Errors
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
try:
|
|
138
|
+
client.place_order(42, "356938035643809", idempotency_key="my-order-1002")
|
|
139
|
+
except unlyx.UnlyxError as e:
|
|
140
|
+
e.code # "low_balance", "invalid_input", "not_found", "service_disabled", "rate_limited", "connection_error" …
|
|
141
|
+
e.http_status # 402, 404, 409, 422, 429 … (0 = no response)
|
|
142
|
+
e.message # readable text from the shop
|
|
143
|
+
e.reference # short support reference — quote it to the shop
|
|
144
|
+
e.retry_after # seconds, on 429
|
|
145
|
+
e.body # decoded JSON body
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Webhooks
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
# Flask
|
|
152
|
+
import os
|
|
153
|
+
from flask import Flask, request, abort
|
|
154
|
+
import unlyx
|
|
155
|
+
|
|
156
|
+
app = Flask(__name__)
|
|
157
|
+
|
|
158
|
+
@app.post("/hooks/unlyx")
|
|
159
|
+
def unlyx_hook():
|
|
160
|
+
try:
|
|
161
|
+
event = unlyx.verify_webhook(request.get_data(), request.headers, os.environ["UNLYX_WEBHOOK_SECRET"])
|
|
162
|
+
except unlyx.UnlyxError:
|
|
163
|
+
abort(400)
|
|
164
|
+
# event["event"] == "order.completed"; event["data"]["reference"], event["data"]["reply"] …
|
|
165
|
+
# Use the X-Unlyx-Delivery header to ignore a delivery you already handled.
|
|
166
|
+
return "", 200
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Always pass the **raw** body bytes. `verify_webhook` checks `X-Unlyx-Signature` (`t=<unix>,v1=<hex HMAC-SHA256 of "<unix>.<body>">`), refuses timestamps more than 5 minutes off (`tolerance=`), and accepts a list of secrets while you rotate.
|
|
170
|
+
|
|
171
|
+
## Tests
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
pip install pytest
|
|
175
|
+
pytest # or: python -m unittest discover -s tests
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The suite uses a mocked transport and never calls a real shop. Plug in your own HTTP stack by passing any object with a `send(HttpRequest) -> HttpResponse` method as `transport=`.
|
|
179
|
+
|
|
180
|
+
## License
|
|
181
|
+
|
|
182
|
+
MIT
|
unlyx-1.0.0/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# unlyx (Python)
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the **Unlyx REST API v1** — balance, services, orders (single and bulk), cancel, webhooks and usage for any Unlyx shop.
|
|
4
|
+
|
|
5
|
+
- Python 3.9+, standard library only. Fully typed (`py.typed`).
|
|
6
|
+
- HTTPS only, TLS verified, redirects never followed.
|
|
7
|
+
- Every POST carries an `Idempotency-Key`; retries re-send the same key, so a network hiccup can never charge an order twice.
|
|
8
|
+
- One exception type: `unlyx.UnlyxError`.
|
|
9
|
+
- `unlyx.verify_webhook()` checks signed webhook deliveries.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install unlyx
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Until the package is on PyPI, install it from a copy of this folder: `pip install ./sdks/python`.
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import os
|
|
23
|
+
import unlyx
|
|
24
|
+
|
|
25
|
+
client = unlyx.Client("https://yourshop.com", os.environ["UNLYX_API_KEY"])
|
|
26
|
+
|
|
27
|
+
account = client.account()
|
|
28
|
+
print(account.balance, account.currency)
|
|
29
|
+
|
|
30
|
+
for service in client.services("imei"):
|
|
31
|
+
print(service.id, service.name, service.price, service.currency)
|
|
32
|
+
|
|
33
|
+
# Use your own order id as the idempotency key: running the same code twice
|
|
34
|
+
# returns the first order, never a second charge.
|
|
35
|
+
order = client.place_order(42, "356938035643809", idempotency_key="my-order-1001")
|
|
36
|
+
print(order.reference, order.status)
|
|
37
|
+
|
|
38
|
+
order = client.order(order.reference)
|
|
39
|
+
if order.is_success:
|
|
40
|
+
print("Result:", order.reply)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Get a key in the shop: **Account → API**. Tick *Sandbox* to build without spending money — sandbox orders get a `SANDBOX-` reference and never reach a supplier.
|
|
44
|
+
|
|
45
|
+
## All methods
|
|
46
|
+
|
|
47
|
+
| Method | Endpoint | Returns |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `account()` | `GET /account` | `Account` |
|
|
50
|
+
| `usage(days=7)` | `GET /usage` | `Usage` |
|
|
51
|
+
| `services(type=None)` | `GET /services` | `list[Service]` |
|
|
52
|
+
| `service(id)` | `GET /services/{id}` | `Service` |
|
|
53
|
+
| `place_order(service, imei=None, *, quantity, fields, note, file, idempotency_key)` | `POST /orders` | `Order` (`replayed`, `balance` set) |
|
|
54
|
+
| `place_orders([...])` | `POST /orders/bulk` | `BulkResult` |
|
|
55
|
+
| `orders(*, status, since, imei, page, per_page)` | `GET /orders` | `OrderPage` |
|
|
56
|
+
| `order(reference)` | `GET /orders/{reference}` | `Order` |
|
|
57
|
+
| `cancel_order(reference)` | `POST /orders/{reference}/cancel` | `Order` |
|
|
58
|
+
| `webhooks()` | `GET /webhooks` | `list[WebhookEndpoint]` |
|
|
59
|
+
| `create_webhook(url, events=None, name=None)` | `POST /webhooks` | `WebhookEndpoint` (`secret` set once) |
|
|
60
|
+
| `delete_webhook(id)` | `DELETE /webhooks/{id}` | `bool` |
|
|
61
|
+
| `request(method, path, query=None, body=None, headers=None)` | anything under `/api/v1` | decoded JSON envelope |
|
|
62
|
+
|
|
63
|
+
Money (`balance`, `price`) is kept as an exact decimal string — use `decimal.Decimal(order.price)` for maths.
|
|
64
|
+
|
|
65
|
+
Bulk: up to 100 lines; each succeeds or fails on its own. A line's `reference` is its idempotency key; lines without one get a random reference so the SDK's retries are safe.
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
result = client.place_orders([
|
|
69
|
+
{"reference": "batch7-1", "service": 42, "imei": "356938035643809"},
|
|
70
|
+
{"reference": "batch7-2", "service": 42, "imei": "359070071234567"},
|
|
71
|
+
])
|
|
72
|
+
for line in result.failed:
|
|
73
|
+
print(line.reference, line.error_message)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Options
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
client = unlyx.Client(
|
|
80
|
+
"https://yourshop.com", key,
|
|
81
|
+
timeout=30, connect_timeout=10,
|
|
82
|
+
max_retries=2, # 0 turns retries off
|
|
83
|
+
max_retry_wait=60, # never sleep longer than this for a Retry-After
|
|
84
|
+
signing_key_id=None, # optional request signing, see below
|
|
85
|
+
signing_secret=None,
|
|
86
|
+
ca_bundle=None, # path to a CA bundle if the system has none
|
|
87
|
+
user_agent="my-panel/2.3",
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### When the SDK retries
|
|
92
|
+
|
|
93
|
+
| Situation | GET | POST |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| Connection failed before anything was sent (DNS, refused, TLS) | retried | retried, same Idempotency-Key |
|
|
96
|
+
| HTTP 429 with `Retry-After` (≤ `max_retry_wait`) | retried after that wait | retried after that wait, same key |
|
|
97
|
+
| HTTP 5xx | retried with backoff | **not retried** (except below) |
|
|
98
|
+
| HTTP 503 with error code `temporary` (shop database busy, nothing placed) | retried | retried, same key |
|
|
99
|
+
| Timeout / connection lost after sending | retried | **not retried** — check the order, or repeat with the same key |
|
|
100
|
+
|
|
101
|
+
### Request signing (optional)
|
|
102
|
+
|
|
103
|
+
If the shop turned on signed requests for your key, pass `signing_key_id` and `signing_secret`. Every request then carries `X-Key-Id`, `X-Timestamp` (unix seconds) and `X-Signature` = hex HMAC-SHA256 of `"{METHOD}\n{path}\n{timestamp}\n{rawBody}"` (path without the query string, e.g. `/api/v1/orders`; empty body for GET).
|
|
104
|
+
|
|
105
|
+
## Errors
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
try:
|
|
109
|
+
client.place_order(42, "356938035643809", idempotency_key="my-order-1002")
|
|
110
|
+
except unlyx.UnlyxError as e:
|
|
111
|
+
e.code # "low_balance", "invalid_input", "not_found", "service_disabled", "rate_limited", "connection_error" …
|
|
112
|
+
e.http_status # 402, 404, 409, 422, 429 … (0 = no response)
|
|
113
|
+
e.message # readable text from the shop
|
|
114
|
+
e.reference # short support reference — quote it to the shop
|
|
115
|
+
e.retry_after # seconds, on 429
|
|
116
|
+
e.body # decoded JSON body
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Webhooks
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
# Flask
|
|
123
|
+
import os
|
|
124
|
+
from flask import Flask, request, abort
|
|
125
|
+
import unlyx
|
|
126
|
+
|
|
127
|
+
app = Flask(__name__)
|
|
128
|
+
|
|
129
|
+
@app.post("/hooks/unlyx")
|
|
130
|
+
def unlyx_hook():
|
|
131
|
+
try:
|
|
132
|
+
event = unlyx.verify_webhook(request.get_data(), request.headers, os.environ["UNLYX_WEBHOOK_SECRET"])
|
|
133
|
+
except unlyx.UnlyxError:
|
|
134
|
+
abort(400)
|
|
135
|
+
# event["event"] == "order.completed"; event["data"]["reference"], event["data"]["reply"] …
|
|
136
|
+
# Use the X-Unlyx-Delivery header to ignore a delivery you already handled.
|
|
137
|
+
return "", 200
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Always pass the **raw** body bytes. `verify_webhook` checks `X-Unlyx-Signature` (`t=<unix>,v1=<hex HMAC-SHA256 of "<unix>.<body>">`), refuses timestamps more than 5 minutes off (`tolerance=`), and accepts a list of secrets while you rotate.
|
|
141
|
+
|
|
142
|
+
## Tests
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
pip install pytest
|
|
146
|
+
pytest # or: python -m unittest discover -s tests
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The suite uses a mocked transport and never calls a real shop. Plug in your own HTTP stack by passing any object with a `send(HttpRequest) -> HttpResponse` method as `transport=`.
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
MIT
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "unlyx"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Official Python SDK for the Unlyx REST API v1 (account, services, orders, webhooks, usage)."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Unlyx" }]
|
|
13
|
+
keywords = ["unlyx", "imei", "unlock", "reseller", "gsm", "api", "sdk", "webhooks"]
|
|
14
|
+
dependencies = []
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 5 - Production/Stable",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Typing :: Typed",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
test = ["pytest>=7"]
|
|
32
|
+
|
|
33
|
+
[tool.setuptools]
|
|
34
|
+
packages = ["unlyx"]
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.package-data]
|
|
37
|
+
unlyx = ["py.typed"]
|
|
38
|
+
|
|
39
|
+
[tool.pytest.ini_options]
|
|
40
|
+
testpaths = ["tests"]
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://unlyx.com/developers"
|
|
44
|
+
Source = "https://github.com/AktherHossion/unlyx-python"
|
|
45
|
+
Issues = "https://github.com/AktherHossion/unlyx-python/issues"
|
unlyx-1.0.0/setup.cfg
ADDED