Skip to content

Commit

Permalink
Updated: Improved testing, coverage, and typing
Browse files Browse the repository at this point in the history
  • Loading branch information
drakes00 committed Apr 12, 2024
1 parent 6990d9a commit fdf13d0
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 67 deletions.
Binary file modified .coverage
Binary file not shown.
2 changes: 1 addition & 1 deletion remake/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def addTargets(self, targets):
self._targets += [targets]

@property
def targets(self):
def targets(self) -> list:
"""Returns the list of targets to build from current context."""
return self._targets

Expand Down
82 changes: 32 additions & 50 deletions remake/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,20 @@
from rich.progress import Progress
from rich.console import Console
from typeguard import typechecked
from typing import Dict, List, Tuple, Union

from remake.context import addContext, popContext, addOldContext, getCurrentContext, getContexts, Context
from remake.context import isDryRun, isDevTest, isClean, setVerbose, setDryRun, setClean
from remake.paths import VirtualTarget, VirtualDep
from remake.builders import Builder # Import needed to avoid imports in ReMakeFile
from remake.rules import Rule, PatternRule

TYP_PATH = Union[pathlib.Path, VirtualTarget, VirtualDep]
TYP_DEP_LIST = List[Union[TYP_PATH, Tuple[Union[TYP_PATH, List[TYP_PATH]], Rule]]]
TYP_DEP_GRAPH = Union[TYP_PATH, Dict[Tuple[TYP_PATH, Rule], List["TYP_DEP_GRAPH"]]]

@typechecked()

@typechecked
class AddTarget():
"""Class registering files as remake targets."""
def __init__(self, targets: list[str | pathlib.Path] | str | pathlib.Path):
Expand All @@ -31,22 +36,22 @@ def __init__(self, targets: list[str | pathlib.Path] | str | pathlib.Path):
getCurrentContext().addTargets([pathlib.Path(_).absolute() for _ in targets])


@typechecked()
class AddVirtualTarget(VirtualTarget):
"""Class registering remake targets that are not files."""
def __init__(self, name: str):
super().__init__(name)
getCurrentContext().addTargets(self)
@typechecked
def AddVirtualTarget(name: str) -> VirtualTarget:
"""Method registering remake targets that are not files."""
ret = VirtualTarget(name)
getCurrentContext().addTargets(ret)
return ret


@typechecked()
@typechecked
class SubReMakeDir():
"""Instanciate a sub context for a call to a sub ReMakeFile."""
def __init__(self, subDir):
executeReMakeFileFromDirectory(subDir)


@typechecked()
@typechecked
def executeReMakeFileFromDirectory(cwd: str, configFile: str = "ReMakeFile", targets: list | None = None) -> Context:
"""Loads ReMakeFile from current directory in a new context and builds
associated targets."""
Expand All @@ -73,8 +78,10 @@ def executeReMakeFileFromDirectory(cwd: str, configFile: str = "ReMakeFile", tar
addOldContext(absCwd, oldContext)
return oldContext

return tmp


@typechecked()
@typechecked
def loadScript(configFile: str = "ReMakeFile") -> None:
"""Loads and execs the ReMakeFile script."""
with open(configFile, "r", encoding="utf-8") as handle:
Expand All @@ -83,14 +90,8 @@ def loadScript(configFile: str = "ReMakeFile") -> None:
exec(script)


@typechecked()
def generateDependencyList(
targets: list | None = None
) -> list[pathlib.Path | tuple[pathlib.Path | tuple[pathlib.Path,
...],
Rule]] | tuple[pathlib.Path | tuple[pathlib.Path,
...],
Rule]:
@typechecked
def generateDependencyList(targets: list[TYP_PATH] | None = None) -> TYP_DEP_LIST:
"""Generates and sorts dependency list."""
deps = []
if targets is None:
Expand All @@ -104,10 +105,8 @@ def generateDependencyList(
return deps


@typechecked()
def findBuildPath(
target: pathlib.Path | VirtualTarget | VirtualDep
) -> VirtualTarget | VirtualDep | pathlib.Path | dict:
@typechecked
def findBuildPath(target: TYP_PATH) -> TYP_DEP_GRAPH:
"""Constructs dependency graph from registered rules."""
depNames = []
foundRule = None
Expand Down Expand Up @@ -188,12 +187,8 @@ def findBuildPath(
sys.exit(1)


@typechecked()
def sortDeps(
deps: list[VirtualTarget | VirtualDep | pathlib.Path | dict]
) -> list[pathlib.Path | tuple[pathlib.Path | tuple[pathlib.Path,
...],
Rule]]:
@typechecked
def sortDeps(deps: List[TYP_DEP_GRAPH]) -> TYP_DEP_LIST:
"""Sorts dependency graph as a reverse level order list.
Snippet from: https://www.geeksforgeeks.org/reverse-level-order-traversal/"""
tmpQueue = deque()
Expand All @@ -214,16 +209,10 @@ def sortDeps(
return list(ret)


@typechecked()
def optimizeDeps(
deps: list[pathlib.Path | tuple[pathlib.Path | tuple[pathlib.Path,
...],
Rule]]
) -> list[pathlib.Path | tuple[pathlib.Path | tuple[pathlib.Path,
...],
Rule]]:
@typechecked
def optimizeDeps(deps: TYP_DEP_LIST) -> TYP_DEP_LIST:
"""Removes rules from dependencies list """
def _mergeTargetsSameRule(origDeps):
def _mergeTargetsSameRule(origDeps: TYP_DEP_LIST) -> TYP_DEP_LIST:
"""Remove duplicate calls to a rule that produces multiple dependencies."""
ret = []
if len(origDeps) < 2:
Expand All @@ -246,7 +235,7 @@ def _mergeTargetsSameRule(origDeps):
if otherTargets:
# If there are other targets, merge them.
allTargetsSameRule = [_[0] for _ in otherTargets] + [target[0]]
allTargetsSameRule = tuple(
allTargetsSameRule = list(
i for (n,
i) in enumerate(allTargetsSameRule) if i not in allTargetsSameRule[:n]
)
Expand All @@ -263,7 +252,7 @@ def _mergeTargetsSameRule(origDeps):
ret = ret[::-1] # And sort back the list to the correct order since we iterated from the end to the begening.
return ret

def _removeDuplicatesWithNoRules(deps):
def _removeDuplicatesWithNoRules(deps: TYP_DEP_LIST) -> TYP_DEP_LIST:
"""Remove duplicate targets that have no associated rule."""
ret = []
if len(deps) < 2:
Expand All @@ -288,11 +277,8 @@ def _removeDuplicatesWithNoRules(deps):
return deps


@typechecked()
def cleanDeps(deps: list[pathlib.Path | tuple[pathlib.Path,
Rule]],
configFile: str = "ReMakeFile") -> list[pathlib.Path | tuple[pathlib.Path,
Rule]]:
@typechecked
def cleanDeps(deps: TYP_DEP_LIST, configFile: str = "ReMakeFile") -> TYP_DEP_LIST:
"""Builds files marked as targets from their dependencies."""
def _cleanDep(target):
if os.path.exists(target):
Expand Down Expand Up @@ -329,12 +315,8 @@ def _cleanDep(target):
return deps


@typechecked()
def buildDeps(deps: list[pathlib.Path | tuple[pathlib.Path,
Rule]],
configFile: str = "ReMakeFile") -> list[tuple[pathlib.Path | tuple[pathlib.Path,
pathlib.Path],
Rule]]:
@typechecked
def buildDeps(deps: TYP_DEP_LIST, configFile: str = "ReMakeFile") -> TYP_DEP_LIST:
"""Builds files marked as targets from their dependencies."""
rulesApplied = []
with Progress() as progress:
Expand Down
32 changes: 32 additions & 0 deletions tests/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,89 @@
# -*- coding: utf-8 -*-
"""Unit tests related to context."""

import pathlib

from ward import test

from remake import setVerbose, unsetVerbose, isVerbose
from remake import setDryRun, unsetDryRun, isDryRun
from remake import setDevTest, unsetDevTest, isDevTest
from remake import setClean, unsetClean, isClean
from remake.context import getCurrentContext
from remake.main import AddTarget, AddVirtualTarget
from remake.paths import VirtualTarget


@test("setVerbose sets VERBOSE to True")
def test_01_setVerbose():
"""setVerbose sets VERBOSE to True"""
setVerbose()
assert isVerbose() is True


@test("unsetVerbose sets VERBOSE to False")
def test_02_unsetVerbose():
"""unsetVerbose sets VERBOSE to False"""
unsetVerbose()
assert isVerbose() is False


@test("setDryRun sets DRY_RUN to True")
def test_03_setDryRun():
"""setDryRun sets DRY_RUN to True"""
setDryRun()
assert isDryRun() is True


@test("unsetDryRun sets DRY_RUN to False")
def test_04_unsetDryRun():
"""unsetDryRun sets DRY_RUN to False"""
unsetDryRun()
assert isDryRun() is False


@test("setDevTest sets DEV_TEST to True")
def test_05_setDevTest():
"""setDevTest sets DEV_TEST to True"""
setDevTest()
assert isDevTest() is True


@test("unsetDevTest sets DEV_TEST to False")
def test_06_unsetDevTest():
"""unsetDevTest sets DEV_TEST to False"""
unsetDevTest()
assert isDevTest() is False


@test("setClean sets CLEAN to True")
def test_07_setClean():
"""setClean sets CLEAN to True"""
setClean()
assert isClean() is True


@test("unsetClean sets CLEAN to False")
def test_08_unsetClean():
"""unsetClean sets CLEAN to False"""
unsetClean()
assert isClean() is False


@test("AddTarget adds targets")
def test_09_addTarget():
"""AddTarget adds targets"""
# One target.
AddTarget("a")
assert getCurrentContext().targets == [pathlib.Path(_).absolute() for _ in ("a")]
getCurrentContext().clearTargets()

# Multiple targets.
AddTarget(["a", "b", "c"])
assert getCurrentContext().targets == [pathlib.Path(_).absolute() for _ in ("a", "b", "c")]
getCurrentContext().clearTargets()

# One virtual target.
AddVirtualTarget("a")
assert getCurrentContext().targets == [VirtualTarget(_) for _ in ("a")]
getCurrentContext().clearTargets()
32 changes: 16 additions & 16 deletions tests/test_func.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,15 +324,15 @@ def test_06_funDepsMultipleTargets(_=ensureCleanContext):
r_1 = Rule(targets=["a", "b"], deps="c", builder=fooBuilder)
AddTarget("a")
AddTarget("b")
assert generateDependencyList() == [pathlib.Path("/tmp/c"), ((pathlib.Path("/tmp/a"), pathlib.Path("/tmp/b")), r_1)]
assert generateDependencyList() == [pathlib.Path("/tmp/c"), ([pathlib.Path("/tmp/a"), pathlib.Path("/tmp/b")], r_1)]
getCurrentContext().clearRules()
getCurrentContext().clearTargets()

# Two targets same rule (no deps).
r_2 = Rule(targets=["a", "b"], builder=fooBuilder)
AddTarget("a")
AddTarget("b")
assert generateDependencyList() == [((pathlib.Path("/tmp/a"), pathlib.Path("/tmp/b")), r_2)]
assert generateDependencyList() == [([pathlib.Path("/tmp/a"), pathlib.Path("/tmp/b")], r_2)]
getCurrentContext().clearRules()
getCurrentContext().clearTargets()

Expand All @@ -343,9 +343,9 @@ def test_06_funDepsMultipleTargets(_=ensureCleanContext):
AddTarget("c")
assert generateDependencyList() == [
pathlib.Path("/tmp/d"),
((pathlib.Path("/tmp/a"),
([pathlib.Path("/tmp/a"),
pathlib.Path("/tmp/b"),
pathlib.Path("/tmp/c")),
pathlib.Path("/tmp/c")],
r_3)
]
getCurrentContext().clearRules()
Expand All @@ -360,12 +360,12 @@ def test_06_funDepsMultipleTargets(_=ensureCleanContext):
AddTarget("e")
assert generateDependencyList() == [
pathlib.Path("/tmp/c"),
((pathlib.Path("/tmp/a"),
pathlib.Path("/tmp/b")),
([pathlib.Path("/tmp/a"),
pathlib.Path("/tmp/b")],
r_4_1),
pathlib.Path("/tmp/f"),
((pathlib.Path("/tmp/d"),
pathlib.Path("/tmp/e")),
([pathlib.Path("/tmp/d"),
pathlib.Path("/tmp/e")],
r_4_2)
]
getCurrentContext().clearRules()
Expand All @@ -380,11 +380,11 @@ def test_06_funDepsMultipleTargets(_=ensureCleanContext):
AddTarget("e")
assert generateDependencyList() == [
pathlib.Path("/tmp/c"),
((pathlib.Path("/tmp/a"),
pathlib.Path("/tmp/b")),
([pathlib.Path("/tmp/a"),
pathlib.Path("/tmp/b")],
r_5_1),
((pathlib.Path("/tmp/d"),
pathlib.Path("/tmp/e")),
([pathlib.Path("/tmp/d"),
pathlib.Path("/tmp/e")],
r_5_2)
]

Expand All @@ -405,11 +405,11 @@ def test_07_ruleMultipleTargetsExecutedOnce(_=ensureCleanContext, _2=ensureEmpty
AddTarget("e")
os.chdir("/tmp")
assert buildDeps(generateDependencyList()) == [
((pathlib.Path("/tmp/a"),
pathlib.Path("/tmp/b")),
([pathlib.Path("/tmp/a"),
pathlib.Path("/tmp/b")],
r_1),
((pathlib.Path("/tmp/d"),
pathlib.Path("/tmp/e")),
([pathlib.Path("/tmp/d"),
pathlib.Path("/tmp/e")],
r_2)
]

Expand Down

0 comments on commit fdf13d0

Please sign in to comment.