diff --git a/README.md b/README.md index cea385093..f9ac0044f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,20 @@ +**This repository is a temporary fork of [openapi-python-client](https://github.com/openapi-generators/openapi-python-client), +and is intended for development work on [binarylane-cli](https://github.com/binarylane/binarylane-cli) only.** + +This repository will be removed once the changes here are available in upstream. + +It contains the following modifications: + +- New `data: Operation` attribute for `Endpoint` instances, allowing custom + templates to obtain additional information from pydantic `Operation` object +- New `enable_lazy_imports: bool` attribute for `Property` class, allowing lazy + imports to be disabled +- `Property` can obtain its `description` attribute from its schema, in + addition to the parameter itself +- Fix `types.py` not having `UNSET` and `Unset` in its `__all__` declaration + +---- + ![Run Checks](https://github.com/openapi-generators/openapi-python-client/workflows/Run%20Checks/badge.svg) [![codecov](https://codecov.io/gh/openapi-generators/openapi-python-client/branch/main/graph/badge.svg)](https://codecov.io/gh/triaxtec/openapi-python-client) [![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/) diff --git a/openapi_python_client/__init__.py b/openapi_python_client/__init__.py index 109ce84c7..a61f4c85d 100644 --- a/openapi_python_client/__init__.py +++ b/openapi_python_client/__init__.py @@ -26,7 +26,7 @@ else: from importlib.metadata import version # type: ignore -__version__ = version(__package__) +__version__ = version("binarylane-python-client") class MetaType(str, Enum): diff --git a/openapi_python_client/parser/openapi.py b/openapi_python_client/parser/openapi.py index e4af95a68..662ccdb84 100644 --- a/openapi_python_client/parser/openapi.py +++ b/openapi_python_client/parser/openapi.py @@ -109,6 +109,7 @@ class Endpoint: Describes a single endpoint on the server """ + data: oai.Operation path: str method: str description: Optional[str] @@ -381,6 +382,12 @@ def add_parameters( unique_parameters.add(unique_param) + # In OpenAPI specification both of a parameter, and its schema, may optionally have a description. + # openapi-python-client only uses the schema description for the parameter, so if + # the schema does not have a description we will supply the parameter's description instead. + if isinstance(param.param_schema, oai.Schema) and param.param_schema.description is None: + param.param_schema.description = param.description + prop, new_schemas = property_from_data( name=param.name, required=param.required, @@ -502,6 +509,7 @@ def from_data( name=name, requires_security=bool(data.security), tag=tag, + data=data, ) result, schemas, parameters = Endpoint.add_parameters( diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index c4fe245e0..8187f4e3f 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -188,12 +188,15 @@ class ListProperty(Property, Generic[InnerProp]): inner_property: InnerProp template: ClassVar[str] = "list_property.py.jinja" + def _quoted(self) -> bool: + return not self.inner_property.is_base_type and Property.enable_lazy_imports + # pylint: disable=unused-argument def get_base_type_string(self, *, quoted: bool = False) -> str: - return f"List[{self.inner_property.get_type_string(quoted=not self.inner_property.is_base_type)}]" + return f"List[{self.inner_property.get_type_string(quoted=self._quoted())}]" def get_base_json_type_string(self, *, quoted: bool = False) -> str: - return f"List[{self.inner_property.get_type_string(json=True, quoted=not self.inner_property.is_base_type)}]" + return f"List[{self.inner_property.get_type_string(json=True, quoted=self._quoted())}]" def get_instance_type_string(self) -> str: """Get a string representation of runtime type that should be used for `isinstance` checks""" @@ -595,6 +598,8 @@ def _property_from_ref( ) if parent: prop = attr.evolve(prop, nullable=parent.nullable) + if parent.description: + prop = attr.evolve(prop, description=parent.description) if isinstance(prop, EnumProperty): default = get_enum_default(prop, parent) if isinstance(default, PropertyError): diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 38080cd40..4bdd90a40 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -60,6 +60,10 @@ def get_imports(self, *, prefix: str) -> Set[str]: "from typing import cast", } ) + + if not Property.enable_lazy_imports: + imports.update({f"from {prefix}{self.self_import}"}) + return imports def get_lazy_imports(self, *, prefix: str) -> Set[str]: @@ -69,6 +73,8 @@ def get_lazy_imports(self, *, prefix: str) -> Set[str]: prefix: A prefix to put before any relative (local) module names. This should be the number of . to get back to the root of the generated client. """ + if not Property.enable_lazy_imports: + return set() return {f"from {prefix}{self.self_import}"} def set_relative_imports(self, relative_imports: Set[str]) -> None: diff --git a/openapi_python_client/parser/properties/property.py b/openapi_python_client/parser/properties/property.py index 4e2aea76c..8733a2ab4 100644 --- a/openapi_python_client/parser/properties/property.py +++ b/openapi_python_client/parser/properties/property.py @@ -30,6 +30,8 @@ class Property: ValidationError: Raised when the default value fails to be converted to the expected type """ + enable_lazy_imports = False + name: str required: bool nullable: bool @@ -143,8 +145,8 @@ def to_string(self) -> str: default = None if default is not None: - return f"{self.python_name}: {self.get_type_string(quoted=True)} = {default}" - return f"{self.python_name}: {self.get_type_string(quoted=True)}" + return f"{self.python_name}: {self.get_type_string(quoted=Property.enable_lazy_imports)} = {default}" + return f"{self.python_name}: {self.get_type_string(quoted=Property.enable_lazy_imports)}" def to_docstring(self) -> str: """Returns property docstring""" diff --git a/openapi_python_client/parser/properties/schemas.py b/openapi_python_client/parser/properties/schemas.py index f3c27a91e..b94c028a8 100644 --- a/openapi_python_client/parser/properties/schemas.py +++ b/openapi_python_client/parser/properties/schemas.py @@ -165,6 +165,7 @@ def parameter_from_data( style=data.style, param_schema=data.param_schema, param_in=data.param_in, + description=data.description, ) parameters = attr.evolve(parameters, classes_by_name={**parameters.classes_by_name, name: new_param}) return new_param, parameters diff --git a/openapi_python_client/schema/openapi_schema_pydantic/open_api.py b/openapi_python_client/schema/openapi_schema_pydantic/open_api.py index 50fdebd5e..fad3b0c27 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/open_api.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/open_api.py @@ -33,7 +33,7 @@ class OpenAPI(BaseModel): security: Optional[List[SecurityRequirement]] = None tags: Optional[List[Tag]] = None externalDocs: Optional[ExternalDocumentation] = None - openapi: 'Union[Literal["3.0.0"], Literal["3.0.1"], Literal["3.0.2"], Literal["3.0.3"]]' + openapi: 'Union[Literal["3.0.0"], Literal["3.0.1"], Literal["3.0.2"], Literal["3.0.3"], Literal["3.0.4"]]' class Config: # pylint: disable=missing-class-docstring extra = Extra.allow diff --git a/openapi_python_client/templates/types.py.jinja b/openapi_python_client/templates/types.py.jinja index c746db6e1..b6aa62b05 100644 --- a/openapi_python_client/templates/types.py.jinja +++ b/openapi_python_client/templates/types.py.jinja @@ -42,4 +42,4 @@ class Response(Generic[T]): parsed: Optional[T] -__all__ = ["File", "Response", "FileJsonType"] +__all__ = ["File", "Response", "FileJsonType", "UNSET", "Unset"] diff --git a/pyproject.toml b/pyproject.toml index f2c254a38..3bbf02afc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] -name = "openapi-python-client" -version = "0.13.3" +name = "binarylane-python-client" +version = "0.13.3a2" description = "Generate modern Python clients from OpenAPI" repository = "https://github.com/triaxtec/openapi-python-client" license = "MIT" diff --git a/tests/conftest.py b/tests/conftest.py index 7f8442ab7..b54c0d7cd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -21,6 +21,9 @@ from openapi_python_client.schema.openapi_schema_pydantic import Parameter from openapi_python_client.schema.parameter_location import ParameterLocation +# Existing test cases expect lazy imports +Property.enable_lazy_imports = True + @pytest.fixture def model_property_factory() -> Callable[..., ModelProperty]: diff --git a/tests/test_parser/test_openapi.py b/tests/test_parser/test_openapi.py index 8ef6b0a6a..7fc876b1d 100644 --- a/tests/test_parser/test_openapi.py +++ b/tests/test_parser/test_openapi.py @@ -126,6 +126,7 @@ def make_endpoint(self): from openapi_python_client.parser.openapi import Endpoint return Endpoint( + data=oai.Operation(responses=dict()), path="path", method="method", description=None, @@ -1077,6 +1078,7 @@ def test_from_data_standard(self, mocker): add_parameters.assert_called_once_with( endpoint=Endpoint( + data=data, path=path, method=method, description=data.description, @@ -1128,6 +1130,7 @@ def test_from_data_no_operation_id(self, mocker): add_parameters.assert_called_once_with( endpoint=Endpoint( + data=data, path=path, method=method, description=data.description, @@ -1180,6 +1183,7 @@ def test_from_data_no_security(self, mocker): add_parameters.assert_called_once_with( endpoint=Endpoint( + data=data, path=path, method=method, description=data.description,