Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cidrblock committed Jul 30, 2024
1 parent f84c482 commit 616fedc
Show file tree
Hide file tree
Showing 10 changed files with 474 additions and 93 deletions.
1 change: 1 addition & 0 deletions .config/dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ reqs
setenv
treemaker
usefixtures
xmltodict
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ line-length = 100

[tool.coverage.report]
exclude_also = ["if TYPE_CHECKING:", "pragma: no cover"]
fail_under = 72
fail_under = 81
ignore_errors = true
show_missing = true
skip_covered = true
Expand Down
97 changes: 62 additions & 35 deletions src/ansible_dev_environment/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from __future__ import annotations

import re
import sys

from dataclasses import dataclass
from pathlib import Path
Expand Down Expand Up @@ -34,14 +33,14 @@ class Collection: # pylint: disable=too-many-instance-attributes
"""

config: Config
path: Path | None = None
opt_deps: str | None = None
local: bool | None = None
cnamespace: str | None = None
cname: str | None = None
csource: list[str] | None = None
specifier: str | None = None
original: str | None = None
path: Path
opt_deps: str
local: bool
cnamespace: str
cname: str
csource: list[str]
specifier: str
original: str

@property
def name(self: Collection) -> str:
Expand Down Expand Up @@ -70,12 +69,7 @@ def site_pkg_path(self: Collection) -> Path:
Returns:
The site packages collection path
Raises:
RuntimeError: If the collection namespace or name is not set
"""
if not self.cnamespace or not self.cname:
msg = "Collection namespace or name not set."
raise RuntimeError(msg)
return self.config.site_pkg_collections_path / self.cnamespace / self.cname


Expand All @@ -90,10 +84,12 @@ def parse_collection_request( # noqa: PLR0915
string: The collection request string
config: The configuration object
output: The output object
Raises:
SystemExit: If the collection request is invalid
Returns:
A collection object
"""
collection = Collection(config=config, original=string)
# spec with dep, local
if "[" in string and "]" in string:
msg = f"Found optional dependencies in collection request: {string}"
Expand All @@ -106,15 +102,25 @@ def parse_collection_request( # noqa: PLR0915
output.critical(msg)
msg = f"Found local collection request with dependencies: {string}"
output.debug(msg)
collection.path = path
msg = f"Setting collection path: {collection.path}"
msg = f"Setting collection path: {path}"
output.debug(msg)
collection.opt_deps = string.split("[")[1].split("]")[0]
msg = f"Setting optional dependencies: {collection.opt_deps}"
opt_deps = string.split("[")[1].split("]")[0]
msg = f"Setting optional dependencies: {opt_deps}"
output.debug(msg)
collection.local = True
local = True
msg = "Setting request as local"
output.debug(msg)
collection = Collection(
config=config,
path=path,
opt_deps=opt_deps,
local=local,
cnamespace="",
cname="",
csource=[],
specifier="",
original=string,
)
get_galaxy(collection=collection, output=output)
return collection
# spec without dep, local
Expand All @@ -124,10 +130,20 @@ def parse_collection_request( # noqa: PLR0915
output.debug(msg)
msg = f"Setting collection path: {path}"
output.debug(msg)
collection.path = path
msg = "Setting request as local"
output.debug(msg)
collection.local = True
local = True
collection = Collection(
config=config,
path=path,
opt_deps="",
local=local,
cnamespace="",
cname="",
csource=[],
specifier="",
original=string,
)
get_galaxy(collection=collection, output=output)
return collection
non_local_re = re.compile(
Expand All @@ -145,28 +161,42 @@ def parse_collection_request( # noqa: PLR0915
output.hint(msg)
msg = f"Failed to parse collection request: {string}"
output.critical(msg)
sys.exit(1)
raise SystemExit(1) # pragma: no cover # (critical is a sys.exit)
msg = f"Found non-local collection request: {string}"
output.debug(msg)

collection.cnamespace = matched.group("cnamespace")
msg = f"Setting collection namespace: {collection.cnamespace}"
cnamespace = matched.group("cnamespace")
msg = f"Setting collection namespace: {cnamespace}"
output.debug(msg)

collection.cname = matched.group("cname")
msg = f"Setting collection name: {collection.cname}"
cname = matched.group("cname")
msg = f"Setting collection name: {cname}"
output.debug(msg)

if matched.group("specifier"):
collection.specifier = matched.group("specifier")
msg = f"Setting collection specifier: {collection.specifier}"
specifier = matched.group("specifier")
msg = f"Setting collection specifier: {specifier}"
output.debug(msg)
else:
specifier = ""
msg = "Setting collection specifier as empty"
output.debug(msg)

collection.local = False
local = False
msg = "Setting request as non-local"
output.debug(msg)

return collection
return Collection(
config=config,
path=Path(),
opt_deps="",
local=local,
cnamespace=cnamespace,
cname=cname,
csource=[],
specifier=specifier,
original=string,
)


def get_galaxy(collection: Collection, output: Output) -> None:
Expand All @@ -178,9 +208,6 @@ def get_galaxy(collection: Collection, output: Output) -> None:
Raises:
SystemExit: If the collection name is not found
"""
if collection is None or collection.path is None:
msg = "get_galaxy called without a collection or path"
raise RuntimeError(msg)
file_name = collection.path / "galaxy.yml"
if not file_name.exists():
err = f"Failed to find {file_name} in {collection.path}"
Expand All @@ -203,4 +230,4 @@ def get_galaxy(collection: Collection, output: Output) -> None:
output.critical(err)
else:
return
raise SystemExit(1) # We shouldn't be here
raise SystemExit(1) # pragma: no cover # (critical is a sys.exit)
61 changes: 30 additions & 31 deletions src/ansible_dev_environment/subcommands/treemaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from typing import TYPE_CHECKING, cast
from typing import TYPE_CHECKING, Union, cast

from ansible_dev_environment.tree import Tree
from ansible_dev_environment.utils import builder_introspect, collect_manifests
Expand All @@ -15,7 +15,8 @@
ScalarVal = bool | str | float | int | None
JSONVal = ScalarVal | list["JSONVal"] | dict[str, "JSONVal"]

NOT_A_DICT = "Tree dict is not a dict."
TreeWithReqs = dict[str, Union[list[str], "TreeWithReqs"]]
TreeWithoutReqs = dict[str, "TreeWithoutReqs"]


class TreeMaker:
Expand All @@ -32,11 +33,7 @@ def __init__(self: TreeMaker, config: Config, output: Output) -> None:
self._output = output

def run(self: TreeMaker) -> None: # noqa: C901, PLR0912, PLR0915
"""Run the command.
Raises:
TypeError: If the tree dict is not a dict.
"""
"""Run the command."""
builder_introspect(self._config, self._output)

with self._config.discovered_python_reqs.open("r") as reqs_file:
Expand All @@ -46,7 +43,7 @@ def run(self: TreeMaker) -> None: # noqa: C901, PLR0912, PLR0915
target=self._config.site_pkg_collections_path,
venv_cache_dir=self._config.venv_cache_dir,
)
tree_dict: dict[str, dict[str, JSONVal]] = {c: {} for c in collections}
tree_dict: TreeWithoutReqs = {c: {} for c in collections}

links: dict[str, str] = {}
for collection_name, collection in collections.items():
Expand All @@ -70,15 +67,17 @@ def run(self: TreeMaker) -> None: # noqa: C901, PLR0912, PLR0915
homepage = collection["collection_info"].get("homepage")
repository = collection["collection_info"].get("repository")
issues = collection["collection_info"].get("issues")
link = repository or homepage or docs or issues or "http://ansible.com"
fallback = "https://ansible.com"
link = repository or homepage or docs or issues or fallback
if not isinstance(link, str):
msg = "Link is not a string."
raise TypeError(msg)
err = f"Collection {collection_name} has malformed repository metadata."
self._output.error(err)
link = fallback
links[collection_name] = link

if self._config.args.verbose >= 1:
add_python_reqs(
tree_dict=tree_dict,
tree_dict=cast(TreeWithReqs, tree_dict),
collection_name=collection_name,
python_deps=python_deps,
)
Expand All @@ -92,27 +91,25 @@ def run(self: TreeMaker) -> None: # noqa: C901, PLR0912, PLR0915

more_verbose = 2
if self._config.args.verbose >= more_verbose:
j_tree_dict = cast(JSONVal, tree_dict)
tree = Tree(obj=j_tree_dict, term_features=self._config.term_features)
tree = Tree(obj=cast(JSONVal, tree_dict), term_features=self._config.term_features)
tree.links = links
tree.green.extend(green)
rendered = tree.render()
print(rendered) # noqa: T201
else:
pruned_tree_dict: JSONVal = {}
if not isinstance(pruned_tree_dict, dict):
raise TypeError(NOT_A_DICT)
for collection_name in list(tree_dict.keys()):
pruned_tree_dict: TreeWithoutReqs = {}
for collection_name in tree_dict:
found = False
for value in tree_dict.values():
if not isinstance(value, dict):
raise TypeError(NOT_A_DICT)
if collection_name in value:
found = True
if not found:
pruned_tree_dict[collection_name] = tree_dict[collection_name]

tree = Tree(obj=pruned_tree_dict, term_features=self._config.term_features)
tree = Tree(
obj=cast(JSONVal, pruned_tree_dict),
term_features=self._config.term_features,
)
tree.links = links
tree.green.extend(green)
rendered = tree.render()
Expand All @@ -126,7 +123,7 @@ def run(self: TreeMaker) -> None: # noqa: C901, PLR0912, PLR0915


def add_python_reqs(
tree_dict: dict[str, dict[str, JSONVal]],
tree_dict: TreeWithReqs,
collection_name: str,
python_deps: list[str],
) -> None:
Expand All @@ -140,17 +137,19 @@ def add_python_reqs(
Raises:
TypeError: If the tree dict is not a dict.
"""
if not isinstance(tree_dict, dict):
raise TypeError(NOT_A_DICT)
collection = tree_dict[collection_name]
if not isinstance(collection, dict):
raise TypeError(NOT_A_DICT)
collection["python requirements"] = []
msg = "Did you really name a collection 'python requirements'?"
raise TypeError(msg)

deps = []
for dep in sorted(python_deps):
name, comment = dep.split("#", 1)
if "#" in dep:
name, comment = dep.split("#", 1)
else:
name = dep
comment = ""
if collection_name in comment:
if not isinstance(collection["python requirements"], list):
msg = "Python requirements is not a list."
raise TypeError(msg)
collection["python requirements"].append(name.strip())
deps.append(name.strip())

collection["python requirements"] = deps
Loading

0 comments on commit 616fedc

Please sign in to comment.