tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / wizards / source / scriptforge / python / ScriptForgeHelper.py
blob370c760b2708b3cb22987b148a9ed989ad93b22e
1 # -*- coding: utf-8 -*-
3 # Copyright 2019-2022 Jean-Pierre LEDURE, Rafael LIMA, Alain ROMEDENNE
5 # ======================================================================================================================
6 # === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
7 # === Full documentation is available on https://help.libreoffice.org/ ===
8 # ======================================================================================================================
10 # ScriptForge is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 # ScriptForge is free software; you can redistribute it and/or modify it under the terms of either (at your option):
16 # 1) The Mozilla Public License, v. 2.0. If a copy of the MPL was not
17 # distributed with this file, you can obtain one at http://mozilla.org/MPL/2.0/ .
19 # 2) The GNU Lesser General Public License as published by
20 # the Free Software Foundation, either version 3 of the License, or
21 # (at your option) any later version. If a copy of the LGPL was not
22 # distributed with this file, see http://www.gnu.org/licenses/ .
24 """
25 Collection of Python helper functions called from the ScriptForge Basic libraries
26 to execute specific services that are not or not easily available from Basic directly.
27 When relevant, the methods present in the ScriptForge Python module (scriptforge.py) might call the
28 functions below for compatibility reasons.
29 """
31 import getpass
32 import os
33 import platform
34 import hashlib
35 import filecmp
36 import webbrowser
37 import json
40 class _Singleton(type):
41 """
42 A Singleton design pattern
43 Credits: « Python in a Nutshell » by Alex Martelli, O'Reilly
44 """
45 instances = {}
47 def __call__(cls, *args, **kwargs):
48 if cls not in cls.instances:
49 cls.instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
50 return cls.instances[cls]
53 # #################################################################
54 # Dictionary service
55 # #################################################################
57 def _SF_Dictionary__ConvertToJson(propval, indent = None) -> str:
58 # used by Dictionary.ConvertToJson() Basic method
59 """
60 Given an array of PropertyValues as argument, convert it to a JSON string
61 """
62 # Array of property values => Dict(ionary) => JSON
63 pvDict = {}
64 for pv in propval:
65 pvDict[pv.Name] = pv.Value
66 return json.dumps(pvDict, indent=indent, skipkeys=True)
69 def _SF_Dictionary__ImportFromJson(jsonstr: str): # used by Dictionary.ImportFromJson() Basic method
70 """
71 Given a JSON string as argument, convert it to a list of tuples (name, value)
72 The value must not be a (sub)dict. This doesn't pass the python-basic bridge.
73 """
74 # JSON => Dictionary => Array of tuples/lists
75 dico = json.loads(jsonstr)
76 result = []
77 for key in iter(dico):
78 value = dico[key]
79 item = value
80 if isinstance(value, dict): # check that first level is not itself a (sub)dict
81 item = None
82 elif isinstance(value, list): # check every member of the list is not a (sub)dict
83 for i in range(len(value)):
84 if isinstance(value[i], dict):
85 value[i] = None
86 result.append((key, item))
87 return result
90 # #################################################################
91 # Exception service
92 # #################################################################
94 def _SF_Exception__PythonPrint(string: str) -> bool:
95 # used by SF_Exception.PythonPrint() Basic method
96 """
97 Write the argument to stdout.
98 If the APSO shell console is active, the argument will be displayed in the console window
99 """
100 print(string)
101 return True
104 # #################################################################
105 # FileSystem service
106 # #################################################################
108 def _SF_FileSystem__CompareFiles(filename1: str, filename2: str, comparecontents=True) -> bool:
109 # used by SF_FileSystem.CompareFiles() Basic method
111 Compare the 2 files, returning True if they seem equal, False otherwise.
112 By default, only their signatures (modification time, ...) are compared.
113 When comparecontents == True, their contents are compared.
115 try:
116 return filecmp.cmp(filename1, filename2, not comparecontents)
117 except Exception:
118 return False
121 def _SF_FileSystem__GetFilelen(systemfilepath: str) -> str: # used by SF_FileSystem.GetFilelen() Basic method
122 return str(os.path.getsize(systemfilepath))
125 def _SF_FileSystem__HashFile(filename: str, algorithm: str) -> str: # used by SF_FileSystem.HashFile() Basic method
127 Hash a given file with the given hashing algorithm
128 cfr. https://www.pythoncentral.io/hashing-files-with-python/
129 Example
130 hash = _SF_FileSystem__HashFile('myfile.txt','MD5')
132 algo = algorithm.lower()
133 try:
134 if algo in hashlib.algorithms_guaranteed:
135 BLOCKSIZE = 65535 # Provision for large size files
136 if algo == 'md5':
137 hasher = hashlib.md5()
138 elif algo == 'sha1':
139 hasher = hashlib.sha1()
140 elif algo == 'sha224':
141 hasher = hashlib.sha224()
142 elif algo == 'sha256':
143 hasher = hashlib.sha256()
144 elif algo == 'sha384':
145 hasher = hashlib.sha384()
146 elif algo == 'sha512':
147 hasher = hashlib.sha512()
148 else:
149 return ''
150 with open(filename, 'rb') as file: # open in binary mode
151 buffer = file.read(BLOCKSIZE)
152 while len(buffer) > 0:
153 hasher.update(buffer)
154 buffer = file.read(BLOCKSIZE)
155 return hasher.hexdigest()
156 else:
157 return ''
158 except Exception:
159 return ''
162 def _SF_FileSystem__Normalize(systemfilepath: str) -> str:
163 # used by SF_FileSystem.Normalize() Basic method
165 Normalize a pathname by collapsing redundant separators and up-level references so that
166 A//B, A/B/, A/./B and A/foo/../B all become A/B.
167 On Windows, it converts forward slashes to backward slashes.
169 return os.path.normpath(systemfilepath)
172 # #################################################################
173 # Platform service
174 # #################################################################
176 def _SF_Platform(propertyname: str): # used by SF_Platform Basic module
178 Switch between SF_Platform properties (read the documentation about the ScriptForge.Platform service)
180 pf = Platform()
181 if propertyname == 'Architecture':
182 return pf.Architecture
183 elif propertyname == 'ComputerName':
184 return pf.ComputerName
185 elif propertyname == 'CPUCount':
186 return pf.CPUCount
187 elif propertyname == 'CurrentUser':
188 return pf.CurrentUser
189 elif propertyname == 'Machine':
190 return pf.Machine
191 elif propertyname == 'OSName':
192 return pf.OSName
193 elif propertyname == 'OSPlatform':
194 return pf.OSPlatform
195 elif propertyname == 'OSRelease':
196 return pf.OSRelease
197 elif propertyname == 'OSVersion':
198 return pf.OSVersion
199 elif propertyname == 'Processor':
200 return pf.Processor
201 elif propertyname == 'PythonVersion':
202 return pf.PythonVersion
203 else:
204 return None
207 class Platform(object, metaclass = _Singleton):
208 @property
209 def Architecture(self): return platform.architecture()[0]
211 @property # computer's network name
212 def ComputerName(self): return platform.node()
214 @property # number of CPU's
215 def CPUCount(self): return os.cpu_count()
217 @property
218 def CurrentUser(self):
219 try:
220 return getpass.getuser()
221 except Exception:
222 return ''
224 @property # machine type e.g. 'i386'
225 def Machine(self): return platform.machine()
227 @property # system/OS name e.g. 'Darwin', 'Java', 'Linux', ...
228 def OSName(self): return platform.system().replace('Darwin', 'macOS')
230 @property # underlying platform e.g. 'Windows-10-...'
231 def OSPlatform(self): return platform.platform(aliased = True)
233 @property # system's release e.g. '2.2.0'
234 def OSRelease(self): return platform.release()
236 @property # system's version
237 def OSVersion(self): return platform.version()
239 @property # real processor name e.g. 'amdk'
240 def Processor(self): return platform.processor()
242 @property # Python major.minor.patchlevel
243 def PythonVersion(self): return 'Python ' + platform.python_version()
246 # #################################################################
247 # Session service
248 # #################################################################
250 def _SF_Session__OpenURLInBrowser(url: str): # Used by SF_Session.OpenURLInBrowser() Basic method
252 Display url using the default browser
254 try:
255 webbrowser.open(url, new = 2)
256 finally:
257 return None
260 # #################################################################
261 # String service
262 # #################################################################
264 def _SF_String__HashStr(string: str, algorithm: str) -> str: # used by SF_String.HashStr() Basic method
266 Hash a given UTF-8 string with the given hashing algorithm
267 Example
268 hash = _SF_String__HashStr('This is a UTF-8 encoded string.','MD5')
270 algo = algorithm.lower()
271 try:
272 if algo in hashlib.algorithms_guaranteed:
273 ENCODING = 'utf-8'
274 bytestring = string.encode(ENCODING) # Hashing functions expect bytes, not strings
275 if algo == 'md5':
276 hasher = hashlib.md5(bytestring)
277 elif algo == 'sha1':
278 hasher = hashlib.sha1(bytestring)
279 elif algo == 'sha224':
280 hasher = hashlib.sha224(bytestring)
281 elif algo == 'sha256':
282 hasher = hashlib.sha256(bytestring)
283 elif algo == 'sha384':
284 hasher = hashlib.sha384(bytestring)
285 elif algo == 'sha512':
286 hasher = hashlib.sha512(bytestring)
287 else:
288 return ''
289 return hasher.hexdigest()
290 else:
291 return ''
292 except Exception:
293 return ''
296 # #################################################################
297 # lists the scripts, that shall be visible inside the Basic/Python IDE
298 # #################################################################
300 g_exportedScripts = ()
302 if __name__ == "__main__":
303 print(_SF_Platform('Architecture'))
304 print(_SF_Platform('ComputerName'))
305 print(_SF_Platform('CPUCount'))
306 print(_SF_Platform('CurrentUser'))
307 print(_SF_Platform('Machine'))
308 print(_SF_Platform('OSName'))
309 print(_SF_Platform('OSPlatform'))
310 print(_SF_Platform('OSRelease'))
311 print(_SF_Platform('OSVersion'))
312 print(_SF_Platform('Processor'))
313 print(_SF_Platform('PythonVersion'))
315 print(hashlib.algorithms_guaranteed)
316 print(_SF_FileSystem__HashFile('/opt/libreoffice7.3/program/libbootstraplo.so', 'md5'))
317 print(_SF_FileSystem__HashFile('/opt/libreoffice7.3/share/Scripts/python/Capitalise.py', 'sha512'))
318 print(_SF_FileSystem__Normalize('A/foo/../B/C/./D//E'))
320 print(_SF_String__HashStr('œ∑¡™£¢∞§¶•ªº–≠œ∑´®†¥¨ˆøπ“‘åß∂ƒ©˙∆˚¬', 'MD5')) # 616eb9c513ad07cd02924b4d285b9987
322 # _SF_Session__OpenURLInBrowser('https://docs.python.org/3/library/webbrowser.html')
324 js = """
325 {"firstName": "John","lastName": "Smith","isAlive": true,"age": 27,
326 "address": {"streetAddress": "21 2nd Street","city": "New York","state": "NY","postalCode": "10021-3100"},
327 "phoneNumbers": [{"type": "home","number": "212 555-1234"},{"type": "office","number": "646 555-4567"}],
328 "children": ["Q", "M", "G", "T"],"spouse": null}
330 arr = _SF_Dictionary__ImportFromJson(js)
331 print(arr)