Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v2.23 complains of invalid gaphor model in file that opens in v2.22 #2995

Closed
2 tasks
mikekidner opened this issue Jan 9, 2024 · 9 comments
Closed
2 tasks
Labels
bug An issue in the application
Milestone

Comments

@mikekidner
Copy link
Contributor

Describe the bug

A (large) model that opens in v2.22 does not open in v2.23.

Gaphor version: 2.23.0
Operating System: Darwin (21.6.0)
Display: GdkMacosDisplay
Python version: 3.11.7
GTK version: 4.12.4
Adwaita version: 1.4.2
GtkSourceView version: 5.10.0
Cairo version: 1.18.0
Pango version: 1.50.14
PyGObject version: 3.46.0
Pycairo version: 1.25.1
pygit2/libgit2 version: 1.13.3 / 1.7.1

Errors:

Time since application startup: 0:00:55
|Traceback (most recent call last):
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/ui/errorhandler.pyc", line 42, in response
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/ui/filemanager.pyc", line 234, in
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/core/eventmanager.pyc", line 83, in handle
| File "/Applications/Gaphor.app/Contents/Frameworks/generic/event.pyc", line 61, in handle
|ExceptionGroup: Error while handling events (1 sub-exception)
└─┬──────────────────────────────╌┄┈
|Traceback (most recent call last):
| File "/Applications/Gaphor.app/Contents/Frameworks/generic/event.pyc", line 57, in handle
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/application.pyc", line 119, in on_session_shutdown
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/application.pyc", line 147, in shutdown_session
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/application.pyc", line 231, in shutdown
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/application.pyc", line 238, in shutdown_service
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/core/modeling/elementfactory.pyc", line 76, in shutdown
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/core/modeling/elementfactory.pyc", line 197, in flush
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/core/modeling/diagram.pyc", line 362, in unlink
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphas/connections.pyc", line 165, in remove_connections_to_item
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphas/connections.pyc", line 148, in _disconnect_item
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/diagram/_connector.pyc", line 108, in call
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/UML/classes/containmentconnect.pyc", line 68, in disconnect
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/core/modeling/element.pyc", line 213, in setattr
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/core/modeling/properties.pyc", line 149, in set
| File "/Applications/Gaphor.app/Contents/Frameworks/gaphor/core/modeling/properties.pyc", line 381, in set
|TypeError: Can not set <gaphor.UML.uml.Package element b0d8bd42-2066-11ee-b860-ba28606445aa>.package to itself
└─────────────────────────────╌┄┈

Expected behavior

The model to open. Or to identify the issue, so it can be corrected.

OS

  • Linux (Please put in notes the specific distro)
  • [x ] macOS
  • Windows

Version

Version of Gaphor:2.23

Additional information

The model opens in 2.22, is there a way to identify the problematic elements?

@danyeaw
Copy link
Member

danyeaw commented Jan 9, 2024

Hey @mikekidner, is it possible to share the model?

@mikekidner
Copy link
Contributor Author

Not publicly, but is there a way I can upload you a copy?

@amolenaar
Copy link
Member

I'm surprised it does open with 2.22. This check is in there for quite some time. An element should not reference itself in the model.

In the model file, there should be a package with id="b0d8bd42-2066-11ee-b860-ba28606445aa", that contains

<package>
<ref refid="b0d8bd42-2066-11ee-b860-ba28606445aa"/>
</package>

If you remove the package tag with contents, the model should load again.

@mikekidner
Copy link
Contributor Author

@amolenaar, thanks for the pointer. I opened in v22.1 and noted on closing the following errors:

gaphor.storage.storage INFO Read 7881 elements from file
gaphor.plugins.errorreports.errorreports ERROR Uncaught exception
.........
    |   File "/Users/mikekidner/BitBucketRepos/gaphortools/gaphortools/lib/python3.11/site-packages/gaphor/UML/classes/containmentconnect.py", line 68, in disconnect
    |     oct.subject.package = owner_package(oct.diagram.owner)
    |     ^^^^^^^^^^^^^^^^^^^
........
    |   File "/Users/mikekidner/BitBucketRepos/gaphortools/gaphortools/lib/python3.11/site-packages/gaphor/core/modeling/properties.py", line 381, in set
    |     raise TypeError(f"Can not set {obj}.{self.name} to itself")
    | TypeError: Can not set <gaphor.UML.uml.Package element b0d8bd42-2066-11ee-b860-ba28606445aa>.package to itself

which agrees with the v2.23 errors.
However on deleting the referenced package, the errors no longer showed from v22.1 but the model still didn't open in v2.23. although no errors were reported.

I extracted a sub set of the model, around the package that raised the initial errors, and this opened in v2.23. So I don't think its related to that area of the model.

The model is quite large 24Mb, let me know if there is a spot I can upload it too if that helps resolve the issue.

@danyeaw
Copy link
Member

danyeaw commented Jan 10, 2024

@mikekidner, what's your email address? I can share a OneDrive folder with you for you to upload it in. Or you could try using Warp to send it to us.

@tompkins-ct
Copy link

tompkins-ct commented Jan 12, 2024

Hi,

I'm getting the same issue with a model I have, unfortunately, I can share the model. It opens in 2.22.1 but fails to open in 2.23.1 with the same error "This file does not contain a valid Gaphor model".

I'm not sure how to see the error log because the window closes as soon as I close the error dialog.

Running via the cmd line doesn't throw any errors. However loading via jupyter provides the following:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[3], line 6
      3 element_factory = ElementFactory()
      5 with open("hermes_proj_definition.gaphor") as file_obj:
----> 6     storage.load(
      7         file_obj,
      8         element_factory,
      9         modeling_language,
     10     )

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/storage/storage.py:271, in load(file_obj, element_factory, modeling_language, status_queue)
    263 def load(
    264     file_obj: io.TextIOBase, element_factory, modeling_language, status_queue=None
    265 ):
    266     """Load a file and create a model if possible.
    267 
    268     Optionally, a status queue function can be given, to which the
    269     progress is written (as status_queue(progress)).
    270     """
--> 271     for status in load_generator(file_obj, element_factory, modeling_language):
    272         if status_queue:
    273             status_queue(status)

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/storage/storage.py:308, in load_generator(file_obj, element_factory, modeling_language)
    306 element_factory.flush()
    307 with element_factory.block_events():
--> 308     for percentage in load_elements_generator(
    309         elements, element_factory, modeling_language, gaphor_version
    310     ):
    311         if percentage:
    312             yield percentage / 2 + 50

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/storage/storage.py:172, in load_elements_generator(elements, element_factory, modeling_language, gaphor_version)
    170 yield from update_status_queue()
    171 assert elem.element
--> 172 elem.element.postload()

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/diagram/presentation.py:386, in LinePresentation.postload(self)
    383 super().postload()
    385 if hasattr(self, "_load_head_connection"):
--> 386     postload_connect(self, self.head, self._load_head_connection)
    387     assert self._connections.get_connection(self.head)
    388     del self._load_head_connection

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/diagram/presentation.py:82, in postload_connect(item, handle, target)
     75 """Reconnect handles after model loading.
     76 
     77 When loading a model, handles should be connected as part of the `postload` step.
     78 
     79 This function finds a suitable spot on the `target` item to connect the `handle` to.
     80 """
     81 target.postload()
---> 82 item.diagram.update_now({item, target})
     83 connector = ConnectorAspect(item, handle, item.diagram.connections)
     84 sink = ConnectionSink(target, distance=float("inf"))

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphas/decorators.py:126, in nonrecursive.<locals>.wrapper(*args, **kwargs)
    124 if m.acquire(False):
    125     try:
--> 126         return func(*args, **kwargs)
    127     finally:
    128         m.release()

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/core/modeling/diagram.py:452, in Diagram.update_now(self, dirty_items)
    450 for item in reversed(list(self.sort(dirty_items_with_ancestors()))):
    451     if update := getattr(item, "update", None):
--> 452         update(UpdateContext(style=self.style(StyledItem(item))))
    454 self._connections.solve()

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/diagram/presentation.py:177, in ElementPresentation.update(self, context)
    175     self.update_shapes()
    176 if self.shape:
--> 177     self.min_width, self.min_height = self.shape.size(
    178         context, bounding_box=Rectangle(0, 0, self.width, self.height)
    179     )

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/diagram/shapes.py:248, in Box.size(self, context, bounding_box)
    246 padding = style.get("padding", DEFAULT_PADDING)
    247 new_bounds = rectangle_shrink(bounding_box, padding)
--> 248 self.sizes = sizes = [c.size(context, new_bounds) for c in self.children]
    249 if sizes:
    250     widths, heights = list(zip(*sizes))

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/diagram/shapes.py:248, in <listcomp>(.0)
    246 padding = style.get("padding", DEFAULT_PADDING)
    247 new_bounds = rectangle_shrink(bounding_box, padding)
--> 248 self.sizes = sizes = [c.size(context, new_bounds) for c in self.children]
    249 if sizes:
    250     widths, heights = list(zip(*sizes))

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/diagram/shapes.py:248, in Box.size(self, context, bounding_box)
    246 padding = style.get("padding", DEFAULT_PADDING)
    247 new_bounds = rectangle_shrink(bounding_box, padding)
--> 248 self.sizes = sizes = [c.size(context, new_bounds) for c in self.children]
    249 if sizes:
    250     widths, heights = list(zip(*sizes))

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/diagram/shapes.py:248, in <listcomp>(.0)
    246 padding = style.get("padding", DEFAULT_PADDING)
    247 new_bounds = rectangle_shrink(bounding_box, padding)
--> 248 self.sizes = sizes = [c.size(context, new_bounds) for c in self.children]
    249 if sizes:
    250     widths, heights = list(zip(*sizes))

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/diagram/shapes.py:584, in CssNode.size(self, context, bounding_box)
    581 def size(
    582     self, context: UpdateContext, bounding_box: Rectangle | None = None
    583 ) -> tuple[Number, Number]:
--> 584     style = compute_inherited_style(context.style, self.style_node(context.style))
    585     new_context = replace(context, style=style)
    587     return self._child.size(new_context, bounding_box)

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/core/styling/inherit.py:10, in compute_inherited_style(style, style_node)
      4 def compute_inherited_style(style: Style, style_node: StyleNode) -> Style:
      5     compiled_style_sheet: CompiledStyleSheet | None = style.get(
      6         "-gaphor-compiled-style-sheet"
      7     )  # type: ignore[assignment]
      9     return (
---> 10         compiled_style_sheet.compute_style(style_node)
     11         if compiled_style_sheet
     12         else style
     13     )

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/core/styling/__init__.py:178, in CompiledStyleSheet._compute_style_uncached(self, node)
    176 parent = node.parent()
    177 parent_style = self.compute_style(parent) if parent else {}
--> 178 return merge_styles(
    179     {n: v for n, v in parent_style.items() if n in INHERITED_DECLARATIONS},  # type: ignore[arg-type]
    180     *(declarations for selector, declarations in self.rules if selector(node)),
    181     {"-gaphor-style-node": node, "-gaphor-compiled-style-sheet": self},
    182 )

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/core/styling/__init__.py:180, in <genexpr>(.0)
    176 parent = node.parent()
    177 parent_style = self.compute_style(parent) if parent else {}
    178 return merge_styles(
    179     {n: v for n, v in parent_style.items() if n in INHERITED_DECLARATIONS},  # type: ignore[arg-type]
--> 180     *(declarations for selector, declarations in self.rules if selector(node)),
    181     {"-gaphor-style-node": node, "-gaphor-compiled-style-sheet": self},
    182 )

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/core/styling/compiler.py:137, in compile_compound_selector.<locals>.<lambda>(el)
    134 @compile_node.register
    135 def compile_compound_selector(selector: selectors.CompoundSelector):
    136     sub_expressions = [compile_node(sel) for sel in selector.simple_selectors]
--> 137     return lambda el: all(expr(el) for expr in sub_expressions)

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/core/styling/compiler.py:137, in <genexpr>(.0)
    134 @compile_node.register
    135 def compile_compound_selector(selector: selectors.CompoundSelector):
    136     sub_expressions = [compile_node(sel) for sel in selector.simple_selectors]
--> 137     return lambda el: all(expr(el) for expr in sub_expressions)

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/core/styling/compiler.py:237, in compile_functional_pseudo_class_selector.<locals>.<lambda>(el)
    235     return lambda el: any(sel(el) for sel, _ in sub_selectors)
    236 elif name == "not":
--> 237     return lambda el: not any(sel(el) for sel, _ in sub_selectors)

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/core/styling/compiler.py:237, in <genexpr>(.0)
    235     return lambda el: any(sel(el) for sel, _ in sub_selectors)
    236 elif name == "not":
--> 237     return lambda el: not any(sel(el) for sel, _ in sub_selectors)

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/core/styling/compiler.py:137, in compile_compound_selector.<locals>.<lambda>(el)
    134 @compile_node.register
    135 def compile_compound_selector(selector: selectors.CompoundSelector):
    136     sub_expressions = [compile_node(sel) for sel in selector.simple_selectors]
--> 137     return lambda el: all(expr(el) for expr in sub_expressions)

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/core/styling/compiler.py:137, in <genexpr>(.0)
    134 @compile_node.register
    135 def compile_compound_selector(selector: selectors.CompoundSelector):
    136     sub_expressions = [compile_node(sel) for sel in selector.simple_selectors]
--> 137     return lambda el: all(expr(el) for expr in sub_expressions)

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/core/styling/compiler.py:185, in compile_attribute_selector.<locals>.<lambda>(el)
    182 value = selector.value and selector.value.lower()
    184 if operator is None:
--> 185     return lambda el: bool(el.attribute(name))
    186 elif operator == "=":
    187     return lambda el: el.attribute(name) == value

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/diagram/shapes.py:617, in StyledCssNode.attribute(self, name)
    615 if not self._shape._element:
    616     return None
--> 617 return lookup_attribute(self._shape._element, name)

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/core/modeling/diagram.py:133, in lookup_attribute(element, name)
    125 """Look up an attribute from an element.
    126 
    127 Attributes can be nested, e.g. ``owner.name``.
   (...)
    130 ``None`` if the attribute does not exist.
    131 """
    132 fields = name.split(".")
--> 133 values = list(rgetattr(element, fields))
    134 attr_values = [v for v in values if v is not NO_ATTR]
    135 if not attr_values and NO_ATTR in values:

File ~/.pyenv/versions/3.11.5/lib/python3.11/site-packages/gaphor/core/modeling/diagram.py:95, in rgetattr(obj, names)
     93 """Recursively get a name, based on a list of names."""
     94 name, *tail = names
---> 95 v = getattr(obj, attrname(obj, name), NO_ATTR)
     96 if isinstance(v, (collection, list, tuple)):
     97     if tail and not v:

TypeError: unhashable type: 'collection'

@danyeaw
Copy link
Member

danyeaw commented Jan 13, 2024

Thanks @mikekidner for the model and @tompkins-ct for the additional traceback, both of those were very helpful to debug this. I opened #3018 to resolve this. 👍

@danyeaw danyeaw added the bug An issue in the application label Jan 13, 2024
@danyeaw danyeaw added this to the 2.23.2 milestone Jan 13, 2024
@danyeaw
Copy link
Member

danyeaw commented Jan 14, 2024

Closed by #3018.

@danyeaw danyeaw closed this as completed Jan 14, 2024
@mikekidner
Copy link
Contributor Author

Thanks @danyeaw! It's a relief to be able to open that model again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug An issue in the application
Projects
None yet
Development

No branches or pull requests

4 participants