Skip to Content.

mget-help - RE: Downloading SSH

Please Wait...

Subject: Marine Geospatial Ecology Tools (MGET) help

Text archives


From: "Jason Roberts" <>
To: "'Hedley Grantham'" <>, <>
Subject: RE: Downloading SSH
Date: Wed, 28 Jan 2009 08:56:37 -0500
Title: Downloading SSH

Hedley,

 

We're sorry you are seeing this problem. Someone just contacted us about it several days ago. We are actively investigating it and would like to resolve it as soon as possible. There are at least two issues in play here. I believe the NameError is not the root issue, but a problem that happens when MGET tries to report the root issue.

 

What version of ArcGIS are you running?

 

Could you please try the following:

 

1.    Save the two attached files to the C:\PythonXY\Lib\site-packages\GeoEco directory, where XY is your version of Python, probably 25 if you have Arc 9.3 or 24 if you have 9.2. Overwrite the two files that are already in that directory.

2.    Try the tool again. Hopefully you will get a different error message this time.

3.    Send me the output.

 

Thanks,

Jason

 

From: Hedley Grantham [mailto:]
Sent: Wednesday, January 28, 2009 1:51 AM
To:
Subject: Downloading SSH

 

I am getting the following error when trying to use MGET to download SSH data.

Have you got any ideas what is going wrong, here is what I am getting:

Executing: AvisoDownloadSSHDatasetToArcGISRasters "Global DT-MSLA Ref Topex/Poseidon, Jason-1" 1/1/2006 1/1/2007 C:\WorkSpace ssh%Y%j false # # # # # # # false false C:\WorkSpace
Start Time: Wed Jan 28 17:41:15 2009
Running script AvisoDownloadSSHDatasetToArcGISRasters...
NameError: global name 'message' is not defined
<type 'exceptions.NameError'>: global name 'message' is not defined
Failed to execute (AvisoDownloadSSHDatasetToArcGISRasters).
End Time: Wed Jan 28 17:41:17 2009 (Elapsed Time: 2.00 seconds)

Cheers
Hedley Grantham

# Logging.py - Implements the Logger class, which other classes in the GeoEco
# Python package use to report activity to the user.
#
# Copyright (C) 2007 Jason J. Roberts
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License (available in the file LICENSE.TXT)
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
USA.

import datetime
import logging
import logging.config
import os
import sys
import time
import traceback
import types

from GeoEco.DynamicDocString import DynamicDocString
from GeoEco.Internationalization import _, UnicodeToUserPreferredEncoding, 
UserPreferredEncodingToUnicode


# Public classes exposed by this module

class Logger(object):
    __doc__ = DynamicDocString()

    _LogInfoAsDebug = False    
    _LogErrorsAsWarnings = False    

    @classmethod
    def GetLogInfoAsDebug(cls):
        return Logger._LogInfoAsDebug

    @classmethod
    def SetLogInfoAsDebug(cls, value):
        if not isinstance(value, types.BooleanType):
            RaiseException(TypeError(_(u'The value provided for the value 
parameter is an invalid type ("%(badType)s" in Python). Please provide a 
value having the Python type "bool".') % {u'badType' : type(value).__name__}))
        Logger._LogInfoAsDebug = value

    LogInfoAsDebug = property(GetLogInfoAsDebug, SetLogInfoAsDebug, 
doc=DynamicDocString())

    @classmethod
    def GetLogErrorsAsWarnings(cls):
        return Logger._LogErrorsAsWarnings

    @classmethod
    def SetLogErrorsAsWarnings(cls, value):
        if not isinstance(value, types.BooleanType):
            RaiseException(TypeError(_(u'The value provided for the value 
parameter is an invalid type ("%(badType)s" in Python). Please provide a 
value having the Python type "bool".') % {u'badType' : type(value).__name__}))
        Logger._LogErrorsAsWarnings = value

    LogErrorsAsWarnings = property(GetLogErrorsAsWarnings, 
SetLogErrorsAsWarnings, doc=DynamicDocString())
    
    @classmethod
    def Debug(cls, format, *args):
        try:
            logging.getLogger(u'GeoEco').debug(format.rstrip(), *args)
        except:
            pass

    @classmethod
    def Info(cls, format, *args):
        try:
            if cls.GetLogInfoAsDebug():
                logging.getLogger(u'GeoEco').debug(format.rstrip(), *args)
            else:
                logging.getLogger(u'GeoEco').info(format.rstrip(), *args)
        except:
            pass

    @classmethod
    def Warning(cls, format, *args):
        try:
            logging.getLogger(u'GeoEco').warning(format.rstrip(), *args)
        except:
            pass

    @classmethod
    def Error(cls, format, *args):
        try:
            if cls.GetLogErrorsAsWarnings():
                logging.getLogger(u'GeoEco').warning(format.rstrip(), *args)
            else:
                logging.getLogger(u'GeoEco').error(format.rstrip(), *args)
        except:
            pass

    @classmethod
    def RaiseException(cls, exception):
        try:
            raise exception
        except:
            if cls.GetLogErrorsAsWarnings():
                cls.LogExceptionAsWarning()
            else:
                cls.LogExceptionAsError()
            raise

    @classmethod
    def LogInfoAndSetInfoToDebug(cls, format, *args):
        cls.Info(format, *args)
        oldValue = cls.GetLogInfoAsDebug()
        cls.SetLogInfoAsDebug(True)
        return oldValue

    @classmethod
    def LogExceptionAsWarning(cls, format=None, *args):
        cls._LogExceptionAndMessage(logging.WARNING, format, *args)

    @classmethod
    def LogExceptionAsError(cls, format=None, *args):
        if cls.GetLogErrorsAsWarnings():
            cls._LogExceptionAndMessage(logging.WARNING, format, *args)
        else:
            cls._LogExceptionAndMessage(logging.ERROR, format, *args)

    _ExceptionTracebackID = None
    _ReportedSubsequentErrors = False
    
    @classmethod
    def _LogExceptionAndMessage(cls, level, format=None, *args):
        try:
            logger = logging.getLogger(u'GeoEco')

            # Log the exception, if it has not been done already.

            tb = sys.exc_info()[2]
            if tb is not None:
                try:

                    # Obtain the inner-most traceback object.
                    
                    while tb.tb_next is not None:
                        tb = tb.tb_next

                    # If we have not logged a traceback yet, or this is a
                    # different one than last time, log it.
                    
                    if cls._ExceptionTracebackID != id(tb):
                        cls._ExceptionTracebackID = id(tb)
                        cls._ReportedSubsequentErrors = False
                        logger.log(level, u'%s: %s', 
unicode(sys.exc_info()[0].__name__), unicode(sys.exc_info()[1]).rstrip())

                        # Log useful debugging information, starting with a 
stack
                        # trace. To log the stack trace, we could just call
                        # logger.debug(exc_info=True), but that function 
ultimately
                        # calls traceback.print_exception(), which only 
prints the
                        # stack frames from the exception back to the frame 
that
                        # handled the exception. It does NOT go all the way 
back up
                        # the stack. That is is not good enough for us, 
because we
                        # need to see how we got to the problem, all the way 
from
                        # the program entry point. So we have to use the
                        # traceback.extract_stack() function to get those 
outer
                        # frames.

                        logger.debug(_(u'---------- BEGINNING OF DEBUGGING 
INFORMATION ----------'))
                        logger.debug(_(u'Traceback (most recent call last):'))
                        stackTraceEntries = 
traceback.extract_stack(tb.tb_frame.f_back) + traceback.extract_tb(tb)
                        for entry in traceback.format_list(stackTraceEntries):
                            for line in entry.split('\n'):
                                if len(line) > 0:
                                    logger.debug(line.rstrip())
                        logger.debug(u'%s: %s', 
unicode(sys.exc_info()[0].__name__), unicode(sys.exc_info()[1]).rstrip())

                        # Log the local and global variables of the most 
recent
                        # GeoEco call, unless it is RaiseException.

                        frame = tb.tb_frame
                        try:
                            while frame is not None and 
(frame.f_code.co_filename.find('GeoEco') == -1 or 
frame.f_code.co_filename.find('AssimilatedModules') >= 0 or 
frame.f_code.co_filename.endswith('Logging.py') and frame.f_code.co_name == 
'RaiseException' or frame.f_code.co_filename.endswith('R.py') and 
frame.f_code.co_name in ['__call__', '__getitem__', '__setitem__', 
'__delitem__', '__getattr__', '__setattr__', '__delattr__']):
                                frame = frame.f_back
                            if frame is None:
                                frame = tb.tb_frame
                            logger.debug(_(u'Local variables for stack frame: 
File "%(file)s", line %(line)i, in %(func)s:') % {u'file' : 
frame.f_code.co_filename, u'line' : frame.f_lineno, u'func' : 
frame.f_code.co_name})
                            keys = frame.f_locals.keys()
                            keys.sort()
                            for key in keys:
                                logger.debug(u'  %s = %s', unicode(key), 
repr(frame.f_locals[key]))

                            logger.debug(_(u'Global variables for stack 
frame: File "%(file)s", line %(line)i, in %(func)s:') % {u'file' : 
frame.f_code.co_filename, u'line' : frame.f_lineno, u'func' : 
frame.f_code.co_name})
                            keys = frame.f_globals.keys()
                            keys.sort()
                            for key in keys:
                                if key != '__builtins__':       # Don't 
bother dumping __builtins__
                                    logger.debug(u'  %s = %s', unicode(key), 
repr(frame.f_globals[key]))
                        finally:
                            del frame            # Avoid memory cycle by 
explicitly deleting frame object; see Python documentation for discussion of 
this problem.

                        # Log other useful info.                              
  

                        logger.debug(_(u'Enviornment variables:'))
                        keys =  os.environ.keys()
                        keys.sort()
                        for key in keys:
                            logger.debug(u'  %s = %s', unicode(key), 
repr(os.environ[key]))

                        logger.debug(_(u'Other variables:'))
                        import GeoEco
                        logger.debug(u'  GeoEco.__version__ = %s', 
repr(GeoEco.__version__))
                        logger.debug(u'  sys.argv = %s', repr(sys.argv))
                        logger.debug(u'  sys.version = %s', str(sys.version))
                        logger.debug(u'  sys.version_info = %s', 
repr(sys.version_info))
                        logger.debug(u'  sys.platform = %s', 
str(sys.platform))
                        if isinstance(sys.platform, basestring) and 
sys.platform.lower() == 'win32':
                            logger.debug(u'  sys.getwindowsversion() = %s', 
repr(sys.getwindowsversion()))
                        logger.debug(u'  os.getcwd() = %s', str(os.getcwd()))
                        logger.debug(u'  sys.path = %s', repr(sys.path))

                        keys =  sys.modules.keys()
                        keys.sort()
                        logger.debug(_(u'Loaded modules: ') + u', 
'.join(keys))

                        logger.debug(_(u'---------- END OF DEBUGGING 
INFORMATION ----------'))
                finally:
                    del tb          # Avoid memory cycle by explicitly 
deleting traceback object; see Python documentation for discussion of this 
problem.
            else:
                cls._ExceptionTracebackID = None

            # If the caller provided a message, log it.

            if format is not None:
                
                # If this is the first message to be logged after the 
exception,
                # first log a message indicating that the following messages 
are
                # consequences of the original exception.

                if cls._ExceptionTracebackID is not None and not 
cls._ReportedSubsequentErrors:
                    logger.log(level, _(u'The following consequences resulted 
from the original error:'))
                    cls._ReportedSubsequentErrors = True

                # Log the message.
                
                logger.log(level, format.rstrip(), *args)
            
        except:
            pass

    @classmethod
    def Initialize(cls, activateArcGISLogging=False, loggingConfigFile=None):
        cls.__doc__.Obj.ValidateMethodInvocation()

        # Hack: Set the _srcfile attribute in the logging module to
        # None, so that the module does not try to walk the stack when
        # emitting log messages. This stack walking screws up our
        # exception logging code when we're running in the Python 2.5
        # exception handler hosted in-process by ArcGIS 9.3.

        logging._srcfile = None

        # Hack: this module (GeoEco.Logging) defines a class 
_ArcGISLoggingHandler
        # which derives from logging.Handler. We want the user to be able to
        # reference this handler in a logging configuration file. But the 
logging.config
        # module offers no way to import this module into its namespace, which
        # prevents it from being able to instantiate our class. So we must 
manually
        # import ourself into the logging namespace (! not logging.config !).

        if not sys.modules[u'logging'].__dict__.has_key(u'GeoEco'):
            sys.modules[u'logging'].__dict__[u'GeoEco'] = 
sys.modules[u'GeoEco']
        if not sys.modules[u'logging'].__dict__.has_key(u'GeoEco.Logging'):
            sys.modules[u'logging'].__dict__[u'GeoEco.Logging'] = 
sys.modules[u'GeoEco.Logging']

        # If the caller provided a file, try to initialize the logging system
        # from it.

        callersFileWarning = None
        if loggingConfigFile is not None:
            callersFileWarning = 
cls._InitializeLoggingFromFile(loggingConfigFile)

        # If the caller did not provide a file, or the logging system could 
not
        # be initialized from it, try the user's default logging config file.

        userDefaultFile = None
        userDefaultFileWarning = None
        if loggingConfigFile is None or callersFileWarning is not None:
            if sys.platform.lower() == u'win32' and 
os.environ.has_key(u'APPDATA'):
                import codecs
                userDefaultFile = 
codecs.mbcs_decode(os.environ['APPDATA'])[0] + u'\\GeoEco\\Logging.ini'
            if userDefaultFile is not None:
                if os.path.isfile(userDefaultFile):
                    userDefaultFileWarning = 
cls._InitializeLoggingFromFile(userDefaultFile)
                else:
                    userDefaultFile = None

        # If if the caller's file or the user's default file did not work, try
        # the system default logging file.

        systemDefaultFile = 
os.path.join(os.path.dirname(sys.modules['GeoEco.Logging'].__file__), 
u'Configuration', u'Logging.ini')
        systemDefaultFileWarning = None
        if (loggingConfigFile is None or callersFileWarning is not None) and 
(userDefaultFile is None or userDefaultFileWarning is not None):
            systemDefaultFileWarning = 
cls._InitializeLoggingFromFile(systemDefaultFile)

        # If all of the above failed, initialize using a hard-coded
        # configuration.

        manualInitializationWarning = None        
        if (loggingConfigFile is None or callersFileWarning is not None) and 
(userDefaultFile is None or userDefaultFileWarning is not None) and 
systemDefaultFileWarning is not None:
            try:
                stdout_handler = logging.StreamHandler(strm=sys.stdout)
                stdout_handler.level = logging.INFO
                logging.getLogger(u'').addHandler(stdout_handler)
                logging.getLogger(u'').setLevel(logging.INFO)
            except Exception, e:
                manualInitializationWarning = _(u'Failed to initialize the 
logging system from hard-coded settings. One of the logging functions 
reported the error: %s: %s') % (e.__class__.__name__, unicode(e))

        # Log warning messages, if any, and a success message. We have to 
delay of
        # all this until now, because until this point, the logging system 
may not
        # have been initialized. If we were able to initialize it, we want any
        # warning messages to be logged using the best possible settings.

        if manualInitializationWarning is not None:
            if callersFileWarning is not None:
                print(callersFileWarning)
            if userDefaultFileWarning is not None:
                print(userDefaultFileWarning)
            print(systemDefaultFileWarning)
            print(manualInitializationWarning)
            print(_(u'The logging system could not be initialized. Log 
messages will not be reported.'))

        elif loggingConfigFile is not None and callersFileWarning is None:
            cls.Debug(_(u'Logging system initialized from config file 
"%s".'), loggingConfigFile)

        elif userDefaultFile is not None and userDefaultFileWarning is None:
            if callersFileWarning is not None:
                cls.Warning(callersFileWarning)
            cls.Debug(_(u'Logging system initialized from config file 
"%s".'), userDefaultFile)

        elif systemDefaultFileWarning is None:
            if callersFileWarning is not None:
                cls.Warning(callersFileWarning)
            if userDefaultFileWarning is not None:
                cls.Warning(userDefaultFileWarning)
            cls.Debug(_(u'Logging system initialized from config file 
"%s".'), systemDefaultFile)

        elif manualInitializationWarning is None:
            if callersFileWarning is not None:
                cls.Warning(callersFileWarning)
            if userDefaultFileWarning is not None:
                cls.Warning(userDefaultFileWarning)
            cls.Warning(systemDefaultFileWarning)
            cls.Info(_(u'Log messages will only be sent to the console output 
(stdout).'))

        # Active ArcGIS logging if requested.

        if activateArcGISLogging and (loggingConfigFile is not None and 
callersFileWarning is None or userDefaultFile is not None and 
userDefaultFileWarning is None or systemDefaultFileWarning is None):
            cls.ActivateArcGISLogging()

    @classmethod
    def ActivateArcGISLogging(cls):
        _ArcGISLoggingHandler.Activate()

    @classmethod
    def _InitializeLoggingFromFile(cls, loggingConfigFile=None):
        assert isinstance(loggingConfigFile, types.UnicodeType), 
u'loggingConfigFile must be a Unicode string'

        # Try to open the file for reading, so we know it is accessible.

        try:
            f = file(loggingConfigFile, u'r')
        except Exception, e:
            return(_(u'Failed to initialize logging from the config file 
"%(file)s". The file could not be opened. The operating system reported: 
%(error)s: %(msg)s') % {u'file': loggingConfigFile, u'error': 
e.__class__.__name__, u'msg': unicode(e)})
        try:
            f.close()
        except:
            pass

        # If that was successful, try to initialize logging.
        #
        # If the logging config file is improperly formatted, the logging
        # initialization function can fail in two ways. First, it may raise an
        # exception. In this case, catch it as usual and return a failure
        # message.
        #
        # In the second failure mode, it simply prints an exception trace to
        # stderr, but does not raise an exception or return a value indicating
        # failure. This would be ok, except that it can leave the logging 
system
        # in an inconsistent state: subsequent calls to logging functions such
        # as logging.debug() will fail. Again, these failed calls swallow
        # exceptions, just printing their traces to stderr.
        #
        # Because we don't want stderr to be spammed when logging fails to
        # initialize, we detect the failure in the logging configuration
        # function by capturing stderr, and return a failure message.

        cap = _StderrCapturer()
        cap.Start()
        try:
            try:
                # First handle a bug in logging.config.fileConfig that causes 
the
                # logging Handler class to raise an exception at shutdown. 
This bug
                # was reported in Aug 2006 and exists in Python 2.4.4 but is
                # supposedly fixed in Python 2.5.
                
                if globals().has_key(u'_TempHandler') and 
globals()[u'_TempHandler'] is not None:
                    
logging.getLogger(u'GeoEco').removeHandler(globals()[u'_TempHandler'])
                    globals()[u'_TempHandler'].close()
                    del globals()[u'_TempHandler']

                # Now read the config file.
                
                logging.config.fileConfig(loggingConfigFile)
                
            except Exception, e:
                return _(u'Failed to initialize logging from the config file 
"%(file)s". Please verify that the contents of the config file are valid. 
Search the Python documentation for "logging configuration file format". In 
Python 2.4, the article is titled "6.29.10.2 Configuration file format". 
Python\'s log configuration file parser (logging.config.fileConfig) reported: 
%(error)s: %(msg)s') % {u'file': loggingConfigFile, u'error': 
e.__class__.__name__, u'msg': unicode(e)}
        finally:
            result = cap.Stop()

        if result is not None and len(result.strip()) > 0:
            result_lines = result.strip().split(u'\n')
            i = 1
            while i < len(result_lines) and (len(result_lines[i]) == 0 or 
result_lines[i][0] == u' '):
                i = i + 1
            message = u''
            for j in range(i, len(result_lines)):
                message = message + result_lines[j] + u'\n'
            message = message.strip()
            return _(u'Failed to initialize logging from the config file 
"%s". Please verify that the contents of the config file are valid. Search 
the Python documentation for "logging configuration file format". In Python 
2.4, the article is titled "6.29.10.2 Configuration file format". Python\'s 
log configuration file parser (logging.config.fileConfig) reported the error: 
%s') % (loggingConfigFile, message)

        # Return successfully.

        return None


class ProgressReporter(object):    
    __doc__ = DynamicDocString()

    def __init__(self,
                 progressMessage1=_(u'Progress report: %(elapsed)s elapsed, 
%(opsCompleted)i operations completed, %(perOp)s per operation, 
%(opsRemaining)i remaining, estimated completion time: %(etc)s.'),
                 progressMessage2=_(u'Progress report: %(elapsed)s elapsed, 
%(opsCompleted)i operations completed, %(perOp)s per operation.'),
                 completionMessage=_(u'Processing complete: %(elapsed)s 
elapsed, %(opsCompleted)i operations completed, %(perOp)s per operation.'),
                 abortedMessage=_(u'Processing stopped before all operations 
were completed: %(elapsed)s elapsed, %(opsCompleted)i operations completed, 
%(perOp)s per operation, %(opsIncomplete)i operations not completed.'),
                 loggingChannel=u'GeoEco'):

        self.ProgressMessage1 = progressMessage1
        self.ProgressMessage2 = progressMessage2
        self.CompletionMessage = completionMessage
        self.AbortedMessage = abortedMessage
        self.LoggingChannel = loggingChannel
        self._TotalOperations = None
        self._OperationsCompleted = 0
        self._TimeStarted = None
        self._TimeCompleted = None
        self._ClockStarted = None
        self._ClockNextReportTime = None

    def _GetProgressMessage1(self):
        return self._ProgressMessage1

    def _SetProgressMessage1(self, value):
        assert isinstance(value, (types.NoneType, types.UnicodeType)), 
u'ProgressMessage1 must be a Unicode string, or None.'
        self._ProgressMessage1 = value
    
    ProgressMessage1 = property(_GetProgressMessage1, _SetProgressMessage1, 
doc=DynamicDocString())

    def _GetProgressMessage2(self):
        return self._ProgressMessage2

    def _SetProgressMessage2(self, value):
        assert isinstance(value, (types.NoneType, types.UnicodeType)), 
u'ProgressMessage2 must be a Unicode string, or None.'
        self._ProgressMessage2 = value
    
    ProgressMessage2 = property(_GetProgressMessage2, _SetProgressMessage2, 
doc=DynamicDocString())

    def _GetCompletionMessage(self):
        return self._CompletionMessage

    def _SetCompletionMessage(self, value):
        assert isinstance(value, (types.NoneType, types.UnicodeType)), 
u'CompletionMessage must be a Unicode string, or None.'
        self._CompletionMessage = value
    
    CompletionMessage = property(_GetCompletionMessage, 
_SetCompletionMessage, doc=DynamicDocString())

    def _GetAbortedMessage(self):
        return self._AbortedMessage

    def _SetAbortedMessage(self, value):
        assert isinstance(value, (types.NoneType, types.UnicodeType)), 
u'AbortedMessage must be a Unicode string, or None.'
        self._AbortedMessage = value
    
    AbortedMessage = property(_GetAbortedMessage, _SetAbortedMessage, 
doc=DynamicDocString())

    def _GetLoggingChannel(self):
        return self._LoggingChannel

    def _SetLoggingChannel(self, value):
        assert isinstance(value, (types.NoneType, types.UnicodeType)), 
u'LoggingChannel must be a Unicode string, or None.'
        self._LoggingChannel = value
    
    LoggingChannel = property(_GetLoggingChannel, _SetLoggingChannel, 
doc=DynamicDocString())

    def _GetTotalOperations(self):
        return self._TotalOperations

    def _SetTotalOperations(self, value):
        assert value is None or (isinstance(value, types.IntType) and value 
>= 0), u'totalOperations must be a non-negative integer, or None'
        self._TotalOperations = value
        if self._TotalOperations is not None and self.HasStarted and 
self._OperationsCompleted >= self._TotalOperations:
            self.Stop()
    
    TotalOperations = property(_GetTotalOperations, _SetTotalOperations, 
doc=DynamicDocString())

    def _GetOperationsCompleted(self):
        return self._OperationsCompleted
    
    OperationsCompleted = property(_GetOperationsCompleted, 
doc=DynamicDocString())

    def _GetTimeStarted(self):
        return self._TimeStarted
    
    TimeStarted = property(_GetTimeStarted, doc=DynamicDocString())

    def _GetTimeCompleted(self):
        return self._TimeCompleted
    
    TimeCompleted = property(_GetTimeCompleted, doc=DynamicDocString())

    def _GetHasStarted(self):
        return self._TimeStarted is not None
    
    HasStarted = property(_GetHasStarted, doc=DynamicDocString())

    def _GetHasCompleted(self):
        return self._TimeCompleted is not None
    
    HasCompleted = property(_GetHasCompleted, doc=DynamicDocString())

    def _GetTimeElapsed(self):
        if self._TimeStarted is None:
            return None
        if self._TimeCompleted is None:
            return datetime.timedelta(seconds = time.clock() - 
self._ClockStarted)
        return self._TimeCompleted - self._TimeStarted

    TimeElapsed = property(_GetTimeElapsed, doc=DynamicDocString())

    def Start(self, totalOperations=None):
        assert not self.HasStarted, u'This ProgressReporter was already 
started and cannot be started a second time'
        
        self.TotalOperations = totalOperations
        self._OperationsCompleted = 0
        self._TimeStarted = datetime.datetime.now()
        self._ClockStarted = time.clock()
        self._ClockNextReportTime = self._ClockStarted + 60.0

        if totalOperations == 0:
            self.Stop()

    def ReportProgress(self, operationsCompleted=1):
        assert self.HasStarted, u'This ProgressReporter has not been started'
        assert operationsCompleted >= 1, u'operationsCompelted must be 
greater than or equal to 1'
        
        self._OperationsCompleted += operationsCompleted
        
        if self._TotalOperations is not None and self._OperationsCompleted >= 
self._TotalOperations:
            self.Stop()
            return
        
        clockNow = time.clock()
        if clockNow >= self._ClockNextReportTime:
            timeElapsed = self.TimeElapsed
            timePerOp = timeElapsed / self._OperationsCompleted

            if self._TotalOperations is not None:
                if self._ProgressMessage1 is not None:
                    now = datetime.datetime.now()
                    timeOfCompletion = now + timePerOp * 
(self._TotalOperations - self._OperationsCompleted)
                    if now.day == timeOfCompletion.day:
                        etc = unicode(timeOfCompletion.strftime('%X'))
                    else:
                        etc = unicode(timeOfCompletion.strftime('%c'))
                    self._Log(self._FormatProgressMessage1(timeElapsed, 
self._OperationsCompleted, timePerOp, self._TotalOperations - 
self._OperationsCompleted, etc))

            elif self._ProgressMessage2 is not None:
                self._Log(self._FormatProgressMessage2(timeElapsed, 
self._OperationsCompleted, timePerOp))

            self._ClockNextReportTime = clockNow + 300.0

    def Stop(self):
        assert self.HasStarted, u'This ProgressReporter has not been started'
        if self.HasCompleted:
            return
        
        self._TimeCompleted = datetime.datetime.now()
        timeElapsed = self.TimeElapsed
        if self._OperationsCompleted > 0:
            timePerOp = timeElapsed / self._OperationsCompleted
        else:
            timePerOp = datetime.timedelta()
        if self._TotalOperations is None or self._OperationsCompleted >= 
self._TotalOperations:
            if self._CompletionMessage is not None:
                self._Log(self._FormatCompletionMessage(timeElapsed, 
self._OperationsCompleted, timePerOp))
        elif self._AbortedMessage is not None:
            self._Log(self._FormatAbortedMessage(timeElapsed, 
self._OperationsCompleted, timePerOp, self._TotalOperations - 
self._OperationsCompleted))

    def _Log(self, message):
        try:
            if self._LoggingChannel == u'GeoEco':
                Logger.Info(message)
            else:
                logging.getLogger(self._LoggingChannel).info(message)
        except:
            pass

    # Private methods intended to be overridden by derived classes
    # that need to do custom formatting of the progress messages.

    def _FormatProgressMessage1(self, timeElapsed, opsCompleted, timePerOp, 
opsRemaining, estimatedTimeOfCompletionString):
        return self._ProgressMessage1 % {u'elapsed' : 
unicode(datetime.timedelta(days=timeElapsed.days, 
seconds=timeElapsed.seconds)), u'opsCompleted': opsCompleted, u'perOp': 
unicode(timePerOp), u'opsRemaining': opsRemaining, u'etc': 
estimatedTimeOfCompletionString}

    def _FormatProgressMessage2(self, timeElapsed, opsCompleted, timePerOp):
        return self._ProgressMessage2 % {u'elapsed' : 
unicode(datetime.timedelta(days=timeElapsed.days, 
seconds=timeElapsed.seconds)), u'opsCompleted': opsCompleted, u'perOp': 
unicode(timePerOp)}

    def _FormatCompletionMessage(self, timeElapsed, opsCompleted, timePerOp):
        return self._CompletionMessage % {u'elapsed' : 
unicode(datetime.timedelta(days=timeElapsed.days, 
seconds=timeElapsed.seconds)), u'opsCompleted': opsCompleted, u'perOp': 
unicode(timePerOp)}

    def _FormatAbortedMessage(self, timeElapsed, opsCompleted, timePerOp, 
opsIncomplete):
        return self._AbortedMessage % {u'elapsed' : 
unicode(datetime.timedelta(days=timeElapsed.days, 
seconds=timeElapsed.seconds)), u'opsCompleted': opsCompleted, u'perOp': 
unicode(timePerOp), u'opsIncomplete': opsIncomplete}


# Private classes and functions global to this module

class _ArcGISLoggingHandler(logging.Handler):

    def __init__(self, level=logging.NOTSET):
        logging.Handler.__init__(self, level)

    def emit(self, record):
        if self.__class__._Instance != self:
            self.__class__._Instance = self
        if not isinstance(record, logging.LogRecord):
            return
        try:
            if self.__class__._PreactivationQueue is not None:
                if len(self.__class__._PreactivationQueue) == 1000:
                    del self.__class__._PreactivationQueue[0]
                self.__class__._PreactivationQueue.append(record)
            else:
                self._Emit(record)
        except:
            pass

    def _Emit(self, record):
        try:
            from GeoEco.ArcGIS import GeoprocessorManager
            message = self.format(record)
            if GeoprocessorManager.GetGeoprocessorIsCOMObject():
                message = UserPreferredEncodingToUnicode(message)
            else:
                message = UnicodeToUserPreferredEncoding(message)
            if record.levelno >= logging.ERROR:
                GeoprocessorManager.GetGeoprocessor().AddError(message)
            elif record.levelno >= logging.WARNING:
                GeoprocessorManager.GetGeoprocessor().AddWarning(message)
            else:
                GeoprocessorManager.GetGeoprocessor().AddMessage(message)
        except:
            pass

    @classmethod
    def GetInstance(cls):
        return _ArcGISLoggingHandler._Instance

    @classmethod
    def Activate(cls):
        if cls._PreactivationQueue is not None:
            from GeoEco.ArcGIS import GeoprocessorManager
            if GeoprocessorManager.GetGeoprocessor() is None:
                GeoprocessorManager.InitializeGeoprocessor()
            if GeoprocessorManager.GetArcGISMajorVersion() == 9 and 
GeoprocessorManager.GetArcGISMinorVersion() == 3 and 
GeoprocessorManager.GetArcGISServicePack() == 0:       # Handle ArcGIS 9.3 
bug NIM036130 - Print statements inside an in-proc script tool will lead to a 
"<type 'exceptions.IOError'>: [Errno 9] Bad file descriptor" error
                _GeoEcoStreamHandler.Deactivate()
            while len(cls._PreactivationQueue) > 0:
                record = cls._PreactivationQueue.pop(0)
                cls._Instance._Emit(record)
                del record
            cls._PreactivationQueue = None

    _Instance = None
    _PreactivationQueue = []


class _GeoEcoStreamHandler(logging.StreamHandler):

    def __init__(self, strm=None):
        logging.StreamHandler.__init__(self, strm)

    def emit(self, record):
        if not _GeoEcoStreamHandler._Deactivated:
            logging.StreamHandler.emit(self, record)

    @classmethod
    def Deactivate(cls):
        _GeoEcoStreamHandler._Deactivated = True

    _Deactivated = False


class _StderrCapturer(object):

    def __init__(self):
        self._Buffer = None
        self._OriginalStderr = None

    def Start(self):
        if self._OriginalStderr is None:
            self._Buffer = None
            self._OriginalStderr = sys.stderr
            sys.stderr = self

    def write(self, obj):
        if self._OriginalStderr is not None:
            if self._Buffer is None:
                self._Buffer = unicode(obj)
            else:
                self._Buffer = self._Buffer + unicode(obj)

    def Stop(self):
        if self._OriginalStderr is not None:
            sys.stderr = self._OriginalStderr
            self._OriginalStderr = None
            return self._Buffer
        else:
            return None


# Initialize, but do not activate, the ArcGIS logging handler. Until the 
handler
# is activated by an external caller (by calling 
GeoEco.Logging.ActivateArcGISLogging)
# the handler will just queue log messages in memory. Then, when it is
# activated, it will dump the queue to ArcGIS. This ensures that all log
# messages are reported to the ArcGIS UI when GeoEco is used from an Arc
# geoprocessing script, even those generated before the Activate call is made.

_TempHandler = None         # This is required to address in Python's 
logging.config.fileConfig function: it calls logging._handlers.clear() but 
does not also remove the handlers from logging._handlerList
try:
    if _ArcGISLoggingHandler.GetInstance() is None:         # I'm fairly sure 
this will always return None, since nobody can instantiate 
_ArcGISLoggingHandler without importing the module first, causing the code 
below to execute first.
        _TempHandler = _ArcGISLoggingHandler(logging.INFO)
        _logger = logging.getLogger(u'GeoEco')
        if _logger is not None:
            _logger.addHandler(_TempHandler)
        del _logger
except:
    pass


###############################################################################
# Metadata: module
###############################################################################

from GeoEco.Metadata import *
from GeoEco.Types import *

AddModuleMetadata(shortDescription=_(u'Implements the Logger class, which 
other GeoEco classes use to report activity to the user.'))

###############################################################################
# Metadata: Logger class
###############################################################################

AddClassMetadata(Logger,
    shortDescription=_(u'Provides methods for reporting messages to the 
user.'),
    isExposedAsCOMServer=True,
    comIID=u'{0DA39145-CDE6-48F2-AFD9-8EA6623A3121}',
    comCLSID=u'{EE5ED6B5-55E7-4D65-B627-EF062B8332C4}')

# Public properties

AddPropertyMetadata(Logger.LogInfoAsDebug,
    typeMetadata=BooleanTypeMetadata(),
    shortDescription=_(u'If True, informational messages will be logged as 
debug messages.'),
    longDescription=_(
u"""Informational messages describe major processing steps that may be
interesting to the user but do not require the user to take any
action. For example, a method that performs three major processing
tasks might report an informational message after each step is
finished.

Sometimes your method may repeatedly call another method to accomplish
some processing that you consider to be relatively unimportant. For
example, your method may copy a bunch of files, and you might want to
inform the user that you are performing the copying but do not want to
inform them about every file. In this situation, set this property to
True prior to entering the copy loop, and restore it to False when you
are done."""),
    isExposedToPythonCallers=True,
    isExposedByCOM=True)

AddPropertyMetadata(Logger.LogErrorsAsWarnings,
    typeMetadata=BooleanTypeMetadata(),
    shortDescription=_(u'If True, errors will be logged as warning 
messages.'),
    longDescription=_(
u"""Error messages describe failures that halt processing and require the user
to fix a problem and restart processing. ArcGIS highlights error messages in 
red
in its user interface, and fails geoprocessing if even one error is reported.

Sometimes you don't want to halt processing when an unimportant operation 
fails.
For example, you may not care that a temporary file could not be deleted. In
that situation, set this property to True prior to calling File.Delete to 
delete
the file. If the deletion fails, the errors that occur will be logged as
warnings rather than errors, and processing will not be halted. After
File.Delete returns, set this property back to False, so that subsequent 
errors
will be logged as errors."""),
    isExposedToPythonCallers=True,
    isExposedByCOM=True)

# Public method: Debug

AddMethodMetadata(Logger.Debug,
    shortDescription=_(u'Reports a debugging message to the user.'),
    longDescription=_(
u"""Like the C printf function or the Python % operator, this method 
generates a
message string by merging in the optional arguments into the format string.

Debugging messages describe processing details that are usually not 
interesting
unless the user is diagnosing a problem. The default configuration of the
logging system causes debugging messages to be discarded."""),
    isExposedToPythonCallers=True,
    isExposedByCOM=True)

AddArgumentMetadata(Logger.Debug, u'cls',
    typeMetadata=ClassOrClassInstanceTypeMetadata(cls=Logger),
    description=_(u'%s class or an instance of it.') % Logger.__name__)

AddArgumentMetadata(Logger.Debug, u'format',
    typeMetadata=UnicodeStringTypeMetadata(),
    description=_(
u"""A printf-style format string. While Unicode strings are encouraged, this
method will also accept an 8-bit string for this parameter. For a complete
specification of the format of this string, look up "% formatting" in the 
Python
documentation."""))

AddArgumentMetadata(Logger.Debug, u'args',
    
typeMetadata=TupleTypeMetadata(elementType=AnyObjectTypeMetadata(canBeNone=True)),
    description=_(u'Values to insert into the format string.'))

# Public method: Info

AddMethodMetadata(Logger.Info,
    shortDescription=_(u'Reports an informational message to the user.'),
    longDescription=_(
u"""Like the C printf function or the Python % operator, this method 
generates a
message string by merging in the optional arguments into the format string.

Informational messages describe major processing steps that may be 
interesting to
the user but do not require the user to take any action. For example, a method
that performs three major processing tasks might report an informational 
message
after each step is finished. Do not report too many informational messages or
you may overwhelm the user. Report processing details as debug messages."""),
    isExposedToPythonCallers=True,
    isExposedByCOM=True)

AddArgumentMetadata(Logger.Info, u'cls',
    typeMetadata=ClassOrClassInstanceTypeMetadata(cls=Logger),
    description=Logger.Debug.__doc__.Obj.Arguments[0].Description)

AddArgumentMetadata(Logger.Info, u'format',
    typeMetadata=UnicodeStringTypeMetadata(),
    description=Logger.Debug.__doc__.Obj.Arguments[1].Description)

AddArgumentMetadata(Logger.Info, u'args',
    
typeMetadata=TupleTypeMetadata(elementType=AnyObjectTypeMetadata(canBeNone=True)),
    description=Logger.Debug.__doc__.Obj.Arguments[2].Description)

# Public method: Warning

AddMethodMetadata(Logger.Warning,
    shortDescription=_(u'Reports a warning message to the user.'),
    longDescription=_(
u"""Like the C printf function or the Python % operator, this method 
generates a
message string by merging in the optional arguments into the format string.

Warning messages describe important events that should be brought to the 
user's
attention but do not necessarily indicate that processing will fail. To draw 
the
user's attention, ArcGIS highlights warning messages in green in its user
interface.

*Note to GeoEco developers:* Do not call this method to report exceptions 
caught
inside methods of GeoEco classes. Call LogExceptionAsWarning instead."""),
    isExposedToPythonCallers=True,
    isExposedByCOM=True)

AddArgumentMetadata(Logger.Warning, u'cls',
    typeMetadata=ClassOrClassInstanceTypeMetadata(cls=Logger),
    description=Logger.Debug.__doc__.Obj.Arguments[0].Description)

AddArgumentMetadata(Logger.Warning, u'format',
    typeMetadata=UnicodeStringTypeMetadata(),
    description=Logger.Debug.__doc__.Obj.Arguments[1].Description)

AddArgumentMetadata(Logger.Warning, u'args',
    
typeMetadata=TupleTypeMetadata(elementType=AnyObjectTypeMetadata(canBeNone=True)),
    description=Logger.Debug.__doc__.Obj.Arguments[2].Description)

# Public method: Error

AddMethodMetadata(Logger.Error,
    shortDescription=_(u'Reports an error message to the user.'),
    longDescription=_(
u"""Like the C printf function or the Python % operator, this method 
generates a
message string by merging in the optional arguments into the format string.

Error messages describe failures that halt processing and require the user to
fix a problem and restart processing. ArcGIS highlights error messages in red 
in
its user interface, and fails geoprocessing if even one error is reported.
Because of this, do not report error messages unless the problem is serious
enough to stop processing. For problems that may or may not be of consequence 
to
the user, report warning messages.

If LogWarningsAsErrors has been set to True, the message will be reported as a
warning rather than an error.

*Note to GeoEco developers:* Do not call this method to report exceptions 
caught
inside methods of GeoEco classes. Call LogExceptionAsError instead."""),
    isExposedToPythonCallers=True,
    isExposedByCOM=True)

AddArgumentMetadata(Logger.Error, u'cls',
    typeMetadata=ClassOrClassInstanceTypeMetadata(cls=Logger),
    description=Logger.Debug.__doc__.Obj.Arguments[0].Description)

AddArgumentMetadata(Logger.Error, u'format',
    typeMetadata=UnicodeStringTypeMetadata(),
    description=Logger.Debug.__doc__.Obj.Arguments[1].Description)

AddArgumentMetadata(Logger.Error, u'args',
    
typeMetadata=TupleTypeMetadata(elementType=AnyObjectTypeMetadata(canBeNone=True)),
    description=Logger.Debug.__doc__.Obj.Arguments[2].Description)

# Public method: LogInfoAndSetInfoToDebug

AddMethodMetadata(Logger.LogInfoAndSetInfoToDebug,
    shortDescription=_(u'Reports an informational message, sets the 
LogInfoAsDebug property to True, and returns its previous value.'),
    longDescription=_(
u"""This function is used to efficiently implement a common logging
scenario: a function wants to log one informational message announcing
the processing it is doing and report everything else as debug
messages, including informational messages reported by nested
functions. The Python pattern for implementing this scenario is::

    from GeoEco.Internationalization import _
    from GeoEco.Logging import Logger

    def MyFunc(...):
        oldLogInfoAsDebug = Logger.LogInfoAndSetInfoToDebug(_(u'Doing my 
processing...'))
        try:
            ...
        finally:
            Logger.SetLogInfoAsDebug(oldLogInfoAsDebug)
"""),
    isExposedToPythonCallers=True,
    isExposedByCOM=True)

CopyArgumentMetadata(Logger.Info, u'cls', Logger.LogInfoAndSetInfoToDebug, 
u'cls')
CopyArgumentMetadata(Logger.Info, u'format', Logger.LogInfoAndSetInfoToDebug, 
u'format')
CopyArgumentMetadata(Logger.Info, u'args', Logger.LogInfoAndSetInfoToDebug, 
u'args')

AddResultMetadata(Logger.LogInfoAndSetInfoToDebug, u'oldLogInfoAsDebug',
    typeMetadata=BooleanTypeMetadata(),
    description=_(u"""The old value of the LogInfoAsDebug property, prior to 
this method being invoked."""))

# Public method: RaiseException

AddMethodMetadata(Logger.RaiseException,
    shortDescription=_(u'Raises a Python exception and logs it as an error 
message and additional information as debug messages.'),
    longDescription=_(
u"""GeoEco classes should not raise exceptions by calling the Python raise
statement directly, but should call this method instead. This method will 
raise
the exception, log it as an error (or warning, if LogWarningsAsErrors has been
set to True), and also log a bunch of debugging information as debug 
messages."""))

AddArgumentMetadata(Logger.RaiseException, u'cls',
    typeMetadata=ClassOrClassInstanceTypeMetadata(cls=Logger),
    description=Logger.Debug.__doc__.Obj.Arguments[0].Description)

AddArgumentMetadata(Logger.RaiseException, u'exception',
    typeMetadata=ClassInstanceTypeMetadata(cls=Exception),
    description=_(u'The Python exception instance to raise.'))

# Public method: LogExceptionAsWarning

AddMethodMetadata(Logger.LogExceptionAsWarning,
    shortDescription=_(u'Logs a Python exception caught by a GeoEco class as 
a warning message and additional information as debug messages.'),
    longDescription=_(
u"""GeoEco classes should use LogExceptionAsWarning or LogExceptionAsError to
report exceptions caught by except clauses of try statements, like this::

    Logger.Debug(_(u'Copying file %s to %s.') % (sourceFile, destinationFile))
    try:
        shutil.copy2(sourceFile, destinationFile)
    except:
        Logger.LogExceptionAsError(_(u'Could not copy file %(source)s to 
%(dest)s.') % \\
                                   {u'source' :  sourceFile, u'dest' : 
destinationFile})
        raise

As shown, the except clause should re-raise the exception (if appropriate) 
using
a raise statement with no parameters. LogExceptionAsWarning and
LogExceptionAsError will log the exception and some debugging information,
including a stack trace. If the caller provides the optional format string, it
is logged as a "consequence" of the original error. For example the code above
produces the following output when the caller does not have permission to 
write
the destination file::

    DEBUG Copying file c:\\foo.txt to c:\\bar.txt.
    ERROR IOError: [Errno 13] Permission denied: u'c:\\\\bar.txt'
    DEBUG ---------- BEGINNING OF DEBUGGING INFORMATION ----------
    DEBUG Traceback (most recent call last):
    DEBUG   File "<stdin>", line 1, in ?
    DEBUG   File "<stdin>", line 2, in tryit
    DEBUG   File 
"C:\\Python24\\Lib\\site-packages\\GeoEco\\FileSystemUtils.py", line 47, in 
CopyFile
    DEBUG     shutil.copy2(sourceFile, destinationFile)
    DEBUG   File "C:\\Python24\\lib\\shutil.py", line 92, in copy2
    DEBUG     copyfile(src, dst)
    DEBUG   File "C:\\Python24\\lib\\shutil.py", line 48, in copyfile
    DEBUG     fdst = open(dst, 'wb')
    DEBUG IOError: [Errno 13] Permission denied: u'c:\\\\bar.txt'
    DEBUG End of traceback. Logging other useful debugging information...
    DEBUG sys.argv = ['']
    DEBUG sys.version = 2.4.4 (#71, Oct 18 2006, 08:34:43) [MSC v.1310 32 bit 
(Intel)]
    DEBUG sys.version_info = (2, 4, 4, 'final', 0)
    DEBUG sys.platform = win32
    DEBUG sys.getwindowsversion() = (5, 1, 2600, 2, 'Service Pack 2')
    DEBUG ...
    DEBUG ---------- END OF DEBUGGING INFORMATION ----------
    ERROR The following consequences resulted from the original error:
    ERROR Could not copy file c:\\foo.txt to c:\\bar.txt.

Unless the user has debugging messages turned on, they will only see the 
warning
and error messages in the log::

    ERROR IOError: [Errno 13] Permission denied: u'c:\\\\bar.txt'
    ERROR The following consequences resulted from the original error:
    ERROR Could not copy file c:\\foo.txt to c:\\bar2\\bar.txt.

Except clauses higher on the stack can also call LogExceptionAsWarning and
LogExceptionAsError. The methods keep track of whether the original exception
was logged and will not log it a second time. Instead they will just log the
optional format string, if provided, as a subsequent "consequence" of the
original exception. This allows nested methods to illustrate how the low-level
failure causes a problem in the high-level operation the user actually cares
about. For example, if the output file from a function cannot be copied from a
temporary location because a directory cannot be created, the log might look
like this::

    ERROR IOError: [Errno 13] Permission denied: u'c:\\\\output'
    ERROR The following consequences resulted from the original error:
    ERROR Could create directory c:\\output.
    ERROR Could not copy file c:\\processing\\results.txt to 
c:\\output\\results.txt.
    ERROR Could not copy the results to the output directory."""))

AddArgumentMetadata(Logger.LogExceptionAsWarning, u'cls',
    typeMetadata=ClassOrClassInstanceTypeMetadata(cls=Logger),
    description=Logger.Debug.__doc__.Obj.Arguments[0].Description)

AddArgumentMetadata(Logger.LogExceptionAsWarning, u'format',
    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
    description=Logger.Debug.__doc__.Obj.Arguments[1].Description)

AddArgumentMetadata(Logger.LogExceptionAsWarning, u'args',
    
typeMetadata=TupleTypeMetadata(elementType=AnyObjectTypeMetadata(canBeNone=True)),
    description=Logger.Debug.__doc__.Obj.Arguments[2].Description)

# Public method: LogExceptionAsError

AddMethodMetadata(Logger.LogExceptionAsError,
    shortDescription=_(u'Logs a Python exception caught by a GeoEco class as 
an error message and additional information as debug messages.'),
    longDescription=Logger.LogExceptionAsWarning.__doc__.Obj.LongDescription)

AddArgumentMetadata(Logger.LogExceptionAsError, u'cls',
    typeMetadata=ClassOrClassInstanceTypeMetadata(cls=Logger),
    
description=Logger.LogExceptionAsWarning.__doc__.Obj.Arguments[0].Description)

AddArgumentMetadata(Logger.LogExceptionAsError, u'format',
    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
    
description=Logger.LogExceptionAsWarning.__doc__.Obj.Arguments[1].Description)

AddArgumentMetadata(Logger.LogExceptionAsError, u'args',
    
typeMetadata=TupleTypeMetadata(elementType=AnyObjectTypeMetadata(canBeNone=True)),
    
description=Logger.LogExceptionAsWarning.__doc__.Obj.Arguments[2].Description)

# Public method: Initialize

AddMethodMetadata(Logger.Initialize,
    shortDescription=_(u'Initializes the logging system.'),
    isExposedToPythonCallers=True,
    isExposedByCOM=True)

AddArgumentMetadata(Logger.Initialize, u'cls',
    typeMetadata=ClassOrClassInstanceTypeMetadata(cls=Logger),
    description=Logger.Debug.__doc__.Obj.Arguments[0].Description)

AddArgumentMetadata(Logger.Initialize, u'activateArcGISLogging',
    typeMetadata=BooleanTypeMetadata(),
    description=_(
u"""If true, logging messages will be delivered to the ArcGIS geoprocessing
system and appear in the ArcGIS user interface. If false, they will be 
reported
to other logging destinations but will be queued in memory for ArcGIS until 
the
ActivateArcGISLogging method is called. To limit memory consumption in the 
event
that the method is never called, the queue retains only the most recent 1000
messages. If you are calling Initialize but not running as part of an ArcGIS
geoprocessing *do not* pass true for this parameter. Passing true will 
decrease
performance by initializing the ArcGIS geoprocessor when it might not be
necessary to do so. (The memory used by the queue is negligible.)"""))

AddArgumentMetadata(Logger.Initialize, u'loggingConfigFile',
    typeMetadata=FileTypeMetadata(canBeNone=True, mustExist=True),
    description=_(
u"""Path to the logging configuration file. If not provided, this
function attempts to load a configuration file from these two
locations:

* The file %APPDATA%\\\\GeoEco\\\\Logging.ini (e.g.
  C:\\\\Documents and Settings\\\\Jason\\\\Application 
Data\\\\GeoEco\\\\Logging.ini).
  This allows each user to configure their own default logging settings.

* The file Logging.ini in the Configuration subdirectory of the GeoEco
  Python package installation directory (e.g.
  
C:\\\\Python24\\\\lib\\\\site-packages\\\\GeoEco\\\\Configuration\\\\Logging.ini).
  These settings will be used if the user does not specify their own
  Logging.ini file.

If neither is found (which would only occur if the GeoEco package
installation is corrupt), then all INFO, WARNING, and ERROR messages
will be logged to the console (i.e. the stdout stream)."""))

###############################################################################
# Metadata: ProgressReporter class
###############################################################################

AddClassMetadata(ProgressReporter,
    shortDescription=_(u'Provides a simple mechanism for long-running 
operations to report periodic progress to the user.'),
    longDescription=_(
u"""This class provides progress-reporting capability for two kinds of
long-running operations: those for which the total number of
sub-operations is known when the long-running operation is started,
and those for which the total number of sub-operations cannot be
determined. In both cases, this class periodically reports the elapsed
time, the number of completed sub-operations, and the average time per
sub-operation. If the number of sub-operations is known, this class
also reports the number of sub-operations remaining and the estimated
time of completion.

When the total number of sub-operations is known, use this class like
this::

    operations = [...]                          # List of operations to 
perform
    progressReporter = ProgressReporter()
    progressReporter.Start(len(operations))
    for op in operations:
        ...                                     # Do one operation
        progressReporter.ReportProgress()

When the total number of sub-operations is not known, use this class
like this::

    progressReporter = ProgressReporter()
    progressReporter.Start()
    while True:
        ...                                     # Do one operation or exit 
loop if done
        progressReporter.ReportProgress()
    progressReporter.Stop()

The ReportProgress method will report the first message after
one minute and an additional message every five minutes thereafter. A
message will also be reported when processing is complete, by
ReportProgress in the first scenario and Stop in the second scenario.
You can configure the format of the progress messages by setting the
ProgressMessage1, ProgressMessage2, and CompletionMessage properties.
All messages are reported at the Info logging level. You can configure
the logging channel that is used to report the messages by setting the
LoggingChannel property."""))

# Public properties

AddPropertyMetadata(ProgressReporter.ProgressMessage1,
    typeMetadata=UnicodeStringTypeMetadata(),
    shortDescription=_(u'printf-style format string for periodically 
reporting progress when the total number of sub-operations is known a 
priori.'),
    longDescription=_(
u"""Your string must include all five format specifiers, as is done in
this example::

    u'Progress report: %(elapsed)s elapsed, %(opsCompleted)i operations 
completed, %(perOp)s per operation, %(opsRemaining)i remaining, estimated 
completion time: %(etc)s.'
"""),
    isExposedToPythonCallers=True)

AddPropertyMetadata(ProgressReporter.ProgressMessage2,
    typeMetadata=UnicodeStringTypeMetadata(),
    shortDescription=_(u'printf-style format string for periodically 
reporting progress when the total number of sub-operations is not known a 
priori.'),
    longDescription=_(
u"""Your string must include all three format specifiers, as is done
in this example::

    u'Progress report: %(elapsed)s elapsed, %(opsCompleted)i operations 
completed, %(perOp)s per operation.'
"""),
    isExposedToPythonCallers=True)

AddPropertyMetadata(ProgressReporter.CompletionMessage,
    typeMetadata=UnicodeStringTypeMetadata(),
    shortDescription=_(u'printf-style format string for reporting that 
processing is complete.'),
    longDescription=_(
u"""Your string must include all three format specifiers, as is done
in this example::

    u'Processing complete: %(elapsed)s elapsed, %(opsCompleted)i operations 
completed, %(perOp)s per operation.'
"""),
    isExposedToPythonCallers=True)

AddPropertyMetadata(ProgressReporter.AbortedMessage,
    typeMetadata=UnicodeStringTypeMetadata(),
    shortDescription=_(u'printf-style format string for reporting that 
processing was stopped prematurely (i.e. that the Stop method was called 
before OperationsCompleted == TotalOperations).'),
    longDescription=_(
u"""Your string must include all four format specifiers, as is done in
this example::

    u'Processing stopped before all operations were completed: %(elapsed)s 
elapsed, %(opsCompleted)i operations completed, %(perOp)s per operation, 
%(opsIncomplete)i operations not completed.'
"""),
    isExposedToPythonCallers=True)

AddPropertyMetadata(ProgressReporter.LoggingChannel,
    typeMetadata=UnicodeStringTypeMetadata(),
    shortDescription=_(u'Logging channel that progress messages should be 
reported to.'),
    longDescription=_(
u"""Please see the documentation for the Python logging module formore
information about logging channels. All progress messages are reported
at the Info logging level. You can configure the message filtering and
destinations by editing the GeoEco Logging.ini file."""),
    isExposedToPythonCallers=True)

AddPropertyMetadata(ProgressReporter.TotalOperations,
    typeMetadata=IntegerTypeMetadata(canBeNone=True),
    shortDescription=_(u'Total number of sub-operations in this long-running 
operation, or None if the number of sub-operations is not known a priori.'),
    longDescription=_(u'This property is initialized by the Start method.'),
    isExposedToPythonCallers=True)

AddPropertyMetadata(ProgressReporter.OperationsCompleted,
    typeMetadata=IntegerTypeMetadata(),
    shortDescription=_(u'Total number of sub-operations that have been 
completed so far.'),
    longDescription=_(u'This property is updated when ReportProgress is 
called.'),
    isExposedToPythonCallers=True)

AddPropertyMetadata(ProgressReporter.TimeStarted,
    typeMetadata=ClassInstanceTypeMetadata(cls=datetime.datetime, 
canBeNone=True),
    shortDescription=_(u'The time processing was started.'),
    longDescription=_(
u"""This property is set to the current system time when Start is
called, using the datetime.datetime.now method."""),
    isExposedToPythonCallers=True)

AddPropertyMetadata(ProgressReporter.TimeCompleted,
    typeMetadata=ClassInstanceTypeMetadata(cls=datetime.datetime, 
canBeNone=True),
    shortDescription=_(u'The time processing was completed.'),
    longDescription=_(
u"""This property is set to the current system time when
ReportProgress is called for the last sub-operation (when the total
number of sub-operations is known a priori) or when Stop is called
(when the the total number of sub-operations is not known a priori),
using the datetime.datetime.now method."""),
    isExposedToPythonCallers=True)

AddPropertyMetadata(ProgressReporter.HasStarted,
    typeMetadata=BooleanTypeMetadata(),
    shortDescription=_(u'True if processing has started (i.e. if Start was 
called).'),
    isExposedToPythonCallers=True)

AddPropertyMetadata(ProgressReporter.HasCompleted,
    typeMetadata=BooleanTypeMetadata(),
    shortDescription=_(u'True if processing has completed (i.e. the last 
operation was reported to ReportProgress or Stop was called).'),
    isExposedToPythonCallers=True)

AddPropertyMetadata(ProgressReporter.TimeElapsed,
    typeMetadata=ClassInstanceTypeMetadata(cls=datetime.timedelta, 
canBeNone=True),
    shortDescription=_(u'The time elapsed since processing was started, or 
None if processing has not started yet.'),
    longDescription=_(
u"""Processing starts when Start is called and stops when
ReportProgress is called for the last sub-operation (when the total
number of sub-operations is known a priori) or when Stop is called
(when the the total number of sub-operations is not known a priori).
After processing has stopped, this property will consistently return
the total time required for processing (it will not keep increasing as
additional time passes)."""),
    isExposedToPythonCallers=True)

# Constructor

AddMethodMetadata(ProgressReporter.__init__,
    shortDescription=_(u'Constructs a new %s instance.') % 
ProgressReporter.__name__,
    isExposedToPythonCallers=True)

AddArgumentMetadata(ProgressReporter.__init__, u'self',
    typeMetadata=ClassInstanceTypeMetadata(cls=ProgressReporter),
    description=_(u'%s instance.') % ProgressReporter.__name__)

AddArgumentMetadata(ProgressReporter.__init__, u'progressMessage1',
    typeMetadata=ProgressReporter.ProgressMessage1.__doc__.Obj.Type,
    
description=ProgressReporter.ProgressMessage1.__doc__.Obj.ShortDescription + 
u'\n\n' + ProgressReporter.ProgressMessage1.__doc__.Obj.LongDescription)

AddArgumentMetadata(ProgressReporter.__init__, u'progressMessage2',
    typeMetadata=ProgressReporter.ProgressMessage2.__doc__.Obj.Type,
    
description=ProgressReporter.ProgressMessage2.__doc__.Obj.ShortDescription + 
u'\n\n' + ProgressReporter.ProgressMessage2.__doc__.Obj.LongDescription)

AddArgumentMetadata(ProgressReporter.__init__, u'completionMessage',
    typeMetadata=ProgressReporter.CompletionMessage.__doc__.Obj.Type,
    
description=ProgressReporter.CompletionMessage.__doc__.Obj.ShortDescription + 
u'\n\n' + ProgressReporter.CompletionMessage.__doc__.Obj.LongDescription)

AddArgumentMetadata(ProgressReporter.__init__, u'abortedMessage',
    typeMetadata=ProgressReporter.AbortedMessage.__doc__.Obj.Type,
    description=ProgressReporter.AbortedMessage.__doc__.Obj.ShortDescription 
+ u'\n\n' + ProgressReporter.AbortedMessage.__doc__.Obj.LongDescription)

AddArgumentMetadata(ProgressReporter.__init__, u'loggingChannel',
    typeMetadata=ProgressReporter.LoggingChannel.__doc__.Obj.Type,
    description=ProgressReporter.LoggingChannel.__doc__.Obj.ShortDescription 
+ u'\n\n' + ProgressReporter.LoggingChannel.__doc__.Obj.LongDescription)

AddResultMetadata(ProgressReporter.__init__, u'progressReporter',
    typeMetadata=ClassInstanceTypeMetadata(cls=ProgressReporter),
    description=_(u'New %s instance.') % ProgressReporter.__name__)

# Public method: Start

AddMethodMetadata(ProgressReporter.Start,
    shortDescription=_(u'Signals the ProgressReporter that processing has 
started.'),
    isExposedToPythonCallers=True)

CopyArgumentMetadata(ProgressReporter.__init__, u'self', 
ProgressReporter.Start, u'self')

AddArgumentMetadata(ProgressReporter.Start, u'totalOperations',
    typeMetadata=IntegerTypeMetadata(canBeNone=True, minValue=1),
    description=_(u'Total number of sub-operations that will be performed, or 
None if the number of sub-operations is not known.'))

# Public method: ReportProgress

AddMethodMetadata(ProgressReporter.ReportProgress,
    shortDescription=_(u'Signals the ProgressReporter that another one or 
more sub-operations just completed.'),
    longDescription=_(
u"""This method will periodically report progress messages as
described in the class-level documentation. You must call Start before
calling this method."""),
    isExposedToPythonCallers=True)

CopyArgumentMetadata(ProgressReporter.__init__, u'self', 
ProgressReporter.ReportProgress, u'self')

AddArgumentMetadata(ProgressReporter.ReportProgress, u'operationsCompleted',
    typeMetadata=IntegerTypeMetadata(minValue=1),
    description=_(
u"""Total number of sub-operations that just completed, typically 1.
You should provide the number of sub-operations that have completed
since you last called this method. Do not pass in a cumulative total."""))

# Public method: Stop

AddMethodMetadata(ProgressReporter.Stop,
    shortDescription=_(u'Signals the ProgressReporter that processing is 
complete.'),
    longDescription=_(
u"""This method will report a completion message as described in the
class-level documentation. You must call Start before calling this
method. If provided a value for totalOperations when you called Start,
and the number of completed operations has not reached this total,
Stop will report AbortedMessage."""),
    isExposedToPythonCallers=True)

CopyArgumentMetadata(ProgressReporter.__init__, u'self', 
ProgressReporter.Stop, u'self')


###############################################################################
# Names exported by this module
###############################################################################

__all__ = ['Logger']
# Dependencies.py - Provides classes that allow other classes in the GeoEco
# Python package to declare dependencies on software and hardware.
#
# Copyright (C) 2007 Jason J. Roberts
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License (available in the file LICENSE.TXT)
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
USA.

import codecs
import os
import platform
import types
import StringIO
import sys

from GeoEco.DynamicDocString import DynamicDocString
from GeoEco.Exceptions import GeoEcoError
from GeoEco.Internationalization import _


# Exceptions raised when a software or hardware dependency is checked and 
fails.


class UnsupportedPlatformError(GeoEcoError):
    __doc__ = DynamicDocString()


class SoftwareNotInstalledError(GeoEcoError):
    __doc__ = DynamicDocString()


# Classes that represent various types of dependencies.


class Dependency(object):
    __doc__ = DynamicDocString()

    def GetResultCacheKey(self):
        return None

    def Initialize(self):
        raise NotImplementedError(u'Derived classes must override this 
method.')


class WindowsDependency(Dependency):
    __doc__ = DynamicDocString()

    def __init__(self, minimumMajorVersion, minimumMinorVersion=None, 
minimumServicePack=None):
        self.SetVersion(minimumMajorVersion, minimumMinorVersion, 
minimumServicePack)

    def SetVersion(self, minimumMajorVersion, minimumMinorVersion=None, 
minimumServicePack=None):
        assert isinstance(minimumMajorVersion, types.IntType), 
u'minimumMajorVersion must be an integer.'
        assert isinstance(minimumMinorVersion, (types.IntType, 
types.NoneType)), 'minimumMinorVersion must be an integer, or None.'
        assert isinstance(minimumServicePack, (types.IntType, 
types.NoneType)), u'minimumServicePack must be an integer, or None.'
        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.'
        assert minimumMinorVersion is None or minimumMinorVersion >= 0, u'If 
minimumMinorVersion is not None it must be >= 0.'
        assert minimumServicePack is None or minimumServicePack >= 0, u'If 
minimumServicePack is not None it must be >= 0.'
        assert minimumMajorVersion >= 5, u'GeoEco itself cannot run Windows 
versions prior to Windows 2000, even if your code will. Please adjust the 
minimumMajorVersion to 5 or greater.'

        if minimumMinorVersion is None:
            minimumMinorVersion = 0
        if minimumServicePack is None:
            minimumServicePack = 0

        self._MinimumMajorVersion = minimumMajorVersion
        self._MinimumMinorVersion = minimumMinorVersion
        self._MinimumServicePack = minimumServicePack

    def _GetMinimumMajorVersion(self):
        return self._MinimumMajorVersion
    
    MinimumMajorVersion = property(_GetMinimumMajorVersion, 
doc=DynamicDocString())

    def _GetMinimumMinorVersion(self):
        return self._MinimumMinorVersion
    
    MinimumMinorVersion = property(_GetMinimumMinorVersion, 
doc=DynamicDocString())

    def _GetMinimumServicePack(self):
        return self._MinimumServicePack
    
    MinimumServicePack = property(_GetMinimumServicePack, 
doc=DynamicDocString())

    def GetResultCacheKey(self):
        return u'WindowsDependency:' + unicode(self._MinimumMajorVersion) + 
u',' + unicode(self._MinimumMinorVersion) + u',' + 
unicode(self._MinimumServicePack)

    def Initialize(self):
        from GeoEco.Logging import Logger
        Logger.Debug(_(u'Checking platform dependency: %s or later.') % 
self.GetProductNameFromVersionNumbers(self.MinimumMajorVersion, 
self.MinimumMinorVersion, self.MinimumServicePack))
        if sys.platform.lower() == u'win32':
            (major, minor, servicePack) = self.GetInstalledVersion()
            if self.MinimumMajorVersion > major or self.MinimumMajorVersion 
== major and self.MinimumMinorVersion > minor or self.MinimumMajorVersion == 
major and self.MinimumMinorVersion == minor and self.MinimumServicePack > 
servicePack:
                Logger.RaiseException(UnsupportedPlatformError(_(u'This 
function can only execute on a computer running %(required)s or a later 
version. Python reports that %(installed)s is installed. Please upgrade the 
operating system.') % {u'required' : 
self.GetProductNameFromVersionNumbers(self.MinimumMajorVersion, 
self.MinimumMinorVersion, self.MinimumServicePack), u'installed' : 
self.GetProductNameFromVersionNumbers(major, minor, servicePack)}))
        else:
            Logger.RaiseException(UnsupportedPlatformError(_(u'This function 
can only execute on a computer running the Microsoft Windows operating 
system. Python reports that this computer is running the \"%s\" operating 
system.') % platform.system()))

    _MajorVersion = None
    _MinorVersion = None
    _ServicePack = None

    @classmethod
    def GetInstalledVersion(cls):
        if WindowsDependency._MajorVersion is not None:
            return (WindowsDependency._MajorVersion, 
WindowsDependency._MinorVersion, WindowsDependency._ServicePack)

        (WindowsDependency._MajorVersion, WindowsDependency._MinorVersion, 
build, plat, text) = sys.getwindowsversion()
        if text is None:
            WindowsDependency._ServicePack = 0
        else:
            try:
                WindowsDependency._ServicePack = 
int(text.strip('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz 
\t\r\n.'))
            except:
                WindowsDependency._ServicePack = 0

        from GeoEco.Logging import Logger
        Logger.Debug(_(u'%s is installed.') % 
cls.GetProductNameFromVersionNumbers(WindowsDependency._MajorVersion, 
WindowsDependency._MinorVersion, WindowsDependency._ServicePack))

        return (WindowsDependency._MajorVersion, 
WindowsDependency._MinorVersion, WindowsDependency._ServicePack)

    @classmethod
    def GetProductNameFromVersionNumbers(cls, majorVersion, minorVersion, 
servicePack):
        osName = None
        if majorVersion == 5:
            if minorVersion == 0:
                osName = _(u'Microsoft Windows 2000')
            elif minorVersion == 1:
                osName = _(u'Microsoft Windows XP')
            else:
                osName = _(u'Microsoft Windows Server 2003')
        elif majorVersion == 6:
            if minorVersion == 0:
                osName = _(u'Microsoft Windows Vista')
            elif minorVersion == 1:
                osName = _(u'Microsoft Windows "Blackcomb" Server')
        if osName is None:
            osName = _(u'Microsoft Windows version %i.%i') % (majorVersion, 
minorVersion)
        if servicePack > 0:
            osName = _(u'%s Service Pack %i') % (osName, servicePack)
        return osName

            
class PythonDependency(Dependency):
    __doc__ = DynamicDocString()

    def __init__(self, minimumMajorVersion, minimumMinorVersion=None, 
minimumPatchVersion=None):
        self.SetVersion(minimumMajorVersion, minimumMinorVersion, 
minimumPatchVersion)

    def SetVersion(self, minimumMajorVersion, minimumMinorVersion=None, 
minimumPatchVersion=None):
        assert isinstance(minimumMajorVersion, types.IntType), 
u'minimumMajorVersion must be an integer.'
        assert isinstance(minimumMinorVersion, (types.IntType, 
types.NoneType)), 'minimumMinorVersion must be an integer, or None.'
        assert isinstance(minimumPatchVersion, (types.IntType, 
types.NoneType)), u'minimumPatchVersion must be an integer, or None.'
        assert minimumMajorVersion is not None and minimumMinorVersion is 
None and minimumPatchVersion is None or minimumMajorVersion is not None and 
minimumMinorVersion is not None and minimumPatchVersion is None or 
minimumMajorVersion is not None and minimumMinorVersion is not None and 
minimumPatchVersion is not None, u'If you specify a minimumMinorVersion you 
must also specify a minimumMajorVersion. If you specify a minimumPatchVersion 
you must also specify a minimumMajorVersion and minimumMinorVersion.'
        assert minimumMinorVersion is None or minimumMinorVersion >= 0, u'If 
minimumMinorVersion is not None it must be >= 0.'
        assert minimumPatchVersion is None or minimumPatchVersion >= 0, u'If 
minimumPatchVersion is not None it must be >= 0.'
        assert minimumMajorVersion >= 3 or minimumMajorVersion >= 2 and 
minimumMinorVersion is not None and minimumMinorVersion >= 4, u'GeoEco itself 
cannot run on Python versions prior to 2.4, even if your code will. Please 
adjust minimumMajorVersion.minimumMajorVersion to 2.4 or greater.'

        if minimumMinorVersion is None:
            minimumMinorVersion = 0
        if minimumPatchVersion is None:
            minimumPatchVersion = 0

        self._MinimumMajorVersion = minimumMajorVersion
        self._MinimumMinorVersion = minimumMinorVersion
        self._MinimumPatchVersion = minimumPatchVersion

    def _GetMinimumMajorVersion(self):
        return self._MinimumMajorVersion
    
    MinimumMajorVersion = property(_GetMinimumMajorVersion, 
doc=DynamicDocString())

    def _GetMinimumMinorVersion(self):
        return self._MinimumMinorVersion
    
    MinimumMinorVersion = property(_GetMinimumMinorVersion, 
doc=DynamicDocString())

    def _GetMinimumPatchVersion(self):
        return self._MinimumPatchVersion
    
    MinimumPatchVersion = property(_GetMinimumPatchVersion, 
doc=DynamicDocString())

    def GetResultCacheKey(self):
        return u'PythonDependency:' + unicode(self._MinimumMajorVersion) + 
u',' + unicode(self._MinimumMinorVersion) + u',' + 
unicode(self._MinimumPatchVersion)

    def Initialize(self):
        from GeoEco.Logging import Logger
        Logger.Debug(_(u'Checking software dependency: Python version 
%i.%i.%i or later.') % (self.MinimumMajorVersion, self.MinimumMinorVersion, 
self.MinimumPatchVersion))
        (major, minor, patch) = self.GetInstalledVersion()
        if self.MinimumMajorVersion > major or self.MinimumMajorVersion == 
major and self.MinimumMinorVersion > minor or self.MinimumMajorVersion == 
major and self.MinimumMinorVersion == minor and self.MinimumPatchVersion > 
patch:
            Logger.RaiseException(SoftwareNotInstalledError(_(u'This function 
requires Python %i.%i.%i or a later version, but version %i.%i.%i is 
currently running. Please ensure the required version (or newer) is 
installed. You may download Python from http://www.python.org/. Also ensure ;
that the GeoEco package for that version of Python is installed (if you 
reinstall Python, you have to reinstall GeoEco). Finally, ensure the 
operating system is configured to invoke the required version of Python when 
it interprets Python scripts, rather than an older version of Python. On 
Windows, you must configure a "file association" that associates the newer 
version of Python with .py files.') % (self.MinimumMajorVersion, 
self.MinimumMinorVersion, self.MinimumPatchVersion, major, minor, patch)))

    _MajorVersion = None
    _MinorVersion = None
    _PatchVersion = None

    @classmethod
    def GetInstalledVersion(cls):
        if PythonDependency._MajorVersion is not None:
            return (PythonDependency._MajorVersion, 
PythonDependency._MinorVersion, PythonDependency._PatchVersion)

        v = platform.python_version_tuple()
        PythonDependency._MajorVersion = cls._ParseVersionNumber(v[0])
        PythonDependency._MinorVersion = cls._ParseVersionNumber(v[1])
        PythonDependency._PatchVersion = cls._ParseVersionNumber(v[2])

        from GeoEco.Logging import Logger
        Logger.Debug(_(u'Python %i.%i.%i is running.') % 
(PythonDependency._MajorVersion, PythonDependency._MinorVersion, 
PythonDependency._PatchVersion))

        return (PythonDependency._MajorVersion, 
PythonDependency._MinorVersion, PythonDependency._PatchVersion)

    @classmethod
    def _ParseVersionNumber(cls, s):
        assert isinstance(s, (types.IntType, basestring))
        if isinstance(s, types.IntType):
            return s
        s = s[:len(s) - len(s.lstrip('0123456789'))]
        if len(s) <= 0:
            return 0
        return int(s)


class PythonModuleDependency(Dependency):
    __doc__ = DynamicDocString()

    def __init__(self, importName, displayName=None, cheeseShopName=None, 
alternateURL=None, additionalMessage=None, logStdout=False):
        assert isinstance(importName, types.StringType), u'importName must be 
a non-Unicode string'
        assert isinstance(displayName, (types.NoneType, types.UnicodeType)), 
u'displayName must be a Unicode string, or None'
        assert isinstance(cheeseShopName, (types.NoneType, 
types.UnicodeType)), u'displayName must be a Unicode string, or None'
        assert isinstance(alternateURL, (types.NoneType, types.UnicodeType)), 
u'displayName must be a Unicode string, or None'
        assert isinstance(additionalMessage, (types.NoneType, 
types.UnicodeType)), u'additionalMessage must be a Unicode string, or None'
        assert isinstance(logStdout, types.BooleanType), u'logStdout must be 
a boolean'

        self.ImportName = importName
        self.DisplayName = displayName
        self.CheeseShopName = cheeseShopName
        self.AlternateURL = alternateURL
        self.AdditionalMessage = additionalMessage
        self.LogStdout = logStdout

    _InstalledModules = {}        

    def GetResultCacheKey(self):
        return u'PythonModuleDependency:' + self.ImportName

    def Initialize(self):
        from GeoEco.Logging import Logger
        Logger.Debug(_(u'Checking software dependency: Python module: %s') % 
self.ImportName)

        # If we already know that it is installed, return immediately.
        # (Question: is this necessary now that 

        if PythonModuleDependency._InstalledModules.has_key(self.ImportName):
            return

        # If requested, start capturing stdout so we can log any messages that
        # are printed when we try to import the module. At the time this code
        # was written, I only knew of one module (rpy) that printed messages 
to
        # stdout during module importation.

        if self.LogStdout:
            oldStdout = sys.stdout
            sys.stdout = StringIO.StringIO()

        # Import the module and, if requested, log any messages that are 
printed
        # to stdout.
        
        try:
            try:
                __import__(self.ImportName)
            finally:
                if self.LogStdout:
                    messages = []
                    try:
                        messages = sys.stdout.getvalue().split('\n')
                    except:
                        pass
                    sys.stdout = oldStdout
                    try:
                        for message in messages:
                            if len(message.strip()) > 0:
                                Logger.Debug(_(u'Python %(module)s module: 
%(message)s') % {u'module' : self.ImportName, u'message' : message.strip()})
                    except:
                        pass
        except Exception, e:
            if self.DisplayName is None:
                message = _(u'This function requires the Python %s module. 
Please verify that it is properly installed for the running version of Python 
(%s.%s). If it is Python-version-specific, ensure you installed the version 
of it for this Python version.') % (self.ImportName, 
unicode(platform.python_version_tuple()[0]), 
unicode(platform.python_version_tuple()[1]))
            else:
                message = _(u'This function requires the %s. Please verify 
that it is properly installed for the running version of Python (%s.%s). If 
it is Python-version-specific, ensure you installed the version of it for 
this Python version.') % (self.DisplayName, 
unicode(platform.python_version_tuple()[0]), 
unicode(platform.python_version_tuple()[1]))
            if self.CheeseShopName is not None and self.AlternateURL is None:
                message = _(u'%s It may be available at 
http://www.python.org/pypi/%s.') % (message, self.CheeseShopName)
            elif self.CheeseShopName is None and self.AlternateURL is not 
None:
                message = _(u'%s It may be available at %s.') % (message, 
self.AlternateURL)
            elif self.CheeseShopName is not None and self.AlternateURL is not 
None:
                message = _(u'%s It may be available at 
http://www.python.org/pypi/%s or %s.') % (message, self.CheeseShopName, 
self.AlternateURL)
            if self.AdditionalMessage is not None:
                message = _(u'%s %s.') % (message, self.AdditionalMessage)
            message = _(u'%s Debugging information: the Python statement 
"__import__(\'%s\')" raised %s: %s"') % (message, self.ImportName, 
e.__class__.__name__, unicode(e))
            Logger.RaiseException(SoftwareNotInstalledError(message))

        Logger.Debug(_(u'Imported Python module %s successfully.') % 
self.ImportName)
        PythonModuleDependency._InstalledModules[self.ImportName] = True      
      


class PythonAggregatedModuleDependency(Dependency):
    __doc__ = DynamicDocString()

    def __init__(self, importName):
        assert isinstance(importName, types.StringType), u'importName must be 
a non-Unicode string'
        self.ImportName = importName

    def GetResultCacheKey(self):
        return u'PythonAggregatedModuleDependency:' + self.ImportName

    def Initialize(self):
        from GeoEco.Logging import Logger
        Logger.Debug(_(u'Checking software dependency: aggregated Python 
module: %s') % self.ImportName)

        # If the module has already been imported, return immediately.

        if sys.modules.has_key(self.ImportName):
            return

        # Import the module.

        import GeoEco
        aggregatedModulesDir = 
os.path.join(os.path.dirname(sys.modules[u'GeoEco'].__file__), 
'AggregatedModules')

        try:
            try:
                __import__(self.ImportName)
            except Exception, e:
                if aggregatedModulesDir in sys.path:
                    raise
                Logger.Debug(_(u'Failed to import Python module %s using the 
unaltered sys.path. The Python statement "__import__(\'%s\')" raised %s: 
%s"') % (self.ImportName, self.ImportName, e.__class__.__name__, unicode(e)))
            else:
                Logger.Debug(_(u'Imported Python module %s from directory 
%s.') % (self.ImportName, 
os.path.dirname(sys.modules[self.ImportName].__file__)))
                return
            sys.path.append(aggregatedModulesDir)
            Logger.Debug(_(u'Appended %s to sys.path.') % 
aggregatedModulesDir)
            __import__(self.ImportName)
        except Exception, e:
            Logger.RaiseException(SoftwareNotInstalledError(_(u'This function 
requires the Python %s module for the running version of Python (%s.%s). 
GeoEco includes a copy of this module but was unable to import it. This 
should never happen. Please contact the GeoEco development team for 
assistance. Debugging information: the Python statement "__import__(\'%s\')" 
raised %s: %s"') % (self.ImportName, 
unicode(platform.python_version_tuple()[0]), 
unicode(platform.python_version_tuple()[1]), self.ImportName, 
e.__class__.__name__, unicode(e))))
        Logger.Debug(_(u'Imported Python module %s from directory %s.') % 
(self.ImportName, os.path.dirname(sys.modules[self.ImportName].__file__)))


# Helper functions for dealing with certain kinds of dependencies.


_GDALModuleHandle = None

def ImportGDALModule(name):

    # If the specified GDAL module has already been loaded, just
    # return it.

    from GeoEco.Logging import Logger

    if not name.startswith('GeoEco.AssimilatedModules.'):
        name = 'GeoEco.AssimilatedModules.' + name
    if sys.modules.has_key(name):
        return sys.modules[name]

    Logger.Debug(_(u'The module %s has not been loaded yet.') % name)

    # Determine the paths to our private copies of the GDAL data and
    # bin directories.

    import GeoEco

    geoEcoBinDir = 
os.path.join(os.path.dirname(sys.modules[u'GeoEco'].__file__), 'Bin', 
sys.platform.lower())
    gdalDataDir = os.path.join(geoEcoBinDir, 'GDAL', 'data')
    gdalBinDir = os.path.join(geoEcoBinDir, 'GDAL', 'bin')

    # Set the GDAL_DATA environment variable.

    SetGDALDataEnvVar()

    # Import the module.
    #
    # For this to succeed, the module has to be able to load the GDAL
    # library (e.g. gdal16.dll on Windows). This usual technique for
    # this involves adding the GDAL bin directory to the PATH
    # environment variable prior to importing the module. But this
    # seems to interfere with ArcGIS in some scenarios, at least when
    # running tests using nosetests. Specifically, I discovered that
    # when I set the PATH variable, subsequent calls to
    # gp.CreatePersonalGDB_managment would crash the Python process.
    # It did not matter what I set PATH to--I could even set it to its
    # current value--that gp call would always crash when running
    # under nosetests. To work around this, on Windows only,
    # explicitly load the GDAL library rather than modifying PATH.

    try:
        if sys.platform == 'win32':

            # Load the GDAL DLL, if we have not done it already.

            if globals()['_GDALModuleHandle'] is None:

                # First call Win32 SetDllDirectory so that when we call
                # LoadLibrary, it can find the DLLs that the GDAL DLL
                # links to.
                
                d = PythonModuleDependency(importName='win32api', 
displayName=_(u'Python for Windows extensions (pywin32)'), 
alternateURL=u'http://sourceforge.net/projects/pywin32')
                d.Initialize()
                import win32api
                if not hasattr(win32api, 'GetDllDirectory'):
                    Logger.RaiseException(SoftwareNotInstalledError(_(u'This 
function requires the Python for Windows extensions (pywin32) build 211 or 
later. An earlier build is installed. Please uninstall your current version 
using Add/Remove Programs in the Control Panel (or Programs and Features, if 
you\'re running Windows Vista). Then download and install build 211 or later 
from http://sourceforge.net/projects/pywin32')))
                oldDllDirectory = None
                try:
                    oldDllDirectory = win32api.GetDllDirectory()        # If 
no DLL directory has been set, this will throw an exception, at least with 
pywin32 build 212.
                except:
                    pass
                win32api.SetDllDirectory(gdalBinDir)
                if oldDllDirectory is not None:
                    Logger.Debug(_(u'Set the Win32 DLL directory to 
"%(new)s". (The old value was "%(old)s".)') % {u'new': gdalBinDir, u'old': 
oldDllDirectory})
                else:
                    Logger.Debug(_(u'Set the Win32 DLL directory to 
"%(new)s". (The old value was NULL.)') % {u'new': gdalBinDir})

                # Now call LoadLibrary.

                try:
                    gdalDLLPath = os.path.join(gdalBinDir, 'gdal16.dll')
                    try:
                        globals()['_GDALModuleHandle'] = 
win32api.LoadLibrary(gdalDLLPath)
                    except Exception, e:
                        Logger.RaiseException(ImportError(_(u'The %(name)s 
Python module could not be imported because 
win32api.LoadLibrary(r\'%(path)s\') raised %(e)s: %(msg)s.') % {u'name': 
name, u'path': gdalDLLPath, u'e': e.__class__.__name__, u'msg': unicode(e)}))
                    Logger.Debug(_(u'Explicitly loaded "%s" by calling Win32 
LoadLibrary.') % gdalDLLPath)

                # Reset the DLL directory to what it was before.

                finally:
                    try:
                        win32api.SetDllDirectory(oldDllDirectory)
                    except Exception, e:
                        Logger.Warning(_(u'Failed to reset the Win32 DLL 
directory to the previous value. win32api.SetDllDirectory(r\'%(path)s\') 
raised %(e)s: %(msg)s.') % {u'path': oldDllDirectory, u'e': 
e.__class__.__name__, u'msg': unicode(e)})
                    if oldDllDirectory is not None:
                        Logger.Debug(_(u'Reset the Win32 DLL directory to 
"%(old)s".') % {u'old': oldDllDirectory})
                    else:
                        Logger.Debug(_(u'Reset the Win32 DLL directory to 
NULL.'))
                    
            # Load the module.

            __import__(name)
            Logger.Debug(_(u'Imported the %s module.') % name)

        else:
            if os.environ['PATH'].find(gdalBinDir) < 0:
                oldPath = os.environ['PATH']
                os.environ['PATH'] = gdalBinDir + ';' + os.environ['PATH']
                Logger.Debug(_(u'Prepended the directory "%s" to the PATH 
environment variable.') % gdalBinDir)
                __import__(name)
                Logger.Debug(_(u'Imported the %s module.') % name)
                os.environ['PATH'] = oldPath
                Logger.Debug(_(u'Removed the directory "%s" from the PATH 
environment variable.') % gdalBinDir)

    # Reset the GDAL_DATA environment variable.

    finally:
        try:
            ResetGDALDataEnvVar()
        except:
            Logger.LogExceptionAsWarning()

    # Return the loaded module.

    return sys.modules[name]

_GDALDataEnvVarSetCount = 0
_OldGDALDataEnvVar = None

def SetGDALDataEnvVar():
    if globals()['_GDALDataEnvVarSetCount'] > 0:
        globals()['_GDALDataEnvVarSetCount'] += 1
    else:
        import GeoEco
        gdalDataDir = 
os.path.join(os.path.dirname(sys.modules[u'GeoEco'].__file__), 'Bin', 
sys.platform.lower(), 'GDAL', 'data')
        
        globals()['_OldGDALDataEnvVar'] = None
        if os.environ.has_key('GDAL_DATA'):
            globals()['_OldGDALDataEnvVar'] = os.environ['GDAL_DATA']
        os.environ['GDAL_DATA'] = gdalDataDir
        globals()['_GDALDataEnvVarSetCount'] = 1
        
        from GeoEco.Logging import Logger
        if globals()['_OldGDALDataEnvVar'] is not None:
            Logger.Debug(_(u'Set the GDAL_DATA environment variable to 
"%(new)s". (The old value was "%(old)s".)') % {u'new': gdalDataDir, u'old': 
globals()['_OldGDALDataEnvVar']})
        else:
            Logger.Debug(_(u'Set the GDAL_DATA environment variable to 
"%(new)s". (The variable was not previously set.)') % {u'new': gdalDataDir})

def ResetGDALDataEnvVar():
    from GeoEco.Logging import Logger
    if globals()['_GDALDataEnvVarSetCount'] == 0:
        Logger.RaiseException(RuntimeError(_(u'Programming error in this 
tool. ResetGDALDataEnvVar called when _GDALDataEnvVarSetCount is 0. Please 
contact the author of this tool for assistance.')))
    globals()['_GDALDataEnvVarSetCount'] -= 1
    if globals()['_GDALDataEnvVarSetCount'] == 0:
        if globals()['_OldGDALDataEnvVar'] is not None:
            os.environ['GDAL_DATA'] = globals()['_OldGDALDataEnvVar']
            Logger.Debug(_(u'Reset the GDAL_DATA environment variable to 
"%(old)s".') % {u'old': globals()['_OldGDALDataEnvVar']})
        else:
            del os.environ['GDAL_DATA']
            Logger.Debug(_(u'Unset the GDAL_DATA environment variable.'))


###############################################################################
# Names exported by this module
###############################################################################

__all__ = ['UnsupportedPlatformError',
           'SoftwareNotInstalledError',
           'Dependency',
           'WindowsDependency',
           'PythonDependency',
           'PythonModuleDependency',
           'PythonAggregatedModuleDependency',
           'ImportGDALModule',
           'SetGDALDataEnvVar',
           'ResetGDALDataEnvVar']
Archives powered by MHonArc.
Top of Page