Hi Dave,

You're right, there was also the idea to benefit from the OpenCascade
documentation. The deal was: 'just go and read occ documentation to get
started, I take care of python development'. I agree it's not suitable
anymore.

The 'strict separation' you're dealing with is currently the object of a
discussion we have with Jelle. Rather than 'strict', I prefer 'functional
separation'. From a functional analysis viewpoint, these different levels
(or layers) in the API serve specific user needs.

Thomas

2010/3/17 Dave Cowden <dave.cow...@gmail.com>

> Hi thomas, thanks as always for weighing in.
>
> You point about c++ to python portability is interesting. There is
> another strong argument that supports retaining a set of objects that
> map exactly to the occ ones. Doing so enables using code and
> suggestions from the opencascade forums and documentation to be
> relevant and accurate.
>
> However all of this is solved by ensuring a fairly strict separation
> between the 'high level' api and the core one. I'll simply decide
> which I want based on the needs at the time.
>
> I think you would see a decent amount of activity in a 'high level'
> api if it was visible and documented. I think this would happen
> because there are lots of people like me: I know little c++, and
> little swig, but hey I can bang out some pure python!
>
> On 3/17/10, Thomas Paviot <tpav...@gmail.com> wrote:
> > Dave, Frank,
> >
> > Thank you for reporting your feelings and needs for a better pythonOCC.
> > Maybe it's time to discuss with you the future of this library, and the
> > paths we should The project was born 2 years ago, and my initial goal is
> > reached: it's now possible to do the same things with pythonOCC as with
> OCC,
> > but from the Python language: all algorithms posted on the OCC forum can
> be
> > easily ported to python, and they work the same way.
> >
> > When starting the project, I wanted pythonOCC to be as close as possible
> to
> > the original C++ API. In my mind, python was just a convenient way to
> > prototype     C++ applications, so it was really important to me that the
> > move from python to C++ (or the opposite) was as simple as possible: same
> > classes/method names, same algorithms etc.
> >
> > However, when developing with pythonOCC, Jelle and I realized that the
> OCC
> > API is not so convenient and provide very low level objects. A more
> > 'pythonic' pythonOCC then raised in our minds. We then started to
> implement
> > what we called a 'high level API' (but we're currently discussing the
> name
> > of this additional layer): this layer provides useful and *pythonic*
> > classes/methods/functions on top of OCC. The 'Parametric Application
> > Framework' is the best example for this API, and is the work I'm the most
> > proud of: I think Jelle and I achieved a very elegant and pythonic way to
> > use and extend the features provided by both OCC/SGEOM libraries (see for
> > instance
> >
> http://code.google.com/p/pythonocc/source/browse/trunk/src/addons/PAF/Context.py
> ).
> > There are also geometric/topological 'high level' functions very similar
> to
> > the Frank's MakeEdge function. They are available from the
> Utils.Construct
> > module (http://api.pythonocc.org/OCC.Utils.Construct-module.html), but
> we
> > never posted anything or documented these functions.
> >
> > I agree with both of you Dave, Frank (and I know Jelle also agrees) that
> the
> > API needs now to be more pythonic. However, making the library more
> pythonic
> > will make us reach an irreversible point: it will not be possible anymore
> to
> > move from python code to C++ code. But I think that pythonOCC has now to
> > live it's own life, and may have to move away from its C++ 'mother' (or
> > 'father', don't know what is the gender of OCC).
> >
> > In order to achieve that point, here are the points we could focus on
> from
> > now:
> > - move TCollection*, TColgp* etc. to python lists. We developed ugly
> hacks
> > to transform such classes to python lists, but's it's not perfect and
> should
> > be done at the SWIG level,
> > - implement more 'high level api' functions/methods (as suggested by
> Frank).
> > Many of these functions have already been developed (the high level API
> > source  code is here:
> > http://code.google.com/p/pythonocc/source/browse/#svn/trunk/src/addons).
> > Maybe they should be more documented,
> > - for the topics related with memory management, Frank, let me think
> about
> > your suggestions the coming two days. Point (1) is closely related to the
> > way SWIG handles C++ references. OpenCascades makes an extensive use of
> > these awful '&' symbol. At the asme time, both OCC and python use a ref
> > counting mechanism for object deletion management: for each class, the
> SWIG
> > wrapper provides a python proxy to a C++ object (this C++ object can be
> > accessed by the  'this' attribute of any pythonOCC object). When deleting
> a
> > python class, you have to take care about the deletion of the proxy
> (python
> > ref count - gc) and the deletion of the pointed object (OCC ref
> counting). I
> > spent *many* hours studying this mechanism, and I can help you to save
> time
> > if you ever decide to dive into SWIG. Point (2) is interesting, never
> > thought about that. I don't know yet whether it's better to do it from
> the
> > Python or the SWIG/C++ level.
> >
> > Best Regards,
> >
> > Thomas
> >
> > 2010/3/17 Dave Cowden <dave.cow...@gmail.com>
> >
> >> Yes I understand, and agree that it would be better if the memory
> >> management was a bit easier
> >>
> >> I've also found that there is a fairly blurry line between
> 'pythonicness'
> >> and 'poor/ugly API'.  Having written about 2000+ lines of code, i've
> >> written
> >> a lot of wrapper classes.
> >>
> >> Many of these wrappers i'd wager are nearly identical to those any other
> >> python developer writes as soon as they deal with openCascade.  for
> >> example
> >> generators to convert topexp iteration to a for/in loop, edge wrappers
> to
> >> return points convieniently, convienience methods to created edges and
> >> curves ( I have a function nearly the same as yours in my code btw  ,
> case
> >> in point ).
> >>
> >> I guess what i'm saying is that, though garbage collection helps, its
> also
> >> true that your MakeEdge method is  somewhat compensating for a klunky
> api
> >> that doesnt just return Shape() and throw an exception instead of doing
> >> Perform() then IsDone() and such.  Its clear that the solution to this
> is
> >> to
> >> let people contribute helper and wrappers over time so that we all
> >> benefit.
> >>
> >> I think what we need from Thomas/Jelle is a place that these can be
> >> checked
> >> in and then apidoc is autogenerated. I'd be more than happy to commit
> some
> >> of my utilty/wrapper code, but i'd want to know where i can look to see
> >> the
> >> apidoc for the other such code is before I add it...
> >>
> >>
> >> On Wed, Mar 17, 2010 at 1:09 PM, Frank Conradie <fr...@qfin.net> wrote:
> >>
> >>>  Hi Dave
> >>>
> >>> Thanks for the suggestion - closures would certainly help. However, I
> am
> >>> currently less interested in ways to work *around* the "un-pythonic"
> >>> behavior of the wrapper, but would rather like to discuss the
> >>> technicalities
> >>> and politics(!) of making the wrapper more "pythonic". Here is what I
> >>> would
> >>> ideally like to do:
> >>>
> >>> * Clear up exactly what "un-pythonic" behavior I have run into while
> >>> using
> >>> the library for a fairly complicated algorithm (two modules, 3000+
> lines
> >>> of
> >>> code).
> >>> * Discuss whether it is *technically* possible to change any of this
> >>> behavior to be more "pythonic"
> >>> * If it is indeed *technically* possible, discuss whether it is
> feasible
> >>> and worthwhile to actually implement
> >>>
> >>> Here is a summary of the current "unexpected" behavior as I see it:
> >>>
> >>> (1). When using OCC objects in Python, an unsuspecting programmer would
> >>> expect them to behave like Python objects, i.e. use Python ref counting
> >>> and
> >>> garbage collection behavior. E.g. a BRepBuilderAPI object has a
> >>> TopoDS_Shape
> >>> member, so this "myShape" has a refcount of 1 initially, and if it is
> >>> retrieved via the Shape() method and put into, say, a list, its
> refcount
> >>> must be 2, etc. So even if the BRepBuilderAPI object goes out of scope,
> >>> its
> >>> internal shape is expected to still have a refcount of 1 and "survive"
> a
> >>> garbage collector purge. However, this refcounting obviously does not
> >>> happen
> >>> on the C++ side, so the BRepBuilderAPI object would have to be kept
> >>> "alive"
> >>> as long as its internal shape is needed.
> >>>
> >>> (2). Getting the "internal" shape from a BRepBuilderAPI, BRepPrimAPI,
> >>> BRepAlgoAPI, etc. object:
> >>>     * C++: calling Shape() always returns the *same* myShape instance
> >>>     * Python: calling Shape() returns a *new* SWIG wrapper object every
> >>> time, each with the same __hash__ value so they "look" like the same
> >>> instance (at least to sets and dicts)
> >>>     * The same unexpected behavior occurs when "exploring" a shape,
> i.e.
> >>> new SWIG wrapper objects are instantiated for every returned shape
> >>>     * The fact that different SWIG wrapper objects hash to the same
> value
> >>> at least allows us to identify unique shapes when put in sets and
> dicts,
> >>> but
> >>> it confuses the GarbageCollector, as the same C++ object can be
> >>> "collected"
> >>> multiple times, once for each SWIG object wrapping it
> >>>
> >>> Now, I can live with point (1), as I can see how this would be near
> >>> impossible to implement to work like it would in Python. However, I
> >>> really
> >>> would like to discuss whether point (2) can be addressed, so that there
> >>> is
> >>> always a 1-to-1 correspondence between a C++ object and its SWIG
> wrapper
> >>> object. The SWIG wrapper already generates a single unique __hash__
> value
> >>> for a single C++ object, so would it not be feasible to keep a map of
> C++
> >>> object --> hash value --> SWIG wrapper object?
> >>>
> >>> In pseudo-code, when the wrapper is asked for an OCC object:
> >>>
> >>> h = hash(occobject)
> >>> if h in occ2swig_map:
> >>>     swig_wrapper = occ2swig_map[h]
> >>> else:
> >>>     swig_wrapper = new SwigWrapper(occobject)
> >>>     occ2swig_map[h] = swig_wrapper
> >>> return swig_wrapper
> >>>
> >>> I have never used SWIG myself, so excuse any ignorance on my side (I
> will
> >>> try and spend some time looking deeper into SWIG over the next few
> days).
> >>>
> >>> Hopefully we can have a fruitful and open discussion about this. I can
> >>> certainly work with the library as-is, and am indeed doing so
> >>> successfully
> >>> at the moment. I just find that it is no fun having to deal with the
> very
> >>> kinds of C++ issues I am trying to avoid by using Python in the 1st
> place
> >>> ;-)
> >>>
> >>> Cheers,
> >>> Frank
> >>>
> >>>
> >>>
> >>>
> >>> On 17/03/2010 4:22 AM, Dave Cowden wrote:
> >>>
> >>> Hi, Frank:
> >>>
> >>>  I am not sure, but I think you could implement your utility function
> >>> using a python closure. This would allow the original object to remain
> in
> >>> scope until the nested function has completed. so for example:
> >>>
> >>>  To do closures in python, you have to use lamda, which creates a new
> >>> function having access to the outer scope, even after the function
> >>> returns.
> >>> So
> >>>
> >>>  def MakeEdge(p1, p2):
> >>>     gp1,gp2 = [gp_Pnt(x,y,z) for x,y,z in (p1,p2)]
> >>>     mkedge = BRepBuilderAPI_MakeEdge(gp1, gp2)
> >>>     return mkedge.Shape()
> >>>
> >>>  Becomes:
> >>>
> >>>  def MakeEdge(p1,p2):
> >>>     gp1,gp2 = [gp_Pnt(x,y,z) for x,y,z in (p1,p2)]
> >>>      mkedge = BRepBuilderAPI_MakeEdge(gp1, gp2)
> >>>       return lambda e: mkedge.Shape();
> >>>
> >>>  now you can do
> >>> me = MakeEdge(p1,p2);
> >>>
> >>>  me.Shape() //return the generated shape
> >>> me.Shape() //return the generated shap again. It will be the same
> object
> >>> because the underlying brepbuilder is still in a closure
> >>>
> >>>  Now if you kill me, the closure will be discarded.
> >>>
> >>>  Dont know if this helps any or not.
> >>>
> >>> On Tue, Mar 16, 2010 at 7:53 PM, Frank Conradie <fr...@qfin.net>
> wrote:
> >>>
> >>>>  Hi Thomas
> >>>>
> >>>> After much investigation today, I have now added a flag to
> >>>> GarbageCollecter to disable it for our use, as it really does not work
> >>>> as
> >>>> intended for relatively complex code (I now manually track which
> objects
> >>>> I
> >>>> create and need to call _kill_pointed for). There are two main issues
> >>>> that
> >>>> we run into with the current GarbageCollector:
> >>>>
> >>>> 1. As already discussed, classes like the BRepPrimAPI and BRepAlgoAPI
> >>>> have an internal "myShape" data member that gets destroyed when the
> >>>> parent
> >>>> object's _kill_pointed method is called, causing all kinds of issues.
> As
> >>>> an
> >>>> example, it makes utility functions like the one below unusable, as
> the
> >>>> local BRepPrimAPI object goes out of scope and is thus collected by
> >>>> GarbageCollector for purging, even though the caller of the function
> may
> >>>> want to use the resulting shape for some time to come, after the
> parent
> >>>> is
> >>>> "purged":
> >>>>
> >>>> def MakeEdge(p1, p2):
> >>>>     gp1,gp2 = [gp_Pnt(x,y,z) for x,y,z in (p1,p2)]
> >>>>     mkedge = BRepBuilderAPI_MakeEdge(gp1, gp2)
> >>>>     return mkedge.Shape()
> >>>>
> >>>> 2. Every time a C++ OCC object is retrieved from Python in any way, a
> >>>> brand new SWIG wrapper object is instantiated for it, very
> >>>> easily/quickly
> >>>> leading to multiple wrapper objects on the Python side for the same
> >>>> underlying C++ object (I see this especially when using OCC shape
> >>>> containers
> >>>> repeatedly). When any one of these wrapper objects goes out of scope
> and
> >>>> is
> >>>> collected and purged, the underlying C++ object is destroyed (via
> >>>> _kill_pointed), even though there may well be other Python wrapper
> >>>> objects
> >>>> still needing access to it.
> >>>>     I guess the ideal solution to this issue would be to keep a
> mapping
> >>>> of C++ object -> SWIG wrapper object on the C++ side, so that there is
> >>>> always a 1-to-1 correspondence between the two, but I'm not sure how
> >>>> plausible this really is from an implementation point-of-view. As an
> >>>> example
> >>>> of what I mean, consider this: every time you call "mkedge.Shape()" in
> >>>> my
> >>>> example above, you get a new/different Python object, wrapping the
> same
> >>>> underlying myShape instance, which is totally unexpected and
> unintuitive
> >>>> from a Python perspective. A "pure" Python programmer would expect to
> >>>> get
> >>>> the exact same Python object back every time, and not different
> wrapper
> >>>> objects with just an "artificial" identical hash value.
> >>>>
> >>>> I realize that this is a very difficult issue, and that as users of
> the
> >>>> PythonOCC wrapper library we will need to take responsibility for
> memory
> >>>> management issues, but I can see lots of confusion for Python
> >>>> programmers
> >>>> using the library and expecting "Python-like" behaviour out of the
> box.
> >>>>
> >>>> I wish I new more about the SWIG wrapper object instantiation process
> so
> >>>> that I could make more concrete suggestions, instead of just
> complaining
> >>>> ;-)  What is your gut-feeling about this Thomas - do you think these
> two
> >>>> issues can be addressed in the wrapper, or is the current way the only
> >>>> viable implementation? If you can give me a short description of the
> >>>> SWIG
> >>>> wrapper instantiation process, I may be able to delve into it myself.
> >>>>
> >>>> Thanks for your help with this thus far,
> >>>> Frank
> >>>>
> >>>>
> >>>>
> >>>>
> >>>> On 16/03/2010 9:04 AM, Thomas Paviot wrote:
> >>>>
> >>>> Hi Frank,
> >>>>
> >>>>  I also suggest you use the push_context() and pop_context() methods
> at
> >>>> the beginning and the end of your algorithm. garbage.push_context()
> >>>> creates
> >>>> a new collector. Objects collected will go to this new list. Then, at
> >>>> the
> >>>> end of your algorithm, just call .smart_purge() and then
> pop_context().
> >>>> Otherwise, when calling the smart_purge() method, you'll erase all
> other
> >>>> objects that might have been stored in the gc thus causing strange
> >>>> things.
> >>>>
> >>>>  def my_high_memory_consuming_algorithm():
> >>>>     GarbageCollector.garbage.push_context()
> >>>>
> >>>>      ... do whatever you want ...
> >>>>
> >>>>      GarbageCollector.garbage.smart_purge() #only the objects created
> >>>> since the call to push_context are erased. Use MMGT_OPT=0 to make sure
> >>>> freeing #memory.
> >>>>     GarbageCollector.pop_context()
> >>>>     return data
> >>>>
> >>>>  There's also a garbage.prevent_object(object) method, that will take
> >>>> care to not delete an object that it's important to keep reference.
> >>>>
> >>>>  These three functions are quite new (but validated with a unittest).
> >>>> I'm curious to have your feedback!
> >>>>
> >>>>  Regards,
> >>>>
> >>>>  Thomas
> >>>>
> >>>>  These two functions are quite new,
> >>>> 2010/3/16 Frank Conradie <fr...@qfin.net>
> >>>>
> >>>>> Hi Thomas
> >>>>>
> >>>>> Well, I'm glad it's not a bug then! I can think of a few ways to
> handle
> >>>>> this situation in my algorithm, so it should not be a problem. I'm
> just
> >>>>> wondering how many other classes behave in this "unexpected" way :O
> >>>>>
> >>>>> Cheers,
> >>>>> Frank
> >>>>>
> >>>>>
> >>>>> On 15/03/2010 10:26 PM, Thomas Paviot wrote:
> >>>>>
> >>>>> Hi Frank,
> >>>>>
> >>>>>  This behaviour does not come from SWIG but from OpenCASCADE itself.
> >>>>> Let me explain you in a few words what happens here.
> >>>>>
> >>>>>  The BRepPrimAPI_MakeBox class inherits from the
> >>>>> BRepBuilderAPI_MakeShape one. This class provides the Shape() method
> >>>>> you can
> >>>>> find in BRepPrimAPI_Make* basic geometry constructors. If you have a
> >>>>> look at
> >>>>>  the Shape() method in the file BRepBuilderAPI_MakeShape.cxx, you
> will
> >>>>> read:
> >>>>>
> >>>>>  """
> >>>>>
> >>>>>
> //=======================================================================
> >>>>> //function : Shape
> >>>>> //purpose  :
> >>>>>
> >>>>>
> //=======================================================================
> >>>>>
> >>>>>  const TopoDS_Shape&  BRepBuilderAPI_MakeShape::Shape() const
> >>>>> {
> >>>>>   if (!IsDone()) {
> >>>>>     // the following is const cast away
> >>>>>     ((BRepBuilderAPI_MakeShape*) (void*) this)->Build();
> >>>>>     Check();
> >>>>>   }
> >>>>>   return myShape;
> >>>>> }
> >>>>>  """
> >>>>>
> >>>>>  The Shape() method then returns a reference to the protected
> attribute
> >>>>> 'myShape' of the class BRepBuilderAPI_MakeBox. In other words, if you
> >>>>> delete
> >>>>> the BRepPrimAPI_MakeBox instance, you loose the reference to the
> shape.
> >>>>>
> >>>>>  For instance:
> >>>>>
> >>>>>  >>> from OCC.BRepPrimAPI_MakeBox import *
> >>>>> >>> from OCC.TopoDS import *
> >>>>> >>> box = BRepPrimAPI_MakeBox(10,10,10)
> >>>>> >>> shp  = box.Shape()
> >>>>> >>> print shp.IsNull()
> >>>>> 0
> >>>>> >>> del box #object is garbage collected, not deleted yet
> >>>>> >>> print shp.IsNull()
> >>>>> 0
> >>>>> >>> GarbageCollector.garbage.smart_purge() # all objects are deleted,
> >>>>> loose the reference to myShape attribute
> >>>>> >>> print shp.IsNull()
> >>>>> 1
> >>>>>
> >>>>>  Then force objects deletion only if you're sure you don't need the
> >>>>> shapes anymore. I don't know what you're trying to do exactly, so
> it's
> >>>>> difficult to suggest any best practice.
> >>>>>
> >>>>>  Regards,
> >>>>>
> >>>>>  Thomas
> >>>>>
> >>>>> 2010/3/15 Frank Conradie <fr...@qfin.net>
> >>>>>
> >>>>>> Hi Thomas
> >>>>>>
> >>>>>> I am getting crash behaviour in the interim 0.5 PythonOCC release,
> >>>>>> related to the new GarbageCollector implementation. The code below
> >>>>>> crashes on my PC:
> >>>>>>
> >>>>>> ----------------------------------------
> >>>>>> from OCC.gp import *
> >>>>>> from OCC.BRepPrimAPI import *
> >>>>>> from OCC.BRepBuilderAPI import *
> >>>>>> from OCC.TopExp import *
> >>>>>> from OCC.TopoDS import *
> >>>>>>
> >>>>>> o = 0.0
> >>>>>> w = 0.1
> >>>>>> fp1 = (o,o,o)
> >>>>>> fp2 = (w,w,w)
> >>>>>>
> >>>>>> mkbox = BRepPrimAPI_MakeBox(gp_Pnt(fp1[0],fp1[1],fp1[2]),
> >>>>>> gp_Pnt(fp2[0],fp2[1],fp2[2]))
> >>>>>> s = mkbox.Shape()
> >>>>>> del mkbox
> >>>>>>
> >>>>>> print s.ShapeType()
> >>>>>> GarbageCollector.garbage.smart_purge()
> >>>>>> # The next line crashes my system
> >>>>>> print s.ShapeType()
> >>>>>> ----------------------------------------
> >>>>>>
> >>>>>> It is almost as if calling _kill_pointed for the mkbox causes shape
> >>>>>> "s"
> >>>>>> to become "corrupted" in some way. I dug through the SWIG code a
> bit,
> >>>>>> but cannot see anything obviously wrong, so I was hoping you could
> >>>>>> shed
> >>>>>> some light on the issue.
> >>>>>>
> >>>>>> By the way, I did get the SVN compiled myself as well, and get the
> >>>>>> same
> >>>>>> crash with my compiled lib as well as the one you made available on
> >>>>>> your
> >>>>>> website.
> >>>>>>
> >>>>>> Thanks,
> >>>>>> Frank
> >>>>>>
> >>>>>>
> >>>>>> _______________________________________________
> >>>>>> Pythonocc-users mailing list
> >>>>>> Pythonocc-users@gna.org
> >>>>>> https://mail.gna.org/listinfo/pythonocc-users
> >>>>>>
> >>>>>
> >>>>>
> >>>>> _______________________________________________
> >>>>> Pythonocc-users mailing
> >>>>> listpythonocc-us...@gna.orghttps://
> mail.gna.org/listinfo/pythonocc-users
> >>>>>
> >>>>>
> >>>>> _______________________________________________
> >>>>> Pythonocc-users mailing list
> >>>>> Pythonocc-users@gna.org
> >>>>> https://mail.gna.org/listinfo/pythonocc-users
> >>>>>
> >>>>>
> >>>>
> >>>> _______________________________________________
> >>>> Pythonocc-users mailing
> >>>> listpythonocc-us...@gna.orghttps://
> mail.gna.org/listinfo/pythonocc-users
> >>>>
> >>>>
> >>>> _______________________________________________
> >>>> Pythonocc-users mailing list
> >>>> Pythonocc-users@gna.org
> >>>> https://mail.gna.org/listinfo/pythonocc-users
> >>>>
> >>>>
> >>>
> >>> _______________________________________________
> >>> Pythonocc-users mailing
> >>> listpythonocc-us...@gna.orghttps://
> mail.gna.org/listinfo/pythonocc-users
> >>>
> >>>
> >>> _______________________________________________
> >>> Pythonocc-users mailing list
> >>> Pythonocc-users@gna.org
> >>> https://mail.gna.org/listinfo/pythonocc-users
> >>>
> >>>
> >>
> >> _______________________________________________
> >> Pythonocc-users mailing list
> >> Pythonocc-users@gna.org
> >> https://mail.gna.org/listinfo/pythonocc-users
> >>
> >>
> >
>
> _______________________________________________
> Pythonocc-users mailing list
> Pythonocc-users@gna.org
> https://mail.gna.org/listinfo/pythonocc-users
>
_______________________________________________
Pythonocc-users mailing list
Pythonocc-users@gna.org
https://mail.gna.org/listinfo/pythonocc-users

Reply via email to