Convert an instance to its class source code representation.
(instance, base_cls=None)
| 283 | |
| 284 | |
| 285 | def instance_to_source(instance, base_cls=None): |
| 286 | """Convert an instance to its class source code representation.""" |
| 287 | cls = instance.__class__ |
| 288 | class_name = cls.__name__ |
| 289 | |
| 290 | # Start building class lines |
| 291 | class_lines = [] |
| 292 | if base_cls: |
| 293 | class_lines.append(f"class {class_name}({base_cls.__name__}):") |
| 294 | else: |
| 295 | class_lines.append(f"class {class_name}:") |
| 296 | |
| 297 | # Add docstring if it exists and differs from base |
| 298 | if cls.__doc__ and (not base_cls or cls.__doc__ != base_cls.__doc__): |
| 299 | class_lines.append(f' """{cls.__doc__}"""') |
| 300 | |
| 301 | # Add class-level attributes |
| 302 | class_attrs = { |
| 303 | name: value |
| 304 | for name, value in cls.__dict__.items() |
| 305 | if not name.startswith("__") |
| 306 | and not name == "_abc_impl" |
| 307 | and not callable(value) |
| 308 | and not (base_cls and hasattr(base_cls, name) and getattr(base_cls, name) == value) |
| 309 | } |
| 310 | |
| 311 | for name, value in class_attrs.items(): |
| 312 | if isinstance(value, str): |
| 313 | # multiline value |
| 314 | if "\n" in value: |
| 315 | escaped_value = value.replace('"""', r"\"\"\"") # Escape triple quotes |
| 316 | class_lines.append(f' {name} = """{escaped_value}"""') |
| 317 | else: |
| 318 | class_lines.append(f" {name} = {json.dumps(value)}") |
| 319 | else: |
| 320 | class_lines.append(f" {name} = {repr(value)}") |
| 321 | |
| 322 | if class_attrs: |
| 323 | class_lines.append("") |
| 324 | |
| 325 | # Add methods |
| 326 | methods = { |
| 327 | name: func.__wrapped__ if hasattr(func, "__wrapped__") else func |
| 328 | for name, func in cls.__dict__.items() |
| 329 | if callable(func) |
| 330 | and ( |
| 331 | not base_cls |
| 332 | or not hasattr(base_cls, name) |
| 333 | or ( |
| 334 | isinstance(func, (staticmethod, classmethod)) |
| 335 | or (getattr(base_cls, name).__code__.co_code != func.__code__.co_code) |
| 336 | ) |
| 337 | ) |
| 338 | } |
| 339 | |
| 340 | for name, method in methods.items(): |
| 341 | method_source = get_source(method) |
| 342 | # Clean up the indentation |
searching dependent graphs…