Search code examples
emacspython-modeelpy

Tabs and Spaces in emacs python-mode / elpy


This has been an exquisite exercise in frustration. For some reason, my python-mode / elpy buffers always have intent-tabs-mode nil. I'm using python-mode 6.2.3, elpy 1.32.0, and emacs 25.2.2. My .emacs file contains:

;; =============================================================================
;; Trying to get indentation to be tabs of width 4 spaces in Python is a real
;; pain. For some unknown reason, unlike EVERY other progmode file in the distro
;; python.el contains this gem:
;;
;;     5480: (set (make-local-variable 'tab-width) 8)
;;
;; which clobbers any defaults you might have, no questoins asked. It also has
;; this wonderful nugget:
;;
;;     5481: (set (make-local-variable 'indent-tabs-mode) nil)
;;
;; Because someone wants to force using spaces (so wby bother forcing
;; tab-width?)
;; -----------------------------------------------------------------------------
;; Do this first. Because of the above, if you don't python.el will clobber
;; your attempts to customize this stuff.
(load "python-mode")
;; Now follow the instructions sprinked all over the Interwebs by the thousands
;; of people frustrated by the above clobbering.
(add-hook 'python-mode-hook
      ;; Don't forget lambda(), God forbid
      (lambda ()
        ;; Now set the tab-width you really want here and cross your fingers
        (setq tab-width 4)
        ;; And tell python-mode to use tabs. I know lots of folks hate on
        ;; tabs, but whatever.
        (setq indent-tabs-mode t)
        ;; If you want to make sure you don't have trailing whitespaces
        ;; at the end of line, uncomment this.
        ;;(add-to-list 'write-file-functions 'delete-trailing-whitespace)
        )
      )

But indent-tabs-mode remains nil. Only in python-mode, of course. From describe-variable from within a python-mode / elpy buffer:

indent-tabs-mode is a variable defined in ‘C source code’.
Its value is nil
Original value was t
Local in buffer config; global value is t

  Automatically becomes buffer-local when set.
  This variable is safe as a file local variable if its value
  satisfies the predicate ‘booleanp’.

Documentation:
Indentation can insert tabs if this is non-nil.

You can customize this variable.

The version I'm using of python-mode.el contains the following [lines 955-964]:

  "Python-mode starts `indent-tabs-mode' with the value specified here, default is nil. "
  :type 'boolean
  :tag "py-indent-tabs-mode"
  :group 'python-mode)

[lines 5159-5165]

       ["indent-tabs-mode"
        (setq indent-tabs-mode
          (not indent-tabs-mode))
        :help "Indentation can insert tabs if this is non-nil.

Use `M-x customize-variable' to set it permanently"
        :style toggle :selected indent-tabs-mode]

[lines 6787-6820]

(defun py-toggle-indent-tabs-mode ()
  "Toggle `indent-tabs-mode'.

Returns value of `indent-tabs-mode' switched to. "
  (interactive)
  (when
      (setq indent-tabs-mode (not indent-tabs-mode))
    (setq tab-width py-indent-offset))
  (when (and py-verbose-p (called-interactively-p 'any)) (message "indent-tabs-mode %s  py-indent-offset %s" indent-tabs-mode py-indent-offset))
  indent-tabs-mode)

(defun py-indent-tabs-mode (arg &optional iact)
  "With positive ARG switch `indent-tabs-mode' on.

With negative ARG switch `indent-tabs-mode' off.
Returns value of `indent-tabs-mode' switched to. "
  (interactive "p")
  (if (< 0 arg)
      (progn
        (setq indent-tabs-mode t)
        (setq tab-width py-indent-offset))
    (setq indent-tabs-mode nil))
  (when (and py-verbose-p (or iact (called-interactively-p 'any))) (message "indent-tabs-mode %s   py-indent-offset %s" indent-tabs-mode py-indent-offset))
  indent-tabs-mode)

(defun py-indent-tabs-mode-on (arg)
  "Switch `indent-tabs-mode' on. "
  (interactive "p")
  (py-indent-tabs-mode (abs arg)(called-interactively-p 'any)))

(defun py-indent-tabs-mode-off (arg)
  "Switch `indent-tabs-mode' off. "
  (interactive "p")
  (py-indent-tabs-mode (- (abs arg))(called-interactively-p 'any)))

[lines 22259-22266]

;;  unconditional Hooks
;;  (orgstruct-mode 1)
(add-hook 'python-mode-hook
      (lambda ()
        (setq imenu-create-index-function py--imenu-create-index-function)
        (setq indent-tabs-mode py-indent-tabs-mode)))

(remove-hook 'python-mode-hook 'python-setup-brm)

And again (almost identical from above) [lines 25021-25033]:

          ["indent-tabs-mode"
           (setq indent-tabs-mode
             (not indent-tabs-mode))
           :help "Indentation can insert tabs if this is non-nil.

Use `M-x customize-variable' to set it permanently"
           :style toggle :selected indent-tabs-mode]

          ["Tab indent"
           (setq py-tab-indent
             (not py-tab-indent))
           :help "Non-nil means TAB in Python mode calls `py-indent-line'.Use `M-x customize-variable' to set it permanently"
           :style toggle :selected py-tab-indent]

I have set every single version of any definition I could find in any *.el file of indent-tabs-mode to t and nothing works.


Solution

  • Turns out, for some versions of python.el, you need to add (yay!) yet another item. This worked, finally:

    ;; =============================================================================
    ;; Trying to get indentation to be tabs of width 4 spaces in Python is a real
    ;; pain. For some unknown reason, unlike EVERY other progmode file in the distro
    ;; python.el contains this gem:
    ;;
    ;;     5480: (set (make-local-variable 'tab-width) 8)
    ;;
    ;; which clobbers any defaults you might have, no questoins asked. It also has
    ;; this wonderful nugget:
    ;;
    ;;     5481: (set (make-local-variable 'indent-tabs-mode) nil)
    ;;
    ;; Because someone wants to force using spaces (so wby bother forcing
    ;; tab-width?)
    ;; -----------------------------------------------------------------------------
    ;; Do this first. Because of the above, if you don't python.el will clobber
    ;; your attempts to customize this stuff.
    (load "python-mode")
    ;; Now follow the instructions sprinked all over the Interwebs by the thousands
    ;; of people frustrated by the above clobbering.
    (add-hook 'python-mode-hook
          ;; Don't forget lambda(), God forbid
          (lambda ()
            ;; Now set the tab-width you really want here and cross your fingers
            (setq tab-width 4)
            ;; And tell python-mode to use tabs. I know lots of folks hate on
            ;; tabs, but whatever.
            (setq indent-tabs-mode t)
            ;; Some verions of python.el use this, others don't
            (setq py-indent-tabs-mode t)
            ;; If you want to make sure you don't have trailing whitespaces
            ;; at the end of line, uncomment this.
            ;;(add-to-list 'write-file-functions 'delete-trailing-whitespace)
            )
          )