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
31 NONE
= 0 # production level
32 ERROR
= 1 # for script developers
33 DEBUG
= 2 # for script framework developers
35 PYSCRIPT_LOG_ENV
= "PYSCRIPT_LOG_LEVEL"
36 PYSCRIPT_LOG_STDOUT_ENV
= "PYSCRIPT_LOG_STDOUT"
38 # Configuration ----------------------------------------------------
39 LogLevel
.use
= LogLevel
.NONE
40 if os
.environ
.get(PYSCRIPT_LOG_ENV
) == "ERROR":
41 LogLevel
.use
= LogLevel
.ERROR
42 elif os
.environ
.get(PYSCRIPT_LOG_ENV
) == "DEBUG":
43 LogLevel
.use
= LogLevel
.DEBUG
45 # True, writes to stdout (difficult on windows)
46 # False, writes to user/Scripts/python/log.txt
47 LOG_STDOUT
= os
.environ
.get(PYSCRIPT_LOG_STDOUT_ENV
, "1") != "0"
49 ENABLE_EDIT_DIALOG
=False # offers a minimal editor for editing.
50 #-------------------------------------------------------------------
53 return uni
.encode( sys
.getfilesystemencoding())
55 def lastException2String():
56 (excType
,excInstance
,excTraceback
) = sys
.exc_info()
57 ret
= str(excType
) + ": "+str(excInstance
) + "\n" + \
58 uno
._uno
_extract
_printable
_stacktrace
( excTraceback
)
61 def logLevel2String( level
):
63 if level
== LogLevel
.ERROR
:
65 elif level
>= LogLevel
.DEBUG
:
73 pathSubst
= uno
.getComponentContext().ServiceManager
.createInstance(
74 "com.sun.star.util.PathSubstitution" )
75 userInstallation
= pathSubst
.getSubstituteVariableValue( "user" )
76 if len( userInstallation
) > 0:
77 systemPath
= uno
.fileUrlToSystemPath( userInstallation
+ "/Scripts/python/log.txt" )
78 ret
= open( systemPath
, "a" )
80 print("Exception during creation of pythonscript logfile: "+ lastException2String() + "\n, delegating log to stdout\n")
83 class Logger(LogLevel
):
84 def __init__(self
, target
):
87 def isDebugLevel( self
):
88 return self
.use
>= self
.DEBUG
90 def debug( self
, msg
):
91 if self
.isDebugLevel():
92 self
.log( self
.DEBUG
, msg
)
94 def isErrorLevel( self
):
95 return self
.use
>= self
.ERROR
97 def error( self
, msg
):
98 if self
.isErrorLevel():
99 self
.log( self
.ERROR
, msg
)
101 def log( self
, level
, msg
):
102 if self
.use
>= level
:
107 logLevel2String( level
) +
113 print("Error during writing to stdout: " +lastException2String() + "\n")
115 log
= Logger( getLogTarget() )
117 log
.debug( "pythonscript loading" )
119 #from com.sun.star.lang import typeOfXServiceInfo, typeOfXTypeProvider
120 from com
.sun
.star
.uno
import RuntimeException
121 from com
.sun
.star
.lang
import IllegalArgumentException
122 from com
.sun
.star
.container
import NoSuchElementException
123 from com
.sun
.star
.lang
import XServiceInfo
124 from com
.sun
.star
.io
import IOException
125 from com
.sun
.star
.ucb
import CommandAbortedException
, XCommandEnvironment
, XProgressHandler
, Command
126 from com
.sun
.star
.task
import XInteractionHandler
127 from com
.sun
.star
.beans
import XPropertySet
, Property
128 from com
.sun
.star
.container
import XNameContainer
129 from com
.sun
.star
.xml
.sax
import XDocumentHandler
, InputSource
130 from com
.sun
.star
.uno
import Exception as UnoException
131 from com
.sun
.star
.script
import XInvocation
132 from com
.sun
.star
.awt
import XActionListener
134 from com
.sun
.star
.script
.provider
import XScriptProvider
, XScript
, XScriptContext
, ScriptFrameworkErrorException
135 from com
.sun
.star
.script
.browse
import XBrowseNode
136 from com
.sun
.star
.script
.browse
.BrowseNodeTypes
import SCRIPT
, CONTAINER
, ROOT
137 from com
.sun
.star
.util
import XModifyListener
139 LANGUAGENAME
= "Python"
140 GLOBAL_SCRIPTCONTEXT_NAME
= "XSCRIPTCONTEXT"
141 CALLABLE_CONTAINER_NAME
= "g_exportedScripts"
143 # pythonloader looks for a static g_ImplementationHelper variable
144 g_ImplementationHelper
= unohelper
.ImplementationHelper()
145 g_implName
= "org.libreoffice.pyuno.LanguageScriptProviderFor"+LANGUAGENAME
150 def readTextFromStream( inputStream
):
152 code
= uno
.ByteSequence( b
"" )
154 read
,out
= inputStream
.readBytes( None , BLOCK_SIZE
)
156 if read
< BLOCK_SIZE
:
160 def toIniName( str ):
161 if platform
.system() == "Windows":
167 """ definition: storageURI is the system dependent, absolute file url, where the script is stored on disk
168 scriptURI is the system independent uri
172 def __init__( self
, ctx
, location
):
175 { "share" : "vnd.sun.star.expand:$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/Scripts/python" , \
176 "share:uno_packages" : "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages", \
177 "user" : "vnd.sun.star.expand:${$BRAND_INI_DIR/" + toIniName( "bootstrap") + "::UserInstallation}/user/Scripts/python" , \
178 "user:uno_packages" : "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages" }
179 self
.m_uriRefFac
= ctx
.ServiceManager
.createInstanceWithContext("com.sun.star.uri.UriReferenceFactory",ctx
)
180 if location
.startswith( "vnd.sun.star.tdoc" ):
181 self
.m_baseUri
= location
+ "/Scripts/python"
182 self
.m_scriptUriLocation
= "document"
184 self
.m_baseUri
= expandUri( self
.s_UriMap
[location
] )
185 self
.m_scriptUriLocation
= location
186 log
.debug( "initialized urihelper with baseUri="+self
.m_baseUri
+ ",m_scriptUriLocation="+self
.m_scriptUriLocation
)
188 def getRootStorageURI( self
):
189 return self
.m_baseUri
191 def getStorageURI( self
, scriptURI
):
192 return self
.scriptURI2StorageUri(scriptURI
)
194 def getScriptURI( self
, storageURI
):
195 return self
.storageURI2ScriptUri(storageURI
)
197 def storageURI2ScriptUri( self
, storageURI
):
198 if not storageURI
.startswith( self
.m_baseUri
):
199 message
= "pythonscript: storage uri '" + storageURI
+ "' not in base uri '" + self
.m_baseUri
+ "'"
201 raise RuntimeException( message
, self
.ctx
)
203 ret
= "vnd.sun.star.script:" + \
204 storageURI
[len(self
.m_baseUri
)+1:].replace("/","|") + \
205 "?language=" + LANGUAGENAME
+ "&location=" + self
.m_scriptUriLocation
206 log
.debug( "converting storageURI="+storageURI
+ " to scriptURI=" + ret
)
209 def scriptURI2StorageUri( self
, scriptURI
):
211 # base path to the python script location
212 sBaseUri
= self
.m_baseUri
+ "/"
213 xBaseUri
= self
.m_uriRefFac
.parse(sBaseUri
)
215 # path to the .py file + "$functionname, arguments, etc
216 xStorageUri
= self
.m_uriRefFac
.parse(scriptURI
)
217 # getName will apply url-decoding to the name, so encode back
218 sStorageUri
= xStorageUri
.getName().replace("%", "%25")
219 sStorageUri
= sStorageUri
.replace( "|", "/" )
221 # path to the .py file, relative to the base
222 funcNameStart
= sStorageUri
.find("$")
223 if funcNameStart
!= -1:
224 sFileUri
= sStorageUri
[0:funcNameStart
]
225 sFuncName
= sStorageUri
[funcNameStart
+1:]
227 sFileUri
= sStorageUri
229 xFileUri
= self
.m_uriRefFac
.parse(sFileUri
)
231 message
= "pythonscript: invalid relative uri '" + sFileUri
+ "'"
233 raise RuntimeException( message
, self
.ctx
)
235 if not xFileUri
.hasRelativePath():
236 message
= "pythonscript: an absolute uri is invalid '" + sFileUri
+ "'"
238 raise RuntimeException( message
, self
.ctx
)
240 # absolute path to the .py file
241 xAbsScriptUri
= self
.m_uriRefFac
.makeAbsolute(xBaseUri
, xFileUri
, True, RETAIN
)
242 sAbsScriptUri
= xAbsScriptUri
.getUriReference()
244 # ensure py file is under the base path
245 if not sAbsScriptUri
.startswith(sBaseUri
):
246 message
= "pythonscript: storage uri '" + sAbsScriptUri
+ "' not in base uri '" + self
.m_baseUri
+ "'"
248 raise RuntimeException( message
, self
.ctx
)
251 if funcNameStart
!= -1:
252 ret
= ret
+ "$" + sFuncName
253 log
.debug( "converting scriptURI="+scriptURI
+ " to storageURI=" + ret
)
255 except UnoException
as e
:
256 log
.error( "error during converting scriptURI="+scriptURI
+ ": " + e
.Message
)
257 raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + e
.Message
, self
.ctx
)
258 except Exception as e
:
259 log
.error( "error during converting scriptURI="+scriptURI
+ ": " + str(e
))
260 raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + str(e
), self
.ctx
)
264 def __init__( self
, lastRead
, module
):
265 self
.lastRead
= lastRead
268 def hasChanged( oldDate
, newDate
):
269 return newDate
.Year
> oldDate
.Year
or \
270 newDate
.Month
> oldDate
.Month
or \
271 newDate
.Day
> oldDate
.Day
or \
272 newDate
.Hours
> oldDate
.Hours
or \
273 newDate
.Minutes
> oldDate
.Minutes
or \
274 newDate
.Seconds
> oldDate
.Seconds
or \
275 newDate
.NanoSeconds
> oldDate
.NanoSeconds
277 def ensureSourceState( code
):
278 if code
.endswith(b
"\n"):
280 code
= code
.replace(b
"\r", b
"")
284 def checkForPythonPathBesideScript( url
):
285 if url
.startswith( "file:" ):
286 path
= unohelper
.fileUrlToSystemPath( url
+"/pythonpath.zip" );
287 log
.log( LogLevel
.DEBUG
, "checking for existence of " + path
)
288 if 1 == os
.access( encfile(path
), os
.F_OK
) and not path
in sys
.path
:
289 log
.log( LogLevel
.DEBUG
, "adding " + path
+ " to sys.path" )
290 sys
.path
.append( path
)
292 path
= unohelper
.fileUrlToSystemPath( url
+"/pythonpath" );
293 log
.log( LogLevel
.DEBUG
, "checking for existence of " + path
)
294 if 1 == os
.access( encfile(path
), os
.F_OK
) and not path
in sys
.path
:
295 log
.log( LogLevel
.DEBUG
, "adding " + path
+ " to sys.path" )
296 sys
.path
.append( path
)
299 class ScriptContext(unohelper
.Base
):
300 def __init__( self
, ctx
, doc
, inv
):
306 def getDocument(self
):
309 return self
.getDesktop().getCurrentComponent()
311 def getDesktop(self
):
312 return self
.ctx
.ServiceManager
.createInstanceWithContext(
313 "com.sun.star.frame.Desktop", self
.ctx
)
315 def getComponentContext(self
):
318 def getInvocationContext(self
):
321 #----------------------------------
322 # Global Module Administration
323 # does not fit together with script
324 # engine lifetime management
325 #----------------------------------
326 #g_scriptContext = ScriptContext( uno.getComponentContext(), None )
328 #def getModuleByUrl( url, sfa ):
329 # entry = g_modules.get(url)
331 # lastRead = sfa.getDateTimeModified( url )
333 # if hasChanged( entry.lastRead, lastRead ):
334 # log.debug("file " + url + " has changed, reloading")
339 # log.debug( "opening >" + url + "<" )
341 # code = readTextFromStream( sfa.openFileRead( url ) )
344 # entry = ModuleEntry( lastRead, types.ModuleType("ooo_script_framework") )
345 # entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = g_scriptContext
346 # entry.module.__file__ = url
347 # exec code in entry.module.__dict__
348 # g_modules[ url ] = entry
349 # log.debug( "mapped " + url + " to " + str( entry.module ) )
350 # return entry.module
352 class ProviderContext
:
353 def __init__( self
, storageType
, sfa
, uriHelper
, scriptContext
):
354 self
.storageType
= storageType
356 self
.uriHelper
= uriHelper
357 self
.scriptContext
= scriptContext
360 self
.mapPackageName2Path
= None
362 def getTransientPartFromUrl( self
, url
):
363 rest
= url
.replace( self
.rootUrl
, "",1 ).replace( "/","",1)
364 return rest
[0:rest
.find("/")]
366 def getPackageNameFromUrl( self
, url
):
367 rest
= url
.replace( self
.rootUrl
, "",1 ).replace( "/","",1)
368 start
= rest
.find("/") +1
369 return rest
[start
:rest
.find("/",start
)]
372 def removePackageByUrl( self
, url
):
373 items
= self
.mapPackageName2Path
.items()
375 if url
in i
[1].paths
:
376 self
.mapPackageName2Path
.pop(i
[0])
379 def addPackageByUrl( self
, url
):
380 packageName
= self
.getPackageNameFromUrl( url
)
381 transientPart
= self
.getTransientPartFromUrl( url
)
382 log
.debug( "addPackageByUrl : " + packageName
+ ", " + transientPart
+ "("+url
+")" + ", rootUrl="+self
.rootUrl
)
383 if packageName
in self
.mapPackageName2Path
:
384 package
= self
.mapPackageName2Path
[ packageName
]
385 package
.paths
= package
.paths
+ (url
, )
387 package
= Package( (url
,), transientPart
)
388 self
.mapPackageName2Path
[ packageName
] = package
390 def isUrlInPackage( self
, url
):
391 values
= self
.mapPackageName2Path
.values()
393 # print ("checking " + url + " in " + str(i.paths))
399 def setPackageAttributes( self
, mapPackageName2Path
, rootUrl
):
400 self
.mapPackageName2Path
= mapPackageName2Path
401 self
.rootUrl
= rootUrl
403 def getPersistentUrlFromStorageUrl( self
, url
):
404 # package name is the second directory
407 pos
= len( self
.rootUrl
) +1
408 ret
= url
[0:pos
]+url
[url
.find("/",pos
)+1:len(url
)]
409 log
.debug( "getPersistentUrlFromStorageUrl " + url
+ " -> "+ ret
)
412 def getStorageUrlFromPersistentUrl( self
, url
):
415 pos
= len(self
.rootUrl
)+1
416 packageName
= url
[pos
:url
.find("/",pos
+1)]
417 package
= self
.mapPackageName2Path
[ packageName
]
418 ret
= url
[0:pos
]+ package
.transientPathElement
+ "/" + url
[pos
:len(url
)]
419 log
.debug( "getStorageUrlFromPersistentUrl " + url
+ " -> "+ ret
)
422 def getFuncsByUrl( self
, url
):
423 src
= readTextFromStream( self
.sfa
.openFileRead( url
) )
424 checkForPythonPathBesideScript( url
[0:url
.rfind('/')] )
425 src
= ensureSourceState( src
)
428 code
= ast
.parse( src
)
430 log
.isDebugLevel() and log
.debug( "pythonscript: getFuncsByUrl: exception while parsing: " + lastException2String())
438 g_exportedScripts
= []
439 for node
in ast
.iter_child_nodes(code
):
440 if isinstance(node
, ast
.FunctionDef
):
441 allFuncs
.append(node
.name
)
442 elif isinstance(node
, ast
.Assign
):
443 for target
in node
.targets
:
445 identifier
= target
.id
446 except AttributeError:
449 if identifier
== "g_exportedScripts":
450 for value
in node
.value
.elts
:
451 g_exportedScripts
.append(value
.id)
452 return g_exportedScripts
455 # for node in code.node.nodes:
456 # if node.__class__.__name__ == 'Function':
457 # allFuncs.append(node.name)
458 # elif node.__class__.__name__ == 'Assign':
459 # for assignee in node.nodes:
460 # if assignee.name == 'g_exportedScripts':
461 # for item in node.expr.nodes:
462 # if item.__class__.__name__ == 'Name':
463 # g_exportedScripts.append(item.name)
464 # return g_exportedScripts
468 def getModuleByUrl( self
, url
):
469 entry
= self
.modules
.get(url
)
471 lastRead
= self
.sfa
.getDateTimeModified( url
)
473 if hasChanged( entry
.lastRead
, lastRead
):
474 log
.debug( "file " + url
+ " has changed, reloading" )
479 log
.debug( "opening >" + url
+ "<" )
481 src
= readTextFromStream( self
.sfa
.openFileRead( url
) )
482 checkForPythonPathBesideScript( url
[0:url
.rfind('/')] )
483 src
= ensureSourceState( src
)
486 entry
= ModuleEntry( lastRead
, types
.ModuleType("ooo_script_framework") )
487 entry
.module
.__dict
__[GLOBAL_SCRIPTCONTEXT_NAME
] = self
.scriptContext
490 if url
.startswith( "file:" ):
491 code
= compile( src
, encfile(uno
.fileUrlToSystemPath( url
) ), "exec" )
493 code
= compile( src
, url
, "exec" )
494 exec(code
, entry
.module
.__dict
__)
495 entry
.module
.__file
__ = url
496 self
.modules
[ url
] = entry
497 log
.debug( "mapped " + url
+ " to " + str( entry
.module
) )
500 #--------------------------------------------------
501 def isScript( candidate
):
503 if isinstance( candidate
, type(isScript
) ):
507 #-------------------------------------------------------
508 class ScriptBrowseNode( unohelper
.Base
, XBrowseNode
, XPropertySet
, XInvocation
, XActionListener
):
509 def __init__( self
, provCtx
, uri
, fileName
, funcName
):
510 self
.fileName
= fileName
511 self
.funcName
= funcName
512 self
.provCtx
= provCtx
518 def getChildNodes(self
):
521 def hasChildNodes(self
):
527 def getPropertyValue( self
, name
):
531 ret
= self
.provCtx
.uriHelper
.getScriptURI(
532 self
.provCtx
.getPersistentUrlFromStorageUrl( self
.uri
+ "$" + self
.funcName
) )
533 elif name
== "Editable" and ENABLE_EDIT_DIALOG
:
534 ret
= not self
.provCtx
.sfa
.isReadOnly( self
.uri
)
536 log
.debug( "ScriptBrowseNode.getPropertyValue called for " + name
+ ", returning " + str(ret
) )
538 log
.error( "ScriptBrowseNode.getPropertyValue error " + lastException2String())
542 def setPropertyValue( self
, name
, value
):
543 log
.debug( "ScriptBrowseNode.setPropertyValue called " + name
+ "=" +str(value
) )
544 def getPropertySetInfo( self
):
545 log
.debug( "ScriptBrowseNode.getPropertySetInfo called " )
548 def getIntrospection( self
):
551 def invoke( self
, name
, params
, outparamindex
, outparams
):
552 if name
== "Editable":
553 servicename
= "com.sun.star.awt.DialogProvider"
554 ctx
= self
.provCtx
.scriptContext
.getComponentContext()
555 dlgprov
= ctx
.ServiceManager
.createInstanceWithContext(
558 self
.editor
= dlgprov
.createDialog(
559 "vnd.sun.star.script:" +
560 "ScriptBindingLibrary.MacroEditor?location=application")
562 code
= readTextFromStream(self
.provCtx
.sfa
.openFileRead(self
.uri
))
563 code
= ensureSourceState( code
)
564 self
.editor
.getControl("EditorTextField").setText(code
)
566 self
.editor
.getControl("RunButton").setActionCommand("Run")
567 self
.editor
.getControl("RunButton").addActionListener(self
)
568 self
.editor
.getControl("SaveButton").setActionCommand("Save")
569 self
.editor
.getControl("SaveButton").addActionListener(self
)
571 self
.editor
.execute()
575 def actionPerformed( self
, event
):
577 if event
.ActionCommand
== "Run":
578 code
= self
.editor
.getControl("EditorTextField").getText()
579 code
= ensureSourceState( code
)
580 mod
= types
.ModuleType("ooo_script_framework")
581 mod
.__dict
__[GLOBAL_SCRIPTCONTEXT_NAME
] = self
.provCtx
.scriptContext
582 exec(code
, mod
.__dict
__)
583 values
= mod
.__dict
__.get( CALLABLE_CONTAINER_NAME
, None )
585 values
= mod
.__dict
__.values()
592 elif event
.ActionCommand
== "Save":
593 toWrite
= uno
.ByteSequence(
594 self
.editor
.getControl("EditorTextField").getText().encode(
595 sys
.getdefaultencoding()) )
596 copyUrl
= self
.uri
+ ".orig"
597 self
.provCtx
.sfa
.move( self
.uri
, copyUrl
)
598 out
= self
.provCtx
.sfa
.openFileWrite( self
.uri
)
599 out
.writeBytes( toWrite
)
601 self
.provCtx
.sfa
.kill( copyUrl
)
602 # log.debug("Save is not implemented yet")
603 # text = self.editor.getControl("EditorTextField").getText()
604 # log.debug("Would save: " + text)
606 # TODO: add an error box here!
607 log
.error( lastException2String() )
610 def setValue( self
, name
, value
):
613 def getValue( self
, name
):
616 def hasMethod( self
, name
):
619 def hasProperty( self
, name
):
623 #-------------------------------------------------------
624 class FileBrowseNode( unohelper
.Base
, XBrowseNode
):
625 def __init__( self
, provCtx
, uri
, name
):
626 self
.provCtx
= provCtx
629 self
.funcnames
= None
634 def getChildNodes(self
):
637 self
.funcnames
= self
.provCtx
.getFuncsByUrl( self
.uri
)
640 for i
in self
.funcnames
:
641 scriptNodeList
.append(
643 self
.provCtx
, self
.uri
, self
.name
, i
))
644 ret
= tuple( scriptNodeList
)
645 log
.debug( "returning " +str(len(ret
)) + " ScriptChildNodes on " + self
.uri
)
647 text
= lastException2String()
648 log
.error( "Error while evaluating " + self
.uri
+ ":" + text
)
652 def hasChildNodes(self
):
654 return len(self
.getChildNodes()) > 0
663 class DirBrowseNode( unohelper
.Base
, XBrowseNode
):
664 def __init__( self
, provCtx
, name
, rootUrl
):
665 self
.provCtx
= provCtx
667 self
.rootUrl
= rootUrl
672 def getChildNodes( self
):
674 log
.debug( "DirBrowseNode.getChildNodes called for " + self
.rootUrl
)
675 contents
= self
.provCtx
.sfa
.getFolderContents( self
.rootUrl
, True )
678 if i
.endswith( ".py" ):
679 log
.debug( "adding filenode " + i
)
680 browseNodeList
.append(
681 FileBrowseNode( self
.provCtx
, i
, i
[i
.rfind("/")+1:len(i
)-3] ) )
682 elif self
.provCtx
.sfa
.isFolder( i
) and not i
.endswith("/pythonpath"):
683 log
.debug( "adding DirBrowseNode " + i
)
684 browseNodeList
.append( DirBrowseNode( self
.provCtx
, i
[i
.rfind("/")+1:len(i
)],i
))
685 return tuple( browseNodeList
)
686 except Exception as e
:
687 text
= lastException2String()
688 log
.error( "DirBrowseNode error: " + str(e
) + " while evaluating " + self
.rootUrl
)
692 def hasChildNodes( self
):
698 def getScript( self
, uri
):
699 log
.debug( "DirBrowseNode getScript " + uri
+ " invoked" )
700 raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri
, self
, 0 )
703 class ManifestHandler( XDocumentHandler
, unohelper
.Base
):
704 def __init__( self
, rootUrl
):
705 self
.rootUrl
= rootUrl
707 def startDocument( self
):
710 def endDocument( self
):
713 def startElement( self
, name
, attlist
):
714 if name
== "manifest:file-entry":
715 if attlist
.getValueByName( "manifest:media-type" ) == "application/vnd.sun.star.framework-script":
717 self
.rootUrl
+ "/" + attlist
.getValueByName( "manifest:full-path" ) )
719 def endElement( self
, name
):
722 def characters ( self
, chars
):
725 def ignoreableWhitespace( self
, chars
):
728 def setDocumentLocator( self
, locator
):
731 def isPyFileInPath( sfa
, path
):
733 contents
= sfa
.getFolderContents( path
, True )
736 ret
= isPyFileInPath(sfa
,i
)
738 if i
.endswith(".py"):
744 # extracts META-INF directory from
745 def getPathsFromPackage( rootUrl
, sfa
):
748 fileUrl
= rootUrl
+ "/META-INF/manifest.xml"
749 inputStream
= sfa
.openFileRead( fileUrl
)
750 parser
= uno
.getComponentContext().ServiceManager
.createInstance( "com.sun.star.xml.sax.Parser" )
751 handler
= ManifestHandler( rootUrl
)
752 parser
.setDocumentHandler( handler
)
753 parser
.parseStream( InputSource( inputStream
, "", fileUrl
, fileUrl
) )
754 for i
in tuple(handler
.urlList
):
755 if not isPyFileInPath( sfa
, i
):
756 handler
.urlList
.remove(i
)
757 ret
= tuple( handler
.urlList
)
759 text
= lastException2String()
760 log
.debug( "getPathsFromPackage " + fileUrl
+ " Exception: " +text
)
766 def __init__( self
, paths
, transientPathElement
):
768 self
.transientPathElement
= transientPathElement
770 class DummyInteractionHandler( unohelper
.Base
, XInteractionHandler
):
771 def __init__( self
):
773 def handle( self
, event
):
774 log
.debug( "pythonscript: DummyInteractionHandler.handle " + str( event
) )
776 class DummyProgressHandler( unohelper
.Base
, XProgressHandler
):
777 def __init__( self
):
780 def push( self
,status
):
781 log
.debug( "pythonscript: DummyProgressHandler.push " + str( status
) )
782 def update( self
,status
):
783 log
.debug( "pythonscript: DummyProgressHandler.update " + str( status
) )
784 def pop( self
, event
):
785 log
.debug( "pythonscript: DummyProgressHandler.push " + str( event
) )
787 class CommandEnvironment(unohelper
.Base
, XCommandEnvironment
):
788 def __init__( self
):
789 self
.progressHandler
= DummyProgressHandler()
790 self
.interactionHandler
= DummyInteractionHandler()
791 def getInteractionHandler( self
):
792 return self
.interactionHandler
793 def getProgressHandler( self
):
794 return self
.progressHandler
796 #maybe useful for debugging purposes
797 #class ModifyListener( unohelper.Base, XModifyListener ):
798 # def __init__( self ):
800 # def modified( self, event ):
801 # log.debug( "pythonscript: ModifyListener.modified " + str( event ) )
802 # def disposing( self, event ):
803 # log.debug( "pythonscript: ModifyListener.disposing " + str( event ) )
805 def getModelFromDocUrl(ctx
, url
):
806 """Get document model from document url."""
808 args
= ("Local", "Office")
809 ucb
= ctx
.getServiceManager().createInstanceWithArgumentsAndContext(
810 "com.sun.star.ucb.UniversalContentBroker", args
, ctx
)
811 identifier
= ucb
.createContentIdentifier(url
)
812 content
= ucb
.queryContent(identifier
)
814 p
.Name
= "DocumentModel"
819 c
.Name
= "getPropertyValues"
820 c
.Argument
= uno
.Any("[]com.sun.star.beans.Property", (p
,))
822 env
= CommandEnvironment()
824 ret
= content
.execute(c
, 0, env
)
825 doc
= ret
.getObject(1, None)
826 except Exception as e
:
827 log
.isErrorLevel() and log
.error("getModelFromDocUrl: %s" % url
)
830 def mapStorageType2PackageContext( storageType
):
832 if( storageType
== "share:uno_packages" ):
834 if( storageType
== "user:uno_packages" ):
838 def getPackageName2PathMap( sfa
, storageType
):
840 packageManagerFactory
= uno
.getComponentContext().getValueByName(
841 "/singletons/com.sun.star.deployment.thePackageManagerFactory" )
842 packageManager
= packageManagerFactory
.getPackageManager(
843 mapStorageType2PackageContext(storageType
))
844 # packageManager.addModifyListener( ModifyListener() )
845 log
.debug( "pythonscript: getPackageName2PathMap start getDeployedPackages" )
846 packages
= packageManager
.getDeployedPackages(
847 packageManager
.createAbortChannel(), CommandEnvironment( ) )
848 log
.debug( "pythonscript: getPackageName2PathMap end getDeployedPackages (" + str(len(packages
))+")" )
851 log
.debug( "inspecting package " + i
.Name
+ "("+i
.Identifier
.Value
+")" )
852 transientPathElement
= penultimateElement( i
.URL
)
853 j
= expandUri( i
.URL
)
854 paths
= getPathsFromPackage( j
, sfa
)
856 # map package name to url, we need this later
857 log
.isErrorLevel() and log
.error( "adding Package " + transientPathElement
+ " " + str( paths
) )
858 ret
[ lastElement( j
) ] = Package( paths
, transientPathElement
)
861 def penultimateElement( aStr
):
862 lastSlash
= aStr
.rindex("/")
863 penultimateSlash
= aStr
.rindex("/",0,lastSlash
-1)
864 return aStr
[ penultimateSlash
+1:lastSlash
]
866 def lastElement( aStr
):
867 return aStr
[ aStr
.rfind( "/" )+1:len(aStr
)]
869 class PackageBrowseNode( unohelper
.Base
, XBrowseNode
):
870 def __init__( self
, provCtx
, name
, rootUrl
):
871 self
.provCtx
= provCtx
873 self
.rootUrl
= rootUrl
878 def getChildNodes( self
):
879 items
= self
.provCtx
.mapPackageName2Path
.items()
882 if len( i
[1].paths
) == 1:
883 browseNodeList
.append(
884 DirBrowseNode( self
.provCtx
, i
[0], i
[1].paths
[0] ))
887 browseNodeList
.append(
888 DirBrowseNode( self
.provCtx
, i
[0]+"."+lastElement(j
), j
) )
889 return tuple( browseNodeList
)
891 def hasChildNodes( self
):
892 return len( self
.provCtx
.mapPackageName2Path
) > 0
897 def getScript( self
, uri
):
898 log
.debug( "PackageBrowseNode getScript " + uri
+ " invoked" )
899 raise IllegalArgumentException( "PackageBrowseNode couldn't instantiate script " + uri
, self
, 0 )
904 class PythonScript( unohelper
.Base
, XScript
):
905 def __init__( self
, func
, mod
, args
):
910 def invoke(self
, args
, out
, outindex
):
911 log
.debug( "PythonScript.invoke " + str( args
) )
915 ret
= self
.func( *args
)
916 except UnoException
as e
:
917 # UNO Exception continue to fly ...
918 text
= lastException2String()
919 complete
= "Error during invoking function " + \
920 str(self
.func
.__name
__) + " in module " + \
921 self
.mod
.__file
__ + " (" + text
+ ")"
922 log
.debug( complete
)
923 # some people may beat me up for modifying the exception text,
924 # but otherwise office just shows
925 # the type name and message text with no more information,
926 # this is really bad for most users.
927 e
.Message
= e
.Message
+ " (" + complete
+ ")"
929 except Exception as e
:
930 # General python exception are converted to uno RuntimeException
931 text
= lastException2String()
932 complete
= "Error during invoking function " + \
933 str(self
.func
.__name
__) + " in module " + \
934 self
.mod
.__file
__ + " (" + text
+ ")"
935 log
.debug( complete
)
936 raise RuntimeException( complete
, self
)
937 log
.debug( "PythonScript.invoke ret = " + str( ret
) )
940 def expandUri( uri
):
941 if uri
.startswith( "vnd.sun.star.expand:" ):
942 uri
= uri
.replace( "vnd.sun.star.expand:", "",1)
943 uri
= uno
.getComponentContext().getByName(
944 "/singletons/com.sun.star.util.theMacroExpander" ).expandMacros( uri
)
945 if uri
.startswith( "file:" ):
946 uri
= uno
.absolutize("",uri
) # necessary to get rid of .. in uri
949 #--------------------------------------------------------------
950 class PythonScriptProvider( unohelper
.Base
, XBrowseNode
, XScriptProvider
, XNameContainer
):
951 def __init__( self
, ctx
, *args
):
952 if log
.isDebugLevel():
957 mystr
= mystr
+ str(i
)
958 log
.debug( "Entering PythonScriptProvider.ctor" + mystr
)
964 if isinstance(args
[0], str):
965 storageType
= args
[0]
966 if storageType
.startswith( "vnd.sun.star.tdoc" ):
967 doc
= getModelFromDocUrl(ctx
, storageType
)
971 doc
= inv
.ScriptContainer
972 content
= ctx
.getServiceManager().createInstanceWithContext(
973 "com.sun.star.frame.TransientDocumentsDocumentContentFactory",
974 ctx
).createDocumentContent(doc
)
975 storageType
= content
.getIdentifier().getContentIdentifier()
976 except Exception as e
:
977 text
= lastException2String()
980 isPackage
= storageType
.endswith( ":uno_packages" )
983 # urlHelper = ctx.ServiceManager.createInstanceWithArgumentsAndContext(
984 # "com.sun.star.script.provider.ScriptURIHelper", (LANGUAGENAME, storageType), ctx)
985 urlHelper
= MyUriHelper( ctx
, storageType
)
986 log
.debug( "got urlHelper " + str( urlHelper
) )
988 rootUrl
= expandUri( urlHelper
.getRootStorageURI() )
989 log
.debug( storageType
+ " transformed to " + rootUrl
)
991 ucbService
= "com.sun.star.ucb.SimpleFileAccess"
992 sfa
= ctx
.ServiceManager
.createInstanceWithContext( ucbService
, ctx
)
994 log
.debug("PythonScriptProvider couldn't instantiate " +ucbService
)
995 raise RuntimeException(
996 "PythonScriptProvider couldn't instantiate " +ucbService
, self
)
997 self
.provCtx
= ProviderContext(
998 storageType
, sfa
, urlHelper
, ScriptContext( uno
.getComponentContext(), doc
, inv
) )
1000 mapPackageName2Path
= getPackageName2PathMap( sfa
, storageType
)
1001 self
.provCtx
.setPackageAttributes( mapPackageName2Path
, rootUrl
)
1002 self
.dirBrowseNode
= PackageBrowseNode( self
.provCtx
, LANGUAGENAME
, rootUrl
)
1004 self
.dirBrowseNode
= DirBrowseNode( self
.provCtx
, LANGUAGENAME
, rootUrl
)
1006 except Exception as e
:
1007 text
= lastException2String()
1008 log
.debug( "PythonScriptProvider could not be instantiated because of : " + text
)
1011 def getName( self
):
1012 return self
.dirBrowseNode
.getName()
1014 def getChildNodes( self
):
1015 return self
.dirBrowseNode
.getChildNodes()
1017 def hasChildNodes( self
):
1018 return self
.dirBrowseNode
.hasChildNodes()
1020 def getType( self
):
1021 return self
.dirBrowseNode
.getType()
1023 # retrieve function args in parenthesis
1024 def getFunctionArguments(self
, func_signature
):
1025 nOpenParenthesis
= func_signature
.find( "(" )
1026 if -1 == nOpenParenthesis
:
1027 function_name
= func_signature
1030 function_name
= func_signature
[0:nOpenParenthesis
]
1031 arg_part
= func_signature
[nOpenParenthesis
+1:len(func_signature
)]
1032 nCloseParenthesis
= arg_part
.find( ")" )
1033 if -1 == nCloseParenthesis
:
1034 raise IllegalArgumentException( "PythonLoader: mismatch parenthesis " + func_signature
, self
, 0 )
1035 arguments
= arg_part
[0:nCloseParenthesis
].strip()
1039 arguments
= tuple([x
.strip().strip('"') for x
in arguments
.split(",")])
1040 return function_name
, arguments
1042 def getScript( self
, scriptUri
):
1044 log
.debug( "getScript " + scriptUri
+ " invoked")
1046 storageUri
= self
.provCtx
.getStorageUrlFromPersistentUrl(
1047 self
.provCtx
.uriHelper
.getStorageURI(scriptUri
) );
1048 log
.debug( "getScript: storageUri = " + storageUri
)
1049 fileUri
= storageUri
[0:storageUri
.find( "$" )]
1050 funcName
= storageUri
[storageUri
.find( "$" )+1:len(storageUri
)]
1052 # retrieve arguments in parenthesis
1053 funcName
, funcArgs
= self
.getFunctionArguments(funcName
)
1054 log
.debug( " getScript : parsed funcname " + str(funcName
) )
1055 log
.debug( " getScript : func args " + str(funcArgs
) )
1057 mod
= self
.provCtx
.getModuleByUrl( fileUri
)
1058 log
.debug( " got mod " + str(mod
) )
1060 func
= mod
.__dict
__[ funcName
]
1062 log
.debug( "got func " + str( func
) )
1063 return PythonScript( func
, mod
, funcArgs
)
1065 text
= lastException2String()
1067 raise ScriptFrameworkErrorException( text
, self
, scriptUri
, LANGUAGENAME
, 0 )
1071 def getSupportedServices( self
):
1072 return g_ImplementationHelper
.getSupportedServices(g_implName
)
1074 def supportsService( self
, ServiceName
):
1075 return g_ImplementationHelper
.supportsService( g_implName
, ServiceName
)
1077 def getImplementationName(self
):
1080 def getByName( self
, name
):
1081 log
.debug( "getByName called" + str( name
))
1085 def getElementNames( self
):
1086 log
.debug( "getElementNames called")
1089 def hasByName( self
, name
):
1091 log
.debug( "hasByName called " + str( name
))
1092 uri
= expandUri(name
)
1093 ret
= self
.provCtx
.isUrlInPackage( uri
)
1094 log
.debug( "hasByName " + uri
+ " " +str( ret
) )
1097 text
= lastException2String()
1098 log
.debug( "Error in hasByName:" + text
)
1101 def removeByName( self
, name
):
1102 log
.debug( "removeByName called" + str( name
))
1103 uri
= expandUri( name
)
1104 if self
.provCtx
.isUrlInPackage( uri
):
1105 self
.provCtx
.removePackageByUrl( uri
)
1107 log
.debug( "removeByName unknown uri " + str( name
) + ", ignoring" )
1108 raise NoSuchElementException( uri
+ "is not in package" , self
)
1109 log
.debug( "removeByName called" + str( uri
) + " successful" )
1111 def insertByName( self
, name
, value
):
1112 log
.debug( "insertByName called " + str( name
) + " " + str( value
))
1113 uri
= expandUri( name
)
1114 if isPyFileInPath( self
.provCtx
.sfa
, uri
):
1115 self
.provCtx
.addPackageByUrl( uri
)
1117 # package is no python package ...
1118 log
.debug( "insertByName: no python files in " + str( uri
) + ", ignoring" )
1119 raise IllegalArgumentException( uri
+ " does not contain .py files", self
, 1 )
1120 log
.debug( "insertByName called " + str( uri
) + " successful" )
1122 def replaceByName( self
, name
, value
):
1123 log
.debug( "replaceByName called " + str( name
) + " " + str( value
))
1124 uri
= expandUri( name
)
1125 self
.removeByName( name
)
1126 self
.insertByName( name
, value
)
1127 log
.debug( "replaceByName called" + str( uri
) + " successful" )
1129 def getElementType( self
):
1130 log
.debug( "getElementType called" )
1131 return uno
.getTypeByName( "void" )
1133 def hasElements( self
):
1134 log
.debug( "hasElements got called")
1137 g_ImplementationHelper
.addImplementation( \
1138 PythonScriptProvider
,g_implName
, \
1139 ("com.sun.star.script.provider.LanguageScriptProvider",
1140 "com.sun.star.script.provider.ScriptProviderFor"+ LANGUAGENAME
,),)
1143 log
.debug( "pythonscript finished initializing" )
1145 # vim: set shiftwidth=4 softtabstop=4 expandtab: