Skip to content

Commit

Permalink
Make jq errors cleaner for users when a header script is prepended. #…
Browse files Browse the repository at this point in the history
  • Loading branch information
mfeit-internet2 authored Mar 15, 2024
1 parent ef4f56a commit 98618ba
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 12 deletions.
4 changes: 1 addition & 3 deletions pscheduler-core/pscheduler-core/validate-limits.raw
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,9 @@ except IOError as ex:
try:
processor = LimitProcessor(infile)
except Exception as ex:
pscheduler.fail(str(ex))
pscheduler.fail(f'Limit configuration is invalid:\n{str(ex)}')

if sys.stdout.isatty() and not options.quiet:
print("Limit configuration is valid.")

pscheduler.succeed()


35 changes: 32 additions & 3 deletions python-pscheduler/pscheduler/pscheduler/jqfilter.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ def __init__(
args={},
output_raw=False,
groom=False,
strip_errors_to=None
):
"""
Construct a filter. Arguments:
"""Construct a filter. Arguments:
filter_spec - The JQ script to be used for this filter. This
may be any subclass of basestring, a list or a dict. Strings
Expand All @@ -95,6 +95,11 @@ def __init__(
top of the script before compiling. This allows filters
prepending functions to user-provided scripts that do
imports to compile properly.
strip_errors_to - Strip all lines in error messages up to the
string provided. This is used to weed known-correct,
program-provided code out of errors on the first line so the
user sees the correct line numbers.
"""

self.output_raw = output_raw
Expand All @@ -109,6 +114,9 @@ def __init__(
if not isinstance(filter_spec, str):
raise ValueError("Filter spec must be plain text, list or dict")

if strip_errors_to is not None and not isinstance(strip_errors_to, str):
raise ValueError("Strip string must be a string")

# Passing an args hash into PyJQ causes a memory corruption
# problem. As a temporary workaround, turn them into "as"
# statements. (See #1059)
Expand All @@ -125,7 +133,28 @@ def __init__(
if groom:
filter_spec = _groom(filter_spec)

self.script = pyjq.compile(filter_spec, args, library_paths=_library_path())
value_error = None
try:
self.script = pyjq.compile(filter_spec, args, library_paths=_library_path())
except ValueError as ex:
# This is held and thrown seprately because Python will
# produce a confusing nested exception message when it's
# thrown.
value_error = ex

if value_error is not None:

if strip_errors_to is not None:
ex_lines = []
strip_length = len(strip_errors_to)
for num, line in enumerate(str(value_error).split('\n')):
place = line.find(strip_errors_to)
if place != -1:
line = line[place+strip_length:]
ex_lines.append(line.rstrip())
value_error = ValueError('\n'.join(ex_lines))

raise value_error



Expand Down
18 changes: 12 additions & 6 deletions python-pscheduler/pscheduler/pscheduler/limitprocessor/rewriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ def __init__(self,
else:
script = transform["script"]


script_lines = [
SCRIPT_HEADER = [

"def classifiers:",
" ." + self.PRIVATE_KEY + ".classifiers",
Expand Down Expand Up @@ -59,15 +58,22 @@ def __init__(self,
" error(\"Task rejected: \" + ($message | tostring))",
";",

script
]
"def __END_HEADER__:",
" .",
";",
"__END_HEADER__ | "
]

transform["script"] = script_lines
# Stuff the entire header onto the first line so errors
# reflect the line numbers of what the user provided.

transform["script"] = ' '.join(SCRIPT_HEADER) + script

self.transform = JQFilter(
filter_spec=transform,
args=transform.get("args", {}),
groom=True
groom=True,
strip_errors_to='__END_HEADER__ | '
)


Expand Down

0 comments on commit 98618ba

Please sign in to comment.