2929import time
3030from datetime import datetime , timezone
3131from pathlib import Path
32- from typing import Any , Dict , Optional , Union
32+ from typing import Any
3333
3434import httpx
3535import yaml
@@ -75,7 +75,7 @@ def with_version(url: str, version: str) -> str:
7575_REDACTED_HEADERS = frozenset ({"x-auth-token" , "x-subject-token" , "authorization" })
7676
7777
78- def _redact_headers (headers : Dict [str , str ]) -> Dict [str , str ]:
78+ def _redact_headers (headers : dict [str , str ]) -> dict [str , str ]:
7979 """Return a copy of ``headers`` with sensitive values replaced by ``***``."""
8080 return {k : ("***" if k .lower () in _REDACTED_HEADERS else v )
8181 for k , v in headers .items ()}
@@ -116,7 +116,7 @@ def _redact_headers(headers: Dict[str, str]) -> Dict[str, str]:
116116)
117117
118118
119- def _parse_retry_after (value : str ) -> Optional [ float ] :
119+ def _parse_retry_after (value : str ) -> float | None :
120120 """Interpret a ``Retry-After`` header per RFC 7231 §7.1.3.
121121
122122 Accepts either delta-seconds (``"5"``) or an HTTP-date (``"Wed, 21 Oct 2015
@@ -528,7 +528,7 @@ def auth_url(self) -> str:
528528 return self ._auth_url
529529
530530 @property
531- def region_name (self ) -> Optional [ str ] :
531+ def region_name (self ) -> str | None :
532532 """The region used to filter the catalogue, or ``None``."""
533533 return self ._region_name
534534
@@ -540,7 +540,7 @@ def interface(self) -> str:
540540 return self ._interface
541541
542542 @property
543- def project_id (self ) -> Optional [ str ] :
543+ def project_id (self ) -> str | None :
544544 """The project ID this client is scoped to, if any."""
545545 return self ._project_id
546546
@@ -652,9 +652,9 @@ def rating_url(self) -> str:
652652
653653 # ── Generic HTTP helpers ──────────────────────────────────────────────────
654654
655- def _headers (self , extra : Optional [ Dict [ str , str ]] = None ,
656- url : Optional [ str ] = None ) -> Dict [str , str ]:
657- h : Dict [str , str ] = {
655+ def _headers (self , extra : dict [ str , str ] | None = None ,
656+ url : str | None = None ) -> dict [str , str ]:
657+ h : dict [str , str ] = {
658658 "X-Auth-Token" : self ._token or "" ,
659659 "Accept" : "application/json" ,
660660 }
@@ -749,7 +749,7 @@ def _handle_response(self, response: httpx.Response) -> Any:
749749 return response .json ()
750750
751751 def _send (self , method : str , url : str ,
752- extra_headers : Optional [ Dict [ str , str ]] = None ,
752+ extra_headers : dict [ str , str ] | None = None ,
753753 ** kwargs : Any ) -> httpx .Response :
754754 """Single HTTP send with inline 401→re-auth retry (once, cached token only)."""
755755 started = time .monotonic ()
@@ -785,7 +785,7 @@ def _send(self, method: str, url: str,
785785 return resp
786786
787787 def _request (self , method : str , url : str ,
788- extra_headers : Optional [ Dict [ str , str ]] = None ,
788+ extra_headers : dict [ str , str ] | None = None ,
789789 ** kwargs : Any ) -> Any :
790790 """Execute an HTTP request with three independent recovery mechanisms:
791791
@@ -849,31 +849,31 @@ def _request(self, method: str, url: str,
849849
850850 # ── Public HTTP methods ───────────────────────────────────────────────────
851851
852- def get (self , url : str , params : Optional [ Dict [ str , Any ]] = None ,
853- headers : Optional [ Dict [ str , str ]] = None ) -> Any :
852+ def get (self , url : str , params : dict [ str , Any ] | None = None ,
853+ headers : dict [ str , str ] | None = None ) -> Any :
854854 return self ._request ("get" , url , extra_headers = headers , params = params )
855855
856- def post (self , url : str , json : Optional [ Union [ Dict [ str , Any ], list ]] = None ,
857- headers : Optional [ Dict [ str , str ]] = None ) -> Any :
856+ def post (self , url : str , json : dict [ str , Any ] | list | None = None ,
857+ headers : dict [ str , str ] | None = None ) -> Any :
858858 return self ._request ("post" , url , extra_headers = headers , json = json )
859859
860- def put (self , url : str , json : Optional [ Union [ Dict [ str , Any ], list ]] = None ,
861- headers : Optional [ Dict [ str , str ]] = None ) -> Any :
860+ def put (self , url : str , json : dict [ str , Any ] | list | None = None ,
861+ headers : dict [ str , str ] | None = None ) -> Any :
862862 return self ._request ("put" , url , extra_headers = headers , json = json )
863863
864- def patch (self , url : str , json : Optional [ Union [ Dict [ str , Any ], list ]] = None ,
865- content : Optional [ bytes ] = None ,
866- content_type : Optional [ str ] = None ) -> Any :
867- extra : Dict [str , str ] = {}
864+ def patch (self , url : str , json : dict [ str , Any ] | list | None = None ,
865+ content : bytes | None = None ,
866+ content_type : str | None = None ) -> Any :
867+ extra : dict [str , str ] = {}
868868 if content_type :
869869 extra ["Content-Type" ] = content_type
870870 if content is not None :
871871 return self ._request ("patch" , url , extra_headers = extra or None , content = content )
872872 return self ._request ("patch" , url , extra_headers = extra or None , json = json )
873873
874- def delete (self , url : str , params : Optional [ Dict [ str , Any ]] = None ,
875- headers : Optional [ Dict [ str , str ]] = None ,
876- json : Optional [ Union [ Dict [ str , Any ], list ]] = None ) -> Any :
874+ def delete (self , url : str , params : dict [ str , Any ] | None = None ,
875+ headers : dict [ str , str ] | None = None ,
876+ json : dict [ str , Any ] | list | None = None ) -> Any :
877877 # Some OpenStack APIs (e.g. Barbican container consumers) require a
878878 # JSON body on DELETE — uncommon for HTTP but valid per RFC 7231.
879879 # Only forward `json` when supplied so httpx's per-method shortcut
@@ -885,8 +885,8 @@ def delete(self, url: str, params: Optional[Dict[str, Any]] = None,
885885
886886 def paginate (self , url : str , key : str , * ,
887887 page_size : int = 1000 ,
888- params : Optional [ Dict [ str , Any ]] = None ,
889- max_items : Optional [ int ] = None ) -> list :
888+ params : dict [ str , Any ] | None = None ,
889+ max_items : int | None = None ) -> list :
890890 """Walk an OpenStack list endpoint using marker-based pagination.
891891
892892 Many services (Nova, Cinder, Neutron with ``allow_pagination``) cap
@@ -908,8 +908,8 @@ def paginate(self, url: str, key: str, *,
908908 max_items: stop once this many items have been collected.
909909 """
910910 collected : list = []
911- marker : Optional [ str ] = None
912- base_params : Dict [str , Any ] = dict (params or {})
911+ marker : str | None = None
912+ base_params : dict [str , Any ] = dict (params or {})
913913 while True :
914914 p = dict (base_params )
915915 p ["limit" ] = page_size
@@ -932,8 +932,8 @@ def paginate(self, url: str, key: str, *,
932932
933933 def put_stream (self , url : str , * , content : Any ,
934934 content_type : str = "application/octet-stream" ,
935- content_length : Optional [ int ] = None ,
936- extra_headers : Optional [ Dict [ str , str ]] = None ) -> httpx .Response :
935+ content_length : int | None = None ,
936+ extra_headers : dict [ str , str ] | None = None ) -> httpx .Response :
937937 """PUT a streaming body (file-like or iterable). Returns the raw httpx response.
938938
939939 Streamed bodies cannot be replayed, so this path deliberately bypasses
@@ -952,8 +952,8 @@ def put_stream(self, url: str, *, content: Any,
952952
953953 def post_stream (self , url : str , * , content : Any ,
954954 content_type : str = "application/octet-stream" ,
955- content_length : Optional [ int ] = None ,
956- extra_headers : Optional [ Dict [ str , str ]] = None ) -> httpx .Response :
955+ content_length : int | None = None ,
956+ extra_headers : dict [ str , str ] | None = None ) -> httpx .Response :
957957 """POST a streaming or raw body. Returns the raw httpx response.
958958
959959 Like :meth:`put_stream`, this bypasses ``_request``'s retry loop so the
@@ -970,7 +970,7 @@ def post_stream(self, url: str, *, content: Any,
970970 return self ._http .post (url , headers = headers , content = content )
971971
972972 def post_no_body (self , url : str , * ,
973- extra_headers : Optional [ Dict [ str , str ]] = None ) -> httpx .Response :
973+ extra_headers : dict [ str , str ] | None = None ) -> httpx .Response :
974974 """POST without a JSON body, returning the raw response.
975975
976976 Used by Swift metadata updates (account/container/object), where the
@@ -983,7 +983,7 @@ def post_no_body(self, url: str, *,
983983 return self ._http .post (url , headers = headers )
984984
985985 def head_request (self , url : str , * ,
986- extra_headers : Optional [ Dict [ str , str ]] = None ) -> httpx .Response :
986+ extra_headers : dict [ str , str ] | None = None ) -> httpx .Response :
987987 """HEAD request with auth headers — returns the raw response so callers
988988 can read metadata from response headers (Swift account/container/object).
989989 """
@@ -993,7 +993,7 @@ def head_request(self, url: str, *,
993993 return self ._http .head (url , headers = headers )
994994
995995 def get_stream (self , url : str , * ,
996- extra_headers : Optional [ Dict [ str , str ]] = None ):
996+ extra_headers : dict [ str , str ] | None = None ):
997997 """GET that returns a streaming response context manager."""
998998 headers = self ._headers (url = url )
999999 if extra_headers :
0 commit comments