Skip to content

Commit

Permalink
Project cleanup, added Requirement.py
Browse files Browse the repository at this point in the history
- Removed Math.py and tests
- Added Requirement.py and tests
  • Loading branch information
davidbrownell committed Apr 25, 2024
1 parent 300ff6c commit 1dd988b
Show file tree
Hide file tree
Showing 10 changed files with 364 additions and 149 deletions.
14 changes: 12 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ classifiers = [
]

dependencies = [
# TODO
"dbrownell_Common",
"pluggy == 1.*",
"typer ~= 0.9"
]

Expand Down Expand Up @@ -72,7 +73,6 @@ Repository = "https://github.com/gt-sse-center/RepoAuditor"
[project.scripts]
RepoAuditor = "RepoAuditor:EntryPoint.app"


# ----------------------------------------------------------------------
# |
# | black
Expand All @@ -81,6 +81,16 @@ RepoAuditor = "RepoAuditor:EntryPoint.app"
[tool.black]
line-length = 100

# ----------------------------------------------------------------------
# |
# | coverage
# |
# ----------------------------------------------------------------------
[tool.coverage.run]
omit = [
"*/Impl/*",
]

# ----------------------------------------------------------------------
# |
# | pylint
Expand Down
47 changes: 5 additions & 42 deletions src/RepoAuditor/EntryPoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from typer.core import TyperGroup # type: ignore [import-untyped]

from RepoAuditor import Math, __version__
from RepoAuditor import __version__


# ----------------------------------------------------------------------
Expand All @@ -34,47 +34,10 @@ def list_commands(self, *args, **kwargs): # pylint: disable=unused-argument


# ----------------------------------------------------------------------
@app.command("Add")
def Add(
x: int,
y: int,
) -> None:
"""Adds 2 values."""

sys.stdout.write(str(Math.Add(x, y)))


# ----------------------------------------------------------------------
@app.command("Sub")
def Sub(
x: int,
y: int,
) -> None:
"""Subtracts 2 values."""

sys.stdout.write(str(Math.Sub(x, y)))


# ----------------------------------------------------------------------
@app.command("Mult")
def Mult(
x: int,
y: int,
) -> None:
"""Multiplies 2 values."""

sys.stdout.write(str(Math.Mult(x, y)))


# ----------------------------------------------------------------------
@app.command("Div")
def Div(
x: int,
y: int,
) -> None:
"""Divides 1 value by another."""

sys.stdout.write(str(Math.Div(x, y)))
@app.command("Placeholder")
def Placeholder() -> None:
"""This is a placeholder command that should be removed once actual functionality is added."""
pass


# ----------------------------------------------------------------------
Expand Down
121 changes: 121 additions & 0 deletions src/RepoAuditor/Impl/ParallelSequentialProcessor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# -------------------------------------------------------------------------------
# | |
# | Copyright (c) 2024 Scientific Software Engineering Center at Georgia Tech |
# | Distributed under the MIT License. |
# | |
# -------------------------------------------------------------------------------
"""Contains functionality to process items in parallel and/or sequentially."""

from enum import auto, Enum
from typing import Callable, cast, Optional, TypeVar

from dbrownell_Common import ExecuteTasks # type: ignore[import-untyped]
from dbrownell_Common.Streams.DoneManager import DoneManager # type: ignore[import-untyped]
from dbrownell_Common.Streams.StreamDecorator import StreamDecorator # type: ignore[import-untyped]


# ----------------------------------------------------------------------
class ExecutionStyle(Enum):
"""Controls the way in which a Requirement, Query, and Module can be processed."""

Sequential = auto()
Parallel = auto()


# ----------------------------------------------------------------------
ItemType = TypeVar("ItemType")
OutputType = TypeVar("OutputType")


def ParallelSequentialProcessor(
items: list[ItemType],
calculate_result_func: Callable[[ItemType], tuple[int, OutputType]],
dm: Optional[DoneManager] = None,
*,
max_num_threads: Optional[int] = None,
) -> list[OutputType]:
if dm is None:
with DoneManager.Create(StreamDecorator(None), "", line_prefix="") as dm:
return _Impl(
dm,
items,
calculate_result_func,
max_num_threads,
)

return _Impl(
dm,
items,
calculate_result_func,
max_num_threads,
)


# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
def _Impl(
dm: DoneManager,
items: list[ItemType],
calculate_result_func: Callable[[ItemType], tuple[int, OutputType]],
max_num_threads: Optional[int],
) -> list[OutputType]:
# Divide the items into those that can be run in parallel and those that must be run sequentially
parallel: list[tuple[int, ItemType]] = []
sequential: list[tuple[int, ItemType]] = []

for index, item in enumerate(items):
execution_style = item.style # type: ignore

if execution_style == ExecutionStyle.Parallel:
parallel.append((index, item))
elif execution_style == ExecutionStyle.Sequential:
sequential.append((index, item))
else:
assert False, execution_style # pragma: no cover

if len(parallel) == 1:
sequential.append(parallel[0])
parallel = []

# Calculate the results
results: list[Optional[OutputType]] = [None] * len(items)

# ----------------------------------------------------------------------
def Execute(
results_index: int,
item: ItemType,
) -> ExecuteTasks.TransformResultComplete:
return_code, result = calculate_result_func(item)

assert results[results_index] is None
results[results_index] = result

return ExecuteTasks.TransformResultComplete(None, return_code)

# ----------------------------------------------------------------------

if parallel:
ExecuteTasks.TransformTasks(
dm,
"Processing",
[
ExecuteTasks.TaskData(item.name, (results_index, item)) # type: ignore
for results_index, item in parallel
],
lambda context, status: Execute(*context),
max_num_threads=max_num_threads,
)

for sequential_index, (results_index, item) in enumerate(sequential):
with dm.Nested(
"Processing '{}' ({} of {})...".format(
item.name, # type: ignore
sequential_index + 1 + len(parallel),
len(items),
),
):
Execute(results_index, item)

assert not any(result is None for result in results), results
return cast(list[OutputType], results)
Empty file.
27 changes: 0 additions & 27 deletions src/RepoAuditor/Math.py

This file was deleted.

94 changes: 94 additions & 0 deletions src/RepoAuditor/Requirement.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# -------------------------------------------------------------------------------
# | |
# | Copyright (c) 2024 Scientific Software Engineering Center at Georgia Tech |
# | Distributed under the MIT License. |
# | |
# -------------------------------------------------------------------------------
"""Contains types used when creating Requirements."""

from abc import ABC, abstractmethod
from dataclasses import dataclass
from enum import auto, Enum
from typing import Any, Optional


from .Impl.ParallelSequentialProcessor import ExecutionStyle


# ----------------------------------------------------------------------
class EvaluateResult(Enum):
"""Result of evaluating a Requirement against a set of data."""

DoesNotApply = auto()
Success = auto()
Warning = auto()
Error = auto()


# ----------------------------------------------------------------------
@dataclass(frozen=True)
class Requirement(ABC):
"""A single requirement that can be evaluated against a set of data."""

# ----------------------------------------------------------------------
# |
# | Public Types
# |
# ----------------------------------------------------------------------
@dataclass(frozen=True)
class EvaluateInfo:
"""Information associated with evaluating a Requirement against a set of data."""

result: EvaluateResult
context: Optional[str]

resolution: Optional[str]
rationale: Optional[str]

requirement: "Requirement"

# ----------------------------------------------------------------------
# |
# | Public Data
# |
# ----------------------------------------------------------------------
name: str
description: str
style: ExecutionStyle

resolution_template: str
rationale_template: str

# ----------------------------------------------------------------------
# |
# | Public Methods
# |
# ----------------------------------------------------------------------
def Evaluate(
self,
query_data: dict[str, Any],
) -> "Requirement.EvaluateInfo":
result, context = self._EvaluateImpl(query_data)

if result in [EvaluateResult.DoesNotApply, EvaluateResult.Success]:
return Requirement.EvaluateInfo(result, context, None, None, self)

return Requirement.EvaluateInfo(
result,
context,
self.resolution_template.format(**query_data),
self.rationale_template.format(**query_data),
self,
)

# ----------------------------------------------------------------------
# |
# | Private Methods
# |
# ----------------------------------------------------------------------
@abstractmethod
def _EvaluateImpl(
self,
query_data: dict[str, Any],
) -> tuple[EvaluateResult, Optional[str]]:
"""Perform the actual evaluation"""
2 changes: 0 additions & 2 deletions src/RepoAuditor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@
# on changes observed in the git repository. The default value below will be used until the value
# here is explicitly updated as part of a commit.
__version__ = "0.1.0"

from .Math import Add, Sub, Mult, Div
Loading

0 comments on commit 1dd988b

Please sign in to comment.