Skip to content

Commit 72638d5

Browse files
3.1 const (#896)
* chore(deps): update dependency knope to v0.13.1 (#888) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): lock file maintenance (#889) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update dependency knope to v0.13.2 (#890) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): lock file maintenance (#891) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): lock file maintenance (#893) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * WIP support of `const` keyword * feat: Basic `const` support --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Dylan Anthony <[email protected]>
1 parent ed8f1e4 commit 72638d5

31 files changed

+1454
-306
lines changed

.github/workflows/preview_release_pr.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
git config user.email [email protected]
1818
- uses: knope-dev/[email protected]
1919
with:
20-
version: 0.13.0
20+
version: 0.13.2
2121
- run: knope prepare-release --verbose
2222
env:
2323
GITHUB_TOKEN: ${{ secrets.PAT }}

.github/workflows/release-dry-run.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ jobs:
1313
- name: Install Knope
1414
uses: knope-dev/[email protected]
1515
with:
16-
version: 0.13.0
16+
version: 0.13.2
1717
- run: knope prepare-release --dry-run

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Install Knope
1818
uses: knope-dev/[email protected]
1919
with:
20-
version: 0.13.0
20+
version: 0.13.2
2121
- name: Install Poetry
2222
run: pip install --upgrade poetry
2323
- name: Push to PyPI

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ htmlcov/
2727

2828
# Generated end to end test data
2929
my-test-api-client/
30-
custom-e2e/
30+
custom-e2e/
31+
3-1-features-client
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
openapi: "3.1.0"
2+
info:
3+
title: "Test 3.1 Features"
4+
description: "Test new OpenAPI 3.1 features"
5+
version: "0.1.0"
6+
paths:
7+
"/const/{path}":
8+
post:
9+
tags: [ "const" ]
10+
parameters:
11+
- in: "path"
12+
required: true
13+
schema:
14+
const: "this goes in the path"
15+
name: "path"
16+
- in: "query"
17+
required: true
18+
schema:
19+
const: "this always goes in the query"
20+
name: "required query"
21+
- in: "query"
22+
schema:
23+
const: "this sometimes goes in the query"
24+
name: "optional query"
25+
requestBody:
26+
required: true
27+
content:
28+
"application/json":
29+
schema:
30+
type: object
31+
properties:
32+
required:
33+
const: "this always goes in the body"
34+
optional:
35+
const: "this sometimes goes in the body"
36+
nullable:
37+
oneOf:
38+
- type: "null"
39+
- const: "this or null goes in the body"
40+
required:
41+
- required
42+
- nullable
43+
responses:
44+
"200":
45+
description: "Successful Response"
46+
content:
47+
"application/json":
48+
schema:
49+
const: "Why have a fixed response? I dunno"

end_to_end_tests/regen_golden_record.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
def regen_golden_record():
1414
runner = CliRunner()
15-
openapi_path = Path(__file__).parent / "openapi_3.0.json"
15+
openapi_path = Path(__file__).parent / "baseline_openapi_3.0.json"
1616

1717
gr_path = Path(__file__).parent / "golden-record"
1818
output_path = Path.cwd() / "my-test-api-client"
@@ -21,7 +21,28 @@ def regen_golden_record():
2121
shutil.rmtree(gr_path, ignore_errors=True)
2222
shutil.rmtree(output_path, ignore_errors=True)
2323

24-
result = runner.invoke(app, ["generate", f"--config={config_path}", f"--path={openapi_path}"])
24+
result = runner.invoke(
25+
app, ["generate", f"--config={config_path}", f"--path={openapi_path}"]
26+
)
27+
28+
if result.stdout:
29+
print(result.stdout)
30+
if result.exception:
31+
raise result.exception
32+
output_path.rename(gr_path)
33+
34+
35+
def regen_golden_record_3_1_features():
36+
runner = CliRunner()
37+
openapi_path = Path(__file__).parent / "3.1_specific.openapi.yaml"
38+
39+
gr_path = Path(__file__).parent / "test-3-1-golden-record"
40+
output_path = Path.cwd() / "test-3-1-features-client"
41+
42+
shutil.rmtree(gr_path, ignore_errors=True)
43+
shutil.rmtree(output_path, ignore_errors=True)
44+
45+
result = runner.invoke(app, ["generate", f"--path={openapi_path}"])
2546

2647
if result.stdout:
2748
print(result.stdout)
@@ -32,7 +53,7 @@ def regen_golden_record():
3253

3354
def regen_custom_template_golden_record():
3455
runner = CliRunner()
35-
openapi_path = Path(__file__).parent / "openapi_3.0.json"
56+
openapi_path = Path(__file__).parent / "baseline_openapi_3.0.json"
3657
tpl_dir = Path(__file__).parent / "test_custom_templates"
3758

3859
gr_path = Path(__file__).parent / "golden-record"
@@ -45,7 +66,13 @@ def regen_custom_template_golden_record():
4566

4667
os.chdir(str(output_path.absolute()))
4768
result = runner.invoke(
48-
app, ["generate", f"--config={config_path}", f"--path={openapi_path}", f"--custom-template-path={tpl_dir}"]
69+
app,
70+
[
71+
"generate",
72+
f"--config={config_path}",
73+
f"--path={openapi_path}",
74+
f"--custom-template-path={tpl_dir}",
75+
],
4976
)
5077

5178
if result.stdout:
@@ -76,4 +103,5 @@ def regen_custom_template_golden_record():
76103

77104
if __name__ == "__main__":
78105
regen_golden_record()
106+
regen_golden_record_3_1_features()
79107
regen_custom_template_golden_record()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
__pycache__/
2+
build/
3+
dist/
4+
*.egg-info/
5+
.pytest_cache/
6+
7+
# pyenv
8+
.python-version
9+
10+
# Environments
11+
.env
12+
.venv
13+
14+
# mypy
15+
.mypy_cache/
16+
.dmypy.json
17+
dmypy.json
18+
19+
# JetBrains
20+
.idea/
21+
22+
/coverage.xml
23+
/.coverage
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# test-3-1-features-client
2+
A client library for accessing Test 3.1 Features
3+
4+
## Usage
5+
First, create a client:
6+
7+
```python
8+
from test_3_1_features_client import Client
9+
10+
client = Client(base_url="https://api.example.com")
11+
```
12+
13+
If the endpoints you're going to hit require authentication, use `AuthenticatedClient` instead:
14+
15+
```python
16+
from test_3_1_features_client import AuthenticatedClient
17+
18+
client = AuthenticatedClient(base_url="https://api.example.com", token="SuperSecretToken")
19+
```
20+
21+
Now call your endpoint and use your models:
22+
23+
```python
24+
from test_3_1_features_client.models import MyDataModel
25+
from test_3_1_features_client.api.my_tag import get_my_data_model
26+
from test_3_1_features_client.types import Response
27+
28+
with client as client:
29+
my_data: MyDataModel = get_my_data_model.sync(client=client)
30+
# or if you need more info (e.g. status_code)
31+
response: Response[MyDataModel] = get_my_data_model.sync_detailed(client=client)
32+
```
33+
34+
Or do the same thing with an async version:
35+
36+
```python
37+
from test_3_1_features_client.models import MyDataModel
38+
from test_3_1_features_client.api.my_tag import get_my_data_model
39+
from test_3_1_features_client.types import Response
40+
41+
async with client as client:
42+
my_data: MyDataModel = await get_my_data_model.asyncio(client=client)
43+
response: Response[MyDataModel] = await get_my_data_model.asyncio_detailed(client=client)
44+
```
45+
46+
By default, when you're calling an HTTPS API it will attempt to verify that SSL is working correctly. Using certificate verification is highly recommended most of the time, but sometimes you may need to authenticate to a server (especially an internal server) using a custom certificate bundle.
47+
48+
```python
49+
client = AuthenticatedClient(
50+
base_url="https://internal_api.example.com",
51+
token="SuperSecretToken",
52+
verify_ssl="/path/to/certificate_bundle.pem",
53+
)
54+
```
55+
56+
You can also disable certificate validation altogether, but beware that **this is a security risk**.
57+
58+
```python
59+
client = AuthenticatedClient(
60+
base_url="https://internal_api.example.com",
61+
token="SuperSecretToken",
62+
verify_ssl=False
63+
)
64+
```
65+
66+
Things to know:
67+
1. Every path/method combo becomes a Python module with four functions:
68+
1. `sync`: Blocking request that returns parsed data (if successful) or `None`
69+
1. `sync_detailed`: Blocking request that always returns a `Request`, optionally with `parsed` set if the request was successful.
70+
1. `asyncio`: Like `sync` but async instead of blocking
71+
1. `asyncio_detailed`: Like `sync_detailed` but async instead of blocking
72+
73+
1. All path/query params, and bodies become method arguments.
74+
1. If your endpoint had any tags on it, the first tag will be used as a module name for the function (my_tag above)
75+
1. Any endpoint which did not have a tag will be in `test_3_1_features_client.api.default`
76+
77+
## Advanced customizations
78+
79+
There are more settings on the generated `Client` class which let you control more runtime behavior, check out the docstring on that class for more info. You can also customize the underlying `httpx.Client` or `httpx.AsyncClient` (depending on your use-case):
80+
81+
```python
82+
from test_3_1_features_client import Client
83+
84+
def log_request(request):
85+
print(f"Request event hook: {request.method} {request.url} - Waiting for response")
86+
87+
def log_response(response):
88+
request = response.request
89+
print(f"Response event hook: {request.method} {request.url} - Status {response.status_code}")
90+
91+
client = Client(
92+
base_url="https://api.example.com",
93+
httpx_args={"event_hooks": {"request": [log_request], "response": [log_response]}},
94+
)
95+
96+
# Or get the underlying httpx client to modify directly with client.get_httpx_client() or client.get_async_httpx_client()
97+
```
98+
99+
You can even set the httpx client directly, but beware that this will override any existing settings (e.g., base_url):
100+
101+
```python
102+
import httpx
103+
from test_3_1_features_client import Client
104+
105+
client = Client(
106+
base_url="https://api.example.com",
107+
)
108+
# Note that base_url needs to be re-set, as would any shared cookies, headers, etc.
109+
client.set_httpx_client(httpx.Client(base_url="https://api.example.com", proxies="http://localhost:8030"))
110+
```
111+
112+
## Building / publishing this package
113+
This project uses [Poetry](https://python-poetry.org/) to manage dependencies and packaging. Here are the basics:
114+
1. Update the metadata in pyproject.toml (e.g. authors, version)
115+
1. If you're using a private repository, configure it with Poetry
116+
1. `poetry config repositories.<your-repository-name> <url-to-your-repository>`
117+
1. `poetry config http-basic.<your-repository-name> <username> <password>`
118+
1. Publish the client with `poetry publish --build -r <your-repository-name>` or, if for public PyPI, just `poetry publish --build`
119+
120+
If you want to install this client into another project without publishing it (e.g. for development) then:
121+
1. If that project **is using Poetry**, you can simply do `poetry add <path-to-this-client>` from that project
122+
1. If that project is not using Poetry:
123+
1. Build a wheel with `poetry build -f wheel`
124+
1. Install that wheel from the other project `pip install <path-to-wheel>`
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[tool.poetry]
2+
name = "test-3-1-features-client"
3+
version = "0.1.0"
4+
description = "A client library for accessing Test 3.1 Features"
5+
6+
authors = []
7+
8+
readme = "README.md"
9+
packages = [
10+
{include = "test_3_1_features_client"},
11+
]
12+
include = ["CHANGELOG.md", "test_3_1_features_client/py.typed"]
13+
14+
[tool.poetry.dependencies]
15+
python = "^3.8"
16+
httpx = ">=0.20.0,<0.26.0"
17+
attrs = ">=21.3.0"
18+
python-dateutil = "^2.8.0"
19+
20+
[build-system]
21+
requires = ["poetry-core>=1.0.0"]
22+
build-backend = "poetry.core.masonry.api"
23+
24+
[tool.ruff]
25+
select = ["F", "I"]
26+
line-length = 120
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
""" A client library for accessing Test 3.1 Features """
2+
from .client import AuthenticatedClient, Client
3+
4+
__all__ = (
5+
"AuthenticatedClient",
6+
"Client",
7+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
""" Contains methods for accessing the API """

end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/api/const/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)