Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions kubernetes/client/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,25 @@ def __deserialize(self, data, klass):
return {k: self.__deserialize(v, sub_kls)
for k, v in six.iteritems(data)}

if klass.startswith('dict[') and klass.endswith(']'):
# Parse dict[key_type, value_type] respecting nested brackets
inner = klass[len('dict['):-1]
bracket_depth = 0
comma_pos = -1
for i, char in enumerate(inner):
if char in '([{':
bracket_depth += 1
elif char in ')]}':
bracket_depth -= 1
elif char == ',' and bracket_depth == 0:
comma_pos = i
break

if comma_pos != -1:
value_type = inner[comma_pos + 1:].strip()
return {k: self.__deserialize(v, value_type)
for k, v in six.iteritems(data)}

# convert str to class
if klass in self.NATIVE_TYPES_MAPPING:
klass = self.NATIVE_TYPES_MAPPING[klass]
Expand Down
26 changes: 26 additions & 0 deletions kubernetes/test/test_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,32 @@ def test_atexit_closes_threadpool(self):
atexit._run_exitfuncs()
self.assertIsNone(client._pool)

def test_deserialize_dict_syntax_compatibility(self):
"""Test ApiClient.__deserialize supports both dict(str, str) and dict[str, str] syntax"""
client = kubernetes.client.ApiClient()

# Test data
test_data = {
'key1': 'value1',
'key2': 'value2'
}

# Test legacy syntax: dict(str, str)
result_legacy = client._ApiClient__deserialize(test_data, 'dict(str, str)')
self.assertEqual(result_legacy, test_data)

# Test modern syntax: dict[str, str]
result_modern = client._ApiClient__deserialize(test_data, 'dict[str, str]')
self.assertEqual(result_modern, test_data)

# Test nested dict: dict[str, dict[str, str]]
nested_data = {
'outer1': {'inner1': 'value1', 'inner2': 'value2'},
'outer2': {'inner3': 'value3'}
}
result_nested = client._ApiClient__deserialize(nested_data, 'dict[str, dict[str, str]]')
self.assertEqual(result_nested, nested_data)

def test_rest_proxycare(self):

pool = { 'proxy': urllib3.ProxyManager, 'direct': urllib3.PoolManager }
Expand Down
28 changes: 28 additions & 0 deletions scripts/api_client_dict_syntax.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--- a/kubernetes/client/api_client.py 2025-11-01 14:37:47
+++ b/kubernetes/client/api_client.py 2025-11-01 15:54:48
@@ -285,6 +285,25 @@
return {k: self.__deserialize(v, sub_kls)
for k, v in six.iteritems(data)}

+ if klass.startswith('dict[') and klass.endswith(']'):
+ # Parse dict[key_type, value_type] respecting nested brackets
+ inner = klass[len('dict['):-1]
+ bracket_depth = 0
+ comma_pos = -1
+ for i, char in enumerate(inner):
+ if char in '([{':
+ bracket_depth += 1
+ elif char in ')]}':
+ bracket_depth -= 1
+ elif char == ',' and bracket_depth == 0:
+ comma_pos = i
+ break
+
+ if comma_pos != -1:
+ value_type = inner[comma_pos + 1:].strip()
+ return {k: self.__deserialize(v, value_type)
+ for k, v in six.iteritems(data)}
+
# convert str to class
if klass in self.NATIVE_TYPES_MAPPING:
klass = self.NATIVE_TYPES_MAPPING[klass]
4 changes: 4 additions & 0 deletions scripts/update-client.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ git apply "${SCRIPT_ROOT}/rest_client_patch.diff"
# once we upgrade to a version of swagger-codegen that includes it (version>= 6.6.0).
# See https://github.com/OpenAPITools/openapi-generator/pull/15283
git apply "${SCRIPT_ROOT}/rest_sni_patch.diff"
# Support dict[str, str] syntax in ApiClient deserializer alongside legacy dict(str, str)
# This enables forward compatibility with modern Python typing syntax while maintaining
# backward compatibility. Users can now convert openapi_types for Pydantic integration.
git apply "${SCRIPT_ROOT}/api_client_dict_syntax.diff"
# The following is commented out due to:
# AttributeError: 'RESTResponse' object has no attribute 'headers'
# OpenAPI client generator prior to 6.4.0 uses deprecated urllib3 APIs.
Expand Down