Branch libreoffice-5-0-4
[LibreOffice.git] / scripting / source / pyprov / pythonscript.py
blob03f0cdeae7c877b745f7e3fa470f6738005f8716
2 # This file is part of the LibreOffice project.
4 # This Source Code Form is subject to the terms of the Mozilla Public
5 # License, v. 2.0. If a copy of the MPL was not distributed with this
6 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 # This file incorporates work covered by the following license notice:
10 # Licensed to the Apache Software Foundation (ASF) under one or more
11 # contributor license agreements. See the NOTICE file distributed
12 # with this work for additional information regarding copyright
13 # ownership. The ASF licenses this file to you under the Apache
14 # License, Version 2.0 (the "License"); you may not use this file
15 # except in compliance with the License. You may obtain a copy of
16 # the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 # XScript implementation for python
19 import uno
20 import unohelper
21 import sys
22 import os
23 import imp
24 import time
25 import ast
27 try:
28 unicode
29 except NameError:
30 # Python 3 compatibility
31 unicode = str
33 class LogLevel:
34 NONE = 0 # production level
35 ERROR = 1 # for script developers
36 DEBUG = 2 # for script framework developers
38 PYSCRIPT_LOG_ENV = "PYSCRIPT_LOG_LEVEL"
39 PYSCRIPT_LOG_STDOUT_ENV = "PYSCRIPT_LOG_STDOUT"
41 # Configuration ----------------------------------------------------
42 LogLevel.use = LogLevel.NONE
43 if os.environ.get(PYSCRIPT_LOG_ENV) == "ERROR":
44 LogLevel.use = LogLevel.ERROR
45 elif os.environ.get(PYSCRIPT_LOG_ENV) == "DEBUG":
46 LogLevel.use = LogLevel.DEBUG
48 # True, writes to stdout (difficult on windows)
49 # False, writes to user/Scripts/python/log.txt
50 LOG_STDOUT = os.environ.get(PYSCRIPT_LOG_STDOUT_ENV, "1") != "0"
52 ENABLE_EDIT_DIALOG=False # offers a minimal editor for editing.
53 #-------------------------------------------------------------------
55 def encfile(uni):
56 return uni.encode( sys.getfilesystemencoding())
58 def lastException2String():
59 (excType,excInstance,excTraceback) = sys.exc_info()
60 ret = str(excType) + ": "+str(excInstance) + "\n" + \
61 uno._uno_extract_printable_stacktrace( excTraceback )
62 return ret
64 def logLevel2String( level ):
65 ret = " NONE"
66 if level == LogLevel.ERROR:
67 ret = "ERROR"
68 elif level >= LogLevel.DEBUG:
69 ret = "DEBUG"
70 return ret
72 def getLogTarget():
73 ret = sys.stdout
74 if not LOG_STDOUT:
75 try:
76 pathSubst = uno.getComponentContext().ServiceManager.createInstance(
77 "com.sun.star.util.PathSubstitution" )
78 userInstallation = pathSubst.getSubstituteVariableValue( "user" )
79 if len( userInstallation ) > 0:
80 systemPath = uno.fileUrlToSystemPath( userInstallation + "/Scripts/python/log.txt" )
81 ret = open( systemPath , "a" )
82 except:
83 print("Exception during creation of pythonscript logfile: "+ lastException2String() + "\n, delagating log to stdout\n")
84 return ret
86 class Logger(LogLevel):
87 def __init__(self , target ):
88 self.target = target
90 def isDebugLevel( self ):
91 return self.use >= self.DEBUG
93 def debug( self, msg ):
94 if self.isDebugLevel():
95 self.log( self.DEBUG, msg )
97 def isErrorLevel( self ):
98 return self.use >= self.ERROR
100 def error( self, msg ):
101 if self.isErrorLevel():
102 self.log( self.ERROR, msg )
104 def log( self, level, msg ):
105 if self.use >= level:
106 try:
107 self.target.write(
108 time.asctime() +
109 " [" +
110 logLevel2String( level ) +
111 "] " +
112 msg +
113 "\n" )
114 self.target.flush()
115 except:
116 print("Error during writing to stdout: " +lastException2String() + "\n")
118 log = Logger( getLogTarget() )
120 log.debug( "pythonscript loading" )
122 #from com.sun.star.lang import typeOfXServiceInfo, typeOfXTypeProvider
123 from com.sun.star.uno import RuntimeException
124 from com.sun.star.lang import IllegalArgumentException
125 from com.sun.star.container import NoSuchElementException
126 from com.sun.star.lang import XServiceInfo
127 from com.sun.star.io import IOException
128 from com.sun.star.ucb import CommandAbortedException, XCommandEnvironment, XProgressHandler, Command
129 from com.sun.star.task import XInteractionHandler
130 from com.sun.star.beans import XPropertySet, Property
131 from com.sun.star.container import XNameContainer
132 from com.sun.star.xml.sax import XDocumentHandler, InputSource
133 from com.sun.star.uno import Exception as UnoException
134 from com.sun.star.script import XInvocation
135 from com.sun.star.awt import XActionListener
137 from com.sun.star.script.provider import XScriptProvider, XScript, XScriptContext, ScriptFrameworkErrorException
138 from com.sun.star.script.browse import XBrowseNode
139 from com.sun.star.script.browse.BrowseNodeTypes import SCRIPT, CONTAINER, ROOT
140 from com.sun.star.util import XModifyListener
142 LANGUAGENAME = "Python"
143 GLOBAL_SCRIPTCONTEXT_NAME = "XSCRIPTCONTEXT"
144 CALLABLE_CONTAINER_NAME = "g_exportedScripts"
146 # pythonloader looks for a static g_ImplementationHelper variable
147 g_ImplementationHelper = unohelper.ImplementationHelper()
148 g_implName = "org.libreoffice.pyuno.LanguageScriptProviderFor"+LANGUAGENAME
152 BLOCK_SIZE = 65536
153 def readTextFromStream( inputStream ):
154 # read the file
155 code = uno.ByteSequence( "" )
156 while True:
157 read,out = inputStream.readBytes( None , BLOCK_SIZE )
158 code = code + out
159 if read < BLOCK_SIZE:
160 break
161 return code.value
163 def toIniName( str ):
164 # TODO: what is the official way to get to know whether i am on the windows platform ?
165 if( hasattr(sys , "dllhandle") ):
166 return str + ".ini"
167 return str + "rc"
170 """ definition: storageURI is the system dependent, absolute file url, where the script is stored on disk
171 scriptURI is the system independent uri
173 class MyUriHelper:
175 def __init__( self, ctx, location ):
176 self.s_UriMap = \
177 { "share" : "vnd.sun.star.expand:$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/Scripts/python" , \
178 "share:uno_packages" : "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages", \
179 "user" : "vnd.sun.star.expand:${$BRAND_INI_DIR/" + toIniName( "bootstrap") + "::UserInstallation}/user/Scripts/python" , \
180 "user:uno_packages" : "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages" }
181 self.m_uriRefFac = ctx.ServiceManager.createInstanceWithContext("com.sun.star.uri.UriReferenceFactory",ctx)
182 if location.startswith( "vnd.sun.star.tdoc" ):
183 self.m_baseUri = location + "/Scripts/python"
184 self.m_scriptUriLocation = "document"
185 else:
186 self.m_baseUri = expandUri( self.s_UriMap[location] )
187 self.m_scriptUriLocation = location
188 log.debug( "initialized urihelper with baseUri="+self.m_baseUri + ",m_scriptUriLocation="+self.m_scriptUriLocation )
190 def getRootStorageURI( self ):
191 return self.m_baseUri
193 def getStorageURI( self, scriptURI ):
194 return self.scriptURI2StorageUri(scriptURI)
196 def getScriptURI( self, storageURI ):
197 return self.storageURI2ScriptUri(storageURI)
199 def storageURI2ScriptUri( self, storageURI ):
200 if not storageURI.startswith( self.m_baseUri ):
201 message = "pythonscript: storage uri '" + storageURI + "' not in base uri '" + self.m_baseUri + "'"
202 log.debug( message )
203 raise RuntimeException( message )
205 ret = "vnd.sun.star.script:" + \
206 storageURI[len(self.m_baseUri)+1:].replace("/","|") + \
207 "?language=" + LANGUAGENAME + "&location=" + self.m_scriptUriLocation
208 log.debug( "converting storageURI="+storageURI + " to scriptURI=" + ret )
209 return ret
211 def scriptURI2StorageUri( self, scriptURI ):
212 try:
213 myUri = self.m_uriRefFac.parse(scriptURI)
214 ret = self.m_baseUri + "/" + myUri.getName().replace( "|", "/" )
215 log.debug( "converting scriptURI="+scriptURI + " to storageURI=" + ret )
216 return ret
217 except UnoException as e:
218 log.error( "error during converting scriptURI="+scriptURI + ": " + e.Message)
219 raise RuntimeException( "pythonscript:scriptURI2StorageUri: " +e.getMessage(), None )
220 except Exception as e:
221 log.error( "error during converting scriptURI="+scriptURI + ": " + str(e))
222 raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + str(e), None )
225 class ModuleEntry:
226 def __init__( self, lastRead, module ):
227 self.lastRead = lastRead
228 self.module = module
230 def hasChanged( oldDate, newDate ):
231 return newDate.Year > oldDate.Year or \
232 newDate.Month > oldDate.Month or \
233 newDate.Day > oldDate.Day or \
234 newDate.Hours > oldDate.Hours or \
235 newDate.Minutes > oldDate.Minutes or \
236 newDate.Seconds > oldDate.Seconds or \
237 newDate.NanoSeconds > oldDate.NanoSeconds
239 def ensureSourceState( code ):
240 if code.endswith(b"\n"):
241 code = code + b"\n"
242 code = code.replace(b"\r", b"")
243 return code
246 def checkForPythonPathBesideScript( url ):
247 if url.startswith( "file:" ):
248 path = unohelper.fileUrlToSystemPath( url+"/pythonpath.zip" );
249 log.log( LogLevel.DEBUG, "checking for existence of " + path )
250 if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path:
251 log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" )
252 sys.path.append( path )
254 path = unohelper.fileUrlToSystemPath( url+"/pythonpath" );
255 log.log( LogLevel.DEBUG, "checking for existence of " + path )
256 if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path:
257 log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" )
258 sys.path.append( path )
261 class ScriptContext(unohelper.Base):
262 def __init__( self, ctx, doc, inv ):
263 self.ctx = ctx
264 self.doc = doc
265 self.inv = inv
267 # XScriptContext
268 def getDocument(self):
269 if self.doc:
270 return self.doc
271 return self.getDesktop().getCurrentComponent()
273 def getDesktop(self):
274 return self.ctx.ServiceManager.createInstanceWithContext(
275 "com.sun.star.frame.Desktop", self.ctx )
277 def getComponentContext(self):
278 return self.ctx
280 def getInvocationContext(self):
281 return self.inv
283 #----------------------------------
284 # Global Module Administration
285 # does not fit together with script
286 # engine lifetime management
287 #----------------------------------
288 #g_scriptContext = ScriptContext( uno.getComponentContext(), None )
289 #g_modules = {}
290 #def getModuleByUrl( url, sfa ):
291 # entry = g_modules.get(url)
292 # load = True
293 # lastRead = sfa.getDateTimeModified( url )
294 # if entry:
295 # if hasChanged( entry.lastRead, lastRead ):
296 # log.debug("file " + url + " has changed, reloading")
297 # else:
298 # load = False
300 # if load:
301 # log.debug( "opening >" + url + "<" )
303 # code = readTextFromStream( sfa.openFileRead( url ) )
305 # execute the module
306 # entry = ModuleEntry( lastRead, imp.new_module("ooo_script_framework") )
307 # entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = g_scriptContext
308 # entry.module.__file__ = url
309 # exec code in entry.module.__dict__
310 # g_modules[ url ] = entry
311 # log.debug( "mapped " + url + " to " + str( entry.module ) )
312 # return entry.module
314 class ProviderContext:
315 def __init__( self, storageType, sfa, uriHelper, scriptContext ):
316 self.storageType = storageType
317 self.sfa = sfa
318 self.uriHelper = uriHelper
319 self.scriptContext = scriptContext
320 self.modules = {}
321 self.rootUrl = None
322 self.mapPackageName2Path = None
324 def getTransientPartFromUrl( self, url ):
325 rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1)
326 return rest[0:rest.find("/")]
328 def getPackageNameFromUrl( self, url ):
329 rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1)
330 start = rest.find("/") +1
331 return rest[start:rest.find("/",start)]
334 def removePackageByUrl( self, url ):
335 items = self.mapPackageName2Path.items()
336 for i in items:
337 if url in i[1].paths:
338 self.mapPackageName2Path.pop(i[0])
339 break
341 def addPackageByUrl( self, url ):
342 packageName = self.getPackageNameFromUrl( url )
343 transientPart = self.getTransientPartFromUrl( url )
344 log.debug( "addPackageByUrl : " + packageName + ", " + transientPart + "("+url+")" + ", rootUrl="+self.rootUrl )
345 if packageName in self.mapPackageName2Path:
346 package = self.mapPackageName2Path[ packageName ]
347 package.paths = package.paths + (url, )
348 else:
349 package = Package( (url,), transientPart)
350 self.mapPackageName2Path[ packageName ] = package
352 def isUrlInPackage( self, url ):
353 values = self.mapPackageName2Path.values()
354 for i in values:
355 # print ("checking " + url + " in " + str(i.paths))
356 if url in i.paths:
357 return True
358 # print ("false")
359 return False
361 def setPackageAttributes( self, mapPackageName2Path, rootUrl ):
362 self.mapPackageName2Path = mapPackageName2Path
363 self.rootUrl = rootUrl
365 def getPersistentUrlFromStorageUrl( self, url ):
366 # package name is the second directory
367 ret = url
368 if self.rootUrl:
369 pos = len( self.rootUrl) +1
370 ret = url[0:pos]+url[url.find("/",pos)+1:len(url)]
371 log.debug( "getPersistentUrlFromStorageUrl " + url + " -> "+ ret)
372 return ret
374 def getStorageUrlFromPersistentUrl( self, url):
375 ret = url
376 if self.rootUrl:
377 pos = len(self.rootUrl)+1
378 packageName = url[pos:url.find("/",pos+1)]
379 package = self.mapPackageName2Path[ packageName ]
380 ret = url[0:pos]+ package.transientPathElement + "/" + url[pos:len(url)]
381 log.debug( "getStorageUrlFromPersistentUrl " + url + " -> "+ ret)
382 return ret
384 def getFuncsByUrl( self, url ):
385 src = readTextFromStream( self.sfa.openFileRead( url ) )
386 checkForPythonPathBesideScript( url[0:url.rfind('/')] )
387 src = ensureSourceState( src )
389 try:
390 code = ast.parse( src )
391 except:
392 log.isDebugLevel() and log.debug( "pythonscript: getFuncsByUrl: exception while parsing: " + lastException2String())
393 raise
395 allFuncs = []
397 if code is None:
398 return allFuncs
400 g_exportedScripts = []
401 for node in ast.iter_child_nodes(code):
402 if isinstance(node, ast.FunctionDef):
403 allFuncs.append(node.name)
404 elif isinstance(node, ast.Assign):
405 for target in node.targets:
406 if target.id == "g_exportedScripts":
407 for value in node.value.elts:
408 g_exportedScripts.append(value.id)
409 return g_exportedScripts
411 # Python 2 only
412 # for node in code.node.nodes:
413 # if node.__class__.__name__ == 'Function':
414 # allFuncs.append(node.name)
415 # elif node.__class__.__name__ == 'Assign':
416 # for assignee in node.nodes:
417 # if assignee.name == 'g_exportedScripts':
418 # for item in node.expr.nodes:
419 # if item.__class__.__name__ == 'Name':
420 # g_exportedScripts.append(item.name)
421 # return g_exportedScripts
423 return allFuncs
425 def getModuleByUrl( self, url ):
426 entry = self.modules.get(url)
427 load = True
428 lastRead = self.sfa.getDateTimeModified( url )
429 if entry:
430 if hasChanged( entry.lastRead, lastRead ):
431 log.debug( "file " + url + " has changed, reloading" )
432 else:
433 load = False
435 if load:
436 log.debug( "opening >" + url + "<" )
438 src = readTextFromStream( self.sfa.openFileRead( url ) )
439 checkForPythonPathBesideScript( url[0:url.rfind('/')] )
440 src = ensureSourceState( src )
442 # execute the module
443 entry = ModuleEntry( lastRead, imp.new_module("ooo_script_framework") )
444 entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.scriptContext
446 code = None
447 if url.startswith( "file:" ):
448 code = compile( src, encfile(uno.fileUrlToSystemPath( url ) ), "exec" )
449 else:
450 code = compile( src, url, "exec" )
451 exec(code, entry.module.__dict__)
452 entry.module.__file__ = url
453 self.modules[ url ] = entry
454 log.debug( "mapped " + url + " to " + str( entry.module ) )
455 return entry.module
457 #--------------------------------------------------
458 def isScript( candidate ):
459 ret = False
460 if isinstance( candidate, type(isScript) ):
461 ret = True
462 return ret
464 #-------------------------------------------------------
465 class ScriptBrowseNode( unohelper.Base, XBrowseNode , XPropertySet, XInvocation, XActionListener ):
466 def __init__( self, provCtx, uri, fileName, funcName ):
467 self.fileName = fileName
468 self.funcName = funcName
469 self.provCtx = provCtx
470 self.uri = uri
472 def getName( self ):
473 return self.funcName
475 def getChildNodes(self):
476 return ()
478 def hasChildNodes(self):
479 return False
481 def getType( self):
482 return SCRIPT
484 def getPropertyValue( self, name ):
485 ret = None
486 try:
487 if name == "URI":
488 ret = self.provCtx.uriHelper.getScriptURI(
489 self.provCtx.getPersistentUrlFromStorageUrl( self.uri + "$" + self.funcName ) )
490 elif name == "Editable" and ENABLE_EDIT_DIALOG:
491 ret = not self.provCtx.sfa.isReadOnly( self.uri )
493 log.debug( "ScriptBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) )
494 except:
495 log.error( "ScriptBrowseNode.getPropertyValue error " + lastException2String())
496 raise
498 return ret
499 def setPropertyValue( self, name, value ):
500 log.debug( "ScriptBrowseNode.setPropertyValue called " + name + "=" +str(value ) )
501 def getPropertySetInfo( self ):
502 log.debug( "ScriptBrowseNode.getPropertySetInfo called " )
503 return None
505 def getIntrospection( self ):
506 return None
508 def invoke( self, name, params, outparamindex, outparams ):
509 if name == "Editable":
510 servicename = "com.sun.star.awt.DialogProvider"
511 ctx = self.provCtx.scriptContext.getComponentContext()
512 dlgprov = ctx.ServiceManager.createInstanceWithContext(
513 servicename, ctx )
515 self.editor = dlgprov.createDialog(
516 "vnd.sun.star.script:" +
517 "ScriptBindingLibrary.MacroEditor?location=application")
519 code = readTextFromStream(self.provCtx.sfa.openFileRead(self.uri))
520 code = ensureSourceState( code )
521 self.editor.getControl("EditorTextField").setText(code)
523 self.editor.getControl("RunButton").setActionCommand("Run")
524 self.editor.getControl("RunButton").addActionListener(self)
525 self.editor.getControl("SaveButton").setActionCommand("Save")
526 self.editor.getControl("SaveButton").addActionListener(self)
528 self.editor.execute()
530 return None
532 def actionPerformed( self, event ):
533 try:
534 if event.ActionCommand == "Run":
535 code = self.editor.getControl("EditorTextField").getText()
536 code = ensureSourceState( code )
537 mod = imp.new_module("ooo_script_framework")
538 mod.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.provCtx.scriptContext
539 exec(code, mod.__dict__)
540 values = mod.__dict__.get( CALLABLE_CONTAINER_NAME , None )
541 if not values:
542 values = mod.__dict__.values()
544 for i in values:
545 if isScript( i ):
547 break
549 elif event.ActionCommand == "Save":
550 toWrite = uno.ByteSequence(
551 str(
552 self.editor.getControl("EditorTextField").getText().encode(
553 sys.getdefaultencoding())) )
554 copyUrl = self.uri + ".orig"
555 self.provCtx.sfa.move( self.uri, copyUrl )
556 out = self.provCtx.sfa.openFileWrite( self.uri )
557 out.writeBytes( toWrite )
558 out.close()
559 self.provCtx.sfa.kill( copyUrl )
560 # log.debug("Save is not implemented yet")
561 # text = self.editor.getControl("EditorTextField").getText()
562 # log.debug("Would save: " + text)
563 except:
564 # TODO: add an error box here !
565 log.error( lastException2String() )
568 def setValue( self, name, value ):
569 return None
571 def getValue( self, name ):
572 return None
574 def hasMethod( self, name ):
575 return False
577 def hasProperty( self, name ):
578 return False
581 #-------------------------------------------------------
582 class FileBrowseNode( unohelper.Base, XBrowseNode ):
583 def __init__( self, provCtx, uri , name ):
584 self.provCtx = provCtx
585 self.uri = uri
586 self.name = name
587 self.funcnames = None
589 def getName( self ):
590 return self.name
592 def getChildNodes(self):
593 ret = ()
594 try:
595 self.funcnames = self.provCtx.getFuncsByUrl( self.uri )
597 scriptNodeList = []
598 for i in self.funcnames:
599 scriptNodeList.append(
600 ScriptBrowseNode(
601 self.provCtx, self.uri, self.name, i ))
602 ret = tuple( scriptNodeList )
603 log.debug( "returning " +str(len(ret)) + " ScriptChildNodes on " + self.uri )
604 except:
605 text = lastException2String()
606 log.error( "Error while evaluating " + self.uri + ":" + text )
607 raise
608 return ret
610 def hasChildNodes(self):
611 try:
612 return len(self.getChildNodes()) > 0
613 except:
614 return False
616 def getType( self):
617 return CONTAINER
621 class DirBrowseNode( unohelper.Base, XBrowseNode ):
622 def __init__( self, provCtx, name, rootUrl ):
623 self.provCtx = provCtx
624 self.name = name
625 self.rootUrl = rootUrl
627 def getName( self ):
628 return self.name
630 def getChildNodes( self ):
631 try:
632 log.debug( "DirBrowseNode.getChildNodes called for " + self.rootUrl )
633 contents = self.provCtx.sfa.getFolderContents( self.rootUrl, True )
634 browseNodeList = []
635 for i in contents:
636 if i.endswith( ".py" ):
637 log.debug( "adding filenode " + i )
638 browseNodeList.append(
639 FileBrowseNode( self.provCtx, i, i[i.rfind("/")+1:len(i)-3] ) )
640 elif self.provCtx.sfa.isFolder( i ) and not i.endswith("/pythonpath"):
641 log.debug( "adding DirBrowseNode " + i )
642 browseNodeList.append( DirBrowseNode( self.provCtx, i[i.rfind("/")+1:len(i)],i))
643 return tuple( browseNodeList )
644 except Exception as e:
645 text = lastException2String()
646 log.error( "DirBrowseNode error: " + str(e) + " while evaluating " + self.rootUrl)
647 log.error( text)
648 return ()
650 def hasChildNodes( self ):
651 return True
653 def getType( self ):
654 return CONTAINER
656 def getScript( self, uri ):
657 log.debug( "DirBrowseNode getScript " + uri + " invoked" )
658 raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 )
661 class ManifestHandler( XDocumentHandler, unohelper.Base ):
662 def __init__( self, rootUrl ):
663 self.rootUrl = rootUrl
665 def startDocument( self ):
666 self.urlList = []
668 def endDocument( self ):
669 pass
671 def startElement( self , name, attlist):
672 if name == "manifest:file-entry":
673 if attlist.getValueByName( "manifest:media-type" ) == "application/vnd.sun.star.framework-script":
674 self.urlList.append(
675 self.rootUrl + "/" + attlist.getValueByName( "manifest:full-path" ) )
677 def endElement( self, name ):
678 pass
680 def characters ( self, chars ):
681 pass
683 def ignoreableWhitespace( self, chars ):
684 pass
686 def setDocumentLocator( self, locator ):
687 pass
689 def isPyFileInPath( sfa, path ):
690 ret = False
691 contents = sfa.getFolderContents( path, True )
692 for i in contents:
693 if sfa.isFolder(i):
694 ret = isPyFileInPath(sfa,i)
695 else:
696 if i.endswith(".py"):
697 ret = True
698 if ret:
699 break
700 return ret
702 # extracts META-INF directory from
703 def getPathsFromPackage( rootUrl, sfa ):
704 ret = ()
705 try:
706 fileUrl = rootUrl + "/META-INF/manifest.xml"
707 inputStream = sfa.openFileRead( fileUrl )
708 parser = uno.getComponentContext().ServiceManager.createInstance( "com.sun.star.xml.sax.Parser" )
709 handler = ManifestHandler( rootUrl )
710 parser.setDocumentHandler( handler )
711 parser.parseStream( InputSource( inputStream , "", fileUrl, fileUrl ) )
712 for i in tuple(handler.urlList):
713 if not isPyFileInPath( sfa, i ):
714 handler.urlList.remove(i)
715 ret = tuple( handler.urlList )
716 except UnoException:
717 text = lastException2String()
718 log.debug( "getPathsFromPackage " + fileUrl + " Exception: " +text )
719 pass
720 return ret
723 class Package:
724 def __init__( self, paths, transientPathElement ):
725 self.paths = paths
726 self.transientPathElement = transientPathElement
728 class DummyInteractionHandler( unohelper.Base, XInteractionHandler ):
729 def __init__( self ):
730 pass
731 def handle( self, event):
732 log.debug( "pythonscript: DummyInteractionHandler.handle " + str( event ) )
734 class DummyProgressHandler( unohelper.Base, XProgressHandler ):
735 def __init__( self ):
736 pass
738 def push( self,status ):
739 log.debug( "pythonscript: DummyProgressHandler.push " + str( status ) )
740 def update( self,status ):
741 log.debug( "pythonscript: DummyProgressHandler.update " + str( status ) )
742 def pop( self, event ):
743 log.debug( "pythonscript: DummyProgressHandler.push " + str( event ) )
745 class CommandEnvironment(unohelper.Base, XCommandEnvironment):
746 def __init__( self ):
747 self.progressHandler = DummyProgressHandler()
748 self.interactionHandler = DummyInteractionHandler()
749 def getInteractionHandler( self ):
750 return self.interactionHandler
751 def getProgressHandler( self ):
752 return self.progressHandler
754 #maybe useful for debugging purposes
755 #class ModifyListener( unohelper.Base, XModifyListener ):
756 # def __init__( self ):
757 # pass
758 # def modified( self, event ):
759 # log.debug( "pythonscript: ModifyListener.modified " + str( event ) )
760 # def disposing( self, event ):
761 # log.debug( "pythonscript: ModifyListener.disposing " + str( event ) )
763 def getModelFromDocUrl(ctx, url):
764 """Get document model from document url."""
765 doc = None
766 args = ("Local", "Office")
767 ucb = ctx.getServiceManager().createInstanceWithArgumentsAndContext(
768 "com.sun.star.ucb.UniversalContentBroker", args, ctx)
769 identifier = ucb.createContentIdentifier(url)
770 content = ucb.queryContent(identifier)
771 p = Property()
772 p.Name = "DocumentModel"
773 p.Handle = -1
775 c = Command()
776 c.Handle = -1
777 c.Name = "getPropertyValues"
778 c.Argument = uno.Any("[]com.sun.star.beans.Property", (p,))
780 env = CommandEnvironment()
781 try:
782 ret = content.execute(c, 0, env)
783 doc = ret.getObject(1, None)
784 except Exception as e:
785 log.isErrorLevel() and log.error("getModelFromDocUrl: %s" % url)
786 return doc
788 def mapStorageType2PackageContext( storageType ):
789 ret = storageType
790 if( storageType == "share:uno_packages" ):
791 ret = "shared"
792 if( storageType == "user:uno_packages" ):
793 ret = "user"
794 return ret
796 def getPackageName2PathMap( sfa, storageType ):
797 ret = {}
798 packageManagerFactory = uno.getComponentContext().getValueByName(
799 "/singletons/com.sun.star.deployment.thePackageManagerFactory" )
800 packageManager = packageManagerFactory.getPackageManager(
801 mapStorageType2PackageContext(storageType))
802 # packageManager.addModifyListener( ModifyListener() )
803 log.debug( "pythonscript: getPackageName2PathMap start getDeployedPackages" )
804 packages = packageManager.getDeployedPackages(
805 packageManager.createAbortChannel(), CommandEnvironment( ) )
806 log.debug( "pythonscript: getPackageName2PathMap end getDeployedPackages (" + str(len(packages))+")" )
808 for i in packages:
809 log.debug( "inspecting package " + i.Name + "("+i.Identifier.Value+")" )
810 transientPathElement = penultimateElement( i.URL )
811 j = expandUri( i.URL )
812 paths = getPathsFromPackage( j, sfa )
813 if len( paths ) > 0:
814 # map package name to url, we need this later
815 log.isErrorLevel() and log.error( "adding Package " + transientPathElement + " " + str( paths ) )
816 ret[ lastElement( j ) ] = Package( paths, transientPathElement )
817 return ret
819 def penultimateElement( aStr ):
820 lastSlash = aStr.rindex("/")
821 penultimateSlash = aStr.rindex("/",0,lastSlash-1)
822 return aStr[ penultimateSlash+1:lastSlash ]
824 def lastElement( aStr):
825 return aStr[ aStr.rfind( "/" )+1:len(aStr)]
827 class PackageBrowseNode( unohelper.Base, XBrowseNode ):
828 def __init__( self, provCtx, name, rootUrl ):
829 self.provCtx = provCtx
830 self.name = name
831 self.rootUrl = rootUrl
833 def getName( self ):
834 return self.name
836 def getChildNodes( self ):
837 items = self.provCtx.mapPackageName2Path.items()
838 browseNodeList = []
839 for i in items:
840 if len( i[1].paths ) == 1:
841 browseNodeList.append(
842 DirBrowseNode( self.provCtx, i[0], i[1].paths[0] ))
843 else:
844 for j in i[1].paths:
845 browseNodeList.append(
846 DirBrowseNode( self.provCtx, i[0]+"."+lastElement(j), j ) )
847 return tuple( browseNodeList )
849 def hasChildNodes( self ):
850 return len( self.provCtx.mapPackageName2Path ) > 0
852 def getType( self ):
853 return CONTAINER
855 def getScript( self, uri ):
856 log.debug( "DirBrowseNode getScript " + uri + " invoked" )
857 raise IllegalArgumentException( "PackageBrowseNode couldn't instantiate script " + uri , self , 0 )
862 class PythonScript( unohelper.Base, XScript ):
863 def __init__( self, func, mod ):
864 self.func = func
865 self.mod = mod
866 def invoke(self, args, out, outindex ):
867 log.debug( "PythonScript.invoke " + str( args ) )
868 try:
869 ret = self.func( *args )
870 except UnoException as e:
871 # UNO Exception continue to fly ...
872 text = lastException2String()
873 complete = "Error during invoking function " + \
874 str(self.func.__name__) + " in module " + \
875 self.mod.__file__ + " (" + text + ")"
876 log.debug( complete )
877 # some people may beat me up for modifying the exception text,
878 # but otherwise office just shows
879 # the type name and message text with no more information,
880 # this is really bad for most users.
881 e.Message = e.Message + " (" + complete + ")"
882 raise
883 except Exception as e:
884 # General python exception are converted to uno RuntimeException
885 text = lastException2String()
886 complete = "Error during invoking function " + \
887 str(self.func.__name__) + " in module " + \
888 self.mod.__file__ + " (" + text + ")"
889 log.debug( complete )
890 raise RuntimeException( complete , self )
891 log.debug( "PythonScript.invoke ret = " + str( ret ) )
892 return ret, (), ()
894 def expandUri( uri ):
895 if uri.startswith( "vnd.sun.star.expand:" ):
896 uri = uri.replace( "vnd.sun.star.expand:", "",1)
897 uri = uno.getComponentContext().getByName(
898 "/singletons/com.sun.star.util.theMacroExpander" ).expandMacros( uri )
899 if uri.startswith( "file:" ):
900 uri = uno.absolutize("",uri) # necessary to get rid of .. in uri
901 return uri
903 #--------------------------------------------------------------
904 class PythonScriptProvider( unohelper.Base, XBrowseNode, XScriptProvider, XNameContainer):
905 def __init__( self, ctx, *args ):
906 if log.isDebugLevel():
907 mystr = ""
908 for i in args:
909 if len(mystr) > 0:
910 mystr = mystr +","
911 mystr = mystr + str(i)
912 log.debug( "Entering PythonScriptProvider.ctor" + mystr )
914 doc = None
915 inv = None
916 storageType = ""
918 if isinstance(args[0],unicode ):
919 storageType = args[0]
920 if storageType.startswith( "vnd.sun.star.tdoc" ):
921 doc = getModelFromDocUrl(ctx, storageType)
922 else:
923 inv = args[0]
924 try:
925 doc = inv.ScriptContainer
926 content = ctx.getServiceManager().createInstanceWithContext(
927 "com.sun.star.frame.TransientDocumentsDocumentContentFactory",
928 ctx).createDocumentContent(doc)
929 storageType = content.getIdentifier().getContentIdentifier()
930 except Exception as e:
931 text = lastException2String()
932 log.error( text )
934 isPackage = storageType.endswith( ":uno_packages" )
936 try:
937 # urlHelper = ctx.ServiceManager.createInstanceWithArgumentsAndContext(
938 # "com.sun.star.script.provider.ScriptURIHelper", (LANGUAGENAME, storageType), ctx)
939 urlHelper = MyUriHelper( ctx, storageType )
940 log.debug( "got urlHelper " + str( urlHelper ) )
942 rootUrl = expandUri( urlHelper.getRootStorageURI() )
943 log.debug( storageType + " transformed to " + rootUrl )
945 ucbService = "com.sun.star.ucb.SimpleFileAccess"
946 sfa = ctx.ServiceManager.createInstanceWithContext( ucbService, ctx )
947 if not sfa:
948 log.debug("PythonScriptProvider couldn't instantiate " +ucbService)
949 raise RuntimeException(
950 "PythonScriptProvider couldn't instantiate " +ucbService, self)
951 self.provCtx = ProviderContext(
952 storageType, sfa, urlHelper, ScriptContext( uno.getComponentContext(), doc, inv ) )
953 if isPackage:
954 mapPackageName2Path = getPackageName2PathMap( sfa, storageType )
955 self.provCtx.setPackageAttributes( mapPackageName2Path , rootUrl )
956 self.dirBrowseNode = PackageBrowseNode( self.provCtx, LANGUAGENAME, rootUrl )
957 else:
958 self.dirBrowseNode = DirBrowseNode( self.provCtx, LANGUAGENAME, rootUrl )
960 except Exception as e:
961 text = lastException2String()
962 log.debug( "PythonScriptProvider could not be instantiated because of : " + text )
963 raise e
965 def getName( self ):
966 return self.dirBrowseNode.getName()
968 def getChildNodes( self ):
969 return self.dirBrowseNode.getChildNodes()
971 def hasChildNodes( self ):
972 return self.dirBrowseNode.hasChildNodes()
974 def getType( self ):
975 return self.dirBrowseNode.getType()
977 def getScript( self, uri ):
978 log.debug( "DirBrowseNode getScript " + uri + " invoked" )
980 raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 )
982 def getScript( self, scriptUri ):
983 try:
984 log.debug( "getScript " + scriptUri + " invoked")
986 storageUri = self.provCtx.getStorageUrlFromPersistentUrl(
987 self.provCtx.uriHelper.getStorageURI(scriptUri) );
988 log.debug( "getScript: storageUri = " + storageUri)
989 fileUri = storageUri[0:storageUri.find( "$" )]
990 funcName = storageUri[storageUri.find( "$" )+1:len(storageUri)]
992 mod = self.provCtx.getModuleByUrl( fileUri )
993 log.debug( " got mod " + str(mod) )
995 func = mod.__dict__[ funcName ]
997 log.debug( "got func " + str( func ) )
998 return PythonScript( func, mod )
999 except:
1000 text = lastException2String()
1001 log.error( text )
1002 raise ScriptFrameworkErrorException( text, self, scriptUri, LANGUAGENAME, 0 )
1005 # XServiceInfo
1006 def getSupportedServices( self ):
1007 return g_ImplementationHelper.getSupportedServices(g_implName)
1009 def supportsService( self, ServiceName ):
1010 return g_ImplementationHelper.supportsService( g_implName, ServiceName )
1012 def getImplementationName(self):
1013 return g_implName
1015 def getByName( self, name ):
1016 log.debug( "getByName called" + str( name ))
1017 return None
1020 def getElementNames( self ):
1021 log.debug( "getElementNames called")
1022 return ()
1024 def hasByName( self, name ):
1025 try:
1026 log.debug( "hasByName called " + str( name ))
1027 uri = expandUri(name)
1028 ret = self.provCtx.isUrlInPackage( uri )
1029 log.debug( "hasByName " + uri + " " +str( ret ) )
1030 return ret
1031 except:
1032 text = lastException2String()
1033 log.debug( "Error in hasByName:" + text )
1034 return False
1036 def removeByName( self, name ):
1037 log.debug( "removeByName called" + str( name ))
1038 uri = expandUri( name )
1039 if self.provCtx.isUrlInPackage( uri ):
1040 self.provCtx.removePackageByUrl( uri )
1041 else:
1042 log.debug( "removeByName unknown uri " + str( name ) + ", ignoring" )
1043 raise NoSuchElementException( uri + "is not in package" , self )
1044 log.debug( "removeByName called" + str( uri ) + " successful" )
1046 def insertByName( self, name, value ):
1047 log.debug( "insertByName called " + str( name ) + " " + str( value ))
1048 uri = expandUri( name )
1049 if isPyFileInPath( self.provCtx.sfa, uri ):
1050 self.provCtx.addPackageByUrl( uri )
1051 else:
1052 # package is no python package ...
1053 log.debug( "insertByName: no python files in " + str( uri ) + ", ignoring" )
1054 raise IllegalArgumentException( uri + " does not contain .py files", self, 1 )
1055 log.debug( "insertByName called " + str( uri ) + " successful" )
1057 def replaceByName( self, name, value ):
1058 log.debug( "replaceByName called " + str( name ) + " " + str( value ))
1059 uri = expandUri( name )
1060 self.removeByName( name )
1061 self.insertByName( name, value )
1062 log.debug( "replaceByName called" + str( uri ) + " successful" )
1064 def getElementType( self ):
1065 log.debug( "getElementType called" )
1066 return uno.getTypeByName( "void" )
1068 def hasElements( self ):
1069 log.debug( "hasElements got called")
1070 return False
1072 g_ImplementationHelper.addImplementation( \
1073 PythonScriptProvider,g_implName, \
1074 ("com.sun.star.script.provider.LanguageScriptProvider",
1075 "com.sun.star.script.provider.ScriptProviderFor"+ LANGUAGENAME,),)
1078 log.debug( "pythonscript finished intializing" )