-
Notifications
You must be signed in to change notification settings - Fork 35
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
Changed classes to inherete from compas.Data #387
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Classes that inherit from Data
should also add implementations for the data
property getter and setter, and I think some of these ones dont. At least RobotSemantics
. Also, the Data
class implements a default from_data
class method that works only if the class has a constructor with all optional params, if that's not the case, there needs to be an overload of from_data
in the class that is able to create instances (usually by first getting the args needed from the data
dictionary, constructing with the minimum, and then calling the .data =
setter.
yes please! they don't need to be super elaborate, but testing the serialization and deserialization on all these classes with some basic data would help ensure they behave properly all the time |
I have revised the list of affected classes:
The following class is skipped as it maybe redesigned in near future
|
ping @gonzalocasas |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. left some comments though.
src/compas_fab/robots/robot.py
Outdated
self._scale_factor = data.get("_scale_factor", 1.0) | ||
self._attached_tools = data.get("_attached_tools", {}) | ||
self._current_ik = data.get("_current_ik", {"request_id": None, "solutions": None}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mmh is this to stay compatible with previously serialized Robot
s? But it didn't implement the Data
interface before, so there shouldn't be any, right?
You could also simply omit these from the Data
dictionary if they take default values, __init__
is ran first anyways. Otherwise you can assume they're there, because they're put there by the getter.
src/compas_fab/robots/robot.py
Outdated
"_scale_factor": self._scale_factor, | ||
"_attached_tools": self._attached_tools, | ||
"_current_ik": self._current_ik, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why the _
s?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't invent this particular variable, and it was there all along, I just serialized it. I believe the intent of the existing code is that the model is not supposed to keep track of extrinsic or modifiable properties of the robot (in this case, the current_ik
). Hence the private _
notation. I should probably have another PR to address the intrinsic vs extrinsic state of the robot problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would remove the _
in the keys.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, current_ik
should not be serialized
@classmethod | ||
def from_data(cls, data): | ||
robot_semantics = cls(None) | ||
robot_semantics.data = data | ||
return robot_semantics |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gonzalocasas commented on this. I'd say setting robot_model=None
in arguments of __init__
is legit. Then you don't need a custom from_data
. While I was not a fan, this convention grew on me.
Having said that, the trend in COMPAS 2.0 is to get rid of the data.setter
acknowledging it's often redundant. So you'd then just have a data
getter and a from_data
which feeds the data
dict to __init__
.
Example (just for illustration, not sure if this works in in LTS): gramaziokohler/compas_timber@d11d83e#diff-5cbdc4545536c895404b80de4fdcc522ec5b95dfb4af73a47e4120888cc22031R42-R48
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if I get your argument.
Are you saying that the style is moving towards having default values for all parameters in the __init__
and then skipping the def from_data()
?
Probably it would be good to have a more wholistic stylistic clean up separately. The style currently used in compas_fab is a mixed bag of things. I can do that as long as we can have some consensus on what convention we adopt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea sorry didn't wanna cause confusion. Let's leave it like that.
assert tool1.name == tool2.name | ||
assert str(tool1.frame) == str(tool2.frame) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd add another unittest checking deepcopy. should behave the same as (de)serializing but sometimes doesn't
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried deepcopy but It doesn't seem to work. I think some years ago, I heard from someone to not use deepcopy on these objects. I don't remember why. But basically I never deepcopied any compas objects since.
Just to give you a taste of that error message:
./tests/robots/test_robot.py::test_robot_deepcopy_with_tool Failed: [undefined]AttributeError: 'Visual' object has no attribute '_guid'
tests\robots\test_robot.py:345: in test_robot_deepcopy_with_tool
robot2 = deepcopy(robot)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:172: in deepcopy
y = _reconstruct(x, memo, *rv)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:271: in _reconstruct
state = deepcopy(state, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:146: in deepcopy
y = copier(x, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:231: in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:146: in deepcopy
y = copier(x, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:231: in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:146: in deepcopy
y = copier(x, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:231: in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:172: in deepcopy
y = _reconstruct(x, memo, *rv)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:271: in _reconstruct
state = deepcopy(state, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:146: in deepcopy
y = copier(x, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:231: in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:146: in deepcopy
y = copier(x, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:231: in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:172: in deepcopy
y = _reconstruct(x, memo, *rv)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:271: in _reconstruct
state = deepcopy(state, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:146: in deepcopy
y = copier(x, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:231: in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:146: in deepcopy
y = copier(x, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:231: in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:146: in deepcopy
y = copier(x, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:206: in _deepcopy_list
append(deepcopy(a, memo))
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:172: in deepcopy
y = _reconstruct(x, memo, *rv)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:271: in _reconstruct
state = deepcopy(state, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:146: in deepcopy
y = copier(x, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:231: in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:146: in deepcopy
y = copier(x, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:231: in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:146: in deepcopy
y = copier(x, memo)
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:206: in _deepcopy_list
append(deepcopy(a, memo))
..\..\..\anaconda3\envs\cf-dev\Lib\copy.py:161: in deepcopy
rv = reductor(4)
..\..\..\anaconda3\envs\cf-dev\Lib\site-packages\compas\data\data.py:121: in __getstate__
"guid": str(self.guid),
..\..\..\anaconda3\envs\cf-dev\Lib\site-packages\compas\data\data.py:200: in guid
if not self._guid:
E AttributeError: 'Visual' object has no attribute '_guid'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's known deepcopy
errors produce the most beautiful stacks..
To this specific one, could there be a class called Visual
(really?) somewhere which inherits from Data
but doesn't call super().__init__()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks okay here. I'm not sure how this Visual object is created. It seems that there is a Geometry class within it and that is created during from_data()
:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If someone wants to debug this, this is the test code:
def test_robot_deepcopy(panda_robot_instance):
import copy
robot = panda_robot_instance
robot.scale(0.001)
robot2 = copy.deepcopy(robot)
assert robot is not robot2
def test_robot_deepcopy2(ur5_robot_instance, robot_tool1):
import copy
robot = ur5_robot_instance
robot.attach_tool(robot_tool1)
robot2 = copy.deepcopy(robot)
assert robot is not robot2
Both test won't work and they have different error message
ping @gonzalocasas |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally LGTM, I added some feedback though
src/compas_fab/robots/robot.py
Outdated
"_scale_factor": self._scale_factor, | ||
"_attached_tools": self._attached_tools, | ||
"_current_ik": self._current_ik, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would remove the _
in the keys.
src/compas_fab/robots/robot.py
Outdated
"_scale_factor": self._scale_factor, | ||
"_attached_tools": self._attached_tools, | ||
"_current_ik": self._current_ik, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, current_ik
should not be serialized
Co-authored-by: Gonzalo Casas <[email protected]>
…compas_fab into migration_data_class
Cool, thanks for the updates! On my side, I think this is ready to go! |
Some of the classes in compas_fab is still based on (object) instead of (Data)
I have selected some of the classes that should support serialization and upgrade them. Upgrading them should be backward compatible. However, it may affect user classes inherited from these classes. This RP does not cover all the classes yet (e.g. classes in backends, reachability map, ghpython and some more). Let me know if you think I should extend to cover all classes.
@gonzalocasas Should I add test files for this?
What type of change is this?
Checklist
Put an
x
in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.CHANGELOG.md
file in theUnreleased
section under the most fitting heading (e.g.Added
,Changed
,Removed
).invoke test
).invoke lint
).compas_fab.robots.CollisionMesh
.