Skip to content

Commit c6a93b6

Browse files
Update client file to include url
1 parent d81bfa8 commit c6a93b6

File tree

4 files changed

+6
-294
lines changed
  • end_to_end_tests
    • golden-record/my_test_api_client
    • literal-enums-golden-record/my_enum_api_client
    • test-3-1-golden-record/test_3_1_features_client
  • openapi_python_client/templates

4 files changed

+6
-294
lines changed

end_to_end_tests/golden-record/my_test_api_client/client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,8 @@ def replace_client_path(client: Client, base_path: str) -> Client:
274274
parsed = urllib.parse.urlparse(client.base_url)
275275
# _replace is not private, it's part of the NamedTuple API but prefixed _ to avoid conflicts
276276
updated_url = parsed._replace(path=base_path)
277-
return client.with_base_url(updated_url.geturl())
277+
client.base_url = updated_url.geturl()
278+
return client
278279

279280

280281
def v3_stable_client(client: Client) -> Client:

end_to_end_tests/literal-enums-golden-record/my_enum_api_client/client.py

Lines changed: 2 additions & 293 deletions
Original file line numberDiff line numberDiff line change
@@ -274,299 +274,8 @@ def replace_client_path(client: Client, base_path: str) -> Client:
274274
parsed = urllib.parse.urlparse(client.base_url)
275275
# _replace is not private, it's part of the NamedTuple API but prefixed _ to avoid conflicts
276276
updated_url = parsed._replace(path=base_path)
277-
return client.with_base_url(updated_url.geturl())
278-
279-
280-
def v3_stable_client(client: Client) -> Client:
281-
"""Override a client's base URL with a v2 stable path."""
282-
return replace_client_path(client, "api/v3-draft")
283-
284-
285-
def v3_alpha_client(client: Client) -> Client:
286-
"""Override a client's base URL with a v2-alpha path."""
287-
return replace_client_path(client, "api/v3-alpha")
288-
289-
290-
def v3_beta_client(client: Client) -> Client:
291-
"""Override a client's base URL with a v2-beta path."""
292-
return replace_client_path(client, "api/v3-beta")
293-
import ssl
294-
from typing import Any, Optional, Union
295-
296-
import httpx
297-
from attrs import define, evolve, field
298-
import urllib.parse
299-
300-
301-
@define
302-
class Client:
303-
"""A class for keeping track of data related to the API
304-
305-
The following are accepted as keyword arguments and will be used to construct httpx Clients internally:
306-
307-
``base_url``: The base URL for the API, all requests are made to a relative path to this URL
308-
309-
``cookies``: A dictionary of cookies to be sent with every request
310-
311-
``headers``: A dictionary of headers to be sent with every request
312-
313-
``timeout``: The maximum amount of a time a request can take. API functions will raise
314-
httpx.TimeoutException if this is exceeded.
315-
316-
``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production,
317-
but can be set to False for testing purposes.
318-
319-
``follow_redirects``: Whether or not to follow redirects. Default value is False.
320-
321-
``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor.
322-
323-
324-
Attributes:
325-
raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a
326-
status code that was not documented in the source OpenAPI document. Can also be provided as a keyword
327-
argument to the constructor.
328-
"""
329-
330-
raise_on_unexpected_status: bool = field(default=False, kw_only=True)
331-
_base_url: str = field(alias="base_url")
332-
_cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies")
333-
_headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers")
334-
_timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout")
335-
_verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl")
336-
_follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects")
337-
_httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args")
338-
_client: Optional[httpx.Client] = field(default=None, init=False)
339-
_async_client: Optional[httpx.AsyncClient] = field(default=None, init=False)
340-
341-
def with_headers(self, headers: dict[str, str]) -> "Client":
342-
"""Get a new client matching this one with additional headers"""
343-
if self._client is not None:
344-
self._client.headers.update(headers)
345-
if self._async_client is not None:
346-
self._async_client.headers.update(headers)
347-
return evolve(self, headers={**self._headers, **headers})
348-
349-
def with_cookies(self, cookies: dict[str, str]) -> "Client":
350-
"""Get a new client matching this one with additional cookies"""
351-
if self._client is not None:
352-
self._client.cookies.update(cookies)
353-
if self._async_client is not None:
354-
self._async_client.cookies.update(cookies)
355-
return evolve(self, cookies={**self._cookies, **cookies})
356-
357-
def with_timeout(self, timeout: httpx.Timeout) -> "Client":
358-
"""Get a new client matching this one with a new timeout (in seconds)"""
359-
if self._client is not None:
360-
self._client.timeout = timeout
361-
if self._async_client is not None:
362-
self._async_client.timeout = timeout
363-
return evolve(self, timeout=timeout)
364-
365-
def set_httpx_client(self, client: httpx.Client) -> "Client":
366-
"""Manually set the underlying httpx.Client
367-
368-
**NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
369-
"""
370-
self._client = client
371-
return self
372-
373-
def get_httpx_client(self) -> httpx.Client:
374-
"""Get the underlying httpx.Client, constructing a new one if not previously set"""
375-
if self._client is None:
376-
self._client = httpx.Client(
377-
base_url=self._base_url,
378-
cookies=self._cookies,
379-
headers=self._headers,
380-
timeout=self._timeout,
381-
verify=self._verify_ssl,
382-
follow_redirects=self._follow_redirects,
383-
**self._httpx_args,
384-
)
385-
return self._client
386-
387-
def __enter__(self) -> "Client":
388-
"""Enter a context manager for self.client—you cannot enter twice (see httpx docs)"""
389-
self.get_httpx_client().__enter__()
390-
return self
391-
392-
def __exit__(self, *args: Any, **kwargs: Any) -> None:
393-
"""Exit a context manager for internal httpx.Client (see httpx docs)"""
394-
self.get_httpx_client().__exit__(*args, **kwargs)
395-
396-
def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "Client":
397-
"""Manually the underlying httpx.AsyncClient
398-
399-
**NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
400-
"""
401-
self._async_client = async_client
402-
return self
403-
404-
def get_async_httpx_client(self) -> httpx.AsyncClient:
405-
"""Get the underlying httpx.AsyncClient, constructing a new one if not previously set"""
406-
if self._async_client is None:
407-
self._async_client = httpx.AsyncClient(
408-
base_url=self._base_url,
409-
cookies=self._cookies,
410-
headers=self._headers,
411-
timeout=self._timeout,
412-
verify=self._verify_ssl,
413-
follow_redirects=self._follow_redirects,
414-
**self._httpx_args,
415-
)
416-
return self._async_client
417-
418-
async def __aenter__(self) -> "Client":
419-
"""Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)"""
420-
await self.get_async_httpx_client().__aenter__()
421-
return self
422-
423-
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
424-
"""Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
425-
await self.get_async_httpx_client().__aexit__(*args, **kwargs)
426-
427-
428-
@define
429-
class AuthenticatedClient:
430-
"""A Client which has been authenticated for use on secured endpoints
431-
432-
The following are accepted as keyword arguments and will be used to construct httpx Clients internally:
433-
434-
``base_url``: The base URL for the API, all requests are made to a relative path to this URL
435-
436-
``cookies``: A dictionary of cookies to be sent with every request
437-
438-
``headers``: A dictionary of headers to be sent with every request
439-
440-
``timeout``: The maximum amount of a time a request can take. API functions will raise
441-
httpx.TimeoutException if this is exceeded.
442-
443-
``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production,
444-
but can be set to False for testing purposes.
445-
446-
``follow_redirects``: Whether or not to follow redirects. Default value is False.
447-
448-
``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor.
449-
450-
451-
Attributes:
452-
raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a
453-
status code that was not documented in the source OpenAPI document. Can also be provided as a keyword
454-
argument to the constructor.
455-
token: The token to use for authentication
456-
prefix: The prefix to use for the Authorization header
457-
auth_header_name: The name of the Authorization header
458-
"""
459-
460-
raise_on_unexpected_status: bool = field(default=False, kw_only=True)
461-
_base_url: str = field(alias="base_url")
462-
_cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies")
463-
_headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers")
464-
_timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout")
465-
_verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl")
466-
_follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects")
467-
_httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args")
468-
_client: Optional[httpx.Client] = field(default=None, init=False)
469-
_async_client: Optional[httpx.AsyncClient] = field(default=None, init=False)
470-
471-
token: str
472-
prefix: str = "Bearer"
473-
auth_header_name: str = "Authorization"
474-
475-
def with_headers(self, headers: dict[str, str]) -> "AuthenticatedClient":
476-
"""Get a new client matching this one with additional headers"""
477-
if self._client is not None:
478-
self._client.headers.update(headers)
479-
if self._async_client is not None:
480-
self._async_client.headers.update(headers)
481-
return evolve(self, headers={**self._headers, **headers})
482-
483-
def with_cookies(self, cookies: dict[str, str]) -> "AuthenticatedClient":
484-
"""Get a new client matching this one with additional cookies"""
485-
if self._client is not None:
486-
self._client.cookies.update(cookies)
487-
if self._async_client is not None:
488-
self._async_client.cookies.update(cookies)
489-
return evolve(self, cookies={**self._cookies, **cookies})
490-
491-
def with_timeout(self, timeout: httpx.Timeout) -> "AuthenticatedClient":
492-
"""Get a new client matching this one with a new timeout (in seconds)"""
493-
if self._client is not None:
494-
self._client.timeout = timeout
495-
if self._async_client is not None:
496-
self._async_client.timeout = timeout
497-
return evolve(self, timeout=timeout)
498-
499-
def set_httpx_client(self, client: httpx.Client) -> "AuthenticatedClient":
500-
"""Manually set the underlying httpx.Client
501-
502-
**NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
503-
"""
504-
self._client = client
505-
return self
506-
507-
def get_httpx_client(self) -> httpx.Client:
508-
"""Get the underlying httpx.Client, constructing a new one if not previously set"""
509-
if self._client is None:
510-
self._headers[self.auth_header_name] = f"{self.prefix} {self.token}" if self.prefix else self.token
511-
self._client = httpx.Client(
512-
base_url=self._base_url,
513-
cookies=self._cookies,
514-
headers=self._headers,
515-
timeout=self._timeout,
516-
verify=self._verify_ssl,
517-
follow_redirects=self._follow_redirects,
518-
**self._httpx_args,
519-
)
520-
return self._client
521-
522-
def __enter__(self) -> "AuthenticatedClient":
523-
"""Enter a context manager for self.client—you cannot enter twice (see httpx docs)"""
524-
self.get_httpx_client().__enter__()
525-
return self
526-
527-
def __exit__(self, *args: Any, **kwargs: Any) -> None:
528-
"""Exit a context manager for internal httpx.Client (see httpx docs)"""
529-
self.get_httpx_client().__exit__(*args, **kwargs)
530-
531-
def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "AuthenticatedClient":
532-
"""Manually the underlying httpx.AsyncClient
533-
534-
**NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
535-
"""
536-
self._async_client = async_client
537-
return self
538-
539-
def get_async_httpx_client(self) -> httpx.AsyncClient:
540-
"""Get the underlying httpx.AsyncClient, constructing a new one if not previously set"""
541-
if self._async_client is None:
542-
self._headers[self.auth_header_name] = f"{self.prefix} {self.token}" if self.prefix else self.token
543-
self._async_client = httpx.AsyncClient(
544-
base_url=self._base_url,
545-
cookies=self._cookies,
546-
headers=self._headers,
547-
timeout=self._timeout,
548-
verify=self._verify_ssl,
549-
follow_redirects=self._follow_redirects,
550-
**self._httpx_args,
551-
)
552-
return self._async_client
553-
554-
async def __aenter__(self) -> "AuthenticatedClient":
555-
"""Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)"""
556-
await self.get_async_httpx_client().__aenter__()
557-
return self
558-
559-
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
560-
"""Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
561-
await self.get_async_httpx_client().__aexit__(*args, **kwargs)
562-
563-
564-
def replace_client_path(client: Client, base_path: str) -> Client:
565-
"""Override a client's base URL with a new path. Does not update scheme, host, or other URL parts."""
566-
parsed = urllib.parse.urlparse(client.base_url)
567-
# _replace is not private, it's part of the NamedTuple API but prefixed _ to avoid conflicts
568-
updated_url = parsed._replace(path=base_path)
569-
return client.with_base_url(updated_url.geturl())
277+
client.base_url = updated_url.geturl()
278+
return client
570279

571280

572281
def v3_stable_client(client: Client) -> Client:

end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/client.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ def replace_client_path(client: Client, base_path: str) -> Client:
274274
parsed = urllib.parse.urlparse(client.base_url)
275275
# _replace is not private, it's part of the NamedTuple API but prefixed _ to avoid conflicts
276276
updated_url = parsed._replace(path=base_path)
277+
client.base_url = updated_url.geturl()
277278
return client.with_base_url(updated_url.geturl())
278279

279280

openapi_python_client/templates/client.py.jinja

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ def replace_client_path(client: Client, base_path: str) -> Client:
196196
parsed = urllib.parse.urlparse(client.base_url)
197197
# _replace is not private, it's part of the NamedTuple API but prefixed _ to avoid conflicts
198198
updated_url = parsed._replace(path=base_path)
199+
client.base_url = updated_url.geturl()
199200
return client.with_base_url(updated_url.geturl())
200201

201202

0 commit comments

Comments
 (0)