From: Barry S. <ba...@ba...> - 2014-11-09 12:33:47
|
On 22 Oct 2014, at 23:22, π <sun...@gm...> wrote: > I've been playing around with PyCXX, slowly figuring it out. > > I've got all of the demos but one working: simple, example, range, iter, but not simple2. simple2 was written to test and fix bugs raise around 2008. If you are not doing so I would recommend that you checkout the svn repo for pycxx and look at log messages related to code you are investigating. These seems to be the interesting svn logs: r160 | barry-scott | 2008-12-01 19:56:37 +0000 (Mon, 01 Dec 2008) | 2 lines can call methods on new style classes but dir(new_style_class) does not return a list of methods ------------------------------------------------------------------------ r159 | barry-scott | 2008-12-01 17:40:54 +0000 (Mon, 01 Dec 2008) | 2 lines Add debug functions under the PYCXX_DEBUG define ------------------------------------------------------------------------ r157 | barry-scott | 2008-11-28 12:12:30 +0000 (Fri, 28 Nov 2008) | 2 lines Fix for Sourceforge bug 2338117 ------------------------------------------------------------------------ r141 | barry-scott | 2008-10-07 21:46:41 +0100 (Tue, 07 Oct 2008) | 2 lines Add test case for problem with using PythonExtension type as dictionary key > > The following code shows the problem, I think: > > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > > import simple2 > import sys > > print( "---" ) > print( id(simple2.xxx.second) ) # 4299353944 > print( id(simple2.xxx.second) ) # 4299353944 > print( id(simple2.xxx.second) ) # 4299353944 > print( "---" ) > > a,b,c = simple2.xxx.second, simple2.xxx.second, simple2.xxx.second > print( id(a) ) # 4299353944 > print( id(b) ) # 4299513704 > print( id(c) ) # 4299214952 > print( "---" ) > print( id(simple2.xxx.second) ) # 4299435416 > print( id(simple2.xxx.second) ) # 4299435416 > print( id(simple2.xxx.second) ) # 4299435416 > print( "---" ) > > p = simple2.xxx.second > q = simple2.xxx.second > > print ( id(p) ) # 4299435416 > print ( id(q) ) # 4299521272 > > print( id(simple2.xxx.second) ) # 4299516904 > print( id(simple2.xxx.second) ) # 4299516904 > print( "---" ) > > print ( "Making dict m..." ) > m = { > simple2.xxx.first : 1, > simple2.xxx.second : 2, > simple2.xxx.third : 3 > } > > print( "---" ) > print( id(simple2.xxx.second) ) # 4299381256 > print( id(simple2.xxx.second) ) # 4299381256 > print( "---" ) > > print ( "... done!" ) > > v = m[ simple2.xxx.second ] > > #Traceback (most recent call last): > # File "./py/test_simple2.py", line 53, in <module> > # v = m[ simple2.xxx.second ] > # > #did EnumString< xxx_t >::EnumString() > #KeyError: <xxx.second> > #-1 > #Program ended with exit code: 0 > > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > > It looks as though every time we assign simple2.xxx.second to some variable, it creates a new instance rather than giving just a new reference. > > And that in defining m = {…}, a new instance is also created. > > If instead we were to do: > > - - - - - - - - - - - - - > u = simple2.xxx.second > m = { > simple2.xxx.first : 1, > u : 2, > simple2.xxx.third : 3 > } > v = m[ u ] > - - - - - - - - - - - - - > > … this works. The point of the test is the use an object not an python interned int. > > The same problem doesn't seem to occur with the root object: > - - - - - - - - - - - - - > print( id(simple2.xxx) ) > print( id(simple2.xxx) ) > > x1 = simple2.xxx > x2 = simple2.xxx > print( id(x1) ) > print( id(x2) ) > - - - - - - - - - - - - - > The above produces the same id all 4 times. equality does not require id() to be the same. > > I think the problem is here: > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > // if the given string has an associated T object, return that T object > Py::Object getattr( const char *_name ) override > { > std::string name( _name ); > T value; > > if( name == "__methods__" ) > return Py::List(); > > if( name == "__members__" ) > return memberList( static_cast<T>( 0 ) ); > > if( toEnum( name, value ) ) // <-- sets value > return Py::asObject( new pysvn_enum_value<T>( value ) ); // <— HERE And no where is there an instance of pysvn_enum_value that can be returned. So a new instance must be made. > > return this->getattr_methods( _name ); > } > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > > Where it can be seen clearly that getattr returns a newly created Python object. > > In fact I think that the only time we ever get the same id returned consecutively is due to coincidence: the first object gets destructed, and another object gets created. If this happen straight away, maybe the Python runtime reuses the ID of the object that just got destroyed. No idea on the id() reuse. You would have to read the python sources. > > Although I still don't fully grok this simple2 example (I don't find it very simple at all), my guess is that to fix this it would be necessary to create a pysvn_enum_value<xxx_t> py-extension-object for each xxx_t enum value, and bind them together using ‘map’ upon creation of simple.xxx (i.e. in it’s init_type). Then whenever e.g. xxx.simple2.second is encountered, get_attr can return a non-owned reference to the corresponding pysvn_enum_value<xxx_t>. Its based on a real world problem that I hit in pysvn. The point of the test is to prevent a regression of the fix made 6 years ago. > > Another thing I notice is that several functions contain: > > static EnumString<T> enum_map; > > This doesn't look like a good idea, surely there should only be one map created. Maybe it should be a static public variable inside EnumString? Correct, however, the code in pysvn is not this shape. You might find the pysvn code interesting to read as it is a major user of pycxx. Barry > > π > ------------------------------------------------------------------------------ > _______________________________________________ > CXX-Users mailing list > CXX...@li... > https://lists.sourceforge.net/lists/listinfo/cxx-users |