CadQuery / cadquery

A python parametric CAD scripting framework based on OCCT
https://cadquery.readthedocs.io
Other
3.25k stars 294 forks source link

Guidance needed on using de-serialization libraries with CADQuery editor #702

Open ornamentist opened 3 years ago

ornamentist commented 3 years ago

I have a simple CADQuery script that makes use of several data classes, including this one:

@dataclass
class Point2:
  '''Represents a 2D point.'''
  x : float
  y : float

I'd like to be able to de-serialize instances of these data classes from Python dictionaries (and later JSON) so I turned to mashumaro, which requires this mixin style:

@dataclass
class Point2(DataClassDictMixin):
  '''Represents a 2D point.'''
  x : float
  y : float

De-serializing data class instances works as expected using this script from the command line, but when run in the CADQuery editor I can see an error in the Current Traceback window KeyError: Temp with the line pointing to the first mixed in data class. Execution stops at that point.

Okay, so I tried doing de-serialization with dacite, with no mixins required, but the editor gives the same error, this time pointing to the relevant from_dict(...) line. Again, the script works as expected when run from the command line.

Is there something special about running these scripts in the editor that I need to take into account?

I'm returning to Python (for CADQuery) after a very long break from using it, so I could well have misunderstood how to go about this.

ornamentist commented 3 years ago

Not sure if this issue should be raised here or the CQ-editor repository, I'll close and move it if that's the case.

fedorkotov commented 3 years ago

Please provide full text of a failing script so that we can reproduce the problem

ornamentist commented 3 years ago

Okay, I've tried to strip this down to a MWE. In the full script there are several other data classes and more functions to generate geometry, but the error is the same. I've also tried my third de-serialization library in this example:

#! /usr/bin/env python3

# External Python libraries
import cadquery as CQ
from dataclasses_json import DataClassJsonMixin

# Python standard libraries
from typing import *
from dataclasses import *

@dataclass
class Point2(DataClassJsonMixin):
  '''Represents a 2D point.'''
  x : float
  y : float

POINT = Point2.from_dict({"x": 10, "y": 10})
print(POINT)

show_object(CQ.Workplane("XY").box(10, 10, 20).faces(">Z").workplane().hole(5))
adam-urbanczyk commented 3 years ago

I get (logical): No module named dataclasses_json. Can you provide a reproducible example?

fedorkotov commented 3 years ago

@adam-urbanczyk dataclasses_json is from dataclasses-json package. After installing it I got the same result as @ornamentist (KeyError: Temp ).

fedorkotov commented 3 years ago

This is the stack trace I got when I executed this script in CQ-editor

import cadquery as CQ
from dataclasses_json import DataClassJsonMixin

# Python standard libraries
from dataclasses import dataclass

@dataclass
class Point2(DataClassJsonMixin):
  '''Represents a 2D point.'''
  x : float = None
  y : float = None

r = CQ.Workplane("XY").box(10, 10, 20)

and then ran Point2.from_dict({"x": 10.123, "y": 10.123, "temp":13}) in CQ-editor's built in console

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-1-5f9150ceb634> in <module>
----> 1 Point2.from_dict({"x": 10.123, "y": 10.123, "temp":13})

~\miniconda3\envs\cadquery\lib\site-packages\dataclasses_json\api.py in from_dict(cls, kvs, infer_missing)
     81                   *,
     82                   infer_missing=False) -> A:
---> 83         return _decode_dataclass(cls, kvs, infer_missing)
     84 
     85     def to_dict(self, encode_json=False) -> Dict[str, Json]:

~\miniconda3\envs\cadquery\lib\site-packages\dataclasses_json\core.py in _decode_dataclass(cls, kvs, infer_missing)
    150 
    151     init_kwargs = {}
--> 152     types = get_type_hints(cls)
    153     for field in fields(cls):
    154         # The field should be skipped from being added

~\miniconda3\envs\cadquery\lib\typing.py in get_type_hints(obj, globalns, localns)
   1221         for base in reversed(obj.__mro__):
   1222             if globalns is None:
-> 1223                 base_globals = sys.modules[base.__module__].__dict__
   1224             else:
   1225                 base_globals = globalns

KeyError: 'temp'

Don't have time to investigate it further now. Maybe this will be useful for someone else.

ornamentist commented 3 years ago

For some reason I don't see a stack trace or any other feedback in the editor windows. This is on an M1 Mac running Big Sur, which may be a factor.

I've also tried dacite and mashumaro as the serialization libraries with the same error.

fedorkotov commented 3 years ago

I did what I described in the previous post on Windows 8.1

jmwright commented 3 years ago

CQ-editor is using a temp module, which I believe would put it in the list of modules to be searched.

https://github.com/CadQuery/CQ-editor/blob/bb61e901e32133e26558dca5984819d1504f757b/cq_editor/widgets/debugger.py#L174

adam-urbanczyk commented 3 years ago

The temp module is used to inject some locals (show_object,log,debug, ...). It is not added to sys.modules. Why would want to access it actually?

adam-urbanczyk commented 3 years ago

If I add the temp module to sys.modules your code works. @ornamentist I can fix it in CQ-editor, but isn't this a bug in the serialization libs you are using?

ornamentist commented 3 years ago

Would it be the same bug in three different serialization libraries?

I could try a fourth one, but as a work-around I switched from data classes to simple dicts and ditched the libraries.

I'll try again with the libraries in the future when the M1 Mac is an officially supported platform.

adam-urbanczyk commented 3 years ago

Can you explain why the libs need to access the temp module via sys.modules?

ornamentist commented 3 years ago

I'm sorry but I have no idea.

I'm returning to Python (for CADQuery) after a very long time away from it. In that situation I don't want to add to the maintenance burden of the project and would be happy to close the issue.