Skip to content

Commit 0c6fc28

Browse files
committed
Refactor _Utils.py to improve module import handling
1 parent 34a6614 commit 0c6fc28

File tree

5 files changed

+56
-28
lines changed

5 files changed

+56
-28
lines changed

src/iop/_utils.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def filename_to_module(filename) -> str:
176176
return module
177177

178178
@staticmethod
179-
def migrate(filename=None,root_path=None):
179+
def migrate(filename=None):
180180
"""
181181
Read the settings.py file and register all the components
182182
settings.py file has two dictionaries:
@@ -188,17 +188,23 @@ def migrate(filename=None,root_path=None):
188188
* key: the name of the production
189189
* value: a dictionary containing the settings for the production
190190
"""
191-
try:
192-
# if the filename is not provided
193-
if filename is None:
194-
settings = importlib.import_module('settings')
191+
path = None
192+
# try to load the settings file
193+
if filename:
194+
# check if the filename is absolute or relative
195+
if os.path.isabs(filename):
196+
path = os.path.dirname(filename)
195197
else:
196-
# import the settings file
197-
settings = _Utils.import_module_from_path('settings',filename)
198-
# get the path of the settings file
199-
path = os.path.dirname(inspect.getfile(settings))
200-
except ModuleNotFoundError as e:
201-
raise ModuleNotFoundError("settings.py not found") from e
198+
raise ValueError("The filename must be absolute")
199+
# add the path to the system path to the beginning
200+
sys.path.insert(0,os.path.normpath(path))
201+
# import settings from the specified file
202+
settings = _Utils.import_module_from_path('settings',filename)
203+
else:
204+
# import settings from the settings module
205+
import settings
206+
# get the path of the settings file
207+
path = os.path.dirname(inspect.getfile(settings))
202208
try:
203209
# set the classes settings
204210
_Utils.set_classes_settings(settings.CLASSES,path)
@@ -209,22 +215,25 @@ def migrate(filename=None,root_path=None):
209215
_Utils.set_productions_settings(settings.PRODUCTIONS,path)
210216
except AttributeError:
211217
print("No productions to register")
218+
try:
219+
# remove the path from the system path (with or without the trailing slash)
220+
sys.path.remove(path+'/')
221+
sys.path.remove(path)
222+
except ValueError:
223+
pass
212224

213225
@staticmethod
214226
def import_module_from_path(module_name, file_path):
215227
if not os.path.isabs(file_path):
216-
file_path = os.path.abspath(file_path)
217-
# check is a file is persent at the path
218-
if not os.path.isfile(file_path):
219-
# append settings.py to the path
220-
file_path = os.path.join(file_path,'settings.py')
228+
raise ValueError("The file path must be absolute")
221229

222230
spec = importlib.util.spec_from_file_location(module_name, file_path)
223231
if spec is None:
224232
raise ImportError(f"Cannot find module named {module_name} at {file_path}")
225233

226234
module = importlib.util.module_from_spec(spec)
227235
sys.modules[module_name] = module
236+
spec.loader.exec_module(module)
228237
return module
229238

230239
@staticmethod

src/iop/cls/IOP/Utils.cls

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,10 @@ ClassMethod GetRemoteClassInfo(
104104
pModule As %String,
105105
pClasspaths As %String,
106106
ByRef pClassDetails,
107-
ByRef pRemoteSettings) As %Status [ Internal, Private ]
107+
ByRef pRemoteSettings) As %Status [ Internal ]
108108
{
109109
#dim tSC As %Status = $$$OK
110110
#dim ex As %Exception.AbstractException
111-
#dim tGateway As %External.Gateway
112-
#dim tGatewayProxy As %Net.Remote.Object
113111

114112
Try {
115113
if pClasspaths '="" {
@@ -120,14 +118,19 @@ ClassMethod GetRemoteClassInfo(
120118
set onePath = $p(extraClasspaths,"|",i)
121119
set onePath = ##class(%File).NormalizeDirectory(onePath)
122120
if onePath?1"$$IRISHOME"1P.E set onePath = $e($system.Util.InstallDirectory(),1,*-1)_$e(onePath,11,*)
123-
if onePath'="" && '$FIND(sys.path,onePath) do sys.path.insert(0, onePath)
121+
if onePath'="" do ##class(IOP.Common).SetPythonPath(onePath)
124122
}
125123
}
126-
;
127124

128125
set importlib = ##class(%SYS.Python).Import("importlib")
129126
set builtins = ##class(%SYS.Python).Import("builtins")
130-
set module = importlib."import_module"(pModule)
127+
set sys = ##class(%SYS.Python).Import("sys")
128+
// Load the module form a specific path
129+
set spec = importlib.util."spec_from_file_location"(pModule, onePath_pModule_".py")
130+
set module = importlib.util."module_from_spec"(spec)
131+
do sys.modules."__setitem__"(pModule, module)
132+
do spec.loader."exec_module"(module)
133+
// Get the class
131134
set class = builtins.getattr(module, pRemoteClassname)
132135
set tClass = class."__new__"(class)
133136

src/tests/test_bench.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
import timeit
55
import os
66

7-
import sys
8-
97
class TestBenchIoP:
108

119
#before all tests
1210
@classmethod
1311
def setup_class(cls):
12+
# get abspath of 'src/tests/bench'
13+
path = os.path.abspath('src/tests/bench/settings.py')
1414
# migrate the production
15-
_Utils.migrate('src/tests/bench')
15+
_Utils.migrate(path)
1616
# stop all productions
1717
_Director.stop_production()
1818
# set the default production
@@ -43,4 +43,3 @@ def teardown_class(cls):
4343
_Director.stop_production()
4444
# set the default production
4545
_Director.set_default_production('test')
46-

src/tests/test_iop_utils.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,23 @@ def test_setup():
2929
except RuntimeError as e:
3030
assert False
3131

32+
def test_migrate_then_register():
33+
# get the path of the current file
34+
path = os.path.dirname(os.path.realpath(__file__))
35+
# get abspath of 'src/tests/bench'
36+
path_migrate = os.path.join(path, 'bench/settings.py')
37+
# migrate the production
38+
_Utils.migrate(path_migrate)
39+
# register the component
40+
module = 'bo'
41+
classname = 'EmailOperation'
42+
# join the registerFolder to the path
43+
path = os.path.join(path, 'registerFilesIop')
44+
overwrite = 1
45+
iris_classname = 'UnitTest.EmailOperation'
46+
result = _Utils.register_component(module, classname, path, overwrite, iris_classname)
47+
48+
3249
def test_register_component():
3350
module = 'bo'
3451
classname = 'EmailOperation'
@@ -253,6 +270,6 @@ def test_migrate_only_classes():
253270
# add mock_settings to sys.modules and __file__ to mock_settings
254271
with patch.dict('sys.modules', {'settings': mock_settings}):
255272
# Act
256-
_Utils.migrate('/path/to/settings/settings.py')
273+
_Utils.migrate()
257274
# Assert
258275
assert True # if no exception is raised, the test is ok

src/tests/test_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,6 @@ def test_migrate_only_classes():
237237
# add mock_settings to sys.modules and __file__ to mock_settings
238238
with patch.dict('sys.modules', {'settings': mock_settings}):
239239
# Act
240-
_Utils.migrate('/path/to/settings/settings.py')
240+
_Utils.migrate()
241241
# Assert
242242
assert True # if no exception is raised, the test is ok

0 commit comments

Comments
 (0)