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

Should delimiters of different priorities be extra-indented? #147

Closed
yarinb opened this issue Apr 20, 2018 · 10 comments
Closed

Should delimiters of different priorities be extra-indented? #147

yarinb opened this issue Apr 20, 2018 · 10 comments
Labels
F: linebreak How should we split up lines? F: parentheses Too many parentheses, not enough parentheses, and so on. S: accepted The changes in this design / enhancement issue have been accepted and can be implemented T: style What do we want Blackened code to look like?

Comments

@yarinb
Copy link

yarinb commented Apr 20, 2018

Black takes an interesting decision regarding how to treat strings as dictionary values.

Following dict has a very long string:

 params = {
        "prompt": "consent",
        "response_type": "code",
        "access_type": "offline",
        "client_id": google_client_id,
        "scope": "profile email https://www.googleapis.com/auth/contacts.readonly https://www.googleapis.com/auth/drive.readonly",
        "redirect_uri": google_callback_uri,
        "state": state,
    }

As Black won't split the string, I manually edited it to this:

  params = {
        "prompt": "consent",
        "response_type": "code",
        "access_type": "offline",
        "client_id": google_client_id,
        "scope": "profile "
                  + "email https://www.googleapis.com/auth/contacts.readonly "
                  + "https://www.googleapis.com/auth/drive.readonly",
        "redirect_uri": google_callback_uri,
        "state": state,
    }

But Black than changes to this:

    params = {
        "prompt": "consent",
        "response_type": "code",
        "access_type": "offline",
        "client_id": google_client_id,
        "scope": "profile "
        + "email https://www.googleapis.com/auth/contacts.readonly "
        + "https://www.googleapis.com/auth/drive.readonly",
        "redirect_uri": google_callback_uri,
        "state": state,
    }

I think the indentation here is important for readability, as one might mistake the broken line as dict pairs.

======================================
Operating system: macOS
Python version: 3.6.4
Black version: 18.4a2
Does also happen on master: yes

@ambv
Copy link
Collaborator

ambv commented Apr 20, 2018

Workaround until I figure out what to do here: wrap the string in parentheses. Then you don't even have to explicitly concatenate it with +.

@ambv ambv added T: bug Something isn't working F: parentheses Too many parentheses, not enough parentheses, and so on. labels Jun 10, 2018
@ambv
Copy link
Collaborator

ambv commented Jun 10, 2018

We will put optional parentheses in this situation.

@ambv ambv changed the title Long strings in dictionary - bad indentation Should delimiters of different priorities be extra-indented? Jun 20, 2018
@ambv ambv added T: style What do we want Blackened code to look like? and removed T: bug Something isn't working F: parentheses Too many parentheses, not enough parentheses, and so on. labels Jun 20, 2018
@ambv
Copy link
Collaborator

ambv commented Jun 20, 2018

I thought about this some more and I'm not sure that parentheses are the right answer here. At the same time, Black currently doesn't have any rule that performs additional indentation based on delimiter priority.

@ambv ambv added the T: user support OP looking for assistance or answers to a question label Jun 20, 2018
@zooba
Copy link

zooba commented Aug 15, 2018

+1 for indenting as in #345

@peterjc
Copy link

peterjc commented Jan 28, 2020

Just using implicit string concatenation the example becomes:

params = {
    "prompt": "consent",
    "response_type": "code",
    "access_type": "offline",
    "client_id": google_client_id,
    "scope": "profile "
    "email https://www.googleapis.com/auth/contacts.readonly "
    "https://www.googleapis.com/auth/drive.readonly",
    "redirect_uri": google_callback_uri,
    "state": state,
}

I find that is undesirable. Adding more brackets does seem OK:

params = {
    "prompt": "consent",
    "response_type": "code",
    "access_type": "offline",
    "client_id": google_client_id,
    "scope": (
        "profile "
        "email https://www.googleapis.com/auth/contacts.readonly "
        "https://www.googleapis.com/auth/drive.readonly"
    ),
    "redirect_uri": google_callback_uri,
    "state": state,
}

It isn't explicit, but is the suggestion here (and in #345 which was marked as a duplicate) the following extra indentation?:

params = {
    "prompt": "consent",
    "response_type": "code",
    "access_type": "offline",
    "client_id": google_client_id,
    "scope":
        "profile "
        "email https://www.googleapis.com/auth/contacts.readonly "
        "https://www.googleapis.com/auth/drive.readonly",
    "redirect_uri": google_callback_uri,
    "state": state,
}

@ambv ambv removed T: user support OP looking for assistance or answers to a question T: style What do we want Blackened code to look like? labels Mar 3, 2020
@ambv
Copy link
Collaborator

ambv commented Mar 3, 2020

What we will do is this:

  • we won't be changing anything when there are existing delimiters;
  • in the special case of implicitly concatenated strings, we will be wrapping with extra parentheses;
  • in the special case of a long lambda (see Lambda wrapped without indent #332), we will be wrapping with extra parentheses.

@ambv ambv added the stable label Mar 3, 2020
@ambv ambv added this to To Do in Stable release via automation Mar 3, 2020
@SwampFalc
Copy link

I would like to point out an edge case: when the value in and of itself would fit on a single line, but key and value together go over.

The following code looks fine in my own eyes:

def dataset_configuration():
    return {
        "internal_mount": "/mnt_external",
        "external_mount": "/home/steria/mnt_external_prd",
        "dataexport_directory": "/home/steria/mnt_external_prd/batches/dataexport",
        "dataexport_log":
            "/home/steria/mnt_external_prd/batches/dataexport/logs/dataexport.log",
        "dataexport_script": 
            "/home/steria/mnt_external_prd/batches/dataexport/bin/run.sh",
    }

No line over 88 characters, clear distinction between keys and values.

But black reformats this to this, which is longer than 88 characters:

def dataset_configuration():
    return {
        "internal_mount": "/mnt_external",
        "external_mount": "/home/steria/mnt_external_prd",
        "dataexport_directory": "/home/steria/mnt_external_prd/batches/dataexport",
        "dataexport_log": "/home/steria/mnt_external_prd/batches/dataexport/logs/dataexport.log",
        "dataexport_script": "/home/steria/mnt_external_prd/batches/dataexport/bin/run.sh",
    }

Because apparently putting keys and values on the same line is a lot more important than sticking to the line length...

But if we're adding parentheses anyway in related cases, why not prefer consistence with this new rule and have this as output instead?

def dataset_configuration():
    return {
        "internal_mount": "/mnt_external",
        "external_mount": "/home/steria/mnt_external_prd",
        "dataexport_directory": "/home/steria/mnt_external_prd/batches/dataexport",
        "dataexport_log": (
            "/home/steria/mnt_external_prd/batches/dataexport/logs/dataexport.log"
        ),
        "dataexport_script": (
            "/home/steria/mnt_external_prd/batches/dataexport/bin/run.sh"
        ),
    }

I mean... I would personally still prefer the original and I don't quite understand why black insists on making a change to begin with, but at least this change stays within the line length.

@ichard26 ichard26 added F: linebreak How should we split up lines? F: parentheses Too many parentheses, not enough parentheses, and so on. hacktoberfest-accepted S: accepted The changes in this design / enhancement issue have been accepted and can be implemented T: style What do we want Blackened code to look like? and removed stable labels Jan 1, 2022
@ichard26 ichard26 removed this from To Do in Stable release Jan 1, 2022
@ichard26 ichard26 added this to To Do (nice to have) in Stable release via automation Jan 1, 2022
@tolomea
Copy link

tolomea commented Sep 14, 2022

I'm going to put in a vote for different types of splitting get different indents
or put another way a multiline statement in a list like should have an extra level of indenting
I see the split string version got fixed, but I still hit stuff like this

        return queryset.filter(
            Q(lifecycle_status_sent_at=None) | Q(lifecycle_status_sent_at__lt=self.now - Consumer.STATUS_INTERVAL),
            lifecycle_updated_at__gt=self.now - Consumer.ALERT_HOLD_OFF,
        )

which currently gets reformatted to

        return queryset.filter(
            Q(lifecycle_status_sent_at=None)
            | Q(lifecycle_status_sent_at__lt=self.now - Consumer.STATUS_INTERVAL),
            lifecycle_updated_at__gt=self.now - Consumer.ALERT_HOLD_OFF,
        )

I would prefer

        return queryset.filter(
            (
                Q(lifecycle_status_sent_at=None)
                | Q(lifecycle_status_sent_at__lt=self.now - Consumer.STATUS_INTERVAL)
            ),
            lifecycle_updated_at__gt=self.now - Consumer.ALERT_HOLD_OFF,
        )

which black (22.8.0 with preview) will persist if it's manually done, but not do automatically

@tolomea
Copy link

tolomea commented Sep 14, 2022

there's a similar issue with inline conditionals where

        data = {
            "topic_type": self.fields["topic_type"].initial,
            "target_type": self.fields["target_type"].initial,
            "override_context": self.fields["override_context"].initial if "override_context" in self.fields else None,
        }

gets reformatted as

        data = {
            "topic_type": self.fields["topic_type"].initial,
            "target_type": self.fields["target_type"].initial,
            "override_context": self.fields["override_context"].initial
            if "override_context" in self.fields
            else None,
        }

@JelleZijlstra
Copy link
Collaborator

This was implemented in #3440. On current main:

% python -m black --preview -c '''params = {
        "prompt": "consent",
        "response_type": "code",
        "access_type": "offline",
        "client_id": google_client_id,
        "scope": "profile email https://www.googleapis.com/auth/contacts.readonly https://www.googleapis.com/auth/drive.readonly",
        "redirect_uri": google_callback_uri,
        "state": state,
    }
data = {
            "topic_type": self.fields["topic_type"].initial,
            "target_type": self.fields["target_type"].initial,
            "override_context": self.fields["override_context"].initial if "override_context" in self.fields else None,
        }
'''
params = {
    "prompt": "consent",
    "response_type": "code",
    "access_type": "offline",
    "client_id": google_client_id,
    "scope": (
        "profile email https://www.googleapis.com/auth/contacts.readonly"
        " https://www.googleapis.com/auth/drive.readonly"
    ),
    "redirect_uri": google_callback_uri,
    "state": state,
}
data = {
    "topic_type": self.fields["topic_type"].initial,
    "target_type": self.fields["target_type"].initial,
    "override_context": (
        self.fields["override_context"].initial
        if "override_context" in self.fields
        else None
    ),
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
F: linebreak How should we split up lines? F: parentheses Too many parentheses, not enough parentheses, and so on. S: accepted The changes in this design / enhancement issue have been accepted and can be implemented T: style What do we want Blackened code to look like?
Projects
No open projects
Stable release
  
To Do (nice to have)
Development

No branches or pull requests

8 participants