wbapi-codegen 0.1.0__tar.gz → 0.1.2__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.
- wbapi_codegen-0.1.2/PKG-INFO +21 -0
- wbapi_codegen-0.1.2/README.md +3 -0
- {wbapi_codegen-0.1.0 → wbapi_codegen-0.1.2}/pyproject.toml +1 -1
- {wbapi_codegen-0.1.0 → wbapi_codegen-0.1.2}/src/wbapi_codegen/codegen.py +57 -24
- wbapi_codegen-0.1.0/PKG-INFO +0 -59
- wbapi_codegen-0.1.0/README.md +0 -41
- {wbapi_codegen-0.1.0 → wbapi_codegen-0.1.2}/.gitignore +0 -0
- {wbapi_codegen-0.1.0 → wbapi_codegen-0.1.2}/src/wbapi_codegen/__init__.py +0 -0
- {wbapi_codegen-0.1.0 → wbapi_codegen-0.1.2}/src/wbapi_codegen/__main__.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wbapi-codegen
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Code generator for wbapi-async — generates types, methods and API client from Wildberries OpenAPI specs
|
|
5
|
+
Project-URL: Repository, https://github.com/serdukow/wbapi-codegen
|
|
6
|
+
Author-email: Andrei Serdiukov <asyncdf@gmail.com>
|
|
7
|
+
Maintainer-email: Andrei Serdiukov <asyncdf@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: api,codegen,openapi,wb,wildberries
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Requires-Dist: pyyaml>=6.0
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# wbapi-codegen
|
|
20
|
+
|
|
21
|
+
Code generator for [wbapi-async](https://github.com/serdukow/wbapi-async).
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "wbapi-codegen"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "Code generator for wbapi-async — generates types, methods and API client from Wildberries OpenAPI specs"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Andrei Serdiukov", email = "asyncdf@gmail.com" },
|
|
@@ -12,8 +12,7 @@ Parses OpenAPI YAML files and generates:
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
14
|
import re
|
|
15
|
-
import
|
|
16
|
-
from dataclasses import dataclass, field
|
|
15
|
+
from dataclasses import dataclass
|
|
17
16
|
from keyword import iskeyword
|
|
18
17
|
from pathlib import Path
|
|
19
18
|
from typing import Any
|
|
@@ -177,6 +176,7 @@ class MethodDef:
|
|
|
177
176
|
description: str = ""
|
|
178
177
|
source_url: str = ""
|
|
179
178
|
type_def: TypeDef | None = None
|
|
179
|
+
empty_response: bool = False # True for 204 No Content or empty schema
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
# ---------------------------------------------------------------------------
|
|
@@ -254,6 +254,27 @@ def _find_response_array(schema: dict, spec: dict) -> tuple[list[FieldDef], str
|
|
|
254
254
|
return fields, dot_path
|
|
255
255
|
|
|
256
256
|
|
|
257
|
+
_DOC_SLUG_MAP: dict[str, str] = {
|
|
258
|
+
"01-general": "api-information",
|
|
259
|
+
"02-products": "work-with-products",
|
|
260
|
+
"03-orders-fbs": "orders-fbs",
|
|
261
|
+
"04-orders-dbw": "orders-dbw",
|
|
262
|
+
"05-orders-dbs": "orders-dbs",
|
|
263
|
+
"06-in-store-pickup": "in-store-pickup",
|
|
264
|
+
"07-orders-fbw": "orders-fbw",
|
|
265
|
+
"08-promotion": "promotion",
|
|
266
|
+
"09-communications": "communications",
|
|
267
|
+
"10-tariffs": "tariffs",
|
|
268
|
+
"11-analytics": "analytics",
|
|
269
|
+
"12-reports": "reports",
|
|
270
|
+
"13-finances": "financial-reports-and-accounting",
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _yaml_stem_to_doc_slug(stem: str) -> str:
|
|
275
|
+
return _DOC_SLUG_MAP.get(stem, stem)
|
|
276
|
+
|
|
277
|
+
|
|
257
278
|
def parse_yaml(yaml_path: Path) -> list[MethodDef]:
|
|
258
279
|
with yaml_path.open() as f:
|
|
259
280
|
spec = yaml.safe_load(f)
|
|
@@ -338,14 +359,16 @@ def parse_yaml(yaml_path: Path) -> list[MethodDef]:
|
|
|
338
359
|
|
|
339
360
|
# response type
|
|
340
361
|
responses = operation.get("responses", {})
|
|
362
|
+
# prefer 200, then 201, then 204
|
|
341
363
|
ok_response = _resolve_schema(
|
|
342
|
-
responses.get("200", responses.get("201", {})), spec
|
|
364
|
+
responses.get("200", responses.get("201", responses.get("204", {}))), spec
|
|
343
365
|
)
|
|
344
366
|
ok_content = ok_response.get("content", {})
|
|
345
367
|
ok_schema = _resolve_schema(
|
|
346
368
|
ok_content.get("application/json", {}).get("schema", {}), spec
|
|
347
369
|
)
|
|
348
370
|
item_fields, data_key = _find_response_array(ok_schema, spec)
|
|
371
|
+
empty_response = not ok_content or not ok_schema
|
|
349
372
|
|
|
350
373
|
# build names from summary
|
|
351
374
|
if summary:
|
|
@@ -373,10 +396,15 @@ def parse_yaml(yaml_path: Path) -> list[MethodDef]:
|
|
|
373
396
|
|
|
374
397
|
tag = (operation.get("tags") or [""])[0]
|
|
375
398
|
tag_slug = re.sub(r"[^a-zA-Z0-9]", "-", tag).strip("-")
|
|
376
|
-
path_encoded =
|
|
399
|
+
path_encoded = (
|
|
400
|
+
path_str.replace("/", "~1")
|
|
401
|
+
.replace("{", "%7B")
|
|
402
|
+
.replace("}", "%7D")
|
|
403
|
+
)
|
|
377
404
|
source_url = (
|
|
378
|
-
f"https://dev.wildberries.ru/en/openapi/"
|
|
379
|
-
f"{yaml_path.stem}
|
|
405
|
+
f"https://dev.wildberries.ru/en/docs/openapi/"
|
|
406
|
+
f"{_yaml_stem_to_doc_slug(yaml_path.stem)}"
|
|
407
|
+
f"#tag/{tag_slug}/paths/{path_encoded}/{http_verb}"
|
|
380
408
|
)
|
|
381
409
|
|
|
382
410
|
results.append(MethodDef(
|
|
@@ -393,6 +421,7 @@ def parse_yaml(yaml_path: Path) -> list[MethodDef]:
|
|
|
393
421
|
params=param_fields,
|
|
394
422
|
description=summary,
|
|
395
423
|
source_url=source_url,
|
|
424
|
+
empty_response=empty_response,
|
|
396
425
|
type_def=TypeDef(
|
|
397
426
|
class_name=type_name,
|
|
398
427
|
fields=item_fields or [],
|
|
@@ -472,6 +501,8 @@ def generate_method_file(md: MethodDef) -> str:
|
|
|
472
501
|
lines += [' """'] + [f" {dl}" if dl else "" for dl in doc_lines] + [' """', ""]
|
|
473
502
|
|
|
474
503
|
lines.append(f" __return__ = {md.return_type}")
|
|
504
|
+
if md.empty_response:
|
|
505
|
+
lines.append(" __empty_response__ = True")
|
|
475
506
|
lines.append(f' __api__ = "{md.api_host}"')
|
|
476
507
|
if md.path_template:
|
|
477
508
|
lines.append(' __method__ = ""')
|
|
@@ -562,7 +593,7 @@ def _api_method_wrapper(md: MethodDef) -> str:
|
|
|
562
593
|
sig_parts.append(f"{p.py_name}: {p.py_type} = {default}")
|
|
563
594
|
|
|
564
595
|
sig = ", ".join(sig_parts)
|
|
565
|
-
ret = f"list[{md.return_type}]"
|
|
596
|
+
ret = "None" if md.empty_response else f"list[{md.return_type}]"
|
|
566
597
|
|
|
567
598
|
call_args = ", ".join(
|
|
568
599
|
f"{p.py_name}={p.py_name}" for p in path_params + non_path_params
|
|
@@ -719,24 +750,28 @@ def generate_test_file(md: MethodDef) -> str:
|
|
|
719
750
|
f"{p.py_name}={_mock_value(p.py_type, p.py_name)}" for p in required_params
|
|
720
751
|
)
|
|
721
752
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
753
|
+
if md.empty_response:
|
|
754
|
+
mock_response = "None"
|
|
755
|
+
assert_lines = [" assert result is None"]
|
|
756
|
+
else:
|
|
757
|
+
mock_response = _build_mock_response(
|
|
758
|
+
md.type_def.fields if md.type_def else [], md.data_key
|
|
759
|
+
)
|
|
760
|
+
assert_lines = [
|
|
761
|
+
f" assert isinstance(result, list)",
|
|
762
|
+
f" assert len(result) == 1",
|
|
763
|
+
f" assert isinstance(result[0], {md.return_type})",
|
|
764
|
+
]
|
|
765
|
+
if md.type_def and md.type_def.fields:
|
|
766
|
+
for f in md.type_def.fields[:3]:
|
|
767
|
+
val = _mock_value(f.py_type, f.alias)
|
|
768
|
+
if val != "None":
|
|
769
|
+
assert_lines.append(f" assert result[0].{f.py_name} == {val}")
|
|
735
770
|
|
|
736
771
|
lines = [
|
|
737
772
|
"import pytest",
|
|
738
773
|
"",
|
|
739
|
-
f"from wbapi_async.types.{md.return_type_file} import {md.return_type}",
|
|
774
|
+
*([f"from wbapi_async.types.{md.return_type_file} import {md.return_type}"] if not md.empty_response else []),
|
|
740
775
|
"from tests.mocked_api import MockedAPI",
|
|
741
776
|
"",
|
|
742
777
|
"",
|
|
@@ -750,9 +785,6 @@ def generate_test_file(md: MethodDef) -> str:
|
|
|
750
785
|
"",
|
|
751
786
|
f" result = await api.{md.method_name}({call_args})",
|
|
752
787
|
"",
|
|
753
|
-
f" assert isinstance(result, list)",
|
|
754
|
-
f" assert len(result) == 1",
|
|
755
|
-
f" assert isinstance(result[0], {md.return_type})",
|
|
756
788
|
*assert_lines,
|
|
757
789
|
"",
|
|
758
790
|
]
|
|
@@ -855,6 +887,7 @@ def run(yaml_files: list[Path], target: Path) -> None:
|
|
|
855
887
|
))
|
|
856
888
|
print(f"[init] {(methods_dir / '__init__.py').relative_to(target)}")
|
|
857
889
|
|
|
890
|
+
all_method_defs.sort(key=lambda m: m.method_name)
|
|
858
891
|
api_path.write_text(generate_api_file(all_method_defs, unofficial_block))
|
|
859
892
|
print(f"[api] {api_path.relative_to(target)}")
|
|
860
893
|
|
wbapi_codegen-0.1.0/PKG-INFO
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: wbapi-codegen
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Code generator for wbapi-async — generates types, methods and API client from Wildberries OpenAPI specs
|
|
5
|
-
Project-URL: Repository, https://github.com/serdukow/wbapi-codegen
|
|
6
|
-
Author-email: Andrei Serdiukov <asyncdf@gmail.com>
|
|
7
|
-
Maintainer-email: Andrei Serdiukov <asyncdf@gmail.com>
|
|
8
|
-
License: MIT
|
|
9
|
-
Keywords: api,codegen,openapi,wb,wildberries
|
|
10
|
-
Classifier: Development Status :: 3 - Alpha
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Topic :: Software Development :: Code Generators
|
|
15
|
-
Requires-Python: >=3.10
|
|
16
|
-
Requires-Dist: pyyaml>=6.0
|
|
17
|
-
Description-Content-Type: text/markdown
|
|
18
|
-
|
|
19
|
-
# wbapi-codegen
|
|
20
|
-
|
|
21
|
-
Code generator for [wbapi-async](https://github.com/serdukow/wbapi-async).
|
|
22
|
-
|
|
23
|
-
Parses Wildberries OpenAPI YAML specs and generates:
|
|
24
|
-
- `src/wbapi_async/types/` — Pydantic response models
|
|
25
|
-
- `src/wbapi_async/methods/` — `WbMethod` request classes
|
|
26
|
-
- `src/wbapi_async/client/api.py` — `WbAPI` wrapper methods
|
|
27
|
-
|
|
28
|
-
Files marked `__unofficial__ = True` are never overwritten.
|
|
29
|
-
|
|
30
|
-
## Install
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
pip install wbapi-codegen
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Usage
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
# process all YAMLs from .wb/swagger/ into current directory
|
|
40
|
-
wbapi-codegen --target /path/to/wbapi-async
|
|
41
|
-
|
|
42
|
-
# custom swagger directory
|
|
43
|
-
wbapi-codegen --target /path/to/wbapi-async --swagger-dir /path/to/yaml/files
|
|
44
|
-
|
|
45
|
-
# single file
|
|
46
|
-
wbapi-codegen --target /path/to/wbapi-async /path/to/02-products.yaml
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## How it works
|
|
50
|
-
|
|
51
|
-
1. Downloads OpenAPI YAMLs from Wildberries developer portal
|
|
52
|
-
2. Parses paths, parameters, request bodies and response schemas
|
|
53
|
-
3. Generates typed Python files following the `wbapi-async` conventions
|
|
54
|
-
4. Creates a PR in the `wbapi-async` repo via GitHub Actions (runs daily at 04:00 UTC)
|
|
55
|
-
|
|
56
|
-
## Setup for GitHub Actions
|
|
57
|
-
|
|
58
|
-
In the `wbapi-codegen` repo, add a secret `WBAPI_PAT` — a GitHub Personal Access Token
|
|
59
|
-
with `repo` scope on the `wbapi-async` repository.
|
wbapi_codegen-0.1.0/README.md
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# wbapi-codegen
|
|
2
|
-
|
|
3
|
-
Code generator for [wbapi-async](https://github.com/serdukow/wbapi-async).
|
|
4
|
-
|
|
5
|
-
Parses Wildberries OpenAPI YAML specs and generates:
|
|
6
|
-
- `src/wbapi_async/types/` — Pydantic response models
|
|
7
|
-
- `src/wbapi_async/methods/` — `WbMethod` request classes
|
|
8
|
-
- `src/wbapi_async/client/api.py` — `WbAPI` wrapper methods
|
|
9
|
-
|
|
10
|
-
Files marked `__unofficial__ = True` are never overwritten.
|
|
11
|
-
|
|
12
|
-
## Install
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
pip install wbapi-codegen
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Usage
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
# process all YAMLs from .wb/swagger/ into current directory
|
|
22
|
-
wbapi-codegen --target /path/to/wbapi-async
|
|
23
|
-
|
|
24
|
-
# custom swagger directory
|
|
25
|
-
wbapi-codegen --target /path/to/wbapi-async --swagger-dir /path/to/yaml/files
|
|
26
|
-
|
|
27
|
-
# single file
|
|
28
|
-
wbapi-codegen --target /path/to/wbapi-async /path/to/02-products.yaml
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## How it works
|
|
32
|
-
|
|
33
|
-
1. Downloads OpenAPI YAMLs from Wildberries developer portal
|
|
34
|
-
2. Parses paths, parameters, request bodies and response schemas
|
|
35
|
-
3. Generates typed Python files following the `wbapi-async` conventions
|
|
36
|
-
4. Creates a PR in the `wbapi-async` repo via GitHub Actions (runs daily at 04:00 UTC)
|
|
37
|
-
|
|
38
|
-
## Setup for GitHub Actions
|
|
39
|
-
|
|
40
|
-
In the `wbapi-codegen` repo, add a secret `WBAPI_PAT` — a GitHub Personal Access Token
|
|
41
|
-
with `repo` scope on the `wbapi-async` repository.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|