root/MGET/Trunk/PythonPackage/src/GeoEco/ArcGIS.py

Revision 480, 120.4 kB (checked in by jjr8, 1 month ago)

Merged [465-479] from Jason branch into the Trunk. This will be released as MGET 0.8 alpha 2.

Line 
1 # ArcGIS.py - Provides utility functions for interacting with the ESRI ArcGIS
2 # software package.
3 #
4 # Copyright (C) 2007 Jason J. Roberts
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License (available in the file LICENSE.TXT)
15 # for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21 import datetime
22 import inspect
23 import logging
24 import new
25 import os
26 import platform
27 import re
28 import sys
29 import types
30 import weakref
31
32 from GeoEco.Dependencies import Dependency, PythonModuleDependency, SoftwareNotInstalledError
33 from GeoEco.DynamicDocString import DynamicDocString
34 from GeoEco.Exceptions import GeoEcoError
35 from GeoEco.Logging import Logger
36 from GeoEco.Internationalization import _, UnicodeToUserPreferredEncoding, UserPreferredEncodingToUnicode
37 from GeoEco.Metadata import ClassMetadata, MethodMetadata, TypeMetadata
38 from GeoEco.Types import DateTimeTypeMetadata
39
40
41 # Private variables global to this module. Initially, I wanted to declare these
42 # as class attributes of the GeoprocessorManager class. But when I did that,
43 # their reference counts were never decreased to 0 by Python when the module was
44 # unloaded. This prevented the ArcGIS geoprocessor COM Automation object from
45 # being released properly, which caused calls to its SetParameterAsText method
46 # to not work as intended. After very careful experimentation, I determined that
47 # the variables could be declared as module globals or as attributes of an "old-
48 # style" class and be released properly. I elected to declare them as module
49 # globals. I could not change the GeoprocessorManager to an old-style class
50 # because I wanted it to have properties, which are only available on "new-
51 # style" classes.
52
53 _Geoprocessor = None
54 _WrappedGeoprocessor = None
55
56
57 # Public classes and functions exported by this module
58
59
60 class GeoprocessorManager(object):
61     __doc__ = DynamicDocString()
62
63     _ArcGISMajorVersion = None
64     _ArcGISMinorVersion = None
65     _ArcGISServicePack = None
66
67     @classmethod
68     def GetGeoprocessor(cls):
69        
70         # Try to return a weak reference to the geoprocessor, so that the caller
71         # cannot accidentally hold on to a strong reference and thereby prevent
72         # the geoprocessor from being released properly when the module unloads.
73         #
74         # First try to return a weak reference, so that the caller cannot
75         # accidentally hold on to a strong reference and thereby prevent the
76         # geoprocessor from being released properly when the module unloads.
77         # If the geoprocessor is the object returned by arcgisscripting.create()
78         # then a weak reference cannot be created to it because it does not have
79         # a __weakref__ member. In this case we just return the object itself.
80         
81         if globals()['_Geoprocessor'] is not None:
82             try:
83                 return weakref.proxy(globals()['_Geoprocessor'])
84             except:
85                 pass
86             return globals()['_Geoprocessor']
87         return None
88
89     @classmethod
90     def SetGeoprocessor(cls, geoprocessor):
91         cls.__doc__.Obj.ValidatePropertyAssignment()
92
93         # If the caller provided a geoprocessor, use it.
94         
95         if geoprocessor is not None:
96             try:
97                 globals()['_Geoprocessor'] = geoprocessor
98                 globals()['_WrappedGeoprocessor'] = _ArcGISObjectWrapper(geoprocessor, u'Geoprocessor')
99             except:
100                 globals()['_WrappedGeoprocessor'] = None
101                 globals()['_Geoprocessor'] = None
102                 raise
103             Logger.Debug(_(u'GeoEco will now use ArcGIS Geoprocessor object 0x%08X for ArcGIS operations.'), id(globals()['_Geoprocessor']))
104
105         # If they provided None, release our geoprocessor.
106
107         elif globals()['_Geoprocessor'] is not None:
108             Logger.Debug(_(u'GeoEco is releasing its reference to ArcGIS Geoprocessor object 0x%08X and will no longer use it for ArcGIS operations.'), id(globals()['_Geoprocessor']))
109             globals()['_WrappedGeoprocessor'] = None
110             globals()['_Geoprocessor'] = None
111
112     Geoprocessor = property(GetGeoprocessor, SetGeoprocessor, doc=DynamicDocString())
113
114     @classmethod
115     def GetWrappedGeoprocessor(cls):
116        
117         # For safety, return a weak reference to the geoprocessor wrapper, so
118         # that the caller cannot accidentally hold on to a strong reference and
119         # thereby prevent the wrapper (and enclosed geoprocessor) from being
120         # released properly when the module unloads.
121         
122         if globals()['_WrappedGeoprocessor'] is not None:
123             return weakref.proxy(globals()['_WrappedGeoprocessor'])
124         return None
125
126     WrappedGeoprocessor = property(GetWrappedGeoprocessor, doc=DynamicDocString())
127
128     @classmethod
129     def GetGeoprocessorIsCOMObject(cls):
130         if not sys.modules.has_key('win32com.client'):
131             return False
132         if isinstance(cls.GetGeoprocessor(), sys.modules['win32com.client'].CDispatch):
133             return True
134         return False
135
136     GeoprocessorIsCOMObject = property(GetGeoprocessorIsCOMObject, doc=DynamicDocString())
137
138     @classmethod
139     def GetArcGISMajorVersion(cls):
140         if GeoprocessorManager._ArcGISMajorVersion is None:
141             cls._GetArcGISVersionNumbers()
142         return GeoprocessorManager._ArcGISMajorVersion
143
144     ArcGISMajorVersion = property(GetArcGISMajorVersion, doc=DynamicDocString())
145
146     @classmethod
147     def GetArcGISMinorVersion(cls):
148         if GeoprocessorManager._ArcGISMajorVersion is None:
149             cls._GetArcGISVersionNumbers()
150         return GeoprocessorManager._ArcGISMinorVersion
151
152     ArcGISMinorVersion = property(GetArcGISMinorVersion, doc=DynamicDocString())
153
154     @classmethod
155     def GetArcGISServicePack(cls):
156         if GeoprocessorManager._ArcGISMajorVersion is None:
157             cls._GetArcGISVersionNumbers()
158         return GeoprocessorManager._ArcGISServicePack
159
160     ArcGISServicePack = property(GetArcGISServicePack, doc=DynamicDocString())
161
162     @classmethod
163     def InitializeGeoprocessor(cls, forceCOMInstantiation=False, forcePythonInstantiation=False):
164        
165         # Validate input arguments.
166
167         if forceCOMInstantiation and forcePythonInstantiation:
168             Logger.RaiseException(ValueError(_(u'forceCOMInstantiation and forcePythonInstantiation cannot both be True. Only one may be True.')))
169        
170         # If the geoprocessor has already been instantiated, return
171         # immediately, unless the caller required a specific method
172         # and the other method was used.
173
174         if cls.GetGeoprocessor() is not None:
175             if cls.GetGeoprocessorIsCOMObject() and forcePythonInstantiation:
176                 Logger.RaiseException(ValueError(_(u'The ArcGIS geoprocessor cannot be instantiated using the Python arcgisscripting module because it was already instantiated using COM Automation.')))
177             if not cls.GetGeoprocessorIsCOMObject() and forceCOMInstantiation:
178                 Logger.RaiseException(ValueError(_(u'The ArcGIS geoprocessor cannot be instantiated using COM Automation because it was already instantiated using the Python arcgisscripting module.')))
179             return
180
181         # Look up the version of ArcGIS that is installed on this
182         # machine.
183
184         major = cls.GetArcGISMajorVersion()
185         minor = cls.GetArcGISMinorVersion()
186         servicePack = cls.GetArcGISServicePack()
187
188         if major is None:
189             Logger.RaiseException(SoftwareNotInstalledError(_(u'The ArcGIS geoprocessor cannot be instantiated because ArcGIS does not appear to be installed.')))
190
191         # Instantiate the geoprocessor, trying the methods that are
192         # appropriate for the version of ArcGIS that is installed.
193
194         if major == 9 and minor == 1:
195             if forcePythonInstantiation:
196                 Logger.RaiseException(ValueError(_(u'The ArcGIS geoprocessor cannot be instantiated using the Python arcgisscripting module because ArcGIS 9.1 is installed and it does not include the arcgisscripting module. Please upgrade to ArcGIS 9.2 or later and try again.')))
197                
198             Logger.Debug(_(u'Attempting to instantiate the ArcGIS geoprocessor using COM Automation because that is the only supported instantiation method for ArcGIS 9.1...'))
199             realGeoprocessor, errorMessage = cls._InstantiateCOMGeoprocessor()
200             if realGeoprocessor is not None:
201                 cls.SetGeoprocessor(realGeoprocessor)
202                 return
203             Logger.RaiseException(RuntimeError(_(u'Failed to instantiate the ArcGIS geoprocessor using COM Automation. Either ArcGIS is not installed, the license server is not accessible, or there is a problem with your installation or license. Error details: %s') % errorMessage))
204        
205         if major == 9 and minor == 2:
206             cls._InstantiateGeoprocessorForArc92OrLater(major, minor, 2, 4, forceCOMInstantiation, forcePythonInstantiation)
207             return
208        
209         if major == 9 and minor >= 3:
210             cls._InstantiateGeoprocessorForArc92OrLater(major, minor, 2, 5, forceCOMInstantiation, forcePythonInstantiation)
211             return
212                    
213         Logger.RaiseException(SoftwareNotInstalledError(_(u'The ArcGIS geoprocessor cannot be instantiated because ArcGIS %i.%i is installed and this tool is only capable of instantiating the geoprocessor if ArcGIS 9.1 or later is installed. Please upgrade to ArcGIS 9.1 or later and try again') % (major, minor)))
214
215     @classmethod
216     def _InstantiateGeoprocessorForArc92OrLater(cls, arcMajor, arcMinor, pyMajor, pyMinor, forceCOMInstantiation, forcePythonInstantiation):
217         if forcePythonInstantiation and not (sys.version_info[0] == pyMajor and sys.version_info[1] == pyMinor):
218             Logger.RaiseException(ValueError(_(u'The ArcGIS geoprocessor cannot be instantiated using the Python arcgisscripting module because this script is executing under Python %(pyMajor1)i.%(pyMinor1)i and the arcgisscripting module from ArcGIS %(arcMajor)i.%(arcMinor)i can only be used with Python %(pyMajor2)i.%(pyMinor2)i.') % {u'pyMajor1': sys.version_info[0], u'pyMinor1': sys.version_info[1], u'arcMajor': arcMajor, u'arcMinor': arcMinor, u'pyMajor2': pyMajor, u'pyMinor2': pyMinor}))
219
220         errorMessage1 = None
221         if not forceCOMInstantiation and (sys.version_info[0] == pyMajor and sys.version_info[1] == pyMinor):
222             Logger.Debug(_(u'Attempting to instantiate the ArcGIS geoprocessor using the arcgisscripting module...'))
223             realGeoprocessor, errorMessage1 = cls._InstantiatePythonGeoprocessor()
224             if realGeoprocessor is not None:
225                 cls.SetGeoprocessor(realGeoprocessor)
226                 return
227             if forcePythonInstantiation:
228                 Logger.RaiseException(RuntimeError(_(u'Failed to instantiate the ArcGIS geoprocessor using the Python arcgisscripting module. Most likely ArcGIS is not installed, the license server is not accessible, or there is a problem with your installation or license. Error details: %s') % errorMessage1))
229             else:
230                 Logger.Debug(_(u'Failed to instantiate the ArcGIS geoprocessor using the Python arcgisscripting module. %s') % errorMessage1)
231
232         Logger.Debug(_(u'Attempting to instantiate the ArcGIS geoprocessor using COM Automation...'))
233         realGeoprocessor, errorMessage2 = cls._InstantiateCOMGeoprocessor()
234         if realGeoprocessor is not None:
235             cls.SetGeoprocessor(realGeoprocessor)
236             return
237         Logger.Debug(_(u'Failed to instantiate the ArcGIS geoprocessor using COM automation. %s') % errorMessage2)
238         if errorMessage1 is not None:
239             Logger.RaiseException(RuntimeError(_(u'Failed to instantiate the ArcGIS geoprocessor using the Python arcgisscripting module and using COM Automation. Most likely ArcGIS is not installed, the license server is not accessible, or there is a problem with your installation or license. If you believe that none of those are the problem, please contact the author of this tool for assistance. If you believe that none of those are the problem, please contact the author of this tool for assistance. Error details: %s %s') % (errorMessage1, errorMessage2)))
240         else:
241             if sys.version_info[0] == pyMajor and sys.version_info[1] == pyMinor:
242                 Logger.Debug(_(u'Attempting to instantiate the ArcGIS geoprocessor using the arcgisscripting module, simply for the purpose of reporting an error message...'))
243                 realGeoprocessor, errorMessage1 = cls._InstantiatePythonGeoprocessor()
244                 if realGeoprocessor is not None:
245                     cls.SetGeoprocessor(realGeoprocessor)
246             Logger.RaiseException(RuntimeError(_(u'Failed to instantiate the ArcGIS geoprocessor using COM Automation. (Due ArcGIS bug NIM010734, this tool is required to instantiate the geoprocessor with COM Automation rather than the arcgisscripting Python module.) %s') % errorMessage2))
247
248     @classmethod
249     def _InstantiateCOMGeoprocessor(cls):
250         try:
251             import pythoncom
252         except Exception, e:
253             if isinstance(e, AttributeError) and str(e).find("'NoneType' object has no attribute 'platform'") >= 0:
254                 return None, _(u'The pythoncom module from the "Python for Windows Extensions" package (also known as "pywin32") could not be imported due to an incompatibility between recent releases of pywin32 and ArcGIS. You may be able to work around this problem temporarily by restarting ArcGIS and running this tool again (do not run any other tools before running this one). For a more permanent solution, uninstall your existing copy of pywin32 and install build 212 instead. Finally, please see http://code.env.duke.edu/projects/mget/ticket/386 for the current status of this problem.')
255             return None, _(u'The "Python for Windows extensions" Python package (also known as "pywin32") does not appear to be properly installed for this version of Python (%(major)s.%(minor)s) because the Python statement "import pythoncom" raised %(e)s: %(msg)s.') % {u'major': sys.version_info[0], u'minor': sys.version_info[1], u'e': e.__class__.__name__, u'msg': unicode(e)}
256         Logger.Debug(_(u'Imported the pythoncom Python module.'))
257            
258         try:
259             import win32com.client
260         except Exception, e:
261             return None, _(u'The "Python for Windows extensions" Python package (also known as "pywin32") does not appear to be properly installed for this version of Python (%(major)s.%(minor)s) because the Python statement "import win32com.client" raised %(e)s: %(msg)s.') % {u'major': sys.version_info[0], u'minor': sys.version_info[1], u'e': e.__class__.__name__, u'msg': unicode(e)}
262         Logger.Debug(_(u'Imported the win32com.client Python module.'))
263
264         progID = 'esriGeoprocessing.GPDispatch'
265         try:
266             realGeoprocessor = win32com.client.Dispatch(progID)
267         except pythoncom.com_error, (hr, msg, exc, arg):
268             from GeoEco.COM import FormatCOMError               
269             return None, _(u'ArcGIS may not be installed, the license server may not be accessible, or there may be problem with the installation or license. If you believe that none of those are the problem, please contact the author of this tool for assistance. Error details: The "%(progid)s" COM Automation object could not be instantiated because the Python statement "win32com.client.Dispatch(\'%(progid)s\')" raised %(msg)s.') % {u'progid': progID, u'msg': FormatCOMError(hr, msg, exc, arg)}
270         except Exception, e:
271             return None, _(u'ArcGIS may not be installed, the license server may not be accessible, or there may be problem with the installation or license. If you believe that none of those are the problem, please contact the author of this tool for assistance. The "%(progid)s" COM Automation object could not be instantiated because the Python statement "win32com.client.Dispatch(\'%(progid)s\')" raised %(e)s: %(msg)s.') % {u'progid': progID, u'e': e.__class__.__name__, u'msg': unicode(e)}
272         Logger.Debug(_(u'Instantiated ArcGIS geoprocessor object 0x%08X using COM Automation.'), id(realGeoprocessor))
273
274         return realGeoprocessor, None
275
276     @classmethod
277     def _InstantiatePythonGeoprocessor(cls):
278         try:
279             import arcgisscripting
280         except Exception, e:
281             return None, _(u'The Python statement "import arcgisscripting" raised %(e)s: %(msg)s.') % {u'e': e.__class__.__name__, u'msg': unicode(e)}
282         Logger.Debug(_(u'Imported the arcgisscripting Python module.'))
283        
284         try:
285             realGeoprocessor = arcgisscripting.create()     # NOTE: DO NOT change this to arcgisscripting.create(9.3)!
286         except Exception, e:
287             return None, _(u'The Python statement "arcgisscripting.create()" raised %(e)s: %(msg)s.') % {u'e': e.__class__.__name__, u'msg': unicode(e)}
288         Logger.Debug(_(u'Instantiated ArcGIS geoprocessor object 0x%08X using arcgisscripting.create().'), id(realGeoprocessor))
289
290         return realGeoprocessor, None
291
292     @classmethod
293     def _GetArcGISVersionNumbers(cls):
294
295         # If we're running on Windows, get the ArcGIS version number from
296         # the Windows registry.
297
298         if sys.platform.lower() == u'win32':
299             Logger.Debug(_(u'Retrieving the ArcGIS version numbers from the Windows Registry.'))
300
301             # First import the win32api and win32con modules.
302
303             d = PythonModuleDependency(importName='win32api', displayName=_(u'Python for Windows extensions (pywin32)'), alternateURL=u'http://sourceforge.net/projects/pywin32')
304             d.Initialize();
305             import win32api
306
307             d = PythonModuleDependency(importName='win32con', displayName=_(u'Python for Windows extensions (pywin32)'), alternateURL=u'http://sourceforge.net/projects/pywin32')
308             d.Initialize();
309             import win32con
310
311             # Read the registry values that contain the version numbers.
312
313             try:
314                 hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\ESRI\\ArcInfo\\Desktop\\8.0')
315                 try:
316                     (realVersionValue, realVersionType) = win32api.RegQueryValueEx(hkey, 'RealVersion')
317                     if not isinstance(realVersionValue, basestring) or not re.match('^[0-9]+\.[0-9]+$', realVersionValue.strip()):
318                         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.'))
319                     GeoprocessorManager._ArcGISMajorVersion = int(realVersionValue.strip().split('.')[0])
320                     GeoprocessorManager._ArcGISMinorVersion = int(realVersionValue.strip().split('.')[1])
321                     spNumberValue = None
322                     try:
323                         (spNumberValue, spNumberType) = win32api.RegQueryValueEx(hkey, 'SPNumber')
324                     except:
325                         pass
326                     if spNumberValue is not None:
327                         if not isinstance(spNumberValue, basestring) or len(spNumberValue.strip()) > 0 and not re.match('^[0-9]+$', spNumberValue.strip()):
328                             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.'))
329                         if len(spNumberValue.strip()) > 0:
330                             GeoprocessorManager._ArcGISServicePack = int(spNumberValue.strip())
331                     else:
332                         GeoprocessorManager._ArcGISServicePack = 0
333                 finally:
334                     try:
335                         win32api.RegCloseKey(hkey)
336                     except:
337                         pass
338             except Exception, e:
339                 GeoprocessorManager._ArcGISMajorVersion = None
340                 GeoprocessorManager._ArcGISMinorVersion = None
341                 GeoprocessorManager._ArcGISServicePack = None
342                 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))
343                
344         # If we're on a non-Windows platform, all we can try to do is
345         # import the arcgisscripting module. If it contains a class
346         # called ExecuteError, it is ArcGIS 9.3. If not, it is ArcGIS
347         # 9.2. Note that this code is experimental; we have not yet
348         # tried to port GeoEco to non-Windows platforms. Also this
349         # code does not know how to determine the service pack that is
350         # installed, if any.
351
352         else:
353             Logger.Debug(_(u'Importing the arcgisscripting Python module to determine if ArcGIS is installed on this non-Windows operating system...'))
354             try:
355                 import arcgisscripting
356                 if 'ExecuteError' in arcgisscripting.__dict__:
357                     GeoprocessorManager._ArcGISMajorVersion = 9
358                     GeoprocessorManager._ArcGISMinorVersion = 3
359                     GeoprocessorManager._ArcGISServicePack = 0
360                     Logger.Debug(_(u'Imported the arcgisscripting Python module and found the ExecuteError class within it. ArcGIS 9.3 or later is installed. We do not presently know how to determine if a service pack is installed, so we will assume that no service pack is installed.'))
361                 else:
362                     GeoprocessorManager._ArcGISMajorVersion = 9
363                     GeoprocessorManager._ArcGISMinorVersion = 2
364                     GeoprocessorManager._ArcGISServicePack = 0
365                     Logger.Debug(_(u'Imported the arcgisscripting Python module but did not find the ExecuteError class within it. ArcGIS 9.2 is installed. We do not presently know how to determine if a service pack is installed, so we will assume that no service pack is installed.'))
366             except Exception, e:
367                 GeoprocessorManager._ArcGISMajorVersion = None
368                 GeoprocessorManager._ArcGISMinorVersion = None
369                 GeoprocessorManager._ArcGISServicePack = None
370                 Logger.Debug(_(u'The arcgisscripting Python module could not be imported (Python raised %s: %s). ArcGIS is not installed.'), e.__class__.__name__, unicode(e))
371
372     @classmethod
373     def RefreshCatalog(cls, directory):
374         cls.__doc__.Obj.ValidateMethodInvocation()
375         gp = cls.GetWrappedGeoprocessor()
376         if gp is not None:
377             gp.RefreshCatalog(directory)
378             Logger.Debug(_(u'Refreshed the ArcGIS catalog for directory %s'), directory)
379
380     @classmethod
381     def ArcGISObjectExists(cls, path, correctTypes, typeDisplayName):
382         cls.__doc__.Obj.ValidateMethodInvocation()
383         gp = cls.GetWrappedGeoprocessor()
384         if gp is None:
385             Logger.RaiseException(RuntimeError(_(u'The ArcGIS geoprocessor must be initialized before this tool may be executed. Please call GeoprocessorManager.InitializeGeoprocessor or GeoprocessorManager.SetGeoprocessor before executing this tool.')))
386         exists = gp.Exists(path)
387         if not exists and u'shapefile' in correctTypes and not path.lower().endswith('.shp') and os.path.isdir(os.path.dirname(path)):
388             exists = gp.Exists(path + u'.shp')
389             if exists:
390                 path = path + u'.shp'
391         isCorrectType = False
392         if exists:
393             correctTypes = map(unicode.lower, correctTypes)
394             d = gp.Describe(path)
395             isCorrectType = d is not None and d.DataType.lower() in correctTypes
396         if not exists:
397             Logger.Debug(_(u'The %(type)s %(path)s does not exist.') % {u'type': typeDisplayName, u'path': path})
398         else:
399             if isCorrectType:
400                 Logger.Debug(_(u'The %(type)s %(path)s exists.') % {u'type': typeDisplayName, u'path': path})
401             else:
402                 Logger.Debug(_(u'%(path)s exists but it is a %(actual)s, not a %(type)s.') % {u'type': typeDisplayName, u'path': path, u'actual': d.DataType})
403         return (exists, isCorrectType)
404
405     @classmethod
406     def DeleteArcGISObject(cls, path, correctTypes, typeDisplayName):
407         cls.__doc__.Obj.ValidateMethodInvocation()
408         exists, isCorrectType = cls.ArcGISObjectExists(path, correctTypes, typeDisplayName)
409         if not exists:
410             Logger.Info(_('The %(type)s %(path)s will not be deleted because it does not exist.') % {u'type': typeDisplayName, u'path': path})
411             return
412         if not isCorrectType:
413             Logger.RaiseException(ValueError(_('%(path)s exists but cannot be deleted because it is not a %(type)s.') % {u'type': typeDisplayName, u'path': path}))
414         try:
415             gp = cls.GetWrappedGeoprocessor()
416             gp.Delete_Management(path)
417         except:
418             Logger.LogExceptionAsError(_(u'Could not delete %(type)s %(path)s.') % {u'type': typeDisplayName, u'path': path})
419             raise
420         Logger.Info(_('Deleted %(type)s %(path)s.') % {u'type': typeDisplayName, u'path': path})
421
422     @classmethod
423     def CopyArcGISObject(cls, source, destination, overwriteExisting, correctTypes, typeDisplayName):
424         cls.__doc__.Obj.ValidateMethodInvocation()
425         exists, isCorrectType = cls.ArcGISObjectExists(source, correctTypes, typeDisplayName)
426         if not exists:
427             Logger.RaiseException(ValueError(_('The %(type)s %(path)s cannot be copied because it does not exist.') % {u'type': typeDisplayName, u'path': source}))
428         if not isCorrectType:
429                 Logger.RaiseException(ValueError(_('%(path)s cannot be copied because it is not a %(type)s.') % {u'type': typeDisplayName, u'path': source}))
430         try:
431             if overwriteExisting:
432                 oldLogInfoAsDebug = Logger.GetLogInfoAsDebug()
433                 Logger.SetLogInfoAsDebug(True)
434                 try:
435                     cls.DeleteArcGISObject(destination, correctTypes, typeDisplayName)
436                 finally:
437                     Logger.SetLogInfoAsDebug(oldLogInfoAsDebug)
438             else:
439                 exists, isCorrectType = cls.ArcGISObjectExists(destination, correctTypes, typeDisplayName)
440                 if exists:
441                     Logger.RaiseException(ValueError(_('%(path)s already exists.') % {u'path': destination}))
442             gp = cls.GetWrappedGeoprocessor()
443             Logger.Info(_('Copying %(type)s %(source)s to %(destination)s.') % {u'type': typeDisplayName, u'source': source, u'destination': destination})
444             gp.Copy_Management(source, destination)
445         except:
446             Logger.LogExceptionAsError(_(u'Could not copy %(type)s %(source)s to %(destination)s.') % {u'type': typeDisplayName, u'source': source, u'destination': destination})
447             raise
448
449     @classmethod
450     def MoveArcGISObject(cls, source, destination, overwriteExisting, correctTypes, typeDisplayName):
451         cls.__doc__.Obj.ValidateMethodInvocation()
452         exists, isCorrectType = cls.ArcGISObjectExists(source, correctTypes, typeDisplayName)
453         if not exists:
454             Logger.RaiseException(ValueError(_('The %(type)s %(path)s cannot be moved because it does not exist.') % {u'type': typeDisplayName, u'path': source}))
455         if not isCorrectType:
456             Logger.RaiseException(ValueError(_('%(path)s cannot be moved because it is not a %(type)s.') % {u'type': typeDisplayName, u'path': source}))
457         try:
458             if overwriteExisting:
459                 oldLogInfoAsDebug = Logger.GetLogInfoAsDebug()
460                 Logger.SetLogInfoAsDebug(True)
461                 try:
462                     cls.DeleteArcGISObject(destination, correctTypes, typeDisplayName)
463                 finally:
464                     Logger.SetLogInfoAsDebug(oldLogInfoAsDebug)
465             else:
466                 exists, isCorrectType = cls.ArcGISObjectExists(destination, correctTypes, typeDisplayName)
467                 if exists:
468                     Logger.RaiseException(ValueError(_('%(path)s already exists.') % {u'path': destination}))
469             gp = cls.GetWrappedGeoprocessor()
470             Logger.Info(_('Moving %(type)s %(source)s to %(destination)s.') % {u'type': typeDisplayName, u'source': source, u'destination': destination})
471             gp.Copy_Management(source, destination)
472             gp.Delete_Management(source)
473         except:
474             Logger.LogExceptionAsError(_(u'Could not move %(type)s %(source)s to %(destination)s.') % {u'type': typeDisplayName, u'source': source, u'destination': destination})
475             raise
476
477     @classmethod
478     def GetUniqueLayerName(cls):
479         gp = cls.GetWrappedGeoprocessor()
480         if gp is None:
481             Logger.RaiseException(RuntimeError(_(u'The ArcGIS geoprocessor must be initialized before this tool may be executed. Please call GeoprocessorManager.InitializeGeoprocessor or GeoprocessorManager.SetGeoprocessor before executing this tool.')))
482         import random
483         name = u'TempLayer%08X' % random.randint(0, 2147483647)
484         while gp.Exists(name):
485             name = u'TempLayer%08X' % random.randint(0, 2147483647)
486         return name
487
488
489 class ArcGISDependency(Dependency):
490     __doc__ = DynamicDocString()
491
492     def __init__(self, minimumMajorVersion, minimumMinorVersion=None, minimumServicePack=None, requiresCOMInstantiation=False, requiresPythonInstantiation=False):
493         self.SetVersion(minimumMajorVersion, minimumMinorVersion, minimumServicePack)
494         assert not (requiresCOMInstantiation and requiresPythonInstantiation), u'requiresCOMInstantiation and requiresPythonInstantiation cannot both be True'
495         self.RequiresCOMInstantiation = requiresCOMInstantiation
496         self.RequiresPythonInstantiation = requiresPythonInstantiation
497
498     def SetVersion(self, minimumMajorVersion, minimumMinorVersion=None, minimumServicePack=None):
499         assert isinstance(minimumMajorVersion, types.IntType), u'minimumMajorVersion must be an integer.'
500         assert isinstance(minimumMinorVersion, (types.IntType, types.NoneType)), 'minimumMinorVersion must be an integer, or None.'
501         assert isinstance(minimumServicePack, (types.IntType, types.NoneType)), u'minimumServicePack must be an integer, or None.'
502         assert minimumMajorVersion is not None and minimumMinorVersion is None and minimumServicePack is None or minimumMajorVersion is not None and minimumMinorVersion is not None and minimumServicePack is None or minimumMajorVersion is not None and minimumMinorVersion is not None and minimumServicePack is not None, u'If you specify a minimumMinorVersion you must also specify a minimumMajorVersion. If you specify a minimumServicePack you must also specify a minimumMajorVersion and minimumMinorVersion.'
503         assert minimumMinorVersion is None or minimumMinorVersion >= 0, u'If minimumMinorVersion is not None it must be >= 0.'
504         assert minimumServicePack is None or minimumServicePack >= 0, u'If minimumServicePack is not None it must be >= 0.'
505         assert minimumMajorVersion > 9 or minimumMajorVersion == 9 and minimumMinorVersion is not None and minimumMinorVersion >= 1, u'GeoEco can only run on ArcGIS 9.1 or later. Even if your code can run on prior versions, GeoEco\'s ArcGIS utility functions cannot. Please adjust your ArcGISDependency to 9.1 or later.'
506
507         if minimumMinorVersion is None:
508             minimumMinorVersion = 0
509         if minimumServicePack is None:
510             minimumServicePack = 0
511
512         self._MinimumMajorVersion = minimumMajorVersion
513         self._MinimumMinorVersion = minimumMinorVersion
514         self._MinimumServicePack = minimumServicePack
515
516     def _GetMinimumMajorVersion(self):
517         return self._MinimumMajorVersion
518    
519     MinimumMajorVersion = property(_GetMinimumMajorVersion, doc=DynamicDocString())
520
521     def _GetMinimumMinorVersion(self):
522         return self._MinimumMinorVersion
523    
524     MinimumMinorVersion = property(_GetMinimumMinorVersion, doc=DynamicDocString())
525
526     def _GetMinimumServicePack(self):
527         return self._MinimumServicePack
528    
529     MinimumServicePack = property(_GetMinimumServicePack, doc=DynamicDocString())
530
531     def _GetMinimumServicePackString(self):
532         if self._MinimumServicePack > 0:
533             return _(u' Service Pack %i') % self._MinimumServicePack
534         else:
535             return u''
536    
537     MinimumServicePackString = property(_GetMinimumServicePackString, doc=DynamicDocString())
538
539     def _SetRequiresCOMInstantiation(self, value):
540         assert isinstance(value, types.BooleanType), u'RequiresCOMInstantiation must be a boolean'
541         self._RequiresCOMInstantiation = value
542
543     def _GetRequiresCOMInstantiation(self):
544         return self._RequiresCOMInstantiation
545    
546     RequiresCOMInstantiation = property(_GetRequiresCOMInstantiation, _SetRequiresCOMInstantiation, doc=DynamicDocString())
547
548     def _SetRequiresPythonInstantiation(self, value):
549         assert isinstance(value, types.BooleanType), u'RequiresPythonInstantiation must be a boolean'
550         self._RequiresPythonInstantiation = value
551
552     def _GetRequiresPythonInstantiation(self):
553         return self._RequiresPythonInstantiation
554    
555     RequiresPythonInstantiation = property(_GetRequiresPythonInstantiation, _SetRequiresPythonInstantiation, doc=DynamicDocString())
556
557     _LoggedInstalledVersion = False
558
559     def GetResultCacheKey(self):
560         if self.RequiresCOMInstantiation:
561             return u'ArcGISDependency:' + unicode(self._MinimumMajorVersion) + u',' + unicode(self._MinimumMinorVersion) + u',' + unicode(self._MinimumServicePack) + u' using COM Automation'
562         if self.RequiresPythonInstantiation:
563             return u'ArcGISDependency:' + unicode(self._MinimumMajorVersion) + u',' + unicode(self._MinimumMinorVersion) + u',' + unicode(self._MinimumServicePack) + u' using arcgisscripting'
564         return u'ArcGISDependency:' + unicode(self._MinimumMajorVersion) + u',' + unicode(self._MinimumMinorVersion) + u',' + unicode(self._MinimumServicePack)
565
566     def Initialize(self):
567
568         if self.RequiresCOMInstantiation:
569             Logger.Debug(_(u'Checking software dependency: ArcGIS version %i.%i%s or later, with the geoprocessor instantiated using Microsoft COM Automation') % (self.MinimumMajorVersion, self.MinimumMinorVersion, self.MinimumServicePackString))
570         elif self.RequiresPythonInstantiation:
571             Logger.Debug(_(u'Checking software dependency: ArcGIS version %i.%i%s or later, with the geoprocessor instantiated using the Python arcgisscripting module') % (self.MinimumMajorVersion, self.MinimumMinorVersion, self.MinimumServicePackString))
572         else:
573             Logger.Debug(_(u'Checking software dependency: ArcGIS version %i.%i%s or later') % (self.MinimumMajorVersion, self.MinimumMinorVersion, self.MinimumServicePackString))
574
575         # Check the ArcGIS version numbers.
576
577         major = GeoprocessorManager.GetArcGISMajorVersion()
578         minor = GeoprocessorManager.GetArcGISMinorVersion()
579         sp = GeoprocessorManager.GetArcGISServicePack()
580
581         if major is None or minor is None or sp is None:
582             Logger.RaiseException(SoftwareNotInstalledError(_(u'This tool requires ArcGIS %i.%i%s or a later version, but ArcGIS is not installed. Please install a compatible version of ArcGIS and try again.') % (self.MinimumMajorVersion, self.MinimumMinorVersion, self.MinimumServicePackString)))
583
584         if not ArcGISDependency._LoggedInstalledVersion:
585             if sp > 0:
586                 servicePackString =  _(u' Service Pack %i') % GeoprocessorManager.GetArcGISServicePack()
587             else:
588                 servicePackString = u''
589             Logger.Debug(_(u'ArcGIS %i.%i%s is installed.'), major, minor, servicePackString)
590             ArcGISDependency._LoggedInstalledVersion = True
591
592         if self.MinimumMajorVersion > major or self.MinimumMajorVersion == major and self.MinimumMinorVersion > minor or self.MinimumMajorVersion == major and self.MinimumMinorVersion == minor and self.MinimumServicePack > sp:
593             if sp > 0:
594                 servicePackString =  _(u' Service Pack %i') % GeoprocessorManager.GetArcGISServicePack()
595             else:
596                 servicePackString = u''
597             Logger.RaiseException(SoftwareNotInstalledError(_(u'This tool requires ArcGIS %i.%i%s or a later version, but version %s.%s%s is installed. Please upgrade your ArcGIS installation to a compatible version and try again.') % (self.MinimumMajorVersion, self.MinimumMinorVersion, self.MinimumServicePackString, major, minor, servicePackString)))
598
599         # If the geoprocessor has already been instantiated and this
600         # tool requires that it be instantiated with a specific
601         # method, report an error if it has already been instantiated
602         # with the other method. Otherwise just return.
603
604         if GeoprocessorManager.GetWrappedGeoprocessor() is not None:
605             if self.RequiresCOMInstantiation and not GeoprocessorManager.GetGeoprocessorIsCOMObject():
606                 Logger.RaiseException(RuntimeError(_(u'This tool requires that the ArcGIS geoprocessor be instantiated using Microsoft COM Automation (via the win32com.client.Dispatch function) but the geoprocessor was already instantiated using the Python arcgisscripting module. Please contact the author of this tool for assistance.')))
607                
608             if self.RequiresPythonInstantiation and GeoprocessorManager.GetGeoprocessorIsCOMObject():
609                 if major == 9 and minor == 2 and not (sys.version_info[0] == 2 and sys.version_info[1] == 4):
610                     Logger.RaiseException(RuntimeError(_(u'This tool requires that the ArcGIS geoprocessor be instantiated using the Python arcgisscripting module but the geoprocessor was already instantiated using Microsoft COM Automation (via the win32com.client.Dispatch function) because the ArcGIS 9.2 arcgisscripting module can only be used with Python 2.4. Please try to run this tool again using Python 2.4.')))
611                 if major == 9 and minor == 3 and not (sys.version_info[0] == 2 and sys.version_info[1] == 5):
612                     Logger.RaiseException(RuntimeError(_(u'This tool requires that the ArcGIS geoprocessor be instantiated using the Python arcgisscripting module but the geoprocessor was already instantiated using Microsoft COM Automation (via the win32com.client.Dispatch function) because the ArcGIS 9.3 arcgisscripting module can only be used with Python 2.5. Please try to run this tool again using Python 2.5.')))
613                 Logger.RaiseException(RuntimeError(_(u'This tool requires that the ArcGIS geoprocessor be instantiated using the Python arcgisscripting module but the geoprocessor was already instantiated using Microsoft COM Automation (via the win32com.client.Dispatch function). Please contact the author of this tool for assistance.')))
614                
615             return
616
617         # Initialize the geoprocessor object.
618
619         GeoprocessorManager.InitializeGeoprocessor(forceCOMInstantiation=self.RequiresCOMInstantiation, forcePythonInstantiation=self.RequiresPythonInstantiation)
620
621
622 class ArcGISProductDependency(Dependency):
623     __doc__ = DynamicDocString()
624
625     def __init__(self, minimumProductLevel):
626         self.MinimumProductLevel = minimumProductLevel
627
628     def _SetMinimumProductLevel(self, value):
629         assert isinstance(value, types.UnicodeType), u'MinimumProductLevel must be a Unicode string'
630         self._MinimumProductLevel = value
631
632     def _GetMinimumProductLevel(self):
633         return self._MinimumProductLevel
634    
635     MinimumProductLevel = property(_GetMinimumProductLevel, _SetMinimumProductLevel, doc=DynamicDocString())
636
637     _ProductStatus = {}   
638
639     def GetResultCacheKey(self):
640         return u'ArcGISProductDependency:' + self._MinimumProductLevel
641
642     def Initialize(self):
643
644         Logger.Debug(_(u'Checking software dependency: ArcGIS product level \"%s\".') % self.MinimumProductLevel)
645
646         # Obtain the geoprocessor. If we can't get it, it means the method that
647         # declared this dependency did not first declare an ArcGISDependency.
648
649         gp = GeoprocessorManager.GetWrappedGeoprocessor()
650         assert gp is not None, u'An ArcGISDependency must be declared before an ArcGISProductDependency is declared. Please add an ArcGISDependency to the method metadata for this method.'
651
652         # If our product status dictionary does not have an entry for this
653         # product level, query ArcGIS for its availability.
654
655         if not ArcGISProductDependency._ProductStatus.has_key(self.MinimumProductLevel):
656             ArcGISProductDependency._ProductStatus[self.MinimumProductLevel] = gp.CheckProduct(self.MinimumProductLevel)
657
658         # Check the product status.
659
660         if ArcGISProductDependency._ProductStatus.has_key(self.MinimumProductLevel):
661             if ArcGISProductDependency._ProductStatus[self.MinimumProductLevel].lower() != u'available' and ArcGISProductDependency._ProductStatus[self.MinimumProductLevel].lower() != u'alreadyinitialized':
662                 Logger.RaiseException(SoftwareNotInstalledError(_(u'This tool requires ArcGIS product license level \"%(level)s\" or higher. The ArcGIS geoprocessor reported the following status for that license level: \"%(status)s\". Please verify that you possess an ArcGIS product license of a sufficient level. If you use an ArcGIS license server, verify that this computer can properly communicate with it.') % {u'level': self.MinimumProductLevel, u'status' : ArcGISProductDependency._ProductStatus[self.MinimumProductLevel]}))
663         else:
664             Logger.RaiseException(SoftwareNotInstalledError(_(u'This tool requires ArcGIS product license level \"%(level)s\" or higher. The ArcGIS geoprocessor failed to report the status of that license level. Please verify that you possess an ArcGIS product license of a sufficient level. If you use an ArcGIS license server, verify that this computer can properly communicate with it.') % {u'level': self.MinimumProductLevel}))
665
666
667 class ArcGISExtensionDependency(Dependency):
668     __doc__ = DynamicDocString()
669
670     def __init__(self, extensionCode):
671         self.ExtensionCode = extensionCode
672
673     def _SetExtensionCode(self, value):
674         assert isinstance(value, types.UnicodeType), u'ExtensionCode must be a Unicode string'
675         self._ExtensionCode = value
676
677     def _GetExtensionCode(self):
678         return self._ExtensionCode
679    
680     ExtensionCode = property(_GetExtensionCode, _SetExtensionCode, doc=DynamicDocString())
681
682     def GetResultCacheKey(self):
683         return u'ArcGISExtensionDependency:' + self._ExtensionCode
684
685     def Initialize(self):
686
687         Logger.Debug(_(u'Checking software dependency: ArcGIS \"%s\" extension.') % self.ExtensionCode)
688
689         # Obtain the geoprocessor. If we can't get it, it means the method that
690         # declared this dependency did not first declare an ArcGISDependency.
691
692         gp = GeoprocessorManager.GetWrappedGeoprocessor()
693         assert gp is not None, u'An ArcGISDependency must be declared before an ArcGISExtensionDependency is declared. Please add an ArcGISDependency to the method metadata for this method.'
694
695         # It appears that the geoprocessor does not maintain a reference count
696         # on checked out extensions. Thus, you can call CheckOutExtension
697         # multiple times for the same extension, but if you call
698         # CheckInExtension just once, the extension is no longer checked out.
699         # As a result, if we checked out an extension in a previous call we
700         # have no guarantee that it is still checked out, because the caller
701         # could have checked it in. To mitigate this, we always check out the
702         # extension every time we're called. This seems to have no ill effects.
703         # It does not cause multiple licenses to be taken from the license
704         # server, and it does not yield an excessive performance hit (the
705         # CheckOutExtension call returns relatively quickly).
706         #
707         # We also never check in the extension, because we don't know if this
708         # would foul up the caller (he may assume the geoprocessor is reference-
709         # counting the extensions, when it really is not...)
710
711         status = gp.CheckOutExtension(self.ExtensionCode)
712         if status is None:
713             Logger.RaiseException(SoftwareNotInstalledError(_(u'This tool requires the ArcGIS \"%(extension)s\" extension. The ArcGIS geoprocessor failed to report the status of the license for that extension. Please verify that you possess a license for that extension and that the extension is installed. If you use an ArcGIS license server, verify that this computer can properly communicate with it.') % {u'extension': self.ExtensionCode}))
714         elif status.lower() != u'checkedout':
715             Logger.RaiseException(SoftwareNotInstalledError(_(u'This tool requires the ArcGIS \"%(extension)s\" extension. The ArcGIS geoprocessor reported the following status for that license level: \"%(status)s\". Please verify that you possess a license for that extension and that the extension is installed. If you use an ArcGIS license server, verify that this computer can properly communicate with it.') % {u'extension': self.ExtensionCode, u'status' : ArcGISProductDependency._ProductStatus[self.MinimumProductLevel]}))
716
717
718 class ArcGISError(GeoEcoError):
719     __doc__ = DynamicDocString()
720
721
722 def ValidateMethodMetadataForExposureAsArcGISTool(moduleName, className, methodName):
723
724         # Validate the class and method metadata.
725
726         assert sys.modules.has_key(moduleName), u'Module %s must be imported before ValidateMethodMetadataForExposureAsArcGISTool is invoked on that module.' % moduleName
727         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)
728         cls = sys.modules[moduleName].__dict__[className]
729         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
730         assert hasattr(cls, methodName) and inspect.ismethod(getattr(cls, methodName)), u'Class %s must contain an instance method or classmethod named %s.' % (className, methodName)
731         assert isinstance(getattr(cls, methodName).__doc__, DynamicDocString) and isinstance(getattr(cls, methodName).__doc__.Obj, MethodMetadata), u'The __doc__ attribute of method %s of class %s must be an instance of DynamicDocString, and that Obj property of that instance must be an instance of MethodMetadata.' % (methodName, className)
732         methodMetadata = getattr(cls, methodName).__doc__.Obj
733         assert methodMetadata.IsInstanceMethod or methodMetadata.IsClassMethod, u'Method %s of class %s must be an instance method or a classmethod.' % (methodName, className)
734         assert methodMetadata.IsExposedAsArcGISTool, u'%s.%s.__doc__.Obj.IsExposedAsArcGISTool must be true.' % (className, methodName)
735         assert isinstance(methodMetadata.ArcGISDisplayName, types.UnicodeType), u'%s.%s.__doc__.Obj.ArcGISDisplayName must be a unicode string.' % (className, methodName)
736
737         # Validate the metadata for the method's arguments.
738
739         (args, varargs, varkw, defaults) = inspect.getargspec(getattr(cls, methodName))
740         assert varargs is None, u'%s.%s cannot include a varargs argument because this method is designated for exposure as an ArcGIS tool (ArcGIS tools do not support varargs arguments). Please remove the *%s argument.' (className, methodName, varargs)
741         assert varkw is None, u'%s.%s cannot include a varkw argument because this method is designated for exposure as an ArcGIS tool (ArcGIS tools do not support varkw arguments). Please remove the **%s argument.' (className, methodName, varkw)
742         assert len(methodMetadata.Arguments) == len(args), u'%s.%s.__doc__.Obj.Arguments must contain exactly one element for each argument to %s.%s. %s.%s.__doc__.Obj.Arguments contains %i elements, but %i elements were expected.' % (className, methodName, className, methodName, className, methodName, len(methodMetadata.Arguments), len(args))
743         for i in range(1, len(args)):   # Skip the self or cls argument
744             assert methodMetadata.Arguments[i].Name == args[i], u'%s.%s.__doc__.Obj.Arguments[%i].Name must match the name of argument %i of %s.%s (where 0 is the first argument).' % (className, methodName, i, i, className, methodName)
745             assert isinstance(methodMetadata.Arguments[i].Type, TypeMetadata), u'%s.%s.__doc__.Obj.Arguments[%i].Type must be an instance of GeoEco.Metadata.TypeMetadata.' % (className, methodName, i)
746             if methodMetadata.Arguments[i].ArcGISDisplayName is not None:
747                 assert isinstance(methodMetadata.Arguments[i].ArcGISDisplayName, types.UnicodeType), u'%s.%s.__doc__.Obj.Arguments[%i].ArcGISDisplayName must be a unicode string.' % (className, methodName, i)
748                 assert methodMetadata.Arguments[i].Type.CanBeArcGISInputParameter, u'%s.%s.__doc__.Obj.Arguments[%i].Type.CanBeArcGISInputParameter must be True' % (className, methodName, i)
749                 assert methodMetadata.Arguments[i].InitializeToArcGISGeoprocessorVariable is None, u'Argument %i of %s.%s cannot have a value for ArcGISDisplayName when InitializeToArcGISGeoprocessorVariable is True. Either the argument can have an ArcGISDisplayName, in which case the argument is exposed as an ArcGIS parameter, or it can have InitializeToArcGISGeoprocessorVariable set to True, in which case the argument is not exposed in ArcGIS but is initialized to a geoprocessor variable.' % (i, className, methodName)
750                 if methodMetadata.Arguments[i].ArcGISParameterDependencies is not None:
751                     for param in methodMetadata.Arguments[i].ArcGISParameterDependencies:
752                         assert param != methodMetadata.Arguments[i].Name, u'%s.%s.__doc__.Obj.Arguments[%i].ArcGISParameterDependencies must not declare that this argument has a dependency on itself.' % (className, methodName, i)
753                         assert param in args, u'%s.%s.__doc__.Obj.Arguments[%i].ArcGISParameterDependencies must declare dependencies on existing arguments. The argument \'%s\' does not exist.' % (className, methodName, i, param)
754             else:
755                 assert methodMetadata.Arguments[i].HasDefault or methodMetadata.Arguments[i].InitializeToArcGISGeoprocessorVariable is not None, u'Argument %i of %s.%s must have a default value, or its metadata must specify that it should be initialized to an ArcGIS geoprocessor variable, because the method is designated for exposure as an ArcGIS tool but the argument itself is not (its ArcGISDisplayName is None).' % (i, className, methodName)
756                
757         # Validate the metadata for the method's results.       
758
759         for i in range(len(methodMetadata.Results)):
760             assert isinstance(methodMetadata.Results[i].Type, TypeMetadata), u'%s.%s.__doc__.Obj.Results[%i].Type must be an instance of GeoEco.Metadata.TypeMetadata.' % (className, methodName, i)
761             if methodMetadata.Results[i].ArcGISDisplayName is not None:
762                 assert isinstance(methodMetadata.Results[i].ArcGISDisplayName, types.UnicodeType), u'%s.%s.__doc__.Obj.Results[%i].ArcGISDisplayName must be a unicode string.' % (className, methodName, i)
763                 assert methodMetadata.Results[i].Type.CanBeArcGISOutputParameter, u'%s.%s.__doc__.Obj.Results[%i].Type.CanBeArcGISOutputParameter must be True' % (className, methodName, i)
764                 if methodMetadata.Results[i].ArcGISParameterDependencies is not None:
765                     for param in methodMetadata.Results[i].ArcGISParameterDependencies:
766                         assert param in args, u'%s.%s.__doc__.Obj.Results[%i].ArcGISParameterDependencies must declare dependencies on existing arguments. The argument \'%s\' does not exist.' % (className, methodName, i, param)
767
768
769 def ExecuteMethodFromCommandLineAsArcGISTool(moduleName, className, methodName, loggingConfigFile=None):
770
771     # Initialize the logging system.
772
773     Logger.Initialize(loggingConfigFile=loggingConfigFile)
774     invokedFromArcGISApplication = False
775     caughtException = False
776     try:
777         Logger.Debug(_(u'Python script %s started.') % sys.argv[0])
778
779         # Import the module containing the method.       
780
781         __import__(moduleName, globals(), locals())
782         cls = sys.modules[moduleName].__dict__[className]
783         methodMetadata = getattr(cls, methodName).__doc__.Obj
784
785         # Look through the method's dependencies for an
786         # ArcGISDependency. If we find one and it requires a specific
787         # method for instantiating the geoprocessor, instantiate it
788         # using that method. Otherwise allow the GeoprocessorManager
789         # to choose the instantiation method.
790
791         geoprocessor = None
792
793         try:
794             if methodMetadata.Dependencies is not None:
795                 for d in methodMetadata.Dependencies:
796                     if isinstance(d, ArcGISDependency):
797                         GeoprocessorManager.InitializeGeoprocessor(forceCOMInstantiation=d.RequiresCOMInstantiation, forcePythonInstantiation=d.RequiresPythonInstantiation)
798                         break
799                    
800             if geoprocessor is None:
801                 GeoprocessorManager.InitializeGeoprocessor()
802
803         finally:
804
805             # If we successfully obtained a geoprocessor, activate
806             # ArcGIS logging. This will cause all queued up log
807             # messages to be sent to the geoprocessing progress
808             # window. If an exception was raised then most of the time
809             # globals()['_Geoprocessor'] will be None. But it won't be
810             # if we tried to instantiate the geoprocessor with COM,
811             # failed, and then tried arcgisscripting just so we could
812             # report an error message. In that case we want to
813             # activate ArcGIS logging so that message makes it to the
814             # ArcGIS console.
815
816             if globals()['_Geoprocessor'] is not None:
817                 Logger.ActivateArcGISLogging()
818
819         # If we're running on ArcGIS 9.3 and not running in the ArcGIS
820         # process, warn the user that execution will be extremely
821         # slow. This is a performance degradation that ESRI introduced
822         # with ArcGIS 9.3 when they added the "Run Python script in
823         # process" option.
824
825         if GeoprocessorManager.GetArcGISMajorVersion() == 9 and GeoprocessorManager.GetArcGISMinorVersion() == 3 and methodMetadata.IsOutOfProcArcGISTool:
826             Logger.Warning(_(u'This tool may run slowly due to a geoprocessing performance degradation that ESRI introduced in ArcGIS 9.3. We are following up with ESRI about this problem and will try to get them to fix it. Please contact us if you have any questions. In the mean time, you can avoid this problem by running this tool on a computer with ArcGIS 9.1 or 9.2.'))
827
828         # Build a list of the input parameters that will be passed to the method
829         # as arguments.
830
831         argsToPass = []
832         argsFromCmdLine = []
833         argsFromGPVars = []
834         for i in range(1, len(methodMetadata.Arguments)):                       # methodMetadata.Arguments[0] is the "self" or "cls" parameter, so we skip it.
835
836             if methodMetadata.Arguments[i].ArcGISDisplayName is not None:
837                 argsFromCmdLine.append(methodMetadata.Arguments[i])
838                 argsToPass.append(methodMetadata.Arguments[i])
839
840             elif methodMetadata.Arguments[i].InitializeToArcGISGeoprocessorVariable is not None:
841                 argsFromGPVars.append(methodMetadata.Arguments[i])
842                 argsToPass.append(methodMetadata.Arguments[i])
843
844         # Determine whether we were invoked from an ArcGIS GUI application, in
845         # which case we can use the geoprocessor's GetParameterAsText API to
846         # retrieve the method's parameters. If we were not invoked from an ArcGIS
847         # GUI, then this API won't work, and we have to get the parameters from
848         # sys.argv. GetParameterAsText is better because it doesn't strip out
849         # quote characters, apostrophes and backslashes like argv does in
850         # certain situations.
851         #
852         # When a given parameter is a multi-value parameter, the values are
853         # separated by semicolons. Unfortunately ArcGIS does not document this,
854         # nor does it provide a robust escaping mechanism that allows the user
855         # to pass semicolons. Sadly, we cannot use the geoprocessor's
856         # GetParameter method, which would probably return an Array object,
857         # because it does not return objects that have IDispatch interfaces, at
858         # least in Arc 9.1.
859
860         expectedParamCount = len(argsFromCmdLine)
861         for i in range(len(methodMetadata.Results)):
862             if methodMetadata.Results[i].ArcGISDisplayName is not None:
863                 expectedParamCount += 1
864        
865         geoprocessor = GeoprocessorManager.GetWrappedGeoprocessor()
866        
867         if geoprocessor.ParameterCount == expectedParamCount:
868             invokedFromArcGISApplication = True
869             useSysArgv = False
870             Logger.Debug(_(u'This python script was invoked from an ArcGIS application.'))
871         elif len(sys.argv) - 1 == expectedParamCount:
872             useSysArgv = True
873             Logger.Debug(_(u'This python script was not invoked from an ArcGIS application.'))
874         else:
875             if geoprocessor.ParameterCount == len(sys.argv) - 1 or geoprocessor.ParameterCount == 0:
876                 Logger.RaiseException(ValueError(_(u'Invalid parameters. This tool expected to receive %i parameters but it received %i. Please review the documentation for this tool to determine the correct parameters. If you are invoking this script from outside of ArcGIS, use a single # character to indicate that a parameter has no value.') % (expectedParamCount, len(sys.argv) - 1)))
877             else:
878                 Logger.RaiseException(ValueError(_(u'Invalid parameters. This tool expected to receive %i parameters but it received %i through the ArcGIS geoprocessor and %i through the command line. The correct number of parameters must be passed by either the ArcGIS geoprocessor or the command line. Please review the documentation for this tool to determine the correct parameters. If you are invoking this script from outside of ArcGIS, use a single # character to indicate that a parameter has no value.')))
879
880         # Parse the command line arguments that will be passed to the method as
881         # arguments. If '#' was provided for an argument that can be None, pass
882         # None. If '#' was provided for an argument that has a default, pass the
883         # default. Otherwise Raise an error.
884
885         for i in range(len(argsFromCmdLine)):
886             if useSysArgv:
887                 value = sys.argv[i + 1]
888             else:
889                 value = geoprocessor.GetParameterAsText(i)
890             if value == '#' or len(value) <= 0:
891                 if argsFromCmdLine[i].Type.CanBeNone:
892                     exec '%s_value = None' % argsFromCmdLine[i].Name in globals(), locals()
893                 elif argsFromCmdLine[i].HasDefault:
894                     exec '%s_value = %s' % (argsFromCmdLine[i].Name, repr(argsFromCmdLine[i].Default))  in globals(), locals()
895                 else:
896                     Logger.RaiseException(ValueError(_(u'The %s parameter (called "%s" in the ArcGIS toolbox) is required. Please provide a value.') % (argsFromCmdLine[i].Name, argsFromCmdLine[i].ArcGISDisplayName)))
897             else:
898                 exec '%s_value = argsFromCmdLine[i].Type.ParseValueFromArcGISInputParameterString(value, argsFromCmdLine[i].ArcGISDisplayName, i+1)' % argsFromCmdLine[i].Name in globals(), locals()
899             Logger.Debug(_(u'Parameter %s = %s') % (argsFromCmdLine[i].Name, repr(locals()[argsFromCmdLine[i].Name + '_value'])))
900
901         # Obtain the values of geoprocessor variables that will be passed to the
902         # method as arguments.
903
904         for i in range(len(argsFromGPVars)):
905             exec '%s_value = geoprocessor.%s' % (argsFromGPVars[i].Name, argsFromGPVars[i].InitializeToArcGISGeoprocessorVariable) in globals(), locals()
906
907             # ArcGIS compatibility hack: If the geoprocessor is a COM Automation
908             # object, then its OverwriteOutput variable is a boolean. If it is
909             # the object returned by arcgisscripting.create(), it is an integer.
910             # We handle this type change so called function does not have to.
911
912             if not GeoprocessorManager.GetGeoprocessorIsCOMObject() and argsFromGPVars[i].InitializeToArcGISGeoprocessorVariable.lower() == u'overwriteoutput':
913                 exec '%s_value = bool(%s_value)' % (argsFromGPVars[i].Name, argsFromGPVars[i].Name) in globals(), locals()
914
915             Logger.Debug(_(u'Parameter %s = %s') % (argsFromGPVars[i].Name, repr(locals()[argsFromGPVars[i].Name + '_value'])))
916
917         # Execute the method.
918
919         if methodMetadata.IsInstanceMethod:
920             format = 'cls().%s(%s)'
921         else:
922             format = 'cls.%s(%s)'
923        
924         sourceCode = format % (methodName, ', '.join(map(lambda argMetadata: '%s=%s_value' % (argMetadata.Name, argMetadata.Name), argsToPass)))
925
926         if len(methodMetadata.Results) > 0:
927             sourceCode = '(' + ', '.join(map(lambda n: 'ret' + str(n), range(len(methodMetadata.Results)))) + ') = ' + sourceCode
928
929         Logger.Debug(_(u'Executing method %s.%s.%s.') % (moduleName, className, methodName))
930         try:
931             exec sourceCode in globals(), locals()
932         except Exception, e:
933             Logger.LogExceptionAsError()
934             if isinstance(e, AttributeError) and str(e).find("'NoneType' object has no attribute 'platform'") >= 0:
935                 Logger.Error(_(u'This problem may result from an incompatibility between the installed versions ArcGIS and the "Python for Windows Extensions" package. You may be able to work around this problem temporarily by restarting ArcGIS and running this tool again (do not run any other tools before running this one). For a more permanent solution, uninstall your existing copy of pywin32 and install build 212 instead. Finally, please see http://code.env.duke.edu/projects/mget/ticket/387 for the current status of this problem.'))
936             raise
937         Logger.Debug(_(u'Method %s.%s.%s returned successfully.') % (moduleName, className, methodName))
938
939         # Set the "derived" output parameters using the returned results.
940
941         if len(methodMetadata.Results) > 0:
942             Logger.Debug(_(u'Setting geoprocessing output parameters.'))
943         paramNum = 0
944         for i in range(len(methodMetadata.Results)):
945             if methodMetadata.Results[i].ArcGISDisplayName is not None:
946                 if locals()[u'ret%s' % i] is not None:
947                     geoprocessor.SetParameterAsText(len(argsFromCmdLine) + paramNum, unicode(locals()[u'ret%s' % i]))
948                 else:
949                     geoprocessor.SetParameterAsText(len(argsFromCmdLine) + paramNum, u'')
950                 paramNum += 1
951
952     # If we caught an exception, log it as an error and run a garbage
953     # collection to try to ensure all __del__ methods get called.
954
955     except:
956         Logger.LogExceptionAsError()
957
958         try:
959             import gc
960             gc.collect()
961         except:
962             pass
963
964         # If we were invoked from an ArcGIS 9.3 application, reraise
965         # the exception so that ArcGIS can catch it. Otherwise clear
966         # the exception information and call sys.exit so that a stack
967         # trace is not dumped. (To get a stack trace, the user should
968         # turn on debug logging.)
969
970         if invokedFromArcGISApplication and (GeoprocessorManager._ArcGISMajorVersion > 9 or GeoprocessorManager._ArcGISMajorVersion == 9 and GeoprocessorManager._ArcGISMinorVersion >= 3):
971             _ShutdownLoggingToAvoidArcGIS93MemoryLeak()
972             GeoprocessorManager.SetGeoprocessor(None)
973             raise
974         else:
975             caughtException = True
976
977     if caughtException:
978         Logger.Debug(_(u'Python script %s exiting with return value 1, indicating failure.') % sys.argv[0])
979         GeoprocessorManager.SetGeoprocessor(None)
980         sys.exit(1)
981
982     # Exit successfully.
983
984     Logger.Debug(_(u'Python script %s completed successfully.') % sys.argv[0])
985
986     if invokedFromArcGISApplication and (GeoprocessorManager._ArcGISMajorVersion > 9 or GeoprocessorManager._ArcGISMajorVersion == 9 and GeoprocessorManager._ArcGISMinorVersion >= 3):
987         _ShutdownLoggingToAvoidArcGIS93MemoryLeak()
988     GeoprocessorManager.SetGeoprocessor(None)
989
990 def _ShutdownLoggingToAvoidArcGIS93MemoryLeak():
991
992     # Clean up objects allocated by the logging module that leak when
993     # tools are run "in process" under ArcGIS 9.3. For a detailed
994     # discussion, see MGET ticket #361.
995     
996     try:
997         logging.raiseExceptions = 0
998         logging.shutdown()
999         while len(logging._handlerList) > 0:
1000             del logging._handlerList[0]
1001         import atexit
1002         for i in range(len(atexit._exithandlers)):
1003             if atexit._exithandlers[i][0] == logging.shutdown:
1004                 del atexit._exithandlers[i]
1005                 break
1006     except:
1007         pass
1008
1009
1010 class _ArcGISObjectWrapper(object):
1011
1012     def __init__(self, obj, name=None):
1013         assert isinstance(name, (types.UnicodeType, types.NoneType)), u'The name argument must be a unicode string, or None.'
1014         if name is not None:
1015             self._Name = name
1016         else:
1017             self._Name = u'<unknown>'
1018         self._Object = obj
1019         self._WrappedMethods = {}
1020
1021     def _LogDebug(self, format, *args, **kwargs):
1022         try:
1023             logging.getLogger(u'GeoEco.ArcGIS').debug(format, *args, **kwargs)
1024         except:
1025             pass
1026
1027     def _LogInfo(self, format, *args, **kwargs):
1028         try:
1029             logging.getLogger(u'GeoEco.ArcGIS').info(format, *args, **kwargs)
1030         except:
1031             pass
1032
1033     def _LogWarning(self, format, *args, **kwargs):
1034         try:
1035             logging.getLogger(u'GeoEco.ArcGIS').warning(format, *args, **kwargs)
1036         except:
1037             pass
1038
1039     def _LogError(self, format, *args, **kwargs):
1040         try:
1041             logging.getLogger(u'GeoEco.ArcGIS').error(format, *args, **kwargs)
1042         except:
1043             pass
1044
1045     def __getattr__(self, name):
1046         assert isinstance(name, basestring), u'name must be a string.'
1047
1048         # If the caller is asking for a private attribute (the name starts with
1049         # an underscore), he wants an attribute of the wrapper class instance,
1050         # not of the wrapped object. In this case, we must use the object
1051         # class's implementation of __getattr__.
1052
1053         if isinstance(name, types.StringType):
1054             name = unicode(name)
1055         if name.startswith(u'_'):
1056             return object.__getattribute__(self, name)
1057
1058         # The caller is asking for a data attribute or a method of the wrapped
1059         # object. If we already built a wrapper method for the specified name,
1060         # it means the caller asked for it before and we determined that it was
1061         # a method. Return the wrapped method to the caller now.
1062
1063         if self._WrappedMethods.has_key(name.lower()):
1064             return new.instancemethod(self._WrappedMethods[name.lower()], self, _ArcGISObjectWrapper)
1065
1066         # Retrieve the attribute from the wrapped object.
1067
1068         try:
1069             # If we imported pythoncom successfully, get the value and
1070             # catch com_error. Raise com_error as ArcGISError.
1071             #
1072             # Note: call raise, rather than Logger.RaiseException.
1073             # This is so the caller can swallow the exception, if
1074             # needed. Python's hasattr function works by calling
1075             # getattr and seeing if it raises an exception. If we call
1076             # Logger.RaiseException, we will break this behavior
1077             # because we'll cause error messages to be reported. We
1078             # leave it to the caller to call
1079             # Logger.LogExceptionAsError, if needed.
1080             
1081             if sys.modules.has_key('pythoncom'):
1082                 try:
1083                     value = getattr(self._Object, name)
1084                 except sys.modules['pythoncom'].com_error, (hr, msg, exc, arg):
1085                     from GeoEco.COM import FormatCOMError
1086                     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)))
1087
1088             # If we did not import pythoncom, set the value and allow the outer
1089             # exception handler to catch any errors.
1090             
1091             else:
1092                 value = getattr(self._Object, name)
1093
1094         # If we catch ArcGISError here, it is the com_error we just caught.
1095         # Reraise it.
1096         
1097         except ArcGISError:
1098             raise
1099
1100         # If we catch some other exception, raise it as ArcGISError.
1101         #
1102         # Note: call raise, rather than Logger.RaiseException. This is
1103         # so the caller can swallow the exception, if needed. Python's
1104         # hasattr function works by calling getattr and seeing if it
1105         # raises an exception. If we call Logger.RaiseException, we
1106         # will break this behavior because we'll cause error messages
1107         # to be reported. We leave it to the caller to call
1108         # Logger.LogExceptionAsError, if needed.
1109         
1110         except Exception, e:
1111             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)))
1112
1113         # If it is a method, create a wrapper, add it to our dictionary of
1114         # wrapped methods, and return the wrapper.
1115
1116         if isinstance(value, (types.MethodType, types.BuiltinFunctionType, types.BuiltinMethodType)):
1117
1118             # Write the methods's definition.
1119             #
1120             # If we obtained the geoprocessor object through COM Automation, the
1121             # object is a Python class with normal methods, and we can obtain
1122             # their signatures through inspect.getargspec.
1123             #
1124             # If we obtained the geoprocessor object through
1125             # arcgisscripting.create(), the object is not a class, but it has
1126             # attributes that are "builtin" functions. These functions cannot be
1127             # inspected so we cannot determine their signatures. In this case,
1128             # we just assume the signature is func(self, *args). This should
1129             # work fine because our callers will not be passing in neither named
1130             # formal parameters nor keyword dictionaries.
1131
1132             if isinstance(value, types.MethodType):
1133                 (args, varargs, varkw, defaults) = inspect.getargspec(value)
1134                 sourceCode = 'def %s%s:\n' % (str(name), inspect.formatargspec(args, varargs, varkw, defaults))
1135             else:
1136                 sourceCode = 'def %s(self, *args):\n' % str(name)
1137
1138             # Write the method's body.
1139             
1140             sourceCode = sourceCode + '    return self._InvokeMethod(self._Object.%s)\n' % name
1141
1142             # Compile the method.
1143             
1144             _locals = {}
1145             exec sourceCode in globals(), _locals
1146
1147             # Add it to our dictionary of wrapped methods.
1148             
1149             self._WrappedMethods[name.lower()] = _locals[name]
1150
1151             # Return it to the caller.
1152             
1153             return new.instancemethod(_locals[name], self, _ArcGISObjectWrapper)
1154
1155         # Log the returned attribute value.
1156
1157         self._LogDebug(_(u'ArcGIS %s object 0x%08X: Get %s returned %s') % (self._Name, id(self._Object), name, repr(value)))
1158
1159         # The returned value is a property. Convert it from the geoprocessor's
1160         # preferred type to the type we prefer.
1161
1162         value = self._FromGeoprocessorPreferredType(value, name)
1163
1164         # Special case: If the geoprocessor was created with
1165         # arcgisscripting.create(), and we're retrieving a field of a row of a
1166         # cursor, and that field is supposed to be a date, parse a datetime from
1167         # the string returned by the geoprocessor.
1168
1169         nameLower = name.lower()
1170         if (self._Name == u'Row' or self._Name == u'ReadOnlyRow') and nameLower != u'getvalue' and nameLower != u'setvalue' and nameLower != u'isnull' and nameLower != u'setnull' and not GeoprocessorManager.GetGeoprocessorIsCOMObject():
1171                 fieldDataType = None
1172                 if self._FieldTypesDict.has_key(nameLower):
1173                     fieldDataType = self._FieldTypesDict[nameLower]
1174                 else:
1175                     try:
1176                         fieldDataType = GeoprocessorManager.GetWrappedGeoprocessor().ListFields(self._Table, nameLower).Next().Type.lower()
1177                     except:
1178                         pass
1179                     self._FieldTypesDict[nameLower] = fieldDataType
1180                 if fieldDataType == u'date':
1181                     d = DateTimeTypeMetadata.ParseDatetimeFromString(value)
1182                     if d is not None:
1183                         value = d
1184
1185         # Return the attribute value.
1186
1187         return value
1188
1189     def __setattr__(self, name, value):
1190         assert isinstance(name, basestring), u'name must be a string.'
1191
1192         # If the caller is asking for a private attribute (the name starts with
1193         # an underscore), he wants to set an attribute of the wrapper class
1194         # instance, not of the wrapped object. In this case, we must use the
1195         # object class's implementation of __setattr__.
1196
1197         if isinstance(name, types.StringType):
1198             name = unicode(name)
1199         if name.startswith(u'_'):
1200             return object.__setattr__(self, name, value)
1201
1202         # The caller wants to set an attribute of the wrapped object. Convert
1203         # the value from our preferred type to that preferred by the
1204         # geoprocessor.
1205
1206         value = self._ToGeoprocessorPreferredType(value)
1207
1208         # Set the attribute.
1209
1210         try:
1211             # If we imported pythoncom successfully, set the value and catch
1212             # com_error. Raise com_error as ArcGISError.
1213             
1214             if sys.modules.has_key('pythoncom'):
1215                 try:
1216                     setattr(self._Object, name, value)
1217                 except sys.modules['pythoncom'].com_error, (hr, msg, exc, arg):
1218                     from GeoEco.COM import FormatCOMError
1219                     Logger.RaiseException(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), repr(value), FormatCOMError(hr, msg, exc, arg))))
1220
1221             # If we did not import pythoncom, set the value and allow the outer
1222             # exception handler to catch any errors.
1223             
1224             else:
1225                 setattr(self._Object, name, value)
1226
1227         # If we catch ArcGISError here, it is the com_error we just caught.
1228         # Reraise it.
1229         
1230         except ArcGISError:
1231             raise
1232
1233         # If we catch some other exception, raise it as ArcGISError.
1234         
1235         except Exception, e:
1236             Logger.RaiseException(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), repr(value), e.__class__.__name__, unicode(e))))
1237
1238         # Log the set value.
1239
1240         self._LogDebug(_(u'ArcGIS %s object 0x%08X: Set %s to %s') % (self._Name, id(self._Object), name, repr(value)))
1241
1242     def _InvokeMethod(self, method):
1243
1244         # Convert the arguments from the types we prefer to those preferred
1245         # by the geoprocessor.
1246
1247         (_args, _varargs, _varkw, _locals) = inspect.getargvalues(inspect.currentframe().f_back)
1248         del _args[0]
1249
1250         for argName in _args:
1251             _locals[argName] = self._ToGeoprocessorPreferredType(_locals[argName])
1252
1253         if _varargs is not None:
1254             _locals[_varargs] = self._ToGeoprocessorPreferredType(_locals[_varargs])
1255
1256         # Build the source code needed to invoke the method.
1257
1258         sourceCode = 'self._Object.%s(' % method.__name__
1259         for argName in _args:
1260             if not sourceCode.endswith('('):
1261                 sourceCode = sourceCode + ', '
1262             sourceCode = sourceCode + '%s=_locals[\'%s\']' % (argName, argName)
1263         if _varargs is not None:
1264             if not sourceCode.endswith('('):
1265                 sourceCode = sourceCode + ', '
1266             sourceCode = sourceCode + '*_locals[\'%s\'] ' % _varargs
1267         sourceCode = sourceCode + ')'
1268
1269         # Invoke the method.       
1270
1271         self._LogDebug(_(u'ArcGIS %s object 0x%08X: Invoking %s%s...') % (self._Name, id(self._Object), method.__name__, inspect.formatargvalues(_args, _varargs, _varkw, _locals)))
1272
1273         try:
1274             # If we imported pythoncom successfully, invoke the method and catch
1275             # com_error. Raise com_error as ArcGISError.
1276             
1277             if sys.modules.has_key('pythoncom'):
1278                 try:
1279                     value = eval(sourceCode, globals(), locals())
1280                 except sys.modules['pythoncom'].com_error, (hr, msg, exc, arg):
1281                     self._LogReturnedGeoprocessingMessages(method.__name__)
1282                     from GeoEco.COM import FormatCOMError
1283                     Logger.RaiseException(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))))
1284
1285             # If we did not import pythoncom, invoke the method and allow the
1286             # outer exception handler to catch any errors.
1287             
1288             else:
1289                 value = eval(sourceCode, globals(), locals())
1290
1291         # If we catch ArcGISError here, it is the com_error we just caught.
1292         # Reraise it.
1293         
1294         except ArcGISError:
1295             raise
1296
1297         # If we catch some other exception, raise it as ArcGISError.
1298         
1299         except Exception, e:
1300             self._LogReturnedGeoprocessingMessages(method.__name__)
1301             Logger.RaiseException(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))))
1302
1303         # The method executed successfully. Log any geoprocessing messages it
1304         # generated.
1305
1306         self._LogReturnedGeoprocessingMessages(method.__name__)
1307
1308         # Log the returned value.
1309
1310         self._LogDebug(_(u'ArcGIS %s object 0x%08X: %s returned %s') % (self._Name, id(self._Object), method.__name__, repr(value)))
1311
1312         # Convert the returned value from the geoprocessor's preferred type to
1313         # the type we prefer.
1314
1315         value = self._FromGeoprocessorPreferredType(value, method.__name__, *(tuple(map(_locals.get, _args)) + _locals[_varargs]))
1316
1317         # Special case: If the geoprocessor was created with
1318         # arcgisscripting.create(), and we're retrieving a field of a row of a
1319         # cursor, and that field is supposed to be a date, parse a datetime from
1320         # the string returned by the geoprocessor.
1321
1322         if not GeoprocessorManager.GetGeoprocessorIsCOMObject() and value is not None:
1323             methodName = method.__name__.lower()
1324
1325             if self._Name == u'Geoprocessor' and (methodName == u'searchcursor' or methodName == u'insertcursor' or methodName == u'updatecursor'):
1326                 if len(_args) > 0:
1327                     value._Table = _locals[_args[0]]
1328                 else:
1329                     value._Table = _locals[_varargs][0]
1330                 value._FieldTypesDict = {}
1331
1332             elif (self._Name == u'SearchCursor' or self._Name == u'UpdateCursor') and methodName == u'next' or self._Name == u'InsertCursor' and methodName == u'newrow':
1333                 value._Table = self._Table
1334                 value._FieldTypesDict = self._FieldTypesDict
1335
1336             elif (self._Name == u'Row' or self._Name == u'ReadOnlyRow') and methodName == u'getvalue':
1337                 if len(_args) > 0:
1338                     field = _locals[_args[0]].lower()
1339                 else:
1340                     field = _locals[_varargs][0].lower()
1341                 fieldDataType = None
1342                 if self._FieldTypesDict.has_key(field):
1343                     fieldDataType = self._FieldTypesDict[field]
1344                 else:
1345                     try:
1346                         fieldDataType = GeoprocessorManager.GetWrappedGeoprocessor().ListFields(self._Table, field).Next().Type.lower()
1347                     except:
1348                         pass
1349                     self._FieldTypesDict[field] = fieldDataType
1350                 if fieldDataType == u'date':
1351                     d = DateTimeTypeMetadata.ParseDatetimeFromString(value)
1352                     if d is not None:
1353                         value = d
1354
1355         # Return the returned value.
1356         
1357         return value
1358
1359     _NonGeoprocessingToolMethods = {
1360         'adderror' : None,
1361         'addmessage' : None,
1362         'addreturnmessage' : None,
1363         'addtoolbox' : None,
1364         'addwarning' : None,
1365         'checkextension' : None,
1366         'checkinextension' : None,
1367         'checkoutextension' : None,
1368         'checkproduct' : None,
1369         'clearenvironment' : None,
1370         'command' : None,
1371         'copyparameter' : None,
1372         'createobject' : None,
1373         'createrandomvaluegenerator' : None,
1374         'createscratchname' : None,
1375         'createuniquename' : None,
1376         'describe' : None,
1377         'exists' : None,
1378         'getmessage' : None,
1379         'getmessages' : None,
1380         'getparameter' : None,
1381         'getparameterastext' : None,
1382         'getparametercount' : None,
1383         'getparametervalue' : None,
1384         'getreturncode' : None,
1385         'getseverity' : None,
1386         'getsystemenvironment' : None,
1387         'insertcursor' : None,
1388         'issynchronous' : None,
1389         'listdatasets' : None,
1390         'listenvironments' : None,
1391         'listfeatureclasses' : None,
1392         'listfields' : None,
1393         'listindexes' : None,
1394         'listrasters' : None,
1395         'listtables' : None,
1396         'listtoolboxes' : None,
1397         'listtools' : None,
1398         'listworkspaces' : None,
1399         'loadsettings' : None,
1400         'parsefieldname' : None,
1401         'parsetablename' : None,
1402         'productinfo' : None,
1403         'qualifyfieldname' : None,
1404         'qualifytablename' : None,
1405         'refreshcatalog' : None,
1406         'removetoolbox' : None,
1407         'resetenvironments' : None,
1408         'savesettings' : None,
1409         'searchcursor' : None,
1410         'setparameter' : None,
1411         'setparameterastext' : None,
1412         'setproduct' : None,
1413         'testschemalock' : None,
1414         'updatecursor' : None,
1415         'usage' : None,
1416         'validatefieldname' : None,
1417         'validatetablename' : None
1418     }
1419
1420     def _LogReturnedGeoprocessingMessages(self, methodName):
1421
1422         # Only log the messages returned by the geoprocessor if the
1423         # invoked method appears to be a geoprocessing tool. The other
1424         # methods of the geoprocessor, the ones that are not
1425         # geoprocessing tools, do not report any messages. If the
1426         # geoprocessor object was created with
1427         # arcgisscripting.create(), it will not clear out the message
1428         # queue when one of these non-geoprocessing-tool methods is
1429         # called. If the messages queue is examined again, it will
1430         # have the same messages as before. For example, if
1431         # SingleOutputMapAlgebra_sa is called, followed by
1432         # RefreshCatalog, the messages from SingleOutputMapAlgebra_sa
1433         # will still be in the queue following the call to
1434         # RefreshCatalog. We must avoid logging these again, as if
1435         # they were reported by RefreshCatalog. See ticket #132.
1436         #
1437         # This behavior of the geoprocessor is a change introduced in
1438         # ArcGIS 9.2 and only seems to occur if the geoprocessor was
1439         # created with arcgisscripting.create(). But we do the same
1440         # logic in all circumstances. This probably yields a minor
1441         # performance gain when calling non-geoprocessing-tool methods
1442         # of the geoprocessor.
1443
1444         if self._Name == u'Geoprocessor' and not _ArcGISObjectWrapper._NonGeoprocessingToolMethods.has_key(methodName.lower()):
1445             i = 0
1446             try:
1447                 geoprocessor = GeoprocessorManager.GetGeoprocessor()
1448                 if geoprocessor is not None:
1449                     try:
1450                         while i < geoprocessor.MessageCount:
1451                             sev = geoprocessor.GetSeverity(i)
1452                             if sev == 0:
1453                                 self._LogInfo(geoprocessor.GetMessage(i))
1454                             elif sev == 1:
1455                                 self._LogWarning(geoprocessor.GetMessage(i))
1456                             else:
1457                                 self._LogError(geoprocessor.GetMessage(i))
1458                             i += 1
1459                     finally:
1460                         del geoprocessor
1461             except:
1462                 pass
1463
1464     def _ToGeoprocessorPreferredType(self, value):
1465
1466         # Process every item in lists and tuples.
1467
1468         if isinstance(value, types.ListType):
1469             return self._ToGeoprocessorPreferredTypeList(value)
1470         if isinstance(value, types.TupleType):
1471             l = list(value)
1472             l = self._ToGeoprocessorPreferredTypeList(l)
1473             return tuple(l)
1474
1475         # When the geoprocessor is obtained through arcgisscripting.create(), it
1476         # expects 8-bit strings.
1477
1478         geoprocessorIsCOMObject = GeoprocessorManager.GetGeoprocessorIsCOMObject()
1479
1480         if not geoprocessorIsCOMObject and isinstance(value, types.UnicodeType):
1481             return UnicodeToUserPreferredEncoding(value)
1482
1483         # When the geoprocessor is obtained through COM Automation, it expects
1484         # dates as pywintypes.TimeType instances. When it is obtained through
1485         # arcgisscripting.create(), it expects strings.
1486
1487         if isinstance(value, datetime.datetime):
1488             if geoprocessorIsCOMObject:
1489                 import pywintypes
1490                 return pywintypes.Time((value.year, value.month, value.day, value.hour, value.minute, value.second, 0, 0, 0))
1491             else:
1492                 return value.strftime('%Y-%m-%d %H:%M:%S')
1493
1494         # When the geoprocessor is obtained through arcgisscripting.create(), it
1495         # expects integers rather than booleans.
1496         #
1497         # NOTE: This is commented out beacuse it seems the geoprocessor will
1498         # accept booleans, even though it returns integers.
1499
1500 ##        if not geoprocessorIsCOMObject and isinstance(value, types.BooleanType):
1501 ##            return int(value)
1502
1503         # When the geoprocessor is obtained through arcgisscripting.create(), it
1504         # does not like to receive None, but empty strings seem to work.
1505
1506         if not geoprocessorIsCOMObject and isinstance(value, types.NoneType):
1507             return ''
1508
1509         # If the value is wrapped with a _ArcGISObjectWrapper, return the
1510         # wrapped object.
1511
1512         if isinstance(value, _ArcGISObjectWrapper):
1513             return value._Object
1514
1515         # The value is fine as it is. Just return it.       
1516
1517         return value
1518
1519     def _ToGeoprocessorPreferredTypeList(self, value):
1520         for i in range(len(value)):
1521             value[i] = self._ToGeoprocessorPreferredType(value[i])
1522         return value
1523
1524     def _FromGeoprocessorPreferredType(self, value, name, *args):
1525
1526         # Process every item in lists and tuples.
1527
1528         if isinstance(value, types.ListType):
1529             return self._FromGeoprocessorPreferredTypeList(value)
1530         if isinstance(value, types.TupleType):
1531             l = list(value)
1532             l = self._FromGeoprocessorPreferredTypeList(l)
1533             return tuple(l)
1534
1535         # When the geoprocessor is obtained through arcgisscripting.create(), it
1536         # returns 8-bit strings, but if we obtain it through COM Automation, it
1537         # returns Unicode strings. We always prefer Unicode, so ensure it is in
1538         # Unicode.
1539
1540         if isinstance(value, types.StringType):
1541             return UserPreferredEncodingToUnicode(value)
1542
1543         # When the geoprocessor is obtained through COM Automation, it
1544         # returns dates as pywintypes.TimeType instances. We prefer
1545         # datetime.datetime instances.
1546
1547         if not sys.modules.has_key('pywintypes'):
1548             try:
1549                 import pywintypes       # It is not really necessary to do this, but because I'm fixing this right before MGET 0.7 is released, I'm being extra careful.
1550             except:
1551                 pass
1552
1553         if sys.modules.has_key('pywintypes') and isinstance(value, sys.modules['pywintypes'].TimeType):
1554             return datetime.datetime(value.year, value.month, value.day, value.hour, value.minute, value.second, value.msec)
1555
1556         # If it is a class instance, wrap it in _ArcGISObjectWrapper.
1557
1558         if not isinstance(value, (types.BooleanType, types.FloatType, types.IntType, types.ListType, types.LongType, types.NoneType, basestring, datetime.datetime)):
1559             wrap = False
1560             objName = self._LookupReturnedObjectName(name, *args)
1561             try:
1562                 if objName is not None or isinstance(value, types.InstanceType) or inspect.getmodule(type(value)) != sys.modules[u'__builtin__']:        # Shouldn't raise an exception, but I'm suspicious of inspect.getmodule
1563                     wrap = True
1564             except:
1565                 pass
1566             if wrap:
1567                 return _ArcGISObjectWrapper(value, objName)
1568
1569         # The value is fine as it is. Just return it.
1570
1571         return value
1572
1573     def _FromGeoprocessorPreferredTypeList(self, value):
1574         for i in range(len(value)):
1575             value[i] = self._FromGeoprocessorPreferredType(value[i], u'<unknown>')
1576         return value
1577
1578     def _LookupReturnedObjectName(self, name, *args):
1579         _Name = self._Name.lower()
1580         name = name.lower()
1581         if not _ArcGISObjectWrapper._NameMap.has_key(_Name):
1582             return None
1583         if not isinstance(_ArcGISObjectWrapper._NameMap[_Name], types.DictType):
1584             return _ArcGISObjectWrapper._NameMap[_Name]
1585         if not _ArcGISObjectWrapper._NameMap[_Name].has_key(name):
1586             return None
1587         if not isinstance(_ArcGISObjectWrapper._NameMap[_Name][name], types.DictType):
1588             return _ArcGISObjectWrapper._NameMap[_Name][name]
1589         d = _ArcGISObjectWrapper._NameMap[_Name][name]
1590         for arg in args:
1591             if isinstance(arg, basestring):
1592                 arg = unicode(arg).lower()
1593             if not d.has_key(arg):
1594                 return None
1595             if not isinstance(d[arg], types.DictType):
1596                 return d[arg]
1597             d = d[arg]
1598         return None
1599
1600     _NameMap = {
1601         u'geoprocessor' : {
1602             u'createobject' : {
1603                 u'spatialreference' : u'SpatialReference',
1604                 u'valuetable' : u'ValueTable',
1605                 u'fieldinfo' : u'FieldInfo',
1606                 u'array' : u'Array',
1607                 u'point' : u'Point',
1608                 u'fieldmappings' : u'FieldMappings',
1609                 u'fieldmap' : u'FieldMap',
1610                 u'field' : u'Field'},
1611             u'describe' : u'Describe',
1612             u'listfields' : u'Fields',
1613             u'listindexes' : u'Indexes',
1614             u'listrasters' : u'Enumeration',
1615             u'listtables' : u'Enumeration',
1616             u'listworkspaces' : u'Enumeration',
1617             u'listdatasets' : u'Enumeration',
1618             u'listfeatureclasses' : u'Enumeration',
1619             u'listenvironments' : u'Enumeration',
1620             u'listtoolboxes' : u'Enumeration',
1621             u'listtools' : u'Enumeration',
1622             u'insertcursor' : u'InsertCursor',
1623             u'searchcursor' : u'SearchCursor',
1624             u'updatecursor' : u'UpdateCursor'},
1625         u'spatialreference' : None,
1626         u'valuetable' : None,
1627         u'fieldinfo' : { u'getfield' : u'Field'},
1628         u'array' : None,             # Some methods return objects but we can't tell what they are from the method signatures.
1629         u'point' : None,
1630         u'fieldmappings' : { u'fields' : u'Fields', u'getfieldmap' : u'FieldMap'},
1631         u'fieldmap' : { u'outputfield' : u'Field'},
1632         u'field' : None,
1633         u'fields' : { u'next' : u'Field'},
1634         u'indexes' : { u'next' : u'Index' },
1635         u'index' : { u'fields' : u'Fields' },
1636         u'searchcursor' : { u'next' : u'ReadOnlyRow' },
1637         u'insertcursor' : { u'newrow' : u'Row' },
1638         u'updatecursor' : { u'next' : u'Row' },
1639         u'readonlyrow' : { u'shape' : u'Geometry', u'GetValue' : { u'shape' : u'Geometry' } },
1640         u'row' : { u'shape' : u'Geometry', u'GetValue' : { u'shape' : u'Geometry' } },
1641         u'geometry': { u'getpart' : u'Point or Array' },           # Geometry.GetPart returns either Point or Array, depending on what type of feature it is.
1642         u'describe' : {
1643             u'fieldinfo' : u'FieldInfo',
1644             u'fields' : u'Fields',
1645             u'indexes' : u'Indexes',
1646             u'spatialreference' : u'SpatialReference',
1647             u'fidset' : u'FIDSet',
1648             u'connectionproperties' : u'PropertySet',
1649             u'tolerances' : u'PropertySet'},
1650         u'fidset' : None,
1651         u'PropertySet' : None}
1652
1653
1654 ###############################################################################
1655 # Metadata: module
1656 ###############################################################################
1657
1658 from GeoEco.Metadata import *
1659 from GeoEco.Types import *
1660
1661 AddModuleMetadata(shortDescription=_(u'Provides utility functions for interacting with the ESRI ArcGIS software package.'))
1662
1663 ###############################################################################
1664 # Metadata: GeoprocessorManager class
1665 ###############################################################################
1666
1667 AddClassMetadata(GeoprocessorManager,
1668     shortDescription=_(u'Manages the instance of the ArcGIS geoprocessor object used whenever any GeoEco function needs to invoke ArcGIS tools.'),
1669     isExposedAsCOMServer=True,
1670     comIID=u'{3DAAF9BD-2C7F-4A0B-8AB1-8975103F3E78}',
1671     comCLSID=u'{6E4B25C1-0E1D-496B-A661-AC75ADCB24C8}')
1672
1673 # Public properties
1674
1675 AddPropertyMetadata(GeoprocessorManager.Geoprocessor,
1676     typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
1677     shortDescription=_(u'The ArcGIS geoprocessor object used whenever any GeoEco function needs to invoke ArcGIS tools.'),
1678     longDescription=_(
1679 u"""This property is a singleton; all Python modules running in the same
1680 instance of the Python interpreter share the same value for this property. This
1681 property remains empty until it is explicitly set, or the InitializeGeoprocessor
1682 method is explicitly invoked, or a GeoEco function that has a dependency on
1683 ArcGIS is invoked (in which case it will invoke InitializeGeoprocessor).
1684
1685 If you want to invoke GeoEco functions from your own ArcGIS geoprocessing script
1686 and you have already obtained a geoprocessor object, you should set this
1687 property to that object before invoking any other GeoEco functions. Failing to
1688 do so will cause GeoEco to allocate its own geoprocessor object, which can yield
1689 unpredictable results. (As far as I can tell, the ArcGIS tools still work
1690 correctly, but log messages may not end up in the ArcGIS GUIs.)
1691
1692 If your geoprocessing script has not yet obtained the geoprocessor object, you
1693 may allow the GeoEco functions to obtain one when they first need it and then
1694 retrieve it by getting the value of this property.
1695
1696 If you want to invoke GeoEco functions from something that is not a
1697 "geoprocessing script" (a program that does not invoke any ArcGIS
1698 geoprocessing tools directly), then you should ignore this property and
1699 allow the GeoEco functions to obtain and use their own geoprocessor object.
1700
1701 See the documentation for InitializeGeoprocessor for more information about this
1702 property.
1703
1704 *Note to GeoEco developers:* Generally, GeoEco functions should *not* use this
1705 property; they should use WrappedGeoprocessor instead. That property implements
1706 a wrapper around the geoprocessor that logs debug messages every time the
1707 geoprocessor is invoked."""),
1708     isExposedToPythonCallers=True,
1709     isExposedByCOM=True)
1710
1711 AddPropertyMetadata(GeoprocessorManager.WrappedGeoprocessor,
1712     typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
1713     shortDescription=_(u'The Geoprocessor property, wrapped by a class that logs messages whenever the geoprocessor is accessed.'),
1714     longDescription=_(
1715 u"""This property is a singleton; all Python modules running in the same
1716 instance of the Python interpreter share the same value for this property. It is
1717 initialized whenever the Geoprocessor property is initialized; see the
1718 documentation for that property for more information.
1719
1720 This property is actually a wrapper class around the ArcGIS geoprocessor object.
1721 The wrapper exports the same interface as the wrapped object but logs a debug
1722 message every time a method is invoked or a property is accessed. By default,
1723 these debug messages are discarded by the GeoEco logging infrastructure. You can
1724 enable them by initializing the Logger class with a custom configuration file
1725 or by editing the default configuration file. See the Logger class documentation
1726 for more information.
1727
1728 All GeoEco functions use WrappedGeoprocessor to access the geoprocessor, rather
1729 than the Geoprocessor property, so that debug messages are reported whenever any
1730 function accesses the geoprocessor. External callers are welcome to use
1731 WrappedGeoprocessor as well, but they should consider using Geoprocessor
1732 instead, to completely eliminate the chance that a bug in the wrapper would
1733 affect their code."""),
1734     isExposedToPythonCallers=True,
1735     isExposedByCOM=True)
1736
1737 AddPropertyMetadata(GeoprocessorManager.GeoprocessorIsCOMObject,
1738     typeMetadata=BooleanTypeMetadata(),
1739     shortDescription=_(u'True if the object returned by the Geoprocessor property is a COM Automation object. False if it is some other kind of object (such as the object returned by arcgisscripting.create()).'),
1740     isExposedToPythonCallers=True,
1741     isExposedByCOM=True)
1742
1743 AddPropertyMetadata(GeoprocessorManager.ArcGISMajorVersion,
1744     typeMetadata=IntegerTypeMetadata(canBeNone=True),
1745     shortDescription=_(u'The major version number for ArcGIS, if it is installed on the machine.'),
1746     longDescription=_(u'This property is empty if ArcGIS is not installed on the machine.'),
1747     isExposedToPythonCallers=True,
1748     isExposedByCOM=True)
1749
1750 AddPropertyMetadata(GeoprocessorManager.ArcGISMinorVersion,
1751     typeMetadata=IntegerTypeMetadata(canBeNone=True),
1752     shortDescription=_(u'The minor version number for ArcGIS, if it is installed on the machine.'),
1753     longDescription=_(u'This property is empty if ArcGIS is not installed on the machine.'),
1754     isExposedToPythonCallers=True,
1755     isExposedByCOM=True)
1756
1757 AddPropertyMetadata(GeoprocessorManager.ArcGISServicePack,
1758     typeMetadata=IntegerTypeMetadata(canBeNone=True),
1759     shortDescription=_(u'The service pack number for ArcGIS, if it is installed on the machine.'),
1760     longDescription=_(u'This property is empty if ArcGIS is not installed on the machine.'),
1761     isExposedToPythonCallers=True,
1762     isExposedByCOM=True)
1763
1764 # Public method: GeoprocessorManager.GetGeoprocessor
1765
1766 AddMethodMetadata(GeoprocessorManager.GetGeoprocessor,
1767     shortDescription=_(u'Returns the value of the Geoprocessor property.'),
1768     isExposedToPythonCallers=True)
1769
1770 AddArgumentMetadata(GeoprocessorManager.GetGeoprocessor, u'cls',
1771     typeMetadata=ClassOrClassInstanceTypeMetadata(cls=GeoprocessorManager),
1772     description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__)
1773
1774 AddResultMetadata(GeoprocessorManager.GetGeoprocessor, u'geoprocessor',
1775     typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
1776     description=_(u'The value of the Geoprocessor property.'))
1777
1778 # Public method: GeoprocessorManager.SetGeoprocessor
1779
1780 AddMethodMetadata(GeoprocessorManager.SetGeoprocessor,
1781     shortDescription=_(u'Sets the value of the Geoprocessor property.'),
1782     isExposedToPythonCallers=True)
1783
1784 AddArgumentMetadata(GeoprocessorManager.SetGeoprocessor, u'cls',
1785     typeMetadata=ClassOrClassInstanceTypeMetadata(cls=GeoprocessorManager),
1786     description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__)
1787
1788 AddArgumentMetadata(GeoprocessorManager.SetGeoprocessor, u'geoprocessor',
1789     typeMetadata=AnyObjectTypeMetadata(),
1790     description=_(u'The ArcGIS geoprocessor object obtained from COM Automation or the arcgisscripting Python module. See the documentation for the Geoprocessor property for more information.'))
1791
1792 # Public method: GeoprocessorManager.GetWrappedGeoprocessor
1793
1794 AddMethodMetadata(GeoprocessorManager.GetWrappedGeoprocessor,
1795     shortDescription=_(u'Returns the value of the WrappedGeoprocessor property.'),
1796     isExposedToPythonCallers=True)
1797
1798 AddArgumentMetadata(GeoprocessorManager.GetWrappedGeoprocessor, u'cls',
1799     typeMetadata=ClassOrClassInstanceTypeMetadata(cls=GeoprocessorManager),
1800     description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__)
1801
1802 AddResultMetadata(GeoprocessorManager.GetWrappedGeoprocessor, u'geoprocessor',
1803     typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
1804     description=_(u'The value of the WrappedGeoprocessor property.'))
1805
1806 # Public method: GeoprocessorManager.GetArcGISMajorVersion
1807
1808 AddMethodMetadata(GeoprocessorManager.GetArcGISMajorVersion,
1809     shortDescription=_(u'Returns the value of the ArcGISMajorVersion property.'),
1810     isExposedToPythonCallers=True)
1811
1812 AddArgumentMetadata(GeoprocessorManager.GetArcGISMajorVersion, u'cls',
1813     typeMetadata=ClassOrClassInstanceTypeMetadata(cls=GeoprocessorManager),
1814     description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__)
1815
1816 AddResultMetadata(GeoprocessorManager.GetArcGISMajorVersion, u'majorVersion',
1817     typeMetadata=IntegerTypeMetadata(canBeNone=True),
1818     description=_(u'The value of the ArcGISMajorVersion property.'))
1819
1820 # Public method: GeoprocessorManager.GetArcGISMinorVersion
1821
1822 AddMethodMetadata(GeoprocessorManager.GetArcGISMinorVersion,
1823     shortDescription=_(u'Returns the value of the ArcGISMinorVersion property.'),
1824     isExposedToPythonCallers=True)
1825
1826 AddArgumentMetadata(GeoprocessorManager.GetArcGISMinorVersion, u'cls',
1827     typeMetadata=ClassOrClassInstanceTypeMetadata(cls=GeoprocessorManager),
1828     description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__)
1829
1830 AddResultMetadata(GeoprocessorManager.GetArcGISMinorVersion, u'minorVersion',
1831     typeMetadata=IntegerTypeMetadata(canBeNone=True),
1832     description=_(u'The value of the ArcGISMinorVersion property.'))
1833
1834 # Public method: GeoprocessorManager.GetArcGISServicePack
1835
1836 AddMethodMetadata(GeoprocessorManager.GetArcGISServicePack,
1837     shortDescription=_(u'Returns the value of the ArcGISServicePack property.'),
1838     isExposedToPythonCallers=True)
1839
1840 AddArgumentMetadata(GeoprocessorManager.GetArcGISServicePack, u'cls',
1841     typeMetadata=ClassOrClassInstanceTypeMetadata(cls=GeoprocessorManager),
1842     description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__)
1843
1844 AddResultMetadata(GeoprocessorManager.GetArcGISServicePack, u'servicePack',
1845     typeMetadata=IntegerTypeMetadata(canBeNone=True),
1846     description=_(u'The value of the ArcGISMinorVersion property.'))
1847
1848 # Public method: GeoprocessorManager.InitializeGeoprocessor
1849
1850 AddMethodMetadata(GeoprocessorManager.InitializeGeoprocessor,
1851     shortDescription=_(u'Initializes the Geoprocessor property with a new ArcGIS geoprocessor object.'),
1852     longDescription=_(
1853 u"""If you want to use GeoEco's GeoprocessorManager to instantiate the
1854 geoprocessor, you should either explicitly invoke this method or call
1855 a GeoEco function that requires ArcGIS; the GeoEco function will
1856 invoke this method on your behalf.
1857
1858 If you do not want to use GeoEco's GeoprocessorManager to instantiate
1859 the geoprocessor, you should instantiate it yourself and call
1860 GeoprocessorManager.SetGeoprocessor. GeoEco will cache a reference to
1861 your geoprocessor and not allocate its own instance.
1862
1863 If the Geoprocessor property has already been initialized with a
1864 geoprocessor object and forceCOMInstantiation and
1865 forcePythonInstantiation are both False, this method does nothing. If
1866 either forceCOMInstantiation or forcePythonInstantiation are True,
1867 this method will raise a RuntimeError if the geoprocessor was
1868 previously instantiated with the opposite method.
1869
1870 You should only pass True for forceCOMInstantiation or
1871 forcePythonInstantiation if you plan to perform geoprocessing
1872 operations that truly require one type of geoprocessor or the other.
1873 If both are False, this method will automatically select the best
1874 technique, as follows:
1875
1876 * If ArcGIS 9.1 is installed, COM Automation will always be used.
1877
1878 * If ArcGIS 9.2 is installed, arcgisscripting will be used if the
1879   script is executing under Python 2.4. Otherwise COM Automation will
1880   be used.
1881
1882 * If ArcGIS 9.3 is installed, arcgisscripting will be used if the
1883   script is executing under Python 2.5. Otherwise COM Automation will
1884   be used.
1885
1886 IMPORTANT NOTE: If arcgisscripting is used to instantiate the
1887 geoprocessor, the arcgisscripting.create function will always be
1888 called without any parameters, regardless of which version of ArcGIS
1889 is installed. This means that, even if ArcGIS 9.3 or later is
1890 installed, the instantiated geoprocessor will always use the inteface
1891 from ArcGIS 9.2."""),
1892     isExposedToPythonCallers=True,
1893     isExposedByCOM=True)
1894
1895 AddArgumentMetadata(GeoprocessorManager.InitializeGeoprocessor, u'cls',
1896     typeMetadata=ClassOrClassInstanceTypeMetadata(cls=GeoprocessorManager),
1897     description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__)
1898
1899 AddArgumentMetadata(GeoprocessorManager.InitializeGeoprocessor, u'forceCOMInstantiation',
1900     typeMetadata=BooleanTypeMetadata(),
1901     description=_(
1902 u"""If True, the geoprocessor will be instantiated with Microsoft COM
1903 Automation, regardless of what version of ArcGIS is installed. The
1904 win32com.client.Dispatch function from the "Python for Windows
1905 extensions" Python package (also known as "pywin32") will be used to
1906 perform the instantiation."""))
1907
1908 AddArgumentMetadata(GeoprocessorManager.InitializeGeoprocessor, u'forcePythonInstantiation',
1909     typeMetadata=BooleanTypeMetadata(),
1910     description=_(
1911 u"""If True, the geoprocessor will be instantiated by calling the
1912 create function in the arcgisscripting Python module, regardless of
1913 which version of ArcGIS is installed."""))
1914
1915 # Public method: GeoprocessorManager.RefreshCatalog
1916
1917 AddMethodMetadata(GeoprocessorManager.RefreshCatalog,
1918     shortDescription=_(u'Refreshes the ArcGIS catalog\'s cached view of the specified directory.'),
1919     longDescription=_(
1920 u"""The ArcGIS geoprocessing system interacts with the file system through the
1921 ArcGIS catalog, a cached view of the files and directories on the computer. It
1922 is important that the catalog be "refreshed" after any changes are made to files
1923 or directories. Otherwise the ArcGIS geoprocessor will not know about the
1924 changes and operate off an obsolete view of the file system, ultimately causing
1925 subsequent geoprocessing operations to fail.
1926
1927 Most GeoEco methods automatically refresh the ArcGIS catalog when it is
1928 necessary to do so. Some methods allow you to explictly control whether the
1929 refresh occurs. In general, you should always allow the catalog to be refreshed.
1930 The main scenario in which it is appropriate to prevent it is when you are
1931 making many consecutive changes inside a directory and do not want to incur the
1932 performance hit of refreshing the catalog after each change. In that scenario,
1933 you can use this method to refresh the catalog's view of that directory when you
1934 are done.
1935
1936 If the GeoprocessorManager class is not holding an instance of the ArcGIS
1937 geoprocessor, then this parameter is ignored. It will be holding one under the
1938 following circumstances:
1939
1940 * Your code invokes a method of some class that has a dependency on ArcGIS and
1941   internally uses the geoprocessor to do whatever work it needs to do. In this,
1942   scenario, that method will cause GeoprocessorManager.InitializeGeoprocessor() to
1943   be called. This is the most common scenario.
1944  
1945 * Your code explicitly initializes the geoprocessor by calling
1946   GeoprocessorManager.InitializeGeoprocessor().
1947
1948 * Your code provides the GeoprocessorManager with a geoprocessor instance by
1949   calling GeoprocessorManager.SetGeoprocessor()."""),
1950     isExposedToPythonCallers=True,
1951     isExposedByCOM=True)
1952
1953 AddArgumentMetadata(GeoprocessorManager.RefreshCatalog, u'cls',
1954     typeMetadata=ClassOrClassInstanceTypeMetadata(cls=GeoprocessorManager),
1955     description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__)
1956
1957 AddArgumentMetadata(GeoprocessorManager.RefreshCatalog, u'directory',
1958     typeMetadata=DirectoryTypeMetadata(mustExist=True),
1959     description=_(
1960 u"""Parent directory of the files or directories that have changed. For example,
1961 if you added three files to C:\\Data, you should pass C:\\Data as this
1962 parameter. If you just created C:\\Data, then you need to pass C:\\ for this
1963 parameter."""))
1964
1965 # Public method: GeoprocessorManager.ArcGISObjectExists
1966
1967 AddMethodMetadata(GeoprocessorManager.ArcGISObjectExists,
1968     shortDescription=_(u'Tests that a given path to an ArcGIS object exists and that the object is of a given type.'),
1969     longDescription=_(
1970 u"""This method uses the ArcGIS geoprocessor's Exists and Describe
1971 functions to check the existence and type of the object."""),
1972     isExposedToPythonCallers=True,
1973     isExposedByCOM=True)
1974
1975 AddArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'cls',
1976     typeMetadata=ClassOrClassInstanceTypeMetadata(cls=GeoprocessorManager),
1977     description=_(u'%s class or an instance of it.') % GeoprocessorManager.__name__)
1978
1979 AddArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'path',
1980     typeMetadata=UnicodeStringTypeMetadata(),
1981     description=_(u'Path to the object (e.g. a file, directory, raster, shapefile, table, etc.).'))
1982
1983 AddArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'correctTypes',
1984     typeMetadata=ListTypeMetadata(elementType=UnicodeStringTypeMetadata()),
1985     description=_(
1986 u"""List of data types that the object is expected to be, chosen from
1987 the possible values of the DataType property of the object returned by
1988 the geoprocessor's Describe function. Please see the ArcGIS
1989 geoprocessing documentation for the possible data type strings."""))
1990
1991 AddArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'typeDisplayName',
1992     typeMetadata=UnicodeStringTypeMetadata(),
1993     description=_(
1994 u"""Name of the expected data type of the object to display in logging
1995 messages. This is usually a more generic name than the entries that
1996 appear in correctTypes. For example, if the object is expected to be
1997 some kind of table, correctTypes may contain five or ten possible
1998 values, while typeDisplayName might simply be "table"."""))
1999
2000 AddResultMetadata(GeoprocessorManager.ArcGISObjectExists, u'exists',
2001     typeMetadata=BooleanTypeMetadata(),
2002     description=_(u'True if the geoprocessor\'s Exists function reports that the specified path exists.'))
2003
2004 AddResultMetadata(GeoprocessorManager.ArcGISObjectExists, u'isCorrectType',
2005     typeMetadata=BooleanTypeMetadata(),
2006     description=_(u'True if the geoprocessor\'s Describe function reports that the specified path is one of the types of objects specified by correctTypes.'))
2007
2008 # Public method: GeoprocessorManager.DeleteArcGISObject
2009
2010 AddMethodMetadata(GeoprocessorManager.DeleteArcGISObject,
2011     shortDescription=_(u'Deletes the specified ArcGIS object, if it exists.'),
2012     longDescription=_(
2013 u"""If the object does not exist, no error will be raised. If the
2014 object exists but the geoprocessor's Describe function reports that it
2015 is not one of the types specified by the correctTypes parameter, a
2016 ValueError will be raised. If it exists and is one of the correct
2017 types, it will be deleted with the geoprocessor's Delete_management
2018 function."""),
2019     isExposedToPythonCallers=True,
2020     isExposedByCOM=True)
2021
2022 CopyArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'cls', GeoprocessorManager.DeleteArcGISObject, u'cls')
2023 CopyArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'path', GeoprocessorManager.DeleteArcGISObject, u'path')
2024 CopyArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'correctTypes', GeoprocessorManager.DeleteArcGISObject, u'correctTypes')
2025 CopyArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'typeDisplayName', GeoprocessorManager.DeleteArcGISObject, u'typeDisplayName')
2026
2027 # Public method: GeoprocessorManager.CopyArcGISObject
2028
2029 AddMethodMetadata(GeoprocessorManager.CopyArcGISObject,
2030     shortDescription=_(u'Copies the specified ArcGIS object.'),
2031     longDescription=_(
2032 u"""A ValueError will be raised if the source object does not exist or
2033 the geoprocessor's Describe function reports that it is not one of the
2034 types specified by the correctTypes parameter. A ValueError will also
2035 be raised if the destination object exists but overwriteExisting is
2036 False or if the object is not one of the correct types. If the
2037 destination object exists and is a correct type, it will be deleted
2038 with the geoprocessor's Delete_management function prior to making the
2039 copy. The source object will be copied with the geoprocessor's
2040 Copy_management function."""),
2041     isExposedToPythonCallers=True,
2042     isExposedByCOM=True)
2043
2044 CopyArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'cls', GeoprocessorManager.CopyArcGISObject, u'cls')
2045
2046 AddArgumentMetadata(GeoprocessorManager.CopyArcGISObject, u'source',
2047     typeMetadata=UnicodeStringTypeMetadata(),
2048     description=_(u'Path to the object to copy (e.g. a file, directory, raster, shapefile, table, etc.).'))
2049
2050 AddArgumentMetadata(GeoprocessorManager.CopyArcGISObject, u'destination',
2051     typeMetadata=UnicodeStringTypeMetadata(),
2052     description=_(u'Path to the copy to create.'))
2053
2054 AddArgumentMetadata(GeoprocessorManager.CopyArcGISObject, u'overwriteExisting',
2055     typeMetadata=BooleanTypeMetadata(),
2056     description=_(u'If True, the destination object will be overwritten.'))
2057
2058 CopyArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'correctTypes', GeoprocessorManager.CopyArcGISObject, u'correctTypes')
2059 CopyArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'typeDisplayName', GeoprocessorManager.CopyArcGISObject, u'typeDisplayName')
2060
2061 # Public method: GeoprocessorManager.MoveArcGISObject
2062
2063 AddMethodMetadata(GeoprocessorManager.MoveArcGISObject,
2064     shortDescription=_(u'Movies the specified ArcGIS object.'),
2065     longDescription=_(
2066 u"""A ValueError will be raised if the source object does not exist or
2067 the geoprocessor's Describe function reports that it is not one of the
2068 types specified by the correctTypes parameter. A ValueError will also
2069 be raised if the destination object exists but overwriteExisting is
2070 False or if the object is not one of the correct types. If the
2071 destination object exists and is a correct type, it will be deleted
2072 with the geoprocessor's Delete_management function prior to making the
2073 copy. The source object will be copied and then deleted with the
2074 geoprocessor's Copy_management and Delete_management functions."""),
2075     isExposedToPythonCallers=True,
2076     isExposedByCOM=True)
2077
2078 CopyArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'cls', GeoprocessorManager.MoveArcGISObject, u'cls')
2079
2080 AddArgumentMetadata(GeoprocessorManager.MoveArcGISObject, u'source',
2081     typeMetadata=UnicodeStringTypeMetadata(),
2082     description=_(u'Path to the object to move (e.g. a file, directory, raster, shapefile, table, etc.).'))
2083
2084 AddArgumentMetadata(GeoprocessorManager.MoveArcGISObject, u'destination',
2085     typeMetadata=UnicodeStringTypeMetadata(),
2086     description=_(u'New path for the object.'))
2087
2088 CopyArgumentMetadata(GeoprocessorManager.CopyArcGISObject, u'overwriteExisting', GeoprocessorManager.MoveArcGISObject, u'overwriteExisting')
2089 CopyArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'correctTypes', GeoprocessorManager.MoveArcGISObject, u'correctTypes')
2090 CopyArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'typeDisplayName', GeoprocessorManager.MoveArcGISObject, u'typeDisplayName')
2091
2092 # Public method: GeoprocessorManager.GetUniqueLayerName
2093
2094 AddMethodMetadata(GeoprocessorManager.GetUniqueLayerName,
2095     shortDescription=_(u'Returns a randomly generated string that may be used as the name of a new geoprocessing layer.'),
2096     longDescription=_(
2097 u"""This function loops through random names until it finds one for
2098 which the geoprocessors Exists function returns False."""),
2099     isExposedToPythonCallers=True,
2100     isExposedByCOM=True)
2101
2102 CopyArgumentMetadata(GeoprocessorManager.ArcGISObjectExists, u'cls', GeoprocessorManager.GetUniqueLayerName, u'cls')
2103
2104 AddResultMetadata(GeoprocessorManager.GetUniqueLayerName, u'name',
2105     typeMetadata=UnicodeStringTypeMetadata(),
2106     description=_(u'Randomly generated string that may be used as the name of a geoprocessing layer.'))
2107
2108 ###############################################################################
2109 # Names exported by this module
2110 ###############################################################################
2111
2112 __all__ = ['GeoprocessorManager',
2113            'ArcGISDependency',
2114            'ArcGISProductDependency',
2115            'ArcGISExtensionDependency',
2116            'ArcGISError',
2117            'ValidateMethodMetadataForExposureAsArcGISTool',
2118            'ExecuteMethodFromCommandLineAsArcGISTool']
Note: See TracBrowser for help on using the browser.