Convert an instance to its class source code representation.
(instance, base_cls=None)
| 256 | |
| 257 | |
| 258 | def instance_to_source(instance, base_cls=None): |
| 259 | """Convert an instance to its class source code representation.""" |
| 260 | cls = instance.__class__ |
| 261 | class_name = cls.__name__ |
| 262 | |
| 263 | # Start building class lines |
| 264 | class_lines = [] |
| 265 | if base_cls: |
| 266 | class_lines.append(f"class {class_name}({base_cls.__name__}):") |
| 267 | else: |
| 268 | class_lines.append(f"class {class_name}:") |
| 269 | |
| 270 | # Add docstring if it exists and differs from base |
| 271 | if cls.__doc__ and (not base_cls or cls.__doc__ != base_cls.__doc__): |
| 272 | class_lines.append(f' """{cls.__doc__}"""') |
| 273 | |
| 274 | # Add class-level attributes |
| 275 | class_attrs = { |
| 276 | name: value |
| 277 | for name, value in cls.__dict__.items() |
| 278 | if not name.startswith("__") |
| 279 | and not callable(value) |
| 280 | and not (base_cls and hasattr(base_cls, name) and getattr(base_cls, name) == value) |
| 281 | } |
| 282 | |
| 283 | for name, value in class_attrs.items(): |
| 284 | if isinstance(value, str): |
| 285 | # multiline value |
| 286 | if "\n" in value: |
| 287 | escaped_value = value.replace('"""', r"\"\"\"") # Escape triple quotes |
| 288 | class_lines.append(f' {name} = """{escaped_value}"""') |
| 289 | else: |
| 290 | class_lines.append(f" {name} = {json.dumps(value)}") |
| 291 | else: |
| 292 | class_lines.append(f" {name} = {repr(value)}") |
| 293 | |
| 294 | if class_attrs: |
| 295 | class_lines.append("") |
| 296 | |
| 297 | # Add methods |
| 298 | methods = { |
| 299 | name: func.__wrapped__ if hasattr(func, "__wrapped__") else func |
| 300 | for name, func in cls.__dict__.items() |
| 301 | if callable(func) |
| 302 | and ( |
| 303 | not base_cls |
| 304 | or not hasattr(base_cls, name) |
| 305 | or ( |
| 306 | isinstance(func, (staticmethod, classmethod)) |
| 307 | or (getattr(base_cls, name).__code__.co_code != func.__code__.co_code) |
| 308 | ) |
| 309 | ) |
| 310 | } |
| 311 | |
| 312 | for name, method in methods.items(): |
| 313 | method_source = get_source(method) |
| 314 | # Clean up the indentation |
| 315 | method_lines = method_source.split("\n") |
nothing calls this directly
no test coverage detected