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

🎨 print statements → log statements #2062

Merged
merged 36 commits into from
Feb 28, 2024

Conversation

shnizzedy
Copy link
Member

@shnizzedy shnizzedy commented Feb 7, 2024

Fixes

Related to #2048 by @nx10
Related to #2058 by @shnizzedy

Description

print statements are useful in some situations (e.g., debugging), but should typically be omitted from production code. print statements can lead to the accidental inclusion of sensitive information in logs, and are not configurable by clients, unlike logging statements.

https://docs.astral.sh/ruff/rules/print/

Like print statements, pprint statements are useful in some situations (e.g., debugging), but should typically be omitted from production code. pprint statements can lead to the accidental inclusion of sensitive information in logs, and are not configurable by clients, unlike logging statements.

https://docs.astral.sh/ruff/rules/p-print/

Technical details

  • Because this PR touches so many files, I'm mostly not taking the time to otherwise lint them as part of this change.
  • "nibabel" = "nib"
    standardizes import nibabel as nib in 71c9e6f (in some places it was import nibabel as nb), but ruff doesn't catch the nipype-style import strings like
    convert_afni_warp_imports = ["import os", "import nibabel as nb"]
    ; those are also fixed in this PR
  • For Function nodes, automatically inserts this line
    "from CPAC.utils.monitoring.custom_logging import FMLOGGER, IFLOGGER, UTLOGGER,"
    " WFLOGGER"
    after the imports so FMLOGGER, IFLOGGER, UTLOGGER, and WFLOGGER are always available in Function nodes as the "nipype.filemanip", "nipype.interface", "nipype.utils", and "nipype.workflow" loggers, respectively

Tests

def faux_fxn(_loggers: bool = True):
"""Require autoassignment (for testing)."""
if _loggers:
return WFLOGGER, IFLOGGER # noqa: F821
return luigi_mario # noqa: F821
@mark.parametrize("as_module", [True, False])
def test_autologger(as_module: bool) -> None:
"""Test autoassignment of global Nipype loggers`."""
interface = Function(
function=faux_fxn, input_names=["_loggers"], as_module=as_module
)
interface.inputs._loggers = False
with raises(NameError) as name_error:
interface.run()
assert "name 'luigi_mario' is not defined" in str(name_error.value)
interface = Function(
function=faux_fxn,
input_names=["_loggers"],
output_names=["logger", "iflogger"],
as_module=as_module,
)
interface.inputs._loggers = True
res = interface.run()
assert res.outputs.logger.name == "nipype.workflow"
assert res.outputs.iflogger.name == "nipype.interface"

Checklist

  • My pull request has a descriptive title (not a vague title like Update index.md).
  • My pull request targets the lint/pep8-boolean-comparison branch of the repository.
  • My commit messages follow best practices.
  • My code follows the established code style of the repository.
  • I added tests for the changes I made.
  • I updated the changelog.
  • I added or updated documentation (if applicable).
  • I tried running the project locally and verified that there are no visible errors.

Developer Certificate of Origin

Developer Certificate of Origin
Developer Certificate of Origin
Version 1.1

Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.


Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.

@shnizzedy shnizzedy mentioned this pull request Feb 7, 2024
15 tasks
@shnizzedy shnizzedy changed the base branch from initial-run/ruff to lint/pep8-boolean-comparison February 13, 2024 03:05
Where reformatting changed things like
> ("line 1"
>  " line 2")

to like

> "line 1" " line 2"

, change to like

> "line 1 line 2"
Copy link
Member Author

@shnizzedy shnizzedy left a comment

Choose a reason for hiding this comment

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

Where I came upon the pattern:

print(error_message)
sys.exit(exitcode)

, I wasn't consistent. Mostly I updated to something like:

logger.error(error_message)
sys.exit(exitcode)

but sometimes I did like:

raise Exception(error_message)

(but with a more specific error class than Exception). I think we generally want to convert all the sys.exits to raise Exceptions, but this PR's already uncomfortably large without doing so. I think that can be a subsequent PR if we care to.

CPAC/isc/isfc.py Outdated
Copy link
Member Author

Choose a reason for hiding this comment

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

This file and CPAC/isc/isc.py are almost identical. Not for this PR, but we might want to DRY these files up.

@@ -175,6 +178,10 @@ def gather_nifti_globs(pipeline_output_folder, resource_list, pull_func=False):
# remove any extra /'s
Copy link
Member Author

Choose a reason for hiding this comment

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

Not for this PR, but

# remove any extra /'s
pipeline_output_folder = pipeline_output_folder.rstrip("/")
logger.info(
"\n\nGathering the output file paths from %s...", pipeline_output_folder
)
# this is just to keep the fsl feat config file derivative_list entries
# nice and lean
dirs_to_grab = []
for derivative_name in derivative_list:
for resource_name in resource_list:
if resource_name in derivative_name:
dirs_to_grab.append(derivative_name)
and
# remove any extra /'s
pipeline_output_folder = pipeline_output_folder.rstrip("/")
logger.info(
"\n\nGathering the output file paths from %s...", pipeline_output_folder
)
# this is just to keep the fsl feat config file derivatives entries
# nice and lean
search_dirs = []
for derivative_name in derivatives:
for resource_name in resource_list:
if resource_name in derivative_name:
search_dirs.append(derivative_name)
could be DRY'd into a single function

@@ -276,6 +301,7 @@ def print_scan_param(index):
" parameters csv file" % (subject_map.get(sub), scan[0])
)

logger.info("site for sub %s -> %s", sub, subject_map.get(sub))
Copy link
Member Author

Choose a reason for hiding this comment

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

This obviously hasn't been a problem functionally since it's been this way for a long time, but only the first of these many print statements goes to stdout; the rest go to a file.

print("site for sub", sub, "->", subject_map.get(sub))
print(" scan_parameters: ", file=f)
print(" tr:", file=f)
print_scan_param(4)
print(" acquisition:", file=f)
print_scan_param(0)
print(" reference:", file=f)
print_scan_param(3)
print(" first_tr:", file=f)
print_scan_param(1)
print(" last_tr:", file=f)
print_scan_param(2)

logger.info("site for sub %s -> %s", sub, subject_map.get(sub))
replicates the original behavior, but we could print the line to the file instead if that was what we wanted to do.

@@ -183,8 +189,9 @@ def cor_graph(self, timeseries, attr=None):
"""
import numpy as np

timeseries[0]
ts = timeseries[0] # noqa: F841
Copy link
Member Author

Choose a reason for hiding this comment

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

This ts isn't used anywhere. Do we care to assign it just to annotate, or should we drop this line?

Copy link
Collaborator

Choose a reason for hiding this comment

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

It's fine for now. Your comments with idea stubs in this PR is a pretty nice list, so we can revisit this when we revisit the other ideas as well.

@shnizzedy shnizzedy marked this pull request as ready for review February 19, 2024 19:58
@shnizzedy
Copy link
Member Author

Oh, I forgot to mention this in the PR message, but updating the Function nodes reminded me of something I wondered ages ago, then forgot about:

Why don't we default

as True (or even not make it an option, and just always have Function nodes "as module"s)?

I.e., when would we want a Function node not be "as module"?

@shnizzedy shnizzedy self-assigned this Feb 21, 2024
@sgiavasis
Copy link
Collaborator

Oh, I forgot to mention this in the PR message, but updating the Function nodes reminded me of something I wondered ages ago, then forgot about:

Why don't we default

as True (or even not make it an option, and just always have Function nodes "as module"s)?
I.e., when would we want a Function node not be "as module"?

This is ancient; I don't remember right now but again, can add to the list of things to revisit in this PR.

@sgiavasis sgiavasis merged commit 4c4c0fd into lint/pep8-boolean-comparison Feb 28, 2024
43 checks passed
@shnizzedy
Copy link
Member Author

[This] is ancient; I don't remember right now but again, can add to the list of things to revisit in this PR.

#2062 (comment)

It's cool; as far as I can tell, it

  1. Includes the variables available in the namespace like if you imported the function normally outside of nipype (so you don't need the imports param or the imports defined in the function itself
  2. Includes the module name in the log instead of just the text of the function

But I don't know how much overhead this costs or saves, or what other considerations we should have about whether / when to use it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

7 participants