<commit_before>// @(#)root/pyroot:$Name:  $:$Id: Pythonize.cxx,v 1.21 2005/06/17 19:14:53 brun Exp $
// Author: <NAME>, Jul 2004

// Bindings
#include "PyROOT.h"
#include "Pythonize.h"
#include "ObjectProxy.h"
#include "MethodProxy.h"
#include "RootWrapper.h"
#include "Utility.h"
#include "PyCallable.h"
#include "PyBufferFactory.h"
#include "FunctionHolder.h"

// ROOT
#include "TClass.h"
#include "TCollection.h"
#include "TDirectory.h"
#include "TSeqCollection.h"
#include "TObject.h"
#include "TFunction.h"
#include "TTree.h"

// CINT
#include "Api.h"

// Standard
#include <stdexcept>
#include <string>
#include <stdio.h>
#include <utility>


namespace {

// for convenience
   using namespace PyROOT;

//____________________________________________________________________________
   inline bool IsTemplatedSTLClass( const std::string& name, const std::string& klass ) {
      const int nsize = (int)name.size();
      const int ksize = (int)klass.size();

      return ( ( ksize   < nsize && name.substr(0,ksize) == klass ) ||
               ( ksize+5 < nsize && name.substr(5,ksize) == klass ) ) &&
             name.find( "::", name.find( ">" ) ) == std::string::npos;
   }

// to prevent compiler warnings about const char* -> char*
   inline PyObject* CallPyObjMethod( PyObject* obj, const char* meth )
   {
      return PyObject_CallMethod( obj, const_cast< char* >( meth ), const_cast< char* >( "" ) );
   }

//____________________________________________________________________________
   inline PyObject* CallPyObjMethod( PyObject* obj, const char* meth, PyObject* arg1 )
   {
      return PyObject_CallMethod(
         obj, const_cast< char* >( meth ), const_cast< char* >( "O" ), arg1 );
   }

//____________________________________________________________________________
   inline PyObject* CallPyObjMethod(
      PyObject* obj, const char* meth, PyObject* arg1, PyObject* arg2 )
   {
      return PyObject_CallMethod(
         obj, const_cast< char* >( meth ), const_cast< char* >( "OO" ), arg1, arg2 );
   }

//____________________________________________________________________________
   inline PyObject* CallPyObjMethod( PyObject* obj, const char* meth, PyObject* arg1, int arg2 )
   {
      return PyObject_CallMethod(
         obj, const_cast< char* >( meth ), const_cast< char* >( "Oi" ), arg1, arg2 );
   }


//- helpers --------------------------------------------------------------------
   PyObject* CallPySelfMethod( PyObject* args, const char* meth, const char* fmt )
   {
      PyObject* self = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( fmt ), &self ) )
         return 0;

      return CallPyObjMethod( self, meth );
   }

//____________________________________________________________________________
   PyObject* CallPySelfObjMethod( PyObject* args, const char* meth, const char* fmt )
   {
      PyObject* self = 0, *obj = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( fmt ), &self, &obj ) )
         return 0;

      return CallPyObjMethod( self, meth, obj );
   }

//____________________________________________________________________________
   PyObject* PyStyleIndex( PyObject* self, PyObject* index )
   {
      long idx = PyInt_AsLong( index );
      if ( PyErr_Occurred() )
         return 0;

      PyObject* pyindex = 0;
      long size = PySequence_Size( self );
      if ( idx >= size || ( idx < 0 && idx <= -size ) ) {
         PyErr_SetString( PyExc_IndexError, "index out of range" );
         return 0;
      }

      if ( idx >= 0 ) {
         Py_INCREF( index );
         pyindex = index;
      } else
         pyindex = PyLong_FromLong( size + idx );

      return pyindex;
   }

//____________________________________________________________________________
   PyObject* callSelfIndex( PyObject* args, const char* meth )
   {
      PyObject* self = PyTuple_GET_ITEM( args, 0 );
      PyObject* obj  = PyTuple_GET_ITEM( args, 1 );

      PyObject* pyindex = PyStyleIndex( self, obj );
      if ( ! pyindex )
         return 0;

      PyObject* result = CallPyObjMethod( self, meth, pyindex );
      Py_DECREF( pyindex );
      return result;
   }


//- TObject behaviour ----------------------------------------------------------
   PyObject* TObjectContains( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *obj = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__contains__" ), &self, &obj ) )
         return 0;

      if ( ! ( ObjectProxy_Check( obj ) || PyString_Check( obj ) ) )
         return PyInt_FromLong( 0l );

      PyObject* found = CallPyObjMethod( self, "FindObject", obj );
      PyObject* result = PyInt_FromLong( PyObject_IsTrue( found ) );
      Py_DECREF( found );
      return result;
   }

//____________________________________________________________________________
   PyObject* TObjectCompare( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *obj = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__cmp__" ), &self, &obj ) )
         return 0;

      if ( ! ObjectProxy_Check( obj ) )
         return PyInt_FromLong( -1l );

      return CallPyObjMethod( self, "Compare", obj );
   }

//____________________________________________________________________________
   PyObject* TObjectIsEqual( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *obj = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__eq__" ), &self, &obj ) )
         return 0;

      if ( ! ObjectProxy_Check( obj ) )
         return PyInt_FromLong( 0l );

      return CallPyObjMethod( self, "IsEqual", obj );
   }


//- TCollection behaviour ------------------------------------------------------
   PyObject* TCollectionExtend( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *obj = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:extend" ), &self, &obj ) )
         return 0;

      for ( int i = 0; i < PySequence_Size( obj ); ++i ) {
         PyObject* item = PySequence_GetItem( obj, i );
         PyObject* result = CallPyObjMethod( self, "Add", item );
         Py_XDECREF( result );
         Py_DECREF( item );
      }

      Py_INCREF( Py_None );
      return Py_None;
   }

//____________________________________________________________________________
   PyObject* TCollectionRemove( PyObject*, PyObject* args )
   {
      PyObject* result = CallPySelfObjMethod( args, "Remove", "OO:remove" );
      if ( ! result )
         return 0;

      if ( ! PyObject_IsTrue( result ) ) {
         Py_DECREF( result );
         PyErr_SetString( PyExc_ValueError, "list.remove(x): x not in list" );
         return 0;
      }

      Py_DECREF( result );
      Py_INCREF( Py_None );
      return Py_None;
   }

//____________________________________________________________________________
   PyObject* TCollectionAdd( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *other = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__add__" ), &self, &other ) )
         return 0;

      PyObject* l = CallPyObjMethod( self, "Clone" );
      if ( ! l )
         return 0;

      PyObject* result = CallPyObjMethod( l, "extend", other );
      if ( ! result ) {
         Py_DECREF( l );
         return 0;
      }

      return l;
   }

//____________________________________________________________________________
   PyObject* TCollectionMul( PyObject*, PyObject* args )
   {
      ObjectProxy* self = 0; long imul = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "Ol:__mul__" ), &self, &imul ) )
         return 0;

      if ( ! ObjectProxy_Check( self ) || ! self->GetObject() ) {
         PyErr_SetString( PyExc_TypeError, "unsubscriptable object" );
         return 0;
      }

      PyObject* nseq = BindRootObject( self->ObjectIsA()->New(), self->ObjectIsA() );

      for ( long i = 0; i < imul; ++i ) {
         PyObject* result = CallPyObjMethod( nseq, "extend", (PyObject*)self );
         Py_DECREF( result );
      }

      return nseq;
   }

//____________________________________________________________________________
   PyObject* TCollectionIMul( PyObject*, PyObject* args )
   {
      PyObject* self = 0; long imul = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "Ol:__imul__" ), &self, &imul ) )
         return 0;

      PyObject* l = PySequence_List( self );

      for ( long i = 0; i < imul - 1; ++i ) {
         CallPyObjMethod( self, "extend", l );
      }

      Py_INCREF( self );
      return self;
   }

//____________________________________________________________________________
   PyObject* TCollectionCount( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *obj = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:count" ), &self, &obj ) )
         return 0;

      int count = 0;
      for ( int i = 0; i < PySequence_Size( self ); ++i ) {
         PyObject* item = PySequence_GetItem( self, i );
         PyObject* found = PyObject_RichCompare( item, obj, Py_EQ );

         Py_DECREF( item );

         if ( ! found )
            return 0;                        // internal problem

         if ( PyObject_IsTrue( found ) )
            count += 1;
         Py_DECREF( found );
      }

      return PyLong_FromLong( count );
   }


//____________________________________________________________________________
   PyObject* TCollectionIter( PyObject*, PyObject* args ) {
      ObjectProxy* self = (ObjectProxy*) PyTuple_GET_ITEM( args, 0 );
      if ( ! ObjectProxy_Check( self ) || ! self->GetObject() ) {
         PyErr_SetString( PyExc_TypeError, "iteration over non-sequence" );
         return 0;
      }

      TCollection* col =
         (TCollection*)self->ObjectIsA()->DynamicCast( TCollection::Class(), self->GetObject() );

      PyObject* pyobject = BindRootObject( (void*) new TIter( col ), TIter::Class() );
      ((ObjectProxy*)pyobject)->fFlags |= ObjectProxy::kIsOwner;
      return pyobject;
   }


//- TSeqCollection behaviour ---------------------------------------------------
   PyObject* TSeqCollectionGetItem( PyObject*, PyObject* args )
   {
      ObjectProxy* self = 0; PySliceObject* index = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__getitem__" ), &self, &index ) )
         return 0;
      
      if ( PySlice_Check( index ) ) {
         if ( ! ObjectProxy_Check( self ) || ! self->GetObject() ) {
            PyErr_SetString( PyExc_TypeError, "unsubscriptable object" );
            return 0;
         }

         TClass* clSeq = self->ObjectIsA();
         TSeqCollection* oseq =
            (TSeqCollection*)clSeq->DynamicCast( TSeqCollection::Class(), self->GetObject() );
         TSeqCollection* nseq = (TSeqCollection*)clSeq->New();

         int start, stop, step;
         PySlice_GetIndices( index, oseq->GetSize(), &start, &stop, &step );
         for ( int i = start; i < stop; i += step ) {
            nseq->Add( oseq->At( i ) );
         }

         return BindRootObject( (void*) nseq, clSeq );
      }

      return callSelfIndex( args, "At" );
   }

//____________________________________________________________________________
   PyObject* TSeqCollectionSetItem( PyObject*, PyObject* args )
   {
      ObjectProxy* self = 0; PyObject* index = 0, *obj = 0;
      if ( ! PyArg_ParseTuple( args,
                const_cast< char* >( "OOO:__setitem__" ), &self, &index, &obj ) )
         return 0;

      if ( PySlice_Check( index ) ) {
         if ( ! ObjectProxy_Check( self ) || ! self->GetObject() ) {
            PyErr_SetString( PyExc_TypeError, "unsubscriptable object" );
            return 0;
         }

         TSeqCollection* oseq = (TSeqCollection*)self->ObjectIsA()->DynamicCast(
            TSeqCollection::Class(), self->GetObject() );

         int start, stop, step;
         PySlice_GetIndices( (PySliceObject*) index, oseq->GetSize(), &start, &stop, &step );
         for ( int i = stop - step; i >= start; i -= step ) {
            oseq->RemoveAt( i );
         }

         for ( int i = 0; i < PySequence_Size( obj ); ++i ) {
            ObjectProxy* item = (ObjectProxy*)PySequence_GetItem( obj, i );
            item->Release();
            oseq->AddAt( (TObject*) item->GetObject(), i + start );
            Py_DECREF( item );
         }

         Py_INCREF( Py_None );
         return Py_None;
      }

      PyObject* pyindex = PyStyleIndex( (PyObject*)self, index );
      if ( ! pyindex )
         return 0;

      PyObject* result  = CallPyObjMethod( (PyObject*)self, "RemoveAt", pyindex );
      if ( ! result ) {
         Py_DECREF( pyindex );
         return 0;
      }

      Py_DECREF( result );
      result = CallPyObjMethod( (PyObject*)self, "AddAt", obj, pyindex );
      Py_DECREF( pyindex );
      return result;
   }

//____________________________________________________________________________
   PyObject* TSeqCollectionDelItem( PyObject*, PyObject* args )
   {
      ObjectProxy* self = 0; PySliceObject* index = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__del__" ), &self, &index ) )
         return 0;

      if ( PySlice_Check( index ) ) {
         if ( ! ObjectProxy_Check( self ) || ! self->GetObject() ) {
            PyErr_SetString( PyExc_TypeError, "unsubscriptable object" );
            return 0;
         }

         TSeqCollection* oseq = (TSeqCollection*)self->ObjectIsA()->DynamicCast(
            TSeqCollection::Class(), self->GetObject() );

         int start, stop, step;
         PySlice_GetIndices( index, oseq->GetSize(), &start, &stop, &step );
         for ( int i = stop - step; i >= start; i -= step ) {
            oseq->RemoveAt( i );
         }

         Py_INCREF( Py_None );
         return Py_None;
      }

      PyObject* result = callSelfIndex( args, "RemoveAt" );
      if ( ! result )
         return 0;

      Py_DECREF( result );
      Py_INCREF( Py_None );
      return Py_None;
   }

//____________________________________________________________________________
   PyObject* TSeqCollectionInsert( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *obj = 0; long idx = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OlO:insert" ), &self, &idx, &obj ) )
         return 0;

      int size = PySequence_Size( self );
      if ( idx < 0 )
         idx = 0;
      else if ( size < idx )
         idx = size;

      return CallPyObjMethod( self, "AddAt", obj, idx );
   }

//____________________________________________________________________________
   PyObject* TSeqCollectionPop( PyObject*, PyObject* args )
   {
      if ( PyTuple_GET_SIZE( args ) == 1 ) {
         PyObject* self = PyTuple_GET_ITEM( args, 0 );
         Py_INCREF( self );

         args = PyTuple_New( 2 );
         PyTuple_SET_ITEM( args, 0, self );
         PyTuple_SET_ITEM( args, 1, PyLong_FromLong( PySequence_Size( self ) - 1 ) );
      }

      return callSelfIndex( args, "RemoveAt" );
   }

//____________________________________________________________________________
   PyObject* TSeqCollectionReverse( PyObject*, PyObject* args )
   {
      PyObject* self = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "O:reverse" ), &self ) )
         return 0;

      PyObject* tup = PySequence_Tuple( self );
      if ( ! tup )
         return 0;

      PyObject* result = CallPyObjMethod( self, "Clear" );
      Py_XDECREF( result );

      for ( int i = 0; i < PySequence_Size( tup ); ++i ) {
         PyObject* result = CallPyObjMethod( self, "AddAt", PyTuple_GET_ITEM( tup, i ), 0 );
         Py_XDECREF( result );
      }

      Py_INCREF( Py_None );
      return Py_None;
   }

//____________________________________________________________________________
   PyObject* TSeqCollectionSort( PyObject*, PyObject* args )
   {
      PyObject* self = PyTuple_GET_ITEM( args, 0 );

      if ( PyTuple_GET_SIZE( args ) == 1 ) {
      // no specialized sort, use ROOT one
         return CallPyObjMethod( self, "Sort" );
      } else {
      // sort in a python list copy
         PyObject* l = PySequence_List( self );
         PyObject* result = CallPyObjMethod( l, "sort", PyTuple_GET_ITEM( args, 1 ) );
         Py_XDECREF( result );
         if ( PyErr_Occurred() ) {
            Py_DECREF( l );
            return 0;
         }

         result = CallPyObjMethod( self, "Clear" );
         Py_XDECREF( result );
         result = CallPyObjMethod( self, "extend", l );
         Py_XDECREF( result );
         Py_DECREF( l );

         Py_INCREF( Py_None );
         return Py_None;
      }
   }

//____________________________________________________________________________
   PyObject* TSeqCollectionIndex( PyObject*, PyObject* args )
   {
      PyObject* index = CallPySelfObjMethod( args, "IndexOf", "OO:index" );
      if ( ! index )
         return 0;

      if ( PyLong_AsLong( index ) < 0 ) {
         Py_DECREF( index );
         PyErr_SetString( PyExc_ValueError, "list.index(x): x not in list" );
         return 0;
      }

      return index;
   }

//- vector behaviour as primitives --------------------------------------------
   PyObject* VectorGetItem( PyObject*, PyObject* args )
   {
      ObjectProxy* self = 0; PySliceObject* index = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__getitem__" ), &self, &index ) )
         return 0;

      if ( PySlice_Check( index ) ) {
         if ( ! ObjectProxy_Check( self ) || ! self->GetObject() ) {
            PyErr_SetString( PyExc_TypeError, "unsubscriptable object" );
            return 0;
         }

         PyObject* pyclass =
            PyObject_GetAttrString( (PyObject*)self, const_cast< char* >( "__class__" ) );
         PyObject* nseq = PyObject_CallObject( pyclass, NULL );
         Py_DECREF( pyclass );
 
         int start, stop, step;
         PySlice_GetIndices( index, PyObject_Length( (PyObject*)self ), &start, &stop, &step );
         for ( int i = start; i < stop; i += step ) {
            PyObject* pyidx = PyInt_FromLong( i );
            CallPyObjMethod( nseq, "push_back", CallPyObjMethod( (PyObject*)self, "_vector__at", pyidx ) );
            Py_DECREF( pyidx );
         }

         return nseq;
      }

      return callSelfIndex( args, "_vector__at" );
   }

//- STL container iterator supprt ---------------------------------------------
   PyObject* StlSequenceIter( PyObject*, PyObject* args )
   {
      PyObject* iter = CallPySelfMethod( args, "begin", "O" );
      if ( iter ) {
         PyObject* end = CallPySelfMethod( args, "end", "O" );
         if ( end )
            PyObject_SetAttrString( iter, const_cast< char* >( "end" ), end );
         Py_XDECREF( end );
      }
      return iter;
   }

//- string behaviour as primitives --------------------------------------------
#define PYROOT_IMPLEMENT_STRING_PYTHONIZATION( name, func )                   \
   PyObject* name##StringRepr( PyObject*, PyObject* args )                    \
   {                                                                          \
      PyObject* data = CallPyObjMethod( PyTuple_GET_ITEM( args, 0 ), #func ); \
      PyObject* repr = PyString_FromFormat( "\'%s\'", PyString_AsString( data ) ); \
      Py_DECREF( data );                                                      \
      return repr;                                                            \
   }                                                                          \
                                                                              \
   PyObject* name##StringCompare( PyObject*, PyObject* args )                 \
   {                                                                          \
      PyObject* data = CallPyObjMethod( PyTuple_GET_ITEM( args, 0 ), #func ); \
      int result = PyObject_Compare( data, PyTuple_GET_ITEM( args, 1 ) );     \
      Py_DECREF( data );                                                      \
      if ( PyErr_Occurred() )                                                 \
         return 0;                                                            \
      return PyInt_FromLong( result );                                        \
   }                                                                          \
                                                                              \
   PyObject* name##StringIsequal( PyObject*, PyObject* args )                 \
   {                                                                          \
      PyObject* data = CallPyObjMethod( PyTuple_GET_ITEM( args, 0 ), #func ); \
      PyObject* result = PyObject_RichCompare( data, PyTuple_GET_ITEM( args, 1 ), Py_EQ );\
      Py_DECREF( data );                                                      \
      if ( ! result )                                                         \
         return 0;                                                            \
      return result;                                                          \
   }

   PYROOT_IMPLEMENT_STRING_PYTHONIZATION( Stl, c_str )
   PYROOT_IMPLEMENT_STRING_PYTHONIZATION(   T, Data )


//- TObjString behaviour -------------------------------------------------------
   PYROOT_IMPLEMENT_STRING_PYTHONIZATION( TObj, GetName )

//____________________________________________________________________________
   PyObject* TObjStringLength( PyObject*, PyObject* args )
   {
      PyObject* data = CallPyObjMethod( PyTuple_GET_ITEM( args, 0 ), "GetName" );
      int size = PySequence_Size( data );
      Py_DECREF( data );
      return PyInt_FromLong( size );
   }


//- TIter behaviour ------------------------------------------------------------
   PyObject* TIterIter( PyObject*, PyObject* args )
   {
      PyObject* iter = PyTuple_GET_ITEM( args, 0 );
      Py_INCREF( iter );
      return iter;
   }

//____________________________________________________________________________
   PyObject* TIterNext( PyObject*, PyObject* args )
   {
      PyObject* next = CallPySelfMethod( args, "Next", "O:next" );

      if ( ! next )
         return 0;

      if ( ! PyObject_IsTrue( next ) ) {
         Py_DECREF( next );
         PyErr_SetString( PyExc_StopIteration, "" );
         return 0;
      }

      return next;
   }


//- STL iterator behaviour -----------------------------------------------------
   PyObject* StlIterCompare( PyObject*, PyObject* args )
   {
      ObjectProxy* self = 0, *pyobj = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__cmp__" ), &self, &pyobj ) )
         return 0;

      if ( ! ObjectProxy_Check( pyobj ) )
         return PyInt_FromLong( -1l );

      return PyInt_FromLong( *(void**)self->GetObject() == *(void**)pyobj->GetObject() ? 0l : 1l );
   }

//____________________________________________________________________________
   PyObject* StlIterNext( PyObject*, PyObject* args )
   {
      PyObject* self = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "O" ), &self ) )
         return 0;

      PyObject* next = 0;

      PyObject* last = PyObject_GetAttrString( self, const_cast< char* >( "end" ) );
      if ( last != 0 ) {
         PyObject* dummy = PyInt_FromLong( 1l );
         PyObject* iter = CallPyObjMethod( self, "__postinc__", dummy );
         Py_DECREF( dummy );
         if ( iter != 0 ) {
            if ( *(void**)((ObjectProxy*)last)->fObject == *(void**)((ObjectProxy*)iter)->fObject )
               PyErr_SetString( PyExc_StopIteration, "" );
            else
               next = CallPyObjMethod( iter, "__deref__" );
         }
         Py_XDECREF( iter );
      }
      Py_XDECREF( last );

      return next;
   }


//- TDirectory member templates ----------------------------------------------
   PyObject* TDirectoryGetObject( PyObject*, PyObject* args )
   {
      ObjectProxy* self = 0; PyObject* name = 0; ObjectProxy* ptr = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OSO:TDirectory::GetObject" ),
               &self, &name, &ptr ) )
         return 0;

      TDirectory* dir =
         (TDirectory*)self->ObjectIsA()->DynamicCast( TDirectory::Class(), self->GetObject() );

      if ( ! dir ) {
         PyErr_SetString( PyExc_TypeError,
           "TDirectory::GetObject must be called with a TDirectory instance as first argument" );
         return 0;
      }

      void* address = dir->GetObjectChecked( PyString_AS_STRING( name ), ptr->ObjectIsA() );
      if ( address ) {
         ptr->Set( address, ptr->ObjectIsA() );

         Py_INCREF( Py_None );
         return Py_None;
      }

      PyErr_Format( PyExc_LookupError, "no such object, \"%s\"", PyString_AS_STRING( name ) );
      return 0;
   }

//____________________________________________________________________________
   PyObject* TDirectoryWriteObject( PyObject*, PyObject* args )
   {
      ObjectProxy *self = 0, *wrt = 0; PyObject *name = 0, *option = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OOS|S:TDirectory::WriteObject" ),
               &self, &wrt, &name, &option ) )
         return 0;

      TDirectory* dir =
         (TDirectory*)self->ObjectIsA()->DynamicCast( TDirectory::Class(), self->GetObject() );

      if ( ! dir ) {
         PyErr_SetString( PyExc_TypeError,
           "TDirectory::WriteObject must be called with a TDirectory instance as first argument" );
         return 0;
      }

      Int_t result = 0;
      if ( option != 0 ) {
         result = dir->WriteObjectAny( wrt->GetObject(), wrt->ObjectIsA(),
            PyString_AS_STRING( name ), PyString_AS_STRING( option ) );
      } else {
         result = dir->WriteObjectAny(
            wrt->GetObject(), wrt->ObjectIsA(), PyString_AS_STRING( name ) );
      }

      return PyInt_FromLong( (long)result );
   }

//- TTree behaviour ----------------------------------------------------------
   class TreeEraser : public TObject {
   public:
      TreeEraser( PyObject* ttree ) : fTree( ttree ) {}

      virtual Bool_t Notify()
      {
         if ( ! fTree )
            return kFALSE;

      // "reset" the dictionary by replacing it with an empty one
         PyObject* dict = PyDict_New();
         PyObject_SetAttrString( fTree, const_cast< char* >( "__dict__" ), dict );
         Py_DECREF( dict );

         return kTRUE;
      }
   private:
      PyObject* fTree;
   };

//____________________________________________________________________________
   PyObject* TTreeGetAttr( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *name = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__getattr__" ), &self, &name ) )
         return 0;

   // setup notification as needed
      PyObject* notify = CallPyObjMethod( self, "GetNotify" );
      if ( PyObject_Not( notify ) ) {
         TObject* te = new TreeEraser( self );
         PyObject* result = CallPyObjMethod( self, "SetNotify", BindRootObject( te, te->IsA() ) );
         Py_XDECREF( result );
      }
      Py_DECREF( notify );

   // allow access to leaves as if they are data members
      PyObject* leaf = CallPyObjMethod( self, "GetLeaf", name );
      if ( ! leaf )
         return 0;

      if ( leaf != Py_None ) {
         PyObject* value = 0;

      // found a leaf, extract value if just one, or wrap buffer if more
         PyObject* lcount = CallPyObjMethod( leaf, "GetLeafCount" );
         PyObject* length = CallPyObjMethod( leaf, "GetLenStatic" );

         if ( lcount == Py_None && PyInt_AS_LONG( length ) <= 1 ) {
            value = CallPyObjMethod( leaf, "GetValue" );
         } else {
            PyObject* ptr = CallPyObjMethod( leaf, "GetValuePointer" );
            void* arr = PyLong_AsVoidPtr( ptr );
            Py_DECREF( ptr );

            PyObject* tname = CallPyObjMethod( leaf, "GetTypeName" );
            std::string stname( PyString_AS_STRING( tname ) );

            BufFac_t* fac = BufFac_t::Instance();
            PyObject* scb = PyObject_GetAttrString( leaf, const_cast< char* >( "GetNdata" ) );

         // TODO: use Converter::FromMemory instead
            Utility::EDataType eType = Utility::EffectiveType( stname );
            if ( eType == Utility::kLong )
               value = fac->PyBuffer_FromMemory( (long*) arr, scb );
            else if ( eType == Utility::kInt )
               value = fac->PyBuffer_FromMemory( (int*) arr, scb );
            else if ( eType == Utility::kDouble )
               value = fac->PyBuffer_FromMemory( (double*) arr, scb );
            else if ( eType == Utility::kFloat )
               value = fac->PyBuffer_FromMemory( (float*) arr, scb );

            Py_DECREF( scb );

         // we're working with addresses: cache result
            PyObject_SetAttr( self, name, value );
         }

         Py_DECREF( length );
         Py_DECREF( lcount );
         Py_DECREF( leaf );

         return value;
      }

   // confused
      Py_DECREF( leaf );
      char txt[ 256 ];
      sprintf( txt, "no such attribute \'%s\'", PyString_AsString( name ) );
      PyErr_SetString( PyExc_AttributeError, txt );
      return 0;
   }

//____________________________________________________________________________
   class TTreeBranch : public PyCallable {
   public:
      TTreeBranch( MethodProxy* org ) { Py_INCREF( org ); m_org = org; }
      TTreeBranch( const TTreeBranch& t ) : PyCallable( t ) { Py_INCREF( t.m_org ); m_org = t.m_org; }
      TTreeBranch& operator=( const TTreeBranch& t )
      {
         if ( &t != this ) {
            Py_INCREF( t.m_org );
            m_org = t.m_org;
         }
         return *this;
      }
      ~TTreeBranch() { Py_DECREF( m_org ); m_org = 0; }

   public:
      virtual PyObject* GetDocString()
      {
         return PyString_FromString( "TBranch* TTree::Branch( ... )" );
      }

      virtual PyObject* operator()( ObjectProxy* self, PyObject* args, PyObject* kwds )
      {
      // acceptable signatures:
      //   ( const char*, void*, const char*, Int_t = 32000 )
      //   ( const char*, const char*, T**, Int_t = 32000, Int_t = 99 )
      //   ( const char*, T**, Int_t = 32000, Int_t = 99 ) 
         int argc = PyTuple_GET_SIZE( args );

         if ( 2 <= argc ) {
            TTree* tree = (TTree*)self->ObjectIsA()->DynamicCast( TTree::Class(), self->GetObject() );

            if ( ! tree ) {
               PyErr_SetString( PyExc_TypeError,
                  "TTree::Branch must be called with a TTree instance as first argument" );
               return 0;
            }

            PyObject *name = 0, *clName = 0, *leaflist = 0;
            PyObject *address = 0;
            PyObject *bufsize = 0, *splitlevel = 0;

         // try: ( const char*, void*, const char*, Int_t = 32000 )
            if ( PyArg_ParseTuple( args, const_cast< char* >( "SOS|O!:Branch" ),
                    &name, &address, &leaflist, &PyInt_Type, &bufsize ) ) {

               void* buf = 0;
               if ( ObjectProxy_Check( address ) )
                  buf = (void*)((ObjectProxy*)address)->GetObject();
               else
                  Utility::GetBuffer( address, '*', 1, buf, false );
 
               if ( buf != 0 ) {
                  TBranch* branch = 0;
                  if ( argc == 4 ) {
                     branch = tree->Branch( PyString_AS_STRING( name ), buf,
                        PyString_AS_STRING( leaflist ), PyInt_AS_LONG( bufsize ) );
                  } else {
                     branch = tree->Branch(
                        PyString_AS_STRING( name ), buf, PyString_AS_STRING( leaflist ) );
                  }

                  return BindRootObject( branch, TBranch::Class() );
               }

            }
            PyErr_Clear();

         // try: ( const char*, const char*, T**, Int_t = 32000, Int_t = 99 )
         //  or: ( const char*,              T**, Int_t = 32000, Int_t = 99 ) 
            bool bIsMatch = false;
            if ( PyArg_ParseTuple( args, const_cast< char* >( "SSO|O!O!:Branch" ),
                    &name, &clName, &address, &PyInt_Type, &bufsize, &PyInt_Type, &splitlevel ) ) {
               bIsMatch = true;
            } else {
               PyErr_Clear();
               if ( PyArg_ParseTuple( args, const_cast< char* >( "SO|O!O!" ),
                       &name, &address, &PyInt_Type, &bufsize, &PyInt_Type, &splitlevel ) )
                  bIsMatch = true;
               else
                  PyErr_Clear();
            }

            if ( bIsMatch == true ) {
               std::string klName = clName ? PyString_AS_STRING( clName ) : "";
               void* buf = 0;

               if ( ObjectProxy_Check( address ) ) {
                  if ( ((ObjectProxy*)address)->fFlags & ObjectProxy::kIsReference )
                     buf = (void*)((ObjectProxy*)address)->fObject;
                  else
                     buf = (void*)&((ObjectProxy*)address)->fObject;
                  
                  if ( ! clName ) {
                     klName = ((ObjectProxy*)address)->ObjectIsA()->GetName();
                     argc += 1;
                  }
               } else
                  Utility::GetBuffer( address, '*', 1, buf, false );

               if ( buf != 0 && klName != "" ) {
                  TBranch* branch = 0;
                  if ( argc == 3 ) {
                     branch = tree->Branch( PyString_AS_STRING( name ), klName.c_str(), buf );
                  } else if ( argc == 4 ) {
                     branch = tree->Branch( PyString_AS_STRING( name ), klName.c_str(), buf,
                        PyInt_AS_LONG( bufsize ) );
                  } else if ( argc == 5 ) {
                     branch = tree->Branch( PyString_AS_STRING( name ), klName.c_str(), buf,
                        PyInt_AS_LONG( bufsize ), PyInt_AS_LONG( splitlevel ) );
                  }

                  return BindRootObject( branch, TBranch::Class() );
               }
            }
         }

      // still here? Then call original Branch() to reach the other overloads:
         Py_INCREF( (PyObject*)self );
         m_org->fSelf = self;
         PyObject* result = PyObject_Call( (PyObject*)m_org, args, kwds );
         m_org->fSelf = 0;
         Py_DECREF( (PyObject*)self );

         return result;
      }

   private:
      MethodProxy* m_org;
   };


//- TFN behaviour ------------------------------------------------------------
   std::map< int, std::pair< PyObject*, int > > gPyObjectCallbacks;
   typedef std::pair< PyObject*, int > CallInfo_t;

   int gLastTag = 99;<commit_msg>From Wim: remove warning reported by Intel's icc.<commit_after>// @(#)root/pyroot:$Name:  $:$Id: Pythonize.cxx,v 1.22 2005/06/24 07:19:03 brun Exp $
// Author: <NAME>, Jul 2004

// Bindings
#include "PyROOT.h"
#include "Pythonize.h"
#include "ObjectProxy.h"
#include "MethodProxy.h"
#include "RootWrapper.h"
#include "Utility.h"
#include "PyCallable.h"
#include "PyBufferFactory.h"
#include "FunctionHolder.h"

// ROOT
#include "TClass.h"
#include "TCollection.h"
#include "TDirectory.h"
#include "TSeqCollection.h"
#include "TObject.h"
#include "TFunction.h"
#include "TTree.h"

// CINT
#include "Api.h"

// Standard
#include <stdexcept>
#include <string>
#include <stdio.h>
#include <utility>


namespace {

// for convenience
   using namespace PyROOT;

//____________________________________________________________________________
   inline bool IsTemplatedSTLClass( const std::string& name, const std::string& klass ) {
      const int nsize = (int)name.size();
      const int ksize = (int)klass.size();

      return ( ( ksize   < nsize && name.substr(0,ksize) == klass ) ||
               ( ksize+5 < nsize && name.substr(5,ksize) == klass ) ) &&
             name.find( "::", name.find( ">" ) ) == std::string::npos;
   }

// to prevent compiler warnings about const char* -> char*
   inline PyObject* CallPyObjMethod( PyObject* obj, const char* meth )
   {
      return PyObject_CallMethod( obj, const_cast< char* >( meth ), const_cast< char* >( "" ) );
   }

//____________________________________________________________________________
   inline PyObject* CallPyObjMethod( PyObject* obj, const char* meth, PyObject* arg1 )
   {
      return PyObject_CallMethod(
         obj, const_cast< char* >( meth ), const_cast< char* >( "O" ), arg1 );
   }

//____________________________________________________________________________
   inline PyObject* CallPyObjMethod(
      PyObject* obj, const char* meth, PyObject* arg1, PyObject* arg2 )
   {
      return PyObject_CallMethod(
         obj, const_cast< char* >( meth ), const_cast< char* >( "OO" ), arg1, arg2 );
   }

//____________________________________________________________________________
   inline PyObject* CallPyObjMethod( PyObject* obj, const char* meth, PyObject* arg1, int arg2 )
   {
      return PyObject_CallMethod(
         obj, const_cast< char* >( meth ), const_cast< char* >( "Oi" ), arg1, arg2 );
   }


//- helpers --------------------------------------------------------------------
   PyObject* CallPySelfMethod( PyObject* args, const char* meth, const char* fmt )
   {
      PyObject* self = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( fmt ), &self ) )
         return 0;

      return CallPyObjMethod( self, meth );
   }

//____________________________________________________________________________
   PyObject* CallPySelfObjMethod( PyObject* args, const char* meth, const char* fmt )
   {
      PyObject* self = 0, *obj = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( fmt ), &self, &obj ) )
         return 0;

      return CallPyObjMethod( self, meth, obj );
   }

//____________________________________________________________________________
   PyObject* PyStyleIndex( PyObject* self, PyObject* index )
   {
      long idx = PyInt_AsLong( index );
      if ( PyErr_Occurred() )
         return 0;

      PyObject* pyindex = 0;
      long size = PySequence_Size( self );
      if ( idx >= size || ( idx < 0 && idx <= -size ) ) {
         PyErr_SetString( PyExc_IndexError, "index out of range" );
         return 0;
      }

      if ( idx >= 0 ) {
         Py_INCREF( index );
         pyindex = index;
      } else
         pyindex = PyLong_FromLong( size + idx );

      return pyindex;
   }

//____________________________________________________________________________
   PyObject* callSelfIndex( PyObject* args, const char* meth )
   {
      PyObject* self = PyTuple_GET_ITEM( args, 0 );
      PyObject* obj  = PyTuple_GET_ITEM( args, 1 );

      PyObject* pyindex = PyStyleIndex( self, obj );
      if ( ! pyindex )
         return 0;

      PyObject* result = CallPyObjMethod( self, meth, pyindex );
      Py_DECREF( pyindex );
      return result;
   }


//- TObject behaviour ----------------------------------------------------------
   PyObject* TObjectContains( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *obj = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__contains__" ), &self, &obj ) )
         return 0;

      if ( ! ( ObjectProxy_Check( obj ) || PyString_Check( obj ) ) )
         return PyInt_FromLong( 0l );

      PyObject* found = CallPyObjMethod( self, "FindObject", obj );
      PyObject* result = PyInt_FromLong( PyObject_IsTrue( found ) );
      Py_DECREF( found );
      return result;
   }

//____________________________________________________________________________
   PyObject* TObjectCompare( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *obj = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__cmp__" ), &self, &obj ) )
         return 0;

      if ( ! ObjectProxy_Check( obj ) )
         return PyInt_FromLong( -1l );

      return CallPyObjMethod( self, "Compare", obj );
   }

//____________________________________________________________________________
   PyObject* TObjectIsEqual( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *obj = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__eq__" ), &self, &obj ) )
         return 0;

      if ( ! ObjectProxy_Check( obj ) )
         return PyInt_FromLong( 0l );

      return CallPyObjMethod( self, "IsEqual", obj );
   }


//- TCollection behaviour ------------------------------------------------------
   PyObject* TCollectionExtend( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *obj = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:extend" ), &self, &obj ) )
         return 0;

      for ( int i = 0; i < PySequence_Size( obj ); ++i ) {
         PyObject* item = PySequence_GetItem( obj, i );
         PyObject* result = CallPyObjMethod( self, "Add", item );
         Py_XDECREF( result );
         Py_DECREF( item );
      }

      Py_INCREF( Py_None );
      return Py_None;
   }

//____________________________________________________________________________
   PyObject* TCollectionRemove( PyObject*, PyObject* args )
   {
      PyObject* result = CallPySelfObjMethod( args, "Remove", "OO:remove" );
      if ( ! result )
         return 0;

      if ( ! PyObject_IsTrue( result ) ) {
         Py_DECREF( result );
         PyErr_SetString( PyExc_ValueError, "list.remove(x): x not in list" );
         return 0;
      }

      Py_DECREF( result );
      Py_INCREF( Py_None );
      return Py_None;
   }

//____________________________________________________________________________
   PyObject* TCollectionAdd( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *other = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__add__" ), &self, &other ) )
         return 0;

      PyObject* l = CallPyObjMethod( self, "Clone" );
      if ( ! l )
         return 0;

      PyObject* result = CallPyObjMethod( l, "extend", other );
      if ( ! result ) {
         Py_DECREF( l );
         return 0;
      }

      return l;
   }

//____________________________________________________________________________
   PyObject* TCollectionMul( PyObject*, PyObject* args )
   {
      ObjectProxy* self = 0; long imul = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "Ol:__mul__" ), &self, &imul ) )
         return 0;

      if ( ! ObjectProxy_Check( self ) || ! self->GetObject() ) {
         PyErr_SetString( PyExc_TypeError, "unsubscriptable object" );
         return 0;
      }

      PyObject* nseq = BindRootObject( self->ObjectIsA()->New(), self->ObjectIsA() );

      for ( long i = 0; i < imul; ++i ) {
         PyObject* result = CallPyObjMethod( nseq, "extend", (PyObject*)self );
         Py_DECREF( result );
      }

      return nseq;
   }

//____________________________________________________________________________
   PyObject* TCollectionIMul( PyObject*, PyObject* args )
   {
      PyObject* self = 0; long imul = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "Ol:__imul__" ), &self, &imul ) )
         return 0;

      PyObject* l = PySequence_List( self );

      for ( long i = 0; i < imul - 1; ++i ) {
         CallPyObjMethod( self, "extend", l );
      }

      Py_INCREF( self );
      return self;
   }

//____________________________________________________________________________
   PyObject* TCollectionCount( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *obj = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:count" ), &self, &obj ) )
         return 0;

      int count = 0;
      for ( int i = 0; i < PySequence_Size( self ); ++i ) {
         PyObject* item = PySequence_GetItem( self, i );
         PyObject* found = PyObject_RichCompare( item, obj, Py_EQ );

         Py_DECREF( item );

         if ( ! found )
            return 0;                        // internal problem

         if ( PyObject_IsTrue( found ) )
            count += 1;
         Py_DECREF( found );
      }

      return PyLong_FromLong( count );
   }


//____________________________________________________________________________
   PyObject* TCollectionIter( PyObject*, PyObject* args ) {
      ObjectProxy* self = (ObjectProxy*) PyTuple_GET_ITEM( args, 0 );
      if ( ! ObjectProxy_Check( self ) || ! self->GetObject() ) {
         PyErr_SetString( PyExc_TypeError, "iteration over non-sequence" );
         return 0;
      }

      TCollection* col =
         (TCollection*)self->ObjectIsA()->DynamicCast( TCollection::Class(), self->GetObject() );

      PyObject* pyobject = BindRootObject( (void*) new TIter( col ), TIter::Class() );
      ((ObjectProxy*)pyobject)->fFlags |= ObjectProxy::kIsOwner;
      return pyobject;
   }


//- TSeqCollection behaviour ---------------------------------------------------
   PyObject* TSeqCollectionGetItem( PyObject*, PyObject* args )
   {
      ObjectProxy* self = 0; PySliceObject* index = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__getitem__" ), &self, &index ) )
         return 0;
      
      if ( PySlice_Check( index ) ) {
         if ( ! ObjectProxy_Check( self ) || ! self->GetObject() ) {
            PyErr_SetString( PyExc_TypeError, "unsubscriptable object" );
            return 0;
         }

         TClass* clSeq = self->ObjectIsA();
         TSeqCollection* oseq =
            (TSeqCollection*)clSeq->DynamicCast( TSeqCollection::Class(), self->GetObject() );
         TSeqCollection* nseq = (TSeqCollection*)clSeq->New();

         int start, stop, step;
         PySlice_GetIndices( index, oseq->GetSize(), &start, &stop, &step );
         for ( int i = start; i < stop; i += step ) {
            nseq->Add( oseq->At( i ) );
         }

         return BindRootObject( (void*) nseq, clSeq );
      }

      return callSelfIndex( args, "At" );
   }

//____________________________________________________________________________
   PyObject* TSeqCollectionSetItem( PyObject*, PyObject* args )
   {
      ObjectProxy* self = 0; PyObject* index = 0, *obj = 0;
      if ( ! PyArg_ParseTuple( args,
                const_cast< char* >( "OOO:__setitem__" ), &self, &index, &obj ) )
         return 0;

      if ( PySlice_Check( index ) ) {
         if ( ! ObjectProxy_Check( self ) || ! self->GetObject() ) {
            PyErr_SetString( PyExc_TypeError, "unsubscriptable object" );
            return 0;
         }

         TSeqCollection* oseq = (TSeqCollection*)self->ObjectIsA()->DynamicCast(
            TSeqCollection::Class(), self->GetObject() );

         int start, stop, step;
         PySlice_GetIndices( (PySliceObject*) index, oseq->GetSize(), &start, &stop, &step );
         for ( int i = stop - step; i >= start; i -= step ) {
            oseq->RemoveAt( i );
         }

         for ( int i = 0; i < PySequence_Size( obj ); ++i ) {
            ObjectProxy* item = (ObjectProxy*)PySequence_GetItem( obj, i );
            item->Release();
            oseq->AddAt( (TObject*) item->GetObject(), i + start );
            Py_DECREF( item );
         }

         Py_INCREF( Py_None );
         return Py_None;
      }

      PyObject* pyindex = PyStyleIndex( (PyObject*)self, index );
      if ( ! pyindex )
         return 0;

      PyObject* result  = CallPyObjMethod( (PyObject*)self, "RemoveAt", pyindex );
      if ( ! result ) {
         Py_DECREF( pyindex );
         return 0;
      }

      Py_DECREF( result );
      result = CallPyObjMethod( (PyObject*)self, "AddAt", obj, pyindex );
      Py_DECREF( pyindex );
      return result;
   }

//____________________________________________________________________________
   PyObject* TSeqCollectionDelItem( PyObject*, PyObject* args )
   {
      ObjectProxy* self = 0; PySliceObject* index = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__del__" ), &self, &index ) )
         return 0;

      if ( PySlice_Check( index ) ) {
         if ( ! ObjectProxy_Check( self ) || ! self->GetObject() ) {
            PyErr_SetString( PyExc_TypeError, "unsubscriptable object" );
            return 0;
         }

         TSeqCollection* oseq = (TSeqCollection*)self->ObjectIsA()->DynamicCast(
            TSeqCollection::Class(), self->GetObject() );

         int start, stop, step;
         PySlice_GetIndices( index, oseq->GetSize(), &start, &stop, &step );
         for ( int i = stop - step; i >= start; i -= step ) {
            oseq->RemoveAt( i );
         }

         Py_INCREF( Py_None );
         return Py_None;
      }

      PyObject* result = callSelfIndex( args, "RemoveAt" );
      if ( ! result )
         return 0;

      Py_DECREF( result );
      Py_INCREF( Py_None );
      return Py_None;
   }

//____________________________________________________________________________
   PyObject* TSeqCollectionInsert( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *obj = 0; long idx = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OlO:insert" ), &self, &idx, &obj ) )
         return 0;

      int size = PySequence_Size( self );
      if ( idx < 0 )
         idx = 0;
      else if ( size < idx )
         idx = size;

      return CallPyObjMethod( self, "AddAt", obj, idx );
   }

//____________________________________________________________________________
   PyObject* TSeqCollectionPop( PyObject*, PyObject* args )
   {
      if ( PyTuple_GET_SIZE( args ) == 1 ) {
         PyObject* self = PyTuple_GET_ITEM( args, 0 );
         Py_INCREF( self );

         args = PyTuple_New( 2 );
         PyTuple_SET_ITEM( args, 0, self );
         PyTuple_SET_ITEM( args, 1, PyLong_FromLong( PySequence_Size( self ) - 1 ) );
      }

      return callSelfIndex( args, "RemoveAt" );
   }

//____________________________________________________________________________
   PyObject* TSeqCollectionReverse( PyObject*, PyObject* args )
   {
      PyObject* self = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "O:reverse" ), &self ) )
         return 0;

      PyObject* tup = PySequence_Tuple( self );
      if ( ! tup )
         return 0;

      PyObject* result = CallPyObjMethod( self, "Clear" );
      Py_XDECREF( result );

      for ( int i = 0; i < PySequence_Size( tup ); ++i ) {
         PyObject* result = CallPyObjMethod( self, "AddAt", PyTuple_GET_ITEM( tup, i ), 0 );
         Py_XDECREF( result );
      }

      Py_INCREF( Py_None );
      return Py_None;
   }

//____________________________________________________________________________
   PyObject* TSeqCollectionSort( PyObject*, PyObject* args )
   {
      PyObject* self = PyTuple_GET_ITEM( args, 0 );

      if ( PyTuple_GET_SIZE( args ) == 1 ) {
      // no specialized sort, use ROOT one
         return CallPyObjMethod( self, "Sort" );
      } else {
      // sort in a python list copy
         PyObject* l = PySequence_List( self );
         PyObject* result = CallPyObjMethod( l, "sort", PyTuple_GET_ITEM( args, 1 ) );
         Py_XDECREF( result );
         if ( PyErr_Occurred() ) {
            Py_DECREF( l );
            return 0;
         }

         result = CallPyObjMethod( self, "Clear" );
         Py_XDECREF( result );
         result = CallPyObjMethod( self, "extend", l );
         Py_XDECREF( result );
         Py_DECREF( l );

         Py_INCREF( Py_None );
         return Py_None;
      }
   }

//____________________________________________________________________________
   PyObject* TSeqCollectionIndex( PyObject*, PyObject* args )
   {
      PyObject* index = CallPySelfObjMethod( args, "IndexOf", "OO:index" );
      if ( ! index )
         return 0;

      if ( PyLong_AsLong( index ) < 0 ) {
         Py_DECREF( index );
         PyErr_SetString( PyExc_ValueError, "list.index(x): x not in list" );
         return 0;
      }

      return index;
   }

//- vector behaviour as primitives --------------------------------------------
   PyObject* VectorGetItem( PyObject*, PyObject* args )
   {
      ObjectProxy* self = 0; PySliceObject* index = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__getitem__" ), &self, &index ) )
         return 0;

      if ( PySlice_Check( index ) ) {
         if ( ! ObjectProxy_Check( self ) || ! self->GetObject() ) {
            PyErr_SetString( PyExc_TypeError, "unsubscriptable object" );
            return 0;
         }

         PyObject* pyclass =
            PyObject_GetAttrString( (PyObject*)self, const_cast< char* >( "__class__" ) );
         PyObject* nseq = PyObject_CallObject( pyclass, NULL );
         Py_DECREF( pyclass );
 
         int start, stop, step;
         PySlice_GetIndices( index, PyObject_Length( (PyObject*)self ), &start, &stop, &step );
         for ( int i = start; i < stop; i += step ) {
            PyObject* pyidx = PyInt_FromLong( i );
            CallPyObjMethod( nseq, "push_back", CallPyObjMethod( (PyObject*)self, "_vector__at", pyidx ) );
            Py_DECREF( pyidx );
         }

         return nseq;
      }

      return callSelfIndex( args, "_vector__at" );
   }

//- STL container iterator supprt ---------------------------------------------
   PyObject* StlSequenceIter( PyObject*, PyObject* args )
   {
      PyObject* iter = CallPySelfMethod( args, "begin", "O" );
      if ( iter ) {
         PyObject* end = CallPySelfMethod( args, "end", "O" );
         if ( end )
            PyObject_SetAttrString( iter, const_cast< char* >( "end" ), end );
         Py_XDECREF( end );
      }
      return iter;
   }

//- string behaviour as primitives --------------------------------------------
#define PYROOT_IMPLEMENT_STRING_PYTHONIZATION( name, func )                   \
   PyObject* name##StringRepr( PyObject*, PyObject* args )                    \
   {                                                                          \
      PyObject* data = CallPyObjMethod( PyTuple_GET_ITEM( args, 0 ), #func ); \
      PyObject* repr = PyString_FromFormat( "\'%s\'", PyString_AsString( data ) ); \
      Py_DECREF( data );                                                      \
      return repr;                                                            \
   }                                                                          \
                                                                              \
   PyObject* name##StringCompare( PyObject*, PyObject* args )                 \
   {                                                                          \
      PyObject* data = CallPyObjMethod( PyTuple_GET_ITEM( args, 0 ), #func ); \
      int result = PyObject_Compare( data, PyTuple_GET_ITEM( args, 1 ) );     \
      Py_DECREF( data );                                                      \
      if ( PyErr_Occurred() )                                                 \
         return 0;                                                            \
      return PyInt_FromLong( result );                                        \
   }                                                                          \
                                                                              \
   PyObject* name##StringIsequal( PyObject*, PyObject* args )                 \
   {                                                                          \
      PyObject* data = CallPyObjMethod( PyTuple_GET_ITEM( args, 0 ), #func ); \
      PyObject* result = PyObject_RichCompare( data, PyTuple_GET_ITEM( args, 1 ), Py_EQ );\
      Py_DECREF( data );                                                      \
      if ( ! result )                                                         \
         return 0;                                                            \
      return result;                                                          \
   }

   PYROOT_IMPLEMENT_STRING_PYTHONIZATION( Stl, c_str )
   PYROOT_IMPLEMENT_STRING_PYTHONIZATION(   T, Data )


//- TObjString behaviour -------------------------------------------------------
   PYROOT_IMPLEMENT_STRING_PYTHONIZATION( TObj, GetName )

//____________________________________________________________________________
   PyObject* TObjStringLength( PyObject*, PyObject* args )
   {
      PyObject* data = CallPyObjMethod( PyTuple_GET_ITEM( args, 0 ), "GetName" );
      int size = PySequence_Size( data );
      Py_DECREF( data );
      return PyInt_FromLong( size );
   }


//- TIter behaviour ------------------------------------------------------------
   PyObject* TIterIter( PyObject*, PyObject* args )
   {
      PyObject* iter = PyTuple_GET_ITEM( args, 0 );
      Py_INCREF( iter );
      return iter;
   }

//____________________________________________________________________________
   PyObject* TIterNext( PyObject*, PyObject* args )
   {
      PyObject* next = CallPySelfMethod( args, "Next", "O:next" );

      if ( ! next )
         return 0;

      if ( ! PyObject_IsTrue( next ) ) {
         Py_DECREF( next );
         PyErr_SetString( PyExc_StopIteration, "" );
         return 0;
      }

      return next;
   }


//- STL iterator behaviour -----------------------------------------------------
   PyObject* StlIterCompare( PyObject*, PyObject* args )
   {
      ObjectProxy* self = 0, *pyobj = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__cmp__" ), &self, &pyobj ) )
         return 0;

      if ( ! ObjectProxy_Check( pyobj ) )
         return PyInt_FromLong( -1l );

      return PyInt_FromLong( *(void**)self->GetObject() == *(void**)pyobj->GetObject() ? 0l : 1l );
   }

//____________________________________________________________________________
   PyObject* StlIterNext( PyObject*, PyObject* args )
   {
      PyObject* self = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "O" ), &self ) )
         return 0;

      PyObject* next = 0;

      PyObject* last = PyObject_GetAttrString( self, const_cast< char* >( "end" ) );
      if ( last != 0 ) {
         PyObject* dummy = PyInt_FromLong( 1l );
         PyObject* iter = CallPyObjMethod( self, "__postinc__", dummy );
         Py_DECREF( dummy );
         if ( iter != 0 ) {
            if ( *(void**)((ObjectProxy*)last)->fObject == *(void**)((ObjectProxy*)iter)->fObject )
               PyErr_SetString( PyExc_StopIteration, "" );
            else
               next = CallPyObjMethod( iter, "__deref__" );
         }
         Py_XDECREF( iter );
      }
      Py_XDECREF( last );

      return next;
   }


//- TDirectory member templates ----------------------------------------------
   PyObject* TDirectoryGetObject( PyObject*, PyObject* args )
   {
      ObjectProxy* self = 0; PyObject* name = 0; ObjectProxy* ptr = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OSO:TDirectory::GetObject" ),
               &self, &name, &ptr ) )
         return 0;

      TDirectory* dir =
         (TDirectory*)self->ObjectIsA()->DynamicCast( TDirectory::Class(), self->GetObject() );

      if ( ! dir ) {
         PyErr_SetString( PyExc_TypeError,
           "TDirectory::GetObject must be called with a TDirectory instance as first argument" );
         return 0;
      }

      void* address = dir->GetObjectChecked( PyString_AS_STRING( name ), ptr->ObjectIsA() );
      if ( address ) {
         ptr->Set( address, ptr->ObjectIsA() );

         Py_INCREF( Py_None );
         return Py_None;
      }

      PyErr_Format( PyExc_LookupError, "no such object, \"%s\"", PyString_AS_STRING( name ) );
      return 0;
   }

//____________________________________________________________________________
   PyObject* TDirectoryWriteObject( PyObject*, PyObject* args )
   {
      ObjectProxy *self = 0, *wrt = 0; PyObject *name = 0, *option = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OOS|S:TDirectory::WriteObject" ),
               &self, &wrt, &name, &option ) )
         return 0;

      TDirectory* dir =
         (TDirectory*)self->ObjectIsA()->DynamicCast( TDirectory::Class(), self->GetObject() );

      if ( ! dir ) {
         PyErr_SetString( PyExc_TypeError,
           "TDirectory::WriteObject must be called with a TDirectory instance as first argument" );
         return 0;
      }

      Int_t result = 0;
      if ( option != 0 ) {
         result = dir->WriteObjectAny( wrt->GetObject(), wrt->ObjectIsA(),
            PyString_AS_STRING( name ), PyString_AS_STRING( option ) );
      } else {
         result = dir->WriteObjectAny(
            wrt->GetObject(), wrt->ObjectIsA(), PyString_AS_STRING( name ) );
      }

      return PyInt_FromLong( (long)result );
   }

}


namespace PyROOT {      // workaround for Intel icc on Linux

//- TTree behaviour ----------------------------------------------------------
   class TreeEraser : public TObject {
   public:
      TreeEraser( PyObject* ttree ) : fTree( ttree ) {}

      virtual Bool_t Notify()
      {
         if ( ! fTree )
            return kFALSE;

      // "reset" the dictionary by replacing it with an empty one
         PyObject* dict = PyDict_New();
         PyObject_SetAttrString( fTree, const_cast< char* >( "__dict__" ), dict );
         Py_DECREF( dict );

         return kTRUE;
      }
   private:
      PyObject* fTree;
   };

//____________________________________________________________________________
   PyObject* TTreeGetAttr( PyObject*, PyObject* args )
   {
      PyObject* self = 0, *name = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:__getattr__" ), &self, &name ) )
         return 0;

   // setup notification as needed
      PyObject* notify = CallPyObjMethod( self, "GetNotify" );
      if ( PyObject_Not( notify ) ) {
         TObject* te = new TreeEraser( self );
         PyObject* result = CallPyObjMethod( self, "SetNotify", BindRootObject( te, te->IsA() ) );
         Py_XDECREF( result );
      }
      Py_DECREF( notify );

   // allow access to leaves as if they are data members
      PyObject* leaf = CallPyObjMethod( self, "GetLeaf", name );
      if ( ! leaf )
         return 0;

      if ( leaf != Py_None ) {
         PyObject* value = 0;

      // found a leaf, extract value if just one, or wrap buffer if more
         PyObject* lcount = CallPyObjMethod( leaf, "GetLeafCount" );
         PyObject* length = CallPyObjMethod( leaf, "GetLenStatic" );

         if ( lcount == Py_None && PyInt_AS_LONG( length ) <= 1 ) {
            value = CallPyObjMethod( leaf, "GetValue" );
         } else {
            PyObject* ptr = CallPyObjMethod( leaf, "GetValuePointer" );
            void* arr = PyLong_AsVoidPtr( ptr );
            Py_DECREF( ptr );

            PyObject* tname = CallPyObjMethod( leaf, "GetTypeName" );
            std::string stname( PyString_AS_STRING( tname ) );

            BufFac_t* fac = BufFac_t::Instance();
            PyObject* scb = PyObject_GetAttrString( leaf, const_cast< char* >( "GetNdata" ) );

         // TODO: use Converter::FromMemory instead
            Utility::EDataType eType = Utility::EffectiveType( stname );
            if ( eType == Utility::kLong )
               value = fac->PyBuffer_FromMemory( (long*) arr, scb );
            else if ( eType == Utility::kInt )
               value = fac->PyBuffer_FromMemory( (int*) arr, scb );
            else if ( eType == Utility::kDouble )
               value = fac->PyBuffer_FromMemory( (double*) arr, scb );
            else if ( eType == Utility::kFloat )
               value = fac->PyBuffer_FromMemory( (float*) arr, scb );

            Py_DECREF( scb );

         // we're working with addresses: cache result
            PyObject_SetAttr( self, name, value );
         }

         Py_DECREF( length );
         Py_DECREF( lcount );
         Py_DECREF( leaf );

         return value;
      }

   // confused
      Py_DECREF( leaf );
      char txt[ 256 ];
      sprintf( txt, "no such attribute \'%s\'", PyString_AsString( name ) );
      PyErr_SetString( PyExc_AttributeError, txt );
      return 0;
   }

//____________________________________________________________________________
   class TTreeBranch : public PyCallable {
   public:
      TTreeBranch( MethodProxy* org ) { Py_INCREF( org ); m_org = org; }
      TTreeBranch( const TTreeBranch& t ) : PyCallable( t ) { Py_INCREF( t.m_org ); m_org = t.m_org; }
      TTreeBranch& operator=( const TTreeBranch& t )
      {
         if ( &t != this ) {
            Py_INCREF( t.m_org );
            m_org = t.m_org;
         }
         return *this;
      }
      ~TTreeBranch() { Py_DECREF( m_org ); m_org = 0; }

   public:
      virtual PyObject* GetDocString()
      {
         return PyString_FromString( "TBranch* TTree::Branch( ... )" );
      }

      virtual PyObject* operator()( ObjectProxy* self, PyObject* args, PyObject* kwds )
      {
      // acceptable signatures:
      //   ( const char*, void*, const char*, Int_t = 32000 )
      //   ( const char*, const char*, T**, Int_t = 32000, Int_t = 99 )
      //   ( const char*, T**, Int_t = 32000, Int_t = 99 ) 
         int argc = PyTuple_GET_SIZE( args );

         if ( 2 <= argc ) {
            TTree* tree = (TTree*)self->ObjectIsA()->DynamicCast( TTree::Class(), self->GetObject() );

            if ( ! tree ) {
               PyErr_SetString( PyExc_TypeError,
                  "TTree::Branch must be called with a TTree instance as first argument" );
               return 0;
            }

            PyObject *name = 0, *clName = 0, *leaflist = 0;
            PyObject *address = 0;
            PyObject *bufsize = 0, *splitlevel = 0;

         // try: ( const char*, void*, const char*, Int_t = 32000 )
            if ( PyArg_ParseTuple( args, const_cast< char* >( "SOS|O!:Branch" ),
                    &name, &address, &leaflist, &PyInt_Type, &bufsize ) ) {

               void* buf = 0;
               if ( ObjectProxy_Check( address ) )
                  buf = (void*)((ObjectProxy*)address)->GetObject();
               else
                  Utility::GetBuffer( address, '*', 1, buf, false );
 
               if ( buf != 0 ) {
                  TBranch* branch = 0;
                  if ( argc == 4 ) {
                     branch = tree->Branch( PyString_AS_STRING( name ), buf,
                        PyString_AS_STRING( leaflist ), PyInt_AS_LONG( bufsize ) );
                  } else {
                     branch = tree->Branch(
                        PyString_AS_STRING( name ), buf, PyString_AS_STRING( leaflist ) );
                  }

                  return BindRootObject( branch, TBranch::Class() );
               }

            }
            PyErr_Clear();

         // try: ( const char*, const char*, T**, Int_t = 32000, Int_t = 99 )
         //  or: ( const char*,              T**, Int_t = 32000, Int_t = 99 ) 
            bool bIsMatch = false;
            if ( PyArg_ParseTuple( args, const_cast< char* >( "SSO|O!O!:Branch" ),
                    &name, &clName, &address, &PyInt_Type, &bufsize, &PyInt_Type, &splitlevel ) ) {
               bIsMatch = true;
            } else {
               PyErr_Clear();
               if ( PyArg_ParseTuple( args, const_cast< char* >( "SO|O!O!" ),
                       &name, &address, &PyInt_Type, &bufsize, &PyInt_Type, &splitlevel ) )
                  bIsMatch = true;
               else
                  PyErr_Clear();
            }

            if ( bIsMatch == true ) {
               std::string klName = clName ? PyString_AS_STRING( clName ) : "";
               void* buf = 0;

               if ( ObjectProxy_Check( address ) ) {
                  if ( ((ObjectProxy*)address)->fFlags & ObjectProxy::kIsReference )
                     buf = (void*)((ObjectProxy*)address)->fObject;
                  else
                     buf = (void*)&((ObjectProxy*)address)->fObject;
                  
                  if ( ! clName ) {
                     klName = ((ObjectProxy*)address)->ObjectIsA()->GetName();
                     argc += 1;
                  }
               } else
                  Utility::GetBuffer( address, '*', 1, buf, false );

               if ( buf != 0 && klName != "" ) {
                  TBranch* branch = 0;
                  if ( argc == 3 ) {
                     branch = tree->Branch( PyString_AS_STRING( name ), klName.c_str(), buf );
                  } else if ( argc == 4 ) {
                     branch = tree->Branch( PyString_AS_STRING( name ), klName.c_str(), buf,
                        PyInt_AS_LONG( bufsize ) );
                  } else if ( argc == 5 ) {
                     branch = tree->Branch( PyString_AS_STRING( name ), klName.c_str(), buf,
                        PyInt_AS_LONG( bufsize ), PyInt_AS_LONG( splitlevel ) );
                  }

                  return BindRootObject( branch, TBranch::Class() );
               }
            }
         }

      // still here? Then call original Branch() to reach the other overloads:
         Py_INCREF( (PyObject*)self );
         m_org->fSelf = self;
         PyObject* result = PyObject_Call( (PyObject*)m_org, args, kwds );
         m_org->fSelf = 0;
         Py_DECREF( (PyObject*)self );

         return result;
      }

   private:
      MethodProxy* m_org;
   };

} // namespace PyROOT


namespace {

// for convenience
   using namespace PyROOT;

//- TFN behaviour ------------------------------------------------------------
   std::map< int, std::pair< PyObject*, int > > gPyObjectCallbacks;
   typedef std::pair< PyObject*, int > CallInfo_t;

   int gLastTag = 99;
{'fraction_non_alphanumeric': 0.13747369948487267, 'fraction_numerical': 0.007081186969455126, 'mean_word_length': 1.9132566790666217, 'pattern_counts': {'":': 2, '<': 127, '<?xml version=': 0, '>': 253, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 80, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7286395', 'n_tokens_mistral': 19928, 'n_tokens_neox': 19725, 'n_words': 4890}