]> SALOME platform Git repositories - modules/kernel.git/blob - bin/SalomeOnDemandTK/extension_utilities.py
Salome HOME
[bos #32522][EDF] SALOME on Demand. Small refact: changed log output, edit docstrings.
[modules/kernel.git] / bin / SalomeOnDemandTK / extension_utilities.py
1 #!/usr/bin/env python3
2 # -*- coding:utf-8 -*-
3 # Copyright (C) 2007-2022  CEA/DEN, EDF R&D, OPEN CASCADE
4 #
5 # Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
6 # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
7 #
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
12 #
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 # Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21 #
22 # See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 #
24
25 #  File   : extension_utilities.py
26 #  Author : Konstantin Leontev, Open Cascade
27 #
28 #  @package SalomeOnDemandTK
29 #  @brief Utilities and constants those help to deal with salome extension files.
30
31 """
32 Utilities and constants those help to deal with salome extension files.
33 """
34
35 import os
36 import logging
37 import json
38 from traceback import format_exc
39
40 # Setup logger's output
41 FORMAT = '%(funcName)s():%(lineno)s: %(message)s'
42 logging.basicConfig(format=FORMAT, level=logging.DEBUG)
43 logger = logging.getLogger()
44
45 SALOME_EXTDIR = '__SALOME_EXT__'
46 ARCFILE_EXT = 'salomex'
47 BFILE_EXT = 'salomexb'
48 CFILE_EXT = 'salomexc'
49 DFILE_EXT = 'salomexd'
50 PYFILE_EXT = 'py'
51 ENVPYFILE_SUF = '_env.py'
52
53 EXTNAME_KEY = 'name'
54 EXTDESCR_KEY = 'descr'
55 EXTDEPENDSON_KEY = 'depends_on'
56 EXTAUTHOR_KEY = 'author'
57 EXTCOMPONENT_KEY = 'components'
58
59
60 def create_salomexd(name, descr='', depends_on=None, author='', components=None):
61     """
62     Create a basic salomexd file from provided args.
63     Current version is a json file with function args as the keys.
64
65     Args:
66         name - the name of the corresponding salome extension.
67         depends_on - list of the modules that current extension depends on.
68         author - an author of the extension.
69         components - the names of the modules those current extension was built from.
70
71     Returns:
72         None
73     """
74
75     logger.debug('Create salomexd file...')
76
77     if depends_on is None:
78         depends_on = []
79
80     if components is None:
81         components = []
82
83     json_string = json.dumps(
84         {
85             EXTNAME_KEY: name,
86             EXTDESCR_KEY: descr,
87             EXTDEPENDSON_KEY: depends_on,
88             EXTAUTHOR_KEY: author,
89             EXTCOMPONENT_KEY: components
90         },
91         indent=4
92     )
93
94     try:
95         with open(name + '.' + DFILE_EXT, "w", encoding="utf-8") as file:
96             file.write(json_string)
97
98     except OSError:
99         logger.error(format_exc())
100
101
102 def read_salomexd(file_path):
103     """
104     Reads a content of a salomexd file. Current version is a json file.
105     There's no check if the file_path is a valid salomexd file name.
106     It's expected that user call isvalid_filename() in advance.
107
108     Args:
109         file_path - the path to the salomexd file.
110
111     Returns:
112         A dictionary that represents the content of the salomexd file.
113     """
114
115     logger.debug('Read salomexd file %s', file_path)
116
117     try:
118         with open(file_path, 'r', encoding='UTF-8') as file:
119             return json.load(file)
120
121     except OSError:
122         logger.error(format_exc())
123         return {}
124
125
126 def create_salomexb(name, included):
127     """
128     Create a salomexb file from a list of included file names.
129     For example:
130     */*.py
131     doc/*.html
132     doc/*.jp
133
134     Args:
135         name - the name of the corresponding salome extension.
136         included - list of the directories those must be included inside a salomex.
137
138     Returns:
139         None
140     """
141
142     logger.debug('Create salomexb file...')
143
144     try:
145         with open(name + '.' + BFILE_EXT, "w", encoding="utf-8") as file:
146             file.write('\n'.join(included[0:]))
147
148     except OSError:
149         logger.error(format_exc())
150
151
152 def read_salomexb(file_path):
153     """
154     Returns a content af a salomexb file as a list of strings.
155     There's no check if the file_path is a valid salomexb file name.
156     It's expected that user call isvalid_filename() in advance.
157
158     Args:
159         file_path - the path to the salomexb file.
160
161     Returns:
162         List of strings - paths to the directories those must be included in
163         corresponding salomex archive file.
164     """
165
166     logger.debug('Read salomexb file %s', file_path)
167
168     try:
169         with open(file_path, 'r', encoding='UTF-8') as file:
170             return [line.rstrip() for line in file]
171
172     except OSError:
173         logger.error(format_exc())
174         return []
175
176
177 def list_files(dir_path):
178     """
179     Returns the recursive list of relative paths to files as strings
180     in the dir_path root directory and all subdirectories.
181
182     Args:
183         dir_path - the path to the directory where you search for files.
184
185     Raises:
186         Raises OSError exception.
187
188     Returns:
189         A list of relative paths to files inside the given directory.
190     """
191
192     files_list = []
193     for root, _, files in os.walk(dir_path):
194         for file in files:
195             files_list += os.path.relpath(os.path.join(root, file), dir_path)
196
197     return files_list
198
199
200 def list_files_filter(dir_path, filter_patterns):
201     """
202     Returns the recursive list of relative paths to files as strings
203     in the dir_path root directory and all subdirectories.
204
205     Args:
206         dir_path - the path to the directory where you search for files.
207         filter_patterns - list of expressions for matching file names.
208
209     Returns:
210         files_abs - a list of absolute paths to selected files.
211         files_rel - a list of relative paths to selected files.
212     """
213
214     logger.debug('Get list of files to add into archive...')
215
216     files_abs = []
217     files_rel = []
218
219     for root, _, files in os.walk(dir_path):
220         for file in files:
221             for pattern in filter_patterns:
222                 filename_abs = os.path.join(root, file)
223                 filename_rel = os.path.relpath(filename_abs, dir_path)
224
225                 if filename_rel.startswith(pattern):
226                     logger.debug('File name %s matches pattern %s', filename_rel, pattern)
227                     files_abs.append(filename_abs)
228                     files_rel.append(filename_rel)
229
230     return files_abs, files_rel
231
232
233 def list_files_ext(dir_path, ext):
234     """
235     Returns a list of abs paths to files with a given extension
236     in the dir_path directory.
237
238     Args:
239         dir_path - the path to the directory where you search for files.
240         ext - a given extension.
241
242     Returns:
243         A list of absolute paths to selected files.
244     """
245
246     logger.debug('Get list of files with extension %s...', ext)
247
248     dot_ext = '.' + ext
249     return [os.path.join(dir_path, f) for f in os.listdir(dir_path) if f.endswith(dot_ext)]
250
251
252 def list_tonewline_str(str_list):
253     """
254     Converts the given list of strings to a newline separated string.
255
256     Args:
257         dir_path - the path to the directory where you search for files.
258
259     Returns:
260         A newline separated string.
261     """
262     return '\n'.join(file for file in str_list)
263
264
265 def isvalid_filename(filename, extension):
266     """
267     Checks if a given filename is valid in a sense that it exists and have a given extension.
268
269     Args:
270         filename - the name of the file to check.
271         extension - expected file name extension.
272
273     Returns:
274         True if the given filename is valid for given extension.
275     """
276
277     logger.debug('Check if the filename %s exists and has an extension %s', filename, extension)
278
279     # First do we even have something to check here
280     if filename == '' or extension == '':
281         logger.error('A filename and extension cannot be empty! Args: filename=%s, extension=%s',
282             filename, extension)
283         return False
284
285     # Check if the filename matchs the provided extension
286     _, ext = os.path.splitext(filename)
287     ext = ext.lstrip('.')
288     if ext != extension:
289         logger.error('The filename %s doesnt have a valid extension! \
290             The valid extension must be: %s, but get: %s',
291             filename, extension, ext)
292         return False
293
294     # Check if the file base name is not empty
295     base_name = os.path.basename(filename)
296     if base_name == '':
297         logger.error('The file name %s has an empty base name!', filename)
298         return False
299
300     # Check if a file with given filename exists
301     if not os.path.isfile(filename):
302         logger.error('The filename %s is not an existing regular file!', filename)
303         return False
304
305     logger.debug('Filename %s exists and has an extension %s', filename, extension)
306     return True
307
308
309 def isvalid_dirname(dirname):
310     """
311     Checks if a given directory name exists.
312
313     Args:
314         dirname - the name of the directory to check.
315
316     Returns:
317         True if the given dirname is valid.
318     """
319
320     logger.debug('Check if the dirname %s exists', dirname)
321
322     # First do we even have something to check here
323     if dirname == '':
324         logger.error('A dirname argument cannot be empty! dirname=%s', dirname)
325         return False
326
327     # Check if a file with given filename exists
328     if not os.path.isdir(dirname):
329         logger.error('The dirname %s is not an existing regular file!', dirname)
330         return False
331
332     logger.debug('Directory %s exists', dirname)
333     return True
334
335
336 def list_dependants(install_dir, salomex_name):
337     """
338     Checks if we have installed extensions those depend on a given extension.
339
340     Args:
341         install_dir - path to SALOME install root directory.
342         salomex_name - a name of salome extension to check.
343
344     Returns:
345         True if the given extension has dependants.
346     """
347
348     logger.debug('Check if there are other extensions that depends on %s...', salomex_name)
349     dependants = []
350     salomexd_files = list_files_ext(install_dir, DFILE_EXT)
351
352     for salomexd_file in salomexd_files:
353         dependant_name, _ = os.path.splitext(os.path.basename(salomexd_file))
354
355         # Don't process <salomex_name> extension itself
356         if dependant_name == salomex_name:
357             continue
358
359         logger.debug('Check dependencies for %s...', salomexd_file)
360         salomexd_content = read_salomexd(salomexd_file)
361
362         if EXTDEPENDSON_KEY in salomexd_content and salomexd_content[EXTDEPENDSON_KEY]:
363             depends_on = salomexd_content[EXTDEPENDSON_KEY]
364             logger.debug('List of dependencies: %s', depends_on)
365
366             if salomex_name in depends_on:
367                 dependants.append(dependant_name)
368
369     if len(dependants) > 0:
370         logger.debug('Followed extensions %s depend on %s',
371             dependants, salomex_name)
372
373     return dependants
374
375
376 def is_empty_dir(directory):
377     """
378     Checks if the given directory is empty.
379
380     Args:
381         directory - path to directory to check.
382
383     Returns:
384         True if the given directory is empty.
385     """
386
387     return not next(os.scandir(directory), None)
388
389
390 def find_file(directory, file_name):
391     """
392     Finds a file in the given directory.
393
394     Args:
395         directory - path to directory to check.
396         file_name - given base filename with extension
397
398     Returns:
399         Abs path if the file exist, otherwise None.
400     """
401
402     logger.debug('Try to find %s file in %s...', file_name, directory)
403     file = os.path.join(directory, file_name)
404     if os.path.isfile(file):
405         logger.debug('File %s exists.', file)
406         return file
407
408     logger.debug('File %s doesnt\'t exist. Return None.', file)
409     return None
410
411
412 def find_salomexd(install_dir, salomex_name):
413     """
414     Finds a salomexd file for the given extension.
415
416     Args:
417         install_dir - path to directory to check.
418         salomex_name - extension's name.
419
420     Returns:
421         Abs path if the file exist, otherwise None.
422     """
423
424     return find_file(install_dir, salomex_name + '.' + DFILE_EXT)
425
426
427 def find_salomexc(install_dir, salomex_name):
428     """
429     Finds a salomexc file for the given extension.
430
431     Args:
432         install_dir - path to directory to check.
433         salomex_name - extension's name.
434
435     Returns:
436         Abs path if the file exist, otherwise None.
437     """
438
439     return find_file(install_dir, salomex_name + '.' + CFILE_EXT)
440
441
442 def find_envpy(install_dir, salomex_name):
443     """
444     Finds a _env.py file for the given extension.
445
446     Args:
447         install_dir - path to directory to check.
448         salomex_name - extension's name.
449
450     Returns:
451         Abs path if the file exist, otherwise None.
452     """
453
454     return find_file(install_dir, salomex_name + ENVPYFILE_SUF)