2

Given the following somewhat silly Python code with a multi-line condition:

if ("VALUE" in my_uncessarily_long_dictionary and
    some_other_long_condition_case):
    do_something()
else:
    pass

This is written as Emacs typically indents each line, which I'm normally fine with. However, quite a few linters prefer the condition to be indented visually different from the next (non-conditional) line, e.g., something like this:

if ("VALUE" in my_uncessarily_long_dictionary and
        some_other_long_condition_case):
    do_something():
else:
    pass

Is there some way to configure python-mode to indent like this?

2 Answers 2

2
+50

The following lines within python-indent--calculate-indentation determine the indent after a paren not following a newline:

(defun python-indent--calculate-indentation ()
  ...
  (pcase (python-indent-context)
    ...
    (`(,(or :inside-paren
            :after-backslash-block-continuation
            :after-backslash-dotted-continuation) . ,start)
     ;; Use the column given by the context.
     (goto-char start)
     (current-column))
    ...
    )
  )

The result of the form (current-column) is the calculated indentation. It is hard-coded and changing the indentation after a paren requires changing the code.
You could do that in the following way:

(defcustom phyton-indent-add-after-paren python-indent-offset
  "Additional indent after paren."
  :type 'number
  :group 'python)

(defun python-indent--calculate-indentation ()
  ...
  (pcase (python-indent-context)
    ...
    (`(:inside-paren . ,start)
     (goto-char start)
     (+ (current-column) phyton-indent-add-after-paren))
    (`(,(or :after-backslash-block-continuation
            :after-backslash-dotted-continuation) . ,start)
     ;; Use the column given by the context.
     (goto-char start)
     (current-column))
    ...
    )
  )

Alternatively, you could use the following advice in your init file:

(defcustom phyton-indent-add-after-paren python-indent-offset
  "Additional indent after paren without following newline."
  :type 'number
  :group 'python)

(defun python-indent-calc-indent-ad (fun)
  "Change indent after opening paren with `phyton-indent-add-after-paren'.
This is an around advice for `python-indent--calculate-indentation'."
  (save-excursion
    (let* ((info (python-indent-context))
       (ctx (car info))
       (start (cdr info)))
      (if (eq ctx :inside-paren)
      (progn
        (goto-char start)
        (+ (current-column) python-indent-offset))
    (funcall fun)))))

(advice-add 'python-indent--calculate-indentation :around #'python-indent-calc-indent-ad)

This is less efficient than the modification of python-indent--calculate-indentation since the heavy duty function python-indent-context is called twice. But if you do not notice the slow down, it should be okay.


Note, this is valid for Emacs 28.1. It may be that other Emacs versions require other changes.

You could issue a feature request at debbugs. See an SE-answer about feature requests.

4
  • 1
    I applied this (almost) as is to Emacs (master) and it seems to work fine. I did notice an inconsistency that anything within parentheses can be "extra" indented, but that should be solvable with a quick backward search for if or elif
    – Xaldew
    Commented Apr 4, 2023 at 14:21
  • 1
    I'll package this up as a patch for Emacs when I have a bit more time
    – Xaldew
    Commented Apr 4, 2023 at 14:22
  • This has now been reported/packaged in the following bug-ticket: debbugs.gnu.org/cgi/bugreport.cgi?bug=62696, feel free to follow along
    – Xaldew
    Commented Apr 6, 2023 at 12:51
  • @Xaldew Thanks for submitting the patch. (Even if I don't use Python.)
    – Tobias
    Commented Apr 6, 2023 at 17:19
0

This indentation behaviour can now be configured as of commit 711e8bc7178d5dd78f4db5c34b2b23f605521fc4, (or Emacs-29 and above I think).

To use it, simply set the python-indent-block-paren-deeper option to t if you want to enable this indentation behaviour, e.g.:

(setq python-indent-block-paren-deeper t)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.