Skip to content
8 changes: 8 additions & 0 deletions pyflow/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

from __future__ import absolute_import

import warnings

warnings.formatwarning = (
lambda mess, category, filename, lineno, *args: f"\033[93m[{category.__name__}] {filename}:{lineno}\n{mess}\n\033[0m"
)
warnings.filterwarnings("default", category=DeprecationWarning)
warn = warnings.warn

from .attributes import (
Aviso,
Complete,
Expand Down
8 changes: 6 additions & 2 deletions pyflow/anchor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import os

from . import warn


class AnchorMixin:
"""
Expand All @@ -12,8 +14,10 @@ def __init__(self, *args, **kwargs):
variables.update(kwargs)

if "out" in kwargs:
print(
"WARNING! out option is deprecated for nodes, use the log_directory option in the host instead"
warn(
"'out' option is deprecated for nodes, use the log_directory option in the host instead",
DeprecationWarning,
stacklevel=2,
)

for control_variable in ("files", "include", "home", "out", "extn"):
Expand Down
3 changes: 2 additions & 1 deletion pyflow/deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import os
import shutil

from pyflow import warn
from pyflow.html import FileListHTMLWrapper


Expand Down Expand Up @@ -232,7 +233,7 @@ def create_directory(self, path):
try:
os.makedirs(path)
except Exception:
print("WARNING: Couldn't create directory: {}".format(path))
warn("Couldn't create directory: {}".format(path), stacklevel=0)

def check(self, target):
"""
Expand Down
20 changes: 15 additions & 5 deletions pyflow/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
import shutil
import textwrap

from . import warn
from .attributes import Label, Limit
from .base import STACK
from .inspect import get_value_at_caller
from .nodes import DuplicateNodeError, Family, ecflow_name

SET_ECF_VARIABLES = """
Expand Down Expand Up @@ -307,9 +309,13 @@ def build_label(self):

def script_submit_arguments(self, submit_arguments):
if len(submit_arguments) > 0:
print(
f"Host {self.__class__.__name__} does not support scheduler submission arguments. \
Submission arguments will be ignored in the script generation",
node = get_value_at_caller("self", 2)
name = getattr(node, "fullname", getattr(node, "name", node))
warn(
f"{name}: Host {self.__class__.__name__} does not support scheduler submission arguments, which "
f"will be ignored in the script generation. ",
UserWarning,
stacklevel=0,
)
return []

Expand Down Expand Up @@ -1169,8 +1175,12 @@ def _translate_sthost(val):
args.append(pragma)
else:
if arg in deprecated:
print(
f"WARNING! '{arg}' is deprecated, use '{deprecated[arg]}' instead"
node = get_value_at_caller("self", stacklevel=2)
name = getattr(node, "fullname", getattr(node, "name", node))
warn(
f"{name}: {arg}' is deprecated in TroikaHost, use '{deprecated[arg]}' instead",
DeprecationWarning,
stacklevel=0,
)
arg = deprecated[arg]
if arg is not None:
Expand Down
23 changes: 23 additions & 0 deletions pyflow/inspect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import inspect


def get_value_at_caller(target="self", stacklevel=2):
"""
returns the value of a variable in the caller's frame. Useful for getting
caller objects or variables at caller level, e.g, a Task name when defining its
submit arguments on its Host.
:param target: The name of the variable to retrieve from the caller's frame.
Defaults to "self", which is common in class methods.
:param stacklevel: The number of frames to go back in the call stack.
Defaults to 2, which means it will look at the frame of immediate caller.
"""

frame = inspect.stack()[1].frame
for _ in range(stacklevel - 1):
if frame is None:
raise ValueError(f"No caller frame found with stacklevel {stacklevel}.")
frame = frame.f_back
# Get local variables in the caller's frame
locals_ = frame.f_locals

return locals_.get(target, None)
4 changes: 2 additions & 2 deletions pyflow/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1161,9 +1161,9 @@ def deploy_suite(self, target=FileSystem, node=None, **options):
script, includes = t.generate_script()
try:
target.deploy_task(t.deploy_path, script, includes)
except RuntimeError:
except Exception as e:
print(f"\nERROR when deploying task: {t.fullname}\n")
raise
raise (e)
for f in node.all_families:
manual = self.generate_stub(f.manual)
if manual:
Expand Down
2 changes: 1 addition & 1 deletion pyflow/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def generate_stub(self):
+ " "
)

return [f"python{self.python_version} -u {args}- <<EOS"] + script_body + ["EOS"]
return [f"python{self.python_version} -u - {args}<<EOS"] + script_body + ["EOS"]


class DelegatingScript(Script):
Expand Down
1 change: 0 additions & 1 deletion tests/test_host.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,6 @@ def test_troika_host():


def test_host_submit_args():

submit_args = {
"troika": {
"tasks": 2, # deprecated option, will be translated to total_tasks
Expand Down
4 changes: 2 additions & 2 deletions tests/test_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ def test_python_script_cmd_args():

checkscript_python3 = textwrap.dedent(
"""
python3 -u --key-1=value-1 --key-2=value-2 - <<EOS
python3 -u - --key-1=value-1 --key-2=value-2 <<EOS
import argparse

if __name__ == "__main__":
Expand All @@ -249,7 +249,7 @@ def test_python_script_cmd_args():

checkscript_python2 = textwrap.dedent(
"""
python2 -u --key-1=value-1 --key-2=value-2 - <<EOS
python2 -u - --key-1=value-1 --key-2=value-2 <<EOS
import argparse

if __name__ == "__main__":
Expand Down
Loading