Skip to content
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
51 changes: 33 additions & 18 deletions process/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@

os.environ["PYTHON_PROCESS_ROOT"] = os.path.join(os.path.dirname(__file__))

logger = logging.getLogger(__name__)
PACKAGE_LOGGING = True
"""Can be set False to disable package-level logging, e.g. in the test suite"""
logger = logging.getLogger("process")


class Process:
Expand Down Expand Up @@ -304,6 +306,8 @@ def run(self):
config = RunProcessConfig(self.config_file)
config.setup()

setup_loggers(Path(config.wdir) / "process.log")

init.init_all_module_vars()
init.init_process()

Expand Down Expand Up @@ -443,9 +447,12 @@ def set_mfile(self):
"""Set the mfile filename."""
self.mfile_path = Path(self.filename_prefix + "MFILE.DAT")

@staticmethod
def initialise():
def initialise(self):
"""Run the init module to call all initialisation routines."""
setup_loggers(
Path(self.output_path.as_posix().replace("OUT.DAT", "process.log"))
)

initialise_imprad()
# Reads in input file
init.init_process()
Expand Down Expand Up @@ -721,28 +728,38 @@ def costs(self, value: CostsProtocol):
logging_stream_handler.setLevel(logging.CRITICAL)
logging_stream_handler.setFormatter(logging_formatter)

logging_file_handler = logging.FileHandler("process.log", mode="w")
logging_file_handler = logging.FileHandler("process.log", mode="a")
logging_file_handler.setLevel(logging.INFO)
logging_file_handler.setFormatter(logging_formatter)

logging_model_handler.setLevel(logging.WARNING)
logging_model_handler.setFormatter(logging_formatter)


def setup_loggers():
def setup_loggers(working_directory_log_path: Path | None = None):
"""A function that adds our handlers to the appropriate logger object."""
# Only add our handlers if PROCESS is being run as an application
# This should allow it to be used as a package (e.g. people import models that log)
# without creating a process.log file... people can then handle our logs as they wish.
# Using basicConfig adds these handlers to the root logger iff the root logger has not
# been setup yet. This means that during testing these hanlders won't be present, which
# will ensure they do not conflict with the pytest handlers.
logging.basicConfig(handlers=[logging_stream_handler, logging_file_handler])
# Remove all of the existing handlers from the 'process' package logger

logger.handlers.clear()

# However, this handler we know to be safe and necessary so we add it to the root logger
# regardless of whether it has already been created.
root_logger = logging.getLogger()
root_logger.addHandler(logging_model_handler)
# we always want to add this handler because otherwise PROCESS' error
# handling system won't work properly
logger.addHandler(logging_model_handler)

if not PACKAGE_LOGGING:
return

# (Re)add the loggers to the 'process' package logger (and its children)
logger.addHandler(logging_stream_handler)
logger.addHandler(logging_file_handler)

if working_directory_log_path is not None:
Copy link
Copy Markdown
Collaborator

@je-cook je-cook Jan 15, 2026

Choose a reason for hiding this comment

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

With this new layout do you get multiple single file file handlers eg if you run two single runs on the same prompt? There are other permutations of that we might need to guard against

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

possibly this is not the case if basicConfig overwrites previous state?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

eg if you run two single runs on the same prompt

As in you have a script that does something like this?:

run1 = SingleRun(...)
run1.run()

run2 = SingleRun(...)
run2.run()

logging_file_input_location_handler = logging.FileHandler(
working_directory_log_path.as_posix(), mode="w"
)
logging_file_input_location_handler.setLevel(logging.INFO)
logging_file_input_location_handler.setFormatter(logging_formatter)
logger.addHandler(logging_file_input_location_handler)


def main(args=None):
Expand All @@ -757,8 +774,6 @@ def main(args=None):
:type args: list, optional
"""

setup_loggers()

Process(args)


Expand Down
10 changes: 10 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from _pytest.fixtures import SubRequest
from system_check import system_compatible

from process import main
from process.log import logging_model_handler


Expand Down Expand Up @@ -157,3 +158,12 @@ def return_to_root():
cwd = os.getcwd()
yield
os.chdir(cwd)


@pytest.fixture(autouse=True)
def disable_package_logger(monkeypatch):
"""Various parts of PROCESS change directories and do not always change back.
This fixture ensures that, at the end of each test, the cwd is reset to what it
was at the beginning of the test.
"""
monkeypatch.setattr(main, "PACKAGE_LOGGING", False)