Changeset 25

Show
Ignore:
Timestamp:
01/08/07 16:48:50 (3 years ago)
Author:
jjr8
Message:

Implemented support for exposing GeoEco? classes as COM objects (Trac ticket #32). There are still several minor things that are not done; I will open separate tickets for these.

Reorganized the GeoEco?.ArcGIS and GeoEco?.Logging code into classes that are exposed as COM objects, so COM callers can initialize the logging system and pass in their own instance of the ArcGIS geoprocessor.

Added metadata to the GeoEco?.ArcGIS and GeoEco?.Logging modules. The metadata is still not complete for either module.

Changed the default logging level to DEBUG for the duration of the development cycle.

Eliminated the GeoEco?.Tool module and the requirement that classes derive from this module in order to be exposed through ArcGIS or COM. This requirement was not really necessary. Now the only requirement is that a class be instrumented with appropriate metadata.

Pursuant to the change above, I changed some error messages to refer to GeoEco? "functions" rather than "tools". From now on, "tool" is probably best used only when discussing something specific to ArcGIS.

Fixed a bug in GeoEco?.Metadata.PropertyMetadata?.ValidatePropertyAssignment? that caused Python to silently exit when you used a classmethod for the fset method of a property.

Added IsReadOnly? flag to the XML representation of property metadata.

Fixed bugs in GeoEco?.Metadata.MethodMetadata?._GetHasDefault and _GetDefault that caused them to assert when invoked for a varargs or varkw argument.

Added AnyPythonInstance?, PythonSequence?, and PythonTuple? to Types.py.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • MGET/Trunk/PythonPackage/BuildWin32.cmd

    r11 r25  
    11@echo off 
     2if not "%DevEnvDir%"=="" goto Build 
     3call "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\vsvars32.bat" 
     4:Build 
    25cmd.exe /C setup.py bdist_wininst --install-script=GeoEcoPostInstall.py 
  • MGET/Trunk/PythonPackage/setup.py

    r16 r25  
    1414import xml.dom.minidom 
    1515 
     16 
    1617# Helper functions 
    1718 
     
    4950            AppendMetadataXMLForModule(submoduleName, p, submodulesNode.appendChild(document.createElement(u'ModuleMetadata')), document) 
    5051 
     52 
    5153def WriteArcGISWrapperScriptForMethod(moduleName, method, scriptsDir): 
    5254    u"""Write a Python wrapper script to scriptsDir for invoking method from an ArcGIS toolbox.""" 
     
    5456    import GeoEco.ArcGIS 
    5557     
    56     GeoEco.ArcGIS.ValidateToolMetadata(moduleName, method.Class.Name, method.Name) 
     58    GeoEco.ArcGIS.ValidateMethodMetadataForExposureAsArcGISTool(moduleName, method.Class.Name, method.Name) 
    5759    fileName = os.path.join(scriptsDir, u'%s.%s.py' % (method.Class.Name, method.Name)) 
    5860    print(u'  Writing \"%s\"' % fileName) 
    5961    f = file(fileName, u'w') 
    6062    f.write('import GeoEco.ArcGIS\n') 
    61     f.write('GeoEco.ArcGIS.ExecuteToolFromCommandLine(\'%s\', \'%s\', \'%s\')\n' % (str(moduleName), str(method.Class.Name), str(method.Name))) 
     63    f.write('GeoEco.ArcGIS.ExecuteMethodFromCommandLineAsArcGISTool(\'%s\', \'%s\', \'%s\')\n' % (str(moduleName), str(method.Class.Name), str(method.Name))) 
    6264    f.close() 
    6365 
     66 
    6467def WriteArcGISWrapperScriptsForMethodsInModule(moduleName, modulePath, scriptsDir): 
    65     u"""For all methods flagged for exposure as ArcGIS tools, write Python wrapper scripts to scriptsDir for invoking these methods from an ArcGIS toolbox. If this is a package, recurse down subpackages and submodules.""" 
     68    u"""For all methods of all classes in the specified module, if the method is flagged for exposure as ArcGIS tool, write a Python wrapper script to scriptsDir for invoking the method from an ArcGIS toolbox. If the specified module is a package, recurse down subpackages and submodules.""" 
    6669 
    6770    import GeoEco.Metadata 
     
    9093                continue 
    9194            WriteArcGISWrapperScriptsForMethodsInModule(submoduleName, p, scriptsDir) 
     95 
     96 
     97def GetIDispatchClassesInModule(moduleName, modulePath): 
     98    u"""Return all classes in the specified module that are flagged for exposure using the Microsoft COM IDispatch interface. If the specified module is a package, recurse down subpackages and submodules.""" 
     99 
     100    import GeoEco.Metadata 
     101 
     102    # Add all classes from the specified module. 
     103 
     104    iDispatchClasses = []     
     105     
     106    if isinstance(sys.modules[moduleName].__doc__, GeoEco.Metadata.DynamicDocString) and isinstance(sys.modules[moduleName].__doc__.Obj, GeoEco.Metadata.ModuleMetadata): 
     107        for (className, cls) in sys.modules[moduleName].__dict__.items(): 
     108            if inspect.isclass(cls) and isinstance(cls.__doc__, GeoEco.Metadata.DynamicDocString) and isinstance(cls.__doc__.Obj, GeoEco.Metadata.ClassMetadata) and cls.__doc__.Obj.Module == sys.modules[moduleName].__doc__.Obj and cls.__doc__.Obj.IsExposedByIDispatch: 
     109                iDispatchClasses.append(cls) 
     110 
     111    # If the specified module is a directory, then it is a Python package. 
     112    # Process the submodules and subpackages. 
     113 
     114    if os.path.isdir(modulePath): 
     115        paths = glob.glob(os.path.join(modulePath, u'*')) 
     116        for p in paths: 
     117            if os.path.isfile(p) and p.endswith(u'.py') and os.path.basename(p) != u'__init__.py': 
     118                submoduleName = moduleName + u'.' + os.path.basename(p)[:-3] 
     119            elif os.path.isdir(p) and p.lower() != u'.svn' and os.path.isfile(os.path.join(p, u'__init__.py')): 
     120                submoduleName = moduleName + u'.' + os.path.basename(p) 
     121            else: 
     122                continue 
     123            moreIDispatchClasses = GetIDispatchClassesInModule(submoduleName, p) 
     124            iDispatchClasses.extend(moreIDispatchClasses) 
     125 
     126    # Return a list of classes that are flagged for exposure using IDispatch, so 
     127    # the caller does not have to enumerate them again. 
     128 
     129    return iDispatchClasses     
     130 
    92131 
    93132def CopyTree(src, dest): 
     
    220259        WriteArcGISWrapperScriptsForMethodsInModule(u'GeoEco', sys.modules[u'GeoEco'].__path__[0], os.path.join(arcGISDir, u'Scripts')) 
    221260 
     261        # TODO: validate that the same tool name is not exposed twice (Trac ticket #29) 
     262 
    222263        # Create the ArcGIS toolbox. 
    223264 
     
    244285        packageData[u'GeoEco'].append(os.path.join(u'ArcGISToolbox', u'Scripts', u'*.*')) 
    245286 
     287    # If running on Windows, generate the COM type library for classes that can 
     288    # be invoked through COM Automation (IDispatch) interfaces. 
     289 
     290    if sys.platform.lower() == u'win32': 
     291        print(u'') 
     292        print(u'********************************************************************************') 
     293        print(u'* Generating the Microsoft COM type library') 
     294        print(u'********************************************************************************') 
     295        print(u'') 
     296 
     297        # Open the IDL file for writing. 
     298 
     299        comDir = os.path.join(srcDir, u'GeoEco', u'COM') 
     300        idlFilePath = os.path.join(comDir, u'GeoEco.idl') 
     301        print(u'Opening %s for writing.' % idlFilePath) 
     302        idlFile = file(idlFilePath, u'w') 
     303        idlFile.write('// IDL for GeoEco Type Library\n') 
     304        idlFile.write('//\n') 
     305        idlFile.write('// GeoEco is a Python package that provides geospatial ecology tools.\n') 
     306        idlFile.write('// Many of these tools are exposed as COM classes. Each class has a\n') 
     307        idlFile.write('// ProgID beginning with "GeoEco." and exposes a dual interface.\n') 
     308        idlFile.write('// Late-binding clients such as script engines can invoke the classes\n') 
     309        idlFile.write('// through the IDispatch interface, while early-binding clients such\n') 
     310        idlFile.write('// as C# can import the GeoEco Type Library and invoke the classes\n') 
     311        idlFile.write('// through the more-efficient vtable interface.\n') 
     312        idlFile.write('//\n') 
     313        idlFile.write('// This IDL file defines all of the interfaces and classes exported by\n') 
     314        idlFile.write('// the GeoEco type library.\n') 
     315        idlFile.write('\n') 
     316        idlFile.write('import "oaidl.idl";\n') 
     317        idlFile.write('import "ocidl.idl";\n') 
     318        idlFile.write('\n') 
     319 
     320        # Obtain a list of all of the classes flagged for exposure through COM 
     321        # IDispatch. Validate the classes' metadata. Also validate that each 
     322        # class has a unique name across the entire set of classes, regardless 
     323        # of which module it appears in. This means developers cannot 
     324        # disambiguate classes with the same names using the Python module 
     325        # hierarchy. The benefit of this restriction is that we don't have to 
     326        # generate multiple type libraries, or a single type library that 
     327        # prepends the full list of module names to the class name. 
     328 
     329        print(u'Validating classes flagged for exposure by IDispatch:') 
     330 
     331        import pythoncom 
     332        import GeoEco.COM 
     333 
     334        classDict = {} 
     335        guidDict = {} 
     336 
     337        iDispatchClasses = GetIDispatchClassesInModule(u'GeoEco', sys.modules[u'GeoEco'].__path__[0]) 
     338        for cls in iDispatchClasses: 
     339            GeoEco.COM.ValidateClassMetadata(cls) 
     340            classMetadata = cls.__doc__.Obj 
     341             
     342            if classDict.has_key(classMetadata.Name): 
     343                sys.exit(u'Two classes flagged for exposure by IDispatch have the same class name: %s.%s and %s.%s. To be exposed by IDispatch, each class must have a unique name, regardless of what module it appears in. Please change the name of one of these classes.' % (classMetadata.Module.Name, classMetadata.Name, classDict[classMetadata.Name].Module.Name, classDict[classMetadata.Name].Name)) 
     344            classDict[classMetadata.Name] = classMetadata 
     345             
     346            if guidDict.has_key(classMetadata.COMIID): 
     347                sys.exit(u'Two classes flagged for exposure by IDispatch are using the same GUID %s: %s.%s is using it for COMIID and %s.%s is using it for %s. To be exposed by IDispatch, each class must have a unique GUID for COMIID and COMCLSID. Here is a new unique GUID you can use: %s' % (classMetadata.COMIID, classMetadata.Module.Name, classMetadata.Name, guidDict[classMetadata.COMIID][1].Module.Name, guidDict[classMetadata.COMIID][1].Name, guidDict[classMetadata.COMIID][0], repr(unicode(pythoncom.CreateGuid())))) 
     348            guidDict[classMetadata.COMIID] = (u'COMIID', classMetadata) 
     349             
     350            if guidDict.has_key(classMetadata.COMCLSID): 
     351                sys.exit(u'Two classes flagged for exposure by IDispatch are using the same GUID %s: %s.%s is using it for COMCLSID and %s.%s is using it for %s. To be exposed by IDispatch, each class must have a unique GUID for COMIID and COMCLSID. Here is a new unique GUID you can use: %s' % (classMetadata.COMIID, classMetadata.Module.Name, classMetadata.Name, guidDict[classMetadata.COMIID][1].Module.Name, guidDict[classMetadata.COMIID][1].Name, guidDict[classMetadata.COMIID][0], repr(unicode(pythoncom.CreateGuid())))) 
     352            guidDict[classMetadata.COMIID] = (u'COMCLSID', classMetadata) 
     353             
     354            print('  %s.%s as ProgID %s' % (cls.__doc__.Obj.Module.Name, cls.__doc__.Obj.Name, cls.__doc__.Obj.COMVersionIndependentProgID)) 
     355 
     356        # Write the interface definitions to the IDL file. 
     357 
     358        print(u'Writing interface definitions to %s.' % idlFilePath) 
     359        for cls in iDispatchClasses: 
     360            idlFile.write(GeoEco.COM.GetIDLInterfaceDefinitionFromMetadata(cls.__doc__.Obj)) 
     361            idlFile.write('\n') 
     362 
     363        # Write type library header to the IDL file. 
     364 
     365        idlFile.write('[\n') 
     366        idlFile.write('    uuid(%s),\n' % GeoEco.COM.TypeLibraryGUID[1:-1]) 
     367        idlFile.write('    version(%i.%i),\n' % (GeoEco.COM.TypeLibraryVersion[0], GeoEco.COM.TypeLibraryVersion[1])) 
     368        idlFile.write('    helpstring("GeoEco %i.%i Type Library")\n' % (GeoEco.COM.TypeLibraryVersion[0], GeoEco.COM.TypeLibraryVersion[1]))     
     369        idlFile.write(']\n') 
     370        idlFile.write('library GeoEco\n') 
     371        idlFile.write('{\n') 
     372        idlFile.write('    importlib("stdole32.tlb");\n') 
     373        idlFile.write('    importlib("stdole2.tlb");\n') 
     374 
     375        # Write the coclass definitions to the IDL file. 
     376 
     377        for cls in iDispatchClasses: 
     378            idlFile.write('\n') 
     379            idlFile.write('    [\n') 
     380            idlFile.write('        uuid(%s),\n' % cls.__doc__.Obj.COMCLSID[1:-1]) 
     381            idlFile.write('        helpstring("%s Class")\n' % cls.__doc__.Obj.Name) 
     382            idlFile.write('    ]\n') 
     383            idlFile.write('    coclass %s\n' % cls.__doc__.Obj.Name) 
     384            idlFile.write('    {\n') 
     385            idlFile.write('        [default] interface I%s;\n' % cls.__doc__.Obj.Name) 
     386            idlFile.write('    };\n') 
     387 
     388        # Close the IDL file.         
     389 
     390        idlFile.write('};\n') 
     391        idlFile.close() 
     392 
     393        # Execute the MIDL compiler on the IDL file to produce the TLB file. 
     394 
     395        tlbFilePath = os.path.splitext(idlFilePath)[0] + u'.tlb'         
     396 
     397        args = [u'midl.exe', u'/out', os.path.dirname(idlFilePath), u'/tlb', tlbFilePath, u'/win32', idlFilePath] 
     398        print('Invoking %s' % ' '.join(args)) 
     399        try: 
     400            p = subprocess.Popen(args) 
     401        except WindowsError, e: 
     402            if e.errno == 2: 
     403                print(e.__class__.__name__ + u': ' + unicode(e)) 
     404                sys.exit(u'The most common cause for failure here is forgetting to register the Microsoft Visual Studio environment variables. This is usually best accomplished by running "C:\\Program Files\\Microsoft Visual Studio 8\\Common7\\Tools\\vsvars32.bat" prior to executing setup.py.') 
     405            else: 
     406                raise 
     407        retcode = p.wait() 
     408        if retcode != 0: 
     409            sys.exit(u'midl.exe failed and returned exit code %i.' % retcode) 
     410        for ext in [u'_i.c', u'_p.c', u'.h']: 
     411            if os.path.exists(os.path.splitext(idlFilePath)[0] + ext): 
     412                os.remove(os.path.splitext(idlFilePath)[0] + ext) 
     413        if os.path.exists(os.path.join(os.path.dirname(idlFilePath), u'dlldata.c')): 
     414            os.remove(os.path.join(os.path.dirname(idlFilePath), u'dlldata.c')) 
     415 
     416        packageData[u'GeoEco'].append(os.path.join(u'COM', u'*.*')) 
     417 
    246418    # If running on Windows, generate the postinstall script. 
    247419 
     
    251423        print(u'* Generating the Windows postinstall script') 
    252424        print(u'********************************************************************************') 
    253         print(u'')     
     425        print(u'') 
     426 
     427        # Generate the list of classes that need COM registration. 
     428         
     429        classesForCOMRegistration = '[' + ', '.join(map(lambda cls: '[' + repr(str(cls.__doc__.Obj.Module.Name)) + ', ' + repr(str(cls.__doc__.Obj.Name)) + ']', iDispatchClasses)) + ']' 
     430 
     431        # Write the install script. 
    254432 
    255433        postInstallFilePath = os.path.join(srcDir, u'GeoEcoPostInstall.py') 
     
    270448import GeoEco 
    271449geoEcoRoot = os.path.dirname(sys.modules['GeoEco'].__file__) 
     450 
     451############################################################################### 
     452#  INPUT DATA 
     453############################################################################### 
     454 
     455classesForCOMRegistration = """ + classesForCOMRegistration + """ 
    272456 
    273457############################################################################### 
     
    308492        import win32process 
    309493    except: 
    310         sys.exit('ERROR: Python for Windows Extensions (pywin32) is not installed. GeoEco requires this Python package. Please uninstall GeoEco, download the package from http://sourceforge.net/projects/pywin32, install it, and install GeoEco again.') 
     494        sys.exit('ERROR: Python for Windows Extensions (pywin32) is not installed. GeoEco requires this Python package. Please uninstall GeoEco, download pywin32 for your version of Python from http://sourceforge.net/projects/pywin32, install it, and install GeoEco again.') 
    311495 
    312496    # Ensure the ArcGIS is installed by reading the InstallDir from the registry. 
     
    366550    return True 
    367551 
     552def RegisterClassesAsCOMIDispatchServers(): 
     553 
     554    # Import pywin32 modules that we need. 
     555     
     556    try: 
     557        import pythoncom 
     558        import win32com.server.register 
     559    except: 
     560        sys.exit('ERROR: Python for Windows Extensions (pywin32) is not installed. GeoEco requires this Python package. Please uninstall GeoEco, download pywin32 for your version of Python from http://sourceforge.net/projects/pywin32, install it, and install GeoEco again.') 
     561 
     562    # Register the classes. 
     563 
     564    success = True 
     565    import GeoEco.COM 
     566    for [moduleName, className] in classesForCOMRegistration: 
     567        if not GeoEco.COM.RegisterIDispatchServerUsingClassMetadata(moduleName, className): 
     568            success = False 
     569 
     570    # Register the type library. 
     571 
     572    tlbFile = os.path.join(os.path.dirname(sys.modules['GeoEco.COM'].__file__), 'COM', 'GeoEco.tlb')     
     573    try: 
     574        tli = pythoncom.LoadTypeLib(tlbFile) 
     575    except Exception, e: 
     576        print('WARNING: Failed to load the GeoEco COM type library. Since the library could not be loaded, it will not be registered with COM, and you will not be able to call GeoEco objects from early-bound programming languages such as C#. You can try registration again by uninstalling and reinstalling GeoEco. The pythoncom.LoadTypeLib(\\'%s\\') function reported: %s: %s' % (tlbFile, e.__class__.__name__, str(e))) 
     577        success = False 
     578 
     579    if success:         
     580        try: 
     581            pythoncom.RegisterTypeLib(tli, tlbFile) 
     582        except Exception, e: 
     583            print('WARNING: Failed to register the GeoEco COM type library. You will not be able to call GeoEco objects from early-bound programming languages such as C#. You can try registration again by uninstalling and reinstalling GeoEco. The pythoncom.RegisterTypeLib(tli, \\'%s\\') function reported: %s: %s' % (tlbFile, e.__class__.__name__, str(e))) 
     584            success = False 
     585     
     586    return success 
     587 
     588def UnregisterClassesAsCOMIDispatchServers(): 
     589 
     590    # Import pywin32 modules that we need. 
     591     
     592    try: 
     593        import pythoncom 
     594        import win32com.server.register 
     595    except: 
     596        return 
     597 
     598    # Unregister the classes. 
     599 
     600    import GeoEco.COM 
     601    for [moduleName, className] in classesForCOMRegistration: 
     602        GeoEco.COM.UnregisterIDispatchServerUsingClassMetadata(moduleName, className) 
     603 
     604    # Unregister the type library: 
     605 
     606    try: 
     607        pythoncom.UnRegisterTypeLib('""" + GeoEco.COM.TypeLibraryGUID + '\', ' + str(GeoEco.COM.TypeLibraryVersion[0]) + ', ' + str(GeoEco.COM.TypeLibraryVersion[1]) + ', ' + str(GeoEco.COM.TypeLibraryLCID) + """, pythoncom.SYS_WIN32) 
     608    except: 
     609        pass 
     610 
    368611############################################################################### 
    369612#  MAIN SCRIPT 
     
    374617if len(sys.argv) >= 2 and sys.argv[1].lower() == '-install': 
    375618    arcGISToolboxInstalled = RegisterToolboxWithArcCatalog('register') 
    376  
    377     if arcGISToolboxInstalled: 
     619    allCOMClassesRegistered = RegisterClassesAsCOMIDispatchServers() 
     620 
     621    if arcGISToolboxInstalled and allCOMClassesRegistered: 
    378622        print('All installation tasks completed successfully.') 
    379623    else: 
     
    386630elif len(sys.argv) >= 2 and sys.argv[1].lower() == '-remove': 
    387631    RegisterToolboxWithArcCatalog('unregister') 
     632    UnregisterClassesAsCOMIDispatchServers() 
    388633 
    389634# If argv[1] is absent or unrecognized, report an error. 
     
    423668    print(u'Removing directory %s...' % os.path.join(setupDir, u'build')) 
    424669    shutil.rmtree(os.path.join(setupDir, u'build'), False) 
     670    print(u'') 
     671    print(u'***** BUILD SUCCESSFUL *****') 
    425672 
    426673if __name__ == u'__main__': 
  • MGET/Trunk/PythonPackage/src/GeoEco/ArcGIS.py

    r20 r25  
    1212import GeoEco.Logging 
    1313from GeoEco.Metadata import * 
    14  
    15  
    16 # Public attributes, functions and classes exported by this module 
    17  
    18 Geoprocessor = None 
    19  
    20 def InitializeGeoprocessor(): 
    21     _InitializeGeoprocessor() 
    22  
    23 def GetRealGeoprocessor(): 
    24     return _RealGeoprocessor 
    25  
    26 def SetRealGeoprocessor(geoprocessor): 
    27     if not isinstance(geoprocessor, object): 
    28         raise TypeError, _(u'The geoprocessor argument must be an instance of the ArcGIS geoprocessor object.') 
    29     try: 
    30         globals()[u'_RealGeoprocessor'] = geoprocessor 
    31         globals()[u'Geoprocessor'] = _ArcGISObjectWrapper(geoprocessor, u'Geoprocessor') 
    32     except: 
    33         globals()[u'_RealGeoprocessor'] = None 
    34         globals()[u'Geoprocessor'] = None 
    35  
     14from GeoEco.Types import * 
     15 
     16 
     17# Public classes and functions exported by this module 
     18 
     19class GeoprocessorManager(object): 
     20    __doc__ = DynamicDocString() 
     21 
     22    _Geoprocessor = None 
     23    _WrappedGeoprocessor = None 
     24    _ArcGISMajorVersion = None 
     25    _ArcGISMinorVersion = None 
     26    _ArcGISServicePack = None 
     27 
     28    @classmethod 
     29    def GetGeoprocessor(cls): 
     30        return GeoprocessorManager._Geoprocessor 
     31 
     32    @classmethod 
     33    def SetGeoprocessor(cls, geoprocessor): 
     34        cls.__doc__.Obj.ValidatePropertyAssignment() 
     35        try: 
     36            GeoprocessorManager._Geoprocessor = geoprocessor 
     37            GeoprocessorManager._WrappedGeoprocessor = _ArcGISObjectWrapper(geoprocessor, u'Geoprocessor') 
     38        except: 
     39            GeoprocessorManager._Geoprocessor = None 
     40            GeoprocessorManager._WrappedGeoprocessor = None 
     41        if GeoprocessorManager._Geoprocessor is not None: 
     42            GeoEco.Logging.Logger.Debug(_(u'GeoEco will now use ArcGIS geoprocessor object 0x%08X.'), id(geoprocessor)) 
     43 
     44    Geoprocessor = property(GetGeoprocessor, SetGeoprocessor, doc=DynamicDocString()) 
     45 
     46    @classmethod 
     47    def GetWrappedGeoprocessor(cls): 
     48        return GeoprocessorManager._WrappedGeoprocessor 
     49 
     50    WrappedGeoprocessor = property(GetWrappedGeoprocessor, doc=DynamicDocString()) 
     51 
     52    @classmethod 
     53    def GetArcGISMajorVersion(cls): 
     54        if GeoprocessorManager._ArcGISMajorVersion is None: 
     55            cls._GetArcGISVersionNumbers() 
     56        return GeoprocessorManager._ArcGISMajorVersion 
     57 
     58    ArcGISMajorVersion = property(GetArcGISMajorVersion, doc=DynamicDocString()) 
     59 
     60    @classmethod 
     61    def GetArcGISMinorVersion(cls): 
     62        if GeoprocessorManager._ArcGISMajorVersion is None: 
     63            cls._GetArcGISVersionNumbers() 
     64        return GeoprocessorManager._ArcGISMinorVersion 
     65 
     66    ArcGISMinorVersion = property(GetArcGISMinorVersion, doc=DynamicDocString()) 
     67 
     68    @classmethod 
     69    def GetArcGISServicePack(cls): 
     70        if GeoprocessorManager._ArcGISMajorVersion is None: 
     71            cls._GetArcGISVersionNumbers() 
     72        return GeoprocessorManager._ArcGISServicePack 
     73 
     74    ArcGISServicePack = property(GetArcGISServicePack, doc=DynamicDocString()) 
     75 
     76    @classmethod 
     77    def InitializeGeoprocessor(cls): 
     78        cls.InitializeGeoprocessorForDependency() 
     79 
     80    @classmethod 
     81    def InitializeGeoprocessorForDependency(cls, dependency=None): 
     82        cls.__doc__.Obj.ValidateMethodInvocation() 
     83         
     84        # Return immediately the geoprocessor has already been initialized. 
     85 
     86        if cls.GetGeoprocessor() is not None: 
     87            GeoEco.Logging.Logger.Debug(_(u'GeoEco.ArcGIS.GeoprocessorManager.InitializeGeoprocessorForDependency was called but the geoprocessor was already initialized. The request to initialize it again will be ignored.')) 
     88            return 
     89 
     90        GeoEco.Logging.Logger.Debug(_(u'Initializing the ArcGIS geoprocessor object...')) 
     91             
     92        # First try to import the arcgisscripting package, which was 
     93        # introduced in ArcGIS 9.2. 
     94 
     95        arcgissscriptingImported = True 
     96        try: 
     97            import arcgisscripting 
     98        except Exception, e: 
     99            GeoEco.Logging.Logger.Debug(_(u'The arcgisscripting Python package could not be imported (Python raised %s: %s). ArcGIS 9.2 or later is not installed on this machine.'), e.__class__.__name__, unicode(e)) 
     100            arcgissscriptingImported = False 
     101 
     102        # If we succesfully imported the arcgisscripting package, use it to 
     103        # create the geoprocessor object. 
     104 
     105        realGeoprocessor = None 
     106 
     107        if arcgissscriptingImported: 
     108            GeoEco.Logging.Logger.Debug(_(u'Imported the arcgisscripting Python package. ArcGIS 9.2 or later must be installed on this machine.')) 
     109            error = None 
     110            try: 
     111                realGeoprocessor = arcgisscripting.create() 
     112            except Exception, e: 
     113                error = (u'%s: %s' % e.__class__.__name__, unicode(e)).strip() 
     114            if realGeoprocessor is None: 
     115                message = _(u'This function requires ESRI ArcGIS. The function successfully imported the Python arcgisscripting package, suggesting that ArcGIS 9.2 or later is installed. But the arcgisscripting.create() function') 
     116                if error is None: 
     117                    message = _(u'%s did not return the geoprocessor object (it returned None without raising an exception). Please verify that ArcGIS is properly installed.') % message 
     118                else: 
     119                    message = _(u'%s raised an exception. Please verify that ArcGIS is properly installed. The exception was: %s') % (message, error) 
     120                raise SoftwareNotInstalledError(message, dependency) 
     121            GeoEco.Logging.Logger.Debug(_(u'Obtained ArcGIS geoprocessor object 0x%08X from arcgisscripting.create().'), id(realGeoprocessor)) 
     122 
     123        # If we failed to import the arcgisscripting package, try the old 
     124        # technique of creating the IDispatch COM object. 
     125 
     126        else: 
     127                 
     128            # The IDispatch technique only works on Windows. Fail if this is 
     129            # not Windows. 
     130 
     131            if platform.system().lower() != u'windows': 
     132                raise SoftwareNotInstalledError(_(u'This function requires ESRI ArcGIS, which does not appear to be installed. If ArcGIS is not supported on your operating system, you must run this function on an operating system that does support it.'), dependency) 
     133 
     134            # Import the Python COM libraries. 
     135             
     136            try: 
     137                import pythoncom 
     138            except Exception, e: 
     139                raise SoftwareNotInstalledError(_(u'This function requires ESRI ArcGIS. ArcGIS  9.2 or a later version does not appear to be installed, because the Python arcgisscripting package could not be imported. A prior version might be installed, but in order for GeoEco to communicate with it, you must install the "Python for Windows extensions" Python package for the running version of Python (%s.%s). This package does not appear to be installed. It may be available at http://sourceforge.net/projects/pywin32. Debugging information: the Python statement "__import__(\'pythoncom\')" raised %s: %s') % (unicode(platform.python_version_tuple()[0]), unicode(platform.python_version_tuple()[1]), e.__class__.__name__, unicode(e)), dependency) 
     140             
     141            try: 
     142                import win32com.client 
     143            except Exception, e: 
     144                raise SoftwareNotInstalledError(_(u'This function requires ESRI ArcGIS. ArcGIS 9.2 or a later version does not appear to be installed, because the Python arcgisscripting package could not be imported. A prior version might be installed, but in order for GeoEco to communicate with it, you must install the "Python for Windows extensions" Python package for the running version of Python (%s.%s). This package does not appear to be installed. It may be available at http://sourceforge.net/projects/pywin32. Debugging information: the Python statement "__import__(\'win32com.client\')" raised %s: %s') % (unicode(platform.python_version_tuple()[0]), unicode(platform.python_version_tuple()[1]), e.__class__.__name__, unicode(e)), dependency) 
     145 
     146            # Create the IDispatch object. 
     147 
     148            progID = 'esriGeoprocessing.GPDispatch' 
     149             
     150            try: 
     151                realGeoprocessor = win32com.client.Dispatch(progID) 
     152            except pythoncom.com_error, (hr, msg, exc, arg): 
     153                raise SoftwareNotInstalledError(_(u'This function requires ESRI ArcGIS, which does not appear to be installed. This function was unable to import the arcgisscripting Python module, indicating that ArcGIS 9.2 or later is not installed. It was also unable to create the %s COM object, indicating that ArcGIS 9.0 or 9.1 is not installed. Debugging information: win32com.client.Dispatch(\'%s\') raised the exception: %s') % (progID, _FormatCOMError(hr, msg, exc, arg)), dependency) 
     154            except Exception, e: 
     155                raise SoftwareNotInstalledError(_(u'This function requires ESRI ArcGIS, which does not appear to be installed. This function was unable to import the arcgisscripting Python module, indicating that ArcGIS 9.2 or later is not installed. It was also unable to create the %s COM object, indicating that ArcGIS 9.0 or 9.1 is not installed. Debugging information: win32com.client.Dispatch(\'%s\') raised the exception: %s: %s') % (progID, e.__class__.__name__, unicode(e)), dependency) 
     156            GeoEco.Logging.Logger.Debug(_(u'Obtained ArcGIS geoprocessor object 0x%08X by creating COM object %s.'), id(realGeoprocessor), progID) 
     157 
     158        # We successfully created the geoprocessor object. 
     159 
     160        cls.SetGeoprocessor(realGeoprocessor) 
     161 
     162    @classmethod 
     163    def _GetArcGISVersionNumbers(cls): 
     164 
     165        # If we're running on Windows, get the ArcGIS version number from 
     166        # the Windows registry. 
     167 
     168        if platform.system().lower() == u'windows': 
     169            GeoEco.Logging.Logger.Debug(_(u'Retrieving the ArcGIS version numbers from the Windows Registry.')) 
     170 
     171            # First import the win32api module. 
     172 
     173            d = GeoEco.Dependencies.PythonPackage(importName='win32api', displayName=_(u'Python for Windows extensions'), alternateURL=u'http://sourceforge.net/projects/pywin32') 
     174            d.Initialize(); 
     175            import win32api 
     176 
     177            d = GeoEco.Dependencies.PythonPackage(importName='win32con', displayName=_(u'Python for Windows extensions'), alternateURL=u'http://sourceforge.net/projects/pywin32') 
     178            d.Initialize(); 
     179            import win32con 
     180 
     181            # Read the registry values that contain the version numbers. 
     182 
     183            try: 
     184                hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\ESRI\\ArcInfo\\Desktop\\8.0') 
     185                try: 
     186                    (realVersionValue, realVersionType) = win32api.RegQueryValueEx(hkey, 'RealVersion') 
     187                    if not isinstance(realVersionValue, basestring) or not re.match('^[0-9]+\.[0-9]+$', realVersionValue.strip()): 
     188                        raise ValueError(_(u'The RealVersion value of the HKEY_LOCAL_MACHINE\\SOFTWARE\\ESRI\\ArcInfo\\Desktop\\8.0 registry key is not a REG_SZ, or it is not formatted properly.')) 
     189                    GeoprocessorManager._ArcGISMajorVersion = int(realVersionValue.strip().split('.')[0]) 
     190                    GeoprocessorManager._ArcGISMinorVersion = int(realVersionValue.strip().split('.')[1]) 
     191                    spNumberValue = None 
     192                    try: 
     193                        (spNumberValue, spNumberType) = win32api.RegQueryValueEx(hkey, 'SPNumber') 
     194                    except: 
     195                        pass 
     196                    if spNumberValue is not None: 
     197                        if not isinstance(spNumberValue, basestring) or len(spNumberValue.strip()) > 0 and not re.match('^[0-9]+$', spNumberValue.strip()): 
     198                            raise ValueError(_(u'The SPNumber value of the HKEY_LOCAL_MACHINE\\SOFTWARE\\ESRI\\ArcInfo\\Desktop\\8.0 registry key is not a REG_SZ, or it is not formatted properly.')) 
     199                        if len(spNumberValue.strip()) > 0: 
     200                            GeoprocessorManager._ArcGISServicePack = int(spNumberValue.strip()) 
     201                    else: 
     202                        GeoprocessorManager._ArcGISServicePack = 0 
     203                finally: 
     204                    try: 
     205                        win32api.RegCloseKey(hkey) 
     206                    except: 
     207                        pass 
     208            except Exception, e: 
     209                GeoprocessorManager._ArcGISMajorVersion = None 
     210                GeoprocessorManager._ArcGISMinorVersion = None 
     211                GeoprocessorManager._ArcGISServicePack = None 
     212                GeoEco.Logging.Logger.Debug(_(u'Failed to retrieve the ArcGIS version numbers from the Windows Registry. ArcGIS must not be installed. Python reported %s: %s'), e.__class__.__name__, unicode(e)) 
     213                 
     214        # If we're on a non-Windows platform, all we can try to do is import the 
     215        # arcgisscripting package. If successful, we know that ArcGIS 9.2 or 
     216        # later is installed. 
     217 
     218        else: 
     219            GeoEco.Logging.Logger.Debug(_(u'Importing the arcgisscripting Python package to determine if ArcGIS is installed on this machine...')) 
     220            arcgissscriptingImported = True 
     221            try: 
     222                import arcgisscripting 
     223                GeoprocessorManager._ArcGISMajorVersion = 9 
     224                GeoprocessorManager._ArcGISMinorVersion = 2 
     225                GeoprocessorManager._ArcGISServicePack = 0 
     226                GeoEco.Logging.Logger.Debug(_(u'Imported the arcgisscripting Python package. ArcGIS 9.2 or later is installed on this machine. We do not presently know how to determine which version is installed, so we will assume it is 9.2.')) 
     227            except Exception, e: 
     228                GeoprocessorManager._ArcGISMajorVersion = None 
     229                GeoprocessorManager._ArcGISMinorVersion = None 
     230                GeoprocessorManager._ArcGISServicePack = None 
     231                GeoEco.Logging.Logger.Debug(_(u'The arcgisscripting Python package could not be imported (Python raised %s: %s). ArcGIS is not installed on this machine.'), e.__class__.__name__, unicode(e)) 
     232             
    36233 
    37234class ArcGISVersion(Dependency): 
     
    83280    def Initialize(self): 
    84281 
    85         GeoEco.Logging.LogDebug(_(u'Checking software dependency: ArcGIS version %i.%i%s or later.') % (self.MinimumMajorVersion, self.MinimumMinorVersion, self.MinimumServicePackString)) 
    86  
    87         # If we have not created the geoprocessor object, do it first. This will 
    88         # raise an exception if ArcGIS is not installed on the machine. 
    89  
    90         if globals()[u'Geoprocessor'] is None: 
    91             _InitializeGeoprocessor(self) 
     282        GeoEco.Logging.Logger.Debug(_(u'Checking software dependency: ArcGIS version %i.%i%s or later.') % (self.MinimumMajorVersion, self.MinimumMinorVersion, self.MinimumServicePackString)) 
     283 
     284        # If the geoprocessor object is not initialized yet, do it now. This 
     285        # will raise an exception if ArcGIS is not installed on the machine. 
     286 
     287        if GeoprocessorManager.GetWrappedGeoprocessor() is None: 
     288            GeoprocessorManager.InitializeGeoprocessorForDependency(self) 
     289 
     290        # If we got to here, then some version of ArcGIS is installed. Check the 
     291        # ArcGIS version numbers 
     292 
     293        if GeoprocessorManager.GetArcGISMajorVersion() is None or GeoprocessorManager.GetArcGISMinorVersion() is None or GeoprocessorManager.GetArcGISServicePack() is None: 
     294            raise SoftwareNotInstalledError(_(u'This function requires ArcGIS %i.%i%s or a later version. Some verison of ArcGIS appears to be installed on this machine but this function was unable to retrieve the version numbers. (On Windows machines, the version numbers are in the RealVersion and SPNumber values of the HKEY_LOCAL_MACHINE\\SOFTWARE\\ESRI\\ArcInfo\\Desktop\\8.0 Registry key). Please verify that ArcGIS is property installed.') % (self.MinimumMajorVersion, self.MinimumMinorVersion, self.MinimumServicePackString), self) 
     295         
     296        if GeoprocessorManager.GetArcGISServicePack() > 0: 
     297            servicePackString =  _(u' Service Pack %i') % GeoprocessorManager.GetArcGISServicePack() 
    92298        else: 
    93             GeoEco.Logging.LogDebug(_(u'The ArcGIS geoprocessor was already initialized.')) 
    94  
    95         # If we got to here, then some version of ArcGIS is installed. If we 
    96         # have not retrieved the version numbers yet, do it now. 
    97  
    98         if globals()[u'_ArcGISMajorVersion'] is None: 
    99  
    100             # If we're running on Windows, get the ArcGIS version number from 
    101             # the Windows registry. 
    102  
    103             if platform.system().lower() == u'windows': 
    104                 GeoEco.Logging.LogDebug(_(u'Retrieving the ArcGIS version numbers from the Windows Registry.')) 
    105  
    106                 # First import the win32api module. 
    107  
    108                 d = GeoEco.Dependencies.PythonPackage(importName='win32api', displayName=_(u'Python for Windows extensions'), alternateURL=u'http://sourceforge.net/projects/pywin32') 
    109                 d.Initialize(); 
    110                 import win32api 
    111  
    112                 d = GeoEco.Dependencies.PythonPackage(importName='win32con', displayName=_(u'Python for Windows extensions'), alternateURL=u'http://sourceforge.net/projects/pywin32') 
    113                 d.Initialize(); 
    114                 import win32con 
    115  
    116                 # Read the registry values that contain the version numbers. 
    117  
    118                 try: 
    119                     hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\ESRI\\ArcInfo\\Desktop\\8.0') 
    120                     try: 
    121                         (realVersionValue, realVersionType) = win32api.RegQueryValueEx(hkey, 'RealVersion') 
    122                         if not isinstance(realVersionValue, basestring) or not re.match('^[0-9]+\.[0-9]+$', realVersionValue.strip()): 
    123                             raise ValueError(_(u'The RealVersion value of the HKEY_LOCAL_MACHINE\\SOFTWARE\\ESRI\\ArcInfo\\Desktop\\8.0 registry key is not a REG_SZ, or it is not formatted properly.')) 
    124                         globals()[u'_ArcGISMajorVersion'] = int(realVersionValue.strip().split('.')[0]) 
    125                         globals()[u'_ArcGISMinorVersion'] = int(realVersionValue.strip().split('.')[1]) 
    126                         spNumberValue = None 
    127                         try: 
    128                             (spNumberValue, spNumberType) = win32api.RegQueryValueEx(hkey, 'SPNumber') 
    129                         except: 
    130                             pass 
    131                         if spNumberValue is not None: 
    132                             if not isinstance(spNumberValue, basestring) or len(spNumberValue.strip()) > 0 and not re.match('^[0-9]+$', spNumberValue.strip()): 
    133                                 raise ValueError(_(u'The SPNumber value of the HKEY_LOCAL_MACHINE\\SOFTWARE\\ESRI\\ArcInfo\\Desktop\\8.0 registry key is not a REG_SZ, or it is not formatted properly.')) 
    134                             if len(spNumberValue.strip()) > 0: 
    135                                 globals()[u'_ArcGISServicePack'] = int(spNumberValue.strip()) 
    136                         else: 
    137                             globals()[u'_ArcGISServicePack'] = 0 
    138                     finally: 
    139                         try: 
    140                             win32api.RegCloseKey(hkey) 
    141                         except: 
    142                             pass 
    143                 except Exception, e: 
    144                     globals()[u'_ArcGISMajorVersion'] = None 
    145                     globals()[u'_ArcGISMinorVersion'] = None 
    146                     globals()[u'_ArcGISServicePack'] = None 
    147                     raise SoftwareNotInstalledError(_(u'This tool requires ArcGIS %i.%i%s or a later version. Some verison of ArcGIS appears to be installed on this machine but this tool was unable to retrieve the version numbers from the Windows Registry (the RealVersion and SPNumber values of the HKEY_LOCAL_MACHINE\\SOFTWARE\\ESRI\\ArcInfo\\Desktop\\8.0 key). Please verify that ArcGIS is property installed. Detailed error information: one of the win32api registry functions raised %s: %s.') % (self.MinimumMajorVersion, self.MinimumMinorVersion, self.MinimumServicePackString, e.__class__.__name__, unicode(e)), self) 
    148  
    149             # If we're on some other platform, issue a warning because we do not 
    150             # know how to obtain the version numbers. 
    151  
    152             else: 
    153                 self._LogWarning(_(u'This tool requires ArcGIS %i.%i%s or a later version, but it does not know how to retrieve the ArcGIS version numbers when running on non-Windows platforms. The tool will continue executing even though it cannot verify the ArcGIS version. If you do not have a sufficient version installed you may experience unexpected results.') % (self.MinimumMajorVersion, self.MinimumMinorVersion, self.MinimumServicePackString)) 
    154  
    155         # If we got the version numbers, check them. (If we did not get them but 
    156         # we still got to this point, it means we are running on a platform that 
    157         # we don't know how to get the version numbers from, and we're willing 
    158         # to go forward anyway with possible unexpected results.) 
    159  
    160         if globals()[u'_ArcGISMajorVersion'] is not None: 
    161             if globals()[u'_ArcGISServicePack'] > 0: 
    162                 servicePackString =  _(u' Service Pack %i') % globals()[u'_ArcGISServicePack'] 
    163             else: 
    164                 servicePackString = u'' 
    165             GeoEco.Logging.LogDebug(_(u'ArcGIS %i.%i%s is installed.') % (globals()[u'_ArcGISMajorVersion'], globals()[u'_ArcGISMinorVersion'], servicePackString)) 
    166             if self.MinimumMajorVersion > globals()[u'_ArcGISMajorVersion'] or self.MinimumMajorVersion == globals()[u'_ArcGISMajorVersion'] and self.MinimumMinorVersion > globals()[u'_ArcGISMinorVersion'] or self.MinimumMajorVersion == globals()[u'_ArcGISMajorVersion'] and self.MinimumMinorVersion == globals()[u'_ArcGISMinorVersion'] and self.MinimumServicePack > globals()[u'_ArcGISServicePack']: 
    167                 raise SoftwareNotInstalledError(_(u'This tool requires ArcGIS %i.%i%s or a later version, but verison %s.%s%s is installed. Please upgrade your ArcGIS installation to a compatible version.') % (self.MinimumMajorVersion, self.MinimumMinorVersion, self.MinimumServicePackString, globals()[u'_ArcGISMajorVersion'], globals()[u'_ArcGISMinorVersion'], servicePackString), self) 
    168  
    169  
    170 def ValidateToolMetadata(moduleName, className, methodName): 
     299            servicePackString = u'' 
     300        GeoEco.Logging.Logger.Debug(_(u'ArcGIS %i.%i%s is installed.'), GeoprocessorManager.GetArcGISMajorVersion(), GeoprocessorManager.GetArcGISMinorVersion(), servicePackString) 
     301 
     302        if self.MinimumMajorVersion > GeoprocessorManager.GetArcGISMajorVersion() or self.MinimumMajorVersion == GeoprocessorManager.GetArcGISMajorVersion() and self.MinimumMinorVersion > GeoprocessorManager.GetArcGISMinorVersion() or self.MinimumMajorVersion == GeoprocessorManager.GetArcGISMajorVersion() and self.MinimumMinorVersion == GeoprocessorManager.GetArcGISMinorVersion() and self.MinimumServicePack > GeoprocessorManager.GetArcGISServicePack(): 
     303            raise SoftwareNotInstalledError(_(u'This function requires ArcGIS %i.%i%s or a later version, but verison %s.%s%s is installed. Please upgrade your ArcGIS installation to a compatible version.') % (self.MinimumMajorVersion, self.MinimumMinorVersion, self.MinimumServicePackString, GeoprocessorManager.GetArcGISMajorVersion(), GeoprocessorManager.GetArcGISMinorVersion(), servicePackString), self) 
     304 
     305 
     306def ValidateMethodMetadataForExposureAsArcGISTool(moduleName, className, methodName): 
    171307 
    172308        # Validate the class and method metadata. 
    173309 
    174         assert sys.modules.has_key(moduleName), u'Module %s must be imported before ValidateToolMetadata is invoked on that module.' % moduleName 
    175         import GeoEco.Tools 
    176         assert sys.modules[moduleName].__dict__.has_key(className) and issubclass(sys.modules[moduleName].__dict__[className], GeoEco.Tool.Tool), u'Module %s must contain a class named %s, and the class must derive from GeoEco.Tool.Tool.' % (moduleName, className) 
     310        assert sys.modules.has_key(moduleName), u'Module %s must be imported before ValidateMethodMetadataForExposureAsArcGISTool is invoked on that module.' % moduleName 
     311        assert sys.modules[moduleName].__dict__.has_key(className) and issubclass(sys.modules[moduleName].__dict__[className], object), u'Module %s must contain a class named %s, and the class must derive from object.' % (moduleName, className) 
    177312        cls = sys.modules[moduleName].__dict__[className] 
    178313        assert isinstance(cls.__doc__, DynamicDocString) and isinstance(cls.__doc__.Obj, ClassMetadata), u'The __doc__ attribute of class %s must be an instance of DynamicDocString, and that Obj property of that instance must be an instance of ClassMetadata.' % className 
     
    211346 
    212347 
    213 def ExecuteToolFromCommandLine(moduleName, className, methodName, loggingConfigFile=None): 
     348def ExecuteMethodFromCommandLineAsArcGISTool(moduleName, className, methodName, loggingConfigFile=None): 
    214349 
    215350    # Initialize the logging system. 
    216351 
    217352    import GeoEco.Logging 
    218     GeoEco.Logging.InitializeLogging(loggingConfigFile) 
     353    GeoEco.Logging.Logger.Initialize(loggingConfigFile) 
    219354    try: 
    220         GeoEco.Logging.ActivateArcGISLogging() 
    221  
    222         # Validate the tool's metadata. This was already done when the GeoEco 
     355        GeoEco.Logging.Logger.ActivateArcGISLogging() 
     356 
     357        # Validate the methods's metadata. This was already done when the GeoEco 
    223358        # package was built, but we do it again here for safety. 
    224359 
    225360        __import__(moduleName, globals(), locals())         
    226         ValidateToolMetadata(moduleName, className, methodName) 
     361        ValidateMethodMetadataForExposureAsArcGISTool(moduleName, className, methodName) 
    227362 
    228363        # Determine whether we were invoked from an ArcGIS GUI application, in 
    229364        # which case we can use the geoprocessor's GetParameterAsText API to 
    230         # retrieve the tool's parameters. If we were not invoked from an ArcGIS 
     365        # retrieve the method's parameters. If we were not invoked from an ArcGIS 
    231366        # GUI, then this API won't work, and we have to get the parameters from 
    232367        # sys.argv. GetParameterAsText is better because it doesn't strip out 
     
    245380        methodMetadata = getattr(cls, methodName).__doc__.Obj 
    246381        expectedParamCount = len(methodMetadata.Arguments) + len(methodMetadata.Results) - 1        # Subtract 1 because the "self" or "cls" parameter is not exposed in ArcGIS 
    247         geoprocessor = globals()[u'Geoprocessor'] 
     382        geoprocessor = GeoprocessorManager.GetWrappedGeoprocessor() 
    248383        if geoprocessor.ParameterCount == expectedParamCount: 
    249384            useSysArgv = False 
     
    290425        for i in range(len(methodMetadata.Results)): 
    291426            if locals()[u'ret%s' % i] is not None: 
    292                 globals()[u'Geoprocessor'].SetParameterAsText(len(methodMetadata.Arguments) - 1 + i, unicode(locals()[u'ret%s' % i])) 
     427                GeoprocessorManager.GetWrappedGeoprocessor().SetParameterAsText(len(methodMetadata.Arguments) - 1 + i, unicode(locals()[u'ret%s' % i])) 
    293428            else: 
    294                 globals()[u'Geoprocessor'].SetParameterAsText(len(methodMetadata.Arguments) - 1 + i, u'') 
     429                GeoprocessorManager.GetWrappedGeoprocessor().SetParameterAsText(len(methodMetadata.Arguments) - 1 + i, u'') 
    295430 
    296431    # If we caught an exception, log it as an error if the logger level is set 
     
    300435        try: 
    301436            if logging.getLogger(u'GeoEco').getEffectiveLevel() <= logging.DEBUG: 
    302                 GeoEco.Logging.LogError(u'%s: %s', e.__class__.__name__, unicode(e), exc_info=e) 
     437                GeoEco.Logging.Logger.Error(u'%s: %s', e.__class__.__name__, unicode(e), exc_info=e) 
    303438            else: 
    304                 GeoEco.Logging.LogError(u'%s: %s', e.__class__.__name__, unicode(e)) 
     439                GeoEco.Logging.Logger.Error(u'%s: %s', e.__class__.__name__, unicode(e)) 
    305440        except: 
    306441            pass 
     
    310445 
    311446    sys.exit(0) 
    312  
    313  
    314 # Private attributes, functions and classes that implement the public interface. 
    315  
    316 _RealGeoprocessor = None        # The geoprocessor instance obtained from ArcGIS, not wrapped by a _ArcGISObjectWrapper instance. 
    317 _ArcGISMajorVersion = None     # Major version number of ArcGIS that is installed on this machine 
    318 _ArcGISMinorVersion = None     # Minor version number of ArcGIS that is installed on this machine 
    319 _ArcGISServicePack = None      # Service pack number of ArcGIS that is installed on this machine 
    320  
    321  
    322 def _InitializeGeoprocessor(dependency=None): 
    323     assert globals()[u'Geoprocessor'] is None and globals()[u'_RealGeoprocessor'] is None or globals()[u'Geoprocessor'] is not None and globals()[u'_RealGeoprocessor'] is not None 
    324     assert isinstance(dependency, (types.NoneType, Dependency)) 
    325      
    326     # Return immediately the geoprocessor has already been initialized. 
    327  
    328     if globals()[u'Geoprocessor'] is not None: 
    329         return 
    330          
    331     # First try to import the arcgisscripting package, which was 
    332     # introduced in ArcGIS 9.2. 
    333  
    334     arcgissscriptingImported = True 
    335     try: 
    336         import arcgisscripting 
    337     except Exception, e: 
    338         arcgissscriptingImported = False 
    339  
    340     # If we succesfully imported the arcgisscripting package, use it to 
    341     # create the geoprocessor object. 
    342  
    343     realGeoprocessor = None 
    344  
    345     if arcgissscriptingImported: 
    346         error = None 
    347         try: 
    348             realGeoprocessor = arcgisscripting.create() 
    349         except Exception, e: 
    350             error = (u'%s: %s' % e.__class__.__name__, unicode(e)).strip() 
    351         if realGeoprocessor is None: 
    352             message = _(u'This tool requires ESRI ArcGIS. The tool successfully imported the Python arcgisscripting package, suggesting that ArcGIS 9.2 or later is installed. But the arcgisscripting.create() function') 
    353             if error is None: 
    354                 message = _(u'%s did not return the geoprocessor object (it returned None without raising an exception). Please verify that ArcGIS is properly installed.') % message 
    355             else: 
    356                 message = _(u'%s raised an exception. Please verify that ArcGIS is properly installed. The exception was: %s') % (message, error) 
    357             raise SoftwareNotInstalledError(message, dependency) 
    358  
    359     # If we failed to import the arcgisscripting package, try the old 
    360     # technique of creating the IDispatch COM object. 
    361  
    362     else: 
    363              
    364         # The IDispatch technique only works on Windows. Fail if this is 
    365         # not Windows. 
    366  
    367         if platform.system().lower() != u'windows': 
    368             raise SoftwareNotInstalledError(_(u'This tool requires ESRI ArcGIS, which does not appear to be installed. If ArcGIS is not supported on your operating system, you must run this tool on an operating system that does support it.'), dependency) 
    369  
    370         # Import the Python COM libraries. 
    371          
    372         try: 
    373             import pythoncom 
    374         except Exception, e: 
    375             raise SoftwareNotInstalledError(_(u'This tool requires ESRI ArcGIS. ArcGIS  9.2 or a later version does not appear to be installed, because the Python arcgisscripting package could not be imported. A prior version might be installed, but in order for GeoEco to communicate with it, you must install the "Python for Windows extensions" Python package for the running version of Python (%s.%s). This package does not appear to be installed. It may be available at http://sourceforge.net/projects/pywin32. Debugging information: the Python statement "__import__(\'pythoncom\')" raised %s: %s') % (unicode(platform.python_version_tuple()[0]), unicode(platform.python_version_tuple()[1]), e.__class__.__name__, unicode(e)), dependency) 
    376          
    377         try: 
    378             import win32com.client 
    379         except Exception, e: 
    380             raise SoftwareNotInstalledError(_(u'This tool requires ESRI ArcGIS. ArcGIS 9.2 or a later version does not appear to be installed, because the Python arcgisscripting package could not be imported. A prior version might be installed, but in order for GeoEco to communicate with it, you must install the "Python for Windows extensions" Python package for the running version of Python (%s.%s). This package does not appear to be installed. It may be available at http://sourceforge.net/projects/pywin32. Debugging information: the Python statement "__import__(\'win32com.client\')" raised %s: %s') % (unicode(platform.python_version_tuple()[0]), unicode(platform.python_version_tuple()[1]), e.__class__.__name__, unicode(e)), dependency) 
    381  
    382         # Create the IDispatch object. 
    383  
    384         progID = 'esriGeoprocessing.GPDispatch' 
    385          
    386         try: 
    387             realGeoprocessor = win32com.client.Dispatch(progID) 
    388         except pythoncom.com_error, (hr, msg, exc, arg): 
    389             raise SoftwareNotInstalledError(_(u'This tool requires ESRI ArcGIS, which does not appear to be installed. This tool was unable to import the arcgisscripting Python module, indicating that ArcGIS 9.2 or later is not installed. It was also unable to create the %s COM object, indicating that ArcGIS 9.0 or 9.1 is not installed. Debugging information: win32com.client.Dispatch(\'%s\') raised the exception: %s') % (progID, _FormatCOMError(hr, msg, exc, arg)), dependency) 
    390         except Exception, e: 
    391             raise SoftwareNotInstalledError(_(u'This tool requires ESRI ArcGIS, which does not appear to be installed. This tool was unable to import the arcgisscripting Python module, indicating that ArcGIS 9.2 or later is not installed. It was also unable to create the %s COM object, indicating that ArcGIS 9.0 or 9.1 is not installed. Debugging information: win32com.client.Dispatch(\'%s\') raised the exception: %s: %s') % (progID, e.__class__.__name__, unicode(e)), dependency) 
    392  
    393     # We successfully created the geoprocessor object. 
    394  
    395     SetRealGeoprocessor(realGeoprocessor) 
    396447 
    397448 
     
    531582                except com_error, (hr, msg, exc, arg): 
    532583                    self._LogReturnedGeoprocessingMessages()            # Most likely there are none, but try it anyway. 
    533                     raise ArcGISError(_(u'Failed to get the value of the %s attribute of ArcGIS %s object 0x%08X. This may result from a problem with your inputs or it may indicate a programming mistake in this tool or ArcGIS itself. Please check your inputs and try again. Also review any preceding error messages and the detailed error information that appears at the end of this message. If you suspect a programming mistake in this tool or ArcGIS, please contact the author of this tool for assistance. Detailed error information: The following exception was raised when the property retrieved: %s') % (name, self._Name, id(self._Object), _FormatCOMError(hr, msg, exc, arg)), sys.exc_info()[1]) 
     584                    raise ArcGISError(_(u'Failed to get the value of the %s attribute of ArcGIS %s object 0x%08X. This may result from a problem with your inputs or it may indicate a programming mistake in this function or ArcGIS itself. Please check your inputs and try again. Also review any preceding error messages and the detailed error information that appears at the end of this message. If you suspect a programming mistake in this function or ArcGIS, please contact the author of this function for assistance. Detailed error information: The following exception was raised when the property retrieved: %s') % (name, self._Name, id(self._Object), _FormatCOMError(hr, msg, exc, arg)), sys.exc_info()[1]) 
    534585 
    535586            # If we did not import pythoncom, set the value and allow the outer 
     
    549600        except Exception, e: 
    550601            self._LogReturnedGeoprocessingMessages()            # Most likely there are none, but try it anyway. 
    551             raise ArcGISError(_(u'Failed to get the value of the %s attribute of ArcGIS %s object 0x%08X. This may result from a problem with your inputs or it may indicate a programming mistake in this tool or ArcGIS itself. Please check your inputs and try again. Also review any preceding error messages and the detailed error information that appears at the end of this message. If you suspect a programming mistake in this tool or ArcGIS, please contact the author of this tool for assistance. Detailed error information: The following exception was raised when the property retrieved: %s: %s') % (name, self._Name, id(self._Object), e.__class__.__name__, unicode(e)), e) 
     602            raise ArcGISError(_(u'Failed to get the value of the %s attribute of ArcGIS %s object 0x%08X. This may result from a problem with your inputs or it may indicate a programming mistake in this function or ArcGIS itself. Please check your inputs and try again. Also review any preceding error messages and the detailed error information that appears at the end of this message. If you suspect a programming mistake in this function or ArcGIS, please contact the author of this function for assistance. Detailed error information: The following exception was raised when the property retrieved: %s: %s') % (name, self._Name, id(self._Object), e.__class__.__name__, unicode(e)), e) 
    552603 
    553604        # If it is a method, create a wrapper, add it to our dictionary of 
     
    625676                    else: 
    626677                        valueStr = repr(value) 
    627                     raise ArcGISError(_(u'Failed to set the %s property of ArcGIS %s object 0x%08X to %s. This may result from a problem with your inputs or it may indicate a programming mistake in this tool or ArcGIS itself. Please check your inputs and try again. Also review any preceding error messages and the detailed error information that appears at the end of this message. If you suspect a programming mistake in this tool or ArcGIS, please contact the author of this tool for assistance. Detailed error information: The following exception was raised when the property was assigned: %s') % (name, self._Name, id(self._Object), valueStr, _FormatCOMError(hr, msg, exc, arg)), sys.exc_info()[1]) 
     678                    raise ArcGISError(_(u'Failed to set the %s property of ArcGIS %s object 0x%08X to %s. This may result from a problem with your inputs or it may indicate a programming mistake in this function or ArcGIS itself. Please check your inputs and try again. Also review any preceding error messages and the detailed error information that appears at the end of this message. If you suspect a programming mistake in this function or ArcGIS, please contact the author of this function for assistance. Detailed error information: The following exception was raised when the property was assigned: %s') % (name, self._Name, id(self._Object), valueStr, _FormatCOMError(hr, msg, exc, arg)), sys.exc_info()[1]) 
    628679 
    629680            # If we did not import pythoncom, set the value and allow the outer 
     
    647698            else: 
    648699                valueStr = repr(value) 
    649             raise ArcGISError(_(u'Failed to set the %s property of ArcGIS %s object 0x%08X to %s. This may result from a problem with your inputs or it may indicate a programming mistake in this tool or ArcGIS itself. Please check your inputs and try again. Also review any preceding error messages and the detailed error information that appears at the end of this message. If you suspect a programming mistake in this tool or ArcGIS, please contact the author of this tool for assistance. Detailed error information: The following exception was raised when the property was assigned: %s: %s') % (name, self._Name, id(self._Object), valueStr, e.__class__.__name__, unicode(e)), e) 
     700            raise ArcGISError(_(u'Failed to set the %s property of ArcGIS %s object 0x%08X to %s. This may result from a problem with your inputs or it may indicate a programming mistake in this function or ArcGIS itself. Please check your inputs and try again. Also review any preceding error messages and the detailed error information that appears at the end of this message. If you suspect a programming mistake in this function or ArcGIS, please contact the author of this function for assistance. Detailed error information: The following exception was raised when the property was assigned: %s: %s') % (name, self._Name, id(self._Object), valueStr, e.__class__.__name__, unicode(e)), e) 
    650701 
    651702        # Log the set value. 
     
    699750                except com_error, (hr, msg, exc, arg): 
    700751                    self._LogReturnedGeoprocessingMessages() 
    701                     raise ArcGISError(_(u'An ArcGIS geoprocessing function failed. This usually results from a problem with your inputs. Please check them and try again. Also review any preceding error messages and the detailed error information that appears at the end of this message. If you suspect the failure is due to a programming mistake in this tool or ArcGIS, please contact the author of this tool for assistance. Detailed error information: ArcGIS %s object 0x%08X: Invocation of %s%s raised %s') % (self._Name, id(self._Object), method.__name__, inspect.formatargvalues(_args, _varargs, _varkw, _locals), _FormatCOMError(hr, msg, exc, arg)), sys.exc_info()[1]) 
     752                    raise ArcGISError(_(u'An ArcGIS geoprocessing function failed. This usually results from a problem with your inputs. Please check them and try again. Also review any preceding error messages and the detailed error information that appears at the end of this message. If you suspect the failure is due to a programming mistake in this function or ArcGIS, please contact the author of this function for assistance. Detailed error information: ArcGIS %s object 0x%08X: Invocation of %s%s raised %s') % (self._Name, id(self._Object), method.__name__, inspect.formatargvalues(_args, _varargs, _varkw, _locals), _FormatCOMError(hr, msg, exc, arg)), sys.exc_info()[1]) 
    702753 
    703754            # If we did not import pythoncom, invoke the method and allow the 
     
    717768        except Exception, e: 
    718769            self._LogReturnedGeoprocessingMessages() 
    719             raise ArcGISError(_(u'An ArcGIS geoprocessing function failed. This usually results from a problem with your inputs. Please check them and try again. Also review any preceding error messages and the detailed error information that appears at the end of this message. If you suspect the failure is due to a programming mistake in this tool or ArcGIS, please contact the author of this tool for assistance. Detailed error information: ArcGIS %s object 0x%08X: Invocation of %s%s raised %s: %s') % (self._Name, id(self._Object), method.__name__, inspect.formatargvalues(_args, _varargs, _varkw, _locals), e.__class__.__name__, unicode(e)), e) 
     770            raise ArcGISError(_(u'An ArcGIS geoprocessing function failed. This usually results from a problem with your inputs. Please check them and try again. Also review any preceding error messages and the detailed error information that appears at the end of this message. If you suspect the failure is due to a programming mistake in this function or ArcGIS, please contact the author of this function for assistance. Detailed error information: ArcGIS %s object 0x%08X: Invocation of %s%s raised %s: %s') % (self._Name, id(self._Object), method.__name__, inspect.formatargvalues(_args, _varargs, _varkw, _locals), e.__class__.__name__, unicode(e)), e) 
    720771 
    721772        # The method executed successfully. Log any geoprocessing messages it 
     
    753804        i = 0 
    754805        try: 
    755             realGeoprocessor = GetRealGeoprocessor() 
     806            realGeoprocessor = GeoprocessorManager.GetGeoprocessor() 
    756807            while i < realGeoprocessor.MessageCount: 
    757808                sev = realGeoprocessor.GetSeverity(i) 
     
    841892        u'fidset' : None, 
    842893        u'PropertySet' : None} 
     894 
     895 
     896############################################################################### 
     897# Metadata: module 
     898############################################################################### 
     899 
     900AddModuleMetadata(shortDescription=_(u'Provides utility functions for interacting with the ESRI ArcGIS software package.')) 
     901 
     902############################################################################### 
     903# Metadata: GeoprocessorManager class 
     904############################################################################### 
     905 
     906AddClassMetadata(GeoprocessorManager, 
     907    shortDescription=_(u'Manages the instance of the ArcGIS geoprocessor object used whenever any GeoEco function needs to invoke ArcGIS tools.'), 
     908    isExposedByIDispatch=True, 
     909    comIID=u'{3DAAF9BD-2C7F-4A0B-8AB1-8975103F3E78}', 
     910    comCLSID=u'{6E4B25C1-0E1D-496B-A661-AC75ADCB24C8}') 
     911 
     912# Public properties 
     913 
     914AddPropertyMetadata(GeoprocessorManager.Geoprocessor, 
     915    typeMetadata=AnyPythonInstance(canBeNone=True), 
     916    shortDescription=_(u'The ArcGIS geoprocessor object used whenever any GeoEco function needs to invoke ArcGIS tools.'), 
     917    longDescription=_( 
     918u"""This property is a singleton; all Python modules running in the same 
     919instance of the Python interpreter share the same value for this property. This 
     920property remains empty until it is explicitly set, or the InitializeGeoprocessor 
     921method is explicitly invoked, or a GeoEco function that has a dependency on 
     922ArcGIS is invoked (in which case it will invoke InitializeGeoprocessor). 
     923 
     924If you want to invoke GeoEco functions from your own ArcGIS geoprocessing script 
     925and you have already obtained a geoprocessor object, you should set this 
     926property to that object before invoking any other GeoEco functions. Failing to 
     927do so will cause GeoEco to allocate its own geoprocessor object, which can yield 
     928unpredictable results. (As far as I can tell, the ArcGIS tools still work 
     929correctly, but log messages do not end up in the ArcGIS GUIs.) 
     930 
     931If your geoprocessing script has not yet obtained the geoprocessor object, you 
     932may allow the GeoEco functions to obtain one when they first need it and then 
     933retrieve it by getting the value of this property. 
     934 
     935If you want to invoke GeoEco functions from something that is not a 
     936"geoprocessing script" (a program that does not invoke any ArcGIS 
     937geoprocessing tools directly), then you should ignore this property and 
     938allow the GeoEco functions to obtain and use their own geoprocessor object. 
     939 
     940See the documentation for InitializeGeoprocessor for more information about this 
     941property."""), 
     942    devTeamNotes=_( 
     943u"""Generally, GeoEco functions should *not* use this property; they should use 
     944WrappedGeoprocessor instead. That property implements a wrapper around the 
     945geoprocessor that logs debug messages every time the geoprocessor is invoked."""), 
     946    isExposedByIDispatch=True) 
     947 
     948AddPropertyMetadata(GeoprocessorManager.WrappedGeoprocessor, 
     949    typeMetadata=AnyPythonInstance(canBeNone=True), 
     950    shortDescription=_(u'The Geoprocessor property, wrapped by a class that logs messages whenever the geoprocessor is accessed.'), 
     951    longDescription=_( 
     952u"""This property is a singleton; all Python modules running in the same 
     953instance of the Python interpreter share the same value for this property. It is 
     954initialized whenever the Geoprocessor property is initialized; see the 
     955documentation for that property for more information. 
     956 
     957This property is actually a wrapper class around the ArcGIS geoprocessor object. 
     958The wrapper exports the same interface as the wrapped object but logs a debug 
     959message every time a method is invoked or a property is accessed. By default, 
     960these debug messages are discarded by the GeoEco logging infrastructure. You can 
     961enable them by initializing the Logger class with a custom configuration file 
     962or by editing the default configuration file. See the Logger class documentation 
     963for more information. 
     964 
     965All GeoEco functions use WrappedGeoprocessor to access the geoprocessor, rather 
     966than the Geoprocessor property, so that debug messages are reported whenever any 
     967function accesses the geoprocessor. External callers are welcome to use 
     968WrappedGeoprocessor as well, but they should consider using Geoprocessor 
     969instead, to completely eliminate the chance that a bug in the wrapper would 
     970affect their code."""), 
     971    devTeamNotes=_(u'Generally, GeoEco functions should use this property rather than the Geoprocessor property.'), 
     972    isExposedByIDispatch=True) 
     973 
     974AddPropertyMetadata(GeoprocessorManager.ArcGISMajorVersion, 
     975    typeMetadata=PythonInteger(canBeNone=True), 
     976    shortDescription=_(u'The major version number for ArcGIS, if it is installed on the machine.'), 
     977    longDescription=_(u'This property is empty if ArcGIS is not installed on the machine.'), 
     978    isExposedByIDispatch=True) 
     979 
     980AddPropertyMetadata(GeoprocessorManager.ArcGISMinorVersion, 
     981    typeMetadata=PythonInteger(canBeNone=True), 
     982    shortDescription=_(u'The minor version number for ArcGIS, if it is installed on the machine.'), 
     983    longDescription=_(u'This property is empty if ArcGIS is not installed on the machine.'), 
     984    isExposedByIDispatch=True) 
     985 
     986AddPropertyMetadata(GeoprocessorManager.ArcGISServicePack, 
     987    typeMetadata=PythonInteger(canBeNone=True), 
     988    shortDescription=_(u'The service pack number for ArcGIS, if it is installed on the machine.'), 
     989    longDescription=_(u'This property is empty if ArcGIS is not installed on the machine.'), 
     990    isExposedByIDispatch=True) 
     991 
     992# Public method: GetGeoprocessor 
     993 
     994AddMethodMetadata(GeoprocessorManager.GetGeoprocessor, 
     995    shortDescription=_(u'Returns the value of the Geoprocessor property.')) 
     996 
     997AddArgumentMetadata(GeoprocessorManager.GetGeoprocessor, u'cls', 
     998    typeMetadata=PythonClassOrClassInstance(cls=GeoprocessorManager), 
     999    description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__) 
     1000 
     1001AddResultMetadata(GeoprocessorManager.GetGeoprocessor, u'geoprocessor', 
     1002    typeMetadata=AnyPythonInstance(canBeNone=True), 
     1003    description=_(u'The value of the Geoprocessor property.')) 
     1004 
     1005# Public method: SetGeoprocessor 
     1006 
     1007AddMethodMetadata(GeoprocessorManager.SetGeoprocessor, 
     1008    shortDescription=_(u'Sets the value of the Geoprocessor property.')) 
     1009 
     1010AddArgumentMetadata(GeoprocessorManager.SetGeoprocessor, u'cls', 
     1011    typeMetadata=PythonClassOrClassInstance(cls=GeoprocessorManager), 
     1012    description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__) 
     1013 
     1014AddArgumentMetadata(GeoprocessorManager.SetGeoprocessor, u'geoprocessor', 
     1015    typeMetadata=AnyPythonInstance(), 
     1016    description=_(u'The ArcGIS geoprocessor object obtained from COM or the arcgisscripting Python module. See the documentation for the Geoprocessor property for more information.')) 
     1017 
     1018# Public method: GetWrappedGeoprocessor 
     1019 
     1020AddMethodMetadata(GeoprocessorManager.GetWrappedGeoprocessor, 
     1021    shortDescription=_(u'Returns the value of the WrappedGeoprocessor property.')) 
     1022 
     1023AddArgumentMetadata(GeoprocessorManager.GetWrappedGeoprocessor, u'cls', 
     1024    typeMetadata=PythonClassOrClassInstance(cls=GeoprocessorManager), 
     1025    description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__) 
     1026 
     1027AddResultMetadata(GeoprocessorManager.GetWrappedGeoprocessor, u'geoprocessor', 
     1028    typeMetadata=AnyPythonInstance(canBeNone=True), 
     1029    description=_(u'The value of the WrappedGeoprocessor property.')) 
     1030 
     1031# Public method: GetArcGISMajorVersion 
     1032 
     1033AddMethodMetadata(GeoprocessorManager.GetArcGISMajorVersion, 
     1034    shortDescription=_(u'Returns the value of the ArcGISMajorVersion property.')) 
     1035 
     1036AddArgumentMetadata(GeoprocessorManager.GetArcGISMajorVersion, u'cls', 
     1037    typeMetadata=PythonClassOrClassInstance(cls=GeoprocessorManager), 
     1038    description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__) 
     1039 
     1040AddResultMetadata(GeoprocessorManager.GetArcGISMajorVersion, u'majorVersion', 
     1041    typeMetadata=PythonInteger(canBeNone=True), 
     1042    description=_(u'The value of the ArcGISMajorVersion property.')) 
     1043 
     1044# Public method: GetArcGISMinorVersion 
     1045 
     1046AddMethodMetadata(GeoprocessorManager.GetArcGISMinorVersion, 
     1047    shortDescription=_(u'Returns the value of the ArcGISMinorVersion property.')) 
     1048 
     1049AddArgumentMetadata(GeoprocessorManager.GetArcGISMinorVersion, u'cls', 
     1050    typeMetadata=PythonClassOrClassInstance(cls=GeoprocessorManager), 
     1051    description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__) 
     1052 
     1053AddResultMetadata(GeoprocessorManager.GetArcGISMinorVersion, u'minorVersion', 
     1054    typeMetadata=PythonInteger(canBeNone=True), 
     1055    description=_(u'The value of the ArcGISMinorVersion property.')) 
     1056 
     1057# Public method: GetArcGISServicePack 
     1058 
     1059AddMethodMetadata(GeoprocessorManager.GetArcGISServicePack, 
     1060    shortDescription=_(u'Returns the value of the ArcGISServicePack property.')) 
     1061 
     1062AddArgumentMetadata(GeoprocessorManager.GetArcGISServicePack, u'cls', 
     1063    typeMetadata=PythonClassOrClassInstance(cls=GeoprocessorManager), 
     1064    description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__) 
     1065 
     1066AddResultMetadata(GeoprocessorManager.GetArcGISServicePack, u'servicePack', 
     1067    typeMetadata=PythonInteger(canBeNone=True), 
     1068    description=_(u'The value of the ArcGISMinorVersion property.')) 
     1069 
     1070# Public method: InitializeGeoprocessor 
     1071 
     1072AddMethodMetadata(GeoprocessorManager.InitializeGeoprocessor, 
     1073    shortDescription=_(u'Initializes the Geoprocessor property with a new ArcGIS geoprocessor object.'), 
     1074    longDescription=_( 
     1075u"""Generally, this method should only be invoked by GeoEco classes. There is 
     1076no need for external callers to invoke this function. They should allow GeoEco 
     1077classes to invoke it internally, or obtain their own goeprocessor object from 
     1078ArcGIS and then set the Geoprocessor property. 
     1079 
     1080If the Geoprocessor property is not empty, this method returns without doing 
     1081anything. If it is empty, this method attempts to obtain a geoprocessor object 
     1082using the following procedure: 
     1083 
     1084* Import the arcgisscripting Python package and invoke the create method. This 
     1085  will succeed if ArcGIS 9.2 or later is installed on the machine. If this 
     1086  succeeds, the returned object is an unknown type. (Note: because I have not 
     1087  tried ArcGIS 9.2 yet, I don't know what kind of object is returned. If it is 
     1088  not an IDispatch interface, then it cannot be returned to COM callers. If you 
     1089  are calling InitializeGeoprocessor using COM, I am not sure what will happen. 
     1090  I will investigate this as soon as I can install ArcGIS 9.2.) 
     1091 
     1092* If the step above fails, create the "esriGeoprocessing.GPDispatch" COM object 
     1093  using the Dispatch function provided by the win32com.client Python package. 
     1094  If this succeeds, the returned object is the COM IDispatch interface for the 
     1095  geoprocessor object."""), 
     1096    isExposedByIDispatch=True) 
     1097 
     1098AddArgumentMetadata(GeoprocessorManager.InitializeGeoprocessor, u'cls', 
     1099    typeMetadata=PythonClassOrClassInstance(cls=GeoprocessorManager), 
     1100    description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__) 
     1101 
     1102# Public method: InitializeGeoprocessorForDependency 
     1103 
     1104AddMethodMetadata(GeoprocessorManager.InitializeGeoprocessorForDependency, 
     1105    shortDescription=_(u'Initializes the Geoprocessor property with a new ArcGIS geoprocessor object on behalf of the specified dependency declared by a GeoEco class method.'), 
     1106    longDescription=_(u'This method should only be invoked directly by the GeoEco.ArcGIS.ArcGISVersion class.')) 
     1107 
     1108AddArgumentMetadata(GeoprocessorManager.InitializeGeoprocessorForDependency, u'cls', 
     1109    typeMetadata=PythonClassOrClassInstance(cls=GeoprocessorManager), 
     1110    description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__) 
     1111 
     1112AddArgumentMetadata(GeoprocessorManager.InitializeGeoprocessorForDependency, u'dependency', 
     1113    typeMetadata=PythonClassInstance(cls=GeoEco.Metadata.Dependency, canBeNone=True), 
     1114    description=_(u'An instance of the %s class, or None if the invocation is not occurring on behalf of a dependency.') % GeoEco.Metadata.Dependency.__name__) 
  • MGET/Trunk/PythonPackage/src/GeoEco/Configuration/Logging.txt

    r18 r25  
    2323# impossible to track them down. For this reason, ALWAYS make a backup 
    2424# copy of your file and change JUST A LITTLE AT A TIME so that 
    25 # mistakes are easier to tracdk down. 
     25# mistakes are easier to track down. 
    2626 
    27 # The handlers specify the types of destinations of log messages: 
     27# The handlers specify the possible destinations for log messages. Python 
     28# provides more than the default ones you see here: 
    2829 
    2930[handlers] 
     
    5455 
    5556[loggers] 
    56 keys=root,GeoEco,ArcGIS 
     57keys=root,GeoEco,ArcGIS,COM 
    5758 
    5859# Log GeoEco informational, warning and error messages to the console. 
    5960# and to ArcGIS. To log debug messages as well, change "level=INFO" 
    6061# to "level=DEBUG". But rather than editing the default GeoEco logging 
    61 # configuration file, consider making a copy  
     62# configuration file, consider making a copy and explicitly initializing the 
     63# logging system with your own file. 
     64
     65# Note: During the initial GeoEco development cycle, we are setting level=DEBUG 
     66# instead of level=INFO. 
    6267 
    6368[logger_GeoEco] 
    64 level=INFO 
     69level=DEBUG 
    6570handlers=Console,ArcGIS 
    6671propagate=0 
     
    6873 
    6974# Only log warnings and errors from GeoEco.ArcGIS. Change "level=WARNING" 
    70 # to "level=DEBUG" to see a complete trace of interactions between GeoEco 
    71 # and the ArcGIS geoprocessor. 
     75# to "level=INFO" to see the messages reported by ArcGIS any time GeoEco invokes 
     76# an ArcGIS geoprocessing tool. Change it to "level=DEBUG" to see a complete 
     77# low-level trace of all interactions between GeoEco and the ArcGIS 
     78# geoprocessor (a message will be generated every time GeoEco accesses a 
     79# property or invokes a method of the geoprocessor). 
     80
     81# Note: During the initial GeoEco development cycle, we are setting level=DEBUG 
     82# instead of level=WARNING. 
    7283 
    7384[logger_ArcGIS] 
    74 level=INFO 
     85level=DEBUG 
    7586handlers=Console,ArcGIS 
    7687propagate=0 
    7788qualname=GeoEco.ArcGIS 
     89 
     90# Only log warnings and errors from GeoEco.COM. Change "level=WARNING" 
     91# to "level=DEBUG" to see a complete low-level trace of calls to properties and 
     92# methods of GeoEco classes made through COM. This is useful for debugging 
     93# obscure COM problems but not much else. 
     94# 
     95# WARNING: *DO NOT* add ArcGIS to the list of handlers; you will create an 
     96# infinite loop! 
     97# 
     98# Note: During the initial GeoEco development cycle, we are setting level=DEBUG 
     99# instead of level=WARNING. 
     100 
     101[logger_COM] 
     102level=DEBUG 
     103handlers=Console 
     104propagate=0 
     105qualname=GeoEco.COM 
    78106 
    79107# Log warnings and errors from any non-GeoEco component to the console. 
  • MGET/Trunk/PythonPackage/src/GeoEco/Dependencies.py

    r16 r25  
    5151                if self.MinimumServicePack > 0: 
    5252                    osName = _(u'%s Service Pack %i') % (osName, self.MinimumServicePack) 
    53                 raise UnsupportedPlatformError(_(u'This tool requires Microsoft Windows %s, or a later version of Windows.') % osName, self) 
     53                raise UnsupportedPlatformError(_(u'This function requires Microsoft Windows %s, or a later version of Windows.') % osName, self) 
    5454 
    5555             
     
    6666        patchlevel = self.__ParseVersionNumber(v[2]) 
    6767        if self.MinimumVersion[0] > major or self.MinimumVersion[0] == major and self.MinimumVersion[1] > minor or self.MinimumVersion[0] == major and self.MinimumVersion[1] == minor and self.MinimumVersion[2] > patchlevel: 
    68             raise SoftwareNotInstalledError(_(u'This tool requires Python version %i.%i.%i (or newer). Version %s is currently running. Please ensure the required version (or newer) is installed. Also ensure that the GeoEco package is installed under that Python installation (if you reinstall Python, you have to reinstall GeoEco). Finally, ensure the operating system is configured to invoke the required version of Python when it interprets Python scripts, rather than an older version of Python. On Windows, you must configure a "file association" that associates the newer version of Python with .py files.') % (self.MinimumVersion[0], self.MinimumVersion[1], self.MinimumVersion[2], platform.python_version()), self) 
     68            raise SoftwareNotInstalledError(_(u'This function requires Python version %i.%i.%i (or newer). Version %s is currently running. Please ensure the required version (or newer) is installed. Also ensure that the GeoEco package is installed under that Python installation (if you reinstall Python, you have to reinstall GeoEco). Finally, ensure the operating system is configured to invoke the required version of Python when it interprets Python scripts, rather than an older version of Python. On Windows, you must configure a "file association" that associates the newer version of Python with .py files.') % (self.MinimumVersion[0], self.MinimumVersion[1], self.MinimumVersion[2], platform.python_version()), self) 
    6969 
    7070    @classmethod 
     
    9797        except Exception, e: 
    9898            if self.DisplayName is None: 
    99                 message = _(u'This tool requires the "%s" Python package. Please verify that it is properly installed for the running version of Python (%s.%s).') % (self.ImportName, unicode(platform.python_version_tuple()[0]), unicode(platform.python_version_tuple()[1])) 
     99                message = _(u'This function requires the "%s" Python package. Please verify that it is properly installed for the running version of Python (%s.%s).') % (self.ImportName, unicode(platform.python_version_tuple()[0]), unicode(platform.python_version_tuple()[1])) 
    100100            else: 
    101                 message = _(u'This tool requires the "%s" Python package. Please verify that it is properly installed for the running version of Python (%s.%s).') % (self.DisplayName, unicode(platform.python_version_tuple()[0]), unicode(platform.python_version_tuple()[1])) 
     101                message = _(u'This function requires the "%s" Python package. Please verify that it is properly installed for the running version of Python (%s.%s).') % (self.DisplayName, unicode(platform.python_version_tuple()[0]), unicode(platform.python_version_tuple()[1])) 
    102102            if self.CheeseShopName is not None and self.AlternateURL is None: 
    103103                message = _(u'%s The package may be available at http://www.python.org/pypi/%s.') % (message, self.CheeseShopName) 
  • MGET/Trunk/PythonPackage/src/GeoEco/Exceptions.py

    r16 r25  
    1919 
    2020    def __unicode__(self): 
    21         return _(u'Execution failed because this tool is running on an unsupported operating system or processor architecture. %s') % self.Message 
     21        return _(u'Execution failed because this function is running on an unsupported operating system or processor architecture. %s') % self.Message 
    2222 
    2323    def AsCOMException(): 
  • MGET/Trunk/PythonPackage/src/GeoEco/Logging.py

    r16 r25  
    77import GeoEco 
    88import GeoEco.ArcGIS 
     9from GeoEco.Metadata import * 
    910from GeoEco.Internationalization import _ 
    1011 
    1112 
    12 # Public functions exported by this module 
    13  
    14 def LogDebug(format, *args, **kwargs): 
    15     try: 
    16         logging.getLogger(u'GeoEco').debug(format, *args, **kwargs) 
    17     except: 
    18         pass 
    19  
    20 def LogInfo(format, *args, **kwargs): 
    21     try: 
    22         logging.getLogger(u'GeoEco').info(format, *args, **kwargs) 
    23     except: 
    24         pass 
    25  
    26 def LogWarning(format, *args, **kwargs): 
    27     try: 
    28         logging.getLogger(u'GeoEco').warning(format, *args, **kwargs) 
    29     except: 
    30         pass 
    31  
    32 def LogError(format, *args, **kwargs): 
    33     try: 
    34         logging.getLogger(u'GeoEco').error(format, *args, **kwargs) 
    35     except: 
    36         pass 
    37  
    38 def InitializeLogging(loggingConfigFile=None): 
    39  
    40     if not isinstance(loggingConfigFile, (types.NoneType, types.UnicodeType)): 
    41         raise TypeError, _(u'loggingConfigFile, if provided, must be a Unicode string') 
    42  
    43     # Hack: this module (GeoEco.Logging) defines a class _ArcGISLoggingHandler 
    44     # which derives from logging.Handler. We want the user to be able to 
    45     # reference this handler in a logging configuration file. But the logging.config 
    46     # module offers no way to import this module into its namespace, which 
    47     # prevents it from being able to instantiate our class. So we must manually 
    48     # import ourself into the logging namespace (! not logging.config !). 
    49  
    50     if not sys.modules[u'logging'].__dict__.has_key(u'GeoEco'): 
    51         sys.modules[u'logging'].__dict__[u'GeoEco'] = sys.modules[u'GeoEco'] 
    52     if not sys.modules[u'logging'].__dict__.has_key(u'GeoEco.Logging'): 
    53         sys.modules[u'logging'].__dict__[u'GeoEco.Logging'] = sys.modules[u'GeoEco.Logging'] 
    54  
    55     # If the caller provided a file, try to initialize the logging system 
    56     # from it. 
    57  
    58     callersFileWarning = None 
    59     if loggingConfigFile is not None: 
    60         callersFileWarning = _InitializeLoggingFromFile(loggingConfigFile) 
    61  
    62     # If the caller did not provide a file, or the logging system could not 
    63     # be initialized from it, try the default logging config file. 
    64  
    65     defaultFileWarning = None         
    66     defaultLoggingConfigFile = os.path.join(GeoEco.ConfigurationDir, u'Logging.txt') 
    67     if loggingConfigFile is None or callersFileWarning is not None: 
    68         defaultFileWarning = _InitializeLoggingFromFile(defaultLoggingConfigFile) 
    69  
    70     # If we failed to initialize from either the caller's file or the 
    71     # default file, initialize using a hard-coded configuration. 
    72  
    73     manualInitializationWarning = None 
     13# Public classes exposed by this module 
     14 
     15class Logger(object): 
     16    __doc__ = GeoEco.Metadata.DynamicDocString() 
    7417     
    75     if (loggingConfigFile is None or callersFileWarning is not None) and defaultFileWarning is not None: 
    76         try: 
    77             stdout_handler = logging.StreamHandler(strm=sys.stdout) 
    78             stdout_handler.level = logging.INFO 
    79             logging.getLogger(u'').addHandler(stdout_handler) 
    80             logging.getLogger(u'').setLevel(logging.INFO) 
     18    @classmethod 
     19    def Debug(cls, format, *args): 
     20        try: 
     21            logging.getLogger(u'GeoEco').debug(format, *args) 
     22        except: 
     23            pass 
     24 
     25    @classmethod 
     26    def Info(cls, format, *args): 
     27        try: 
     28            logging.getLogger(u'GeoEco').info(format, *args) 
     29        except: 
     30            pass 
     31 
     32    @classmethod 
     33    def Warning(cls, format, *args): 
     34        try: 
     35            logging.getLogger(u'GeoEco').warning(format, *args) 
     36        except: 
     37            pass 
     38 
     39    @classmethod 
     40    def Error(cls, format, *args): 
     41        try: 
     42            logging.getLogger(u'GeoEco').error(format, *args) 
     43        except: 
     44            pass 
     45 
     46    @classmethod 
     47    def Initialize(cls, loggingConfigFile=None, activateArcGISLogging=False): 
     48 
     49        if not isinstance(loggingConfigFile, (types.NoneType, types.UnicodeType)): 
     50            raise TypeError, _(u'loggingConfigFile, if provided, must be a Unicode string') 
     51 
     52        if not isinstance(activateArcGISLogging, (types.NoneType, types.BooleanType)): 
     53            raise TypeError, _(u'activateArcGISLogging, if provided, must be a boolean') 
     54 
     55        # Hack: this module (GeoEco.Logging) defines a class _ArcGISLoggingHandler 
     56        # which derives from logging.Handler. We want the user to be able to 
     57        # reference this handler in a logging configuration file. But the logging.config 
     58        # module offers no way to import this module into its namespace, which 
     59        # prevents it from being able to instantiate our class. So we must manually 
     60        # import ourself into the logging namespace (! not logging.config !). 
     61 
     62        if not sys.modules[u'logging'].__dict__.has_key(u'GeoEco'): 
     63            sys.modules[u'logging'].__dict__[u'GeoEco'] = sys.modules[u'GeoEco'] 
     64        if not sys.modules[u'logging'].__dict__.has_key(u'GeoEco.Logging'): 
     65            sys.modules[u'logging'].__dict__[u'GeoEco.Logging'] = sys.modules[u'GeoEco.Logging'] 
     66 
     67        # If the caller provided a file, try to initialize the logging system 
     68        # from it. 
     69 
     70        callersFileWarning = None 
     71        if loggingConfigFile is not None: 
     72            callersFileWarning = cls._InitializeLoggingFromFile(loggingConfigFile) 
     73 
     74        # If the caller did not provide a file, or the logging system could not 
     75        # be initialized from it, try the default logging config file. 
     76 
     77        defaultFileWarning = None         
     78        defaultLoggingConfigFile = os.path.join(GeoEco.ConfigurationDir, u'Logging.txt') 
     79        if loggingConfigFile is None or callersFileWarning is not None: 
     80            defaultFileWarning = cls._InitializeLoggingFromFile(defaultLoggingConfigFile) 
     81 
     82        # If we failed to initialize from either the caller's file or the 
     83        # default file, initialize using a hard-coded configuration. 
     84 
     85        manualInitializationWarning = None 
     86         
     87        if (loggingConfigFile is None or callersFileWarning is not None) and defaultFileWarning is not None: 
     88            try: 
     89                stdout_handler = logging.StreamHandler(strm=sys.stdout) 
     90                stdout_handler.level = logging.INFO 
     91                logging.getLogger(u'').addHandler(stdout_handler) 
     92                logging.getLogger(u'').setLevel(logging.INFO) 
     93            except Exception, e: 
     94                manualInitializationWarning = _(u'Failed to initialize the logging system from hard-coded settings. One of the logging functions reported the error: ') + unicode(e) 
     95 
     96        # Log warning messages, if any, and a success message. We have to delay of 
     97        # all this until now, because until this point, the logging system may not 
     98        # have been initialized. If we were able to initialize it, we want any 
     99        # warning messages to be logged using the best possible settings. 
     100 
     101        if (loggingConfigFile is None or callersFileWarning is not None) and defaultFileWarning is not None and manualInitializationWarning is not None: 
     102            if callersFileWarning is not None: 
     103                print(callersFileWarning) 
     104            print(defaultFileWarning) 
     105            print(manualInitializationWarning) 
     106            print(_(u'The logging system could not be initialized. Log messages will not be reported.')) 
     107        elif loggingConfigFile is not None and callersFileWarning is None: 
     108            cls.Info(_(u'Logging system initialized from config file "%s".'), loggingConfigFile) 
     109        elif defaultFileWarning is None: 
     110            if callersFileWarning is not None: 
     111                cls.Warning(callersFileWarning) 
     112            cls.Info(_(u'Logging system initialized from config file "%s".'), defaultLoggingConfigFile) 
     113        elif manualInitializationWarning is None: 
     114            if callersFileWarning is not None: 
     115                cls.Warning(callersFileWarning) 
     116            if defaultFileWarning is not None: 
     117                cls.Warning(defaultFileWarning) 
     118            cls.Info(_(u'Log messages will only be sent to the console output (stdout).')) 
     119        else: 
     120            raise AssertionError, u'This line of code should never be executed.' 
     121 
     122        # Active ArcGIS logging if requested. 
     123 
     124        if activateArcGISLogging: 
     125            cls.ActivateArcGISLogging() 
     126 
     127    @classmethod 
     128    def ActivateArcGISLogging(cls): 
     129        _ArcGISLoggingHandler.Activate() 
     130 
     131    @classmethod 
     132    def _InitializeLoggingFromFile(cls, loggingConfigFile=None): 
     133        assert isinstance(loggingConfigFile, types.UnicodeType), u'loggingConfigFile must be a Unicode string' 
     134 
     135        # Try to open the file for reading, so we know it is accessible. 
     136 
     137        try: 
     138            f = file(loggingConfigFile, u'r') 
    81139        except Exception, e: 
    82             manualInitializationWarning = _(u'Failed to initialize the logging system from hard-coded settings. One of the logging functions reported the error: ') + unicode(e) 
    83  
    84     # Log warning messages, if any, and a success message. We have to delay of 
    85     # all this until now, because until this point, the logging system may not 
    86     # have been initialized. If we were able to initialize it, we want any 
    87     # warning messages to be logged using the best possible settings. 
    88  
    89     if (loggingConfigFile is None or callersFileWarning is not None) and defaultFileWarning is not None and manualInitializationWarning is not None: 
    90         if callersFileWarning is not None: 
    91             print(callersFileWarning) 
    92         print(defaultFileWarning) 
    93         print(manualInitializationWarning) 
    94         print(_(u'The logging system could not be initialized. Log messages will not be reported.')) 
    95     elif loggingConfigFile is not None and callersFileWarning is None: 
    96         LogInfo(_(u'Logging system initialized from config file "%s".'), loggingConfigFile) 
    97     elif defaultFileWarning is None: 
    98         if callersFileWarning is not None: 
    99             LogWarning(callersFileWarning) 
    100         LogInfo(_(u'Logging system initialized from config file "%s".'), defaultLoggingConfigFile) 
    101     elif manualInitializationWarning is None: 
    102         if callersFileWarning is not None: 
    103             LogWarning(callersFileWarning) 
    104         if defaultFileWarning is not None: 
    105             LogWarning(defaultFileWarning) 
    106         LogInfo(_(u'Log messages will only be sent to the console output (stdout).')) 
    107     else: 
    108         raise AssertionError, u'This line of code should never be executed.' 
    109  
    110 def ActivateArcGISLogging(): 
    111     _ArcGISLoggingHandler.Activate() 
     140            return(_(u'Failed to initialize logging from the config file "%s". The file could not be opened. The operating system reported the error: %s') % (loggingConfigFile, unicode(e).strip())) 
     141        try: 
     142            f.close() 
     143        except: 
     144            pass 
     145 
     146        # If that was successful, try to initialize logging. 
     147        # 
     148        # If the logging config file is improperly formatted, the logging 
     149        # initialization function can fail in two ways. First, it may raise an 
     150        # exception. In this case, catch it as usual and return a failure 
     151        # message. 
     152        # 
     153        # In the second failure mode, it simply prints an exception trace to 
     154        # stderr, but does not raise an exception or return a value indicating 
     155        # failure. This would be ok, except that it can leave the logging system 
     156        # in an inconsistent state: subsequent calls to logging functions such 
     157        # as logging.debug() will fail. Again, these failed calls swallow 
     158        # exceptions, just printing their traces to stderr. 
     159        # 
     160        # Because we don't want stderr to be spammed when logging fails to 
     161        # initialize, we detect the failure in the logging configuration 
     162        # function by capturing stderr, and return a failure message. 
     163 
     164        cap = _StderrCapturer() 
     165        cap.Start() 
     166        try: 
     167            try: 
     168                # First handle a bug in logging.config.fileConfig that causes the 
     169                # logging Handler class to raise an exception at shutdown. This bug 
     170                # was reported in Aug 2006 and exists in Python 2.4.4 but is 
     171                # supposedly fixed in Python 2.5. 
     172                 
     173                if globals().has_key(u'_TempHandler') and globals()[u'_TempHandler'] is not None: 
     174                    logging.getLogger(u'GeoEco').removeHandler(globals()[u'_TempHandler']) 
     175                    globals()[u'_TempHandler'].close() 
     176                    del globals()[u'_TempHandler'] 
     177 
     178                # Now read the config file. 
     179                 
     180                logging.config.fileConfig(loggingConfigFile) 
     181                 
     182            except Exception, e: 
     183                return _(u'Failed to initialize logging from the config file "%s". Please verify that the contents of the config file are valid. Search the Python documentation for "logging configuration file format". In Python 2.4, the article is titled "6.29.10.2 Configuration file format". Python\'s log configuration file parser (logging.config.fileConfig) reported the error: %s') % (loggingConfigFile, unicode(e).strip()) 
     184        finally: 
     185            result = cap.Stop() 
     186 
     187        if result is not None and len(result.strip()) > 0: 
     188            result_lines = result.strip().split(u'\n') 
     189            i = 1 
     190            while i < len(result_lines) and (len(result_lines[i]) == 0 or result_lines[i][0] == u' '): 
     191                i = i + 1 
     192            message = u'' 
     193            for j in range(i, len(result_lines)): 
     194                message = message + result_lines[j] + u'\n' 
     195            message = message.strip() 
     196            return _(u'Failed to initialize logging from the config file "%s". Please verify that the contents of the config file are valid. Search the Python documentation for "logging configuration file format". In Python 2.4, the article is titled "6.29.10.2 Configuration file format". Python\'s log configuration file parser (logging.config.fileConfig) reported the error: %s') % (loggingConfigFile, message) 
     197 
     198        # Return successfully. 
     199 
     200        return None 
     201 
    112202 
    113203# Private classes and functions global to this module 
     
    137227            message = self.format(record) 
    138228            if record.levelno >= logging.ERROR: 
    139                 GeoEco.ArcGIS.GetRealGeoprocessor().AddError(message) 
     229                GeoEco.ArcGIS.GeoprocessorManager.GetGeoprocessor().AddError(message) 
    140230            elif record.levelno >= logging.WARNING: 
    141                 GeoEco.ArcGIS.GetRealGeoprocessor().AddWarning(message) 
     231                GeoEco.ArcGIS.GeoprocessorManager.GetGeoprocessor().AddWarning(message) 
    142232            else: 
    143                 GeoEco.ArcGIS.GetRealGeoprocessor().AddMessage(message) 
     233                GeoEco.ArcGIS.GeoprocessorManager.GetGeoprocessor().AddMessage(message) 
    144234        except: 
    145235            self.handleError(record) 
     
    152242    def Activate(cls): 
    153243        if cls._PreactivationQueue is not None: 
    154             if GeoEco.ArcGIS.GetRealGeoprocessor() is None: 
    155                 GeoEco.ArcGIS.InitializeGeoprocessor() 
     244            if GeoEco.ArcGIS.GeoprocessorManager.GetGeoprocessor() is None: 
     245                GeoEco.ArcGIS.GeoprocessorManager.InitializeGeoprocessor() 
    156246            while len(cls._PreactivationQueue) > 0: 
    157247                record = cls._PreactivationQueue.pop(0) 
     
    163253    _PreactivationQueue = [] 
    164254 
    165 def _InitializeLoggingFromFile(loggingConfigFile=None): 
    166  
    167     assert isinstance(loggingConfigFile, types.UnicodeType), u'loggingConfigFile must be a Unicode string' 
    168  
    169     # Try to open the file for reading, so we know it is accessible. 
    170  
    171     try: 
    172         f = file(loggingConfigFile, u'r') 
    173     except Exception, e: 
    174         return(_(u'Failed to initialize logging from the config file "%s". The file could not be opened. The operating system reported the error: %s') % (loggingConfigFile, unicode(e).strip())) 
    175     try: 
    176         f.close() 
    177     except: 
    178         pass 
    179  
    180     # If that was successful, try to initialize logging. 
    181     # 
    182     # If the logging config file is improperly formatted, the logging 
    183     # initialization function can fail in two ways. First, it may raise an 
    184     # exception. In this case, catch it as usual and return a failure 
    185     # message. 
    186     # 
    187     # In the second failure mode, it simply prints an exception trace to 
    188     # stderr, but does not raise an exception or return a value indicating 
    189     # failure. This would be ok, except that it can leave the logging system 
    190     # in an inconsistent state: subsequent calls to logging functions such 
    191     # as logging.debug() will fail. Again, these failed calls swallow 
    192     # exceptions, just printing their traces to stderr. 
    193     # 
    194     # Because we don't want stderr to be spammed when logging fails to 
    195     # initialize, we detect the failure in the logging configuration 
    196     # function by capturing stderr, and return a failure message. 
    197  
    198     cap = StderrCapturer() 
    199     cap.Start() 
    200     try: 
    201         try: 
    202             # First handle a bug in logging.config.fileConfig that causes the 
    203             # logging Handler class to raise an exception at shutdown. This bug 
    204             # was reported in Aug 2006 and exists in Python 2.4.4 but is 
    205             # supposedly fixed in Python 2.5. 
    206              
    207             if globals().has_key(u'_TempHandler') and globals()[u'_TempHandler'] is not None: 
    208                 logging.getLogger(u'GeoEco').removeHandler(globals()[u'_TempHandler']) 
    209                 globals()[u'_TempHandler'].close() 
    210                 del globals()[u'_TempHandler'] 
    211  
    212             # Now read the config file. 
    213              
    214             logging.config.fileConfig(loggingConfigFile) 
    215              
    216         except Exception, e: 
    217             return _(u'Failed to initialize logging from the config file "%s". Please verify that the contents of the config file are valid. Search the Python documentation for "logging configuration file format". In Python 2.4, the article is titled "6.29.10.2 Configuration file format". Python\'s log configuration file parser (logging.config.fileConfig) reported the error: %s') % (loggingConfigFile, unicode(e).strip()) 
    218     finally: 
    219         result = cap.Stop() 
    220  
    221     if result is not None and len(result.strip()) > 0: 
    222         result_lines = result.strip().split(u'\n') 
    223         i = 1 
    224         while i < len(result_lines) and (len(result_lines[i]) == 0 or result_lines[i][0] == u' '): 
    225             i = i + 1 
    226         message = u'' 
    227         for j in range(i, len(result_lines)): 
    228             message = message + result_lines[j] + u'\n' 
    229         message = message.strip() 
    230         return _(u'Failed to initialize logging from the config file "%s". Please verify that the contents of the config file are valid. Search the Python documentation for "logging configuration file format". In Python 2.4, the article is titled "6.29.10.2 Configuration file format". Python\'s log configuration file parser (logging.config.fileConfig) reported the error: %s') % (loggingConfigFile, message) 
    231  
    232     # Return successfully. 
    233  
    234     return None 
    235  
    236 class StderrCapturer(object): 
     255 
     256class _StderrCapturer(object): 
    237257 
    238258    def __init__(self): 
     
    260280        else: 
    261281            return None 
     282 
    262283 
    263284# Initialize, but do not activate, the ArcGIS logging handler. Until the handler 
     
    278299except: 
    279300    pass 
     301 
     302 
     303############################################################################### 
     304# Metadata: module 
     305############################################################################### 
     306 
     307AddModuleMetadata(shortDescription=_(u'Implements the Logger class, which other GeoEco classes use to report activity to the user.')) 
     308 
     309############################################################################### 
     310# Metadata: Logger class 
     311############################################################################### 
     312 
     313AddClassMetadata(Logger, 
     314    shortDescription=_(u'Provides methods for reporting messages to the user.'), 
     315    isExposedByIDispatch=True, 
     316    comIID=u'{0DA39145-CDE6-48F2-AFD9-8EA6623A3121}', 
     317    comCLSID=u'{EE5ED6B5-55E7-4D65-B627-EF062B8332C4}') 
     318 
     319# Public method: Debug 
     320 
     321AddMethodMetadata(Logger.Debug, 
     322    shortDescription=_(u'Reports a debugging message to the user.'), 
     323    longDescription=_( 
     324u"""Like the C printf function or the Python % operator, this method generates a 
     325message string by merging in the optional arguments into the format string. 
     326 
     327Debugging messages describe processing details that are usually not interesting 
     328unless the user is diagnosing a problem. The default configuration of the 
     329logging system causes debugging messages to be discarded."""), 
     330    isExposedByIDispatch=True) 
     331 
     332AddArgumentMetadata(Logger.Debug, u'cls', 
     333    typeMetadata=PythonClassOrClassInstance(cls=Logger), 
     334    description=_(u'%s class or an instance of it.') % Logger.__name__) 
     335 
     336AddArgumentMetadata(Logger.Debug, u'format', 
     337    typeMetadata=PythonUnicodeString(), 
     338    description=_( 
     339u"""A printf-style format string. While Unicode strings are encouraged, this 
     340method will also accept an 8-bit string for this parameter. For a complete 
     341specification of the format of this string, look up "% formatting" in the Python 
     342documentation.""")) 
     343 
     344AddArgumentMetadata(Logger.Debug, u'args', 
     345    typeMetadata=PythonTuple(elementType=AnyPythonInstance(canBeNone=True)), 
     346    description=_(u'Values to insert into the format string.')) 
     347 
     348# Public method: Info 
     349 
     350AddMethodMetadata(Logger.Info, 
     351    shortDescription=_(u'Reports an informational message to the user.'), 
     352    longDescription=_( 
     353u"""Like the C printf function or the Python % operator, this method generates a 
     354message string by merging in the optional arguments into the format string. 
     355 
     356Informational messages describe major processing steps that may be interesting to 
     357the user but do not require the user to take any action. For example, a method 
     358that performs three major processing tasks might report an informational message 
     359after each step is finished. Do not report too many informational messages or 
     360you may overwhelm the user. Report processing details as debug messages."""), 
     361    isExposedByIDispatch=True) 
     362 
     363AddArgumentMetadata(Logger.Info, u'cls', 
     364    typeMetadata=PythonClassOrClassInstance(cls=Logger), 
     365    description=_(u'%s class or an instance of it.') % Logger.__name__) 
     366 
     367AddArgumentMetadata(Logger.Info, u'format', 
     368    typeMetadata=PythonUnicodeString(), 
     369    description=_( 
     370u"""A printf-style format string. While Unicode strings are encouraged, this 
     371method will also accept an 8-bit string for this parameter. For a complete 
     372specification of the format of this string, look up "% formatting" in the Python 
     373documentation.""")) 
     374 
     375AddArgumentMetadata(Logger.Info, u'args', 
     376    typeMetadata=PythonTuple(elementType=AnyPythonInstance(canBeNone=True)), 
     377    description=_(u'Values to insert into the format string.')) 
     378 
     379# Public method: Warning 
     380 
     381AddMethodMetadata(Logger.Warning, 
     382    shortDescription=_(u'Reports a warning message to the user.'), 
     383    longDescription=_( 
     384u"""Like the C printf function or the Python % operator, this method generates a 
     385message string by merging in the optional arguments into the format string. 
     386 
     387Warning messages describe important events that should be brought to the user's 
     388attention but do not necessarily indicate that processing will fail."""), 
     389    isExposedByIDispatch=True) 
     390 
     391AddArgumentMetadata(Logger.Warning, u'cls', 
     392    typeMetadata=PythonClassOrClassInstance(cls=Logger), 
     393    description=_(u'%s class or an instance of it.') % Logger.__name__) 
     394 
     395AddArgumentMetadata(Logger.Warning, u'format', 
     396    typeMetadata=PythonUnicodeString(), 
     397    description=_( 
     398u"""A printf-style format string. While Unicode strings are encouraged, this 
     399method will also accept an 8-bit string for this parameter. For a complete 
     400specification of the format of this string, look up "% formatting" in the Python 
     401documentation.""")) 
     402 
     403AddArgumentMetadata(Logger.Warning, u'args', 
     404    typeMetadata=PythonTuple(elementType=AnyPythonInstance(canBeNone=True)), 
     405    description=_(u'Values to insert into the format string.')) 
     406 
     407# Public method: Error 
     408 
     409AddMethodMetadata(Logger.Error, 
     410    shortDescription=_(u'Reports an error message to the user.'), 
     411    longDescription=_( 
     412u"""Like the C printf function or the Python % operator, this method generates a 
     413message string by merging in the optional arguments into the format string. 
     414 
     415Error messages describe failures that halt processing and require the user to 
     416fix a problem and restart processing."""), 
     417    isExposedByIDispatch=True) 
     418 
     419AddArgumentMetadata(Logger.Error, u'cls', 
     420    typeMetadata=PythonClassOrClassInstance(cls=Logger), 
     421    description=_(u'%s class or an instance of it.') % Logger.__name__) 
     422 
     423AddArgumentMetadata(Logger.Error, u'format', 
     424    typeMetadata=PythonUnicodeString(), 
     425    description=_( 
     426u"""A printf-style format string. While Unicode strings are encouraged, this 
     427method will also accept an 8-bit string for this parameter. For a complete 
     428specification of the format of this string, look up "% formatting" in the Python 
     429documentation.""")) 
     430 
     431AddArgumentMetadata(Logger.Error, u'args', 
     432    typeMetadata=PythonTuple(elementType=AnyPythonInstance(canBeNone=True)), 
     433    description=_(u'Values to insert into the format string.')) 
     434 
     435# Public method: Initialize 
     436 
     437AddMethodMetadata(Logger.Initialize, 
     438    shortDescription=_(u'Initializes the logging system.'), 
     439    longDescription=_( 
     440u"""Like the C printf function or the Python % operator, this method generates a 
     441message string by merging in the optional arguments into the format string. 
     442 
     443Error messages describe failures that halt processing and require the user to 
     444fix a problem and restart processing."""), 
     445    isExposedByIDispatch=True) 
     446 
     447AddArgumentMetadata(Logger.Initialize, u'cls', 
     448    typeMetadata=PythonClassOrClassInstance(cls=Logger), 
     449    description=_(u'%s class or an instance of it.') % Logger.__name__) 
     450 
     451AddArgumentMetadata(Logger.Initialize, u'loggingConfigFile', 
     452    typeMetadata=PythonUnicodeString(canBeNone=True), 
     453    description=_( 
     454u"""Path to the logging configuration file. If not provided, the default logging 
     455configuration file will be used. The default file, Logging.txt, is located in 
     456the Configuration subdirectory of the GeoEco Python package subdirectory.""")) 
     457 
     458AddArgumentMetadata(Logger.Initialize, u'activateArcGISLogging', 
     459    typeMetadata=PythonBoolean(canBeNone=True), 
     460    description=_( 
     461u"""If true, logging messages will be delivered to the ArcGIS geoprocessing 
     462system and appear in the ArcGIS user interface. If false, they will be reported 
     463to other logging destinations but will be queued in memory for ArcGIS until the 
     464ActivateArcGISLogging method is called. To limit memory consumption in the event 
     465that the method is never called, the queue retains only the most recent 1000 
     466messages. If you are calling Initialize but not running as part of an ArcGIS 
     467geoprocessing *do not* pass true for this parameter. Passing true will decrease 
     468performance by initializing the ArcGIS geoprocessor when it might not be 
     469necessary to do so. (The memory used by the queue is negligible.)""")) 
  • MGET/Trunk/PythonPackage/src/GeoEco/Metadata.py

    r24 r25  
    310310    __doc__ = DynamicDocString() 
    311311     
    312     def __init__(self, name, moduleMetadata=None, shortDescription=None, longDescription=None, devTeamNotes=None): 
     312    def __init__(self, name, moduleMetadata=None, shortDescription=None, longDescription=None, devTeamNotes=None, isExposedByIDispatch=False, comIID=None, comCLSID=None, comVersionIndependentProgID=None, comVersionDependentProgID=None): 
    313313 
    314314        # If the caller did not provide a ModuleMetadata instance for the module 
     
    325325        assert moduleMetadata.Object.__dict__.has_key(name) and inspect.isclass(moduleMetadata.Object.__dict__[name]), u'Module %s must contain a class named %s.' % (moduleMetadata.Name, name) 
    326326        self._Module = moduleMetadata 
     327        self.IsExposedByIDispatch = isExposedByIDispatch 
     328        self.COMIID = comIID 
     329        self.COMCLSID = comCLSID 
     330        self.COMVersionIndependentProgID = comVersionIndependentProgID 
     331        self.COMVersionDependentProgID = comVersionDependentProgID 
    327332 
    328333    def _GetModule(self): 
     
    330335     
    331336    Module = property(_GetModule, doc=DynamicDocString()) 
     337 
     338    def _GetIsExposedByIDispatch(self): 
     339        return self._IsExposedByIDispatch 
     340     
     341    def _SetIsExposedByIDispatch(self, value): 
     342        assert isinstance(value, types.BooleanType), u'IsExposedByIDispatch must be a %s.' % types.BooleanType.__name__ 
     343        self._IsExposedByIDispatch = value 
     344         
     345    IsExposedByIDispatch = property(_GetIsExposedByIDispatch, _SetIsExposedByIDispatch, doc=DynamicDocString()) 
     346 
     347    def _GetCOMIID(self): 
     348        return self._COMIID 
     349     
     350    def _SetCOMIID(self, value): 
     351        assert isinstance(value, (types.UnicodeType, types.NoneType)), u'COMIID must be a %s, or None.' % types.UnicodeType.__name__ 
     352        if value is not None: 
     353            assert re.match(u'^\{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\}$', value), u'COMIID must be a GUID, in string format (e.g. u\'{CB2E1BC5-D6A5-11D2-852D-204C4F4F5020}\').' 
     354        self._COMIID = value 
     355         
     356    COMIID = property(_GetCOMIID, _SetCOMIID, doc=DynamicDocString()) 
     357 
     358    def _GetCOMCLSID(self): 
     359        return self._COMCLSID 
     360     
     361    def _SetCOMCLSID(self, value): 
     362        assert isinstance(value, (types.UnicodeType, types.NoneType)), u'COMCLSID must be a %s, or None.' % types.UnicodeType.__name__ 
     363        if value is not None: 
     364            assert re.match(u'^\{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\}$', value), u'COMCLSID must be a GUID, in string format (e.g. u\'{CB2E1BC5-D6A5-11D2-852D-204C4F4F5020}\').' 
     365        self._COMCLSID = value 
     366         
     367    COMCLSID = property(_GetCOMCLSID, _SetCOMCLSID, doc=DynamicDocString()) 
     368 
     369    def _GetCOMVersionIndependentProgID(self): 
     370        return self._COMVersionIndependentProgID 
     371     
     372    def _SetCOMVersionIndependentProgID(self, value): 
     373        assert isinstance(value, (types.UnicodeType, types.NoneType)), u'COMVersionIndependentProgID must be a %s, or None.' % types.UnicodeType.__name__ 
     374        if value is None: 
     375            self._COMVersionIndependentProgID = u'GeoEco.' + unicode(self.Name) 
     376        else: 
     377            self._COMVersionIndependentProgID = value 
     378         
     379    COMVersionIndependentProgID = property(_GetCOMVersionIndependentProgID, _SetCOMVersionIndependentProgID, doc=DynamicDocString()) 
     380 
     381    def _GetCOMVersionDependentProgID(self): 
     382        return self._COMVersionDependentProgID 
     383     
     384    def _SetCOMVersionDependentProgID(self, value): 
     385        assert isinstance(value, (types.UnicodeType, types.NoneType)), u'COMVersionDependentProgID must be a %s, or None.' % types.UnicodeType.__name__ 
     386        if value is None: 
     387            self._COMVersionDependentProgID = u'GeoEco.' + unicode(self.Name) + u'.1' 
     388        else: 
     389            self._COMVersionDependentProgID = value 
     390         
     391    COMVersionDependentProgID = property(_GetCOMVersionDependentProgID, _SetCOMVersionDependentProgID, doc=DynamicDocString()) 
    332392 
    333393    def _GetObject(self): 
     
    496556        assert issubclass(cls, object), u'ValidatePropertyAssignment should only be called from the "fset" method of a property. By definition, only "new-style" classes (those that derive from object) can have properties.' 
    497557 
    498         # Validate that the method is the fset method for a property of that 
    499         # class. 
    500          
     558        # Now we need to identify the class's property object that uses the 
     559        # calling method as the "fset" method. Once we have that property object 
     560        # we can use the property's metadata to validate the assigned value. 
     561        # 
     562        # First, determine if the calling method is an instance method or a 
     563        # classmethod. 
     564 
     565        methodType = None         
     566        mro = inspect.getmro(cls) 
     567        for mroClass in mro: 
     568            if mroClass.__dict__.has_key(method.__name__): 
     569                if isinstance(mroClass.__dict__[method.__name__], classmethod): 
     570                    methodType = classmethod 
     571                else: 
     572                    methodType = types.MethodType 
     573                break 
     574        assert methodType is not None, u'Programming error in GeoEco.Metadata.ClassMetadata.ValidatePropertyAssignment: methodType is None. Please contact the authors of GeoEco for assistance.' 
     575 
    501576        propName = None 
    502577        prop = None 
    503         props = inspect.getmembers(cls, inspect.isdatadescriptor) 
    504         for (name, p) in props: 
    505             if p.fset.func_code == method.im_func.func_code: 
    506                 propName = name 
    507                 prop = p 
    508                 break 
     578 
     579        # If the calling method is an instance method, look for the property 
     580        # that has its fset property set to the method object itself. 
     581 
     582        if methodType == types.MethodType: 
     583            props = inspect.getmembers(cls, inspect.isdatadescriptor) 
     584            for (name, p) in props: 
     585                if hasattr(p, 'fset') and isinstance(p.fset, types.MethodType) and p.fset == method: 
     586                    propName = name 
     587                    prop = p 
     588                    break 
     589 
     590        # If the calling method is a classmethod, we have to look up the 
     591        # classmethod for which the calling method is the implementation. We 
     592        # cannot get this from p.fset.im_func or anything like that. Any time I 
     593        # try to dereference fset when it is a classmethod, Python just exits. 
     594 
     595        else: 
     596 
     597            # First find the classmethod for which the calling method is the 
     598            # implementation. 
     599 
     600            classmethodForMethod = None 
     601            for mroClass in mro: 
     602                for key in mroClass.__dict__.keys(): 
     603                    if isinstance(mroClass.__dict__[key], classmethod) and getattr(mroClass, key) == method: 
     604                        classmethodForMethod = mroClass.__dict__[key] 
     605                        break 
     606                if classmethodForMethod is not None: 
     607                    break 
     608 
     609            # Now look up the property that has its fset property set to the 
     610            # classmethod object. 
     611 
     612            if classmethodForMethod is not None:                 
     613                props = inspect.getmembers(cls, inspect.isdatadescriptor) 
     614                for (name, p) in props: 
     615                    if hasattr(p, 'fset') and isinstance(p.fset, classmethod) and p.fset == classmethodForMethod: 
     616                        propName = name 
     617                        prop = p 
     618                        break 
     619 
    509620        assert propName is not None and prop is not None, u'ValidatePropertyAssignment should only be called from the "fset" method of a property. None of the properties in class %s use method %s for their "fset" method. Please do not call ValidatePropertyAssignment from %s.' % (cls.__name__, method.__name__, method.__name__) 
    510621 
     
    564675    def AppendXMLNodes(self, node, document): 
    565676        super(ClassMetadata, self).AppendXMLNodes(node, document) 
     677        self.AppendPropertyXMLNode(self, u'IsExposedByIDispatch', node, document) 
     678        self.AppendPropertyXMLNode(self, u'COMIID', node, document) 
     679        self.AppendPropertyXMLNode(self, u'COMCLSID', node, document) 
     680        self.AppendPropertyXMLNode(self, u'COMVersionIndependentProgID', node, document) 
     681        self.AppendPropertyXMLNode(self, u'COMVersionDependentProgID', node, document) 
    566682        propertiesNode = node.appendChild(document.createElement(u'Properties')) 
    567683        for (name, prop) in inspect.getmembers(self.Object, inspect.isdatadescriptor): 
     
    680796        self.AppendPropertyXMLNode(self, u'IsExposedByPython', node, document) 
    681797        self.AppendPropertyXMLNode(self, u'IsExposedByIDispatch', node, document) 
     798        self.AppendPropertyXMLNode(self, u'IsReadOnly', node, document) 
    682799         
    683800     
     
    8831000        self._Method = methodMetadata 
    8841001        (args, varargs, varkw, defaults) = inspect.getargspec(methodMetadata.Object) 
    885         argFound = varargs is not None and name == varargs or varkw is not None and name == varkw 
     1002        argFound = (varargs is not None and name == varargs or varkw is not None and name == varkw) 
    8861003        if not argFound: 
    8871004            for arg in args: 
     
    9961113    def _GetHasDefault(self): 
    9971114        (args, varargs, varkw, defaults) = inspect.getargspec(self.Method.Object) 
     1115        if self.Name == varargs or self.Name == varkw: 
     1116            return False 
    9981117        for i in range(len(args)): 
    9991118            if args[i] == self.Name: 
     
    10071126    def _GetDefault(self): 
    10081127        (args, varargs, varkw, defaults) = inspect.getargspec(self.Method.Object) 
     1128        if self.Name == varargs or self.Name == varkw: 
     1129            return None 
    10091130        for i in range(len(args)): 
    10101131            if args[i] == self.Name: 
     
    11241245class TypeMetadata(object): 
    11251246 
    1126     def __init__(self, pythonType, canBeNone=False, arcGISType=None, arcGISAssembly=None, canBeArcGISInputParameter=False, canBeArcGISOutputParameter=False): 
     1247    def __init__(self, pythonType, canBeNone=False, comIDLType=None, canBeIDispatchParameter=False, arcGISType=None, arcGISAssembly=None, canBeArcGISInputParameter=False, canBeArcGISOutputParameter=False): 
    11271248        assert isinstance(pythonType, (types.TypeType, types.ClassType)) and not issubclass(pythonType, types.NoneType), u'pythonType must be a type or old-style class, and it may not be types.NoneType or derived from it.' 
    11281249        assert isinstance(canBeNone, types.BooleanType), u'canBeNone must be a boolean.' 
     1250        assert isinstance(comIDLType, (types.NoneType, types.UnicodeType)), u'comIDLType must be a unicode string, or None.' 
     1251        assert isinstance(canBeIDispatchParameter, types.BooleanType), u'canBeIDispatchParameter must be a boolean.' 
    11291252        assert arcGISType is None and arcGISAssembly is None or isinstance(arcGISType, types.UnicodeType) and isinstance(arcGISAssembly, types.UnicodeType), u'arcGISType and arcGISAssembly must both be Unicode strings or both be None.' 
    11301253        assert isinstance(canBeArcGISInputParameter, types.BooleanType), u'canBeArcGISInputParameter must be a boolean.' 
     
    11321255        self._PythonType = pythonType 
    11331256        self._CanBeNone = canBeNone 
     1257        self._COMIDLType = comIDLType 
     1258        self._CanBeIDispatchParameter = canBeIDispatchParameter 
    11341259        self._ArcGISType = arcGISType 
    11351260        self._ArcGISAssembly = arcGISAssembly 
     
    11461271     
    11471272    CanBeNone = property(_GetCanBeNone, doc=DynamicDocString()) 
     1273 
     1274    def _GetCOMIDLType(self): 
     1275        return self._COMIDLType 
     1276     
     1277    COMIDLType = property(_GetCOMIDLType, doc=DynamicDocString()) 
     1278 
     1279    def _GetCanBeIDispatchParameter(self): 
     1280        return self._CanBeIDispatchParameter 
     1281     
     1282    CanBeIDispatchParameter = property(_GetCanBeIDispatchParameter, doc=DynamicDocString()) 
    11481283 
    11491284    def _GetArcGISType(self): 
     
    11781313        node.appendChild(document.createElement(u'PythonType')).appendChild(document.createTextNode(self.PythonType.__name__)) 
    11791314        Metadata.AppendPropertyXMLNode(self, u'CanBeNone', node, document) 
     1315        Metadata.AppendPropertyXMLNode(self, u'COMIDLType', node, document) 
     1316        Metadata.AppendPropertyXMLNode(self, u'CanBeIDispatchParameter', node, document) 
    11801317        Metadata.AppendPropertyXMLNode(self, u'ArcGISType', node, document) 
    11811318        Metadata.AppendPropertyXMLNode(self, u'ArcGISAssembly', node, document) 
     
    11921329        elif not isinstance(value, self.PythonType): 
    11931330            raise TypeError(_(u'The value provided for the %s is invalid because it is a %s, an invalid type. Please provide a value of type %s.') % (variableName, type(value).__name__, self.PythonType.__name__)) 
     1331 
     1332    def COMIDLRepresentationOfValue(self, value): 
     1333        return None 
    11941334 
    11951335    def ParseValueFromArcGISInputParameterString(self, paramString, paramDisplayName, paramIndex): 
     
    13301470 
    13311471 
    1332 def AddClassMetadata(cls, shortDescription, longDescription=None, devTeamNotes=None, module=None): 
     1472def AddClassMetadata(cls, shortDescription, longDescription=None, devTeamNotes=None, module=None, isExposedByIDispatch=False, comIID=None, comCLSID=None, comVersionIndependentProgID=None, comVersionDependentProgID=None): 
    13331473    module = _GetModuleObject(module) 
    13341474    assert isinstance(module.__doc__, DynamicDocString) and isinstance(module.__doc__.Obj, ModuleMetadata), u'The __doc__ attribute of module %s must be an instance of GeoEco.Metadata.DynamicDocString, and __doc__.Obj must be an instance of GeoEco.Metadata.ModuleMetadata. Use the GeoEco.Metadata.AddModuleMetadata function to add the module\'s metadata before calling GeoEco.Metadata.AddClassMetadata.' % module.__name__ 
    13351475    cls = _GetClassObject(cls, module) 
    1336     assert isinstance(cls.__doc__, DynamicDocString), u'The __doc__ attribute of class %s defined in module %s must be an instance of GeoEco.Metadata.DynamicDocString. You should place the line\n\n    __doc__ = GeoEco.Metadata.DynamicDocString()\n\nin your class definition.' %s (cls.__name__, module.__name__) 
     1476    assert isinstance(cls.__doc__, DynamicDocString), u'The __doc__ attribute of class %s defined in module %s must be an instance of GeoEco.Metadata.DynamicDocString. You should place the line\n\n    __doc__ = GeoEco.Metadata.DynamicDocString()\n\nin your class definition.' % (cls.__name__, module.__name__) 
    13371477    assert cls.__doc__.Obj is None, u'%s.__doc__.Obj must be None. Do not call AddClassMetadata on a class that already has metadata.' % cls.__name__ 
    1338     cls.__doc__.Obj = ClassMetadata(unicode(cls.__name__), module.__doc__.Obj, shortDescription, longDescription, devTeamNotes
     1478    cls.__doc__.Obj = ClassMetadata(unicode(cls.__name__), module.__doc__.Obj, shortDescription, longDescription, devTeamNotes, isExposedByIDispatch, comIID, comCLSID, comVersionIndependentProgID, comVersionDependentProgID
    13391479 
    13401480 
     
    13641504                        propName = attr 
    13651505        assert cls is not None, u'If prop is a property object and cls is None, module %s must contain the class to which prop belongs.' % module.__name__ 
    1366     assert isinstance(cls.__doc__, DynamicDocString), u'The __doc__ attribute of class %s defined in module %s must be an instance of GeoEco.Metadata.DynamicDocString. You should place the line\n\n    __doc__ = GeoEco.Metadata.DynamicDocString()\n\nin your class definition.' %s (cls.__name__, module.__name__) 
     1506    assert isinstance(cls.__doc__, DynamicDocString), u'The __doc__ attribute of class %s defined in module %s must be an instance of GeoEco.Metadata.DynamicDocString. You should place the line\n\n    __doc__ = GeoEco.Metadata.DynamicDocString()\n\nin your class definition.' % (cls.__name__, module.__name__) 
    13671507    assert isinstance(cls.__doc__.Obj, ClassMetadata), u'%s.__doc__.Obj must be an instance of GeoEco.Metadata.ClassMetadata. Before calling GeoEco.Metadata.AddPropertyMetadata, use GeoEco.Metadata.AddClassMetadata to add ClassMetadata to class %s.' % (cls.__name__, cls.__name__) 
    13681508    assert isinstance(prop.__doc__, DynamicDocString), u'%s.%s.__doc__ must be an instance of GeoEco.Metadata.DynamicDocString. When defining the property, set the doc parameter to a new instance of DynamicDocString, like this:\n\n    %s = property(..., doc=DynamicDocString())' % (cls.__name__, propName, propName) 
     
    16671807    shortDescription=_(u'%s instance for the module that contains this class.') % ModuleMetadata.__name__) 
    16681808 
     1809AddPropertyMetadata(ClassMetadata.IsExposedByIDispatch, 
     1810    typeMetadata=PythonBoolean(), 
     1811    shortDescription=_(u'True if this class is exposed using the Microsoft COM IDispatch interface on Windows operating systems.'), 
     1812    longDescription=_(u'Components that expose an IDispatch interface are sometimes called COM Automation components or ActiveX components. Exposing the IDispatch interface allows the component to be invoked from virtually any programming language.')) 
     1813 
     1814AddPropertyMetadata(ClassMetadata.COMIID, 
     1815    typeMetadata=PythonUnicodeString(), 
     1816    shortDescription=_(u'Microsoft COM IID (interface ID) for the COM interface exposed by this class, in GUID string format (e.g. u\'{CB2E1BC5-D6A5-11D2-852D-204C4F4F5020}\').'), 
     1817    longDescription=_(u'The IID is the globally-unique identifier for the COM interface that represents this class under the COM infrastructure. Windows programs invoke properties and methods of this class through COM using this indentifier.'), 
     1818    devTeamNotes=_( 
     1819u"""This property is only relevant if IsExposedByIDispatch is True. If you set 
     1820IsExposedByIDispatch to True, you must supply a value for COMIID. You may 
     1821obtain a random, globally-unique value like this:: 
     1822 
     1823    >>> import pythoncom 
     1824    >>> unicode(pythoncom.CreateGuid()) 
     1825    u'{890E7DF8-FC16-43BD-BF14-B08890F6CC0C}' 
     1826""")) 
     1827 
     1828AddPropertyMetadata(ClassMetadata.COMCLSID, 
     1829    typeMetadata=PythonUnicodeString(), 
     1830    shortDescription=_(u'Microsoft COM CLSID (class ID) for this class, in GUID string format (e.g. u\'{CB2E1BC5-D6A5-11D2-852D-204C4F4F5020}\').'), 
     1831    longDescription=_(u'The CLSID is the globally-unique identifier for this class under the COM infrastructure. Windows programs create instances of this class through COM using this indentifier.'), 
     1832    devTeamNotes=_( 
     1833u"""This property is only relevant if IsExposedByIDispatch is True. If you set 
     1834IsExposedByIDispatch to True, you must supply a value for COMCLSID. You may 
     1835obtain a random, globally-unique value like this:: 
     1836 
     1837    >>> import pythoncom 
     1838    >>> unicode(pythoncom.CreateGuid()) 
     1839    u'{890E7DF8-FC16-43BD-BF14-B08890F6CC0C}' 
     1840""")) 
     1841 
     1842AddPropertyMetadata(ClassMetadata.COMVersionIndependentProgID, 
     1843    typeMetadata=PythonUnicodeString(), 
     1844    shortDescription=_(u'Microsoft COM "version-independent ProgID" for this class.'), 
     1845    longDescription=_( 
     1846u"""A ProgID is human-readable identifier for a class under the COM 
     1847infrastructure, such as "'GeoEco.Tools.HelloWorld". Windows programs can 
     1848create instances of this class through COM using the ProgID raster than the 
     1849CLSID. Many programmers prefer to use the ProgID rather than the CLSID."""), 
     1850    devTeamNotes=_( 
     1851u"""The ProgID provides one level of indirection in component name resolution. When 
     1852a program creates a component instance using the ProgID, COM looks up the CLSID 
     1853from the ProgID and instantiates the component using the CLSID. This allows the 
     1854component developer to change the component's CLSID while leaving the ProgID 
     1855unchanged. The calling programs will automatically create the new component 
     1856without any code changes. 
     1857 
     1858A further level of indirection is allowed by the provision of both version- 
     1859independent and version-dependent ProgIDs. Traditionally, a component provides 
     1860a single version-independent ProgID, such as "GeoEco.Tools.HelloWorld" and 
     1861one or more version-dependent ProgIDs, such as 
     1862"GeoEco.Tools.HelloWorld.HelloWorld.1", "GeoEco.Tools.HelloWorld.HelloWorld.2" 
     1863and "GeoEco.Tools.HelloWorld.HelloWorld.3". The version-independent ProgID 
     1864resolves to the latest version-dependent ProgID, 
     1865"GeoEco.Tools.HelloWorld.HelloWorld.3" in this case. Programs instantiate the 
     1866version-independent ID if they always want the latest version of the component, 
     1867or a version-dependent ID if they want a specific version. Multiple versions of 
     1868the component may be installed simultaneously, if the component supports it. 
     1869 
     1870GeoEco does not support multiple simultaneous installations. A program that 
     1871instantiates a GeoEco class by ProgID may use either of two strategies: 
     1872 
     1873* Use the version-independent ProgID. This will guarantee that the program will 
     1874  obtain an instance no matter what version of GeoEco is installed, but the 
     1875  program must handle any behavior changes between component versions. 
     1876 
     1877* Use the version-dependent ProgIDs. This will guarantee that the program will 
     1878  only obtain an instance of a specific version. The program will not need to 
     1879  deal with behavior differences between versions, but instantiation will fail 
     1880  if the requested version is not installed. 
     1881 
     1882To facilitate these strategies, the following approach is recommended for 
     1883developers of GeoEco classes that will be exposed through COM. 
     1884 
     1885* Do not specify a COMVersionIndependentProgID for your ClassMetadata. By 
     1886  default, your class's version-independent ProgID will be "GeoEco." plus the 
     1887  class name, e.g. "GeoEco.HelloWorld". This scheme means that your class's name 
     1888  must be unique across all GeoEco classes exposed by COM, regardless of what 
     1889  modules they are defined in. You cannot use submodules and subpackages to 
     1890  disambiguate classes with the same name. (This restriction is cumbersome 
     1891  but it allows the ProgIDs to remain consistent with the type information 
     1892  exposed to early-bound callers by the GeoEco type library. Please contact the 
     1893  author of GeoEco's COM infrastructure for a full explanation.) 
     1894 
     1895* For the first version of your class, do not specify a COMVersionDependentProgID. 
     1896  By default, your class's version dependent ProgID will by the module name plus 
     1897  the class name plus ".1", e.g. "GeoEco.HelloWorld.1". 
     1898 
     1899* If your component ever changes its behavior "significantly" between GeoEco 
     1900  versions, specify a new version-dependent ProgID that is "one number higher" 
     1901  than the previous one, e.g. "GeoEco.HelloWorld.2" for the second version. It 
     1902  is up to you what "significantly" means. One possible definition of a 
     1903  "signficant" change is one that would cause a double-digit percentage of your 
     1904  callers to fail or receive bad results if they blindly invoked the new version. 
     1905  (Callers that invoke using the version-independent ProgID will still break, 
     1906  but they incurred that risk when they chose to bind to that ProgID, rather 
     1907  than the version-dependent one.) 
     1908""")) 
     1909 
     1910AddPropertyMetadata(ClassMetadata.COMVersionDependentProgID, 
     1911    typeMetadata=PythonUnicodeString(), 
     1912    shortDescription=_(u'Microsoft COM "version-dependent ProgID" for this class.'), 
     1913    longDescription=ClassMetadata.COMVersionIndependentProgID.__doc__.Obj.LongDescription, 
     1914    devTeamNotes=ClassMetadata.COMVersionIndependentProgID.__doc__.Obj.DevTeamNotes) 
     1915 
    16691916# Public method: ValidatePropertyAssignment 
    16701917 
  • MGET/Trunk/PythonPackage/src/GeoEco/Tools/HelloWorld.py

    r24 r25  
    44from GeoEco.Logging import * 
    55from GeoEco.Metadata import * 
    6 from GeoEco.Tool import * 
    76from GeoEco.Types import * 
    87 
    9 class HelloWorld(Tool): 
     8class HelloWorld(object): 
    109    __doc__ = DynamicDocString() 
    1110 
     11    _public_methods_ = ['GreetPerson'] 
     12     
    1213    @classmethod 
    1314    def GreetPerson(cls, person=_(u'World')): 
    1415        cls.__doc__.Obj.ValidateMethodInvocation() 
    1516        greeting = _(u'Hello %s!') % person.strip() 
    16         LogInfo(greeting) 
     17        Logger.Info(greeting) 
    1718        return greeting 
    1819     
     
    2526            greeting = _(u'Hello %s!') % people[0] 
    2627        else: 
    27             LogError(_(u'You must specify at least one person to greet.')) 
    28         LogInfo(greeting) 
     28            Logger.Error(_(u'You must specify at least one person to greet.')) 
     29        Logger.Info(greeting) 
    2930        return greeting 
    3031 
    3132    @classmethod 
    3233    def TestParameterTypes(cls, oneBoolean=True, listOfBooleans=[False,True,False], oneFloat=1.0, listOfFloats=[2.0, 3.0, 4.0], oneInt=5, listOfInts=[5,6,7], oneString=u'Foo', listOfStrings=[u'Blah',u'Blah',u'Blah']): 
    33         LogInfo(u'oneBoolean = ' + unicode(repr(oneBoolean))) 
    34         LogInfo(u'listOfBooleans = ' + unicode(repr(listOfBooleans))) 
    35         LogInfo(u'oneFloat = ' + unicode(repr(oneFloat))) 
    36         LogInfo(u'listOfFloats = ' + unicode(repr(listOfFloats))) 
    37         LogInfo(u'oneInt = ' + unicode(repr(oneInt))) 
    38         LogInfo(u'listOfInts = ' + unicode(repr(listOfInts))) 
    39         LogInfo(u'oneString = ' + unicode(repr(oneString))) 
    40         LogInfo(u'listOfStrings = ' + unicode(repr(listOfStrings))) 
     34        Logger.Info(u'oneBoolean = ' + unicode(repr(oneBoolean))) 
     35        Logger.Info(u'listOfBooleans = ' + unicode(repr(listOfBooleans))) 
     36        Logger.Info(u'oneFloat = ' + unicode(repr(oneFloat))) 
     37        Logger.Info(u'listOfFloats = ' + unicode(repr(listOfFloats))) 
     38        Logger.Info(u'oneInt = ' + unicode(repr(oneInt))) 
     39        Logger.Info(u'listOfInts = ' + unicode(repr(listOfInts))) 
     40        Logger.Info(u'oneString = ' + unicode(repr(oneString))) 
     41        Logger.Info(u'listOfStrings = ' + unicode(repr(listOfStrings))) 
    4142        return oneBoolean, oneFloat, oneInt, oneString 
    4243 
     
    4647############################################################################### 
    4748 
    48 AddModuleMetadata(shortDescription=_(u'Contains the HelloWorld class, which illustrates how to build a GeoEco tool.')) 
     49AddModuleMetadata(shortDescription=_(u'Contains the HelloWorld class, which illustrates how to build a GeoEco class exposed to external callers.')) 
    4950 
    5051############################################################################### 
     
    5354 
    5455AddClassMetadata(HelloWorld, 
    55     shortDescription=_(u'llustrates how to build a GeoEco tool using the classic "hello world" paradigm.'), 
     56    shortDescription=_(u'llustrates how to build a GeoEco class exposed to external callers using the classic "hello world" paradigm.'), 
    5657    longDescription= 
    5758_(u"""At the beginning of their classic text on the C programming language, Brian 
     
    6162authors have introduced new programming languages. In the spririt of this 
    6263tradition, I have provided a HelloWorld class to illustrate, as succinctly as 
    63 possible, the features of the GeoEco Python class framework and build script.""")) 
     64possible, the features of the GeoEco Python class framework and build script."""), 
     65    isExposedByIDispatch=True, 
     66    comIID=u'{CEEE9091-E817-4F39-9A90-20E43FDB8AC8}', 
     67    comCLSID=u'{B2ED9CEF-D035-4011-A5A6-DA4BC14CBF71}') 
    6468 
    6569# Public method: GreetPerson 
     
    6771AddMethodMetadata(HelloWorld.GreetPerson, 
    6872    shortDescription=_(u'Greets a specified person.'), 
     73    isExposedByIDispatch=True, 
    6974    isExposedAsArcGISTool=True, 
    7075    arcGISDisplayName=_(u'Greet Person'), 
    7176    arcGISToolCategory=_(u'Examples')) 
    7277 
    73 HelloWorld.GreetPerson.im_func.__doc__.Obj.Dependencies.append(ArcGISVersion(minimumMajorVersion=9, minimumMinorVersion=1)) 
     78#HelloWorld.GreetPerson.im_func.__doc__.Obj.Dependencies.append(ArcGISVersion(minimumMajorVersion=9, minimumMinorVersion=1)) 
    7479 
    7580AddArgumentMetadata(HelloWorld.GreetPerson, u'cls', 
     
    7883 
    7984AddArgumentMetadata(HelloWorld.GreetPerson, u'person', 
    80     typeMetadata=PythonUnicodeString(), 
     85    typeMetadata=PythonUnicodeString(canBeNone=True), 
    8186    description=_(u'Person to greet.'), 
    8287    arcGISDisplayName=_(u'Person to Greet')) 
  • MGET/Trunk/PythonPackage/src/GeoEco/Types.py

    r24 r25  
    88 
    99 
    10 # Core Python types from which everything else should derive: 
     10# Specialized types from which few things derive 
     11 
     12 
     13class AnyPythonInstance(TypeMetadata): 
     14 
     15    def __init__(self, canBeNone=False): 
     16        super(AnyPythonInstance, self).__init__(pythonType=types.ObjectType, canBeNone=canBeNone, comIDLType=u'VARIANT', canBeIDispatchParameter=True) 
    1117 
    1218 
     
    3238 
    3339 
     40# Simple Python types from which many other types derive: 
     41 
     42 
    3443class PythonBoolean(TypeMetadata): 
    3544 
    3645    def __init__(self, canBeNone=False): 
    37         super(PythonBoolean, self).__init__(pythonType=types.BooleanType, canBeNone=canBeNone, arcGISType=u'ESRI.ArcGIS.Geoprocessing.GPBooleanTypeClass', arcGISAssembly=u'ESRI.ArcGIS.Geoprocessing', canBeArcGISInputParameter=True, canBeArcGISOutputParameter=True) 
     46        super(PythonBoolean, self).__init__(pythonType=types.BooleanType, canBeNone=canBeNone, comIDLType=u'VARIANT_BOOL', canBeIDispatchParameter=True, arcGISType=u'ESRI.ArcGIS.Geoprocessing.GPBooleanTypeClass', arcGISAssembly=u'ESRI.ArcGIS.Geoprocessing', canBeArcGISInputParameter=True, canBeArcGISOutputParameter=True) 
    3847 
    3948    def AppendXMLNodesForValue(self, value, node, document): 
     
    4251        assert isinstance(document, xml.dom.Node) and document.nodeType == xml.dom.Node.DOCUMENT_NODE, u'node must be an instance of xml.dom.Node with nodeType==DOCUMENT_NODE' 
    4352        node.appendChild(document.createElement(u'boolean')).appendChild(document.createTextNode(unicode(value).lower())) 
     53 
     54    def COMIDLRepresentationOfValue(self, value): 
     55        assert isinstance(value, types.BooleanType), u'value must be a boolean' 
     56        if value: 
     57            return 'VARIANT_TRUE' 
     58        return 'VARIANT_FALSE' 
    4459 
    4560    def ParseValueFromArcGISInputParameterString(self, paramString, paramDisplayName, paramIndex): 
     
    5570 
    5671    def __init__(self, canBeNone=False): 
    57         super(PythonFloat, self).__init__(pythonType=types.FloatType, canBeNone=canBeNone, arcGISType=u'ESRI.ArcGIS.Geoprocessing.GPDoubleTypeClass', arcGISAssembly=u'ESRI.ArcGIS.Geoprocessing', canBeArcGISInputParameter=True, canBeArcGISOutputParameter=True) 
     72        super(PythonFloat, self).__init__(pythonType=types.FloatType, canBeNone=canBeNone, comIDLType=u'double', canBeIDispatchParameter=True, arcGISType=u'ESRI.ArcGIS.Geoprocessing.GPDoubleTypeClass', arcGISAssembly=u'ESRI.ArcGIS.Geoprocessing', canBeArcGISInputParameter=True, canBeArcGISOutputParameter=True) 
    5873 
    5974    def AppendXMLNodesForValue(self, value, node, document): 
     
    6277        assert isinstance(document, xml.dom.Node) and document.nodeType == xml.dom.Node.DOCUMENT_NODE, u'node must be an instance of xml.dom.Node with nodeType==DOCUMENT_NODE' 
    6378        node.appendChild(document.createElement(u'double')).appendChild(document.createTextNode(unicode(value))) 
     79 
     80    def COMIDLRepresentationOfValue(self, value): 
     81        assert isinstance(value, types.FloatType), u'value must be a float' 
     82        return str(value) 
    6483 
    6584    def ParseValueFromArcGISInputParameterString(self, paramString, paramDisplayName, paramIndex): 
     
    7594 
    7695    def __init__(self, canBeNone=False): 
    77         super(PythonInteger, self).__init__(pythonType=types.IntType, canBeNone=canBeNone, arcGISType=u'ESRI.ArcGIS.Geoprocessing.GPLongTypeClass', arcGISAssembly=u'ESRI.ArcGIS.Geoprocessing', canBeArcGISInputParameter=True, canBeArcGISOutputParameter=True) 
     96        super(PythonInteger, self).__init__(pythonType=types.IntType, canBeNone=canBeNone, comIDLType=u'long', canBeIDispatchParameter=True, arcGISType=u'ESRI.ArcGIS.Geoprocessing.GPLongTypeClass', arcGISAssembly=u'ESRI.ArcGIS.Geoprocessing', canBeArcGISInputParameter=True, canBeArcGISOutputParameter=True) 
    7897 
    7998    def AppendXMLNodesForValue(self, value, node, document): 
     
    82101        assert isinstance(document, xml.dom.Node) and document.nodeType == xml.dom.Node.DOCUMENT_NODE, u'node must be an instance of xml.dom.Node with nodeType==DOCUMENT_NODE' 
    83102        node.appendChild(document.createElement(u'int')).appendChild(document.createTextNode(unicode(value))) 
     103 
     104    def COMIDLRepresentationOfValue(self, value): 
     105        assert isinstance(value, types.IntType), u'value must be an int' 
     106        return str(value) 
    84107 
    85108    def ParseValueFromArcGISInputParameterString(self, paramString, paramDisplayName, paramIndex): 
     
    95118 
    96119    def __init__(self, canBeNone=False): 
    97         super(PythonUnicodeString, self).__init__(pythonType=types.UnicodeType, canBeNone=canBeNone, arcGISType=u'ESRI.ArcGIS.Geoprocessing.GPStringTypeClass', arcGISAssembly=u'ESRI.ArcGIS.Geoprocessing', canBeArcGISInputParameter=True, canBeArcGISOutputParameter=True) 
     120        super(PythonUnicodeString, self).__init__(pythonType=types.UnicodeType, canBeNone=canBeNone, comIDLType=u'BSTR', canBeIDispatchParameter=True, arcGISType=u'ESRI.ArcGIS.Geoprocessing.GPStringTypeClass', arcGISAssembly=u'ESRI.ArcGIS.Geoprocessing', canBeArcGISInputParameter=True, canBeArcGISOutputParameter=True) 
     121 
     122    def COMIDLRepresentationOfValue(self, value): 
     123        assert isinstance(value, types.UnicodeType), u'value must be a Unicode string' 
     124        return '"' + str(value).replace('"', '\\"') + '"' 
    98125 
    99126    def AppendXMLNodesForValue(self, value, node, document): 
     
    104131 
    105132 
    106 class PythonList(TypeMetadata): 
    107  
    108     def __init__(self, elementType, canBeNone=False): 
     133# Python sequence types 
     134 
     135 
     136class PythonSequence(TypeMetadata): 
     137 
     138    def __init__(self, elementType, pythonType=types.ObjectType, canBeNone=False, comIDLType=u'SAFEARRAY(VARIANT)', canBeIDispatchParameter=True, arcGISType=u'ESRI.ArcGIS.Geoprocessing.GPMultiValueTypeClass', arcGISAssembly=u'ESRI.ArcGIS.Geoprocessing', canBeArcGISInputParameter=True, canBeArcGISOutputParameter=False): 
    109139        assert isinstance(elementType, TypeMetadata), u'elementType must be an instance of TypeMetadata' 
    110         super(PythonList, self).__init__(pythonType=types.ListType, canBeNone=canBeNone, arcGISType=u'ESRI.ArcGIS.Geoprocessing.GPMultiValueTypeClass', arcGISAssembly=u'ESRI.ArcGIS.Geoprocessing', canBeArcGISInputParameter=(issubclass(elementType.PythonType, basestring) or not hasattr(elementType.PythonType, '__getitem__')), canBeArcGISOutputParameter=False) 
     140        assert not canBeArcGISInputParameter or not arcGISType != u'ESRI.ArcGIS.Geoprocessing.GPMultiValueTypeClass' or (issubclass(elementType.PythonType, basestring) or not hasattr(elementType.PythonType, '__getitem__')), u'For this sequence type to be passed as an ArcGIS input parameter using the ArcGIS data type ESRI.ArcGIS.Geoprocessing.GPMultiValueTypeClass, its elements may not themselves be sequences unless they are strings. (In other words, you can\'t pass nested sequences using ESRI.ArcGIS.Geoprocessing.GPMultiValueTypeClass.)' 
     141        super(PythonSequence, self).__init__(pythonType=pythonType, canBeNone=canBeNone, comIDLType=comIDLType, canBeIDispatchParameter=canBeIDispatchParameter, arcGISType=arcGISType, arcGISAssembly=arcGISAssembly, canBeArcGISInputParameter=canBeArcGISInputParameter, canBeArcGISOutputParameter=canBeArcGISOutputParameter) 
    111142        self._ElementType = elementType 
    112143 
     
    117148 
    118149    def AppendXMLNodes(self, node, document): 
    119         super(PythonList, self).AppendXMLNodes(node, document) 
     150        super(PythonSequence, self).AppendXMLNodes(node, document) 
    120151        elementTypeNode = node.appendChild(document.createElement(u'ElementTypeMetadata')) 
    121152        self.ElementType.AppendXMLNodes(elementTypeNode, document) 
     
    130161 
    131162    def ValidateValue(self, value, variableName): 
    132         super(PythonList, self).ValidateValue(value, variableName) 
     163        super(PythonSequence, self).ValidateValue(value, variableName) 
     164        if not hasattr(value, '__len__') or not hasattr(value, '__getitem__') or not hasattr(value, '__setitem__') or not hasattr(value, '__iter__'): 
     165            raise TypeError(_(u'The value provided for the %s is invalid because it is a %s, which is not a Python sequence type. Please provide sequence type, such as %s, %s or %s.') % (variableName, type(value).__name__, types.ListType.__name__, types.TupleType.__name__, types.DictType.__name__)) 
    133166        for i in range(len(value)): 
    134167            self.ElementType.ValidateValue(value[i], _(u'element %i of the %s (where 0 is the first element)')) 
    135168 
    136169    def ParseValueFromArcGISInputParameterString(self, paramString, paramDisplayName, paramIndex): 
    137         paramString = super(PythonList, self).ParseValueFromArcGISInputParameterString(paramString, paramDisplayName, paramIndex) 
     170        paramString = super(PythonSequence, self).ParseValueFromArcGISInputParameterString(paramString, paramDisplayName, paramIndex) 
    138171 
    139172        # The input string is a list of values formatted as follows: 
     
    161194        while i < len(paramString): 
    162195 
    163             #GeoEco.Logging.LogDebug('----------------------------') 
    164             #GeoEco.Logging.LogDebug(u'i = %i, c = %s, state = %i, paramStart = %i, paramCharCount= %i' % (i, paramString[i], state, paramStart, paramCharCount)) 
     196            #GeoEco.Logging.Logger.Debug('----------------------------') 
     197            #GeoEco.Logging.Logger.Debug(u'i = %i, c = %s, state = %i, paramStart = %i, paramCharCount= %i' % (i, paramString[i], state, paramStart, paramCharCount)) 
    165198             
    166199            if state == FIRST_CHARACTER: 
     
    213246                raise RuntimeError(_(u'Failed to parse a list of values from the string provided for the %s parameter (parameter number %i) due to a programming error in the parser: the parser was found to be in unknown state %i. Please contact the author of this tool for assistance.') % (paramDisplayName, paramIndex, state)) 
    214247 
    215             #GeoEco.Logging.LogDebug('state = %i, paramStart = %i, paramCharCount= %i' % (state, paramStart, paramCharCount)) 
     248            #GeoEco.Logging.Logger.Debug('state = %i, paramStart = %i, paramCharCount= %i' % (state, paramStart, paramCharCount)) 
    216249 
    217250            i += 1 
     
    249282                 
    250283        return values 
     284 
     285 
     286class PythonList(PythonSequence): 
     287 
     288    def __init__(self, elementType, canBeNone=False): 
     289        super(PythonList, self).__init__(elementType=elementType, pythonType=types.ListType, canBeNone=canBeNone) 
     290 
     291 
     292class PythonTuple(PythonSequence): 
     293 
     294    def __init__(self, elementType, canBeNone=False): 
     295        super(PythonTuple, self).__init__(elementType=elementType, pythonType=types.TupleType, canBeNone=canBeNone) 
     296 
     297    def ParseValueFromArcGISInputParameterString(self, paramString, paramDisplayName, paramIndex): 
     298        resultList = super(PythonTuple, self).ParseValueFromArcGISInputParameterString(paramString, paramDisplayName, paramIndex) 
     299        return tuple(resultList) 
  • MGET/Trunk/VisualStudioSolutions/CreateArcGISToolbox/Program.cs

    r19 r25  
    4646        #region Fields deserialized from Metadata.xml 
    4747 
     48        public bool IsExposedByIDispatch; 
     49        public string COMIID; 
     50        public string COMCLSID; 
     51        public string COMVersionIndependentProgID; 
     52        public string COMVersionDependentProgID; 
    4853        public PropertyMetadata[] Properties; 
    4954        public MethodMetadata[] Methods; 
     
    5762 
    5863        [XmlElement(typeof(TypeMetadata)), 
     64        XmlElement(typeof(AnyPythonInstance)), 
    5965        XmlElement(typeof(PythonClassInstance)), 
    6066        XmlElement(typeof(PythonClassOrClassInstance)), 
     
    6369        XmlElement(typeof(PythonInteger)), 
    6470        XmlElement(typeof(PythonUnicodeString)), 
    65         XmlElement(typeof(PythonList))] 
     71        XmlElement(typeof(PythonSequence)), 
     72        XmlElement(typeof(PythonList)), 
     73        XmlElement(typeof(PythonTuple))] 
    6674        public TypeMetadata TypeMetadata; 
    6775        public bool IsExposedByPython; 
    6876        public bool IsExposedByIDispatch; 
     77        public bool IsReadOnly; 
    6978 
    7079        #endregion 
     
    95104        public string Name; 
    96105        [XmlElement(typeof(TypeMetadata)), 
     106        XmlElement(typeof(AnyPythonInstance)), 
    97107        XmlElement(typeof(PythonClassInstance)), 
    98108        XmlElement(typeof(PythonClassOrClassInstance)), 
     
    101111        XmlElement(typeof(PythonInteger)), 
    102112        XmlElement(typeof(PythonUnicodeString)), 
    103         XmlElement(typeof(PythonList))] 
     113        XmlElement(typeof(PythonSequence)), 
     114        XmlElement(typeof(PythonList)), 
     115        XmlElement(typeof(PythonTuple))] 
    104116        public TypeMetadata TypeMetadata; 
    105117        [XmlAnyElement(Name = "Description")] 
     
    127139        public string Name; 
    128140        [XmlElement(typeof(TypeMetadata)), 
     141        XmlElement(typeof(AnyPythonInstance)), 
    129142        XmlElement(typeof(PythonClassInstance)), 
    130143        XmlElement(typeof(PythonClassOrClassInstance)), 
     
    133146        XmlElement(typeof(PythonInteger)), 
    134147        XmlElement(typeof(PythonUnicodeString)), 
    135         XmlElement(typeof(PythonList))] 
     148        XmlElement(typeof(PythonSequence)), 
     149        XmlElement(typeof(PythonList)), 
     150        XmlElement(typeof(PythonTuple))] 
    136151        public TypeMetadata TypeMetadata; 
    137152        [XmlAnyElement(Name = "Description")] 
     
    150165        public string PythonType; 
    151166        public bool CanBeNone; 
     167        public string COMIDLType; 
     168        public bool CanBeIDispatchParameter; 
     169        public string ArcGISType; 
     170        public string ArcGISAssembly; 
    152171        public bool CanBeArcGISInputParameter; 
    153172        public bool CanBeArcGISOutputParameter; 
    154         public string ArcGISType; 
    155         public string ArcGISAssembly; 
    156173 
    157174        #endregion 
     
    214231    } 
    215232 
    216     public class PythonClassOrClassInstance : TypeMetadata 
     233    public class AnyPythonInstance : TypeMetadata 
    217234    { 
    218235        #region Overridden TypeMetadata members 
     
    228245    } 
    229246 
     247    public class PythonClassOrClassInstance : TypeMetadata 
     248    { 
     249        #region Overridden TypeMetadata members 
     250 
     251        public override System.Type DotNetType { get { throw new NotSupportedException(); } } 
     252        public override string NameForSerializedXmlValue { get { throw new NotSupportedException(); } } 
     253        public override object DeserializeValueFromXmlElement(XmlElement element) { throw new NotSupportedException(); } 
     254        public override IGPDataType GetIGPDataTypeInstance() { throw new NotSupportedException(); } 
     255        public override IGPValue GetIGPValueInstance(object value) { throw new NotSupportedException(); } 
     256        public override IGPValue GetIGPValueInstanceForDerivedOutput(Result result) { throw new NotSupportedException(); } 
     257 
     258        #endregion 
     259    } 
     260 
    230261    public class PythonBoolean : TypeMetadata 
    231262    { 
     
    275306    } 
    276307 
    277     public class PythonList : TypeMetadata 
     308    public class PythonSequence : TypeMetadata 
    278309    { 
    279310        #region Fields deserialized from Metadata.xml 
    280311 
    281312        [XmlElement("ElementTypeMetadata", typeof(TypeMetadata)), 
     313        XmlElement(typeof(AnyPythonInstance)), 
    282314        XmlElement(typeof(PythonClassInstance)), 
    283315        XmlElement(typeof(PythonClassOrClassInstance)), 
     
    286318        XmlElement(typeof(PythonInteger)), 
    287319        XmlElement(typeof(PythonUnicodeString)), 
    288         XmlElement(typeof(PythonList))] 
     320        XmlElement(typeof(PythonSequence)), 
     321        XmlElement(typeof(PythonList)), 
     322        XmlElement(typeof(PythonTuple))] 
    289323        public TypeMetadata ElementTypeMetadata; 
    290324 
     
    342376        #endregion 
    343377    } 
     378 
     379    public class PythonList : PythonSequence { } 
     380 
     381    public class PythonTuple : PythonSequence { } 
    344382 
    345383    class Program