Skip to content

Commit 1a3a022

Browse files
committed
client: accept dict[...] in ApiClient.__deserialize (keep dict(...) support)
Adds post-gen patch; aligns with list[...] and pydantic adapters; no behavior change for legacy types. Fixes #2463
1 parent fe1a331 commit 1a3a022

File tree

4 files changed

+77
-0
lines changed

4 files changed

+77
-0
lines changed

kubernetes/client/api_client.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,25 @@ def __deserialize(self, data, klass):
285285
return {k: self.__deserialize(v, sub_kls)
286286
for k, v in six.iteritems(data)}
287287

288+
if klass.startswith('dict[') and klass.endswith(']'):
289+
# Parse dict[key_type, value_type] respecting nested brackets
290+
inner = klass[len('dict['):-1]
291+
bracket_depth = 0
292+
comma_pos = -1
293+
for i, char in enumerate(inner):
294+
if char in '([{':
295+
bracket_depth += 1
296+
elif char in ')]}':
297+
bracket_depth -= 1
298+
elif char == ',' and bracket_depth == 0:
299+
comma_pos = i
300+
break
301+
302+
if comma_pos != -1:
303+
value_type = inner[comma_pos + 1:].strip()
304+
return {k: self.__deserialize(v, value_type)
305+
for k, v in six.iteritems(data)}
306+
288307
# convert str to class
289308
if klass in self.NATIVE_TYPES_MAPPING:
290309
klass = self.NATIVE_TYPES_MAPPING[klass]

kubernetes/test/test_api_client.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,32 @@ def test_atexit_closes_threadpool(self):
2525
atexit._run_exitfuncs()
2626
self.assertIsNone(client._pool)
2727

28+
def test_deserialize_dict_syntax_compatibility(self):
29+
"""Test ApiClient.__deserialize supports both dict(str, str) and dict[str, str] syntax"""
30+
client = kubernetes.client.ApiClient()
31+
32+
# Test data
33+
test_data = {
34+
'key1': 'value1',
35+
'key2': 'value2'
36+
}
37+
38+
# Test legacy syntax: dict(str, str)
39+
result_legacy = client._ApiClient__deserialize(test_data, 'dict(str, str)')
40+
self.assertEqual(result_legacy, test_data)
41+
42+
# Test modern syntax: dict[str, str]
43+
result_modern = client._ApiClient__deserialize(test_data, 'dict[str, str]')
44+
self.assertEqual(result_modern, test_data)
45+
46+
# Test nested dict: dict[str, dict[str, str]]
47+
nested_data = {
48+
'outer1': {'inner1': 'value1', 'inner2': 'value2'},
49+
'outer2': {'inner3': 'value3'}
50+
}
51+
result_nested = client._ApiClient__deserialize(nested_data, 'dict[str, dict[str, str]]')
52+
self.assertEqual(result_nested, nested_data)
53+
2854
def test_rest_proxycare(self):
2955

3056
pool = { 'proxy': urllib3.ProxyManager, 'direct': urllib3.PoolManager }
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--- a/kubernetes/client/api_client.py 2025-11-01 14:37:47
2+
+++ b/kubernetes/client/api_client.py 2025-11-01 15:54:48
3+
@@ -285,6 +285,25 @@
4+
return {k: self.__deserialize(v, sub_kls)
5+
for k, v in six.iteritems(data)}
6+
7+
+ if klass.startswith('dict[') and klass.endswith(']'):
8+
+ # Parse dict[key_type, value_type] respecting nested brackets
9+
+ inner = klass[len('dict['):-1]
10+
+ bracket_depth = 0
11+
+ comma_pos = -1
12+
+ for i, char in enumerate(inner):
13+
+ if char in '([{':
14+
+ bracket_depth += 1
15+
+ elif char in ')]}':
16+
+ bracket_depth -= 1
17+
+ elif char == ',' and bracket_depth == 0:
18+
+ comma_pos = i
19+
+ break
20+
+
21+
+ if comma_pos != -1:
22+
+ value_type = inner[comma_pos + 1:].strip()
23+
+ return {k: self.__deserialize(v, value_type)
24+
+ for k, v in six.iteritems(data)}
25+
+
26+
# convert str to class
27+
if klass in self.NATIVE_TYPES_MAPPING:
28+
klass = self.NATIVE_TYPES_MAPPING[klass]

scripts/update-client.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ git apply "${SCRIPT_ROOT}/rest_client_patch.diff"
7878
# once we upgrade to a version of swagger-codegen that includes it (version>= 6.6.0).
7979
# See https://github.com/OpenAPITools/openapi-generator/pull/15283
8080
git apply "${SCRIPT_ROOT}/rest_sni_patch.diff"
81+
# Support dict[str, str] syntax in ApiClient deserializer alongside legacy dict(str, str)
82+
# This enables forward compatibility with modern Python typing syntax while maintaining
83+
# backward compatibility. Users can now convert openapi_types for Pydantic integration.
84+
git apply "${SCRIPT_ROOT}/api_client_dict_syntax.diff"
8185
# The following is commented out due to:
8286
# AttributeError: 'RESTResponse' object has no attribute 'headers'
8387
# OpenAPI client generator prior to 6.4.0 uses deprecated urllib3 APIs.

0 commit comments

Comments
 (0)