Skip to content

Commit

Permalink
Merge pull request #9 from troycomi/feat/parsable
Browse files Browse the repository at this point in the history
Add parsable option
  • Loading branch information
troycomi authored May 20, 2022
2 parents c9f3467 + 0047892 commit 138a1e4
Show file tree
Hide file tree
Showing 12 changed files with 241 additions and 124 deletions.
60 changes: 41 additions & 19 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,23 +1,45 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-merge-conflict
- id: check-symlinks
- id: check-yaml
- id: debug-statements
- id: end-of-file-fixer
- id: mixed-line-ending
- id: requirements-txt-fixer
- id: trailing-whitespace

- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.9.0
hooks:
- id: python-check-blanket-noqa
- id: python-no-eval
- id: python-use-type-annotations
- id: rst-backticks
- id: rst-directive-colons
- id: rst-inline-touching-normal

- repo: https://github.com/PyCQA/flake8
rev: 3.9.2
hooks:
- id: flake8
- repo: https://github.com/codespell-project/codespell
rev: v2.1.0
hooks:
- id: codespell
args: [--ignore-words-list, "absense,inout"]

- repo: https://github.com/asottile/pyupgrade
rev: v2.29.0
hooks:
- id: pyupgrade
args: [--py37-plus]
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.8.0.4
hooks:
- id: shellcheck

- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black

- repo: https://github.com/PyCQA/flake8
rev: 3.9.2
hooks:
- id: flake8
2 changes: 1 addition & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


locations = "src", "tests", "noxfile.py"
nox.options.sessions = "lint", "safety", "mypy", "pytype", "tests"
nox.options.sessions = "lint", "safety", "mypy", "pytype", "tests", "tests_old_click"
package = "reportseff"


Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "reportseff"
version = "2.1.2"
version = "2.2.0"
description= "Tablular seff output"
authors = ["Troy Comi <[email protected]>"]
license = "MIT"
Expand Down
87 changes: 32 additions & 55 deletions src/reportseff/console.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
"""CLI for reportseff."""
from shutil import which
import sys
from typing import Dict, List, Tuple
from typing import Any, Dict, List, Tuple

import click

from . import __version__
from .db_inquirer import BaseInquirer, SacctInquirer
from .job_collection import JobCollection
from .output_renderer import OutputRenderer
from .parameters import ReportseffParameters


@click.command()
Expand Down Expand Up @@ -80,56 +81,28 @@
"Sets `node` and adds `GPU` to format automatically."
),
)
@click.option(
"--parsable",
"-p",
is_flag=True,
default=False,
help="Output will be '|' delmited without a '|' at the end.",
)
@click.version_option(version=__version__)
@click.argument("jobs", nargs=-1)
def main(
modified_sort: bool,
color: bool,
format_str: str,
debug: bool,
user: str,
state: str,
not_state: str,
since: str,
jobs: tuple,
node: bool,
node_and_gpu: bool,
) -> None:
def main(**kwargs: Any) -> None:
"""Main entry point for reportseff."""
if format_str.startswith("+"):
format_str = "JobID%>,State,Elapsed%>,TimeEff,CPUEff,MemEff," + format_str[1:]

output, entries = get_jobs(
jobs=jobs,
format_str=format_str,
user=user,
modified_sort=modified_sort,
state=state,
not_state=not_state,
since=since,
debug=debug,
node=node,
node_and_gpu=node_and_gpu,
)
args = ReportseffParameters(**kwargs)

output, entries = get_jobs(args)

if entries > 20:
click.echo_via_pager(output, color=color)
click.echo_via_pager(output, color=args.color)
else:
click.echo(output, color=color)


def get_jobs(
jobs: tuple,
format_str: str = "",
user: str = "",
debug: bool = False,
modified_sort: bool = False,
state: str = "",
not_state: str = "",
since: str = "",
node: bool = False,
node_and_gpu: bool = False,
) -> Tuple[str, int]:
click.echo(output, color=args.color)


def get_jobs(args: ReportseffParameters) -> Tuple[str, int]:
"""Helper method to get jobs from db_inquirer.
Returns:
Expand All @@ -141,38 +114,40 @@ def get_jobs(
"""
job_collection = JobCollection()

inquirer, renderer = get_implementation(format_str, node, node_and_gpu)
inquirer, renderer = get_implementation(
args.format_str, args.node, args.node_and_gpu, args.parsable
)

inquirer.set_state(state)
inquirer.set_not_state(not_state)
inquirer.set_state(args.state)
inquirer.set_not_state(args.not_state)

inquirer.set_since(since)
inquirer.set_since(args.since)

add_jobs = False

try:
if user:
inquirer.set_user(user)
if args.user:
inquirer.set_user(args.user)
add_jobs = True
elif inquirer.has_since() and not jobs: # since is set
elif inquirer.has_since() and not args.jobs: # since is set
inquirer.all_users()
add_jobs = True
else:
job_collection.set_jobs(jobs)
job_collection.set_jobs(args.jobs)

except ValueError as error:
click.secho(str(error), fg="red", err=True)
sys.exit(1)

db_output = get_db_output(inquirer, renderer, job_collection, debug)
db_output = get_db_output(inquirer, renderer, job_collection, args.debug)
for entry in db_output:
try:
job_collection.process_entry(entry, add_job=add_jobs)
except Exception as error:
click.echo(f"Error processing entry: {entry}", err=True)
raise error

found_jobs = job_collection.get_sorted_jobs(modified_sort)
found_jobs = job_collection.get_sorted_jobs(args.modified_sort)
found_jobs = [j for j in found_jobs if j.state]

return renderer.format_jobs(found_jobs), len(found_jobs)
Expand All @@ -182,6 +157,7 @@ def get_implementation(
format_str: str,
node: bool = False,
node_and_gpu: bool = False,
parsable: bool = False,
) -> Tuple[BaseInquirer, OutputRenderer]:
"""Get system-specific objects.
Expand All @@ -199,6 +175,7 @@ def get_implementation(
format_str,
node=node or node_and_gpu,
gpu=node_and_gpu,
parsable=parsable,
)
else:
click.secho("No supported scheduling systems found!", fg="red", err=True)
Expand Down
16 changes: 8 additions & 8 deletions src/reportseff/db_inquirer.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def get_db_output(
jobs: List[str],
debug_cmd: Optional[Callable],
) -> List[Dict[str, str]]:
"""Query the databse with the supplied columns.
"""Query the database with the supplied columns.
Args:
columns: validated format names as strings
Expand Down Expand Up @@ -75,7 +75,7 @@ def set_since(self, since: str) -> None:
Args:
since: the string for filtering. If specified as time=amount
will subract that amount from the current time
will subtract that amount from the current time
"""

@abstractmethod
Expand Down Expand Up @@ -114,7 +114,7 @@ def get_valid_formats(self) -> List[str]:
stdout=subprocess.PIPE,
encoding="utf8",
check=True,
text=True,
universal_newlines=True,
shell=False,
)
if cmd_result.returncode != 0:
Expand All @@ -128,7 +128,7 @@ def get_db_output(
jobs: List[str],
debug_cmd: Optional[Callable] = None,
) -> List[Dict[str, str]]:
"""Query the databse with the supplied columns.
"""Query the database with the supplied columns.
Args:
columns: validated format names as strings
Expand All @@ -138,7 +138,7 @@ def get_db_output(
Returns:
List of rows, where each row is a dictionary
with the columns as keys and entries as values
Output order is not guarunteed to match the jobs list
Output order is not guaranteed to match the jobs list
Raises:
RuntimeError: if sacct doesn't return properly
Expand All @@ -160,10 +160,10 @@ def get_db_output(
try:
cmd_result = subprocess.run(
args=args,
capture_output=True,
stdout=subprocess.PIPE,
encoding="utf8",
check=True,
text=True,
universal_newlines=True,
shell=False,
)
cmd_result.check_returncode()
Expand Down Expand Up @@ -234,7 +234,7 @@ def set_since(self, since: str) -> None:
Args:
since: the string for filtering. If specified as time=amount
will subract that amount from the current time
will subtract that amount from the current time
"""
if not since:
return
Expand Down
4 changes: 2 additions & 2 deletions src/reportseff/job.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Module for reprsenting scheduler jobs."""
"""Module for representing scheduler jobs."""
import base64
from datetime import timedelta
import gzip
Expand Down Expand Up @@ -348,7 +348,7 @@ def parsemem(mem: str, nodes: int = 1, cpus: int = 1) -> float:
Returns:
The number of bytes for the job.
if mem is empty, return 0.
if mem ends with n or c, scale by the provided nodes or cpus respecitvely
if mem ends with n or c, scale by the provided nodes or cpus respectively
the multiple of memory (e.g. M or G) is always scaled if provided
Raises:
Expand Down
2 changes: 1 addition & 1 deletion src/reportseff/job_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def process_entry(self, entry: Dict, add_job: bool = False) -> None:
"""Update the jobs collection with information from the provided entry.
Args:
entry: the accout entry from a db inquirer
entry: the account entry from a db inquirer
add_job: if true, will add the job to the collection if it doesn't exist
"""
job_id = entry["JobID"].split(".")[0]
Expand Down
Loading

0 comments on commit 138a1e4

Please sign in to comment.