1 # -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
3 # This file is part of the LibreOffice project.
5 # This Source Code Form is subject to the terms of the Mozilla Public
6 # License, v. 2.0. If a copy of the MPL was not distributed with this
7 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 # This file incorporates work covered by the following license notice:
11 # Licensed to the Apache Software Foundation (ASF) under one or more
12 # contributor license agreements. See the NOTICE file distributed
13 # with this work for additional information regarding copyright
14 # ownership. The ASF licenses this file to you under the Apache
15 # License, Version 2.0 (the "License"); you may not use this file
16 # except in compliance with the License. You may obtain a copy of
17 # the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 # XScript implementation for python
28 from com
.sun
.star
.uri
.RelativeUriExcessParentSegments
import RETAIN
29 from urllib
.parse
import unquote
32 NONE
= 0 # production level
33 ERROR
= 1 # for script developers
34 DEBUG
= 2 # for script framework developers
36 PYSCRIPT_LOG_ENV
= "PYSCRIPT_LOG_LEVEL"
37 PYSCRIPT_LOG_STDOUT_ENV
= "PYSCRIPT_LOG_STDOUT"
39 # Configuration ----------------------------------------------------
40 LogLevel
.use
= LogLevel
.NONE
41 if os
.environ
.get(PYSCRIPT_LOG_ENV
) == "ERROR":
42 LogLevel
.use
= LogLevel
.ERROR
43 elif os
.environ
.get(PYSCRIPT_LOG_ENV
) == "DEBUG":
44 LogLevel
.use
= LogLevel
.DEBUG
46 # True, writes to stdout (difficult on windows)
47 # False, writes to user/Scripts/python/log.txt
48 LOG_STDOUT
= os
.environ
.get(PYSCRIPT_LOG_STDOUT_ENV
, "1") != "0"
50 ENABLE_EDIT_DIALOG
=False # offers a minimal editor for editing.
51 #-------------------------------------------------------------------
54 return uni
.encode( sys
.getfilesystemencoding())
56 def lastException2String():
57 (excType
,excInstance
,excTraceback
) = sys
.exc_info()
58 ret
= str(excType
) + ": "+str(excInstance
) + "\n" + \
59 uno
._uno
_extract
_printable
_stacktrace
( excTraceback
)
62 def logLevel2String( level
):
64 if level
== LogLevel
.ERROR
:
66 elif level
>= LogLevel
.DEBUG
:
74 pathSubst
= uno
.getComponentContext().ServiceManager
.createInstance(
75 "com.sun.star.util.PathSubstitution" )
76 userInstallation
= pathSubst
.getSubstituteVariableValue( "user" )
77 if len( userInstallation
) > 0:
78 systemPath
= uno
.fileUrlToSystemPath( userInstallation
+ "/Scripts/python/log.txt" )
79 ret
= open( systemPath
, "a" )
81 print("Exception during creation of pythonscript logfile: "+ lastException2String() + "\n, delegating log to stdout\n")
84 class Logger(LogLevel
):
85 def __init__(self
, target
):
88 def isDebugLevel( self
):
89 return self
.use
>= self
.DEBUG
91 def debug( self
, msg
):
92 if self
.isDebugLevel():
93 self
.log( self
.DEBUG
, msg
)
95 def isErrorLevel( self
):
96 return self
.use
>= self
.ERROR
98 def error( self
, msg
):
99 if self
.isErrorLevel():
100 self
.log( self
.ERROR
, msg
)
102 def log( self
, level
, msg
):
103 if self
.use
>= level
:
108 logLevel2String( level
) +
114 print("Error during writing to stdout: " +lastException2String() + "\n")
116 log
= Logger( getLogTarget() )
118 log
.debug( "pythonscript loading" )
120 #from com.sun.star.lang import typeOfXServiceInfo, typeOfXTypeProvider
121 from com
.sun
.star
.uno
import RuntimeException
122 from com
.sun
.star
.lang
import IllegalArgumentException
123 from com
.sun
.star
.container
import NoSuchElementException
124 from com
.sun
.star
.lang
import XServiceInfo
125 from com
.sun
.star
.io
import IOException
126 from com
.sun
.star
.ucb
import CommandAbortedException
, XCommandEnvironment
, XProgressHandler
, Command
127 from com
.sun
.star
.task
import XInteractionHandler
128 from com
.sun
.star
.beans
import XPropertySet
, Property
129 from com
.sun
.star
.container
import XNameContainer
130 from com
.sun
.star
.xml
.sax
import XDocumentHandler
, InputSource
131 from com
.sun
.star
.uno
import Exception as UnoException
132 from com
.sun
.star
.script
import XInvocation
133 from com
.sun
.star
.awt
import XActionListener
135 from com
.sun
.star
.script
.provider
import XScriptProvider
, XScript
, XScriptContext
, ScriptFrameworkErrorException
136 from com
.sun
.star
.script
.browse
import XBrowseNode
137 from com
.sun
.star
.script
.browse
.BrowseNodeTypes
import SCRIPT
, CONTAINER
, ROOT
138 from com
.sun
.star
.util
import XModifyListener
140 LANGUAGENAME
= "Python"
141 GLOBAL_SCRIPTCONTEXT_NAME
= "XSCRIPTCONTEXT"
142 CALLABLE_CONTAINER_NAME
= "g_exportedScripts"
144 # pythonloader looks for a static g_ImplementationHelper variable
145 g_ImplementationHelper
= unohelper
.ImplementationHelper()
146 g_implName
= "org.libreoffice.pyuno.LanguageScriptProviderFor"+LANGUAGENAME
151 def readTextFromStream( inputStream
):
153 code
= uno
.ByteSequence( b
"" )
155 read
,out
= inputStream
.readBytes( None , BLOCK_SIZE
)
157 if read
< BLOCK_SIZE
:
161 def toIniName( str ):
162 if platform
.system() == "Windows":
168 """ definition: storageURI is the system dependent, absolute file url, where the script is stored on disk
169 scriptURI is the system independent uri
173 def __init__( self
, ctx
, location
):
176 { "share" : "vnd.sun.star.expand:$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/Scripts/python" , \
177 "share:uno_packages" : "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages", \
178 "user" : "vnd.sun.star.expand:${$BRAND_INI_DIR/" + toIniName( "bootstrap") + "::UserInstallation}/user/Scripts/python" , \
179 "user:uno_packages" : "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages" }
180 self
.m_uriRefFac
= ctx
.ServiceManager
.createInstanceWithContext("com.sun.star.uri.UriReferenceFactory",ctx
)
181 if location
.startswith( "vnd.sun.star.tdoc" ):
182 self
.m_baseUri
= location
+ "/Scripts/python"
183 self
.m_scriptUriLocation
= "document"
185 self
.m_baseUri
= expandUri( self
.s_UriMap
[location
] )
186 self
.m_scriptUriLocation
= location
187 log
.debug( "initialized urihelper with baseUri="+self
.m_baseUri
+ ",m_scriptUriLocation="+self
.m_scriptUriLocation
)
189 def getRootStorageURI( self
):
190 return self
.m_baseUri
192 def getStorageURI( self
, scriptURI
):
193 return self
.scriptURI2StorageUri(scriptURI
)
195 def getScriptURI( self
, storageURI
):
196 return self
.storageURI2ScriptUri(storageURI
)
198 def storageURI2ScriptUri( self
, storageURI
):
199 if not storageURI
.startswith( self
.m_baseUri
):
200 message
= "pythonscript: storage uri '" + storageURI
+ "' not in base uri '" + self
.m_baseUri
+ "'"
202 raise RuntimeException( message
, self
.ctx
)
204 ret
= "vnd.sun.star.script:" + \
205 storageURI
[len(self
.m_baseUri
)+1:].replace("/","|") + \
206 "?language=" + LANGUAGENAME
+ "&location=" + self
.m_scriptUriLocation
207 log
.debug( "converting storageURI="+storageURI
+ " to scriptURI=" + ret
)
210 def scriptURI2StorageUri( self
, scriptURI
):
212 # base path to the python script location
213 sBaseUri
= self
.m_baseUri
+ "/"
214 xBaseUri
= self
.m_uriRefFac
.parse(sBaseUri
)
216 # path to the .py file + "$functionname, arguments, etc
217 xStorageUri
= self
.m_uriRefFac
.parse(scriptURI
)
218 # getName will apply url-decoding to the name, so encode back
219 sStorageUri
= xStorageUri
.getName().replace("%", "%25")
220 sStorageUri
= sStorageUri
.replace( "|", "/" )
222 # path to the .py file, relative to the base
223 funcNameStart
= sStorageUri
.find("$")
224 if funcNameStart
!= -1:
225 sFileUri
= sStorageUri
[0:funcNameStart
]
226 sFuncName
= sStorageUri
[funcNameStart
+1:]
228 sFileUri
= sStorageUri
230 xFileUri
= self
.m_uriRefFac
.parse(sFileUri
)
232 message
= "pythonscript: invalid relative uri '" + sFileUri
+ "'"
234 raise RuntimeException( message
, self
.ctx
)
236 if not xFileUri
.hasRelativePath():
237 message
= "pythonscript: an absolute uri is invalid '" + sFileUri
+ "'"
239 raise RuntimeException( message
, self
.ctx
)
241 # absolute path to the .py file
242 xAbsScriptUri
= self
.m_uriRefFac
.makeAbsolute(xBaseUri
, xFileUri
, True, RETAIN
)
243 sAbsScriptUri
= xAbsScriptUri
.getUriReference()
245 # ensure py file is under the base path
246 if not sAbsScriptUri
.startswith(sBaseUri
):
247 message
= "pythonscript: storage uri '" + sAbsScriptUri
+ "' not in base uri '" + self
.m_baseUri
+ "'"
249 raise RuntimeException( message
, self
.ctx
)
252 if funcNameStart
!= -1:
253 ret
= ret
+ "$" + sFuncName
254 log
.debug( "converting scriptURI="+scriptURI
+ " to storageURI=" + ret
)
256 except UnoException
as e
:
257 log
.error( "error during converting scriptURI="+scriptURI
+ ": " + e
.Message
)
258 raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + e
.Message
, self
.ctx
)
259 except Exception as e
:
260 log
.error( "error during converting scriptURI="+scriptURI
+ ": " + str(e
))
261 raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + str(e
), self
.ctx
)
265 def __init__( self
, lastRead
, module
):
266 self
.lastRead
= lastRead
269 def hasChanged( oldDate
, newDate
):
270 return newDate
.Year
> oldDate
.Year
or \
271 newDate
.Month
> oldDate
.Month
or \
272 newDate
.Day
> oldDate
.Day
or \
273 newDate
.Hours
> oldDate
.Hours
or \
274 newDate
.Minutes
> oldDate
.Minutes
or \
275 newDate
.Seconds
> oldDate
.Seconds
or \
276 newDate
.NanoSeconds
> oldDate
.NanoSeconds
278 def ensureSourceState( code
):
279 if code
.endswith(b
"\n"):
281 code
= code
.replace(b
"\r", b
"")
285 def checkForPythonPathBesideScript( url
):
286 if url
.startswith( "file:" ):
287 path
= unohelper
.fileUrlToSystemPath( url
+"/pythonpath.zip" );
288 log
.log( LogLevel
.DEBUG
, "checking for existence of " + path
)
289 if 1 == os
.access( encfile(path
), os
.F_OK
) and not path
in sys
.path
:
290 log
.log( LogLevel
.DEBUG
, "adding " + path
+ " to sys.path" )
291 sys
.path
.append( path
)
293 path
= unohelper
.fileUrlToSystemPath( url
+"/pythonpath" );
294 log
.log( LogLevel
.DEBUG
, "checking for existence of " + path
)
295 if 1 == os
.access( encfile(path
), os
.F_OK
) and not path
in sys
.path
:
296 log
.log( LogLevel
.DEBUG
, "adding " + path
+ " to sys.path" )
297 sys
.path
.append( path
)
300 class ScriptContext(unohelper
.Base
):
301 def __init__( self
, ctx
, doc
, inv
):
307 def getDocument(self
):
310 return self
.getDesktop().getCurrentComponent()
312 def getDesktop(self
):
313 return self
.ctx
.ServiceManager
.createInstanceWithContext(
314 "com.sun.star.frame.Desktop", self
.ctx
)
316 def getComponentContext(self
):
319 def getInvocationContext(self
):
322 #----------------------------------
323 # Global Module Administration
324 # does not fit together with script
325 # engine lifetime management
326 #----------------------------------
327 #g_scriptContext = ScriptContext( uno.getComponentContext(), None )
329 #def getModuleByUrl( url, sfa ):
330 # entry = g_modules.get(url)
332 # lastRead = sfa.getDateTimeModified( url )
334 # if hasChanged( entry.lastRead, lastRead ):
335 # log.debug("file " + url + " has changed, reloading")
340 # log.debug( "opening >" + url + "<" )
342 # code = readTextFromStream( sfa.openFileRead( url ) )
345 # entry = ModuleEntry( lastRead, types.ModuleType("ooo_script_framework") )
346 # entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = g_scriptContext
347 # entry.module.__file__ = url
348 # exec code in entry.module.__dict__
349 # g_modules[ url ] = entry
350 # log.debug( "mapped " + url + " to " + str( entry.module ) )
351 # return entry.module
353 class ProviderContext
:
354 def __init__( self
, storageType
, sfa
, uriHelper
, scriptContext
):
355 self
.storageType
= storageType
357 self
.uriHelper
= uriHelper
358 self
.scriptContext
= scriptContext
361 self
.mapPackageName2Path
= None
363 def getTransientPartFromUrl( self
, url
):
364 rest
= url
.replace( self
.rootUrl
, "",1 ).replace( "/","",1)
365 return rest
[0:rest
.find("/")]
367 def getPackageNameFromUrl( self
, url
):
368 rest
= url
.replace( self
.rootUrl
, "",1 ).replace( "/","",1)
369 start
= rest
.find("/") +1
370 return rest
[start
:rest
.find("/",start
)]
373 def removePackageByUrl( self
, url
):
374 items
= self
.mapPackageName2Path
.items()
376 if url
in i
[1].paths
:
377 self
.mapPackageName2Path
.pop(i
[0])
380 def addPackageByUrl( self
, url
):
381 packageName
= self
.getPackageNameFromUrl( url
)
382 transientPart
= self
.getTransientPartFromUrl( url
)
383 log
.debug( "addPackageByUrl : " + packageName
+ ", " + transientPart
+ "("+url
+")" + ", rootUrl="+self
.rootUrl
)
384 if packageName
in self
.mapPackageName2Path
:
385 package
= self
.mapPackageName2Path
[ packageName
]
386 package
.paths
= package
.paths
+ (url
, )
388 package
= Package( (url
,), transientPart
)
389 self
.mapPackageName2Path
[ packageName
] = package
391 def isUrlInPackage( self
, url
):
392 values
= self
.mapPackageName2Path
.values()
394 # print ("checking " + url + " in " + str(i.paths))
400 def setPackageAttributes( self
, mapPackageName2Path
, rootUrl
):
401 self
.mapPackageName2Path
= mapPackageName2Path
402 self
.rootUrl
= rootUrl
404 def getPersistentUrlFromStorageUrl( self
, url
):
405 # package name is the second directory
408 pos
= len( self
.rootUrl
) +1
409 ret
= url
[0:pos
]+url
[url
.find("/",pos
)+1:len(url
)]
410 log
.debug( "getPersistentUrlFromStorageUrl " + url
+ " -> "+ ret
)
413 def getStorageUrlFromPersistentUrl( self
, url
):
416 pos
= len(self
.rootUrl
)+1
417 packageName
= url
[pos
:url
.find("/",pos
+1)]
418 package
= self
.mapPackageName2Path
[ packageName
]
419 ret
= url
[0:pos
]+ package
.transientPathElement
+ "/" + url
[pos
:len(url
)]
420 log
.debug( "getStorageUrlFromPersistentUrl " + url
+ " -> "+ ret
)
423 def getFuncsByUrl( self
, url
):
424 src
= readTextFromStream( self
.sfa
.openFileRead( url
) )
425 checkForPythonPathBesideScript( url
[0:url
.rfind('/')] )
426 src
= ensureSourceState( src
)
429 code
= ast
.parse( src
)
431 log
.isDebugLevel() and log
.debug( "pythonscript: getFuncsByUrl: exception while parsing: " + lastException2String())
439 g_exportedScripts
= []
440 for node
in ast
.iter_child_nodes(code
):
441 if isinstance(node
, ast
.FunctionDef
):
442 allFuncs
.append(node
.name
)
443 elif isinstance(node
, ast
.Assign
):
444 for target
in node
.targets
:
446 identifier
= target
.id
447 except AttributeError:
450 if identifier
== "g_exportedScripts":
451 for value
in node
.value
.elts
:
452 g_exportedScripts
.append(value
.id)
453 return g_exportedScripts
456 # for node in code.node.nodes:
457 # if node.__class__.__name__ == 'Function':
458 # allFuncs.append(node.name)
459 # elif node.__class__.__name__ == 'Assign':
460 # for assignee in node.nodes:
461 # if assignee.name == 'g_exportedScripts':
462 # for item in node.expr.nodes:
463 # if item.__class__.__name__ == 'Name':
464 # g_exportedScripts.append(item.name)
465 # return g_exportedScripts
469 def getModuleByUrl( self
, url
):
470 entry
= self
.modules
.get(url
)
472 lastRead
= self
.sfa
.getDateTimeModified( url
)
474 if hasChanged( entry
.lastRead
, lastRead
):
475 log
.debug( "file " + url
+ " has changed, reloading" )
480 log
.debug( "opening >" + url
+ "<" )
482 src
= readTextFromStream( self
.sfa
.openFileRead( url
) )
483 checkForPythonPathBesideScript( url
[0:url
.rfind('/')] )
484 src
= ensureSourceState( src
)
487 entry
= ModuleEntry( lastRead
, types
.ModuleType("ooo_script_framework") )
488 entry
.module
.__dict
__[GLOBAL_SCRIPTCONTEXT_NAME
] = self
.scriptContext
491 if url
.startswith( "file:" ):
492 code
= compile( src
, encfile(uno
.fileUrlToSystemPath( url
) ), "exec" )
494 code
= compile( src
, url
, "exec" )
495 exec(code
, entry
.module
.__dict
__)
496 entry
.module
.__file
__ = url
497 self
.modules
[ url
] = entry
498 log
.debug( "mapped " + url
+ " to " + str( entry
.module
) )
501 #--------------------------------------------------
502 def isScript( candidate
):
504 if isinstance( candidate
, type(isScript
) ):
508 #-------------------------------------------------------
509 class ScriptBrowseNode( unohelper
.Base
, XBrowseNode
, XPropertySet
, XInvocation
, XActionListener
):
510 def __init__( self
, provCtx
, uri
, fileName
, funcName
):
511 self
.fileName
= fileName
512 self
.funcName
= funcName
513 self
.provCtx
= provCtx
519 def getChildNodes(self
):
522 def hasChildNodes(self
):
528 def getPropertyValue( self
, name
):
532 ret
= self
.provCtx
.uriHelper
.getScriptURI(
533 self
.provCtx
.getPersistentUrlFromStorageUrl( self
.uri
+ "$" + self
.funcName
) )
534 elif name
== "Editable" and ENABLE_EDIT_DIALOG
:
535 ret
= not self
.provCtx
.sfa
.isReadOnly( self
.uri
)
537 log
.debug( "ScriptBrowseNode.getPropertyValue called for " + name
+ ", returning " + str(ret
) )
539 log
.error( "ScriptBrowseNode.getPropertyValue error " + lastException2String())
543 def setPropertyValue( self
, name
, value
):
544 log
.debug( "ScriptBrowseNode.setPropertyValue called " + name
+ "=" +str(value
) )
545 def getPropertySetInfo( self
):
546 log
.debug( "ScriptBrowseNode.getPropertySetInfo called " )
549 def getIntrospection( self
):
552 def invoke( self
, name
, params
, outparamindex
, outparams
):
553 if name
== "Editable":
554 servicename
= "com.sun.star.awt.DialogProvider"
555 ctx
= self
.provCtx
.scriptContext
.getComponentContext()
556 dlgprov
= ctx
.ServiceManager
.createInstanceWithContext(
559 self
.editor
= dlgprov
.createDialog(
560 "vnd.sun.star.script:" +
561 "ScriptBindingLibrary.MacroEditor?location=application")
563 code
= readTextFromStream(self
.provCtx
.sfa
.openFileRead(self
.uri
))
564 code
= ensureSourceState( code
)
565 self
.editor
.getControl("EditorTextField").setText(code
)
567 self
.editor
.getControl("RunButton").setActionCommand("Run")
568 self
.editor
.getControl("RunButton").addActionListener(self
)
569 self
.editor
.getControl("SaveButton").setActionCommand("Save")
570 self
.editor
.getControl("SaveButton").addActionListener(self
)
572 self
.editor
.execute()
576 def actionPerformed( self
, event
):
578 if event
.ActionCommand
== "Run":
579 code
= self
.editor
.getControl("EditorTextField").getText()
580 code
= ensureSourceState( code
)
581 mod
= types
.ModuleType("ooo_script_framework")
582 mod
.__dict
__[GLOBAL_SCRIPTCONTEXT_NAME
] = self
.provCtx
.scriptContext
583 exec(code
, mod
.__dict
__)
584 values
= mod
.__dict
__.get( CALLABLE_CONTAINER_NAME
, None )
586 values
= mod
.__dict
__.values()
593 elif event
.ActionCommand
== "Save":
594 toWrite
= uno
.ByteSequence(
595 self
.editor
.getControl("EditorTextField").getText().encode(
596 sys
.getdefaultencoding()) )
597 copyUrl
= self
.uri
+ ".orig"
598 self
.provCtx
.sfa
.move( self
.uri
, copyUrl
)
599 out
= self
.provCtx
.sfa
.openFileWrite( self
.uri
)
600 out
.writeBytes( toWrite
)
602 self
.provCtx
.sfa
.kill( copyUrl
)
603 # log.debug("Save is not implemented yet")
604 # text = self.editor.getControl("EditorTextField").getText()
605 # log.debug("Would save: " + text)
607 # TODO: add an error box here!
608 log
.error( lastException2String() )
611 def setValue( self
, name
, value
):
614 def getValue( self
, name
):
617 def hasMethod( self
, name
):
620 def hasProperty( self
, name
):
624 #-------------------------------------------------------
625 class FileBrowseNode( unohelper
.Base
, XBrowseNode
):
626 def __init__( self
, provCtx
, uri
, name
):
627 self
.provCtx
= provCtx
630 self
.funcnames
= None
635 def getChildNodes(self
):
638 self
.funcnames
= self
.provCtx
.getFuncsByUrl( self
.uri
)
641 for i
in self
.funcnames
:
642 scriptNodeList
.append(
644 self
.provCtx
, self
.uri
, self
.name
, i
))
645 ret
= tuple( scriptNodeList
)
646 log
.debug( "returning " +str(len(ret
)) + " ScriptChildNodes on " + self
.uri
)
648 text
= lastException2String()
649 log
.error( "Error while evaluating " + self
.uri
+ ":" + text
)
653 def hasChildNodes(self
):
655 return len(self
.getChildNodes()) > 0
664 class DirBrowseNode( unohelper
.Base
, XBrowseNode
):
665 def __init__( self
, provCtx
, name
, rootUrl
):
666 self
.provCtx
= provCtx
668 self
.rootUrl
= rootUrl
673 def getChildNodes( self
):
675 log
.debug( "DirBrowseNode.getChildNodes called for " + self
.rootUrl
)
676 contents
= self
.provCtx
.sfa
.getFolderContents( self
.rootUrl
, True )
679 if i
.endswith( ".py" ):
680 log
.debug( "adding filenode " + i
)
681 browseNodeList
.append(
682 FileBrowseNode( self
.provCtx
, i
, i
[i
.rfind("/")+1:len(i
)-3] ) )
683 elif self
.provCtx
.sfa
.isFolder( i
) and not i
.endswith("/pythonpath"):
684 log
.debug( "adding DirBrowseNode " + i
)
685 browseNodeList
.append( DirBrowseNode( self
.provCtx
, i
[i
.rfind("/")+1:len(i
)],i
))
686 return tuple( browseNodeList
)
687 except Exception as e
:
688 text
= lastException2String()
689 log
.error( "DirBrowseNode error: " + str(e
) + " while evaluating " + self
.rootUrl
)
693 def hasChildNodes( self
):
699 def getScript( self
, uri
):
700 log
.debug( "DirBrowseNode getScript " + uri
+ " invoked" )
701 raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri
, self
, 0 )
704 class ManifestHandler( XDocumentHandler
, unohelper
.Base
):
705 def __init__( self
, rootUrl
):
706 self
.rootUrl
= rootUrl
708 def startDocument( self
):
711 def endDocument( self
):
714 def startElement( self
, name
, attlist
):
715 if name
== "manifest:file-entry":
716 if attlist
.getValueByName( "manifest:media-type" ) == "application/vnd.sun.star.framework-script":
718 self
.rootUrl
+ "/" + attlist
.getValueByName( "manifest:full-path" ) )
720 def endElement( self
, name
):
723 def characters ( self
, chars
):
726 def ignoreableWhitespace( self
, chars
):
729 def setDocumentLocator( self
, locator
):
732 def isPyFileInPath( sfa
, path
):
734 contents
= sfa
.getFolderContents( path
, True )
737 ret
= isPyFileInPath(sfa
,i
)
739 if i
.endswith(".py"):
745 # extracts META-INF directory from
746 def getPathsFromPackage( rootUrl
, sfa
):
749 fileUrl
= rootUrl
+ "/META-INF/manifest.xml"
750 inputStream
= sfa
.openFileRead( fileUrl
)
751 parser
= uno
.getComponentContext().ServiceManager
.createInstance( "com.sun.star.xml.sax.Parser" )
752 handler
= ManifestHandler( rootUrl
)
753 parser
.setDocumentHandler( handler
)
754 parser
.parseStream( InputSource( inputStream
, "", fileUrl
, fileUrl
) )
755 for i
in tuple(handler
.urlList
):
756 if not isPyFileInPath( sfa
, i
):
757 handler
.urlList
.remove(i
)
758 ret
= tuple( handler
.urlList
)
760 text
= lastException2String()
761 log
.debug( "getPathsFromPackage " + fileUrl
+ " Exception: " +text
)
767 def __init__( self
, paths
, transientPathElement
):
769 self
.transientPathElement
= transientPathElement
771 class DummyInteractionHandler( unohelper
.Base
, XInteractionHandler
):
772 def __init__( self
):
774 def handle( self
, event
):
775 log
.debug( "pythonscript: DummyInteractionHandler.handle " + str( event
) )
777 class DummyProgressHandler( unohelper
.Base
, XProgressHandler
):
778 def __init__( self
):
781 def push( self
,status
):
782 log
.debug( "pythonscript: DummyProgressHandler.push " + str( status
) )
783 def update( self
,status
):
784 log
.debug( "pythonscript: DummyProgressHandler.update " + str( status
) )
785 def pop( self
, event
):
786 log
.debug( "pythonscript: DummyProgressHandler.push " + str( event
) )
788 class CommandEnvironment(unohelper
.Base
, XCommandEnvironment
):
789 def __init__( self
):
790 self
.progressHandler
= DummyProgressHandler()
791 self
.interactionHandler
= DummyInteractionHandler()
792 def getInteractionHandler( self
):
793 return self
.interactionHandler
794 def getProgressHandler( self
):
795 return self
.progressHandler
797 #maybe useful for debugging purposes
798 #class ModifyListener( unohelper.Base, XModifyListener ):
799 # def __init__( self ):
801 # def modified( self, event ):
802 # log.debug( "pythonscript: ModifyListener.modified " + str( event ) )
803 # def disposing( self, event ):
804 # log.debug( "pythonscript: ModifyListener.disposing " + str( event ) )
806 def getModelFromDocUrl(ctx
, url
):
807 """Get document model from document url."""
809 args
= ("Local", "Office")
810 ucb
= ctx
.getServiceManager().createInstanceWithArgumentsAndContext(
811 "com.sun.star.ucb.UniversalContentBroker", args
, ctx
)
812 identifier
= ucb
.createContentIdentifier(url
)
813 content
= ucb
.queryContent(identifier
)
815 p
.Name
= "DocumentModel"
820 c
.Name
= "getPropertyValues"
821 c
.Argument
= uno
.Any("[]com.sun.star.beans.Property", (p
,))
823 env
= CommandEnvironment()
825 ret
= content
.execute(c
, 0, env
)
826 doc
= ret
.getObject(1, None)
827 except Exception as e
:
828 log
.isErrorLevel() and log
.error("getModelFromDocUrl: %s" % url
)
831 def mapStorageType2PackageContext( storageType
):
833 if( storageType
== "share:uno_packages" ):
835 if( storageType
== "user:uno_packages" ):
839 def getPackageName2PathMap( sfa
, storageType
):
841 packageManagerFactory
= uno
.getComponentContext().getValueByName(
842 "/singletons/com.sun.star.deployment.thePackageManagerFactory" )
843 packageManager
= packageManagerFactory
.getPackageManager(
844 mapStorageType2PackageContext(storageType
))
845 # packageManager.addModifyListener( ModifyListener() )
846 log
.debug( "pythonscript: getPackageName2PathMap start getDeployedPackages" )
847 packages
= packageManager
.getDeployedPackages(
848 packageManager
.createAbortChannel(), CommandEnvironment( ) )
849 log
.debug( "pythonscript: getPackageName2PathMap end getDeployedPackages (" + str(len(packages
))+")" )
852 log
.debug( "inspecting package " + i
.Name
+ "("+i
.Identifier
.Value
+")" )
853 transientPathElement
= penultimateElement( i
.URL
)
854 j
= expandUri( i
.URL
)
855 paths
= getPathsFromPackage( j
, sfa
)
857 # map package name to url, we need this later
858 log
.debug( "adding Package " + transientPathElement
+ " " + str( paths
) )
859 ret
[ lastElement( j
) ] = Package( paths
, transientPathElement
)
862 def penultimateElement( aStr
):
863 lastSlash
= aStr
.rindex("/")
864 penultimateSlash
= aStr
.rindex("/",0,lastSlash
-1)
865 return aStr
[ penultimateSlash
+1:lastSlash
]
867 def lastElement( aStr
):
868 return aStr
[ aStr
.rfind( "/" )+1:len(aStr
)]
870 class PackageBrowseNode( unohelper
.Base
, XBrowseNode
):
871 def __init__( self
, provCtx
, name
, rootUrl
):
872 self
.provCtx
= provCtx
874 self
.rootUrl
= rootUrl
879 def getChildNodes( self
):
880 items
= self
.provCtx
.mapPackageName2Path
.items()
883 if len( i
[1].paths
) == 1:
884 browseNodeList
.append(
885 DirBrowseNode( self
.provCtx
, i
[0], i
[1].paths
[0] ))
888 browseNodeList
.append(
889 DirBrowseNode( self
.provCtx
, i
[0]+"."+lastElement(j
), j
) )
890 return tuple( browseNodeList
)
892 def hasChildNodes( self
):
893 return len( self
.provCtx
.mapPackageName2Path
) > 0
898 def getScript( self
, uri
):
899 log
.debug( "PackageBrowseNode getScript " + uri
+ " invoked" )
900 raise IllegalArgumentException( "PackageBrowseNode couldn't instantiate script " + uri
, self
, 0 )
905 class PythonScript( unohelper
.Base
, XScript
):
906 def __init__( self
, func
, mod
, args
):
911 def invoke(self
, args
, out
, outindex
):
912 log
.debug( "PythonScript.invoke " + str( args
) )
916 ret
= self
.func( *args
)
917 except UnoException
as e
:
918 # UNO Exception continue to fly ...
919 text
= lastException2String()
920 complete
= "Error during invoking function " + \
921 str(self
.func
.__name
__) + " in module " + \
922 self
.mod
.__file
__ + " (" + text
+ ")"
923 log
.debug( complete
)
924 # some people may beat me up for modifying the exception text,
925 # but otherwise office just shows
926 # the type name and message text with no more information,
927 # this is really bad for most users.
928 e
.Message
= e
.Message
+ " (" + complete
+ ")"
930 except Exception as e
:
931 # General python exception are converted to uno RuntimeException
932 text
= lastException2String()
933 complete
= "Error during invoking function " + \
934 str(self
.func
.__name
__) + " in module " + \
935 self
.mod
.__file
__ + " (" + text
+ ")"
936 log
.debug( complete
)
937 raise RuntimeException( complete
, self
)
938 log
.debug( "PythonScript.invoke ret = " + str( ret
) )
941 def expandUri( uri
):
942 if uri
.startswith( "vnd.sun.star.expand:" ):
943 uri
= uri
.replace( "vnd.sun.star.expand:", "",1)
944 uri
= uno
.getComponentContext().getByName(
945 "/singletons/com.sun.star.util.theMacroExpander" ).expandMacros( unquote(uri
) )
946 if uri
.startswith( "file:" ):
947 uri
= uno
.absolutize("",uri
) # necessary to get rid of .. in uri
950 #--------------------------------------------------------------
951 class PythonScriptProvider( unohelper
.Base
, XBrowseNode
, XScriptProvider
, XNameContainer
):
952 def __init__( self
, ctx
, *args
):
953 if log
.isDebugLevel():
958 mystr
= mystr
+ str(i
)
959 log
.debug( "Entering PythonScriptProvider.ctor" + mystr
)
965 if isinstance(args
[0], str):
966 storageType
= args
[0]
967 if storageType
.startswith( "vnd.sun.star.tdoc" ):
968 doc
= getModelFromDocUrl(ctx
, storageType
)
972 doc
= inv
.ScriptContainer
973 content
= ctx
.getServiceManager().createInstanceWithContext(
974 "com.sun.star.frame.TransientDocumentsDocumentContentFactory",
975 ctx
).createDocumentContent(doc
)
976 storageType
= content
.getIdentifier().getContentIdentifier()
977 except Exception as e
:
978 text
= lastException2String()
981 isPackage
= storageType
.endswith( ":uno_packages" )
984 # urlHelper = ctx.ServiceManager.createInstanceWithArgumentsAndContext(
985 # "com.sun.star.script.provider.ScriptURIHelper", (LANGUAGENAME, storageType), ctx)
986 urlHelper
= MyUriHelper( ctx
, storageType
)
987 log
.debug( "got urlHelper " + str( urlHelper
) )
989 rootUrl
= expandUri( urlHelper
.getRootStorageURI() )
990 log
.debug( storageType
+ " transformed to " + rootUrl
)
992 ucbService
= "com.sun.star.ucb.SimpleFileAccess"
993 sfa
= ctx
.ServiceManager
.createInstanceWithContext( ucbService
, ctx
)
995 log
.debug("PythonScriptProvider couldn't instantiate " +ucbService
)
996 raise RuntimeException(
997 "PythonScriptProvider couldn't instantiate " +ucbService
, self
)
998 self
.provCtx
= ProviderContext(
999 storageType
, sfa
, urlHelper
, ScriptContext( uno
.getComponentContext(), doc
, inv
) )
1001 mapPackageName2Path
= getPackageName2PathMap( sfa
, storageType
)
1002 self
.provCtx
.setPackageAttributes( mapPackageName2Path
, rootUrl
)
1003 self
.dirBrowseNode
= PackageBrowseNode( self
.provCtx
, LANGUAGENAME
, rootUrl
)
1005 self
.dirBrowseNode
= DirBrowseNode( self
.provCtx
, LANGUAGENAME
, rootUrl
)
1007 except Exception as e
:
1008 text
= lastException2String()
1009 log
.debug( "PythonScriptProvider could not be instantiated because of : " + text
)
1012 def getName( self
):
1013 return self
.dirBrowseNode
.getName()
1015 def getChildNodes( self
):
1016 return self
.dirBrowseNode
.getChildNodes()
1018 def hasChildNodes( self
):
1019 return self
.dirBrowseNode
.hasChildNodes()
1021 def getType( self
):
1022 return self
.dirBrowseNode
.getType()
1024 # retrieve function args in parenthesis
1025 def getFunctionArguments(self
, func_signature
):
1026 nOpenParenthesis
= func_signature
.find( "(" )
1027 if -1 == nOpenParenthesis
:
1028 function_name
= func_signature
1031 function_name
= func_signature
[0:nOpenParenthesis
]
1032 arg_part
= func_signature
[nOpenParenthesis
+1:len(func_signature
)]
1033 nCloseParenthesis
= arg_part
.find( ")" )
1034 if -1 == nCloseParenthesis
:
1035 raise IllegalArgumentException( "PythonLoader: mismatch parenthesis " + func_signature
, self
, 0 )
1036 arguments
= arg_part
[0:nCloseParenthesis
].strip()
1040 arguments
= tuple([x
.strip().strip('"') for x
in arguments
.split(",")])
1041 return function_name
, arguments
1043 def getScript( self
, scriptUri
):
1045 log
.debug( "getScript " + scriptUri
+ " invoked")
1047 storageUri
= self
.provCtx
.getStorageUrlFromPersistentUrl(
1048 self
.provCtx
.uriHelper
.getStorageURI(scriptUri
) );
1049 log
.debug( "getScript: storageUri = " + storageUri
)
1050 fileUri
= storageUri
[0:storageUri
.find( "$" )]
1051 funcName
= storageUri
[storageUri
.find( "$" )+1:len(storageUri
)]
1053 # retrieve arguments in parenthesis
1054 funcName
, funcArgs
= self
.getFunctionArguments(funcName
)
1055 log
.debug( " getScript : parsed funcname " + str(funcName
) )
1056 log
.debug( " getScript : func args " + str(funcArgs
) )
1058 mod
= self
.provCtx
.getModuleByUrl( fileUri
)
1059 log
.debug( " got mod " + str(mod
) )
1061 func
= mod
.__dict
__[ funcName
]
1063 log
.debug( "got func " + str( func
) )
1064 return PythonScript( func
, mod
, funcArgs
)
1066 text
= lastException2String()
1068 raise ScriptFrameworkErrorException( text
, self
, scriptUri
, LANGUAGENAME
, 0 )
1072 def getSupportedServices( self
):
1073 return g_ImplementationHelper
.getSupportedServices(g_implName
)
1075 def supportsService( self
, ServiceName
):
1076 return g_ImplementationHelper
.supportsService( g_implName
, ServiceName
)
1078 def getImplementationName(self
):
1081 def getByName( self
, name
):
1082 log
.debug( "getByName called" + str( name
))
1086 def getElementNames( self
):
1087 log
.debug( "getElementNames called")
1090 def hasByName( self
, name
):
1092 log
.debug( "hasByName called " + str( name
))
1093 uri
= expandUri(name
)
1094 ret
= self
.provCtx
.isUrlInPackage( uri
)
1095 log
.debug( "hasByName " + uri
+ " " +str( ret
) )
1098 text
= lastException2String()
1099 log
.debug( "Error in hasByName:" + text
)
1102 def removeByName( self
, name
):
1103 log
.debug( "removeByName called" + str( name
))
1104 uri
= expandUri( name
)
1105 if self
.provCtx
.isUrlInPackage( uri
):
1106 self
.provCtx
.removePackageByUrl( uri
)
1108 log
.debug( "removeByName unknown uri " + str( name
) + ", ignoring" )
1109 raise NoSuchElementException( uri
+ "is not in package" , self
)
1110 log
.debug( "removeByName called" + str( uri
) + " successful" )
1112 def insertByName( self
, name
, value
):
1113 log
.debug( "insertByName called " + str( name
) + " " + str( value
))
1114 uri
= expandUri( name
)
1115 if isPyFileInPath( self
.provCtx
.sfa
, uri
):
1116 self
.provCtx
.addPackageByUrl( uri
)
1118 # package is no python package ...
1119 log
.debug( "insertByName: no python files in " + str( uri
) + ", ignoring" )
1120 raise IllegalArgumentException( uri
+ " does not contain .py files", self
, 1 )
1121 log
.debug( "insertByName called " + str( uri
) + " successful" )
1123 def replaceByName( self
, name
, value
):
1124 log
.debug( "replaceByName called " + str( name
) + " " + str( value
))
1125 uri
= expandUri( name
)
1126 self
.removeByName( name
)
1127 self
.insertByName( name
, value
)
1128 log
.debug( "replaceByName called" + str( uri
) + " successful" )
1130 def getElementType( self
):
1131 log
.debug( "getElementType called" )
1132 return uno
.getTypeByName( "void" )
1134 def hasElements( self
):
1135 log
.debug( "hasElements got called")
1138 g_ImplementationHelper
.addImplementation( \
1139 PythonScriptProvider
,g_implName
, \
1140 ("com.sun.star.script.provider.LanguageScriptProvider",
1141 "com.sun.star.script.provider.ScriptProviderFor"+ LANGUAGENAME
,),)
1144 log
.debug( "pythonscript finished initializing" )
1146 # vim: set shiftwidth=4 softtabstop=4 expandtab: