Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
yaml: prefer importing the system yaml module
Check for the existence of yaml in the system libs before going the Return an error when no module is found. Using the system yaml guarantees that safe_load and safe_dump works. LP: #2007234 LP: #2007241 Signed-off-by: Renan Rodrigo <renanrodrigo@canonical.com>
- Loading branch information
1 parent
3c5593c
commit 4d786ae
Showing
13 changed files
with
163 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import mock | ||
import pytest | ||
|
||
from uaclient.exceptions import UserFacingError | ||
from uaclient.yaml import get_imported_yaml_module | ||
|
||
M_PREFIX = "uaclient.yaml." | ||
|
||
|
||
@mock.patch(M_PREFIX + "sys") | ||
class TestYamlImport: | ||
def test_get_yaml_module_returns_imported(self, m_sys): | ||
imported_yaml = mock.MagicMock() | ||
m_sys.modules = {"yaml": imported_yaml} | ||
yaml = get_imported_yaml_module() | ||
assert yaml is imported_yaml | ||
|
||
@pytest.mark.parametrize("has_yaml_in_system", (True, False)) | ||
@mock.patch(M_PREFIX + "importlib_machinery") | ||
@mock.patch(M_PREFIX + "importlib_util") | ||
def test_get_yaml_module_imports_from_system( | ||
self, m_util, m_machinery, m_sys, has_yaml_in_system | ||
): | ||
m_sys.modules = {} | ||
m_finder = mock.MagicMock() | ||
m_machinery.PathFinder.return_value = m_finder | ||
|
||
m_yaml_spec = mock.MagicMock() | ||
m_yaml_module = mock.MagicMock() | ||
|
||
m_util.module_from_spec.return_value = m_yaml_module | ||
|
||
if has_yaml_in_system: | ||
m_finder.find_spec.return_value = m_yaml_spec | ||
else: | ||
m_finder.find_spec.return_value = None | ||
|
||
if has_yaml_in_system: | ||
yaml = get_imported_yaml_module() | ||
|
||
assert m_yaml_module == yaml | ||
assert [ | ||
mock.call("yaml", path=["/usr/lib/python3/dist-packages"]) | ||
] == m_finder.find_spec.call_args_list | ||
assert [ | ||
mock.call(m_yaml_spec) | ||
] == m_util.module_from_spec.call_args_list | ||
assert m_sys.modules["yaml"] == m_yaml_module | ||
assert [ | ||
mock.call(m_yaml_module) | ||
] == m_yaml_spec.loader.exec_module.call_args_list | ||
else: | ||
with pytest.raises(UserFacingError) as excinfo: | ||
yaml = get_imported_yaml_module() | ||
|
||
assert "missing-yaml-module" == excinfo.value.msg_code | ||
assert "Couldn't import the YAML module" in excinfo.value.msg | ||
assert [ | ||
mock.call("yaml", path=["/usr/lib/python3/dist-packages"]) | ||
] == m_finder.find_spec.call_args_list |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import sys | ||
from importlib import machinery as importlib_machinery | ||
from importlib import util as importlib_util | ||
|
||
from uaclient.exceptions import UserFacingError | ||
from uaclient.messages import MISSING_YAML_MODULE | ||
|
||
|
||
def get_imported_yaml_module(): | ||
# Maybe we already imported it? | ||
if "yaml" in sys.modules: | ||
return sys.modules["yaml"] | ||
|
||
# Try to get the system yaml module | ||
finder = importlib_machinery.PathFinder() | ||
pyyaml_spec = finder.find_spec( | ||
"yaml", path=["/usr/lib/python3/dist-packages"] | ||
) | ||
# If there is no system 'yaml' module (which is weird enough), | ||
# then raise an exception asking for it to be there | ||
if pyyaml_spec is None: | ||
raise UserFacingError( | ||
MISSING_YAML_MODULE.msg, MISSING_YAML_MODULE.name | ||
) | ||
|
||
# Import it! | ||
yaml_module = importlib_util.module_from_spec(pyyaml_spec) | ||
sys.modules["yaml"] = yaml_module | ||
loader = pyyaml_spec.loader | ||
# The loader shouldn't be None, documentation says: | ||
# "The finder should always set this attribute." | ||
# But mypy complains, so we check anyway. | ||
if loader is not None: | ||
loader.exec_module(yaml_module) | ||
|
||
return yaml_module | ||
|
||
|
||
yaml = get_imported_yaml_module() | ||
|
||
safe_load = yaml.safe_load | ||
safe_dump = yaml.safe_dump | ||
parser = yaml.parser |