vesremont-api 0.2.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vesremont
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.
@@ -0,0 +1,70 @@
1
+ Metadata-Version: 2.4
2
+ Name: vesremont-api
3
+ Version: 0.2.0
4
+ Summary: Small standard-library client for the Vesremont public API
5
+ License-Expression: MIT
6
+ Project-URL: Documentation, https://vesremont.com/for_ai/api_ai/
7
+ Project-URL: Homepage, https://vesremont.com/developers/
8
+ Project-URL: Source, https://github.com/oooafganec-dev/vesremont-sdk/tree/main/python
9
+ Project-URL: Issues, https://github.com/oooafganec-dev/vesremont-sdk/issues
10
+ Keywords: vesremont,api,sdk,oauth,pkce
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Dynamic: license-file
17
+
18
+ # Vesremont Python SDK 0.2.0
19
+
20
+ Python 3.10+, только стандартная библиотека во время работы.
21
+ MIT. Официальные исходники:
22
+ https://github.com/oooafganec-dev/vesremont-sdk/tree/main/python.
23
+ Команда установки опубликованного релиза из PyPI:
24
+
25
+ ```bash
26
+ python -m pip install vesremont-api==0.2.0
27
+ ```
28
+
29
+ Альтернатива — прямой wheel с сайта Vesremont:
30
+
31
+ ```bash
32
+ python -m pip install https://vesremont.com/developers/downloads/vesremont_api-0.2.0-py3-none-any.whl
33
+ ```
34
+
35
+ Готовый wheel не требует сборки. Также доступен исходный sdist
36
+ `vesremont_api-0.2.0.tar.gz`; для его сборки нужен setuptools 77.0.3+.
37
+ Runtime-зависимостей нет.
38
+
39
+ ```python
40
+ from vesremont_api import VesremontClient, ApiError
41
+
42
+ api = VesremontClient()
43
+ products = api.search("труборез", page=1, per_page=10)
44
+ product = api.product(375094)
45
+ draft = api.cart_draft(375094, 1)
46
+ # draft["share_url"] передаётся покупателю, не в публичные логи.
47
+ ```
48
+
49
+ `status`, `search`, `product`, `cart`, `cart_draft` — короткие помощники.
50
+ Остальные REST-операции: `request(method, '/path', query={}, body={}, idempotency_key=...)`.
51
+ Путь относительно `/api/v1`; GET вызывается без body. `filters` в query — JSON-строка.
52
+ Подборку можно дополнить через `cart_draft(..., draft=token)`; token берётся
53
+ из query `draft` ранее полученной share_url. Серверные схемы:
54
+ https://vesremont.com/openapi.json.
55
+
56
+ Для личных данных передайте уже полученный REST OAuth token:
57
+ `VesremontClient(token=token)`. OAuth-протокол и минимальные scopes описаны
58
+ в https://vesremont.com/llms/api.md. Браузерный OAuth мастер есть в Node CLI;
59
+ в Python SDK второй мастер не дублируется. Токен не пишется на диск, не входит в URL.
60
+
61
+ Timeout по умолчанию 15 секунд (параметр `timeout`, до 60). TLS проверяется,
62
+ redirects и автоматические повторы отсутствуют; API host фиксирован.
63
+ Ответ ограничен 2 MiB, тело запроса — 64 KiB.
64
+ `ApiError` содержит `status`, `code`, `request_id`, `retry_after`, без raw body.
65
+ После неопределённого исхода записи сначала перечитайте состояние.
66
+ Для submit нужны prepare, подтверждение покупателя на Vesremont и стабильный
67
+ `idempotency_key`; SDK не обходит эти проверки и не создаёт фиктивных заказов.
68
+
69
+ Используйте с внешнего ПК/хостинга. Это production API, sandbox не предоставляется.
70
+ Наличие SDK не означает включение отключённых операций или завершение приёмки.
@@ -0,0 +1,53 @@
1
+ # Vesremont Python SDK 0.2.0
2
+
3
+ Python 3.10+, только стандартная библиотека во время работы.
4
+ MIT. Официальные исходники:
5
+ https://github.com/oooafganec-dev/vesremont-sdk/tree/main/python.
6
+ Команда установки опубликованного релиза из PyPI:
7
+
8
+ ```bash
9
+ python -m pip install vesremont-api==0.2.0
10
+ ```
11
+
12
+ Альтернатива — прямой wheel с сайта Vesremont:
13
+
14
+ ```bash
15
+ python -m pip install https://vesremont.com/developers/downloads/vesremont_api-0.2.0-py3-none-any.whl
16
+ ```
17
+
18
+ Готовый wheel не требует сборки. Также доступен исходный sdist
19
+ `vesremont_api-0.2.0.tar.gz`; для его сборки нужен setuptools 77.0.3+.
20
+ Runtime-зависимостей нет.
21
+
22
+ ```python
23
+ from vesremont_api import VesremontClient, ApiError
24
+
25
+ api = VesremontClient()
26
+ products = api.search("труборез", page=1, per_page=10)
27
+ product = api.product(375094)
28
+ draft = api.cart_draft(375094, 1)
29
+ # draft["share_url"] передаётся покупателю, не в публичные логи.
30
+ ```
31
+
32
+ `status`, `search`, `product`, `cart`, `cart_draft` — короткие помощники.
33
+ Остальные REST-операции: `request(method, '/path', query={}, body={}, idempotency_key=...)`.
34
+ Путь относительно `/api/v1`; GET вызывается без body. `filters` в query — JSON-строка.
35
+ Подборку можно дополнить через `cart_draft(..., draft=token)`; token берётся
36
+ из query `draft` ранее полученной share_url. Серверные схемы:
37
+ https://vesremont.com/openapi.json.
38
+
39
+ Для личных данных передайте уже полученный REST OAuth token:
40
+ `VesremontClient(token=token)`. OAuth-протокол и минимальные scopes описаны
41
+ в https://vesremont.com/llms/api.md. Браузерный OAuth мастер есть в Node CLI;
42
+ в Python SDK второй мастер не дублируется. Токен не пишется на диск, не входит в URL.
43
+
44
+ Timeout по умолчанию 15 секунд (параметр `timeout`, до 60). TLS проверяется,
45
+ redirects и автоматические повторы отсутствуют; API host фиксирован.
46
+ Ответ ограничен 2 MiB, тело запроса — 64 KiB.
47
+ `ApiError` содержит `status`, `code`, `request_id`, `retry_after`, без raw body.
48
+ После неопределённого исхода записи сначала перечитайте состояние.
49
+ Для submit нужны prepare, подтверждение покупателя на Vesremont и стабильный
50
+ `idempotency_key`; SDK не обходит эти проверки и не создаёт фиктивных заказов.
51
+
52
+ Используйте с внешнего ПК/хостинга. Это production API, sandbox не предоставляется.
53
+ Наличие SDK не означает включение отключённых операций или завершение приёмки.
@@ -0,0 +1,23 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.3"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "vesremont-api"
7
+ version = "0.2.0"
8
+ description = "Small standard-library client for the Vesremont public API"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ keywords = ["vesremont", "api", "sdk", "oauth", "pkce"]
14
+ classifiers = ["Programming Language :: Python :: 3", "Operating System :: OS Independent"]
15
+
16
+ [project.urls]
17
+ Documentation = "https://vesremont.com/for_ai/api_ai/"
18
+ Homepage = "https://vesremont.com/developers/"
19
+ Source = "https://github.com/oooafganec-dev/vesremont-sdk/tree/main/python"
20
+ Issues = "https://github.com/oooafganec-dev/vesremont-sdk/issues"
21
+
22
+ [tool.setuptools]
23
+ py-modules = ["vesremont_api"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,70 @@
1
+ Metadata-Version: 2.4
2
+ Name: vesremont-api
3
+ Version: 0.2.0
4
+ Summary: Small standard-library client for the Vesremont public API
5
+ License-Expression: MIT
6
+ Project-URL: Documentation, https://vesremont.com/for_ai/api_ai/
7
+ Project-URL: Homepage, https://vesremont.com/developers/
8
+ Project-URL: Source, https://github.com/oooafganec-dev/vesremont-sdk/tree/main/python
9
+ Project-URL: Issues, https://github.com/oooafganec-dev/vesremont-sdk/issues
10
+ Keywords: vesremont,api,sdk,oauth,pkce
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Dynamic: license-file
17
+
18
+ # Vesremont Python SDK 0.2.0
19
+
20
+ Python 3.10+, только стандартная библиотека во время работы.
21
+ MIT. Официальные исходники:
22
+ https://github.com/oooafganec-dev/vesremont-sdk/tree/main/python.
23
+ Команда установки опубликованного релиза из PyPI:
24
+
25
+ ```bash
26
+ python -m pip install vesremont-api==0.2.0
27
+ ```
28
+
29
+ Альтернатива — прямой wheel с сайта Vesremont:
30
+
31
+ ```bash
32
+ python -m pip install https://vesremont.com/developers/downloads/vesremont_api-0.2.0-py3-none-any.whl
33
+ ```
34
+
35
+ Готовый wheel не требует сборки. Также доступен исходный sdist
36
+ `vesremont_api-0.2.0.tar.gz`; для его сборки нужен setuptools 77.0.3+.
37
+ Runtime-зависимостей нет.
38
+
39
+ ```python
40
+ from vesremont_api import VesremontClient, ApiError
41
+
42
+ api = VesremontClient()
43
+ products = api.search("труборез", page=1, per_page=10)
44
+ product = api.product(375094)
45
+ draft = api.cart_draft(375094, 1)
46
+ # draft["share_url"] передаётся покупателю, не в публичные логи.
47
+ ```
48
+
49
+ `status`, `search`, `product`, `cart`, `cart_draft` — короткие помощники.
50
+ Остальные REST-операции: `request(method, '/path', query={}, body={}, idempotency_key=...)`.
51
+ Путь относительно `/api/v1`; GET вызывается без body. `filters` в query — JSON-строка.
52
+ Подборку можно дополнить через `cart_draft(..., draft=token)`; token берётся
53
+ из query `draft` ранее полученной share_url. Серверные схемы:
54
+ https://vesremont.com/openapi.json.
55
+
56
+ Для личных данных передайте уже полученный REST OAuth token:
57
+ `VesremontClient(token=token)`. OAuth-протокол и минимальные scopes описаны
58
+ в https://vesremont.com/llms/api.md. Браузерный OAuth мастер есть в Node CLI;
59
+ в Python SDK второй мастер не дублируется. Токен не пишется на диск, не входит в URL.
60
+
61
+ Timeout по умолчанию 15 секунд (параметр `timeout`, до 60). TLS проверяется,
62
+ redirects и автоматические повторы отсутствуют; API host фиксирован.
63
+ Ответ ограничен 2 MiB, тело запроса — 64 KiB.
64
+ `ApiError` содержит `status`, `code`, `request_id`, `retry_after`, без raw body.
65
+ После неопределённого исхода записи сначала перечитайте состояние.
66
+ Для submit нужны prepare, подтверждение покупателя на Vesremont и стабильный
67
+ `idempotency_key`; SDK не обходит эти проверки и не создаёт фиктивных заказов.
68
+
69
+ Используйте с внешнего ПК/хостинга. Это production API, sandbox не предоставляется.
70
+ Наличие SDK не означает включение отключённых операций или завершение приёмки.
@@ -0,0 +1,8 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ vesremont_api.py
5
+ vesremont_api.egg-info/PKG-INFO
6
+ vesremont_api.egg-info/SOURCES.txt
7
+ vesremont_api.egg-info/dependency_links.txt
8
+ vesremont_api.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ vesremont_api
@@ -0,0 +1,108 @@
1
+ """Vesremont REST client. Verified TLS, fixed origin, no redirects or retries."""
2
+ import json
3
+ import re
4
+ import ssl
5
+ from urllib.error import HTTPError, URLError
6
+ from urllib.parse import urlencode
7
+ from urllib.request import HTTPSHandler, HTTPRedirectHandler, Request, build_opener
8
+
9
+
10
+ class ApiError(Exception):
11
+ def __init__(self, status, code, request_id=None, retry_after=None):
12
+ super().__init__(f"Vesremont: {code} (HTTP {status})")
13
+ self.status, self.code = status, code
14
+ self.request_id, self.retry_after = request_id, retry_after
15
+
16
+
17
+ class _NoRedirect(HTTPRedirectHandler):
18
+ def redirect_request(self, req, fp, code, msg, headers, newurl):
19
+ return None
20
+
21
+
22
+ def _safe(value):
23
+ return value if isinstance(value, str) and re.fullmatch(r"[A-Za-z0-9_.:-]{1,128}", value) else None
24
+
25
+
26
+ def _id(value):
27
+ if type(value) is not int or not 1 <= value <= 2147483647:
28
+ raise ValueError("Positive product ID required")
29
+ return value
30
+
31
+
32
+ class VesremontClient:
33
+ def __init__(self, token="", timeout=15):
34
+ if not isinstance(token, str) or (token and not re.fullmatch(r"[A-Za-z0-9_.~-]{1,8192}", token)):
35
+ raise ValueError("Invalid access token")
36
+ if type(timeout) not in (int, float) or not 0 < timeout <= 60:
37
+ raise ValueError("Timeout must be 0..60 seconds")
38
+ self._token, self._timeout = token, timeout
39
+ self._opener = build_opener(_NoRedirect(), HTTPSHandler(context=ssl.create_default_context()))
40
+
41
+ def request(self, method, path, *, query=None, body=None, idempotency_key=None):
42
+ if method not in ("GET", "POST", "PATCH", "DELETE") or not re.fullmatch(r"/[A-Za-z0-9_-]+(?:/[A-Za-z0-9_-]+)*/?", path):
43
+ raise ValueError("Invalid API method/path")
44
+ if body is not None and (method == "GET" or not isinstance(body, dict)):
45
+ raise ValueError("JSON object required; GET body is not supported")
46
+ if method == "POST" and path.rstrip("/") == "/orders/submit" and not idempotency_key:
47
+ raise ValueError("Idempotency-Key is required")
48
+ params = {}
49
+ for name, value in (query or {}).items():
50
+ if not re.fullmatch(r"[a-z_]{1,40}", name) or type(value) not in (str, int, float, bool):
51
+ raise ValueError("Query values must be scalar")
52
+ params[name] = str(value).lower() if type(value) is bool else str(value)
53
+ encoded = urlencode(params)
54
+ if len(encoded) > 8192:
55
+ raise ValueError("Query exceeds 8192 characters")
56
+ url = "https://vesremont.com/api/v1" + path + ("?" + encoded if encoded else "")
57
+ headers = {"Accept": "application/json", "User-Agent": "Vesremont-Python/0.2.0"}
58
+ if self._token:
59
+ headers["Authorization"] = "Bearer " + self._token
60
+ if idempotency_key is not None:
61
+ if not isinstance(idempotency_key, str) or not re.fullmatch(r"[A-Za-z0-9._:-]{16,128}", idempotency_key):
62
+ raise ValueError("Invalid idempotency key")
63
+ headers["Idempotency-Key"] = idempotency_key
64
+ payload = None if body is None else json.dumps(body, ensure_ascii=False, allow_nan=False).encode("utf-8")
65
+ if payload is not None:
66
+ if len(payload) > 65536:
67
+ raise ValueError("Request body exceeds 64 KiB")
68
+ headers["Content-Type"] = "application/json"
69
+ try:
70
+ try:
71
+ response = self._opener.open(Request(url, data=payload, headers=headers, method=method), timeout=self._timeout)
72
+ except HTTPError as error:
73
+ response = error
74
+ with response:
75
+ raw = response.read(2 * 1024 * 1024 + 1)
76
+ if len(raw) > 2 * 1024 * 1024:
77
+ raise ApiError(response.code, "response_too_large")
78
+ try:
79
+ data = json.loads(raw.decode("utf-8")) if raw else None
80
+ except (ValueError, UnicodeError):
81
+ raise ApiError(response.code, "invalid_json") from None
82
+ if not 200 <= response.code < 300:
83
+ problem = data if isinstance(data, dict) else {}
84
+ raise ApiError(response.code, _safe(problem.get("code")) or "http_error",
85
+ _safe(problem.get("request_id")), _safe(response.headers.get("Retry-After")))
86
+ return data
87
+ except (URLError, OSError):
88
+ raise ApiError(0, "transport_error") from None
89
+
90
+ def status(self):
91
+ return self.request("GET", "/status")
92
+
93
+ def search(self, q, **query):
94
+ return self.request("GET", "/products", query={**query, "q": q})
95
+
96
+ def product(self, product_id):
97
+ return self.request("GET", f"/products/{_id(product_id)}")
98
+
99
+ def cart(self):
100
+ return self.request("GET", "/cart")
101
+
102
+ def cart_draft(self, product_id, quantity=1, draft=None):
103
+ if type(quantity) is not int or not 1 <= quantity <= 99:
104
+ raise ValueError("Quantity must be 1..99")
105
+ query = {"product_id": _id(product_id), "quantity": quantity}
106
+ if draft is not None:
107
+ query["draft"] = draft
108
+ return self.request("GET", "/cart-drafts/", query=query)