Convert a single JSON-parsed value to the correct Python type.
(value: Any, hint: Any, model: ifcopenshell.file)
| 155 | |
| 156 | |
| 157 | def _coerce_shape_value(value: Any, hint: Any, model: ifcopenshell.file) -> Any: |
| 158 | """Convert a single JSON-parsed value to the correct Python type.""" |
| 159 | import typing |
| 160 | |
| 161 | if hint is None or value is None: |
| 162 | return value |
| 163 | |
| 164 | origin = typing.get_origin(hint) |
| 165 | args = typing.get_args(hint) |
| 166 | |
| 167 | # Optional[X] / Union — try each non-None branch in order |
| 168 | if origin is typing.Union: |
| 169 | if value is None: |
| 170 | return None |
| 171 | for t in (a for a in args if a is not type(None)): |
| 172 | try: |
| 173 | return _coerce_shape_value(value, t, model) |
| 174 | except (ValueError, TypeError): |
| 175 | continue |
| 176 | return value |
| 177 | |
| 178 | # entity_instance: resolve integer or "#N" string step ID |
| 179 | if hint is ifcopenshell.entity_instance or ( |
| 180 | isinstance(hint, type) and issubclass(hint, ifcopenshell.entity_instance) |
| 181 | ): |
| 182 | entity_id = int(str(value).lstrip("#")) |
| 183 | entity = model.by_id(entity_id) |
| 184 | if entity is None: |
| 185 | raise ValueError(f"Entity #{entity_id} not found in model") |
| 186 | return entity |
| 187 | |
| 188 | # Sequence[entity_instance]: resolve each element in the list |
| 189 | import collections.abc |
| 190 | |
| 191 | if origin is not None and issubclass(origin, collections.abc.Sequence) and not isinstance(value, str): |
| 192 | if args and ( |
| 193 | args[0] is ifcopenshell.entity_instance |
| 194 | or (isinstance(args[0], type) and issubclass(args[0], ifcopenshell.entity_instance)) |
| 195 | ): |
| 196 | if isinstance(value, (list, tuple)): |
| 197 | return [_coerce_shape_value(v, args[0], model) for v in value] |
| 198 | |
| 199 | # bool: JSON gives actual bools; also accept string representations |
| 200 | if hint is bool: |
| 201 | if isinstance(value, bool): |
| 202 | return value |
| 203 | return str(value).lower() in ("true", "1", "yes") |
| 204 | |
| 205 | # Everything else (float, int, VectorType lists, dicts, Literals) passes through |
| 206 | return value |
| 207 | |
| 208 | |
| 209 | class IfcSessionError(RuntimeError): |
no test coverage detected