Board index FlightGear Development New features

FGPython an propose for Python as an nasal alternative

Discussion and requests for new features. Please note that FlightGear developers are volunteers and may or may not be able to consider these requests.

Re: FGPython an propose for Python as an nasal alternative

Postby Hooray » Thu Dec 31, 2015 9:32 pm

If all you need is SGPropertyNode access for standalone (non FG) projects, that is even easier to do, see the nasal-props.cxx code, all that would need changing there is using the Python APIs instead of the Nasal stuff. But I stand by my assertion that it is much easier to use a wrapping API like Boost and/or end up using SIP/SWIG to create those bindings automatically (it's working pretty much like IDL)
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: FGPython an propose for Python as an nasal alternative

Postby Philosopher » Thu Dec 31, 2015 9:48 pm

FWIW Python has a __getattr__ method which would make for a really cool OOP interface for the property tree! As Curt described, properties could thus be referenced using Python syntax.

[avoiding debate]
Philosopher
 
Posts: 1593
Joined: Sun Aug 12, 2012 7:29 pm

Re: FGPython an propose for Python as an nasal alternative

Postby Hooray » Thu Dec 31, 2015 10:07 pm

which is pretty much what the getValues() method in props.nas is doing by returning a property tree (branch) as a Nasal hash.
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: FGPython an propose for Python as an nasal alternative

Postby bugman » Sat Jan 02, 2016 12:03 am

@Hooray: I have direct experience coding at the Python-C interface (for example this C module implementing a Python module). Using a wrapper layer is not the way to go. You'll see no additional wrapper layer in my code. It is quite popular for those who wish to avoid learning C. But if you know both Python and C, you should work at the raw interface. This is far more advanced and powerful than any wrapper ever could be. For exposing the FG internals to an embedded interpreter, having full control is quite important. If you read the documentation about the C->Python interface (embedding the interpreter), you'll see no mention of wrappers. The person who adds this to FG will need to know C, and therefore the wrapper layer is moot. More useful would be to write the C code to expose the internals of FG via Python functions (in special packages written in C), so that this can be the wrapper the Python developer uses.

@Curt: For the props Python object, I would suggest implementing this in C as a new type of Python object. You can then define the __getattr__(), __setattr__(), __delattr__() methods as C wrapper functions for user interaction with the object. I would suggest that __getattr__() simply be a wrapper for the fgGetNode() C++ function. The C++ property node object can then be presented as a Python object, set up the same way as 'props'. I would personally construct the props object tree in this way, as a fully custom Python object written in C, with the Python object property methods defined as wrappers for the current C interface to the property tree. This will give the control needed for creating a very powerful Python props object.

Regards,
Edward
bugman
Moderator
 
Posts: 1808
Joined: Thu Mar 19, 2015 10:01 am
Version: next

Re: FGPython an propose for Python as an nasal alternative

Postby Hooray » Sat Jan 02, 2016 12:35 am

Using a wrapper layer is not the way to go [...]
It is quite popular for those who wish to avoid learning C.

I suggested the use of a wrapper, like Boost.Python, not because people may not know C, but because it takes away from the low-level details of dealing with the low-level nature of C, (e.g. typing, pointers), i.e. due to concepts like RAII and ecapsulation of state (think C pointers, structs etc) that would otherwise need to be handled manually.

The person who adds this to FG will need to know C, and therefore the wrapper layer is moot.


If that were the case, we would not have the cppbind framework either.

I'd say that anybody able to use sophisticated C++ code like Boost.Python would/should also be able to use lower-level C, it's just more tedious obviously - just like assembly is more tedious than C. Obviously, the Nasal engine is also written in C, but the cppbind framework takes away many of the redundant, lower level, details of dealing with Nasal's C interface.

At the end of the day, it's obviously up to the people doing this (should anybody step up to tackle this).
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: FGPython an propose for Python as an nasal alternative

Postby wkitty42 » Sat Jan 02, 2016 3:09 am

Hooray wrote in Sat Jan 02, 2016 12:35 am:
Using a wrapper layer is not the way to go [...]
It is quite popular for those who wish to avoid learning C.

I suggested the use of a wrapper, like Boost.Python, not because people may not know C, but because it takes away from the low-level details of dealing with the low-level nature of C,

wrappers add layers of code... layers of code add to processing time and, more importantly, code bloat for no real good reason...

one of my instructors years ago tasked out class with a project... one of the important tenants was that the code be "like a bikini. short and to the point."... wrappers ruin the bikini concept of code (and beaches) ;) O:)
"You get more air close to the ground," said Angalo. "I read that in a book. You get lots of air low down, and not much when you go up."
"Why not?" said Gurder.
"Dunno. It's frightened of heights, I guess."
User avatar
wkitty42
 
Posts: 9146
Joined: Fri Feb 20, 2015 4:46 pm
Location: central NC, USA
Callsign: wk42
Version: git next
OS: Kubuntu 20.04

Re: FGPython an propose for Python as an nasal alternative

Postby Hooray » Sat Jan 02, 2016 3:39 pm

wrappers add layers of code... layers of code add to processing time and, more importantly, code bloat for no real good reason...

Again, it's about the pain/gain ratio - otherwise, FG would be written in C, or even assembly language, and it would not be using OpenSceneGraph but OpenGL.

The degree of "code bloat" you are talking about is at the code generation level, i.e. is easy to optimize for a compiler's middle-end, in contrast to tons of explicit hand-written spaghetti code. Creating 1000 instances of a class, each having 10 methods and using virtual dispatch, the compiler can make assumptions about the layout of the code, that it simply cannot make when dealing with a similar amount of hand-written code in a lower level language - which even is a problem for RTL and SSA representations (you don't get much lower level than register transfer language or static-single-assignment form).

Thus, the difference is writing 100% of the code yourself, or using a more expressive form to have it generated for you (think classes, templates), by the compiler.
Note that the gcc project used to be written entirely in C, but the FSF has meanwhile changed its standpoint and allows C++ as the implementation language for gcc.

So if you want to debate "code bloat", please pick the right audience (I suggest the gcc mailing list), but also make sure that you understand what you are talking about.

In this context, the only thing that matters is to come up with a simple prototype - a wrapper may significantly help with that, if you don't agree that's great - you are free to use C. The main technical argument in favor of not using a wrapper is that the lower-level APIs are much richer obviously, while some wrappers are fairly incomplete still
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: FGPython an propose for Python as an nasal alternative

Postby www2 » Sat Jan 02, 2016 4:18 pm

In Other news.
I work current on a standalone prototype library for the fgfs module for nasal to python ports and testing new code.
www2
 
Posts: 319
Joined: Thu Apr 16, 2009 2:58 pm
OS: Ubuntu

Re: FGPython an propose for Python as an nasal alternative

Postby Hooray » Sat Jan 02, 2016 8:28 pm

I am not sure what it is that you are doing, but I agree with bugman's earlier posting on how to proceed with prototyping this.
I don't know how many people are actually interested in seeing this implemented - but maybe people should post their skills here to see if we can get this going.
I could certainly post a patch within a couple of days to integrate Python according to Curt's experiments, i.e. using a dedicated SGSubsystem - but based on what I am seeing, it seems like a waste of time, unless there's actually someone willing to take it from there - i.e. ideally people who don't just want to "use" Python, but who are already able to patch/build fg and use git.

As far as I can tell, bugman is the only one to volunteer with the coding side of this - any other takers ?
Anybody else familiar with C or C++ ?
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: FGPython an propose for Python as an nasal alternative

Postby www2 » Sat Jan 02, 2016 10:38 pm

Hooray I work on the prototype of the main module that interface pops and canvas to python. (note i work current on wrap subset of pops.nas)
www2
 
Posts: 319
Joined: Thu Apr 16, 2009 2:58 pm
OS: Ubuntu

Re: FGPython an propose for Python as an nasal alternative

Postby curt » Sat Jan 02, 2016 11:03 pm

Meanwhile I wrote a really quick/simple property tree implementation in python ... it doesn't do any of the fancy stuff the FlightGear property system does (like listeners, read/write tracing, etc.) But it does implement a basic shared property tree and allows (in python) direct referencing of property tree elements. https://github.com/AuraUAS/aura-core/bl ... n/props.py
curt
Administrator
 
Posts: 1168
Joined: Thu Jan 01, 1970 1:00 am
Location: Minneapolis, MN

Re: FGPython an propose for Python as an nasal alternative

Postby bugman » Fri Jan 22, 2016 6:18 pm

Just a quick update for those not following the all important development mailing list. This follows from my post:

    [flightgear-devel] A FGPythonSys implementation: The embedded Python interpreter as a FlightGear subsystem. (Gmane thread, SF archive).

In summary, I have implemented the FGPythonSys subsystem to provide access to the embedded Python interpreter and have implemented the 'props' data structure for accessing the property tree. The code is in the branches:


And a test py-ogeL aircraft exists which I used for testing the implementation:


The full capabilities of the current system can be seen in the included test.py script. The result is visible during start up as the printouts:

Code: Select all
[edward@localhost python]$ fgfs --aircraft=py-ogel
Enabling ATI viewport hack
Starting automatic scenery download/synchronization. Using built-in SVN support. Directory: '/flightgear/home/.fgfs/TerraSync'.
No path in sim/sound/path
Loading local weather routines...
Animated jetways ... initialized

********************************************************************************
This is the py-ogel, test.py external python script.


#######################################
# Check out the property tree module. #
#######################################

Importing prop_tree.
prop_tree.__dict__ = ['Node', 'Props', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'extract_alias', 'extract_path', 'extract_type', 'extract_value', 'is_alias', 'is_node']
prop_tree.Props.__dict__ = ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
prop_tree.Node.__dict__ = ['__abs__', '__add__', '__and__', '__bool__', '__class__', '__delattr__', '__delitem__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__iand__', '__ifloordiv__', '__ilshift__', '__imod__', '__imul__', '__init__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__', '__isub__', '__itruediv__', '__ixor__', '__le__', '__len__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__xor__', '_path']


############################
# Testing some properties. #
############################

props.sim:           <prop_tree.Node object at 0x1071ab90, value=None, type='none', path='/sim'>
x:                   ogel-jsbsim-2.0
extract_path(x):     /sim/aero
x:                   ogel-jsbsim-2.0
y:                   True
extract_path(x):     /sim/aero
extract_path(y):     /systems/electrical/serviceable
repr(x):             <prop_tree.Node object at 0x10512a30, value='ogel-jsbsim-2.0', type='string', path='/sim/aero'>
repr(y):             <prop_tree.Node object at 0x10517f30, value=True, type='bool', path='/systems/electrical/serviceable'>


########################################
# Testing deletion of an aliased node. #
########################################



#############################
# Testing property setting. #
#############################

z:                   <prop_tree.Node object at 0x1071ab90, value=0.3, type='double', path='/environment/moonlight'>
z:                   <prop_tree.Node object at 0x1071ab90, value=0.3, type='double', path='/environment/moonlight'>
z:                   <prop_tree.Node object at 0x10512a30, value=20.0, type='double', path='/environment/moonlight[0]'>
<prop_tree.Node object at 0x10517730, value=True, type='bool', path='/systems/electrical/serviceable'>
<prop_tree.Node object at 0x10517730, value=False, type='bool', path='/systems/electrical/serviceable'>
<prop_tree.Node object at 0x10517730, value=True, type='bool', path='/systems/electrical/serviceable'>
<prop_tree.Node object at 0x1071ab90, value='test string', type='string', path='/environment/aaa'>
<prop_tree.Node object at 0x1071ab90, value=100, type='int', path='/environment/testint'>


##########################
# Testing concatenation. #
##########################


Set 0: ['a', 'b', 'ab']
        True result: ab
        Property tree result: <prop_tree.Node object at 0x106f4430, value='ab', type='string', path='/py-testing[0]/c'>
        The results match.

Set 1: [1, 2, 3]
        True result: 3
        Property tree result: <prop_tree.Node object at 0x106f4430, value=3, type='int', path='/py-testing[1]/c'>
        The results match.

Set 2: [1.0, 2.0, 3.0]
        True result: 3.0
        Property tree result: <prop_tree.Node object at 0x106f3c30, value=3.0, type='double', path='/py-testing[2]/c'>
        The results match.


#####################################
# Testing property setting failure. #
#####################################

Skipping the AttributeError.


#############################
# Testing maths operations. #
#############################

Type - bool:
[snip]
float(z)             = 1.0                  (From <prop_tree.Node object at 0x106f4c30, value=True, type='bool', path='/test-inplace[53]'>)

Type - int:
<node> + 1           = 21                   (From <prop_tree.Node object at 0x106f4c30, value=20, type='int', path='/test-inplace[54]'>)
[snip]
float(z)             = -3.0                 (From <prop_tree.Node object at 0x106f4c30, value=-3, type='int', path='/test-inplace[111]'>)

Type - float:
<node> + 1.0         = 21.0                 (From <prop_tree.Node object at 0x106f4c30, value=20.0, type='double', path='/test-inplace[112]'>)
1.0 + <node>         = 21.0                 (From <prop_tree.Node object at 0x106f4c30, value=20.0, type='double', path='/test-inplace[114]'>)
<node> - 1.0         = 19.0                 (From <prop_tree.Node object at 0x106f4c30, value=20.0, type='double', path='/test-inplace[116]'>)
1.0 - <node>         = -19.0                (From <prop_tree.Node object at 0x106f4c30, value=20.0, type='double', path='/test-inplace[118]'>)
<node> * 2.0         = 40.0                 (From <prop_tree.Node object at 0x106f4c30, value=20.0, type='double', path='/test-inplace[120]'>)
2.0 * <node>         = 40.0                 (From <prop_tree.Node object at 0x106f4c30, value=20.0, type='double', path='/test-inplace[122]'>)
<node> / 3.0         = 6.666666666666667    (From <prop_tree.Node object at 0x106f4c30, value=20.0, type='double', path='/test-inplace[124]'>)
3.0 / <node>         = 0.15                 (From <prop_tree.Node object at 0x106f4c30, value=20.0, type='double', path='/test-inplace[126]'>)
<node> ** 2.0        = 25.0                 (From <prop_tree.Node object at 0x106f4c30, value=5.0, type='double', path='/test-inplace[128]'>)
2.0 ** <node>        = 32.0                 (From <prop_tree.Node object at 0x106f4c30, value=5.0, type='double', path='/test-inplace[130]'>)
<node> % 9.0         = 6.0                  (From <prop_tree.Node object at 0x106f4c30, value=24.0, type='double', path='/test-inplace[132]'>)
2.0 % <node>         = 2.0                  (From <prop_tree.Node object at 0x106f4c30, value=5.0, type='double', path='/test-inplace[134]'>)
<node> // 3.0        = 6.0                  (From <prop_tree.Node object at 0x106f4c30, value=20.0, type='double', path='/test-inplace[136]'>)
333.0 // <node>      = 55.0                 (From <prop_tree.Node object at 0x106f4c30, value=6.0, type='double', path='/test-inplace[138]'>)
divmod(2.0, <node>)  = (-2.0, 1.0)          (From <prop_tree.Node object at 0x106f4c30, value=-3.0, type='double', path='/test-inplace[140]'>)
divmod(<node>, 2.0)  = (-1.0, -1.0)         (From <prop_tree.Node object at 0x106f4c30, value=-3.0, type='double', path='/test-inplace[142]'>)
neg(z)               = 3.0                  (From <prop_tree.Node object at 0x106f4c30, value=-3.0, type='double', path='/test-inplace[144]'>)
pos(z)               = -3.0                 (From <prop_tree.Node object at 0x106f4c30, value=-3.0, type='double', path='/test-inplace[145]'>)
abs(z)               = 3.0                  (From <prop_tree.Node object at 0x106f4c30, value=-3.0, type='double', path='/test-inplace[146]'>)
~(z)                 : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=13.0, type='double', path='/test-inplace[147]'>)
<node> << 3.0        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=13.0, type='double', path='/test-inplace[148]'>)
3.0 << <node>        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=13.0, type='double', path='/test-inplace[149]'>)
<node> >> 3.0        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=13.0, type='double', path='/test-inplace[150]'>)
3.0 >> <node>        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=13.0, type='double', path='/test-inplace[151]'>)
<node> & 0.0         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=1.0, type='double', path='/test-inplace[152]'>)
0.0 & <node>         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=1.0, type='double', path='/test-inplace[153]'>)
<node> ^ 0.0         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=1.0, type='double', path='/test-inplace[154]'>)
0.0 ^ <node>         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=1.0, type='double', path='/test-inplace[155]'>)
<node> | 0.0         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=1.0, type='double', path='/test-inplace[156]'>)
0.0 | <node>         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=1.0, type='double', path='/test-inplace[157]'>)
int(z)               = -3                   (From <prop_tree.Node object at 0x106f4c30, value=-3.0, type='double', path='/test-inplace[158]'>)
float(z)             = -3.0                 (From <prop_tree.Node object at 0x106f4c30, value=-3.0, type='double', path='/test-inplace[159]'>)

Type - str:
<node> + '1'         = 201                  (From <prop_tree.Node object at 0x106f4c30, value='20', type='string', path='/test-inplace[160]'>)
'1' + <node>         = 120                  (From <prop_tree.Node object at 0x106f4c30, value='20', type='string', path='/test-inplace[162]'>)
<node> - '1'         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='20', type='string', path='/test-inplace[164]'>)
'1' - <node>         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='20', type='string', path='/test-inplace[165]'>)
<node> * '2'         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='20', type='string', path='/test-inplace[166]'>)
'2' * <node>         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='20', type='string', path='/test-inplace[167]'>)
<node> / '3'         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='20', type='string', path='/test-inplace[168]'>)
'3' / <node>         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='20', type='string', path='/test-inplace[169]'>)
<node> ** '2'        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='5', type='string', path='/test-inplace[170]'>)
'2' ** <node>        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='5', type='string', path='/test-inplace[171]'>)
<node> % '9'         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='24', type='string', path='/test-inplace[172]'>)
'2' % <node>         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='5', type='string', path='/test-inplace[173]'>)
<node> // '3'        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='20', type='string', path='/test-inplace[174]'>)
'3' // <node>        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='6', type='string', path='/test-inplace[175]'>)
divmod('2', <node>)  : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='-3', type='string', path='/test-inplace[176]'>)
divmod(<node>, '2')  : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='-3', type='string', path='/test-inplace[177]'>)
neg(z)               : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='-3', type='string', path='/test-inplace[178]'>)
pos(z)               : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='-3', type='string', path='/test-inplace[179]'>)
abs(z)               : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='-3', type='string', path='/test-inplace[180]'>)
~(z)                 : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='13', type='string', path='/test-inplace[181]'>)
<node> << '3'        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='13', type='string', path='/test-inplace[182]'>)
'3' << <node>        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='13', type='string', path='/test-inplace[183]'>)
<node> >> '3'        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='13', type='string', path='/test-inplace[184]'>)
'3' >> <node>        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='13', type='string', path='/test-inplace[185]'>)
<node> & '0'         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='1', type='string', path='/test-inplace[186]'>)
'0' & <node>         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='1', type='string', path='/test-inplace[187]'>)
<node> ^ '0'         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='1', type='string', path='/test-inplace[188]'>)
'0' ^ <node>         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='1', type='string', path='/test-inplace[189]'>)
<node> | '0'         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='1', type='string', path='/test-inplace[190]'>)
'0' | <node>         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='1', type='string', path='/test-inplace[191]'>)
int(z)               = -3                   (From <prop_tree.Node object at 0x106f4c30, value='-3', type='string', path='/test-inplace[192]'>)
int(z)               : The <class 'ValueError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='-3.0', type='string', path='/test-inplace[193]'>)
float(z)             = -3.0                 (From <prop_tree.Node object at 0x106f4c30, value='-3.0', type='string', path='/test-inplace[194]'>)
int(z)               : The <class 'ValueError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='abcd', type='string', path='/test-inplace[195]'>)
float(z)             : The <class 'ValueError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='abcd', type='string', path='/test-inplace[196]'>)


#####################################
# Testing invalid maths operations. #
#####################################

<prop_tree.Node object at 0x106f4c30, value='X', type='string', path='/invalid-math/type[0]'>
Correct TypeError for <str> + 1
Correct TypeError for 1 + <str>
Correct TypeError for <str> - 1
Correct TypeError for 1 - <str>
Correct TypeError for <str> * 1
Correct TypeError for 1 * <str>
Correct TypeError for <str> / 1
Correct TypeError for 1 / <str>


###############################################################
# Testing the non-existant path '/abc' for an AttributeError. #
###############################################################

AttributeError for props.abc correctly raised.


##########################################
# Testing path '-' and '_' translations. #
##########################################

<prop_tree.Node object at 0x106f3c30, value=-9999.0, type='double', path='/position/altitude-ft'>
<prop_tree.Node object at 0x106f3c30, value='underscore test', type='string', path='/underscore/a-b_c-d_e'>


###################################
# Testing the length of an array. #
###################################

<prop_tree.Node object at 0x106f3c30, value=None, type='none', path='/py-testing'>
The props.py_testing length: 3 (should be 3).
Element 0: <prop_tree.Node object at 0x106f3c30, value=None, type='none', path='/py-testing[0]'>
Element 1: <prop_tree.Node object at 0x106f4c30, value=None, type='none', path='/py-testing[1]'>
Element 2: <prop_tree.Node object at 0x106f4c30, value=None, type='none', path='/py-testing[2]'>
<prop_tree.Node object at 0x106f3c30, value=True, type='bool', path='/systems/electrical/serviceable'>
The props.systems.electrical.serviceable length: 1 (should be 1).
Element 0: <prop_tree.Node object at 0x106f3c30, value=True, type='bool', path='/systems/electrical/serviceable[0]'>


########################################################
# Testing setattr() and hasattr() on the props object. #
########################################################

<prop_tree.Node object at 0x106f3c30, value=True, type='bool', path='/setattr-test'>


###############################
# Equality condition testing. #
###############################

Equality testing of 'True':
        Testing if "props.test_bool_equality == True": Test passed.
        Testing if "props.test_bool_equality != False": Test passed.
Equality testing of '10':
        Testing if "props.test_int_equality == 10": Test passed.
        Testing if "props.test_int_equality != True": Test passed.
        Testing if "props.test_int_equality <= 10": Test passed.
        Testing if "props.test_int_equality <= 21": Test passed.
        Testing if "props.test_int_equality >= 10": Test passed.
        Testing if "props.test_int_equality >= 1": Test passed.
        Testing if "props.test_int_equality < 20000": Test passed.
        Testing if "props.test_int_equality > -2000": Test passed.
Equality testing of '10.0':
        Testing if "props.test_double_equality == 10.0": Test passed.
        Testing if "props.test_double_equality != True": Test passed.
        Testing if "props.test_double_equality <= 10.0": Test passed.
        Testing if "props.test_double_equality <= 21.0": Test passed.
        Testing if "props.test_double_equality >= 10.0": Test passed.
        Testing if "props.test_double_equality >= 1.0": Test passed.
        Testing if "props.test_double_equality < 20000.0": Test passed.
        Testing if "props.test_double_equality > -2000.0": Test passed.
Equality testing of 'hello':
        Testing if "props.test_string_equality == hello": Test passed.
        Testing if "props.test_string_equality != Hello": Test passed.
Segfault checking:
Equality testing of 'hello':
        Testing if "props.test_string_equality_segfault == hello": Test passed.
        Testing if "props.test_string_equality_segfault != Hello": Test passed.
        Testing if "props.test_string_equality_segfault <= 10.0": Test failed - TypeError.
        Testing if "props.test_string_equality_segfault <= 21.0": Test failed - TypeError.
        Testing if "props.test_string_equality_segfault >= 10.0": Test failed - TypeError.
        Testing if "props.test_string_equality_segfault >= 1.0": Test failed - TypeError.
        Testing if "props.test_string_equality_segfault < 20000.0": Test failed - TypeError.
        Testing if "props.test_string_equality_segfault > -2000.0": Test failed - TypeError.

NameError caught - no segfault.


######################################
# Testing the string representation. #
######################################

The string representation of props.test_string_repr is 'Hello!': Test passed.
The string representation of props.test_string_repr[1] is '200': Test passed.
The string representation of props.test_string_repr[2] is 'True': Test passed.


####################
# Subnode testing. #
####################

Normal subnode.
        Setting: a = props.test_subnode
        a.c: <prop_tree.Node object at 0x106f4430, value='subnode', type='string', path='/test-subnode/c'>
        Setting: b = props.test_subnode.c; b += '_test'
        Setting: c = a.c
        a: <prop_tree.Node object at 0x106f3c30, value=None, type='none', path='/test-subnode'>
        b: <prop_tree.Node object at 0x106f4c30, value='subnode_test', type='string', path='/test-subnode/c'>
        c: <prop_tree.Node object at 0x10517730, value='subnode_test', type='string', path='/test-subnode/c'>
Array-type subnode.
        Setting: a = props.test_subnode[1]
        a.c: <prop_tree.Node object at 0x106f3c30, value='subnode', type='string', path='/test-subnode[1]/c'>
        Setting: b = props.test_subnode[1].c; b += '_test'
        Setting: c = a.c
        a: <prop_tree.Node object at 0x1049dca0, value=None, type='none', path='/test-subnode[1]'>
        b: <prop_tree.Node object at 0x106f3c30, value='subnode_test', type='string', path='/test-subnode[1]/c'>
        c: <prop_tree.Node object at 0x106f4430, value='subnode_test', type='string', path='/test-subnode[1]/c'>


#####################################
# Testing inplace maths operations. #
#####################################

Type - bool:
<node> + False       = True                 (From <prop_tree.Node object at 0x106f4c30, value=True, type='bool', path='/test-inplace[197]'>)
<node> - False       = True                 (From <prop_tree.Node object at 0x106f4c30, value=True, type='bool', path='/test-inplace[198]'>)
<node> * False       = False                (From <prop_tree.Node object at 0x106f4c30, value=True, type='bool', path='/test-inplace[199]'>)
<node> / False       : The <class 'ZeroDivisionError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=True, type='bool', path='/test-inplace[200]'>)
<node> / True        = False                (From <prop_tree.Node object at 0x106f4c30, value=False, type='bool', path='/test-inplace[201]'>)
<node> ** False      = True                 (From <prop_tree.Node object at 0x106f4c30, value=True, type='bool', path='/test-inplace[202]'>)
<node> % False       : The <class 'ZeroDivisionError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=True, type='bool', path='/test-inplace[203]'>)
<node> % True        = False                (From <prop_tree.Node object at 0x106f4c30, value=False, type='bool', path='/test-inplace[204]'>)
<node> // False      : The <class 'ZeroDivisionError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=True, type='bool', path='/test-inplace[205]'>)
<node> // True       = False                (From <prop_tree.Node object at 0x106f4c30, value=False, type='bool', path='/test-inplace[206]'>)
<node> << False      = True                 (From <prop_tree.Node object at 0x106f4c30, value=True, type='bool', path='/test-inplace[207]'>)
<node> >> False      = True                 (From <prop_tree.Node object at 0x106f4c30, value=True, type='bool', path='/test-inplace[208]'>)
<node> & False       = False                (From <prop_tree.Node object at 0x106f4c30, value=True, type='bool', path='/test-inplace[209]'>)
<node> ^ False       = True                 (From <prop_tree.Node object at 0x106f4c30, value=True, type='bool', path='/test-inplace[210]'>)
<node> | False       = True                 (From <prop_tree.Node object at 0x106f4c30, value=True, type='bool', path='/test-inplace[211]'>)

Type - int:
<node> + 1           = 21                   (From <prop_tree.Node object at 0x106f4c30, value=20, type='int', path='/test-inplace[212]'>)
<node> - 1           = 19                   (From <prop_tree.Node object at 0x106f4c30, value=20, type='int', path='/test-inplace[213]'>)
<node> * 2           = 40                   (From <prop_tree.Node object at 0x106f4c30, value=20, type='int', path='/test-inplace[214]'>)
<node> / 3           = 6                    (From <prop_tree.Node object at 0x106f4c30, value=20, type='int', path='/test-inplace[215]'>)
<node> ** 2          = 25                   (From <prop_tree.Node object at 0x106f4c30, value=5, type='int', path='/test-inplace[216]'>)
<node> % 9           = 6                    (From <prop_tree.Node object at 0x106f4c30, value=24, type='int', path='/test-inplace[217]'>)
<node> // 3          = 6                    (From <prop_tree.Node object at 0x106f4c30, value=20, type='int', path='/test-inplace[218]'>)
<node> << 3          = 104                  (From <prop_tree.Node object at 0x106f4c30, value=13, type='int', path='/test-inplace[219]'>)
<node> >> 3          = 1                    (From <prop_tree.Node object at 0x106f4c30, value=13, type='int', path='/test-inplace[220]'>)
<node> & 0           = 0                    (From <prop_tree.Node object at 0x106f4c30, value=1, type='int', path='/test-inplace[221]'>)
<node> ^ 0           = 1                    (From <prop_tree.Node object at 0x106f4c30, value=1, type='int', path='/test-inplace[222]'>)
<node> | 0           = 1                    (From <prop_tree.Node object at 0x106f4c30, value=1, type='int', path='/test-inplace[223]'>)

Type - float:
<node> + 1.0         = 21.0                 (From <prop_tree.Node object at 0x106f4c30, value=20.0, type='double', path='/test-inplace[224]'>)
<node> - 1.0         = 19.0                 (From <prop_tree.Node object at 0x106f4c30, value=20.0, type='double', path='/test-inplace[225]'>)
<node> * 2.0         = 40.0                 (From <prop_tree.Node object at 0x106f4c30, value=20.0, type='double', path='/test-inplace[226]'>)
<node> / 3.0         = 6.666666666666667    (From <prop_tree.Node object at 0x106f4c30, value=20.0, type='double', path='/test-inplace[227]'>)
<node> ** 2.0        = 25.0                 (From <prop_tree.Node object at 0x106f4c30, value=5.0, type='double', path='/test-inplace[228]'>)
<node> % 9.0         = 6.0                  (From <prop_tree.Node object at 0x106f4c30, value=24.0, type='double', path='/test-inplace[229]'>)
<node> // 3.0        = 6.0                  (From <prop_tree.Node object at 0x106f4c30, value=20.0, type='double', path='/test-inplace[230]'>)
<node> << 3.0        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=13.0, type='double', path='/test-inplace[231]'>)
<node> >> 3.0        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=13.0, type='double', path='/test-inplace[232]'>)
<node> & 0.0         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=1.0, type='double', path='/test-inplace[233]'>)
<node> ^ 0.0         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=1.0, type='double', path='/test-inplace[234]'>)
<node> | 0.0         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value=1.0, type='double', path='/test-inplace[235]'>)

Type - str:
<node> + '1'         = 201                  (From <prop_tree.Node object at 0x106f4c30, value='20', type='string', path='/test-inplace[236]'>)
<node> - '1'         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='20', type='string', path='/test-inplace[237]'>)
<node> * '2'         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='20', type='string', path='/test-inplace[238]'>)
<node> / '3'         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='20', type='string', path='/test-inplace[239]'>)
<node> ** '2'        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='5', type='string', path='/test-inplace[240]'>)
<node> % '9'         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='24', type='string', path='/test-inplace[241]'>)
<node> // '3'        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='20', type='string', path='/test-inplace[242]'>)
<node> << '3'        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='13', type='string', path='/test-inplace[243]'>)
<node> >> '3'        : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='13', type='string', path='/test-inplace[244]'>)
<node> & '0'         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='1', type='string', path='/test-inplace[245]'>)
<node> ^ '0'         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='1', type='string', path='/test-inplace[246]'>)
<node> | '0'         : The <class 'TypeError'> error was raised as expected. (From node: <prop_tree.Node object at 0x106f4c30, value='1', type='string', path='/test-inplace[247]'>)


###################
# Boolean checks. #
###################

Truth check for the bool type object <prop_tree.Node object at 0x10517730, value=True, type='bool', path='/test-bool-check[0]'>: Test passed.
Falsity check for the bool type object <prop_tree.Node object at 0x10517730, value=False, type='bool', path='/test-bool-check[0]'>: Test passed.
Truth check for the int type object <prop_tree.Node object at 0x10517730, value=1, type='int', path='/test-bool-check[1]'>: Test passed.
Falsity check for the int type object <prop_tree.Node object at 0x10517730, value=0, type='int', path='/test-bool-check[1]'>: Test passed.
Truth check for the double type object <prop_tree.Node object at 0x10517730, value=1.0, type='double', path='/test-bool-check[2]'>: Test passed.
Falsity check for the double type object <prop_tree.Node object at 0x10517730, value=0.0, type='double', path='/test-bool-check[2]'>: Test passed.
Truth check for the string type object <prop_tree.Node object at 0x10517730, value='Hello', type='string', path='/test-bool-check[3]'>: Test passed.
Falsity check for the string type object <prop_tree.Node object at 0x10517730, value='', type='string', path='/test-bool-check[3]'>: Test passed.


#####################
# Testing indexing. #
#####################

The values match, '/test-indexing' == '/test-indexing'.
The values match, '/test-indexing[10]' == '/test-indexing[10]'.
The values match, <prop_tree.Node object at 0x106f4c30, value=10, type='int', path='/test-indexing[2]'> == 10.
The values match, <prop_tree.Node object at 0x106f4c30, value=4, type='int', path='/test-indexing[10]'> == 4.
The values match, <prop_tree.Node object at 0x106f4c30, value='Hello!', type='string', path='/test-indexing[2]/a[2]'> == 'Hello!'.
The values match, <prop_tree.Node object at 0x106f4c30, value='Hello', type='string', path='/test-indexing[10]/a[2]'> == 'Hello'.
The values match, <prop_tree.Node object at 0x10517730, value='X', type='string', path='/test-indexing2'> == 'X'.
The values match, <prop_tree.Node object at 0x1071ab90, value='Y', type='string', path='/test-indexing2[1]'> == 'Y'.
The values match, <prop_tree.Node object at 0x106f4c30, value='Z', type='string', path='/test-indexing2[2]'> == 'Z'.


################################
# Testing prop_tree functions. #
################################


Checking <prop_tree.Node object at 0x10512a30, value='Random string', type='string', path='/test-node-functions[2]'>.
extract_alias(obj) = None
extract_path(obj)  = '/test-node-functions[2]'
extract_type(obj)  = 'string'
extract_value(obj) = 'Random string'
is_alias(obj)      = False
is_node(obj)       = True

Checking <prop_tree.Node object at 0x10512a30, alias='/test-node-functions[2]', value='Random string', type='string', path='/test-node-functions[3]'>.
extract_alias(obj) = '/test-node-functions[2]'
extract_path(obj)  = '/test-node-functions[3]'
extract_type(obj)  = 'string'
extract_value(obj) = 'Random string'
is_alias(obj)      = True
is_node(obj)       = True

Checking 2.
is_node(obj)       = False

Checking 'Hello'.
is_node(obj)       = False

Checking <class 'object'>.
is_node(obj)       = False

Checking <prop_tree.Props object at 0x7f539d7a9070>.
is_node(obj)       = False

View the function docs:

extract_path.__doc__ =
"""
Return the string representation of the property tree path.

@return:   The property tree path.
@rtype:    str
"""

extract_type.__doc__ =
"""
Return the property tree type corresponding to the value.

@return:   The type of value that the property tree path refers to, as defined in the property tree rather than a Python type.
@rtype:    str
"""

extract_value.__doc__ =
"""
Return the property tree value.

@return:   The property tree value.
@rtype:    object
"""

is_node.__doc__ =
"""
Return the property tree value.

@return:   The property tree value.
@rtype:    object
"""


################################
# Testing Node hidden objects. #
################################

props.test_node_hidden.__doc__: 'A Python property tree node.'
props.test_node_hidden._path: '/'
extract_path(props.test_node_hidden): '/test-node-hidden'


###########################################################
# Testing a new copy of Props and Node (segfault checks). #
###########################################################

A new Node: <prop_tree.Node object at 0x1049dca0, value=True, type='bool', path='/test-new-node'>
A new Props object: <prop_tree.Props object at 0x7f539d7a9080>
The new Props object test_new_node Node: <prop_tree.Node object at 0x10512a30, value=True, type='bool', path='/test-new-node'>


#######################
# Test Node aliasing. #
#######################


Source value:        'Hop'
Pre-target value:    'test'
Source node:         <prop_tree.Node object at 0x10512a30, value='Hop', type='string', path='/test-alias[0]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value='test', type='string', path='/test-alias-new[0]/iter[0]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[0]', value='Hop', type='string', path='/test-alias-new[0]/iter[0]'>

Source value:        ''
Pre-target value:    'test'
Source node:         <prop_tree.Node object at 0x1049f4b0, value='', type='string', path='/test-alias[1]'>
Pre-target node:     <prop_tree.Node object at 0x10512a30, value='test', type='string', path='/test-alias-new[0]/iter[1]'>
Updated target node: <prop_tree.Node object at 0x10512a30, alias='/test-alias[1]', value='', type='string', path='/test-alias-new[0]/iter[1]'>

Source value:        True
Pre-target value:    'test'
Source node:         <prop_tree.Node object at 0x1049f4b0, value=True, type='bool', path='/test-alias[2]'>
Pre-target node:     <prop_tree.Node object at 0x10512a30, value='test', type='string', path='/test-alias-new[0]/iter[2]'>
Updated target node: <prop_tree.Node object at 0x10512a30, alias='/test-alias[2]', value=True, type='bool', path='/test-alias-new[0]/iter[2]'>

Source value:        False
Pre-target value:    'test'
Source node:         <prop_tree.Node object at 0x1049f4b0, value=False, type='bool', path='/test-alias[3]'>
Pre-target node:     <prop_tree.Node object at 0x10512a30, value='test', type='string', path='/test-alias-new[0]/iter[3]'>
Updated target node: <prop_tree.Node object at 0x10512a30, alias='/test-alias[3]', value=False, type='bool', path='/test-alias-new[0]/iter[3]'>

Source value:        1
Pre-target value:    'test'
Source node:         <prop_tree.Node object at 0x1049f4b0, value=1, type='int', path='/test-alias[4]'>
Pre-target node:     <prop_tree.Node object at 0x10512a30, value='test', type='string', path='/test-alias-new[0]/iter[4]'>
Updated target node: <prop_tree.Node object at 0x10512a30, alias='/test-alias[4]', value=1, type='int', path='/test-alias-new[0]/iter[4]'>

Source value:        0
Pre-target value:    'test'
Source node:         <prop_tree.Node object at 0x1049f4b0, value=0, type='int', path='/test-alias[5]'>
Pre-target node:     <prop_tree.Node object at 0x10512a30, value='test', type='string', path='/test-alias-new[0]/iter[5]'>
Updated target node: <prop_tree.Node object at 0x10512a30, alias='/test-alias[5]', value=0, type='int', path='/test-alias-new[0]/iter[5]'>

Source value:        1.0
Pre-target value:    'test'
Source node:         <prop_tree.Node object at 0x1049f4b0, value=1.0, type='double', path='/test-alias[6]'>
Pre-target node:     <prop_tree.Node object at 0x10512a30, value='test', type='string', path='/test-alias-new[0]/iter[6]'>
Updated target node: <prop_tree.Node object at 0x10512a30, alias='/test-alias[6]', value=1.0, type='double', path='/test-alias-new[0]/iter[6]'>

Source value:        0.0
Pre-target value:    'test'
Source node:         <prop_tree.Node object at 0x1049f4b0, value=0.0, type='double', path='/test-alias[7]'>
Pre-target node:     <prop_tree.Node object at 0x10512a30, value='test', type='string', path='/test-alias-new[0]/iter[7]'>
Updated target node: <prop_tree.Node object at 0x10512a30, alias='/test-alias[7]', value=0.0, type='double', path='/test-alias-new[0]/iter[7]'>

Source value:        'Hop'
Pre-target value:    False
Source node:         <prop_tree.Node object at 0x10512a30, value='Hop', type='string', path='/test-alias[0]'>
Pre-target node:     <prop_tree.Node object at 0x10512a30, value=False, type='bool', path='/test-alias-new[1]/iter[0]'>
Updated target node: <prop_tree.Node object at 0x10512a30, alias='/test-alias[0]', value='Hop', type='string', path='/test-alias-new[1]/iter[0]'>

Source value:        ''
Pre-target value:    False
Source node:         <prop_tree.Node object at 0x1049f4b0, value='', type='string', path='/test-alias[1]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=False, type='bool', path='/test-alias-new[1]/iter[1]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[1]', value='', type='string', path='/test-alias-new[1]/iter[1]'>

Source value:        True
Pre-target value:    False
Source node:         <prop_tree.Node object at 0x1049f4b0, value=True, type='bool', path='/test-alias[2]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=False, type='bool', path='/test-alias-new[1]/iter[2]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[2]', value=True, type='bool', path='/test-alias-new[1]/iter[2]'>

Source value:        False
Pre-target value:    False
Source node:         <prop_tree.Node object at 0x1049f4b0, value=False, type='bool', path='/test-alias[3]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=False, type='bool', path='/test-alias-new[1]/iter[3]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[3]', value=False, type='bool', path='/test-alias-new[1]/iter[3]'>

Source value:        1
Pre-target value:    False
Source node:         <prop_tree.Node object at 0x1049f4b0, value=1, type='int', path='/test-alias[4]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=False, type='bool', path='/test-alias-new[1]/iter[4]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[4]', value=1, type='int', path='/test-alias-new[1]/iter[4]'>

Source value:        0
Pre-target value:    False
Source node:         <prop_tree.Node object at 0x1049f4b0, value=0, type='int', path='/test-alias[5]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=False, type='bool', path='/test-alias-new[1]/iter[5]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[5]', value=0, type='int', path='/test-alias-new[1]/iter[5]'>

Source value:        1.0
Pre-target value:    False
Source node:         <prop_tree.Node object at 0x1049f4b0, value=1.0, type='double', path='/test-alias[6]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=False, type='bool', path='/test-alias-new[1]/iter[6]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[6]', value=1.0, type='double', path='/test-alias-new[1]/iter[6]'>

Source value:        0.0
Pre-target value:    False
Source node:         <prop_tree.Node object at 0x1049f4b0, value=0.0, type='double', path='/test-alias[7]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=False, type='bool', path='/test-alias-new[1]/iter[7]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[7]', value=0.0, type='double', path='/test-alias-new[1]/iter[7]'>

Source value:        'Hop'
Pre-target value:    -5
Source node:         <prop_tree.Node object at 0x10512a30, value='Hop', type='string', path='/test-alias[0]'>
Pre-target node:     <prop_tree.Node object at 0x10512a30, value=-5, type='int', path='/test-alias-new[2]/iter[0]'>
Updated target node: <prop_tree.Node object at 0x10512a30, alias='/test-alias[0]', value='Hop', type='string', path='/test-alias-new[2]/iter[0]'>

Source value:        ''
Pre-target value:    -5
Source node:         <prop_tree.Node object at 0x1049f4b0, value='', type='string', path='/test-alias[1]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=-5, type='int', path='/test-alias-new[2]/iter[1]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[1]', value='', type='string', path='/test-alias-new[2]/iter[1]'>

Source value:        True
Pre-target value:    -5
Source node:         <prop_tree.Node object at 0x1049f4b0, value=True, type='bool', path='/test-alias[2]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=-5, type='int', path='/test-alias-new[2]/iter[2]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[2]', value=True, type='bool', path='/test-alias-new[2]/iter[2]'>

Source value:        False
Pre-target value:    -5
Source node:         <prop_tree.Node object at 0x1049f4b0, value=False, type='bool', path='/test-alias[3]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=-5, type='int', path='/test-alias-new[2]/iter[3]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[3]', value=False, type='bool', path='/test-alias-new[2]/iter[3]'>

Source value:        1
Pre-target value:    -5
Source node:         <prop_tree.Node object at 0x1049f4b0, value=1, type='int', path='/test-alias[4]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=-5, type='int', path='/test-alias-new[2]/iter[4]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[4]', value=1, type='int', path='/test-alias-new[2]/iter[4]'>

Source value:        0
Pre-target value:    -5
Source node:         <prop_tree.Node object at 0x1049f4b0, value=0, type='int', path='/test-alias[5]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=-5, type='int', path='/test-alias-new[2]/iter[5]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[5]', value=0, type='int', path='/test-alias-new[2]/iter[5]'>

Source value:        1.0
Pre-target value:    -5
Source node:         <prop_tree.Node object at 0x1049f4b0, value=1.0, type='double', path='/test-alias[6]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=-5, type='int', path='/test-alias-new[2]/iter[6]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[6]', value=1.0, type='double', path='/test-alias-new[2]/iter[6]'>

Source value:        0.0
Pre-target value:    -5
Source node:         <prop_tree.Node object at 0x1049f4b0, value=0.0, type='double', path='/test-alias[7]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=-5, type='int', path='/test-alias-new[2]/iter[7]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[7]', value=0.0, type='double', path='/test-alias-new[2]/iter[7]'>

Source value:        'Hop'
Pre-target value:    20.0
Source node:         <prop_tree.Node object at 0x10512a30, value='Hop', type='string', path='/test-alias[0]'>
Pre-target node:     <prop_tree.Node object at 0x10512a30, value=20.0, type='double', path='/test-alias-new[3]/iter[0]'>
Updated target node: <prop_tree.Node object at 0x10512a30, alias='/test-alias[0]', value='Hop', type='string', path='/test-alias-new[3]/iter[0]'>

Source value:        ''
Pre-target value:    20.0
Source node:         <prop_tree.Node object at 0x1049f4b0, value='', type='string', path='/test-alias[1]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=20.0, type='double', path='/test-alias-new[3]/iter[1]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[1]', value='', type='string', path='/test-alias-new[3]/iter[1]'>

Source value:        True
Pre-target value:    20.0
Source node:         <prop_tree.Node object at 0x1049f4b0, value=True, type='bool', path='/test-alias[2]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=20.0, type='double', path='/test-alias-new[3]/iter[2]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[2]', value=True, type='bool', path='/test-alias-new[3]/iter[2]'>

Source value:        False
Pre-target value:    20.0
Source node:         <prop_tree.Node object at 0x1049f4b0, value=False, type='bool', path='/test-alias[3]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=20.0, type='double', path='/test-alias-new[3]/iter[3]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[3]', value=False, type='bool', path='/test-alias-new[3]/iter[3]'>

Source value:        1
Pre-target value:    20.0
Source node:         <prop_tree.Node object at 0x1049f4b0, value=1, type='int', path='/test-alias[4]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=20.0, type='double', path='/test-alias-new[3]/iter[4]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[4]', value=1, type='int', path='/test-alias-new[3]/iter[4]'>

Source value:        0
Pre-target value:    20.0
Source node:         <prop_tree.Node object at 0x1049f4b0, value=0, type='int', path='/test-alias[5]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=20.0, type='double', path='/test-alias-new[3]/iter[5]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[5]', value=0, type='int', path='/test-alias-new[3]/iter[5]'>

Source value:        1.0
Pre-target value:    20.0
Source node:         <prop_tree.Node object at 0x1049f4b0, value=1.0, type='double', path='/test-alias[6]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=20.0, type='double', path='/test-alias-new[3]/iter[6]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[6]', value=1.0, type='double', path='/test-alias-new[3]/iter[6]'>

Source value:        0.0
Pre-target value:    20.0
Source node:         <prop_tree.Node object at 0x1049f4b0, value=0.0, type='double', path='/test-alias[7]'>
Pre-target node:     <prop_tree.Node object at 0x1049f4b0, value=20.0, type='double', path='/test-alias-new[3]/iter[7]'>
Updated target node: <prop_tree.Node object at 0x1049f4b0, alias='/test-alias[7]', value=0.0, type='double', path='/test-alias-new[3]/iter[7]'>


********************************************************************************

Here is an AttributeError, for luck:
Traceback (most recent call last):
  File "/flightgear/hangar/py-ogel/test.py", line 1019, in <module>
    print(props.does_not_exist)
AttributeError: The property tree node '/does-not-exist' does not exist.

********************************************************************************
This is the py-ogel, XML inline python script.
a: 11
********************************************************************************



Regards,
Edward
bugman
Moderator
 
Posts: 1808
Joined: Thu Mar 19, 2015 10:01 am
Version: next

Re: FGPython an propose for Python as an nasal alternative

Postby www2 » Sat Jan 23, 2016 1:37 am

bugman
can you add a check for python 2 and python 3.
my compiler wand add python 2 in state of python 3

edit:
I fix this by adding the foiling line
from:
Code: Select all
# check required dependencies
find_package(Boost   REQUIRED)

to:
Code: Select all
# check required dependencies
set(Python_ADDITIONAL_VERSIONS 3.4)
www2
 
Posts: 319
Joined: Thu Apr 16, 2009 2:58 pm
OS: Ubuntu

Re: FGPython an propose for Python as an nasal alternative

Postby www2 » Sat Jan 23, 2016 3:39 am

This is a historic moment for python in FG:
Code: Select all
Enabling ATI viewport hack
Starting automatic scenery download/synchronization. Using built-in SVN support. Directory: '/home/user-name/.fgfs/TerraSync'.
No path in sim/sound/path
canvas::Text: Missing 'ttf' font reader
canvas::Text: Failed to open font file /home/user-name/fgfs2/install/flightgear/fgdata/Fonts/LiberationFonts/LiberationSans-Bold.ttf
I wand spam
  Sorry, qdot doesn't appear to be trimmable
  Trim Results:
          Altitude AGL:    4.1  wdot:  3.22e+01 Tolerance: 1e-03  Failed
           Pitch Angle:  -0.12  qdot:  1.01e-09 Tolerance: 1e-04  Passed
            Roll Angle:  -0.31  pdot: -6.67e-08 Tolerance: 1e-04  Passed

  Trim Statistics:
    Total Iterations: 2
    Sub-iterations:
    wdot: 5 average: 2.5  successful:  1  stability: 2
    qdot: 0 average: 0  successful:  0  stability: 2
    pdot: 6 average: 3  successful:  2  stability: 2
    Run Count: 66


After some hacking i print "I wand spam" that is from a lib inside $fg_root/Python
code py-ogle-set.xml:
Code: Select all
...
    <python>
....
      <eggs>
        <script>
            import hello
            hello.spam()
        </script>
      </eggs>
    </python>

scode diff
Code: Select all
diff --git a/src/Python/PythonSys.cxx b/src/Python/PythonSys.cxx
index 4b654af..2f49ec1 100644
--- a/src/Python/PythonSys.cxx
+++ b/src/Python/PythonSys.cxx
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+
 #include <Python.h>
 
 #include <simgear/props/props.hxx>
@@ -81,6 +82,12 @@ void FGPythonSys::init()
         PyRun_SimpleString("builtins.props = prop_tree.Props()");
         PyRun_SimpleString("del prop_tree");
         PyRun_SimpleString("del builtins");
+        //add FGhome/Python
+        PyRun_SimpleString("import sys");
+        char FGpythonPath[512] = "";
+        snprintf(FGpythonPath, 512,"sys.path.append('%s/Python')",globals->get_fg_root().c_str());
+        PyRun_SimpleString(FGpythonPath);
+        PyRun_SimpleString("del sys");
     }
 
     // Load all the scripts out of the property tree.

python lib code:
Code: Select all
def spam():
    print("I wand spam")
www2
 
Posts: 319
Joined: Thu Apr 16, 2009 2:58 pm
OS: Ubuntu

Re: FGPython an propose for Python as an nasal alternative

Postby Hooray » Sat Jan 23, 2016 10:03 am

@bugman:



Wow, that's quite an accomplishment for someone just recently claiming not to be a C++ programmer ;-)

I haven't actually built your branches, but went through your commits/patches and read your comments on the devel list.

So take my comments with a grain of salt (of course...), I am making them specifically with a focus on 1) the way Nasal scripting currently works in FlightGear (and the challenges resulting from that), and 2) your comments to hopefully provide Python support as a viable alternative for "software prototyping" and adopting HLA via scripting.

That being said, here's some feedback:

  • to bring the Python interface up to par with Nasal, timers and listeners would be useful - because those are currently the main building blocks to trigger scripted code in the FlightGear main loop.
  • Over time, this has become a bit problematic in FlightGear - and the core developers have come to the conclusion that RAII is needed for timer/listener management - which is to say that you would want to look at the OOP maketimer() API in FGNasalSys and explore having two similar APIs for wrapping timer/listener functionality in a makelistener() fashion.
  • One of the issues causing Nasal code to be a real challenge compared to its C++ counterpart is that none of the Nasal code is using the existing SGSubsystem interface - so if your comments on "software prototyping" were/are serious, we would probably want to look at exposing SGSubsystem as a base class which can be inherited from in Python space to register new subsystems that are implemented in Python.
  • Like you mentioned on the devel list, many things like the property tree/subsystems would ideally be using some form of IPC (like HLA) to interface with the rest of the simulation - but the logical first step is exposing conventional bindings for the property tree and subsystems.

The way your code is currently structured, it seems that you pretty much used FGNasalSys as a template, using copy & paste. As you probably noticed already, there is quite a bit of overlapping code/functionality, so that it /might/ make sense to introduce a common base class in the form of FGScriptingHost that inherit from SGSubsystem, from which FGPythonSys/FGNasalSys could then inherit their own child-classes, so that shared functionality can reside in the parent class (or customized/overridden accordingly).

Otherwise, I do applaud your effort to make the whole thing compatible with the ongoing reset/re-init work, because that could be cosidered a key part of the whole HLA thing, and most core developers continue to make commits that are basically incompatible with reset/re-init, i.e. complicating matters for Zakalawe tremendously (on the other hand, his reset/re-init and headless work admittedly didn't even make it to the "updated roadmap" either).

Another issue that FGNasalSys is suffering from is that it is designed to be a singleton - and it seems FGPythonSys is sharing this limitation. You may want to reconsider if that is ultimately what you are aiming for - simply because in FG, the whole thing of a scripting session depends largely on the "context" - and here FGNasalSys has become a rather messy part of FlightGear, because we don't even differentiate between different "types" of scripts (think aircraft vs. scenery vs. GUI vs. core etc).

I do think that this could also help address the security issues that Rebecca briefly touched on the devel list, and which I mentioned a few weeks ago on the forum, too ;-)

Had FGNasalSys been designed to support independent instances, we could have multiple Nasal interpreters running independently for different script types, and could also grant them different permissions ($FG_HOME, $FG_AIRCRAFT, $FG_SCENERY)

That is something that would greatly help simplify state management for reset/re-init, including use-cases where people may want to save/resume flights (in fact arbitrary simulator state).

For the time being, this is not possible and causing tons of Nasal related challenges for reset/re-init.

Equally, there is the very long standing issue of the Nasal interpreter not being available earlier, despite it being potentially useful.

As you can see on the wiki ("Initializing Nasal earlier"), James, Melchior and others were once contemplating to change that so that FlightGear scripting would become available sooner.

The main thing that prevents this currently for the Nasal VM is the fact that it has a plethora of SGSubsystem-specific "bindings" (basically subsystem-specific APIs) that are dumped into the globals context, and subsequently assumed to be available "automatically".

This is a fragile/broken assumption in an increasingly multi-threaded environment, especially because of the ongoing reset/re-init work, where subsystems may be shut down/restarted "on demand", i.e. their availability may change dynamically.

Like I mentioned previously in this thread, we can easily work around this issue by using the ::bind() and ::unbind() methods to register/remove certain API symbols that depend on other subsystems.

With Nasal it is kinda "too late" unfortunately, because of the plethora of APIs and subsystems currently exposed - for FGPythonSys, this may be much more straightforward, because it's still in its infancy.

In layman's terms that basically means that an API like maketimer() will depends on the events subsystem (in the case of Nasal), whereas maketimer() would depend on the property tree being around, and navDB related APIs would obviously depend on the navDB.

By the way, when it comes to main loop timers, you may want to consider having your own "python-events" subsystem to ensure that Nasal and Python related timers show up in different subsystems (i.e. in the performance monitor stats).

You may also want to provide a startup option to entirely disable Python to help ensure that people can troubleshoot issues and determine if they are affected by Python being active or not - i.e. Python being built-in but adding the subsystem is determined conditionally using a fgGetBool() and or logic in $FG_SRC/Main/options.cxx

I am just suggesting that because we have seen countless of posting where Nasal (and its GC) were said to be the culprit, and only when we walked people through removing all Nasal code from the aircraft (think Su15), they finally realized that Nasal was not the issue at all.

That is also one of the reasons why I believe that the whole "Singleton" assumption will become problematic over time, because if something like FGPythonSys is not a singleton, we can also much more easily stress-test it - while also being able to deal with scripts differently, i.e. depending on their "context" (scope) being aircraft/scenery or GUI related.

Equally, a FGPythonSys interpreter that is up and running very early could become really helpeful for debugging/troubleshooting purposes, but also help us implement benchmarking support.

Overall, my suggestion would really be to closely look at where Nasal "failed", and for which reasons - and try to learn from these lessons.

Incremental re-initialization and saving/loading state really is a crucial part of the simulation, and it would go a long way to come up with a new scripting option that does not suffer from certain restrictions, especially in an increasingly multi-threaded and distributed environment like HLA.

Finally, you did an outstanding job there with all the unit testing you implemented, which is another thing where Nasal failed.

Again, your mileage may vary - so if in doubt, raise any of this with some of the folks on the devel list who are willing to help review/commit your patches - my priorities are obviously biased due to my own experiences with FGNasalSys :D
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

PreviousNext

Return to New features

Who is online

Users browsing this forum: No registered users and 4 guests