PyXR

c:\python24\lib\site-packages\win32 \ com \ makegw \ makegw.py



0001 """Utility functions for writing out gateway C++ files
0002 
0003   This module will generate a C++/Python binding for a specific COM
0004   interface.
0005   
0006   At this stage, no command line interface exists.  You must start Python, 
0007   import this module,  change to the directory where the generated code should
0008   be written, and run the public function.
0009   
0010   This module is capable of generating both 'Interfaces' (ie, Python
0011   client side support for the interface) and 'Gateways' (ie, Python
0012   server side support for the interface).  Many COM interfaces are useful
0013   both as Client and Server.  Other interfaces, however, really only make
0014   sense to implement one side or the other.  For example, it would be pointless
0015   for Python to implement Server side for 'IRunningObjectTable', unless we were
0016   implementing core COM for an operating system in Python (hey - now there's an idea!)
0017   
0018   Most COM interface code is totally boiler-plate - it consists of
0019   converting arguments, dispatching the call to Python, and processing
0020   any result values.
0021   
0022   This module automates the generation of such code.  It has the ability to
0023   parse a .H file generated by the MIDL tool (ie, almost all COM .h files)
0024   and build almost totally complete C++ code.
0025   
0026   The module understands some of the well known data types, and how to
0027   convert them.  There are only a couple of places where hand-editing is
0028   necessary, as detailed below:
0029 
0030   unsupported types -- If a type is not known, the generator will
0031   pretty much ignore it, but write a comment to the generated code.  You
0032   may want to add custom support for this type.  In some cases, C++ compile errors
0033   will result.  These are intentional - generating code to remove these errors would
0034   imply a false sense of security that the generator has done the right thing.
0035 
0036   other return policies -- By default, Python never sees the return SCODE from
0037   a COM function.  The interface usually returns None if OK, else a COM exception
0038   if "FAILED(scode)" is TRUE.  You may need to change this if:
0039   * EXCEPINFO is passed to the COM function.  This is not detected and handled
0040   * For some reason Python should always see the result SCODE, even if it
0041     did fail or succeed.  For example, some functions return a BOOLEAN result
0042     in the SCODE, meaning Python should always see it.
0043   * FAILED(scode) for the interface still has valid data to return (by default,
0044     the code generated does not process the return values, and raise an exception
0045     to Python/COM
0046   
0047 """
0048 
0049 import regsub
0050 import string
0051 import makegwparse
0052 
0053 def make_framework_support(header_file_name, interface_name, bMakeInterface = 1, bMakeGateway = 1):
0054   """Generate C++ code for a Python Interface and Gateway
0055   
0056   header_file_name -- The full path to the .H file which defines the interface.
0057   interface_name -- The name of the interface to search for, and to generate.
0058   bMakeInterface = 1 -- Should interface (ie, client) support be generated.
0059   bMakeGatewayInterface = 1 -- Should gateway (ie, server) support be generated.
0060   
0061   This method will write a .cpp and .h file into the current directory,
0062   (using the name of the interface to build the file name.
0063   
0064   """
0065   fin=open(header_file_name)
0066   try:
0067     interface = makegwparse.parse_interface_info(interface_name, fin)
0068   finally:
0069     fin.close()
0070     
0071   if bMakeInterface and bMakeGateway:
0072         desc = "Interface and Gateway"
0073   elif bMakeInterface and not bMakeGateway:
0074         desc = "Interface"
0075   else:
0076         desc = "Gateway"
0077   if interface.name[:5]=="IEnum": # IEnum - use my really simple template-based one
0078     import win32com.makegw.makegwenum
0079     ifc_cpp_writer = win32com.makegw.makegwenum._write_enumifc_cpp
0080     gw_cpp_writer = win32com.makegw.makegwenum._write_enumgw_cpp
0081   else: # Use my harder working ones.
0082     ifc_cpp_writer = _write_ifc_cpp
0083     gw_cpp_writer = _write_gw_cpp
0084         
0085   fout=open("Py%s.cpp" % interface.name, "w")
0086   try:
0087     fout.write(\
0088 '''\
0089 // This file implements the %s %s for Python.
0090 // Generated by makegw.py
0091 
0092 #include "shell_pch.h"
0093 ''' % (interface.name, desc))
0094 #    if bMakeGateway:
0095 #      fout.write('#include "PythonCOMServer.h"\n')
0096 #    if interface.base not in ["IUnknown", "IDispatch"]:
0097 #      fout.write('#include "Py%s.h"\n' % interface.base)
0098     fout.write('#include "Py%s.h"\n\n// @doc - This file contains autoduck documentation\n' % interface.name)
0099     if bMakeInterface: ifc_cpp_writer(fout, interface)
0100     if bMakeGateway: gw_cpp_writer(fout, interface)
0101   finally:
0102     fout.close()
0103   fout=open("Py%s.h" % interface.name, "w")
0104   try:
0105     fout.write(\
0106 '''\
0107 // This file declares the %s %s for Python.
0108 // Generated by makegw.py
0109 ''' % (interface.name, desc))
0110 
0111     if bMakeInterface: _write_ifc_h(fout, interface)
0112     if bMakeGateway: _write_gw_h(fout, interface)
0113   finally:
0114     fout.close()
0115 
0116 ###########################################################################
0117 #
0118 # INTERNAL FUNCTIONS
0119 #
0120 #
0121 
0122 def _write_ifc_h(f, interface):
0123   f.write(\
0124 '''\
0125 // ---------------------------------------------------
0126 //
0127 // Interface Declaration
0128 
0129 class Py%s : public Py%s
0130 {
0131 public:
0132         MAKE_PYCOM_CTOR(Py%s);
0133         static %s *GetI(PyObject *self);
0134         static PyComTypeObject type;
0135 
0136         // The Python methods
0137 ''' % (interface.name, interface.base, interface.name, interface.name))
0138   for method in interface.methods:
0139     f.write('\tstatic PyObject *%s(PyObject *self, PyObject *args);\n' % method.name)
0140   f.write(\
0141 '''\
0142 
0143 protected:
0144         Py%s(IUnknown *pdisp);
0145         ~Py%s();
0146 };
0147 ''' % (interface.name, interface.name))
0148 
0149 def _write_ifc_cpp(f, interface):
0150   name = interface.name
0151   f.write(\
0152 '''\
0153 // ---------------------------------------------------
0154 //
0155 // Interface Implementation
0156 
0157 Py%(name)s::Py%(name)s(IUnknown *pdisp):
0158         Py%(base)s(pdisp)
0159 {
0160         ob_type = &type;
0161 }
0162 
0163 Py%(name)s::~Py%(name)s()
0164 {
0165 }
0166 
0167 /* static */ %(name)s *Py%(name)s::GetI(PyObject *self)
0168 {
0169         return (%(name)s *)Py%(base)s::GetI(self);
0170 }
0171 
0172 ''' % (interface.__dict__))
0173 
0174   ptr = regsub.gsub('[a-z]', '', interface.name)
0175   strdict = {'interfacename':interface.name, 'ptr': ptr}
0176   for method in interface.methods:
0177     strdict['method'] = method.name
0178     f.write(\
0179 '''\
0180 // @pymethod |Py%(interfacename)s|%(method)s|Description of %(method)s.
0181 PyObject *Py%(interfacename)s::%(method)s(PyObject *self, PyObject *args)
0182 {
0183         %(interfacename)s *p%(ptr)s = GetI(self);
0184         if ( p%(ptr)s == NULL )
0185                 return NULL;
0186 ''' % strdict)
0187     argsParseTuple = argsCOM = formatChars = codePost = \
0188                      codePobjects = codeCobjects = cleanup = cleanup_gil = ""
0189     needConversion = 0
0190 #    if method.name=="Stat": import win32dbg;win32dbg.brk()
0191     for arg in method.args:
0192       try:
0193         argCvt = makegwparse.make_arg_converter(arg)
0194         if arg.HasAttribute("in"):
0195           val = argCvt.GetFormatChar()
0196           if val:
0197             f.write ('\t' + argCvt.GetAutoduckString() + "\n")
0198             formatChars = formatChars + val
0199             argsParseTuple = argsParseTuple + ", " + argCvt.GetParseTupleArg()
0200             codePobjects = codePobjects + argCvt.DeclareParseArgTupleInputConverter()
0201             codePost = codePost + argCvt.GetParsePostCode()
0202             needConversion = needConversion or argCvt.NeedUSES_CONVERSION()
0203             cleanup = cleanup + argCvt.GetInterfaceArgCleanup()
0204             cleanup_gil = cleanup_gil + argCvt.GetInterfaceArgCleanupGIL()
0205         comArgName, comArgDeclString = argCvt.GetInterfaceCppObjectInfo()
0206         if comArgDeclString: # If we should declare a variable
0207           codeCobjects = codeCobjects + "\t%s;\n" % (comArgDeclString)
0208         argsCOM = argsCOM + ", " + comArgName
0209       except makegwparse.error_not_supported, why:
0210         f.write('// *** The input argument %s of type "%s" was not processed ***\n//     Please check the conversion function is appropriate and exists!\n' % (arg.name, arg.raw_type))
0211 
0212         f.write('\t%s %s;\n\tPyObject *ob%s;\n' % (arg.type, arg.name, arg.name))
0213         f.write('\t// @pyparm <o Py%s>|%s||Description for %s\n' % (arg.type, arg.name, arg.name))
0214         codePost = codePost + '\tif (bPythonIsHappy && !PyObject_As%s( ob%s, &%s )) bPythonIsHappy = FALSE;\n' % (arg.type, arg.name, arg.name)
0215 
0216         formatChars = formatChars + "O"
0217         argsParseTuple = argsParseTuple + ", &ob%s" % (arg.name)
0218 
0219         argsCOM = argsCOM + ", " + arg.name
0220         cleanup = cleanup + "\tPyObject_Free%s(%s);\n" % (arg.type, arg.name)
0221 
0222     if needConversion: f.write("\tUSES_CONVERSION;\n")
0223     f.write(codePobjects);
0224     f.write(codeCobjects);
0225     f.write('\tif ( !PyArg_ParseTuple(args, "%s:%s"%s) )\n\t\treturn NULL;\n' % (formatChars, method.name, argsParseTuple))
0226     if codePost:
0227       f.write('\tBOOL bPythonIsHappy = TRUE;\n')
0228       f.write(codePost);
0229       f.write('\tif (!bPythonIsHappy) return NULL;\n')
0230     strdict['argsCOM'] = argsCOM[1:]
0231     strdict['cleanup'] = cleanup
0232     strdict['cleanup_gil'] = cleanup_gil
0233     f.write(\
0234 '''        HRESULT hr;
0235         PY_INTERFACE_PRECALL;
0236         hr = p%(ptr)s->%(method)s(%(argsCOM)s );
0237 %(cleanup)s
0238         PY_INTERFACE_POSTCALL;
0239 %(cleanup_gil)s
0240         if ( FAILED(hr) )
0241                 return PyCom_BuildPyException(hr, p%(ptr)s, IID_%(interfacename)s );
0242 ''' % strdict)
0243     codePre = codePost = formatChars = codeVarsPass = codeDecl = ""
0244     for arg in method.args:
0245       if not arg.HasAttribute("out"):
0246         continue
0247       try:
0248         argCvt =  makegwparse.make_arg_converter(arg)
0249         formatChar = argCvt.GetFormatChar()
0250         if formatChar:
0251           formatChars = formatChars + formatChar
0252           codePre = codePre + argCvt.GetBuildForInterfacePreCode()
0253           codePost = codePost + argCvt.GetBuildForInterfacePostCode()
0254           codeVarsPass = codeVarsPass + ", " + argCvt.GetBuildValueArg()
0255           codeDecl = codeDecl + argCvt.DeclareParseArgTupleInputConverter()
0256       except makegwparse.error_not_supported, why:
0257         f.write('// *** The output argument %s of type "%s" was not processed ***\n//     %s\n' % (arg.name, arg.raw_type, why))
0258         continue
0259     if formatChars:
0260       f.write('%s\n%s\tPyObject *pyretval = Py_BuildValue("%s"%s);\n%s\treturn pyretval;' % (codeDecl, codePre, formatChars, codeVarsPass, codePost))
0261     else:
0262       f.write('\tPy_INCREF(Py_None);\n\treturn Py_None;\n')
0263     f.write('\n}\n\n')
0264 
0265   f.write ('// @object Py%s|Description of the interface\n' % (name))
0266   f.write('static struct PyMethodDef Py%s_methods[] =\n{\n' % name)
0267   for method in interface.methods:
0268     f.write('\t{ "%s", Py%s::%s, 1 }, // @pymeth %s|Description of %s\n' % (method.name, interface.name, method.name, method.name, method.name))
0269 
0270   interfacebase = interface.base
0271   f.write('''\
0272         { NULL }
0273 };
0274 
0275 PyComTypeObject Py%(name)s::type("Py%(name)s",
0276                 &Py%(interfacebase)s::type,
0277                 sizeof(Py%(name)s),
0278                 Py%(name)s_methods,
0279                 GET_PYCOM_CTOR(Py%(name)s));
0280 ''' % locals())
0281 
0282 def _write_gw_h(f, interface):
0283   if interface.name[0] == "I":
0284     gname = 'PyG' + interface.name[1:]
0285   else:
0286     gname = 'PyG' + interface.name
0287   name = interface.name
0288   if interface.base == "IUnknown" or interface.base == "IDispatch":
0289     base_name = "PyGatewayBase"
0290   else:
0291     if interface.base[0] == "I":
0292       base_name = 'PyG' + interface.base[1:]
0293     else:
0294       base_name = 'PyG' + interface.base
0295   f.write(\
0296 '''\
0297 // ---------------------------------------------------
0298 //
0299 // Gateway Declaration
0300 
0301 class %s : public %s, public %s
0302 {
0303 protected:
0304         %s(PyObject *instance) : %s(instance) { ; }
0305         PYGATEWAY_MAKE_SUPPORT2(%s, %s, IID_%s, %s)
0306 
0307 ''' % (gname, base_name, name, gname, base_name, gname, name, name, base_name))
0308   if interface.base != "IUnknown":
0309     f.write("\t// %s\n\t// *** Manually add %s method decls here\n\n" % (interface.base, interface.base))
0310   else:
0311     f.write('\n\n')
0312 
0313   f.write("\t// %s\n" % name)
0314 
0315   for method in interface.methods:
0316     f.write('\tSTDMETHOD(%s)(\n' % method.name)
0317     if method.args:
0318       for arg in method.args[:-1]:
0319         f.write("\t\t%s,\n" % (arg.GetRawDeclaration()))
0320       arg = method.args[-1]
0321       f.write("\t\t%s);\n\n" % (arg.GetRawDeclaration()))
0322     else:
0323       f.write('\t\tvoid);\n\n')
0324 
0325   f.write('};\n')
0326   f.close()
0327 
0328 def _write_gw_cpp(f, interface):
0329   if interface.name[0] == "I":
0330     gname = 'PyG' + interface.name[1:]
0331   else:
0332     gname = 'PyG' + interface.name
0333   name = interface.name
0334   if interface.base == "IUnknown" or interface.base == "IDispatch":
0335     base_name = "PyGatewayBase"
0336   else:
0337     if interface.base[0] == "I":
0338       base_name = 'PyG' + interface.base[1:]
0339     else:
0340       base_name = 'PyG' + interface.base
0341   f.write('''\
0342 // ---------------------------------------------------
0343 //
0344 // Gateway Implementation
0345 ''' % {'name':name, 'gname':gname, 'base_name':base_name})
0346 
0347   for method in interface.methods:
0348     f.write(\
0349 '''\
0350 STDMETHODIMP %s::%s(
0351 ''' % (gname, method.name))
0352 
0353     if method.args:
0354       for arg in method.args[:-1]:
0355         inoutstr = string.join(arg.inout, '][')
0356         f.write("\t\t/* [%s] */ %s,\n" % (inoutstr, arg.GetRawDeclaration()))
0357         
0358       arg = method.args[-1]
0359       inoutstr = string.join(arg.inout, '][')
0360       f.write("\t\t/* [%s] */ %s)\n" % (inoutstr, arg.GetRawDeclaration()))
0361     else:
0362       f.write('\t\tvoid)\n')
0363 
0364     f.write("{\n\tPY_GATEWAY_METHOD;\n")
0365     cout = 0
0366     codePre = codePost = codeVars = ""
0367     argStr = ""
0368     needConversion = 0
0369     formatChars = ""
0370     if method.args:
0371       for arg in method.args:
0372         if arg.HasAttribute("out"):
0373           cout = cout + 1
0374           if arg.indirectionLevel ==2 :
0375             f.write("\tif (%s==NULL) return E_POINTER;\n" % arg.name)
0376         if arg.HasAttribute("in"):
0377           try:
0378             argCvt = makegwparse.make_arg_converter(arg)
0379             argCvt.SetGatewayMode()
0380             formatchar = argCvt.GetFormatChar();
0381             needConversion = needConversion or argCvt.NeedUSES_CONVERSION()
0382 
0383             if formatchar:
0384               formatChars = formatChars + formatchar
0385               codeVars = codeVars + argCvt.DeclareParseArgTupleInputConverter()
0386               argStr = argStr + ", " + argCvt.GetBuildValueArg()
0387             codePre = codePre + argCvt.GetBuildForGatewayPreCode()
0388             codePost = codePost + argCvt.GetBuildForGatewayPostCode()
0389           except makegwparse.error_not_supported, why:
0390             f.write('// *** The input argument %s of type "%s" was not processed ***\n//   - Please ensure this conversion function exists, and is appropriate\n//   - %s\n' % (arg.name, arg.raw_type, why))
0391             f.write('\tPyObject *ob%s = PyObject_From%s(%s);\n' % (arg.name, arg.type, arg.name))
0392             f.write('\tif (ob%s==NULL) return PyCom_HandlePythonFailureToCOM();\n' % arg.name)
0393             codePost = codePost + "\tPy_DECREF(ob%s);\n" % arg.name
0394             formatChars = formatChars + "O"
0395             argStr = argStr + ", ob%s" % (arg.name)
0396     
0397     if needConversion: f.write('\tUSES_CONVERSION;\n')
0398     f.write(codeVars)
0399     f.write(codePre)
0400     if cout:
0401       f.write("\tPyObject *result;\n")
0402       resStr = "&result"
0403     else:
0404       resStr = "NULL"
0405       
0406     if formatChars:
0407       fullArgStr = '%s, "%s"%s' % (resStr, formatChars, argStr)
0408     else:
0409       fullArgStr = resStr
0410 
0411     f.write('\tHRESULT hr=InvokeViaPolicy("%s", %s);\n' % (method.name, fullArgStr))
0412     f.write(codePost)
0413     if cout:
0414       f.write("\tif (FAILED(hr)) return hr;\n")
0415       f.write("\t// Process the Python results, and convert back to the real params\n")
0416       # process the output arguments.
0417       formatChars = codePobjects = codePost = argsParseTuple = ""
0418       needConversion = 0
0419       for arg in method.args:
0420         if not arg.HasAttribute("out"):
0421           continue
0422         try:
0423           argCvt = makegwparse.make_arg_converter(arg)
0424           argCvt.SetGatewayMode()
0425           val = argCvt.GetFormatChar()
0426           if val:
0427             formatChars = formatChars + val
0428             argsParseTuple = argsParseTuple + ", " + argCvt.GetParseTupleArg()
0429             codePobjects = codePobjects + argCvt.DeclareParseArgTupleInputConverter()
0430             codePost = codePost + argCvt.GetParsePostCode()
0431             needConversion = needConversion or argCvt.NeedUSES_CONVERSION()
0432         except makegwparse.error_not_supported, why:
0433           f.write('// *** The output argument %s of type "%s" was not processed ***\n//     %s\n' % (arg.name, arg.raw_type, why))
0434 
0435       if formatChars: # If I have any to actually process.
0436         if len(formatChars)==1:
0437           parseFn = "PyArg_Parse"
0438         else:
0439           parseFn = "PyArg_ParseTuple"
0440         if codePobjects: f.write(codePobjects)
0441         f.write('\tif (!%s(result, "%s" %s)) return PyCom_HandlePythonFailureToCOM(/*pexcepinfo*/);\n' % (parseFn, formatChars, argsParseTuple))
0442       if codePost: 
0443         f.write('\tBOOL bPythonIsHappy = TRUE;\n')
0444         f.write(codePost)
0445         f.write('\tif (!bPythonIsHappy) hr = PyCom_HandlePythonFailureToCOM(/*pexcepinfo*/);\n')
0446       f.write('\tPy_DECREF(result);\n');
0447     f.write('\treturn hr;\n}\n\n')
0448   
0449 def test():
0450 # make_framework_support("d:\\msdev\\include\\objidl.h", "ILockBytes")
0451         make_framework_support("d:\\msdev\\include\\objidl.h", "IStorage")
0452 # make_framework_support("d:\\msdev\\include\\objidl.h", "IEnumSTATSTG")
0453 

Generated by PyXR 0.9.4
SourceForge.net Logo