Skip to content

Commit f71d115

Browse files
authored
Merge pull request #457 from linkml/issue-9203_compile_python_module_name
module_name parameter for compile_python resolves linkml/linkml#2903
2 parents a1381ca + 7438906 commit f71d115

File tree

2 files changed

+78
-9
lines changed

2 files changed

+78
-9
lines changed

linkml_runtime/utils/compile_python.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import os
24
import sys
35
from logging import warning
@@ -7,28 +9,32 @@
79
def file_text(txt_or_fname: str) -> str:
810
"""
911
Determine whether text_or_fname is a file name or a string and, if a file name, read it
10-
:param txt_or_fname:
11-
:return:
12+
13+
:param txt_or_fname: Text content or filename to read
14+
:return: File content as string
1215
"""
1316
if len(txt_or_fname) > 4 and "\n" not in txt_or_fname:
1417
with open(txt_or_fname) as ef:
1518
return ef.read()
1619
return txt_or_fname
1720

1821

19-
def compile_python(text_or_fn: str, package_path: str = None) -> ModuleType:
22+
def compile_python(text_or_fn: str, package_path: str | None = None, module_name: str | None = "test") -> ModuleType:
2023
"""
2124
Compile the text or file and return the resulting module
22-
@param text_or_fn: Python text or file name that references python file
23-
@param package_path: Root package path. If omitted and we've got a python file, the package is the containing
24-
directory
25-
@return: Compiled module
25+
26+
:param text_or_fn: Python text or file name that references python file
27+
:param package_path: Root package path. If omitted and we've got a python file, the package is the containing directory
28+
:param module_name: Used in an import statement, default 'test'
29+
:return: Compiled module
2630
"""
31+
if not module_name:
32+
module_name = "test"
2733
python_txt = file_text(text_or_fn)
2834
if package_path is None and python_txt != text_or_fn:
2935
package_path = text_or_fn
30-
spec = compile(python_txt, "test", "exec")
31-
module = ModuleType("test")
36+
spec = compile(python_txt, module_name, "exec")
37+
module = ModuleType(module_name)
3238
if package_path:
3339
package_path_abs = os.path.join(os.getcwd(), package_path)
3440
# We have to calculate the path to expected path relative to the current working directory
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from __future__ import annotations
2+
3+
from types import ModuleType
4+
5+
import pytest
6+
7+
from linkml_runtime.utils.compile_python import compile_python
8+
9+
10+
@pytest.fixture(scope="module")
11+
def base_module() -> str:
12+
return """
13+
x: int = 2
14+
15+
def fun(value: int):
16+
return f'known value {value}'
17+
"""
18+
19+
20+
@pytest.fixture(scope="module")
21+
def importing_module() -> str:
22+
return """
23+
import MODULE_NAME as m
24+
25+
def more_fun(message: str):
26+
return f'got "{message}"'
27+
"""
28+
29+
30+
def check_generated_module(module: ModuleType, module_name: str) -> None:
31+
assert isinstance(module, ModuleType)
32+
assert module.__name__ == module_name
33+
assert module.x == 2
34+
assert module.fun(3) == "known value 3"
35+
36+
37+
@pytest.mark.parametrize(("name_arg", "module_name"), [(None, "test"), ("", "test"), ("base_module", "base_module")])
38+
def test_compile_python_module_name(base_module: str, name_arg: str | None, module_name: str) -> None:
39+
"""Test the compilation of python code to create a module."""
40+
m = compile_python(base_module, module_name=name_arg)
41+
check_generated_module(m, module_name)
42+
43+
44+
@pytest.mark.parametrize(("name_arg", "module_name"), [(None, "test"), ("", "test"), ("base_module", "base_module")])
45+
def test_compile_python_importing_module_local_module(
46+
base_module: str,
47+
importing_module: str,
48+
name_arg: str | None,
49+
module_name: str,
50+
) -> None:
51+
"""Test the compilation of python code to create a local module and then compile a second module that imports the first."""
52+
m = compile_python(base_module, module_name=name_arg)
53+
check_generated_module(m, module_name)
54+
55+
# switch in the appropriate module name
56+
importing_module_text = importing_module.replace("MODULE_NAME", module_name)
57+
m2 = compile_python(importing_module_text, package_path=".", module_name="module_2")
58+
assert isinstance(m2, ModuleType)
59+
assert m2.__name__ == "module_2"
60+
assert m2.more_fun("hello") == 'got "hello"'
61+
62+
# check the imported module, m2.m, has the correct type, name, etc.
63+
check_generated_module(m2.m, module_name)

0 commit comments

Comments
 (0)