PATHCHANGE = object() def convert_to_nested_dict(dotted_dict):
    """Convert a dict with dotted path keys to corresponding nested dict."""
    nested_dict = {}
    for k, v in iterate_flattened(dotted_dict):
        set_by_dotted_path(nested_dict, k, v)
    return nested_dict def get_by_dotted_path(d, path, default=None):
    """
    Get an entry from nested dictionaries using a dotted path.

    Example
    -------
    >>> get_by_dotted_path({'foo': {'a': 12}}, 'foo.a')
    12
    """
    if not path:
        return d
    split_path = path.split(".")
    current_option = d
    for p in split_path:
        if p not in current_option:
            return default
        current_option = current_option[p]
    return current_option def is_prefix(pre_path, path):
    """Return True if pre_path is a path-prefix of path."""
    pre_path = pre_path.strip(".")
    path = path.strip(".")
    return not pre_path or path.startswith(pre_path + ".") def iter_prefixes(path):
    """
    Iterate through all (non-empty) prefixes of a dotted path.

    Example
    -------
    >>> list(iter_prefixes('foo.bar.baz'))
    ['foo', 'foo.bar', 'foo.bar.baz']
    """
    split_path = path.split(".")
    for i in range(1, len(split_path) + 1):
        yield join_paths(*split_path[:i]) def iterate_flattened(d):
    """
    Recursively iterate over the items of a dictionary.

    Provides a full dotted paths for every leaf.
    """
    for key in sorted(d.keys()):
        value = d[key]
        if isinstance(value, dict) and value:
            for k, v in iterate_flattened(d[key]):
                yield join_paths(key, k), v
        else:
            yield key, value def iterate_flattened_separately(dictionary, manually_sorted_keys=None):
    """
    Recursively iterate over the items of a dictionary in a special order.

    First iterate over manually sorted keys and then over all items that are
    non-dictionary values (sorted by keys), then over the rest
    (sorted by keys), providing full dotted paths for every leaf.
    """
    manually_sorted_keys = manually_sorted_keys or []

    def get_order(key_and_value):
        key, value = key_and_value
        if key in manually_sorted_keys:
            return 0, manually_sorted_keys.index(key)
        elif not is_non_empty_dict(value):
            return 1, key
        else:
            return 2, key

    for key, value in sorted(dictionary.items(), key=get_order):
        if is_non_empty_dict(value):
            yield key, PATHCHANGE
            for k, val in iterate_flattened_separately(value, manually_sorted_keys):
                yield join_paths(key, k), val
        else:
            yield key, value def join_paths(*parts):
    """Join different parts together to a valid dotted path."""
    return ".".join(str(p).strip(".") for p in parts if p) def recursive_update(d, u):
    """
    Given two dictionaries d and u, update dict d recursively.

    E.g.:
    d = {'a': {'b' : 1}}
    u = {'c': 2, 'a': {'d': 3}}
    => {'a': {'b': 1, 'd': 3}, 'c': 2}
    """
    for k, v in u.items():
        if isinstance(v, collections.abc.Mapping):
            r = recursive_update(d.get(k, {}), v)
            d[k] = r
        else:
            d[k] = u[k]
    return d def set_by_dotted_path(d, path, value):
    """
    Set an entry in a nested dict using a dotted path.

    Will create dictionaries as needed.

    Examples
    --------
    >>> d = {'foo': {'bar': 7}}
    >>> set_by_dotted_path(d, 'foo.bar', 10)
    >>> d
    {'foo': {'bar': 10}}
    >>> set_by_dotted_path(d, 'foo.d.baz', 3)
    >>> d
    {'foo': {'bar': 10, 'd': {'baz': 3}}}

    """
    split_path = path.split(".")
    current_option = d
    for p in split_path[:-1]:
        if p not in current_option:
            current_option[p] = dict()
        current_option = current_option[p]
    current_option[split_path[-1]] = value def get_inheritors(cls):
    """Get a set of all classes that inherit from the given class."""
    subclasses = set()
    work = [cls]
    while work:
        parent = work.pop()
        for child in parent.__subclasses__():
            if child not in subclasses:
                subclasses.add(child)
                work.append(child)
    return subclasses def convert_camel_case_to_snake_case(name):
    """Convert CamelCase to snake_case."""
    s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
    return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower() def apply_backspaces_and_linefeeds(text):
    """
    Interpret backspaces and linefeeds in text like a terminal would.

    Interpret text like a terminal by removing backspace and linefeed
    characters and applying them line by line.

    If final line ends with a carriage it keeps it to be concatenable with next
    output chunk.
    """
    orig_lines = text.split("\n")
    orig_lines_len = len(orig_lines)
    new_lines = []
    for orig_line_idx, orig_line in enumerate(orig_lines):
        chars, cursor = [], 0
        orig_line_len = len(orig_line)
        for orig_char_idx, orig_char in enumerate(orig_line):
            if orig_char == "\r" and (
                orig_char_idx != orig_line_len - 1
                or orig_line_idx != orig_lines_len - 1
            ):
                cursor = 0
            elif orig_char == "\b":
                cursor = max(0, cursor - 1)
            else:
                if (
                    orig_char == "\r"
                    and orig_char_idx == orig_line_len - 1
                    and orig_line_idx == orig_lines_len - 1
                ):
                    cursor = len(chars)
                if cursor == len(chars):
                    chars.append(orig_char)
                else:
                    chars[cursor] = orig_char
                cursor += 1
        new_lines.append("".join(chars))
    return "\n".join(new_lines) def module_exists(modname):
    """Checks if a module exists without actually importing it."""
    try:
        return pkgutil.find_loader(modname) is not None
    except ImportError:
        # TODO: Temporary fix for tf 1.14.0.
        # Should be removed once fixed in tf.
        return True def module_is_in_cache(modname):
    """Checks if a module was imported before (is in the import cache)."""
    return modname in sys.modules def get_package_version(name):
    """Returns a parsed version string of a package."""
    version_string = importlib.import_module(name).__version__
    return parse_version(version_string) def parse_version(version_string):
    """Returns a parsed version string."""
    return version.parse(version_string) def rel_path(base, path):
    """Return path relative to base."""
    if base == path:
        return ""
    assert is_prefix(base, path), "{} not a prefix of {}".format(base, path)
    return path[len(base) :].strip(".")
import pytest
from sacred.utils import (
    PATHCHANGE,
    convert_to_nested_dict,
    get_by_dotted_path,
    is_prefix,
    iter_prefixes,
    iterate_flattened,
    iterate_flattened_separately,
    join_paths,
    recursive_update,
    set_by_dotted_path,
    get_inheritors,
    convert_camel_case_to_snake_case,
    apply_backspaces_and_linefeeds,
    module_exists,
    module_is_in_cache,
    get_package_version,
    parse_version,
    rel_path,
)
def test_join_paths():
    assert join_paths() == ""
    assert join_paths("foo") == "foo"
    assert join_paths("foo", "bar") == "foo.bar"
    assert join_paths("a", "b", "c", "d") == "a.b.c.d"
    assert join_paths("", "b", "", "d") == "b.d"
    assert join_paths("a.b", "c.d.e") == "a.b.c.d.e"
    assert join_paths("a.b.", "c.d.e") == "a.b.c.d.e"


def test_is_prefix():
    assert is_prefix("", "foo")
    assert is_prefix("foo", "foo.bar")
    assert is_prefix("foo.bar", "foo.bar.baz")

    assert not is_prefix("a", "foo.bar")
    assert not is_prefix("a.bar", "foo.bar")
    assert not is_prefix("foo.b", "foo.bar")
    assert not is_prefix("foo.bar", "foo.bar")


def test_convert_to_nested_dict():
    dotted_dict = {"foo.bar": 8, "foo.baz": 7}
    assert convert_to_nested_dict(dotted_dict) == {"foo": {"bar": 8, "baz": 7}}


def test_convert_to_nested_dict_nested():
    dotted_dict = {"a.b": {"foo.bar": 8}, "a.b.foo.baz": 7}
    assert convert_to_nested_dict(dotted_dict) == {
        "a": {"b": {"foo": {"bar": 8, "baz": 7}}}
    }


def test_get_inheritors():
    class A:
        pass

    class B(A):
        pass

    class C(B):
        pass

    class D(A):
        pass

    class E:
        pass

    assert get_inheritors(A) == {B, C, D}


@pytest.mark.parametrize(
    "name,expected",
    [
        ("CamelCase", "camel_case"),
        ("snake_case", "snake_case"),
        ("CamelCamelCase", "camel_camel_case"),
        ("Camel2Camel2Case", "camel2_camel2_case"),
        ("getHTTPResponseCode", "get_http_response_code"),
        ("get2HTTPResponseCode", "get2_http_response_code"),
        ("HTTPResponseCode", "http_response_code"),
        ("HTTPResponseCodeXYZ", "http_response_code_xyz"),
    ],
)
def test_convert_camel_case_to_snake_case(name, expected):
    assert convert_camel_case_to_snake_case(name) == expected


@pytest.mark.parametrize(
    "text,expected",
    [
        ("", ""),
        ("\b", ""),
        ("\r", "\r"),
        ("\r\n", "\n"),
        ("ab\bc", "ac"),
        ("\ba", "a"),
        ("ab\nc\b\bd", "ab\nd"),
        ("abc\rdef", "def"),
        ("abc\r", "abc\r"),
        ("abc\rd", "dbc"),
        ("abc\r\nd", "abc\nd"),
        ("abc\ndef\rg", "abc\ngef"),
        ("abc\ndef\r\rg", "abc\ngef"),
        ("abcd\refg\r", "efgd\r"),
        ("abcd\refg\r\n", "efgd\n"),
    ],
)
def test_apply_backspaces_and_linefeeds(text, expected):
    assert apply_backspaces_and_linefeeds(text) == expected


def test_module_exists_base_level_modules():
    assert module_exists("pytest")
    assert not module_exists("clearly_non_existing_module_name")


def test_module_exists_does_not_import_module():
    assert module_exists("tests.donotimport")


def test_module_is_in_cache():
    assert module_is_in_cache("pytest")
    assert module_is_in_cache("pkgutil")
    assert not module_is_in_cache("does_not_even_exist")


def test_get_package_version():
    package_version = get_package_version("pytest")
    assert str(package_version) == "6.2.3"


def test_parse_version():
    parsed_version = parse_version("6.2.3")
    assert str(parsed_version) == "6.2.3"


def test_get_package_version_comparison():
    package_version = get_package_version("pytest")

    current_version = parse_version("6.2.3")