Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions docs/contents/specification/utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,14 @@ description: "Utility functions specification"
This page specifies utilities from `inorbit_connector.utils`.

(spec-utils-readyaml)=
## `read_yaml(fname, robot_id=None) -> dict`
## `read_yaml(fname) -> dict`

Reads a YAML file and returns a dictionary.

Behavior:

- If the file is empty (YAML `null` / no content), returns `{}`.
- If `robot_id` is **not** provided, returns the entire YAML object.
- If `robot_id` **is** provided and is a top-level key in the YAML object, returns `data[robot_id]` and emits a **DeprecationWarning** (this selection format is deprecated).
- If `robot_id` is provided but not found, raises `IndexError`.

Notes:

- New configurations should follow the `ConnectorConfig` schema described in [Configuration](../configuration).
- Otherwise, returns the parsed YAML content.

## Constants

Expand Down
39 changes: 5 additions & 34 deletions inorbit_connector/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
#
# SPDX-License-Identifier: MIT

# Standard
import os

# Third-party
import yaml
import os
import warnings

# Constants
DEFAULT_TIMEZONE = "UTC"
Expand All @@ -17,48 +18,18 @@
)


def read_yaml(fname: str, robot_id: str = None) -> dict:
def read_yaml(fname: str) -> dict:
"""Reads a YAML file and returns the data as a dictionary.

Loads the specified configuration file and returns an object corresponding to the
given robot_id or the entire file if no robot_id is provided.

* If no robot_id is provided, the entire configuration file is returned.
* If the configuration file is empty, an empty dictionary is returned.

Args:
fname (str): The file name or path of the YAML file
robot_id (str, optional, deprecated): The ID of the robot to retrieve from the
YAML or None to return the entire file

Returns:
dict: The data read from the YAML file as a dictionary
Raises:
IndexError: If the specified robot ID is not found in the abstract file
FileNotFoundError: If the configuration file does not exist
yaml.YAMLError: If the configuration file is not valid YAML
"""
with open(fname, "r") as file:
data = yaml.safe_load(file)

# When the file is empty, data is None
if not data:
data = {}

# If the `robot_id` is not provided, return the entire abstract.
if not robot_id:
return data

# If the `robot_id` is provided, return that abstract robot.
elif robot_id in data:
warnings.warn(
"This configuration format is deprecated. Refer to the documentation "
"for the new format.",
DeprecationWarning,
stacklevel=2,
)
return data[robot_id]

# If the `robot_id` is provided but not found, raise an error.
else:
raise IndexError(f"Robot ID '{robot_id}' not found in {fname}")
return data if data is not None else {}
36 changes: 14 additions & 22 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,28 +44,6 @@ def test_read_yaml_returns_entire_file(_):
assert result == expected


@pytest.mark.filterwarnings("ignore::DeprecationWarning")
@mock.patch(
"builtins.open",
new_callable=mock.mock_open,
read_data="id1: {k1: v1, k2: v2}\nid2: {k3: v3, k4: v4}",
)
def test_read_yaml_returns_specific_robot(_):
"""Test reading YAML for specific robot (deprecated format)."""
result = utils.read_yaml("dummy.yaml", "id1")
expected = {"k1": "v1", "k2": "v2"}
assert result == expected


@mock.patch(
"builtins.open",
new_callable=mock.mock_open,
read_data="id1: {k1: v1, k2: v2}\nid2: {k3: v3, k4: v4}",
)
def test_read_yaml_raises_error_when_robot_id_not_present(_):
with pytest.raises(IndexError):
utils.read_yaml("dummy.yaml", "id3")


@mock.patch("builtins.open", new_callable=mock.mock_open, read_data="")
def test_read_yaml_returns_empty_dict_when_file_empty(_):
Expand All @@ -74,6 +52,20 @@ def test_read_yaml_returns_empty_dict_when_file_empty(_):
assert result == expected


@pytest.mark.parametrize(
"yaml_content,expected",
[
("false", False),
("0", 0),
("[]", []),
],
)
def test_read_yaml_preserves_falsy_but_valid_values(yaml_content, expected):
with mock.patch("builtins.open", mock.mock_open(read_data=yaml_content)):
result = utils.read_yaml("dummy.yaml")
assert result == expected


@mock.patch(
"builtins.open",
new_callable=mock.mock_open,
Expand Down
Loading