drivendataorg / erdantic

Entity relationship diagrams for Python data model classes like Pydantic
https://erdantic.drivendata.org/
MIT License
317 stars 20 forks source link

Support Classes with No fields #120

Closed xaellison closed 4 months ago

xaellison commented 4 months ago

This works

from pydantic import BaseModel
class WrapperClass(BaseModel):
    field: int

erd.create(WrapperClass)

but this does not

from pydantic import BaseModel
class EmptyClass(BaseModel):
    pass

erd.create(EmptyClass)
stack trace ```python StopIteration Traceback (most recent call last) File ~/.pyenv/versions/3.8.11/envs/default/lib/python3.8/site-packages/IPython/core/formatters.py:344, in BaseFormatter.__call__(self, obj) 342 method = get_real_method(obj, self.print_method) 343 if method is not None: --> 344 return method() 345 return None 346 else: File ~/.pyenv/versions/3.8.11/envs/default/lib/python3.8/site-packages/erdantic/core.py:712, in EntityRelationshipDiagram._repr_png_(self) 710 def _repr_png_(self) -> bytes: 711 """IPython special method to display object as a PNG image.""" --> 712 graph = self.to_graphviz() 713 return graph.draw(prog="dot", format="png") File ~/.pyenv/versions/3.8.11/envs/default/lib/python3.8/site-packages/erdantic/core.py:644, in EntityRelationshipDiagram.to_graphviz(self, graph_attr, node_attr, edge_attr) 640 g.edge_attr.update(edge_attr or {}) 641 for full_name, model_info in self.models.items(): 642 g.add_node( 643 full_name, --> 644 label=model_info.to_dot_label(), 645 tooltip=model_info.description.replace("\n", " "), 646 ) 647 for edge in self.edges.values(): 648 g.add_edge( 649 edge.source_model_full_name, 650 edge.target_model_full_name, (...) 654 arrowtail=edge.source_dot_arrow_shape(), 655 ) File ~/.pyenv/versions/3.8.11/envs/default/lib/python3.8/site-packages/erdantic/core.py:302, in ModelInfo.to_dot_label(self) 294 """Returns the DOT language "HTML-like" syntax specification of a table for this data 295 model. It is used as the `label` attribute of data model's node in the graph's DOT 296 representation. (...) 299 str: DOT language for table 300 """ 301 # Get number of columns dynamically from first row --> 302 num_cols = next(iter(self.fields.values())).to_dot_row().count("
jayqi commented 4 months ago

Hi @xaellison, can you explain a little more about your use case here?

I'm not sure about whether drawing a class with no fields makes sense, but at least there's room for either a better error message or more graceful handling of the error.

xaellison commented 4 months ago

Hey @jayqi, thanks for the reply. I'm collaborating on a project that has some placeholder class definitions with no fields - we're going to implement them later.

I don't particularly care about being able to render an empty class. It'd be great if it did not error out, though.

  1. Would it be possible to treat it like a primitive type like an int that shows up as a field but doesn't point anywhere?
  2. Could we populate a fake field in if there are no real ones?
jayqi commented 4 months ago

Should be fixed in v1.0.3.

xaellison commented 4 months ago

great thank you!