Skip to content

Commit

Permalink
Support new union syntax in 3.10 +
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul Hallett committed Oct 3, 2023
1 parent 9dcb49a commit ebbbecc
Show file tree
Hide file tree
Showing 14 changed files with 209 additions and 138 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11"]
python-version: ["3.10", "3.11"]
steps:
- uses: actions/checkout@v3
#----------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Change log


## 0.7.0

* `constants.py` has been renamed to `config.py` to better reflect how it is used.
* If you are using Python 3.10 or later, the `typing.Unions` types will generate as the short hand `|` instead. This required a big rework of the code to enable templating.

## 0.6.3

Expand Down
22 changes: 20 additions & 2 deletions clientele/generator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from distutils.dir_util import copy_tree
from os.path import exists
from os import remove
from shutil import copyfile
from typing import Optional

Expand All @@ -8,8 +9,14 @@
from clientele.generators.clients import ClientsGenerator
from clientele.generators.http import HTTPGenerator
from clientele.generators.schemas import SchemasGenerator
from clientele.settings import CLIENT_TEMPLATE_ROOT, CONSTANTS_ROOT, VERSION
from clientele.writer import write_to_manifest
from clientele.settings import (
CLIENT_TEMPLATE_ROOT,
CONSTANTS_ROOT,
VERSION,
PY_VERSION,
templates,
)
from clientele.writer import write_to_manifest, write_to_http


class Generator:
Expand Down Expand Up @@ -51,6 +58,16 @@ def __init__(
self.file = file
self.url = url

def generate_http_py(self):
if exists(f"{self.output_dir}/http.py"):
remove(f"{self.output_dir}/http.py")
new_unions = PY_VERSION[1] > 10
template = templates.get_template("http_py.jinja2")
content = template.render(
new_unions=new_unions,
)
write_to_http(content, output_dir=self.output_dir)

def generate_manifest(self):
"""
A manifest file with useful information
Expand Down Expand Up @@ -79,6 +96,7 @@ def generate(self) -> None:
f"{CONSTANTS_ROOT}/config_template.py",
f"{self.output_dir}/config.py",
)
self.generate_http_py()
self.generate_manifest()
self.schemas_generator.generate_schema_classes()
self.clients_generator.generate_paths()
Expand Down
5 changes: 3 additions & 2 deletions clientele/generators/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
get_param_from_ref,
get_type,
schema_ref,
union_for_py_ver,
)
from clientele.writer import write_to_client

Expand Down Expand Up @@ -183,7 +184,7 @@ def generate_response_types(self, responses: dict, func_name: str) -> str:
responses=responses, func_name=func_name
)
if len(response_class_names) > 1:
return f"""typing.Union[{', '.join([f'schemas.{r}' for r in response_class_names])}]"""
return union_for_py_ver([f"schemas.{r}" for r in response_class_names])
elif len(response_class_names) == 0:
return "None"
else:
Expand All @@ -196,7 +197,7 @@ def generate_input_types(self, request_body: dict) -> str:
# It doesn't exist! Generate the schema for it
self.schemas_generator.generate_input_class(schema=request_body)
if len(input_class_names) > 1:
return f"""typing.Union[{', '.join([f'schemas.{r}' for r in input_class_names])}]"""
return union_for_py_ver([f"schemas.{r}" for r in input_class_names])
elif len(input_class_names) == 0:
return "None"
else:
Expand Down
2 changes: 1 addition & 1 deletion clientele/generators/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def add_status_codes_to_bundle(
self.function_and_status_codes_bundle[func_name] = status_code_map

def writeable_function_and_status_codes_bundle(self) -> str:
return f"func_response_code_maps = {self.function_and_status_codes_bundle}"
return f"\nfunc_response_code_maps = {self.function_and_status_codes_bundle}"

def generate_http_content(self) -> None:
write_to_http(
Expand Down
8 changes: 8 additions & 0 deletions clientele/settings.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import platform
from os.path import abspath, dirname

from jinja2 import Environment, PackageLoader
Expand All @@ -10,3 +11,10 @@
VERSION = "0.7.0"

templates = Environment(loader=PackageLoader("clientele", "templates"))


def split_ver():
return [int(v) for v in platform.python_version().split(".")]


PY_VERSION = split_ver()
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import typing
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse

{% if new_unions %}
import types
{% endif %}
import httpx # noqa

from . import config as c # noqa
Expand Down Expand Up @@ -50,7 +52,11 @@ def handle_response(func, response):
status_code = response.status_code
# Get the response types
response_types = typing.get_type_hints(func)["return"]
{% if new_unions %}
if typing.get_origin(response_types) in [typing.Union, types.UnionType]:
{% else %}
if typing.get_origin(response_types) == typing.Union:
{% endif %}
response_types = list(typing.get_args(response_types))
else:
response_types = [response_types]
Expand All @@ -70,3 +76,5 @@ def handle_response(func, response):
][0]
data = response.json()
return response_type.model_validate(data)

# Func map
10 changes: 10 additions & 0 deletions clientele/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from openapi_core import Spec

from clientele import settings


class DataType:
INTEGER = "integer"
Expand Down Expand Up @@ -107,3 +109,11 @@ def get_param_from_ref(spec: Spec, param: dict) -> dict:
def get_schema_from_ref(spec: Spec, ref: str) -> dict:
stripped_name = schema_ref(ref)
return spec["components"]["schemas"][stripped_name]


def union_for_py_ver(union_items: list) -> str:
minor = settings.PY_VERSION[1]
if int(minor) >= 10:
return " | ".join(union_items)
else:
return f"typing.Union[{', '.join(union_items)}]"
5 changes: 5 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change log

## 0.7.0

* `constants.py` has been renamed to `config.py` to better reflect how it is used.
* If you are using Python 3.10 or later, the `typing.Unions` types will generate as the short hand `|` instead. This required a big rework of the code to enable templating.

## 0.6.3

* Packaged application installs in the correct location. Resolving [#6](https://github.com/phalt/clientele/issues/6)
Expand Down
Loading

0 comments on commit ebbbecc

Please sign in to comment.