Skip to content

Align with UML Specification: Literals #3749

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

Merged
merged 13 commits into from
Feb 19, 2025
Merged

Conversation

pbrown12303
Copy link
Contributor

This change brings the core structure of Gaphor UML into alignment with the UML 2.5 specification. It does not implement all of the UML specification. There are two significant areas of modification:

  • The <<simpleAttribute>> stereotype was removed from ValueSpecification. This required implementing the Literal subclasses of ValueSpecification, with the exception of LiteralReal. Attributes that previously referenced ValueSpecifications as simple values have been converted to references to ValueSpecifications. This required updating all code that accessed these references. Recipes have been added to facilitate accessing and updating these references. The scope of this change was fairly broad.
  • All relationships have been aligned with the UML specification. This particularly impacted parent-child relationships. An update to the property subset logic was required. It should be noted that in a couple of places the UML specification has two different relationships between the same two classes that use the same role name. StructuredClassifier <--> Property and Property <--> Association are two examples. In both cases, one of the relationships had to be renamed as Gaphor treats the role names as class property names, which led to ambiguity.

The storage.py file has been updated to make these changes backwards compatible.

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bug fix
  • Feature
  • Chore (refactoring, formatting, local variables, other cleanup)
  • Documentation content changes

What is the current behavior?

Issue Number: #3720, #3517,

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

@github-actions github-actions bot added the python Pull requests that update Python code label Feb 3, 2025
pbrown12303 and others added 2 commits February 3, 2025 14:30
There are some issues:

* I cannot set default values for attributes in Gaphor
@amolenaar
Copy link
Member

I merge the main branch into this PR. In the process it looks like I introduced an issue: I cannot set a default value on an attribute anymore.

@pbrown12303
Copy link
Contributor Author

Thanks @amolenaar - I couldn't figure out the git incantation to do this merge on my machine. I'll take a look and fix the problem.

@amolenaar
Copy link
Member

In also want to make another attempt.

It's a bit of a pity though that all changes are in one commit. If it were multiple smaller commits, merge conflicts are easier to resolve.

@@ -4,6 +4,8 @@

from __future__ import annotations

from decimal import Decimal as UnlimitedNatural
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use Decimal as UnlimitedNatural?

A natural number is whole number right? If so, we can use int: integers in Python grow and do not overflow, similar to BigInteger in Java.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The int type can't handle "*". I used Decimal with conversions in and out. Can't see any other way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I could create a type as something like "int | math.inf", that would do the trick, but math.inf is not a type. The only types in Python that can handle infinite values are Decimal and Float. I chose Decimal. Of course, this requires constraining the Decimal input values to int or math.inf, and constraining the Decimal values to str(int(value)) or "*" when converting back to a string. I don't really see any alternatives - do you?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use a union type int | typing.Literal["*"] instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. Let me try that.

Comment on lines +240 to +244
if self.type is decimal.Decimal and isinstance(value, str):
if value == "*" or value == "inf" or value == "Infinity":
value = decimal.Decimal(math.inf)
else:
value = decimal.Decimal(int(value))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why we need decimal here. We deal with natural (whole, positive) numbers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See earlier comment abut UnlimitedNatural. The issue is handling infinite values.

@pbrown12303
Copy link
Contributor Author

This is bizarre. The test that failed on windows (in the teardown) passes when I run it in the debugger in my development environment (which is where and how I usually run the tests), but it fails when I run it without the debugger! I'm looking at it now.

Comment on lines 953 to 954
if opposite is not None:
self.original.opposite = opposite
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably not change the original opposite relation when a relation is redefined.

I assume the opposite relation should wither point to the original opposite end, or a redefined one.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this code, and instead make the relation Property.class_ <-> Class.ownedAttribute redefined Property.structuredClassifier <-> StructuredClassifier.ownedAttribute.

Property.class_ did not redefine Property.structuredClassifier, but was a subset, whereas Class.ownedAttribute redefined StructuredClassifier.ownedAttribute. Both ends should redefine their "super parts" for this to work.

See 3dc4747.

Comment on lines 272 to 273
assoc.ownedEnd = end_a
assoc.ownedEnd = end_b
Copy link
Member

@amolenaar amolenaar Feb 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why has this changed to ownedEnd? Shouldn't an association end be at least a memberEnd of an association? Otherwise Property.association is not set, which should be set always if a property belongs to an association, if I understand correctly.

Setting ownedEnd has no effect, since the change is undone by set_navigability.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed this: Association.ownedEnd is set by the set_navigability function. To my understanding, Property.association should referenjce the association.

See 3dc4747

Redefined properties should not overwrite opposite end of original

Property.class_ pointer to Class.ownedAttribute, but
Class.ownedAttribute pointed to Property.structuredClassifier.

In case an association is birections, both ends should be redefined.
It's still used for expressions.

This reverts commit 56619b6.
@amolenaar amolenaar self-requested a review February 14, 2025 09:59
Copy link
Member

@amolenaar amolenaar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can merge this.

The use of decimal.Decimal is internal, so we can factor that out in a later PR.

@amolenaar amolenaar merged commit d96c4fd into gaphor:main Feb 19, 2025
24 checks passed
@amolenaar
Copy link
Member

@pbrown12303 Thanks again for your help on making Gaphor the best modeling tool out there :)

@pbrown12303
Copy link
Contributor Author

@amolenaar You are quite welcome. I hope to add more. Starting my research now...

@pbrown12303 pbrown12303 deleted the uml-updates branch February 19, 2025 17:42
@amolenaar amolenaar changed the title Align with UML Specification - all tests pass Align with UML Specification: Literals Apr 16, 2025
@danyeaw danyeaw added feature A new feature and removed python Pull requests that update Python code labels May 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature A new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants