Coverage for pydelica/options/library.py: 73%

1 import glob

2 import logging

3 import os

4 import platform

5 import re

6 import shutil

7 import tempfile

8

9 from pydelica.exception import UnknownLibraryError

10

11

12 class LibrarySetup:

13 """Object containing setup for a particular Modelica library version"""

14

15 def __init__(self) -> None:

16 """Create a setup object

17

18 Parameters

19 ----------

20 library_folder : str, optional

21 specify location of the Modelica libraries, else use system defaults

22 """

23 self._logger = logging.getLogger("PyDelica.LibrarySetup")

24 self._libraries = []

25 self._session_library = None

26 self._libraries = self._get_system_libraries()

27

28 def __enter__(self) -> "LibrarySetup":

29 return self

30

31 def __exit__(self, *_, **__) -> None:

32 if self._session_library:

33 shutil.rmtree(self._session_library)

34

35 def use_library(self, name: str, version: str, directory: str = "") -> None:

36 """Use a specific library version

37

38 This function looks through all library directories (symlinks) and

39 checks which match the requested library. The version number of the

40 directory is then compared to requested version and is symlinked

41 (or copied if Windows), other versions are unlinked (or removed).

42

43 Parameters

44 ----------

45 name : str

46 library to select

47 version : str

48 version requested

49 directory : str, optional

50 alternative directory containing library

51

52 Raises

53 ------

54 UnknownLibraryError

55 if the library and version are not recognised

56 """

57 _test_str = f"{name} {version}"

58 self._logger.debug(f"Selecting Library '{_test_str}'")

59

60 if all(_test_str not in i for i in self._libraries):

61 raise UnknownLibraryError(

62 f"Cannot import library '{name}' version '{version}', "

63 "library not found."

64 )

65

66 if directory:

67 _libraries = glob.glob(os.path.join(directory, "*"))

68 else:

69 _libraries = self._libraries

70

71 if not self._session_library:

72 self._session_library = tempfile.mkdtemp()

73

74 for library in _libraries:

75 # If library does not contain requested library name ignore

76 # and continue

77 if name.lower() not in library.lower():

78 continue

79

80 # Create the address for the symlink/destination

81 _new_lib = os.path.join(self._session_library, os.path.basename(library))

82

83 # if no space in library filename then assume no version number

84 if " " not in os.path.basename(library):

85 continue

86

87 _name, _info = os.path.basename(library).split()

88

89 # If the split name is length 1 this means there is no

90 # version string anyway

91 if not (_version_re_search := re.findall(r"[0-9]+\.[0-9]+\.[0-9]+", _info)):

92 continue

93

94 _version: str = _version_re_search[0]

95

96 # Check that the name matches the requested library name

97 if name.lower().strip() == _name.lower().strip():

98 # Check the version matches the requested version

99 # if it does ensure this is symlinked/copied,

100 # if not unlink/remove it

101 if version.lower().strip() == _version.lower().strip():

102 if not os.path.exists(_new_lib):

103 if platform.system() != "Windows":

104 self._logger.debug(f"Linking: {library} -> {_new_lib}")

105 os.symlink(library, _new_lib)

106 elif os.path.isdir(library):

107 # Libraries are directories in Windows

108 self._logger.debug(f"Copying: {library} -> {_new_lib}")

109 shutil.copytree(library, _new_lib, symlinks=True)

110 else:

111 # Libraries are directories in Windows

112 self._logger.debug(f"Copying: {library} -> {_new_lib}")

113 shutil.copyfile(library, _new_lib)

114 elif os.path.exists(_new_lib):

115 if platform.system() != "Windows":

116 self._logger.debug(f"Unlinking: {_new_lib} -> {library}")

117 os.unlink(_new_lib)

118 else:

119 self._logger.debug(f"Removing: {_new_lib}")

120 # Libraries are directories in Windows

121 if os.path.isdir(_new_lib):

122 shutil.rmtree(_new_lib)

123 else:

124 os.remove(_new_lib)

125

126 @property

127 def session_library(self) -> str | None:

128 return self._session_library

129

130 def _get_system_libraries(self) -> list[str]:

131 if "MODELICAPATH" in os.environ:

132 _library_dirs = os.environ["MODELICAPATH"].split(os.pathsep)

133 _libs = []

134 for library_dir in _library_dirs:

135 _libs += glob.glob(os.path.join(library_dir, "*"))

136 elif platform.system() == "Windows":

137 _home = os.environ["USERPROFILE"]

138 _user_libraries = os.path.join(

139 _home, "AppData", "Roaming", ".openmodelica", "libraries"

140 )

141 _library_dir = os.path.join(

142 os.environ["OPENMODELICAHOME"], "lib", "omlibrary"

143 )

144 _libs = glob.glob(os.path.join(_user_libraries, "*"))

145 _libs += glob.glob(os.path.join(_library_dir, "*"))

146 else:

147 # Try typical linux locations

148 _library_dirs = [

149 "/usr/lib/omlibrary",

150 f"{os.environ['HOME']}/.openmodelica/libraries",

151 ]

152 _libs = []

153 for library_dir in _library_dirs:

154 _libs += glob.glob(os.path.join(library_dir, "*"))

155 return _libs