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
30 # Python 3 compatibility
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 #-------------------------------------------------------------------
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
)
64 def logLevel2String( level
):
66 if level
== LogLevel
.ERROR
:
68 elif level
>= LogLevel
.DEBUG
:
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" )
83 print("Exception during creation of pythonscript logfile: "+ lastException2String() + "\n, delagating log to stdout\n")
86 class Logger(LogLevel
):
87 def __init__(self
, 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
:
110 logLevel2String( level
) +
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
153 def readTextFromStream( inputStream
):
155 code
= uno
.ByteSequence( "" )
157 read
,out
= inputStream
.readBytes( None , BLOCK_SIZE
)
159 if read
< BLOCK_SIZE
:
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") ):
170 """ definition: storageURI is the system dependent, absolute file url, where the script is stored on disk
171 scriptURI is the system independent uri
175 def __init__( self
, ctx
, location
):
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"
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
+ "'"
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
)
211 def scriptURI2StorageUri( self
, scriptURI
):
213 myUri
= self
.m_uriRefFac
.parse(scriptURI
)
214 ret
= self
.m_baseUri
+ "/" + myUri
.getName().replace( "|", "/" )
215 log
.debug( "converting scriptURI="+scriptURI
+ " to storageURI=" + 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 )
226 def __init__( self
, lastRead
, module
):
227 self
.lastRead
= lastRead
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"):
242 code
= code
.replace(b
"\r", b
"")
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
):
268 def getDocument(self
):
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
):
280 def getInvocationContext(self
):
283 #----------------------------------
284 # Global Module Administration
285 # does not fit together with script
286 # engine lifetime management
287 #----------------------------------
288 #g_scriptContext = ScriptContext( uno.getComponentContext(), None )
290 #def getModuleByUrl( url, sfa ):
291 # entry = g_modules.get(url)
293 # lastRead = sfa.getDateTimeModified( url )
295 # if hasChanged( entry.lastRead, lastRead ):
296 # log.debug("file " + url + " has changed, reloading")
301 # log.debug( "opening >" + url + "<" )
303 # code = readTextFromStream( sfa.openFileRead( url ) )
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
318 self
.uriHelper
= uriHelper
319 self
.scriptContext
= scriptContext
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()
337 if url
in i
[1].paths
:
338 self
.mapPackageName2Path
.pop(i
[0])
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
, )
349 package
= Package( (url
,), transientPart
)
350 self
.mapPackageName2Path
[ packageName
] = package
352 def isUrlInPackage( self
, url
):
353 values
= self
.mapPackageName2Path
.values()
355 # print ("checking " + url + " in " + str(i.paths))
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
369 pos
= len( self
.rootUrl
) +1
370 ret
= url
[0:pos
]+url
[url
.find("/",pos
)+1:len(url
)]
371 log
.debug( "getPersistentUrlFromStorageUrl " + url
+ " -> "+ ret
)
374 def getStorageUrlFromPersistentUrl( self
, url
):
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
)
384 def getFuncsByUrl( self
, url
):
385 src
= readTextFromStream( self
.sfa
.openFileRead( url
) )
386 checkForPythonPathBesideScript( url
[0:url
.rfind('/')] )
387 src
= ensureSourceState( src
)
390 code
= ast
.parse( src
)
392 log
.isDebugLevel() and log
.debug( "pythonscript: getFuncsByUrl: exception while parsing: " + lastException2String())
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
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
425 def getModuleByUrl( self
, url
):
426 entry
= self
.modules
.get(url
)
428 lastRead
= self
.sfa
.getDateTimeModified( url
)
430 if hasChanged( entry
.lastRead
, lastRead
):
431 log
.debug( "file " + url
+ " has changed, reloading" )
436 log
.debug( "opening >" + url
+ "<" )
438 src
= readTextFromStream( self
.sfa
.openFileRead( url
) )
439 checkForPythonPathBesideScript( url
[0:url
.rfind('/')] )
440 src
= ensureSourceState( src
)
443 entry
= ModuleEntry( lastRead
, imp
.new_module("ooo_script_framework") )
444 entry
.module
.__dict
__[GLOBAL_SCRIPTCONTEXT_NAME
] = self
.scriptContext
447 if url
.startswith( "file:" ):
448 code
= compile( src
, encfile(uno
.fileUrlToSystemPath( url
) ), "exec" )
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
) )
457 #--------------------------------------------------
458 def isScript( candidate
):
460 if isinstance( candidate
, type(isScript
) ):
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
475 def getChildNodes(self
):
478 def hasChildNodes(self
):
484 def getPropertyValue( self
, name
):
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
) )
495 log
.error( "ScriptBrowseNode.getPropertyValue error " + lastException2String())
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 " )
505 def getIntrospection( self
):
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(
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()
532 def actionPerformed( self
, event
):
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 )
542 values
= mod
.__dict
__.values()
549 elif event
.ActionCommand
== "Save":
550 toWrite
= uno
.ByteSequence(
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
)
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)
564 # TODO: add an error box here !
565 log
.error( lastException2String() )
568 def setValue( self
, name
, value
):
571 def getValue( self
, name
):
574 def hasMethod( self
, name
):
577 def hasProperty( self
, name
):
581 #-------------------------------------------------------
582 class FileBrowseNode( unohelper
.Base
, XBrowseNode
):
583 def __init__( self
, provCtx
, uri
, name
):
584 self
.provCtx
= provCtx
587 self
.funcnames
= None
592 def getChildNodes(self
):
595 self
.funcnames
= self
.provCtx
.getFuncsByUrl( self
.uri
)
598 for i
in self
.funcnames
:
599 scriptNodeList
.append(
601 self
.provCtx
, self
.uri
, self
.name
, i
))
602 ret
= tuple( scriptNodeList
)
603 log
.debug( "returning " +str(len(ret
)) + " ScriptChildNodes on " + self
.uri
)
605 text
= lastException2String()
606 log
.error( "Error while evaluating " + self
.uri
+ ":" + text
)
610 def hasChildNodes(self
):
612 return len(self
.getChildNodes()) > 0
621 class DirBrowseNode( unohelper
.Base
, XBrowseNode
):
622 def __init__( self
, provCtx
, name
, rootUrl
):
623 self
.provCtx
= provCtx
625 self
.rootUrl
= rootUrl
630 def getChildNodes( self
):
632 log
.debug( "DirBrowseNode.getChildNodes called for " + self
.rootUrl
)
633 contents
= self
.provCtx
.sfa
.getFolderContents( self
.rootUrl
, True )
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
)
650 def hasChildNodes( self
):
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
):
668 def endDocument( self
):
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":
675 self
.rootUrl
+ "/" + attlist
.getValueByName( "manifest:full-path" ) )
677 def endElement( self
, name
):
680 def characters ( self
, chars
):
683 def ignoreableWhitespace( self
, chars
):
686 def setDocumentLocator( self
, locator
):
689 def isPyFileInPath( sfa
, path
):
691 contents
= sfa
.getFolderContents( path
, True )
694 ret
= isPyFileInPath(sfa
,i
)
696 if i
.endswith(".py"):
702 # extracts META-INF directory from
703 def getPathsFromPackage( rootUrl
, sfa
):
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
)
717 text
= lastException2String()
718 log
.debug( "getPathsFromPackage " + fileUrl
+ " Exception: " +text
)
724 def __init__( self
, paths
, transientPathElement
):
726 self
.transientPathElement
= transientPathElement
728 class DummyInteractionHandler( unohelper
.Base
, XInteractionHandler
):
729 def __init__( self
):
731 def handle( self
, event
):
732 log
.debug( "pythonscript: DummyInteractionHandler.handle " + str( event
) )
734 class DummyProgressHandler( unohelper
.Base
, XProgressHandler
):
735 def __init__( self
):
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 ):
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."""
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
)
772 p
.Name
= "DocumentModel"
777 c
.Name
= "getPropertyValues"
778 c
.Argument
= uno
.Any("[]com.sun.star.beans.Property", (p
,))
780 env
= CommandEnvironment()
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
)
788 def mapStorageType2PackageContext( storageType
):
790 if( storageType
== "share:uno_packages" ):
792 if( storageType
== "user:uno_packages" ):
796 def getPackageName2PathMap( sfa
, storageType
):
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
))+")" )
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
)
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
)
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
831 self
.rootUrl
= rootUrl
836 def getChildNodes( self
):
837 items
= self
.provCtx
.mapPackageName2Path
.items()
840 if len( i
[1].paths
) == 1:
841 browseNodeList
.append(
842 DirBrowseNode( self
.provCtx
, i
[0], i
[1].paths
[0] ))
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
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
):
866 def invoke(self
, args
, out
, outindex
):
867 log
.debug( "PythonScript.invoke " + str( args
) )
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
+ ")"
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
) )
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
903 #--------------------------------------------------------------
904 class PythonScriptProvider( unohelper
.Base
, XBrowseNode
, XScriptProvider
, XNameContainer
):
905 def __init__( self
, ctx
, *args
):
906 if log
.isDebugLevel():
911 mystr
= mystr
+ str(i
)
912 log
.debug( "Entering PythonScriptProvider.ctor" + mystr
)
918 if isinstance(args
[0],unicode ):
919 storageType
= args
[0]
920 if storageType
.startswith( "vnd.sun.star.tdoc" ):
921 doc
= getModelFromDocUrl(ctx
, storageType
)
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()
934 isPackage
= storageType
.endswith( ":uno_packages" )
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
)
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
) )
954 mapPackageName2Path
= getPackageName2PathMap( sfa
, storageType
)
955 self
.provCtx
.setPackageAttributes( mapPackageName2Path
, rootUrl
)
956 self
.dirBrowseNode
= PackageBrowseNode( self
.provCtx
, LANGUAGENAME
, rootUrl
)
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
)
966 return self
.dirBrowseNode
.getName()
968 def getChildNodes( self
):
969 return self
.dirBrowseNode
.getChildNodes()
971 def hasChildNodes( self
):
972 return self
.dirBrowseNode
.hasChildNodes()
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
):
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
)
1000 text
= lastException2String()
1002 raise ScriptFrameworkErrorException( text
, self
, scriptUri
, LANGUAGENAME
, 0 )
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
):
1015 def getByName( self
, name
):
1016 log
.debug( "getByName called" + str( name
))
1020 def getElementNames( self
):
1021 log
.debug( "getElementNames called")
1024 def hasByName( self
, name
):
1026 log
.debug( "hasByName called " + str( name
))
1027 uri
= expandUri(name
)
1028 ret
= self
.provCtx
.isUrlInPackage( uri
)
1029 log
.debug( "hasByName " + uri
+ " " +str( ret
) )
1032 text
= lastException2String()
1033 log
.debug( "Error in hasByName:" + text
)
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
)
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
)
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")
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" )