Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for import from CSV and import dir #253

Merged
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
36 changes: 22 additions & 14 deletions perun/cli_groups/import_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,22 @@
# Perun Imports
from perun.logic import commands
from perun.profile import imports
from perun.utils.common import cli_kit


@click.group("import")
@click.option(
"--machine-info",
"-i",
type=click.Path(resolve_path=True, readable=True),
help="Imports machine info from file in JSON format (by default, machine info is loaded from the current host)."
"You can use `utils/generate_machine_info.sh` script to generate the machine info file.",
help="Imports machine info from file in JSON format (by default, machine info is loaded from "
"the current host). You can use `utils/generate_machine_info.sh` script to generate the "
"machine info file.",
)
@click.option(
"--import-dir",
"-d",
type=click.Path(resolve_path=True, readable=True),
help="Specifies the directory to import profiles from.",
)
@click.option(
"--minor-version",
Expand All @@ -31,18 +37,18 @@
help="Specifies the head minor version, for which the profiles will be imported.",
)
@click.option(
"--exitcode",
"-e",
"--stats-info",
"-t",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why -t?

nargs=1,
required=False,
default="?",
help=("Exit code of the command."),
default=None,
metavar="<stat1-description,...>",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
metavar="<stat1-description,...>",
metavar="[STAT]",

In my WIP I'm rewritting the metavars to conform to [...] notation. Please, use this.

help="Describes the stats associated with the imported profiles. Please see the import "
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be file? If not, change to [STR] and write an example, how, the stats should look.

"documentation for details regarding the stat description format.",
)
@click.option(
"--cmd",
"-c",
nargs=1,
required=False,
default="",
help=(
"Command that was being profiled. Either corresponds to some"
Expand All @@ -53,7 +59,6 @@
"--workload",
"-w",
nargs=1,
required=False,
default="",
help="Inputs for <cmd>. E.g. ``./subdir`` is possible workload for ``ls`` command.",
)
Expand Down Expand Up @@ -85,7 +90,8 @@ def perf_group(ctx: click.Context, **kwargs: Any) -> None:
This supports either profiles collected in:

1. Binary format: e.g., `collected.data` files, that are results of `perf record`
2. Text format: result of `perf script` that parses the binary into user-friendly and parsing-friendly text format
2. Text format: result of `perf script` that parses the binary into user-friendly and
parsing-friendly text format
"""
ctx.obj.update(kwargs)

Expand All @@ -107,7 +113,7 @@ def from_binary(ctx: click.Context, imported: list[str], **kwargs: Any) -> None:


@perf_group.command("script")
@click.argument("imported", type=click.Path(resolve_path=True), nargs=-1, required=True)
@click.argument("imported", type=str, nargs=-1, required=True)
@click.pass_context
def from_text(ctx: click.Context, imported: list[str], **kwargs: Any) -> None:
"""Import Perun profiles from output generated by `perf script` command"""
Expand All @@ -116,9 +122,11 @@ def from_text(ctx: click.Context, imported: list[str], **kwargs: Any) -> None:


@perf_group.command("stack")
@click.argument("imported", type=click.Path(resolve_path=True), nargs=-1, required=True)
@click.argument("imported", type=str, nargs=-1, required=True)
@click.pass_context
def from_stacks(ctx: click.Context, imported: list[str], **kwargs: Any) -> None:
"""Import Perun profiles from output generated by `perf script | stackcollapse-perf.pl` command"""
"""Import Perun profiles from output generated by `perf script | stackcollapse-perf.pl`
command
"""
kwargs.update(ctx.obj)
imports.import_perf_from_stack(imported, **kwargs)
51 changes: 50 additions & 1 deletion perun/profile/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@
from __future__ import annotations

# Standard Imports
from typing import Any, TYPE_CHECKING
from typing import Any, TYPE_CHECKING, ClassVar
import json
import operator
import os
import re
import time
from dataclasses import dataclass

# Third-Party Imports

Expand Down Expand Up @@ -609,3 +610,51 @@ def is_compatible_with_profile(self, profile: profiles.Profile) -> bool:
"checksum",
"source",
]


@dataclass
class ProfileStat:
ALLOWED_ORDERING: ClassVar[dict[str, bool]] = {
"higher_is_better": True,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider simplifying to better with type Literal["higher", "lower"]. I could imagine, that there might be case, when there is something different (maybe equal?).

"lower_is_better": False,
}

name: str
unit: str = "#"
ordering: bool = True
tooltip: str = ""
value: int | float = 0.0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

float subsumes int, I would keep float.


@classmethod
def from_string(
cls,
name: str = "empty",
unit: str = "#",
ordering: str = "higher_is_better",
tooltip: str = "",
*_: Any,
) -> ProfileStat:
if name == "empty":
# Invalid stat specification, warn
perun_log.warn("Empty profile stat specification. Creating a dummy 'empty' stat.")
if ordering not in cls.ALLOWED_ORDERING:
# Invalid stat ordering, warn
perun_log.warn(
f"Unknown stat ordering: {ordering}. Please choose one of "
f"({', '.join(cls.ALLOWED_ORDERING.keys())}). "
f"Using the default stat ordering value."
)
ordering_bool = ProfileStat.ordering
else:
ordering_bool = cls.ALLOWED_ORDERING[ordering]
return cls(name, unit, ordering_bool, tooltip)

def get_normalized_tooltip(self) -> str:
# Find the string representation of the ordering to use in the tooltip
ordering: str = ""
for str_desc, bool_repr in self.ALLOWED_ORDERING.items():
if bool_repr == self.ordering:
ordering = str_desc.replace("_", " ")
if self.tooltip:
return f"{self.tooltip} ({ordering})"
return ordering
Loading
Loading