Coverage for fake_kubernetes_client/resource_field.py: 38%
74 statements
« prev ^ index » next coverage.py v7.10.1, created at 2025-07-29 12:31 +0300
« prev ^ index » next coverage.py v7.10.1, created at 2025-07-29 12:31 +0300
1"""FakeResourceField implementation for fake Kubernetes client"""
3import copy
4from typing import Any, Iterator, Union
7class FakeResourceField:
8 """Fake implementation of kubernetes.dynamic.resource.ResourceField"""
10 def __init__(self, data: Union[dict[str, Any], None]) -> None:
11 self._data = data if data is not None else {}
13 def __getattribute__(self, name: str) -> Any:
14 # ONLY handle essential internal attributes and methods here
15 # Let __getattr__ handle all dynamic attribute access
16 if name.startswith("_") or name in {"to_dict", "keys", "values", "get"}:
17 return object.__getattribute__(self, name)
19 # Special case: handle 'items' in __getattribute__ to override method lookup
20 if name == "items":
21 try:
22 data = object.__getattribute__(self, "_data")
23 if "items" in data:
24 value = data["items"]
25 if isinstance(value, list):
26 return [FakeResourceField(item) if isinstance(item, dict) else item for item in value]
27 return value
28 except AttributeError:
29 pass
30 # Fall back to the method if no items data
31 return object.__getattribute__(self, name)
33 # For all other attributes, let __getattr__ handle it
34 raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
36 def __getattr__(self, name: str) -> Any:
37 # This is called ONLY when __getattribute__ raises AttributeError
38 # Handle all dynamic attribute access here
40 # Direct access to _data without using hasattr (avoids recursion)
41 try:
42 data = object.__getattribute__(self, "_data")
43 except AttributeError:
44 data = {}
46 # For resource definition access, return simple values for common attributes
47 # This ensures compatibility with ocp_resources code that expects strings
48 if name in ["api_version", "group_version", "kind", "plural", "singular", "group", "version"]:
49 return data.get(name, "")
51 # Handle general data access
52 value = data.get(name)
53 if value is None:
54 return FakeResourceField(data={})
55 elif isinstance(value, dict):
56 return FakeResourceField(data=value)
57 elif isinstance(value, list):
58 return [FakeResourceField(data=item) if isinstance(item, dict) else item for item in value]
59 else:
60 return value
62 def __getitem__(self, key: str) -> Any:
63 value = self._data.get(key)
64 if value is None:
65 return FakeResourceField(data={})
66 elif isinstance(value, dict):
67 return FakeResourceField(data=value)
68 elif isinstance(value, list):
69 return [FakeResourceField(data=item) if isinstance(item, dict) else item for item in value]
70 else:
71 return value
73 def __contains__(self, key: str) -> bool:
74 return key in self._data
76 def __bool__(self) -> bool:
77 return bool(self._data)
79 def __str__(self) -> str:
80 return str(self._data)
82 def __repr__(self) -> str:
83 return f"FakeResourceField({self._data})"
85 def get(self, key: str, default: Any = None) -> Any:
86 value = self._data.get(key, default)
87 if isinstance(value, dict) and value != default:
88 return FakeResourceField(data=value)
89 return value
91 def to_dict(self) -> dict[str, Any]:
92 """Convert to dictionary"""
93 return copy.deepcopy(self._data)
95 def keys(self) -> Any:
96 """Get dictionary keys"""
97 return self._data.keys()
99 def values(self) -> Any:
100 """Get dictionary values"""
101 return self._data.values()
103 def items(self) -> Any:
104 """Get dictionary items - but handle special case where 'items' is data"""
105 # If 'items' key exists in data, return that instead of dict.items()
106 if "items" in self._data:
107 value = self._data["items"]
108 if isinstance(value, list):
109 return [FakeResourceField(data=item) if isinstance(item, dict) else item for item in value]
110 return value
111 # Otherwise return dict.items()
112 return self._data.items()
114 def __iter__(self) -> Iterator[str]:
115 """Make it iterable like a dictionary"""
116 return iter(self._data)
118 def __len__(self) -> int:
119 """Get length like a dictionary"""
120 return len(self._data)