diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/default/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/default/__init__.py index a4580103f..726a77104 100644 --- a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/default/__init__.py +++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/default/__init__.py @@ -2,7 +2,7 @@ import types -from . import get_common_parameters, post_common_parameters +from . import get_common_parameters, post_common_parameters, reserved_parameters class DefaultEndpoints: @@ -13,3 +13,7 @@ def get_common_parameters(cls) -> types.ModuleType: @classmethod def post_common_parameters(cls) -> types.ModuleType: return post_common_parameters + + @classmethod + def reserved_parameters(cls) -> types.ModuleType: + return reserved_parameters diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/default/reserved_parameters.py b/end_to_end_tests/golden-record/my_test_api_client/api/default/reserved_parameters.py new file mode 100644 index 000000000..d7eceda17 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/api/default/reserved_parameters.py @@ -0,0 +1,119 @@ +from http import HTTPStatus +from typing import Any, Dict, Optional + +import httpx + +from ... import errors +from ...client import Client +from ...types import UNSET, Response + + +def _get_kwargs( + *, + client: Client, + client_query: str, + url_query: str, +) -> Dict[str, Any]: + url = "{}/naming/reserved-parameters".format(client.base_url) + + headers: Dict[str, str] = client.get_headers() + cookies: Dict[str, Any] = client.get_cookies() + + params: Dict[str, Any] = {} + params["client"] = client_query + + params["url"] = url_query + + params = {k: v for k, v in params.items() if v is not UNSET and v is not None} + + return { + "method": "get", + "url": url, + "headers": headers, + "cookies": cookies, + "timeout": client.get_timeout(), + "follow_redirects": client.follow_redirects, + "params": params, + } + + +def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: + if response.status_code == HTTPStatus.OK: + return None + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + *, + client: Client, + client_query: str, + url_query: str, +) -> Response[Any]: + """ + Args: + client_query (str): + url_query (str): + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[Any] + """ + + kwargs = _get_kwargs( + client=client, + client_query=client_query, + url_query=url_query, + ) + + response = httpx.request( + verify=client.verify_ssl, + **kwargs, + ) + + return _build_response(client=client, response=response) + + +async def asyncio_detailed( + *, + client: Client, + client_query: str, + url_query: str, +) -> Response[Any]: + """ + Args: + client_query (str): + url_query (str): + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[Any] + """ + + kwargs = _get_kwargs( + client=client, + client_query=client_query, + url_query=url_query, + ) + + async with httpx.AsyncClient(verify=client.verify_ssl) as _client: + response = await _client.request(**kwargs) + + return _build_response(client=client, response=response) diff --git a/end_to_end_tests/openapi.json b/end_to_end_tests/openapi.json index 11baa0cc5..1dfeb8322 100644 --- a/end_to_end_tests/openapi.json +++ b/end_to_end_tests/openapi.json @@ -1150,6 +1150,35 @@ } } }, + "/naming/reserved-parameters": { + "description": "Ensure that parameters can't be named things that the code generator needs as variables", + "get": { + "operationId": "reserved-parameters", + "parameters": [ + { + "name": "client", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "url", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "" + } + } + } + }, "/parameter-references/{path_param}": { "get": { "tags": [ diff --git a/openapi_python_client/parser/openapi.py b/openapi_python_client/parser/openapi.py index 05fe112c3..90c1e355b 100644 --- a/openapi_python_client/parser/openapi.py +++ b/openapi_python_client/parser/openapi.py @@ -14,6 +14,7 @@ from ..utils import PythonIdentifier, get_content_type from .errors import GeneratorError, ParseError, PropertyError from .properties import ( + AnyProperty, Class, EnumProperty, ModelProperty, @@ -346,11 +347,15 @@ def add_parameters( endpoint = deepcopy(endpoint) unique_parameters: Set[Tuple[str, oai.ParameterLocation]] = set() - parameters_by_location = { + parameters_by_location: Dict[str, Dict[str, Property]] = { oai.ParameterLocation.QUERY: endpoint.query_parameters, oai.ParameterLocation.PATH: endpoint.path_parameters, oai.ParameterLocation.HEADER: endpoint.header_parameters, oai.ParameterLocation.COOKIE: endpoint.cookie_parameters, + "RESERVED": { # These can't be param names because codegen needs them as vars, the properties don't matter + "client": AnyProperty("client", True, False, None, PythonIdentifier("client", ""), None, None), + "url": AnyProperty("url", True, False, None, PythonIdentifier("url", ""), None, None), + }, } for param in data.parameters: @@ -412,7 +417,7 @@ def add_parameters( continue existing_prop: Property = parameters_dict[prop.name] # Existing should be converted too for consistency - endpoint.used_python_identifiers.remove(existing_prop.python_name) + endpoint.used_python_identifiers.discard(existing_prop.python_name) existing_prop.set_python_name(new_name=f"{existing_prop.name}_{location}", config=config) if existing_prop.python_name in endpoint.used_python_identifiers: