-
Notifications
You must be signed in to change notification settings - Fork 77
Read target connection settings from a YAML file #670
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ | |
| from itertools import groupby | ||
| from operator import itemgetter | ||
| from weakref import WeakSet | ||
| from ruamel.yaml import YAML | ||
|
|
||
| import ctypes | ||
| import logging | ||
|
|
@@ -590,6 +591,30 @@ def __str__(self): | |
| return message.format(self.filepath, self.lineno, self.message) | ||
|
|
||
|
|
||
| def load_struct_from_yaml(filepath): | ||
| """ | ||
| Parses a config structure from a YAML file. | ||
| The structure should be composed of basic Python types. | ||
|
|
||
| :param filepath: Input file which contains YAML data. | ||
| :type filepath: str | ||
|
|
||
| :raises LoadSyntaxError: if there is a syntax error in YAML data. | ||
|
|
||
| :return: A dictionary which contains parsed YAML data | ||
| :rtype: Dict | ||
| """ | ||
|
|
||
| try: | ||
| yaml = YAML(typ='safe', pure=True) | ||
| with open(filepath, 'r', encoding='utf-8') as file_handler: | ||
| return yaml.load(file_handler) | ||
| except yaml.YAMLError as ex: | ||
| message = ex.message if hasattr(ex, 'message') else '' | ||
| lineno = ex.problem_mark.line if hasattr(ex, 'problem_mark') else None | ||
| raise LoadSyntaxError(message, filepath=filepath, lineno=lineno) from ex | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what LoadSyntaxError was initially for. Currently it seems like dead code in devlib. Unless it really adds something, I'd recommend just letting the YAMLError bubble up.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use this in WA to try and provide a more friendly error message to clearly indicate the location of the syntax error.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good, I did not look beyond devlib codebase |
||
|
|
||
|
|
||
| RAND_MOD_NAME_LEN = 30 | ||
| BAD_CHARS = string.punctuation + string.whitespace | ||
| TRANS_TABLE = str.maketrans(BAD_CHARS, '_' * len(BAD_CHARS)) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| LocalLinuxTarget: | ||
| entry-0: | ||
| connection_settings: | ||
| unrooted: True | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,35 +14,63 @@ | |
| # limitations under the License. | ||
| # | ||
|
|
||
| """Module for testing targets.""" | ||
|
|
||
| import os | ||
| import shutil | ||
| import tempfile | ||
| from unittest import TestCase | ||
| from pprint import pp | ||
| import pytest | ||
|
|
||
| from devlib import LocalLinuxTarget | ||
| from devlib.utils.misc import load_struct_from_yaml | ||
|
|
||
|
|
||
| def build_targets(): | ||
| """Read targets from a YAML formatted config file""" | ||
|
|
||
| config_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'target_configs.yaml') | ||
|
|
||
| target_configs = load_struct_from_yaml(config_file) | ||
| if target_configs is None: | ||
| raise ValueError(f'{config_file} looks empty!') | ||
|
|
||
| targets = [] | ||
|
|
||
| if target_configs.get('LocalLinuxTarget') is not None: | ||
| print('> LocalLinux targets:') | ||
| for entry in target_configs['LocalLinuxTarget'].values(): | ||
| pp(entry) | ||
| ll_target = LocalLinuxTarget(connection_settings=entry['connection_settings']) | ||
| targets.append(ll_target) | ||
|
|
||
| return targets | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("target", build_targets()) | ||
| def test_read_multiline_values(target): | ||
| """ | ||
| Test Target.read_tree_values_flat() | ||
| class TestReadTreeValues(TestCase): | ||
| :param target: Type of target per :class:`Target` based classes. | ||
| :type target: Target | ||
| """ | ||
|
|
||
| def test_read_multiline_values(self): | ||
| data = { | ||
| 'test1': '1', | ||
| 'test2': '2\n\n', | ||
| 'test3': '3\n\n4\n\n', | ||
| } | ||
| data = { | ||
| 'test1': '1', | ||
| 'test2': '2\n\n', | ||
| 'test3': '3\n\n4\n\n', | ||
| } | ||
|
|
||
| tempdir = tempfile.mkdtemp(prefix='devlib-test-') | ||
| for key, value in data.items(): | ||
| path = os.path.join(tempdir, key) | ||
| with open(path, 'w') as wfh: | ||
| wfh.write(value) | ||
| tempdir = tempfile.mkdtemp(prefix='devlib-test-') | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should use tempfile.TemporaryDirectory, as a context manager.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Latest version of this code: with target.make_temp() as tempdir:
...
raw_result = target.read_tree_values_flat(tempdir)
result = {os.path.basename(k): v for k, v in raw_result.items()}And this is @contextmanager
def make_temp(self, is_directory=True, directory='', prefix='devlib-test'):
...
try:
cmd = f'mktemp -p {directory} {prefix}-XXXXXX'
if is_directory:
cmd += ' -d'
temp_obj = self.execute(cmd).strip()
yield temp_obj
finally:
if temp_obj is not None:
self.remove(temp_obj) |
||
| for key, value in data.items(): | ||
| path = os.path.join(tempdir, key) | ||
| with open(path, 'w', encoding='utf-8') as wfh: | ||
| wfh.write(value) | ||
|
|
||
| t = LocalLinuxTarget(connection_settings={'unrooted': True}) | ||
| raw_result = t.read_tree_values_flat(tempdir) | ||
| result = {os.path.basename(k): v for k, v in raw_result.items()} | ||
| raw_result = target.read_tree_values_flat(tempdir) | ||
| result = {os.path.basename(k): v for k, v in raw_result.items()} | ||
|
|
||
| shutil.rmtree(tempdir) | ||
| shutil.rmtree(tempdir) | ||
|
|
||
| self.assertEqual({k: v.strip() | ||
| for k, v in data.items()}, | ||
| result) | ||
| assert {k: v.strip() for k, v in data.items()} == result | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now we don't want the testing infrastructure to be a public API, so this needs a non-public name starting with a single underscore (https://peps.python.org/pep-0008/)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
load_struct_from_yaml()is not for testing purposes only.It's just moved from WA to devlib: ARM-software/workload-automation#1248