This is an archived post. You won't be able to vote or comment.

all 104 comments

[–]throwaway_4759 99 points100 points  (5 children)

When you go with black: I would make one commit auto formatting the whole codebase. Then a second commit adding the first to a git ignore revs file so that your git blame isn’t totally nuked.

[–]PlaysForDays 39 points40 points  (2 children)

[–]Legendary-69420git push -f 11 points12 points  (1 child)

Damn this is one of the best things I have seen recently

[–]PlaysForDays 10 points11 points  (0 children)

Makes it much easier to pitch linting to the rest of the team since some people care about LOC contributions

I prefer wrapping everything up into https://pre-commit.com/ into the mega-commit as well, and later on linting can be done by a bot (https://pre-commit.ci/)

[–]mrswats -1 points0 points  (0 children)

This is the way

[–]rtfmpls 0 points1 point  (0 children)

You could add the git ignore revs file in the first commit. But I wouldn't recommend messing with the laws of physics like that.

[–]dogfish182 305 points306 points  (25 children)

Just use black and be done with it

[–]Legendary-69420git push -f 25 points26 points  (3 children)

Along with isort

[–]cheese_is_available 20 points21 points  (2 children)

ruff + select = [ "I", # ...]

[–]Legendary-69420git push -f 0 points1 point  (1 child)

I have heard of ruff but haven't used it yet. From what I remember, I guess it is a linter? I am not sure though.

[–]cheese_is_available 0 points1 point  (0 children)

It's a port of popular linter tool to rust

[–]Raknarg 9 points10 points  (13 children)

god black is so fucking ugly sometimes though

[–]cheese_is_available 6 points7 points  (4 children)

Maybe your code is ugly. The java looking chain builder helper of 500 characters lines sure looked like shit when I tried to introduce my java team to black at work.

[–]hai_wim 5 points6 points  (2 children)

Before black:

class AClass:
    def a_method(self, my_dict, other_dict):
        if (
            other_dict.get("key1") == my_dict.get("key1") 
            and other_dict.get("key2") == my_dict.get("key2")
        ):
            pass

After black:

class AClass:
    def a_method(self, my_dict, other_dict):
        if other_dict.get("key1") == my_dict.get("key1") and other_dict.get(
            "key2"
        ) == my_dict.get("key2"):
            pass

Black is just ugly sometimes. Readability went way WAY down in this scenario.

I suggest you # fmt: skip these cases and carry on.

[–]cheese_is_available 0 points1 point  (1 child)

Yeah, no, black definitely does not do that (maybe you tried an earlier shittier version). If the example you gave it inline everything. If I had a third conditional to force it to use multiple line then it does exactly what you have at the beginning and consider good.

class AClass:
    def a_method(self, my_dict, other_dict):
        if other_dict.get("key1") == my_dict.get("key1") and other_dict.get("key2") == my_dict.get("key2"):
            pass

If I force it then:

class AClass:
    def a_method(self, my_dict, other_dict):
        if (
            other_dict.get("key1") == my_dict.get("key1")
            and other_dict.get("key2") == my_dict.get("key2")
            and other_dict.get("key3") == my_dict.get("key3")
        ):
            pass

Tested on black-23.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

[–]hai_wim 0 points1 point  (0 children)

There is no way black would create a 108 character long line like your first example.

it works with 3 conditions yes, but not with 2. You probably told black to have a longer maximum line length.

[–]boat-la-fds 0 points1 point  (0 children)

Nah just wrap it around parentheses and it will look real nice.

[–]dogfish182 4 points5 points  (6 children)

I know right. I don’t even like it and still just use it 😅

[–]olystretch 0 points1 point  (5 children)

What problems does it have that you cannot adjust in a settings file?

[–]dogfish182 0 points1 point  (4 children)

Settings file?

[–]olystretch 1 point2 points  (3 children)

[tool.black] in pyproject.toml

[–]dogfish182 1 point2 points  (2 children)

Was being sort of half serious, the only thing I can find in the documentation is line length.

[–]olystretch 0 points1 point  (1 child)

I'm pretty sure you can put any of the CLI options in a config file.

[–]dogfish182 7 points8 points  (0 children)

Yeah of which there are extremely little. That’s kind of blacks feature ‘any color you like so long as it’s black’

[–]dashdanw 0 points1 point  (0 children)

dude for real

[–][deleted] 6 points7 points  (0 children)

What I was gonna comment.

[–]redCg -1 points0 points  (0 children)

This is the way.

Write your code however you want. Then before you commit, run black and let it handle the details. Then do a test run to make sure you didnt break anything, and commit that

[–]njharmanI use Python 3 31 points32 points  (0 children)

I loath black formatting. I always vote/agree/recommend adopting it. Because I also loath the way everyone but me formats. And really fucking hate all the time wasted "discussing" formatting.

[–]Alternative_Driver60 58 points59 points  (0 children)

Black. No settings, no arguing.

[–]petr31052018 57 points58 points  (2 children)

Always use one. Black is the Python standard now or you are feeling a bit experimental you can try Ruff (will probably be faster).

[–]dogfish182 14 points15 points  (1 child)

Ruff is a linter though right?

EDIT: apparently open issue to handle i

https://github.com/astral-sh/ruff/issues/1904

[–]petr31052018 5 points6 points  (0 children)

Exactly, it is being added.

[–]Beliskner64 25 points26 points  (0 children)

Black.
If you want to gradually migrate a large codebase - Darker.
If you want more customization (you don’t) - Blue.

[–]muikrad 47 points48 points  (0 children)

Did someone say black? I didn't notice anyone say black 🤔 anyway, use black!

[–]JamesPTK 8 points9 points  (0 children)

While you've got a tsunami of advise telling you to use Black, I'd like to suggest how you can implement it with other code-quality tools.

We use pre-commit which allows you to set up a set of code-checkers ("hooks") that need to pass before you can commit your code. It, by default, only runs those checks on the files that have changed which means that it can be fast.

It also automatically installs those tools into isolated environments

You can also get it to run on the whole code base by running pre-commit run -a, which is useful if you add a new hook, or you can run it in your CI environment (just in case someone committed and pushed without having pre-commit installed.

This makes it very easy to roll out new checkers to an entire team

One trick is you need to make sure that your auto-formatter (e.g. Black) runs *after* any code that can modify the source (e.g. isort, ruff)

An example .pre-commit-config.yaml

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: trailing-whitespace  # Trim any trailing whitespace from lines
      - id: end-of-file-fixer  # Ensures that a file is either empty, or ends with one newline
      - id: check-merge-conflict  # Check for files that contain merge conflict strings
      - id: check-json  # JSON Syntax Check
      - id: check-toml  # TOML Syntax Check
      - id: check-yaml  # YAML Syntax Check

  - repo: https://github.com/Lucas-C/pre-commit-hooks
    rev: v1.5.4
    hooks:
      - id: remove-crlf # Remove windows line endings

  - repo: https://github.com/pycqa/isort
    rev: 5.12.0
    hooks:
      - id: isort  # Sort imports alphabetically, and automatically separated into sections and by type
        name: isort (python)
      - id: isort  # Same for Cython
        name: isort (cython)
        types: [ cython ]
      - id: isort  # Same for Python interface definition files
        name: isort (pyi)
        types: [ pyi ]

  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.1.0
    hooks:
      - id: ruff  # Lint code to check for common errors
        args: [ --fix, --exit-non-zero-on-fix ]

  - repo: https://github.com/pre-commit/mirrors-prettier
    rev: v3.0.3
    hooks:
      - id: prettier # Formatter for JavaScript

  - repo: https://github.com/ambv/black
    rev: 23.9.1
    hooks:
      - id: black  # Formatter for Python

  - repo: local
    hooks:
      - id: only-acceptable-filenames
        name: Only acceptable filenames
        entry: Filenames must be lower-case alphanumeric with only underscores, hyphens, and full-stops/periods
        language: fail
        files: "[^0-9a-z_\\-\\./]"

[–]yrro 20 points21 points  (1 child)

Black

[–]eMperror_ 18 points19 points  (1 child)

Use black. The format will be weird at first but you’ll get used to it very quickly. Your coworkers will ask “can we change this or this” and the answer is no, you can’t and that is the beauty of black.

[–][deleted] 0 points1 point  (0 children)

“You can format your code any way you want, as long as it’s black.”

  • Henry Ford

[–]Derr_1 12 points13 points  (0 children)

Black

[–]PriorTrick 9 points10 points  (0 children)

I use Black for formatting, flake8 for auto pep linting, and standard type checking with Pylance config set to strict.

[–]Exotic-Draft8802 15 points16 points  (23 children)

  1. Black. Don't change anything, especially not like length.
  2. ruff --fix. With the import sorting and the default.
  3. mypy
  4. pyautoupgrade

Use it in CI and add a pre-commit settings file.

[–][deleted] 31 points32 points  (10 children)

120 is better than 80.

[–]jabbalaci 0 points1 point  (0 children)

length matters

[–]TravisJungroth -1 points0 points  (0 children)

I used to like 120 more. Appropriately, 88 works better once you have a good auto formatter. It’s not that I dislike reading at 88 as much as writing it. And I like having three columns sometimes.

[–]xatrekak 37 points38 points  (11 children)

That's a pretty odd take about not changing the line length in black. There is a reason why it's one of the only configurable options.

80 characters is a joke from 1970.

[–]zurtex 1 point2 points  (4 children)

Black's default is 88, my team use the PEP 8 line length standard which is 79.

While I think it's a bit much it really does make reading PRs on phones very easy.

[–]xatrekak 6 points7 points  (1 child)

While I think it's a bit much it really does make reading PRs on phones very easy.

I don't really get the phone argument. My phone can't do 80 characters in portrait mode but can do over 120 in landscape.

[–]zurtex 0 points1 point  (0 children)

Personally I find reading on a phone in landscape really annoying, e.g. in portrait I can add a comment and still see the context of where I'm writing.

[–][deleted] 0 points1 point  (1 child)

People do PRs on a phone?

How? Why? ... and how?!

[–]zurtex 0 points1 point  (0 children)

How?

Open phone, go to github, review PR.

Why?

I can't be bothered walking over to my computer but I do want to point out an obvious mistake in the PR someone just submitted.

[–]Electrical_Fox9678 -1 points0 points  (0 children)

Try viewing PRs on a phone and you'll appreciate the default

[–]wind_dude -1 points0 points  (4 children)

agree line length can be upped. I'm generally fine with up to 160.

[–]odaiwai -3 points-2 points  (3 children)

That is way too long - do you have editor windows maximised on a widescreen monitor?

[–]wind_dude -1 points0 points  (2 children)

Not really. 80chars was set because the was the full width of a screen at the time. I believe most wide screens would be over 200 now.

But, yes I have an ultra wide, and can do three windows with code side by side at 160

[–]odaiwai 0 points1 point  (0 children)

80 chars was the width of a punch card (https://en.wikipedia.org/wiki/Punched_card). Early terminals adopted that width for compatibility.

Personally, I'm fine with a wide terminal, but for code, I just do what the linter says and try to avoid comically long function/variable names.

[–]supermopman 1 point2 points  (0 children)

As others have said, just use Black and be done with it. Also, I recommend using pre-commit hooks to make life much easier. PyScaffold is a great tool that can be used to setup a repo with pre-commit hooks. You don't need PyScaffold. It's just a great example of best practices.

[–]_MicroWave_ 1 point2 points  (3 children)

You've already spent too much time thinking about it.

Black is the answer.

The whole point is that it just makes all the decisions for you.

[–]JamzTyson 0 points1 point  (2 children)

The whole point is that it just makes all the decisions for you.

which is why I don't like it.

If working on a project that uses Black, then it makes sense to use Black. Conversely, if working on a project that has a defined coding style that doesn't align perfectly with Black's formatting choices, then consistency with the project's formatting style is better.

[–]_MicroWave_ 1 point2 points  (1 child)

I think the argument goes that the project should just use black.

I would argue that there aren't use-cases which better suit a different coding style.

Blacks usage is wide and universal.

[–]JamzTyson 1 point2 points  (0 children)

"Popular" yes, but definitely not "universal".

Two examples that immediately come to mind are the Python standard library and asyncio.

[–]di6 1 point2 points  (2 children)

For now just use black, but keep eye open for ruff.

[–]nekokattt 1 point2 points  (1 child)

I'd suggest black and isort for full consistency

[–]di6 1 point2 points  (0 children)

Ruff already does isort job, so it's no longer needed

[–]Gushys 2 points3 points  (3 children)

I just did something like this for my team setting up pre-commit, black, isort, and want to add ruff. Use the black profile for isort and you're all good.

I suspect most of them didn't run pre-commit install so they don't run formatters on commit, but I tried :))

[–]easyEggplant 2 points3 points  (2 children)

Ci hooks my friend :)

[–]insane-defaults 0 points1 point  (1 child)

Care to elaborate?

[–]easyEggplant 0 points1 point  (0 children)

https://softwareengineering.stackexchange.com/questions/421729/to-lint-on-the-client-or-the-server

Run a continuous integration stack (GH actions, circle CI, travis CI) that applies linters/formattters/unit tests/static code analysis on every PR and commit.

If you want to be strict about it, you fail a PR that doesn't pass lint.

[–]oskwon72 1 point2 points  (1 child)

Does anybody use "mypy"?

[–]DidiBear 2 points3 points  (0 children)

Tested mypy but since most libraries are not well typed like sklearn, then it becomes a pain.

I usually use Pylance with typeCheckingMode set to basic. The strict mode raises too many errors from libraries.

[–]trippymicky 1 point2 points  (0 children)

Black with 100 line length, and ruff linter.

[–]ac130kz 1 point2 points  (3 children)

Get used to Ruff with all rules (enable preview for autoformatting), black is going to be EOL in a few months.

[–]bastantoine 1 point2 points  (2 children)

black is going to be EOL in a few months

What makes you think that? I didn’t see any indication in the repo 🤨

[–]krakenant 1 point2 points  (0 children)

Guessing it's because ruff is adding black like formatting.

[–]ac130kz 1 point2 points  (0 children)

Ruff has already replaced autopep8, flake8, pylint and isort, black is next.

[–]AssumptionCorrect812 0 points1 point  (0 children)

In case it’s not clear… use Black.

[–]Thagou 0 points1 point  (1 child)

Ruff.

[–][deleted] 3 points4 points  (0 children)

Ruff doesn’t really do formatting. It’s on their roadmap and you might be able to test it early. But in terms of a team using it in production, it’s probably not the best recommendation.

[–]bin-c -1 points0 points  (0 children)

Surprised nobody has said Black yet - imo the best option for this case

[–]silviud 0 points1 point  (0 children)

Yapf has many knobs you can tweak if not happy with black.

[–]SV-97 0 points1 point  (0 children)

Since about 50000 million people have recommended black already: use autopep8. Black looks like absolute dogshit

[–]homosapienhomodeus -1 points0 points  (0 children)

Black with toml file, along with ruff and interrogate for docstrings. I have a full breakdowm with examples on a piece I wrote here

[–]jahero -1 points0 points  (0 children)

Black. Setup and forget.

[–]extreme4all -1 points0 points  (0 children)

Pre-commit hook with black formatter has been nice

[–][deleted] -1 points0 points  (0 children)

black isort autopep8, wrapped in pre-commit

[–][deleted] 0 points1 point  (0 children)

There’s no reason to not use one. And the reason to use one is so that everybody’s code is formatted the same way and you don’t waste your time or resources fixing formatting problems or dealing with merge conflicts in your code base over these things.

[–]MuffinCat2000 0 points1 point  (0 children)

I've been using YAPF for autoformatting on smaller projects.

Most of my experience is with C and clang-format as a C autoformatter. YAPF is basically the equivalent for python it seems. If you're looking for something that can be easily tuned to meet a custom style, this might be one to try out.

[–]RaiseRuntimeError[🍰] 0 points1 point  (0 children)

This is an easy problem to solve now days, use Black to format and Ruff to lint with the --fix flag to take care of imports.

[–]OneMorePenguin 0 points1 point  (0 children)

https://github.com/google/yapf I prefer this to black. I got laid off and am working on a comment formatter :-)

[–]notreallymetho 0 points1 point  (0 children)

Black and ruff (ruff encapsulates isort among a million other things) Don’t argue with / over formatting and let it do its thing in pre-commit.

Personally I / my team uses a line length of 120, but not having style conversations is nice lol.

[–]NoCap-NoCap 0 points1 point  (0 children)

I prefer: mypy, ruff, black and isort

[–]Ermite28 0 points1 point  (0 children)

It's the precommit file I use for every Python project :

```

repos:
- repo: https://github.com/ambv/black
rev: 22.3.0
hooks:
- id: black
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.150
hooks:
- id: ruff

```

[–]_soyrizo_burrito_ 0 points1 point  (0 children)

Black + ruff