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

CI: Ruff #5123

Merged
merged 10 commits into from
Aug 21, 2024
Merged

CI: Ruff #5123

merged 10 commits into from
Aug 21, 2024

Conversation

ax3l
Copy link
Member

@ax3l ax3l commented Aug 10, 2024

Ruff is an extremely fast Python linter and code formatter. It replaces tools such as autoflake, black, flake8, pyflakes, or pylint.

Close #4366

We already use ruff in ImpactX and pyAMReX, this adds it to WarpX using the same fast check & Python code formatter methods.

Reviewer note: This pull request is best reviewed commit-by-commit.

@dpgrote
Copy link
Member

dpgrote commented Aug 10, 2024

I was looking over the changes in the python scripts and see a some places where the changes will break things. I'll add comments for those that I see. But this looks great otherwise!

@ax3l ax3l changed the title CI: Ruff [WP] CI: Ruff Aug 10, 2024
@ax3l ax3l changed the title [WP] CI: Ruff [WIP] CI: Ruff Aug 10, 2024
@ax3l ax3l marked this pull request as draft August 10, 2024 20:10
@ax3l ax3l force-pushed the ci-ruff branch 4 times, most recently from 606585c to c4cbe59 Compare August 14, 2024 22:45
Ruff is an extremely fast Python linter and code formatter. It replaces
tools such as autoflake, flake8, pyflakes, or pylint.
ax3l and others added 4 commits August 18, 2024 10:55
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- add missing imports
- fix shadowing of import names with variables
Fix "Redefinition of unused `c`".
@ax3l ax3l marked this pull request as ready for review August 18, 2024 18:05
@ax3l ax3l changed the title [WIP] CI: Ruff CI: Ruff Aug 18, 2024
@ax3l ax3l requested a review from dpgrote August 18, 2024 18:05
@ax3l ax3l added the cleaning Clean code, improve readability label Aug 18, 2024
Properly close file after reading `warpx_used_inputs`.
@ax3l ax3l requested a review from lucafedeli88 August 18, 2024 18:12
help="Unique identifier for finding compilation line in the log file",
default="WarpX/Source")
parser.add_argument("--input", help="Ccache log file", default="ccache.log.txt")
parser.add_argument(
Copy link
Member

Choose a reason for hiding this comment

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

If I were formatting this, I would do this, which I think is easier to read (maybe I'm just more used to it)

parser.add_argument("--identifier",
                    help = "Unique identifier for finding compilation line in the log file",
                    default = "WarpX/Source")

The arguments aligned with the routine name, and a space before and after the =.

Copy link
Member Author

@ax3l ax3l Aug 19, 2024

Choose a reason for hiding this comment

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

Thanks. I think the reason that this is broken to newlines in black is that it will reduce the diff size when adding more or removing arguments in a later PR, which makes code easier to review (less files changed).

This is auto-formatted using black style (FAQ), as we do in all other projects already:
https://docs.astral.sh/ruff/formatter/

Developers can install a pre-commit:

python -m pip install pre-commit
pre-commit install

to get auto-formatting, otherwise the PR auto-bot does it for them.

fout.write("\n")

fout.write(".SECONDEXPANSION:\n")
fout.write("clang-tidy: $$(all_targets)\n")
fout.write("\t@echo SUCCESS\n\n")

exe_re = re.compile(r" Executing .*? (-.*{}.*) -c .* -o .* (\S*)".format(args.identifier))
exe_re = re.compile(
Copy link
Member

Choose a reason for hiding this comment

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

My own preference is allowing longer lines. Keeping this as one line is more succinct and easier to read (for me at least).

Copy link
Member Author

@ax3l ax3l Aug 19, 2024

Choose a reason for hiding this comment

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

We usually do not mingle with the default, which are currently around 100 characters (soft limitish with some heuristics). The bot auto-breaks them. This makes diffs on github easier to read.

We can increase that limit manually, but since a bot formats them we did keep the defaults on all other BLAST projects so far.

Looking at line 38, the limit is still pretty long, so I would not bump it further.


numfig = True
math_eqref_format = "{number}"
numfig_format = {'figure': 'Fig. %s',
Copy link
Member

Choose a reason for hiding this comment

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

I like this formatting better. Is there an option with ruff to use this style?

Copy link
Member Author

@ax3l ax3l Aug 19, 2024

Choose a reason for hiding this comment

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

Nope, it just uses black style. As far as I read the style is intentional, the extra break reduces the amount of lines changed when adding/removing new arguments in PRs to dictionaries. (Note also the trailing ,.)

Black aims for consistency, generality, readability and reducing git diffs.

The prior formatting would often change the first and last line on updates to the dict.

@@ -65,7 +67,7 @@ def solver_initialize_inputs(self):
self.nxguardphi = 1
self.nzguardphi = 1

self.phi = np.zeros(self.nz + 1 + 2*self.nzguardphi)
Copy link
Member

@dpgrote dpgrote Aug 19, 2024

Choose a reason for hiding this comment

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

There's probably not an option for this, but I much prefer no spaces around * (and /) since it makes it look more like standard math expressions, with the grouping of multiplied terms. Compare these two expressions

x = (3 + 2 * a) * (b * c + 55 * d)
x = (3 + 2*a)*(b*c + 55*d)

Which is easier to understand?

Copy link
Member Author

Choose a reason for hiding this comment

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

Spaces around operators is a pretty standard thing these days in C++ and Python. PEP8 requires it even.

For long and block formulas, we can always disable formatting, so we can have nice alignments when we need it, e.g.,

# fmt: off
x = a * ( 42 * b
         + 3 * c
         - 7 * d
        ) 
# fmt: on

Copy link
Member

@dpgrote dpgrote Aug 20, 2024

Choose a reason for hiding this comment

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

Oh yes, I'm well aware that the spaces around operators is standard, but I never liked it for * and /. I've never understood why something that is so much harder to read and more prone to typos would be the standard.
BTW, PEP8 actually says to use white space around the lower priority operators, so my example would be

x = (3 + 2*a) * (b*c + 55*d)

similar to what I would do.

Copy link
Member Author

Choose a reason for hiding this comment

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

I know 😅 That is how I type as well but I don't mind the auto-formatter adding spaces by now anymore :D

In the end, there are more precedences than I have spaces for, e.g., C++:
https://en.cppreference.com/w/cpp/language/operator_precedence

Copy link
Member

Choose a reason for hiding this comment

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

"I don't mind the auto-formatter adding spaces" meaning you've gotten used to the bad formatting decision :)

@@ -48,114 +49,114 @@
plasma_ymax = 20e-06
plasma_zmax = None
uniform_distribution = picmi.UniformDistribution(
density = plasma_density,
Copy link
Member

Choose a reason for hiding this comment

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

I prefer spaces around = since it makes the text less dense and easier to read.

Copy link
Member Author

@ax3l ax3l Aug 19, 2024

Choose a reason for hiding this comment

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

I think PEP8 requires for arguments no spaces and spaces only for assignments. Independent of PEP8, black style seems to do the same.

Copy link
Member

Choose a reason for hiding this comment

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

PEP8 does say no space around = for arguments, but only shows this when the arguments are all on one line. This is fine. But for multiline function calls, I find it much more readable to have spaces around =. It is inconsistent to require spaces around all operators (to spread things out) except in this one case where it would in fact make the code easier to read.

Copy link
Member Author

@ax3l ax3l Aug 20, 2024

Choose a reason for hiding this comment

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

You are right, I think it's a pragmatic choice in black. They do spaces for assignments and assignments in definitions (default values) but not in call site arguments. I kinda like it because I like my call sites compact, but as all style choices it might come down to taste and what one is used to :)

Docs/source/conf.py Outdated Show resolved Hide resolved
a_sq = a0**2 * np.exp(-2 * (xi - xi_0)**2 / (c**2 * tau**2))*np.sin(2*np.pi*(xi - xi_0)/lambda_laser)**2
a_sq = (
a0**2
* np.exp(-2 * (xi - xi_0) ** 2 / (c**2 * tau**2))
Copy link
Member

Choose a reason for hiding this comment

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

It's a little unfortunate that the standard is inconsistent with powers:

(xi - xi_0) ** 2

but

c**2

Is there a way to have this be consistent, i.e., use ()**2 instead of () ** 2?

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member

@roelof-groenewald roelof-groenewald left a comment

Choose a reason for hiding this comment

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

This is an awesome change to enforce consistency in Python code! 🎉

I left two comments on things I think would be nice to change / add (formatting of powers in arithmetic expressions and forcing f-strings). But I'll approve this PR so long and those can be changed in future PRs.
If you agree, @ax3l, it would be nice to undo the changes to the capacitive_discharge analysis files before merging.

@ax3l ax3l merged commit 3dda26f into ECP-WarpX:development Aug 21, 2024
46 of 47 checks passed
@ax3l ax3l deleted the ci-ruff branch August 21, 2024 00:19
@jwestern
Copy link
Contributor

jwestern commented Sep 19, 2024

I just want to raise awareness that style changes can have costs for some users, which might not be obvious. Particularly users working with custom extensions of WarpX. This commit created (a lot of) trivial auto-merge conflicts in our workflow, which took a while to track down, and I'm still looking for a workaround.

@ax3l
Copy link
Member Author

ax3l commented Sep 20, 2024

Hi @jwestern,

Thank you for raising this.

Fully understand. This style change is intended to be a one-time change, which is once a bit painful (e.g., forks and open PRs) but will actually make all follow-up work easier to maintain, review, pull and merge - if, you also use ruff in your PRs (automatic) and forks (please set up). Sorry for this one-time change, we try to keep those overall reformats to a minimum.

As a general recommendation for extending WarpX, we do move fast indeed. As people in other projects (e.g., the Cling people on LLVM), maintaining a true fork can be challenging contrary to extending a project.
This is why, we recommend to do the following if you like to extend WarpX:

  • plan which parts are generally needed/useful or enabling for your workflow and contribute them upstream
  • add additional callbacks/hooks and options you might need upstream here, and only
  • extend WarpX via our Python bindings (can still couple in more C++, e.g., via pybind11)
    • you can also use WarpX and extend it as a pure C++ library, in fact both our app executable and Python bindings link against exactly a libwarpx, but it is more productive to go via Python as glue code

That way, you would not have to maintain a full fork and only would have to update breaking API changes, which change less often.

Examples of a fork of WarpX that has same challenge as you: https://github.com/AMReX-Microelectronics/artemis

Examples of companies extending WarpX and contributing common/core functionality: Modern Electron (a few years back), TAE, Helion, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cleaning Clean code, improve readability component: Python Python layer component: tests Tests and CI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants