Could we add extra logic that won't always join lines if they fit into max length?
I believe that it's very common for people to pretty format such data structures with each entry on new line.
For example such code formatting in django docs:

$ cat a.py
YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
)
$ cat a.py| black -
YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'), ('SO', 'Sophomore'), ('JR', 'Junior'), ('SR', 'Senior')
)
$ cat a.py
CONST = {
'a': 'a',
'b': 'b',
}
$ cat a.py| black -
CONST = {'a': 'a', 'b': 'b'}
Something like what prettier do for js objects would be ideal:
$ cat a.js
CONST = {
a: 'a',
b: 'b',
}
$ prettier a.js
CONST = {
a: 'a',
b: 'b',
};
$ cat a.js
CONST = { a: 'a', b: 'b' }
$ prettier a.js
CONST = { a: 'a', b: 'b' };
We have logic like this, it's called # fmt: off. Otherwise, I want Black to be obvious in how it formats everything. In other formatters you sometimes ask yourself: "cool, but why did it choose this formatting?" or learn tricks like "put a trailing comma and then it will do something else". I don't want any of that.
In general, I think that if something fits on one line, it belongs on one line. Splitting it further is wasting vertical whitespace. But most importantly, that requires looking at previous formatting which Black famously does not do (apart from empty lines and recognizing where comments should go).
I'll talk to @vjeux about this, maybe prettier has a trick up its sleeve here.
@ambv sure fmt: off would do the job but after working some time with advanced formatter like prettier I can say that I never use // prettier-ignore. I think the reason for that is because they iterated a lot on defaults collecting feedback from the community as a result they got to the point there almost everyone can leave with it as default formatter. Would be good to have similar thing for the python.
Hey, I'm working on prettier and I implemented the logic you mentioned for objects. The way I think about writing a pretty printer is 1) to try and understand the meaning of a piece of code based on the AST and 2) print each different meanings in a different way.
The problem with JavaScript is that pre-ES6, Objects were used in so many contexts (data, named arguments, classes, switch statement...) that just by looking at the AST it was impossible to guess the meaning of the code. The least shitty workaround I found to address this situation is if the opening and closing bracket are not on the same line in the original file, then break.
On the surface, it sounds like a good idea because the diff is smaller. However, there is one big downside: if sometimes in the past you added a \n inside, it's ALWAYS going to be expanded, even if it doesn't need to be anymore. If you don't want it to be expanded anymore, then it's very annoying to undo as you need to remove all the spaces, the total opposite of what prettier is supposed to do: make you stop worrying about formatting.
People have asked to get the same behavior for arrays but I always refused because arrays don't have the same ambiguity issues. ES6 added a bunch of syntax for all the use cases where objects were used so for those codebases we could remove this behavior, but not enough code is using it yet that we'll probably going to support it for a long time.
I would strongly recommend against having the same behavior if you don't absolutely have to.
How about a # fmt: expand annotation after a closing bracket to force an expansion?
I'd be in support of something to denote expansion. I came here looking for something similar; I'm starting to use black on a very large existing Django project, and I'm noticing lots of blocks like this one:
patterns = [
url(
"request_access/(?P<gdd_account_id>\d+)/$",
views.request_gdd_signup,
name="request_gdd_signup",
),
url(
"resend_request/(?P<request_id>\d+)/$", views.resend_gdd_request, name="resend_gdd_request"
),
url(
"request_access/success$",
views.gdd_signup_request_success,
name="gdd_signup_request_success",
),
]
The second one absolutely fits on one line, but the document itself is more difficult to visually parse because of it, particularly in files hundreds of lines long (yeah, yeah, I know). I think chopping down is sometimes a reasonable request to make.
If you put a comment on a line by itself inside the expression you want to explode, Black will do the correct thing:
patterns = [
url(
"request_access/(?P<gdd_account_id>\d+)/$",
views.request_gdd_signup,
name="request_gdd_signup",
),
url(
"resend_request/(?P<request_id>\d+)/$",
views.resend_gdd_request,
name="resend_gdd_request",
# the content of the comment doesn't matter so you might as well make it useful for the reader
),
url(
"request_access/success$",
views.gdd_signup_request_success,
name="gdd_signup_request_success",
),
]
In other words, you can put # fmt: expand on its own line inside your expression and Black will have no choice but to split it. But instead of a magic # fmt: expand comment, I'd suggest putting a comment that explains something worthwhile.
Seems like a reasonable short-term solution and I can work with that for now.
However, most of these are just URL definitions and aren't going to have any valid comment, so I'm adding them for the express purpose of switching from wrapping to chopping. It would still be nice (and potentially far less work on the user's end) to have a single flag that I could put on the containing list to get the same behavior.
I'm opposed to things like # fmt: expand because they don't communicate with enough granularity what the user means:
As you can see, there's a lot of decisions here that will work in one scenario but won't in another. So, I am offering two solutions:
# fmt: off and # fmt: on.Hopefully users unhappy with that latter option won't choose to abandon Black at all simply because it won't format 100% of their code. That would mean giving up 99% of auto-formatting.
Last thought, when you find yourself unhappy with how readable a line is to you when it's using its entire width, you probably don't want that line width to begin with.
I don't see anything actionable here. Let me know if anything new surfaces and we'll reopen.
Most helpful comment
Hey, I'm working on prettier and I implemented the logic you mentioned for objects. The way I think about writing a pretty printer is 1) to try and understand the meaning of a piece of code based on the AST and 2) print each different meanings in a different way.
The problem with JavaScript is that pre-ES6, Objects were used in so many contexts (data, named arguments, classes, switch statement...) that just by looking at the AST it was impossible to guess the meaning of the code. The least shitty workaround I found to address this situation is if the opening and closing bracket are not on the same line in the original file, then break.
On the surface, it sounds like a good idea because the diff is smaller. However, there is one big downside: if sometimes in the past you added a \n inside, it's ALWAYS going to be expanded, even if it doesn't need to be anymore. If you don't want it to be expanded anymore, then it's very annoying to undo as you need to remove all the spaces, the total opposite of what prettier is supposed to do: make you stop worrying about formatting.
People have asked to get the same behavior for arrays but I always refused because arrays don't have the same ambiguity issues. ES6 added a bunch of syntax for all the use cases where objects were used so for those codebases we could remove this behavior, but not enough code is using it yet that we'll probably going to support it for a long time.
I would strongly recommend against having the same behavior if you don't absolutely have to.