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

[MAINT] remove leading underscore from non private functions #4086

Merged
merged 31 commits into from Dec 9, 2023

Conversation

Remi-Gau
Copy link
Collaborator

@Remi-Gau Remi-Gau commented Oct 27, 2023

Changes proposed in this pull request:

  • remove leading underscore from functions used outside of their own module in:
    • nilearn/*.py
    • nilearn/decoding
    • nilearn/datasets
    • nilearn/mass_univariate
  • rename module nilearn/decoding.objective_functions.py to nilearn/decoding._objective_functions.py
  • keep non user facing functions from nilearn/datasets/utils.py into a new file nilearn/datasets/_utils.py

@github-actions
Copy link
Contributor

👋 @Remi-Gau Thanks for creating a PR!

Until this PR is ready for review, you can include the [WIP] tag in its title, or leave it as a github draft.

Please make sure it is compliant with our contributing guidelines. In particular, be sure it checks the boxes listed below.

  • PR has an interpretable title.
  • PR links to Github issue with mention Closes #XXXX (see our documentation on PR structure)
  • Code is PEP8-compliant (see our documentation on coding style)
  • Changelog or what's new entry in doc/changes/latest.rst (see our documentation on PR structure)

For new features:

  • There is at least one unit test per new function / class (see our documentation on testing)
  • The new feature is demoed in at least one relevant example.

For bug fixes:

  • There is at least one test that would fail under the original bug conditions.

We will review it as quick as possible, feel free to ping us with questions if needed.

@codecov
Copy link

codecov bot commented Oct 27, 2023

Codecov Report

Attention: 110 lines in your changes are missing coverage. Please review.

Comparison is base (3ac5ebc) 91.81% compared to head (daf36d5) 91.68%.

Files Patch % Lines
nilearn/datasets/_utils.py 75.50% 69 Missing and 29 partials ⚠️
nilearn/datasets/func.py 84.61% 10 Missing ⚠️
nilearn/decoding/proximal_operators.py 84.61% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4086      +/-   ##
==========================================
- Coverage   91.81%   91.68%   -0.13%     
==========================================
  Files         144      145       +1     
  Lines       16241    16245       +4     
  Branches     3386     3386              
==========================================
- Hits        14911    14894      -17     
- Misses        789      807      +18     
- Partials      541      544       +3     
Flag Coverage Δ
macos-latest_3.10_test_plotting 91.67% <82.94%> (+<0.01%) ⬆️
macos-latest_3.11_test_plotting 91.67% <82.94%> (+<0.01%) ⬆️
macos-latest_3.12_test_plotting 91.67% <82.94%> (+<0.01%) ⬆️
macos-latest_3.8_test_plotting 91.63% <82.58%> (+<0.01%) ⬆️
macos-latest_3.9_test_plotting 91.63% <82.58%> (+<0.01%) ⬆️
ubuntu-latest_3.10_test_plotting 91.67% <82.94%> (+<0.01%) ⬆️
ubuntu-latest_3.11_test_plotting ?
ubuntu-latest_3.12_test_plotting ?
ubuntu-latest_3.12_test_pre ?
ubuntu-latest_3.8_test_plot_min ?
ubuntu-latest_3.8_test_plotting ?
ubuntu-latest_3.9_test_plotting ?
windows-latest_3.10_test_plotting ?
windows-latest_3.11_test_plotting ?
windows-latest_3.12_test_plotting ?
windows-latest_3.8_test_plotting ?
windows-latest_3.9_test_plotting ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@ymzayek ymzayek left a comment

Choose a reason for hiding this comment

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

I generally see a bit of an issue with some files that I think should be named with a leading underscore (e.g. having functions in a file not named with a leading underscore that are used outside of that file but only within their respective parent module). So to me naming such functions with a leading underscore was done to signal that they are private to that module. So in short I think it might be worth discussing how to reconcile the way certain modules are structured with the guidelines we defined.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should make this utils file private (rename to _utils.py). Only to make it clear that these functions are not part of the public API (exposed in docs) even though they are public in terms of the structure we defined.

Copy link
Member

Choose a reason for hiding this comment

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

On second thought that could break user code. The problem is that this file has 2 functions (get_data_dirs, load_sample_motor_activation_image) exposed in the docs which we cannot just change without deprecations but for the ones with names starting with underscores, I'm wondering how we signal that these can change at any moment without warning.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

create a _utils.py module where we put the private stuff?

Copy link
Member

Choose a reason for hiding this comment

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

Sure if it's that simple I like it

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I guess we would have to make it uber explicit in the doc string of the modules why there is a _utils and utils in the same folder...

nilearn/decoding/proximal_operators.py Outdated Show resolved Hide resolved
@Remi-Gau
Copy link
Collaborator Author

So in short I think it might be worth discussing how to reconcile the way certain modules are structured with the guidelines we defined.

fine though I think the function level naming rule would still apply even if we renamed the module where it comes from

@Remi-Gau
Copy link
Collaborator Author

will make the change we agreed on and will probably not do much more in this PR to keep things reviewable

@@ -150,7 +150,7 @@ def _full_brain_info(volume_img, mesh='fsaverage5', threshold=None,

"""
info = {}
mesh = surface.surface._check_mesh(mesh)
mesh = surface.surface.check_mesh(mesh)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

by the way I am assuming that usage like this mean that the function is used "outside" of its module because one could have done

from nilearn.surface.surface import _check_mesh

@Remi-Gau
Copy link
Collaborator Author

in a separate PR:

rename either nilearn.datasets.utils._fetch_file or nilearn.datasets.utils._fetch_files

but having both of them differing by a single letter is just not OK

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK the number of line changed blew up because of splitting this file in 2

doc/changes/0.10.1.rst Outdated Show resolved Hide resolved
@@ -225,7 +225,7 @@ def _sigmoid(t, copy=True):
return t


def _logistic(X, y, w):
def logistic_loss(X, y, w):
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

note the function name change



def _div(v):
def divergence(v):
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

note the function name change

@@ -122,7 +122,7 @@ def _tv_l1_from_gradient(spatial_grad):
return l1_term + tv_term


def _div_id(grad, l1_ratio=0.5):
def divergence_id(grad, l1_ratio=0.5):
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

note the function name change

@Remi-Gau
Copy link
Collaborator Author

Eventhough the functions changed here are private and therefore should not be used by users, I think that those changes should be mentioned in the changelog because I would be surprised if not a single users used some of those utility functions directly in their code.

any thoughts?

@Remi-Gau Remi-Gau marked this pull request as ready for review October 28, 2023 11:09
@Remi-Gau Remi-Gau requested a review from ymzayek October 28, 2023 11:09
Copy link
Member

@ymzayek ymzayek left a comment

Choose a reason for hiding this comment

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

@Remi-Gau the PR LGTM!

One thing I'm a bit unsure about is that now in some cases as in the change of _check_mesh to check_mesh, how do we signal to the user and future developers that this function should be used internally (which also implies they can change without deprecation)? You can argue that it is not exposed in the API reference docs but some people don't look at that. One way could be to not include such functions in __all__ and explicitly use the module.file name when calling the function as is now done with check_mesh. But then we'd need to make this consistent across modules (with the exceptions being _utils) making the content of __all__ consistent with the API references in the docs. I'm not sure if this is the way to go so I'm happy to hear other thoughts

@Remi-Gau
Copy link
Collaborator Author

Remi-Gau commented Nov 8, 2023

For context worth checking what pep8 says:
https://peps.python.org/pep-0008/#public-and-internal-interfaces

how do we signal to the user and future developers that this function should be used internally (which also implies they can change without deprecation)?

I agree that, unless I misunderstand something about PEP8, it feels that users would always need to go and look into the "all" of the init of a module to figure out if a given function is public or private: not convenient.

You can argue that it is not exposed in the API reference docs but some people don't look at that.

Well then in that case, it's not on us. We cannot control what people do or do not do. We can control how clear our doc is.

One way could be to not include such functions in __all__

yes

and explicitly use the module.file name when calling the function as is now done with check_mesh.

So do not do

from surface import check_mesh

check_mesh(foo)

but do

import surface

surface.check_mesh(foo)

If so that still does not signal that check_mesh is private.

But then we'd need to make this consistent across modules (with the exceptions being _utils) making the content of __all__ consistent with the API references in the docs

In any case I would be in favor of this.

@Remi-Gau
Copy link
Collaborator Author

Remi-Gau commented Nov 9, 2023

FYI: we should expect a few more PRs like this one as this only touches a few modules

Remi-Gau and others added 12 commits November 9, 2023 13:44
* update DOI

* update which DOI is used and add RRID
Co-authored-by: Remi-Gau <Remi-Gau@users.noreply.github.com>
* expose CubicSpline extrapolate parameter (nilearn#4028)

* [FIX] discard non-interpolated volumes and reset the sample_mask indexes

* [REF] discard non-interpolated volumes and reset sample_mask in '_handle_scrubbed_volumes'

* [FMT] black formatting

* improve deprecation warning message

* check that extrapolation warning is raised

* add changelog and contribution entries

* check consistency of modified sample_mask

* assert modified sample_mask can be applied to signals and confounds

* [REF] split signal extrapolation testing in 2 tests
Co-authored-by: ymzayek <ymzayek@users.noreply.github.com>
* Add listing utilities

* Add possibility to filter by modules

* Add some tests

* Fix PEP8

* Clarify ignored modules by default

* Add section on Nilearn API in the user guide

* Export at the end

* [circle full] Add whats new

* revert

* more revert

* lint

* fix tests

* lint

* adapt tests to check number of public functions and classes

* update doc strings

* fix

---------

Co-authored-by: Remi Gau <remi_gau@hotmail.com>
@Remi-Gau
Copy link
Collaborator Author

Remi-Gau commented Nov 9, 2023

Ok the listing utilities test is doing its job as "number of public function change detector" is working as intended as shown by the latest failures.

@Remi-Gau
Copy link
Collaborator Author

Remi-Gau commented Dec 7, 2023

Unless anyone has second thoughts about this one, I will merge it tomorrow and then open separate issues + PR for the things mentioned in here.

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

Successfully merging this pull request may close these issues.

None yet

5 participants