Guest User

Untitled

a guest
Jul 17th, 2017
590
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 41.19 KB | None | 0 0
  1. '''
  2.    scalalib.py - (C) 2005-2015 Scala, Guillaume Proux, Mike Miller
  3.    A module to simplify using Scala from python, including a few extra
  4.    helpful functions.  Send questions regarding this module to mike dot miller
  5.    at scala dot com.
  6.  
  7.    Place in the lib/site-packages folder of your Scala for Python installation.
  8.  
  9.    The dictionary, persistent_data, may be used to carry information from
  10.    script to script running under the same interpreter.
  11. '''
  12. if True:            # initialize, enable folding
  13.     import sys, os, time
  14.     import logging, logging.handlers
  15.     try:                    from win32com.client import Dispatch as COMObject
  16.     except ImportError:     COMObject = None
  17.     if os.name == 'nt':
  18.         try:
  19.            import colorama
  20.         except ImportError:
  21.            colorama = None
  22.  
  23.     __version__ = '1.33'
  24.     _def_logport = 8400
  25.     persistent_data = {}    # A place to keep information across scripts
  26.     # logging setup
  27.     _log = logging.getLogger(__name__)
  28.     logging.NOTE = 32       # level, positive, yet important
  29.     logging.FATAL = 52      # shorter, more accurate synonym
  30.     logging.addLevelName(logging.NOTE, 'NOTE')
  31.     logging.addLevelName(logging.CRITICAL, 'CRITICL')   # shorten
  32.     logging.addLevelName(logging.FATAL, 'FATAL')
  33.  
  34.     # Scala COM handles, initialized on demand
  35.     _player  = None;    _player_name    = 'ScalaPlayer.ScalaPlayer.1'
  36.     _runic   = None;    _runic_name     = 'Scala.InfoPlayer5'
  37.     _netic   = None;    _netic_name     = 'Scala.InfoNetwork'
  38.     _publshr = None;    _publshr_name   = 'Scala.Publisher'
  39.     _thumbnl = None;    _thumbnl_name   = 'Scala.Thumbnailer'
  40.     _filelock_name  = 'ScalaFileLock.ScalaFileLock.1'
  41.     _netic_svc      = 'ScalaNetClient5'
  42.     _pub_svc        = 'ScalaPublish5'
  43.     _thumb_svc      = 'ScalaSupport5'
  44.     sleep_was_successful = False
  45.  
  46.  
  47. # Classes
  48. # ---------------------------------------------------------------------
  49. class sharedvars:   # from scala5
  50.     '''
  51.        A class to wrap Scala shared variables (WSH Objects), so using .Value
  52.        isn't necessary to set/access their values.  Shared variables can be
  53.        accessed as attributes of the resulting object.
  54.  
  55.        Argument:
  56.            defaults                - A dictionary of default values to return.
  57.        Additional behavior:
  58.            - If attempting to read a variable that doesn't exist, or wasn't
  59.                shared, this returns a default (if given), otherwise None.
  60.            - Will silently drop assignments to variables that weren't shared.
  61.            - The Scala vars continue to be available in the main namespace.
  62.            - This version fixes the bug regarding one-element arrays not
  63.                returning to the Scala Script.
  64.        Notes:
  65.            Use of this object is optional and not required to access Scala's
  66.            shared variables.   However, its use makes it easier to write
  67.            scripts that work at the command prompt as well, since scripts
  68.            won't crash on variable access.
  69.  
  70.        Example:
  71.            import scalalib
  72.            svars = scalalib.sharedvars()  # a defaults dict can be passed here.
  73.            f = file(svars.filename)       # get value
  74.            svars.result = 15              # set value
  75.    '''
  76.     def __init__(self, defaults={}):
  77.         self.__dict__['main'] = sys._getframe(1).f_globals
  78.         self.__dict__['defaults'] = defaults
  79.  
  80.     def __getattr__(self, name):
  81.         try:
  82.             result = self.__dict__['main'][name].Value
  83.             if type(result) is tuple:
  84.                 result = list(result)
  85.             return result
  86.         except KeyError:
  87.             return self.defaults.get(name, None)
  88.  
  89.     def __setattr__(self, name, newval):
  90.         try:
  91.             svar = self.__dict__['main'][name]
  92.             if type(newval) is list:
  93.                 if len(newval) == 1:    # to ensure new value sticks at len = 1
  94.                     default = { str:'', int:0, float:0.0, bool:False
  95.                         }[type(newval[0])]
  96.                     newval = tuple(newval) + (default,)
  97.                 else:
  98.                     newval = tuple(newval)
  99.             svar.Value = newval
  100.         except KeyError: pass           # drop assignments that don't apply
  101.  
  102.  
  103. class _ObjectNotAvailable(Exception):
  104.     '''Thrown when a Scala COM Object is unable to be instantiated.'''
  105.     pass
  106.  
  107.  
  108. class _ScalaLogHandler(logging.Handler):
  109.     '''A class implementing the Scala logging system as a python logging
  110.    handler.  Log messages are written to the Scala log.'''
  111.     def emit(self, record):
  112.         try:
  113.             msg = self.format(record)
  114.             _log_scala(msg)
  115.         except Exception:
  116.             self.handleError(record)
  117.  
  118.  
  119. class _ScalaVarHandler(logging.Handler):
  120.     '''A class implementing a Scala variable as a python logging handler.
  121.    Log messages are written to the named variable, and are available for
  122.    viewing in Scala Player or Designer.'''
  123.     def __init__(self, debug_var_name, level=logging.NOTSET):
  124.         logging.Handler.__init__(self, level=level)
  125.         self.dvn = debug_var_name
  126.         self.frame = 0
  127.  
  128.     def emit(self, record):
  129.         try:
  130.             msg = self.format(record)
  131.             # The frame (or stack) number can change depending on which function
  132.             # we are calling from.  The first time, we look for the var in frame
  133.             # 0.  If we fail, we search for it, and if found, continue to try
  134.             # that one every time until we fail again.  Only searches if var is
  135.             # not found, saving work when the fr# doesn't change (most likely).
  136.             try: sys._getframe(self.frame).f_globals[self.dvn].Value = msg
  137.             except KeyError:
  138.                 for i in range(10):     # Try to find the frame if we've lost it
  139.                     try:
  140.                         sys._getframe(i).f_globals[self.dvn]
  141.                         self.frame = i; break
  142.                     except KeyError: continue   # not in this frame
  143.                     except ValueError: break    # too far
  144.                     break                       # success
  145.                 # try again
  146.                 try: sys._getframe(self.frame).f_globals[self.dvn].Value = msg
  147.                 except KeyError: pass  # couldn't find it, skip this event
  148.         except Exception:
  149.             self.handleError(record)
  150.  
  151.  
  152. class _Win32ColorConHandler(logging.Handler):
  153.     '''A class that adds color to the standard console StreamHandler.
  154.       Deprecated as of Scala's distribution of Python 2.7.
  155.    '''
  156.     def __init__(self, level=logging.NOTSET):
  157.         logging.Handler.__init__(self, level)
  158.         import WConio
  159.         self.WConio = WConio
  160.         self.colormap = {
  161.             'DEBUG': WConio.BLUE, 'INFO':WConio.GREEN, 'NOTE': WConio.CYAN,
  162.             'WARNING':WConio.YELLOW, 'ERROR':WConio.LIGHTRED,
  163.             'CRITICL':WConio.WHITE, 'FATAL':WConio.WHITE,
  164.             'NOTSET':WConio.LIGHTGREY
  165.         }
  166.  
  167.     def emit(self, record):
  168.         WConio = self.WConio
  169.         try:
  170.             msg = self.format(record)
  171.             levelname = record.levelname
  172.             out = sys.__stdout__  # in case redirected by filelogger, etc
  173.             if levelname in msg:
  174.                 part1, part2 = msg.split(levelname, 1)
  175.                 out.write(part1)
  176.                 out.flush()
  177.                 saved_color = WConio.gettextinfo()[4]
  178.                 WConio.textattr( self.colormap.get(levelname,
  179.                     WConio.LIGHTGREY) )
  180.                 if levelname in ('CRITICL', 'FATAL'):
  181.                     WConio.textbackground(WConio.RED)
  182.                 WConio.cputs(levelname)
  183.                 WConio.textattr(saved_color)  # restore
  184.                 print >> out, part2
  185.             else:
  186.                 print >> out, msg
  187.             out.flush()
  188.         except Exception:
  189.             self.handleError(record)
  190.  
  191.  
  192. class _AnsiConHandler(logging.Handler):
  193.     '''A class that adds color to the standard console StreamHandler.'''
  194.     def __init__(self, level=logging.NOTSET):
  195.         logging.Handler.__init__(self, level)
  196.         self.colormap = {
  197.             'DEBUG':    '\x1b[34m',         # blue
  198.             'INFO':     '\x1b[32m',         # green
  199.             'NOTE':     '\x1b[36;1m',       # cyan bold
  200.             'WARNING':  '\x1b[33;1m',       # yellow bold
  201.             'ERROR':    '\x1b[31;1m',       # red bold
  202.             'CRITICL':  '\x1b[41;37;1m',    # white bold on red
  203.             'FATAL':    '\x1b[41;37;1m',    # white bold on red
  204.             'NOTSET':   '\x1b[0m',          # normal
  205.         }
  206.  
  207.     def emit(self, record):
  208.         try:
  209.             levelname = record.levelname
  210.             colormap = self.colormap
  211.             out = sys.__stdout__  # in case redirected by filelogger, etc
  212.             spaces = (7 - len(levelname)) * ' ' # overlaps with %s formatting
  213.             record.levelname = (colormap.get(levelname, '') + levelname +
  214.                                 colormap['NOTSET'] + spaces)
  215.             print >> out, self.format(record)
  216.             record.levelname = levelname  # fix for handlers coming later
  217.         except Exception:
  218.             self.handleError(record)
  219.  
  220.  
  221. class _CursesConHandler(logging.Handler):
  222.     'A class for Unix that adds color to the standard console StreamHandler.'
  223.     def __init__(self, level=logging.NOTSET):
  224.         logging.Handler.__init__(self, level)
  225.         import curses
  226.         curses.setupterm()
  227.         if curses.tigetnum('colors') < 8:
  228.             raise EnvironmentError, 'Not enough colors available.'
  229.         self.curses = curses
  230.         setf = curses.tigetstr('setaf')
  231.         setbg = curses.tigetstr('setab')
  232.         bold = curses.tparm(curses.tigetstr('bold'), curses.A_BOLD)
  233.         self.colormap = {
  234.             'DEBUG':    curses.tparm(setf, curses.COLOR_BLUE),
  235.             'INFO':     curses.tparm(setf, curses.COLOR_GREEN),
  236.             'WARNING':  curses.tparm(setf, curses.COLOR_YELLOW) + bold,
  237.             'NOTE':     curses.tparm(setf, curses.COLOR_CYAN) + bold,
  238.             'ERROR':    curses.tparm(setf, curses.COLOR_RED) + bold,
  239.             'CRITICL':  curses.tparm(setbg, curses.COLOR_RED) + bold +
  240.                         curses.tparm(setf, curses.COLOR_WHITE),
  241.             'FATAL':    curses.tparm(setbg, curses.COLOR_RED) + bold +
  242.                         curses.tparm(setf, curses.COLOR_WHITE),
  243.             'NOTSET':   curses.tigetstr('sgr0')
  244.             }
  245.  
  246.     def emit(self, record):
  247.         try:
  248.             levelname = record.levelname
  249.             colormap = self.colormap
  250.             out = sys.__stdout__  # in case redirected by filelogger, etc
  251.             spaces = (7 - len(levelname)) * ' ' # overlaps with %s formatting
  252.             record.levelname = (colormap.get(levelname, '') + levelname +
  253.                                 colormap['NOTSET'] + spaces)
  254.             print >> out, self.format(record)
  255.             record.levelname = levelname  # fix for handlers coming later
  256.         except Exception:
  257.             self.handleError(record)
  258.  
  259.  
  260. class _PlainTextSocketHandler(logging.handlers.SocketHandler):
  261.     'Overrides the standard SocketHandler to send text instead of pickles.'
  262.     def emit(self, record):
  263.         try:
  264.             msg = self.format(record)
  265.             if not self.sock:
  266.                 self.sock = self.makeSocket()
  267.             self.send('%s\n' % msg)
  268.         except Exception:
  269.             self.handleError(record)
  270.  
  271. # Create a null handler to avoid "no handler" error messages
  272. class _NullHandler(logging.Handler):
  273.     def emit(self, record):
  274.         pass
  275. if _log.handlers:                   # secondary inits, handle module reload
  276.     if isinstance(_log.handlers[0], _NullHandler):
  277.         _nullh = _log.handlers[0]   # restore orig _nullh to test in get_logger
  278.     else: _nullh = _NullHandler()
  279. else:
  280.     _nullh = _NullHandler()         # first init, no handlers yet
  281.     _log.addHandler(_nullh)         # install handler to quiet warning msg
  282.  
  283.  
  284. # Functions
  285. # ---------------------------------------------------------------------
  286. def _log_report(exception_obj=None):
  287.     'A generic debug logging function.'
  288.     if exception_obj:   func = _log.error
  289.     else:               func = _log.debug
  290.     func('%s: %s' % (sys._getframe(1).f_code.co_name,
  291.         sys._getframe(1).f_locals) )
  292.  
  293.  
  294. def check_plan(autostart=True):
  295.     '''
  296.        Ask the Transmission Client to check for a new plan.xml file.
  297.  
  298.        Options:
  299.            autostart       - Start the Transmission Client service if needed.
  300.    '''
  301.     import pythoncom
  302.     global _netic
  303.     if autostart:
  304.         from scalatools import start_svc
  305.         start_svc(_netic_svc)
  306.     try:
  307.         # don't cache netic, it doesn't work more than once # if not _netic:
  308.         _netic = COMObject(_netic_name)
  309.         _netic.CheckPlanNow()
  310.         _log.info('called.')
  311.     except pythoncom.com_error as err:
  312.         errstr = '%s\n\nPlayer Transmission Client (%s) not available.' % (
  313.             str(err), _netic_name)
  314.         _log.error( errstr.replace('\n', '  ') )
  315.         raise _ObjectNotAvailable, errstr
  316.     except Exception as err:
  317.         _log_report(err)
  318.  
  319.  
  320. def get_logger(*details, **destinations):
  321.     '''
  322.        A convenience function that returns a standard python logger object.
  323.        http://docs.python.org/library/logging.html (#logger-objects)
  324.  
  325.        Keyword Arguments (**destinations):
  326.            level               - Specifies the default logging level, else:
  327.                                  logging.WARN.  Also accepts a string in:
  328.                                  {debug|info|warn|error|critical}
  329.            format              - Specifies the default record format, else:
  330.                                  '%(asctime)s %(levelname)s %(message)s'
  331.            datefmt             - Specifies date format for asctime above,
  332.                                  default: '%Y-%m-%d %H:%M:%S'
  333.  
  334.            These arguments accept a testable item (e.g. Boolean) as value.
  335.            ---------------------------------------------------------------
  336.            scala               - Scala logging system
  337.            ntevt               - Windows NT Event Log
  338.            con                 - Console stdout w/color (default: True)
  339.            con_nocolor         - Console stdout
  340.  
  341.            The following require a specific value.  Multiple locations of the
  342.            same type can be configured by appending a char, e.g. "2"
  343.            ---------------------------------------------------------------
  344.            svar = 'var_name'   - Write text to a Scala variable.
  345.            net = 'host[:port]' - To a network host, default port: 8400.
  346.            file = 'c:/log.txt' - Files: Forward or backslashes acceptable.
  347.            smtp = (m,f,t,s,c)  - SMTP Mail, ERROR+ only.  Pass a tuple with
  348.                                  the same args as SMTPHandler, see Py docs.
  349.            http = (h,p,m)      - Pass a tuple with same args as HTTPHandler.
  350.            rotf|trtf = (f,...) - RotatingFileHandler, TimedRotatingFileHandler
  351.  
  352.        Arguments (*details):
  353.            If a more flexible configuration is needed, a number of dictionaries
  354.            containing destination details can be passed for finer control:
  355.                keys:   One of the following: 'typ', 'arg', 'fmt', 'lvl'
  356.                vals:   One of the corresponding values described above.
  357.        Returns:
  358.            A standard python logger object.
  359.        Note:
  360.            Use of logging.shutdown() is not needed, and may cause problems on
  361.            subsequent runs since Designer/Player keeps the interpreter open
  362.            until exit.  Therefore it is not permitted to modify the logger
  363.            object from this function, although it can be done directly.
  364.  
  365.        Examples:
  366.            1. The simplest, pass warning and above to the console:
  367.                from scalalib import get_logger
  368.                log = get_logger()
  369.                log.info('Byyye your commaaand.')
  370.  
  371.            2.  Multiple destinations, with a few of the same type:
  372.                log = get_logger( level='debug', scala=False, con=1,
  373.                    net='localhost:5292', file1='c:/1.txt', file2='c:/2.txt' )
  374.                log.warn("Don't cross the streams!")
  375.  
  376.            3. Finer control:  Send info+ to a net host, mail critical errors:
  377.                logmail = { 'typ':'smtp', 'arg':(m,f,t,s), 'lvl':'critical' }
  378.                # using a dict constructor is a bit easier to type:
  379.                lognet = dict(typ='net', arg='localhost:5150',
  380.                    fmt='%(message)s', lvl='info')
  381.                log = get_logger(logmail, lognet)
  382.                log.critical('You have %s seconds to reach minimum safe distance.')
  383.    '''
  384.     if _nullh in _log.handlers:
  385.         try:                _log.removeHandler(_nullh)  # prevent race condition
  386.         except ValueError:  pass
  387.         # log is empty. Test is necessary since Designer runs interpreter
  388.         # multiple times without exit.  Duplicate handlers create many problems.
  389.         destinations.setdefault('con', True)
  390.         level = destinations.pop('level', None)
  391.         if level:
  392.             if isinstance(level, basestring):
  393.                 level = { 'CRITICAL':logging.CRITICAL, 'ERROR':logging.ERROR,
  394.                     'WARN':logging.WARNING, 'WARNING':logging.WARNING,
  395.                     'INFO':logging.INFO, 'DEBUG':logging.DEBUG,
  396.                     }.get(level.upper(), logging.NOTSET)
  397.             _log.setLevel(level)
  398.         else:   level =  logging.NOTSET
  399.         format = destinations.pop('format',
  400.             '%(asctime)s %(levelname)s %(funcName)s: %(message)s')
  401.         short_fmt = '%(levelname)s %(funcName)s: %(message)s'
  402.         datefmt = destinations.pop('datefmt', '%Y-%m-%d %H:%M:%S')
  403.  
  404.         # convert simple destinations into detail blobs:
  405.         for k,v in destinations.items():
  406.             detail = dict(typ=k, arg=v, lvl=level, fmt=format, copied=1)
  407.             details += (detail,)
  408.  
  409.         for dest in details:  # where will we be logging to?
  410.             handler = None
  411.             htype = dest['typ'].lower()  # type must exist or KeyError
  412.             arg = dest['arg']
  413.             myfmt = format;  mylvl = level
  414.             if arg:
  415.                 if htype == 'scala':
  416.                     handler = _ScalaLogHandler()
  417.                     myfmt = short_fmt
  418.                 elif htype == 'ntevt':
  419.                     handler = logging.handlers.NTEventLogHandler(__name__)
  420.                     myfmt = short_fmt
  421.                 elif htype == 'con':
  422.                     try:
  423.                         if ((not hasattr(sys.stdout, 'isatty')) or
  424.                             (not sys.stdout.isatty())):
  425.                             raise EnvironmentError, ': no tty, color disabled.'
  426.                         if os.name == 'nt':
  427.                             if colorama:
  428.                                 #~ colorama.init()  # in case using fileLogger
  429.                                 from colorama.initialise import wrap_stream
  430.                                 sys.__stdout__ = wrap_stream(sys.__stdout__,
  431.                                                         None, None, None, True)
  432.                                 handler = _AnsiConHandler()
  433.                             else: handler = _Win32ColorConHandler() # deprecatd
  434.                         else:
  435.                             handler = _CursesConHandler()
  436.                     except (EnvironmentError, ImportError) as err:
  437.                         print err
  438.                         handler = logging.StreamHandler(sys.stdout)
  439.                 elif htype == 'con_nocolor':
  440.                     handler = logging.StreamHandler(sys.stdout)
  441.                 elif htype.startswith('svar'):
  442.                     handler = _ScalaVarHandler(arg)
  443.                     myfmt = short_fmt
  444.                 elif htype.startswith('net'):
  445.                     port = _def_logport;  host = arg
  446.                     if ':' in host:  host, port = host.split(':')
  447.                     handler = _PlainTextSocketHandler(host, int(port))
  448.                 elif htype.startswith('file'):
  449.                     handler = logging.FileHandler(arg)
  450.                 elif htype.startswith('rotf'):
  451.                     handler = logging.handlers.RotatingFileHandler(*arg)
  452.                 elif htype.startswith('trtf'):
  453.                     handler = logging.handlers.TimedRotatingFileHandler(*arg)
  454.                 elif htype.startswith('http'):
  455.                     handler = logging.handlers.HTTPHandler(*arg)
  456.                 elif htype.startswith('smtp'):
  457.                     handler = logging.handlers.SMTPHandler(*arg)
  458.                     if mylvl < logging.ERROR:   # minimize mail by default
  459.                         mylvl = logging.ERROR
  460.                 else:
  461.                     raise TypeError('Incorrect destination type: "%s"' % htype)
  462.  
  463.                 # detailed destination settings take precedence if they exist
  464.                 if not dest.get('copied'):  # copied use defaults
  465.                     mylvl = dest.get('lvl') or mylvl
  466.                     myfmt = dest.get('fmt') or myfmt
  467.                 handler.setLevel(mylvl)
  468.                 handler.setFormatter(logging.Formatter(myfmt, datefmt))
  469.                 _log.addHandler(handler)
  470.  
  471.         _log.shutdown = logging.shutdown  # for convenience, not needed Py 2.4+?
  472.         if not COMObject: _log.debug('Warning: Windows support not available.')
  473.         _log.debug( 'logging destinations: %s' % (details,) )
  474.         def _makefunc(level):
  475.             def thefunc(msg, *args, **kwargs):
  476.                 if _log.isEnabledFor(level):
  477.                     _log._log(level, msg, args, **kwargs)
  478.             return thefunc
  479.         # add convenience funcs
  480.         _log.note = _makefunc(logging.NOTE)
  481.         _log.fatal = _makefunc(logging.FATAL)
  482.     return _log
  483.  
  484.  
  485. def get_metaval(name, filename='ScalaNet:\\metadata.xml'):
  486.     'Deprecated and moved to scalatools.  Please use the new version.'
  487.     import scalatools as st
  488.     return st.get_metaval(name, filename)
  489.  
  490.  
  491. def get_scriptdir():
  492.     '''
  493.        Returns the current working folder of the script,
  494.        If there is an error, returns None.
  495.    '''
  496.     global _player
  497.     try:
  498.         if not _player:  _player = COMObject(_player_name)
  499.         return _player.ScriptDir
  500.     except Exception as err:
  501.         _log_report(err)
  502.         return None
  503.  
  504.  
  505. def import_mod(name):
  506.     '''
  507.        Search for a module, import, and return if found.
  508.  
  509.        Argument:
  510.            name            - The name of a module to search for, using
  511.                              scalatools.find_file().
  512.        Returns:
  513.            A python module object.  Raises ImportError if not found.
  514.        Example:
  515.            from scalalib import import_mod
  516.            mymod = import_mod('mymod')  # .py not necessary
  517.    '''
  518.     if not name: raise ValueError, 'name must not be blank.'
  519.     try:
  520.         return sys.modules[name]
  521.     except KeyError:
  522.         import imp
  523.         import scalatools as st
  524.         name = os.path.splitext(name)[0]  # remove any extension
  525.  
  526.         # search for the file
  527.         try:  modpath = st.find_file('%s.pyc' % name)
  528.         except IOError:
  529.             try: modpath = st.find_file('%s.py' % name)
  530.             except IOError:
  531.                 errstr = '"%s" not found.' % name
  532.                 _log_report(errstr)
  533.                 raise ImportError, errstr
  534.  
  535.         _log.info('"%s" as "%s"' % (modpath, name) )
  536.         if modpath.lower().endswith('.pyc'):
  537.             try:    # module could be from an old version of python
  538.                 return imp.load_compiled(name, modpath)
  539.             except ImportError:  # try again with .py
  540.                 return imp.load_source(name, modpath[:-1])
  541.         elif modpath.lower().endswith('.py'):
  542.             return imp.load_source(name, modpath)
  543.  
  544.  
  545. def install_content(abspath, subfolder='', autostart=True):
  546.     r'''
  547.        Copies a file to the local Scala Content: folder.
  548.  
  549.        Argument:
  550.            abspath         - A file (specified by its absolute path) to copy.
  551.        Options:
  552.            subfolder       - Place it into a subfolder to group related files.
  553.            autostart       - Start the Transmission Client service if necessary.
  554.        Notes:
  555.            Installed files may be found later using a Scala-style virtual path
  556.            string, passed to lock_content, e.g.:
  557.                lock_content(r'Content:\filename.ext')
  558.    '''
  559.     import pythoncom
  560.     global _netic
  561.     if not os.path.exists(abspath):
  562.         raise IOError, 'File "%s" does not exist.' % abspath
  563.     if autostart:
  564.         from scalatools import start_svc
  565.         start_svc(_netic_svc)
  566.  
  567.     try:
  568.         # don't cache netic, it doesn't work more than once # if not _netic:
  569.         _netic = COMObject(_netic_name)
  570.         _netic.IntegrateContentLocally(abspath, subfolder)
  571.         _log.info(abspath)
  572.     except pythoncom.com_error as err:
  573.         errstr = '%s\n\nPlayer Transmission Client (%s) not available.' % (
  574.             str(err), _netic_name)
  575.         _log.error( errstr.replace('\n', '  ') )
  576.         raise _ObjectNotAvailable, errstr
  577.     except Exception as err:
  578.         _log_report(err)
  579.  
  580.  
  581. def lock_content(scalapath, report_err=True):
  582.     r'''
  583.        Locks a content file so the player doesn't try to remove it.
  584.  
  585.        Argument/Option:
  586.            scalapath       -  A Scala-style virtual path, e.g.:
  587.                               "Content:\file.ext"
  588.            report_err      -  Log errors.
  589.        Returns:
  590.            The Windows path to the affected file.
  591.            If the file is not found or there is an error, returns None.
  592.        Example:
  593.            from scalalib import lock_content
  594.            winpath = scalalib.lock_content('Content:\example.png')
  595.    '''
  596.     class _StringAndLock(unicode):
  597.         '''A subclass of a string that carries a Scala lock object around with
  598.        it.  The lock will be unlocked upon deletion when the object falls out of
  599.        scope or is deleted explicitly.'''
  600.         def __del__(self):
  601.             if hasattr(self, 'lockObj'):
  602.                 _log.debug('unlock_content: "%s"' % scalapath)
  603.                 self.lockObj.UnlockScalaFile()
  604.     try:
  605.         lockObj = COMObject(_filelock_name)
  606.         windows_path = lockObj.LockScalaFile(scalapath)
  607.  
  608.         # Add the lock into the string, to unlock upon its deletion.
  609.         windows_path = _StringAndLock(windows_path)
  610.         windows_path.lockObj = lockObj
  611.  
  612.         _log.info( '"%s" @ "%s"' % (scalapath, windows_path) )
  613.         return windows_path
  614.     except Exception as err:
  615.         if report_err: _log_report(err)
  616.         return None
  617.  
  618.  
  619. def log_external(message, errcode=1001, module='', autostart=True):
  620.     '''
  621.        Writes a custom message to the Scala log and Content Manager Player
  622.        Health Screen.  Tries Player interface, then InfoNetwork.
  623.        Arguments:
  624.            message         The message to log.
  625.        Options:
  626.            errcode         Error code, see notes below.
  627.            module          The name of the source of the message.
  628.            autostart       If using InfoNetwork, whether to start service.
  629.        Notes:
  630.            https://license.scala.com/readme/ScriptingAutomation.html#toc_CustomProblems
  631.    '''
  632.     import pythoncom
  633.     global _player
  634.     if not _player:  _player = COMObject(_player_name)
  635.     try:
  636.         _player.LogExternalError(errcode, module, message)
  637.     except UnicodeError:
  638.         _player.LogExternalError(errcode, module, message.encode('UTF-8'))
  639.     except pythoncom.com_error as err:   # do not log this; creates a loop.
  640.         print 'COM Error: %s' % err
  641.         global _netic
  642.         if autostart:
  643.             from scalatools import start_svc
  644.             start_svc(_netic_svc)
  645.         # don't cache netic, it doesn't work more than once :/
  646.         _netic = COMObject(_netic_name)
  647.         try:
  648.             _netic.LogExternalError(errcode, module, message)
  649.         except pythoncom.com_error: pass
  650.         except UnicodeError:
  651.             _netic.LogExternalError(errcode, module, message.encode('UTF-8'))
  652.  
  653.  
  654. def _log_scala(message):
  655.     'Writes the given message string to the Scala Player Log file, IC.log.'
  656.     import pythoncom
  657.     global _player
  658.     if not _player:  _player = COMObject(_player_name)
  659.     try:
  660.         _player.Log(message)
  661.     except pythoncom.com_error as err:
  662.         print 'COM Error: %s' % err     # do not log this; creates a loop.
  663.     except UnicodeError:
  664.         _player.Log(message.encode('UTF-8'))
  665.  
  666.  
  667. def publish(scriptlist, targeturl, targetfolder='', logfilename='',
  668.     editpassword='', options='', autostart=True):
  669.     r'''
  670.        Performs the equivalent of the Publish to Network function of Scala
  671.        Designer5.  Requires the Scala Publish Automation EX Module5 which is an
  672.        additional software component that is installed seperately.
  673.  
  674.        Arguments:
  675.            scriptlist      - A string containing the absolute paths of one
  676.                                or more script files to publish, one per line,
  677.                                or alternately, a python list of them.
  678.            targeturl       - Publish location path/URL with username and passwd.
  679.                                Examples:
  680.                                - UNC:      \\server\share\folder
  681.                                - FTP URL:  ftp://user:pass@host[:port]/folder/
  682.                                - HTTP/s Content Manager URL:
  683.                                    http://user:pass@host[:port]/folder?netname
  684.            targetfolder    - Sub-folder into which to publish
  685.            logfilename     - Absolute path to a log file to report progress,
  686.                                otherwise to: "%TEMP%\pub_log.txt".
  687.            editpassword    - Optional password to apply to published script(s).
  688.            options         - A string containing zero or more of these flags:
  689.                                - 'd': Show progress GUI.
  690.                                - 'i': Ignore errors.
  691.                                - 'f': Do NOT include fonts.
  692.                                - 'w': Do NOT include wipes.
  693.                                - 'x': Skip cleanup.
  694.                                - 'p': Use passive FTP.
  695.            autostart       - Start the Publisher service if necessary.
  696.        Returns:
  697.            handle          - A unique handle for use with publish_check().
  698.  
  699.        Example:
  700.            # See the sca_publisher.py script for a general solution.
  701.            import time, glob, scalalib as sl
  702.            targeturl = 'http://user:passwd@mycm.com:8080/ContentManager?MyCo'
  703.            scripts = glob.glob('*.sca')
  704.            pubhandle = sl.publish(scripts, targeturl, options='d')
  705.            while True:
  706.                time.sleep(3)
  707.                status = sl.publish_check(pubhandle)
  708.                statstr = ('Publishing script %(currentscriptnum)s of %(numberofscripts)s'
  709.                    + ' - %(overallpercentdone)3.3s%% complete, %(currentscriptname)s ')
  710.                print statstr % status
  711.                if status.get('overallpercentdone') == 100: break
  712.    '''
  713.     import pythoncom
  714.     global _publshr
  715.     if autostart:
  716.         from scalatools import start_svc
  717.         start_svc(_pub_svc)
  718.  
  719.     # massage parameters to make sure paths are absolute
  720.     if type(scriptlist) is list:
  721.         scriptlist = [ os.path.abspath(x) for x in scriptlist ]
  722.         scriptlist = '\n'.join(scriptlist)
  723.     elif type(scriptlist) is str:
  724.         scriptlist = os.path.abspath(scriptlist)
  725.     if not logfilename:
  726.         logfilename = os.path.join(os.environ['TEMP'], 'pub_log.txt')
  727.     else:
  728.         logfilename = os.path.abspath(logfilename)
  729.     local_opts = locals()                       # log parameters
  730.  
  731.     try:
  732.         if not _publshr:
  733.             _publshr = COMObject(_publshr_name)
  734.         pubhandle = None
  735.  
  736.         _log.debug('options: %s' % local_opts )
  737.         try:
  738.             censoredurl = targeturl  # censor url if needed.
  739.             if ( targeturl.lower().startswith('http')
  740.                 or targeturl.lower().startswith('ftp') ):
  741.                 censoredurl = targeturl.split('//', 1)
  742.                 censoredurl = '%s%s%s%s' % (censoredurl[0], '//', 'xxx:xxx@',
  743.                     censoredurl[1].split('@',1)[1])
  744.             _log.info('publishing to %s' % censoredurl)
  745.         except IndexError:
  746.             _log.info('publishing to %s' % targeturl)
  747.  
  748.         if targetfolder:
  749.             pubhandle = _publshr.GoPublishFolder(scriptlist, targeturl,
  750.                 targetfolder, logfilename, editpassword, options)
  751.         else:
  752.             pubhandle = _publshr.GoPublish(scriptlist, targeturl, logfilename,
  753.                 editpassword, options)
  754.         if pubhandle == None:
  755.             _log.warn('Publish operation returned None.  Is the dongle present?')
  756.         return pubhandle
  757.  
  758.     except pythoncom.com_error as err:
  759.         errstr = ('%s: %s services not available.  Is it installed, running, ' +
  760.             'and not disabled?') % (err.__class__.__name__, _publshr_name)
  761.         _log.critical(errstr)
  762.         raise err
  763.     except Exception as err:
  764.         _log_report(err)
  765.         raise err
  766.  
  767.  
  768. def publish_check(pubhandle):
  769.     '''
  770.        Checks the status of a Scala publishing operation.
  771.  
  772.        Argument:
  773.            pubhandle           - A publishing handle returned from publish().
  774.        Returns:
  775.            A status dictionary with the following keys:
  776.  
  777.            numberofscripts     - Total number of scripts in this operation.
  778.            currentscriptnum    - Number in sequence of script currently being
  779.                                    published, starting with 1.
  780.            currentscriptname   - Name of script currently being published, no
  781.                                    path or extension.
  782.            scriptpercentdone   - Current script progress 0-100 (%)
  783.            overallpercentdone  - Overall progress 0-100 (%)
  784.            completedscripts    - A string listing successfully publish scripts,
  785.                                    one name per line.
  786.            failedscripts       - A string listing all scripts that failed to
  787.                                    publish, one name per line.
  788.            allerrors           - A string listing all errors encountered in
  789.                                    this publish operation
  790.    '''
  791.     import pythoncom as _pythoncom
  792.     global _publshr
  793.     try:
  794.         if not _publshr:
  795.             _publshr = COMObject(_publshr_name)
  796.  
  797.         (numberofscripts, currentscriptnum, currentscriptname,
  798.             scriptpercentdone, overallpercentdone, completedscripts,
  799.             failedscripts, allerrors) = _publshr.CheckPublish(pubhandle)
  800.         if overallpercentdone == None:
  801.             _log.warn('Publish returned None.  Is the dongle present?')
  802.  
  803.         # create a dictionary of all local vars not starting with "_"
  804.         # list comp works in 2.3 unlike gen expression
  805.         status = dict( [ x for x in locals().items()
  806.                          if not x[0].startswith('_') ] )
  807.         _log.debug(status)
  808.         return status
  809.  
  810.     except _pythoncom.com_error as err:
  811.         _log.error( '%s: %s not available.' % (err, _publshr_name) )
  812.         raise err
  813.     except Exception as err:
  814.         _log_report(err)
  815.  
  816.  
  817. def quit_player():
  818.     '''
  819.        Signals the player to shut down gracefully, by sending the ESC keystroke.
  820.        There is a possiblity it may not succeed if the keystroke is lost.
  821.    '''
  822.     import scalatools
  823.     _log.info('called.')
  824.     scalatools.send_key('escape')
  825.  
  826.  
  827. def restart_play():
  828.     'Restart Playback on the Scala Player.'
  829.     import pythoncom
  830.     global _runic
  831.     try:
  832.         # don't cache obj, doesn't work more than once # if not _runic:
  833.         _runic = COMObject(_runic_name)
  834.         _log.info('called.')
  835.         _runic.RestartPlayback()
  836.     except pythoncom.com_error as err:
  837.         errstr = '%s\n\nScala Player (%s) not available.' % (
  838.             str(err), _runic_name)
  839.         _log.error( errstr.replace('\n', '  ') )
  840.         raise _ObjectNotAvailable, errstr
  841.     except Exception as err:
  842.         _log_report(err)
  843.  
  844.  
  845. time._orig_sleep = time.sleep  # for backward compatibility w/sleep wrapper
  846. def sleep(msecs):
  847.     '''
  848.        Pause execution for the specified interval specified in milliseconds.
  849.  
  850.        Notes:
  851.            This function can be useful at times to give the player time to
  852.            notice shared variables have changed.
  853.            If ScalaPlayer.Sleep() is available, this function will use it.
  854.            Otherwise, it will fall back on time.sleep().
  855.    '''
  856.     import pythoncom
  857.     global _player, sleep_was_successful
  858.     try:
  859.         if not _player:  _player = COMObject(_player_name)
  860.         _player.Sleep(msecs)
  861.         _log.debug('Scala sleep complete.')
  862.         sleep_was_successful = True
  863.     except pythoncom.com_error as err:
  864.         _log.debug('%s' % err)
  865.         if sleep_was_successful:        # we had the Player once, but lost
  866.             sys.exit(9)                 # at shutdown, don't hang up :/
  867.         else:                           # we never had the Player, cmd-line?
  868.             time.sleep(msecs/1000.0)    # accepts whole float seconds
  869.  
  870.  
  871. def thumbnail_gen(scriptpath, filename, width=96, height=96,
  872.     options='', pagename='', xmltext='', autostart=True, **templ_args):
  873.     r'''
  874.        Generates thumbnails of Scala Scripts/Templates.  Requires the Scala
  875.        Server Support Service from Content Manager.
  876.  
  877.        Arguments:
  878.            scriptpath      - The full path to the Scala Script.
  879.            filename        - Full path to desired image (.jpg, .jpeg, .png)
  880.            width           - Thumbnail width in pixels
  881.            height          - Thumbnail height in pixels
  882.            options         - A string that can contain one of these flags:
  883.                                - 'rl': Rotate 90 degrees left
  884.                                - 'rr': Rotate 90 degrees right
  885.                                - 'ru': Rotate 180 degrees (upside down)
  886.                                - 'k':  Keep aspect
  887.            pagename        - Name of the page to generate, defaults to first.
  888.                                Ignored when scriptpath points to a template.
  889.            xmltext         - An XML snippet that defines template fields, e.g:
  890.                                <ingredients>
  891.                                    <text name="Headline">Breaking News</text>
  892.                                    <boolean name="Enabled">On</boolean>
  893.                                    <integer name="age">123</integer>
  894.                                    <real name="price">1.23</real>
  895.                                    <filename name="Logo">C:\logo.png</filename>
  896.                                </ingredients>
  897.            templ_args      - Alternately, additional keyword args passed will
  898.                                create xmltext automatically; type is inferred.
  899.                                eg. (Enabled=True, Logo=r'c:\logo.png')
  900.            autostart       - Start the Server Support service if necessary.
  901.        Returns:
  902.            numthumbs       - The number of thumbnails created, as there may be
  903.                                more than one page in a script.
  904.        Example:
  905.            from scalalib import thumbnail_gen
  906.            thumbnail_gen('d:\\tmpl.sca', 'd:\\thbn.jpg', 96, 96, options='k',
  907.                tmpl_CityName='Kathmandu', tmpl_Greeting='d:\\Namaste.jpg')
  908.    '''
  909.     import pythoncom
  910.     global _thumbnl
  911.     if autostart:
  912.         from scalatools import start_svc
  913.         start_svc(_thumb_svc)
  914.     try:
  915.         if not _thumbnl:   # initialize vars
  916.             _thumbnl = COMObject(_thumbnl_name)
  917.         errmsgs = None
  918.         numthumbs = 1
  919.  
  920.         if templ_args:
  921.             xmltext = '<ingredients>\n'
  922.             for name,value in templ_args.items():
  923.                 if   type(value) is bool:
  924.                                             vartype = 'boolean'
  925.                                             if value:   value = 'On'
  926.                                             else:       value = 'Off'
  927.                 elif type(value) is int:    vartype = 'integer'
  928.                 elif type(value) is float:  vartype = 'real'
  929.                 elif type(value) is str or type(value) is unicode:
  930.                     if (len(value) > 3 and value[1] == ':' and
  931.                         '\\' in value):     vartype = 'filename'
  932.                     else:                   vartype = 'text'
  933.                 else: raise 'error: gen_thumbs - unknown type passed.'
  934.                 xmltext += '    <%s name="%s">%s</%s>\n' % (
  935.                     vartype, name, value, vartype)
  936.             xmltext += '</ingredients>\n'
  937.             _log.debug('xmltext generated: %r' % xmltext )
  938.  
  939.         if xmltext:
  940.             _thumbnl.GeneratePreviewThumbnails(scriptpath, xmltext,
  941.                 filename, width, height, options, numthumbs, errmsgs)
  942.         else:
  943.             _thumbnl.GenerateThumbnail(scriptpath, pagename,
  944.                 filename, width, height, options, errmsgs)
  945.  
  946.         _log.info('Generated %s thumbnail(s) of "%s".' % (numthumbs, scriptpath))
  947.         if errmsgs:  _log.warn(errmsgs)
  948.         return numthumbs
  949.  
  950.     except pythoncom.com_error as err:
  951.         _log.error( '%s: %s' % (err, _thumbnl_name) )
  952.         raise err
  953.     except Exception as err:
  954.         _log_report(err)
Add Comment
Please, Sign In to add comment