aboutsummaryrefslogtreecommitdiff
path: root/modules/prog-python.el
blob: d8556c4d7064fee4023c45c6ac255d83407ae064 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
;;; prog-python --- Python Specific Setup and Functionality -*- lexical-binding: t; coding: utf-8; -*-
;; author Craig Jennings <c@cjennings.net>

;;; Commentary:
;;
;; Layer: 3 (Domain Workflow).
;; Category: D/P.
;; Load shape: eager.
;; Eager reason: none necessary; currently eager but should load by Python major
;;   mode (Phase 6 deferral candidate).
;; Top-level side effects: package configuration via use-package; warns at load
;;   if pyright is missing.
;; Runtime requires: system-lib.
;; Direct test load: yes.
;;
;; Python programming environment with LSP, tree-sitter, and formatting.
;;
;; Installation:
;;   pip install pyright mypy
;;
;; LSP will provide:
;;   - Intelligent code completion
;;   - Jump to definition (M-.)
;;   - Find references
;;   - On-the-fly type checking
;;   - Documentation on hover
;;
;; For Django projects, also install:
;;   pip install django-stubs

;;; Code:

(defvar python-ts-mode-map)

(require 'system-lib)  ; for cj/executable-find-or-warn

;; Forward declarations for LSP
(declare-function lsp-deferred "lsp-mode")

;; Forward declarations for external packages
(declare-function company-mode "company")
(declare-function cj/disabled "system-defaults")
(declare-function pdb "gud")
(defvar poetry-tracking-strategy)

(defvar pyright-path "pyright"
  "Path to pyright language server.
Install with: pip install pyright")

(defvar mypy-path "mypy"
  "Path to mypy static type checker.
Install with: pip install mypy")

;; Warn at load time if pyright is missing rather than waiting for the
;; first LSP attach to fail with a confusing connection error.
(cj/executable-find-or-warn pyright-path "pyright LSP" 'prog-python)

;; -------------------------------- Python Setup -------------------------------
;; preferences for Python programming

(defun cj/python-setup ()
  "My default code preferences for Python coding."
  (company-mode)                      ;; completion framework
  (flyspell-prog-mode)                ;; spell check comments
  (superword-mode)                    ;; see-this-as-one-word
  (setq-local fill-column 80)         ;; wrap at 80 columns
  (setq-local tab-width 4)            ;; set the tab width to 4 spaces
  (setq-local standard-indent 4)      ;; indent 4 spaces
  (setq-local indent-tabs-mode nil)   ;; disable tab characters
  (electric-pair-local-mode t)        ;; match delimiters automatically (buffer-local)

  ;; Enable LSP if available
  (when (and (fboundp 'lsp-deferred)
             (executable-find pyright-path))
    (lsp-deferred)))

(defun cj/--python-mypy-command (target)
  "Return the shell command that runs mypy against TARGET."
  (format "%s %s" mypy-path (shell-quote-argument target)))

(defun cj/--python-debug-command (file)
  "Return the shell command that runs pdb against FILE."
  (format "python3 -m pdb %s" (shell-quote-argument file)))

(defun cj/python-mypy ()
  "Run mypy static type checker on the current Python file or directory."
  (interactive)
  (if (executable-find mypy-path)
      (compile (cj/--python-mypy-command (or (buffer-file-name) default-directory)))
    (message "mypy not found. Install with: pip install mypy")))

(defun cj/python-debug ()
  "Start Python debugger (pdb) on the current file."
  (interactive)
  (if buffer-file-name
      (pdb (cj/--python-debug-command buffer-file-name))
    (message "No file associated with this buffer")))

(defun cj/python-mode-keybindings ()
  "Set up keybindings for Python programming.
Overrides default prog-mode keybindings with Python-specific commands."
  ;; S-f5: Run mypy (static type checking)
  (local-set-key (kbd "S-<f5>") #'cj/python-mypy)

  ;; S-f6: Debug with pdb
  (local-set-key (kbd "S-<f6>") #'cj/python-debug))

;; ----------------------------------- Python ----------------------------------
;; configuration for python-ts-mode (treesit-based Python editing)

(use-package python
  :ensure nil ;; built-in
  :hook
  ((python-ts-mode . cj/python-setup)
   (python-ts-mode . cj/python-mode-keybindings))
  :custom
  (python-shell-interpreter "python3")
  :config
  ;; remove the "guess indent" python message
  (setq python-indent-guess-indent-offset-verbose nil)
  ;; Remove python-mode from auto-mode-alist to prefer python-ts-mode
  (setq auto-mode-alist
        (rassq-delete-all 'python-mode auto-mode-alist)))

;; ------------------------------- LSP for Python ------------------------------
;; Python-specific LSP configuration via pyright
;; Core LSP setup is in prog-general.el

(use-package lsp-pyright
  :hook (python-ts-mode . (lambda ()
                            (require 'lsp-pyright)
                            (lsp-deferred))))

;; ----------------------------------- Poetry ----------------------------------
;; virtual environments and dependencies

(use-package poetry
  :defer t
  :after (python)
  :hook (python-ts-mode . poetry-tracking-mode)
  :config
  ;; Checks for the correct virtualenv. Better strategy IMO because the default
  ;; one is quite slow.
  (setq poetry-tracking-strategy 'switch-buffer))

;; ---------------------------------- Blacken ----------------------------------
;; formatting on save

(use-package blacken
  :custom
  (blacken-allow-py36 t)
  (blacken-skip-string-normalization t)
  :hook (python-ts-mode . blacken-mode)
  :bind (:map python-ts-mode-map
              ("C-; f" . blacken-buffer)))

;; ---------------------------------- Numpydoc ---------------------------------
;; automatically insert NumPy style docstrings in Python function definitions

(use-package numpydoc
  :custom
  (numpydoc-insert-examples-block nil)
  (numpydoc-template-long nil)
  :bind (:map python-ts-mode-map
			  ("C-c C-n" . numpydoc-generate)))

;; ------------------------------------ TOML -----------------------------------
;; editing support and documentation for TOML files

(use-package toml-mode
  :mode "\\.toml\\'")

(use-package eldoc-toml
  :hook (toml-mode . eldoc-toml-mode))


(provide 'prog-python)
;;; prog-python.el ends here