Porting to python3

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
wmayer
Site Admin
Posts: 14614
Joined: Thu Feb 19, 2009 10:32 am

Re: Porting to python3

Postby wmayer » Thu Jan 07, 2016 1:30 pm

I can do that too, just go to the compiled lib folder, run a python3 console and "import FreeCAD". This is what I have in that folder, btw, no libFreeCAD.so:
Ha ha ha! Stupid me. You are right. But at least what I said applies to FreeCADGui, then. :D
wmayer
Site Admin
Posts: 14614
Joined: Thu Feb 19, 2009 10:32 am

Re: Porting to python3

Postby wmayer » Thu Jan 07, 2016 1:44 pm

Yes that might be the problem, but I still couldn't find where/how that is done... At the moment I can see no init function in src/App/Application.cpp (I suppose that is where it should be), I thought all this was done automatically by the module init macros, but maybe I was wrong... Will investigate further.
No. That's what I wanted to explain. FreeCADApp.so (or .dll) is not a Python module. However, internally inside Application::Application we create a Python module called "FreeCAD" and add the classes Vector, Matrix, ... to it and a few things more. You don't need this "init" function here because you cannot import FreeCADApp via Python.

To see if the port for https://github.com/yorikvanhavre/FreeCA ... n.cpp#L189 is correct you should start FreeCADCmd. Does this crash at startup or not? The point with the Python3 API is that we have to do something (we return it to Python) with the pointer we get from PyModule_Create when using it inside an init function.

So, maybe we also have to hand over this pointer to Python we create inside Application::Application.
User avatar
yorik
Site Admin
Posts: 11458
Joined: Tue Feb 17, 2009 9:16 pm
Location: São Paulo, Brazil
Contact:

Re: Porting to python3

Postby yorik » Fri Jan 08, 2016 1:36 am

Aah okay now I understood. importing the FreeCAD module from an external console uses the stuff in mainpy.cpp, which isn't used when you launch FreeCAD directly...

Okay, let's leave mainpy for later then. One headache at a time :)

I fixed a couple of more errors, the latest stuff is now on https://github.com/yorikvanhavre/FreeCAD/tree/py3-5

So far, it compiles with both python2 and 3 (with all modules disabled) and doesn't crash anymore when running FreeCADCmd. However, some python stuff still fails to initialize and the app terminates...
With py2:

Code: Select all

 ~/Sources/build/fcpy2> bin/FreeCADCmd   
FreeCAD 0.16, Libs: 0.16R6179 +12 (Git)
(c) Juergen Riegel, Werner Mayer, Yorik van Havre 2001-2015
  #####                 ####  ###   ####  
  #                    #      # #   #   # 
  #     ##  #### ####  #     #   #  #   # 
  ####  # # #  # #  #  #     #####  #   # 
  #     #   #### ####  #    #     # #   # 
  #     #   #    #     #    #     # #   #  ##  ##  ##
  #     #   #### ####   ### #     # ####   ##  ##  ##

Initialization of FreeCAD failed:
While initializing FreeCAD the  following exception occurred: ''ParameterGrp' object has no attribute 'GetString''

Python is searching for its runtime files in the following directories:
/usr/lib/python2.7/:/usr/lib/python2.7/plat-x86_64-linux-gnu:/usr/lib/python2.7/lib-tk:/usr/lib/python2.7/lib-old:/usr/lib/python2.7/lib-dynload

Python version information:
2.7.11 (default, Dec  9 2015, 00:29:25) 
[GCC 5.3.1 20151205]

Please contact the application's support team for more information.
with py3:

Code: Select all

~/Sources/build/fcpy3> bin/FreeCADCmd   
FreeCAD 0.16, Libs: 0.16R6179 +12 (Git)
(c) Juergen Riegel, Werner Mayer, Yorik van Havre 2001-2015
  #####                 ####  ###   ####  
  #                    #      # #   #   # 
  #     ##  #### ####  #     #   #  #   # 
  ####  # # #  # #  #  #     #####  #   # 
  #     #   #### ####  #    #     # #   # 
  #     #   #    #     #    #     # #   #  ##  ##  ##
  #     #   #### ####   ### #     # ####   ##  ##  ##

Initialization of FreeCAD failed:
While initializing FreeCAD the  following exception occurred: 'No module named 'FreeCAD''

Python is searching for its runtime files in the following directories:
0x1f25fd0

Python version information:
3.5.1 (default, Dec 10 2015, 14:34:41) 
[GCC 5.3.1 20151207]

Please contact the application's support team for more information.
The errors are different but it might be the same thing. Couldn't find what's wrong yet...
wmayer
Site Admin
Posts: 14614
Joined: Thu Feb 19, 2009 10:32 am

Re: Porting to python3

Postby wmayer » Fri Jan 08, 2016 11:43 am

I think i's time that I start to compile your branch. Is there anything I have to be aware of?
User avatar
yorik
Site Admin
Posts: 11458
Joined: Tue Feb 17, 2009 9:16 pm
Location: São Paulo, Brazil
Contact:

Re: Porting to python3

Postby yorik » Fri Jan 08, 2016 12:18 pm

wmayer wrote:I think i's time that I start to compile your branch
:mrgreen:
wmayer wrote:Is there anything I have to be aware of?
Nope... I just untick all modules in cmake-gui, and when compiling with python3 just change all python2-related libs to python3. Make sure you're using minimum version 3.5 (3.5 received new utf8-to-unicode conversion tools that we'd be foolish to refuse)

All the python3-related commits are prefixed with py3. Most of the changes are actually always the same thing, basically either:

- pystring is removed in python3, so everything pystring-related becomes pyunicode when using py3
- the creation of python classes has changed a little bit (it uses other macros, a couple of builtin methods have been removed, getattr/setattr are replaced by getattro/setattro, etc..). This is the part that affects both python2 and 3, and most certainly where I screwed something :) Fortunately it doesn't happen in a lot of places, basically almost everything is in Base/PyObjectBase and Tools/generate*
- the creation of modules is different too, but this normally doesn't affect py2 because they are in different #if clauses
wmayer
Site Admin
Posts: 14614
Joined: Thu Feb 19, 2009 10:32 am

Re: Porting to python3

Postby wmayer » Fri Jan 08, 2016 6:21 pm

I came one step further. It's exactly like I assumed that we have to do something with the pointer we get from PyModule_Create. It isn't obvious and I didn't find anything in the documentation but I know that Python's sys module is written in C and it's a built-in module. So, I searched through the source code and found the undocumented function _PyImport_FixupBuiltin.

So, we have to add this line:

Code: Select all

_PyImport_FixupBuiltin(pAppModule, "FreeCAD");
However, FreeCADCmd still crashes afterwards but when commenting out the line

Code: Select all

Interpreter().runString(Base::ScriptFactory().ProduceScript("FreeCADInit"));
the application works. Then you can do an "import FreeCAD" and use its module functions.
wmayer
Site Admin
Posts: 14614
Joined: Thu Feb 19, 2009 10:32 am

Re: Porting to python3

Postby wmayer » Fri Jan 08, 2016 6:32 pm

When

Code: Select all

Interpreter().runString(Base::ScriptFactory().ProduceScript("FreeCADInit"));
is enabled then the error message is
<built-in function PrintLog> returned a result with an error set
So, this means something is wrong with https://github.com/yorikvanhavre/FreeCA ... e.cpp#L524

EDIT:
When you pass a string to the Log function it raises an exception. If you pass a number or None then everything works as expected.
User avatar
yorik
Site Admin
Posts: 11458
Joined: Tue Feb 17, 2009 9:16 pm
Location: São Paulo, Brazil
Contact:

Re: Porting to python3

Postby yorik » Fri Jan 08, 2016 11:02 pm

Ok I added the fixup function too just after the PyModule_Create lines, indeed now the module starts!
Crazy, when looking for that function name on the net, you find nothing... Only you are bold enough to go dig into the python code itself Werner!
wmayer wrote:When
Interpreter().runString(Base::ScriptFactory().ProduceScript("FreeCADInit"));
is enabled then the error message is
<built-in function PrintLog> returned a result with an error set
Now this is a similar error to when compiling the branch with python2, but not at the same place:

Code: Select all

While initializing FreeCAD the  following exception occurred: ''ParameterGrp' object has no attribute 'GetString''
So yes the problem is probably in neither of the two places but at something more fundamental like the console init, or the interpreter init (I had some trouble to find what to do there too).

It's really not easy to find similar cases on the net...
wmayer
Site Admin
Posts: 14614
Joined: Thu Feb 19, 2009 10:32 am

Re: Porting to python3

Postby wmayer » Sat Jan 09, 2016 11:40 am

Crazy, when looking for that function name on the net, you find nothing... Only you are bold enough to go dig into the python code itself Werner!
The function responsible to load the shared library and invoke its PyInit entry point is _PyImport_LoadDynamicModuleWithSpec. If the initialization succeeded it calls _PyImport_FixupExtensionObject to let te interpreter know the new module. And this latter function is invoked by _PyImport_FixupBuiltin.

I assume since these functions start with an underscore they are considered for internal purposes only and that's why they are not documented.
While initializing FreeCAD the following exception occurred: ''ParameterGrp' object has no attribute 'GetString''
I don't think there is something wrong with interpreter init but the init of the ParameterGrp class.

When I want to clone your branch on github and send you pull requests what exactly do I have to do? Until now I never did something on github.
User avatar
yorik
Site Admin
Posts: 11458
Joined: Tue Feb 17, 2009 9:16 pm
Location: São Paulo, Brazil
Contact:

Re: Porting to python3

Postby yorik » Sat Jan 09, 2016 2:52 pm

wmayer wrote:I don't think there is something wrong with interpreter init but the init of the ParameterGrp class.
Maybe, but I'm not so sure.. This is basically what changed in that file:
https://github.com/yorikvanhavre/FreeCA ... f12330b72c
Not much really, unless there is some obvious mistake (or the switch from getattr -> getattro is flawed, but from what I've read it was recommended already well before python3), but I haven't found it yet...
wmayer wrote:When I want to clone your branch on github and send you pull requests what exactly do I have to do? Until now I never did something on github.
Why not push the branch with your changes to the official FreeCAD repo, I'll take it from there, we can delete it afterwards when we're done... After all the official FreeCAD repo is a bit yours by right :)

Otherwise using the github stuff is easy too, just go to https://github.com/FreeCAD/FreeCAD and click the "fork" button above, it will copy the whole repo to your own github account. Then you'll need to add the URL of this new repo of yours to your local remotes, and you can force-push any branch to there instead of the default with

Code: Select all

git push mygithub branchname
assuming mygithub is the name you gave to your new remote. If you use -u (git push -u mygithub branchname), it will "associate" that branch to that remote and afterwards you only need to "git push"

If you fork my repo instead of the official FreeCAD one, github will detect that you worked on one of my branches, and will offer you to do an automatic pull request whenever you push something there (just visit your repo page on github after a push). But this is just candy, I can grab your branch myself of course.

The only problem with having a copy of the FreeCAD repo on your own github account is the hassle to keep both up to date... I know there are ways to make it automatically push anything to both, but I was too lazy, and until today I always do "git push && git push github" whenever I push something...