Subject: 30.0.50; Packages without a maintainer Email address cannot be described
From: Philip Kaludercic <[email-masked]>
To: [email-masked]
Date: Fri, 15 Sep 2023 07:19:57 +0000
List-Id: <debbugs-submit.debbugs.gnu.org>

C-h P raises an error when the maintainer does not give an email
address.  For example, C-h P gtags-mode RET triggers this stack trace:

--8<---------------cut here---------------start------------->8---
Debugger entered--Lisp error: (wrong-type-argument listp "Jimmy Aguilar Mena")
  car("Jimmy Aguilar Mena")
  (if (car recipient) (progn (insert (car recipient))))
  package--print-email-button("Jimmy Aguilar Mena")
  (let ((maintainer (car tail))) (if (bolp) (progn (insert (make-string 13 32)))) (package--print-email-button maintainer) (setq tail (cdr tail)))
  (while tail (let ((maintainer (car tail))) (if (bolp) (progn (insert (make-string 13 32)))) (package--print-email-button maintainer) (setq tail (cdr tail))))
  (let ((tail maintainers)) (while tail (let ((maintainer (car tail))) (if (bolp) (progn (insert (make-string 13 32)))) (package--print-email-button maintainer) (setq tail (cdr tail)))))
  (progn (if (proper-list-p maintainers) nil (setq maintainers (list maintainers))) (package--print-help-section (if (cdr maintainers) "Maintainers" "Maintainer")) (let ((tail maintainers)) (while tail (let ((maintainer (car tail))) (if (bolp) (progn (insert (make-string 13 32)))) (package--print-email-button maintainer) (setq tail (cdr tail))))))
  (if maintainers (progn (if (proper-list-p maintainers) nil (setq maintainers (list maintainers))) (package--print-help-section (if (cdr maintainers) "Maintainers" "Maintainer")) (let ((tail maintainers)) (while tail (let ((maintainer (car tail))) (if (bolp) (progn (insert ...))) (package--print-email-button maintainer) (setq tail (cdr tail)))))))
  (let* ((desc (or (if (let* ... ...) pkg) (car (cdr ...)) (let (...) (if built-in ... ...)))) (name (if desc (let* (...) (progn ... ...)) pkg)) (pkg-dir (if desc (let* (...) (progn ... ...)))) (reqs (if desc (let* (...) (progn ... ...)))) (required-by (if desc (package--used-elsewhere-p desc nil 'all))) (version (if desc (let* (...) (progn ... ...)))) (archive (if desc (let* (...) (progn ... ...)))) (extras (and desc (let* (...) (progn ... ...)))) (website (cdr (assoc :url extras))) (commit (cdr (assoc :commit extras))) (keywords (if desc (package-desc--keywords desc))) (built-in (eq pkg-dir 'builtin)) (installable (and archive (not built-in))) (status (if desc (package-desc-status desc) "orphan")) (incompatible-reason (package--incompatible-p desc)) (signed (if desc (let* (...) (progn ... ...)))) (maintainers (or (cdr (assoc :maintainers extras)) (cdr (assoc :maintainer extras)))) (authors (cdr (assoc :authors extras))) (news (let* ((pkg-dir ...) (s ...) (file ...) (s ...) (s ...)) (if s (progn file))))) (if (string= status "avail-obso") (progn (setq status "available obsolete"))) (if incompatible-reason (progn (setq status "incompatible"))) (princ (format "Package %S is %s.\n\n" name status)) (package--print-help-section "Status") (cond (built-in (insert (propertize (capitalize status) 'font-lock-face 'package-status-built-in) ".")) (pkg-dir (insert (propertize (if ... "Installed" ...) 'font-lock-face 'package-status-built-in)) (insert (substitute-command-keys " in `")) (let ((dir ...)) (help-insert-xref-button dir 'help-package-def pkg-dir)) (if (and (package-built-in-p name) (not ...)) (insert (substitute-command-keys "',\n             shadowing a ") (propertize "built-in package" ... ...)) (insert (substitute-quotes "'"))) (if signed (insert ".") (insert " (unsigned).")) (if (and (let* ... ...) (not required-by) (member status ...)) (progn (insert " ") (package-make-button "Delete" ... ... ... desc)))) (incompatible-reason (insert (propertize "Incompatible" 'font-lock-face font-lock-warning-face) " because it depends on ") (if (stringp incompatible-reason) (insert "Emacs " incompatible-reason ".") (insert "uninstallable packages."))) (installable (insert (capitalize status)) (insert " from " (format "%s" archive)) (insert " -- ") (package-make-button "Install" 'action 'package-install-button-action 'package-desc desc)) (t (insert (capitalize status) "."))) (insert "\n") (if (and pkg-dir (not archive)) nil (package--print-help-section "Archive" (or archive "n/a"))) (and version (package--print-help-section "Version" (package-version-join version))) (if commit (progn (package--print-help-section "Commit" commit))) (if desc (progn (package--print-help-section "Summary" (let* (...) (progn ... ...))))) (setq reqs (if desc (let* ((cl-x desc)) (progn (or ... ...) (aref cl-x 4))))) (if reqs (progn (package--print-help-section "Requires") (let ((first t)) (let (...) (while tail ...)) (insert "\n")))) (if required-by (progn (package--print-help-section "Required by") (let ((first t)) (let (...) (while tail ...)) (insert "\n")))) (if website (progn (if (string-match-p "^http://\\(?:\\(?:elpa\\|git\\|www\\)\\.\\)?\\..." website) (progn (let ... ...))) (package--print-help-section "Website") (help-insert-xref-button website 'help-url website) (insert "\n"))) (if keywords (progn (package--print-help-section "Keywords") (let ((tail keywords)) (while tail (let ... ... ... ...))) (insert "\n"))) (if maintainers (progn (if (proper-list-p maintainers) nil (setq maintainers (list maintainers))) (package--print-help-section (if (cdr maintainers) "Maintainers" "Maintainer")) (let ((tail maintainers)) (while tail (let ... ... ... ...))))) (if authors (progn (package--print-help-section (if (cdr authors) "Authors" "Author")) (let ((tail authors)) (while tail (let ... ... ... ...))))) (let* ((all-pkgs (append (cdr ...) (cdr ...) (let ... ...))) (other-pkgs (delete desc all-pkgs))) (if other-pkgs (progn (package--print-help-section "Other versions" (mapconcat ... other-pkgs ", ") ".")))) (insert "\n") (let ((start-of-description (point))) (if built-in (insert (or (lm-commentary ...) "")) (if (package-installed-p desc) (insert (package--get-description desc)) (let* (... readme-string) (package--with-response-buffer-1 ... ... :file basename :async nil :error-function ... :noerror t) (insert ...)))) (if news (progn (insert "\n" (make-separator-line) "\n" (propertize "* News" ... ...) "\n\n") (insert-file-contents news))) (goto-char start-of-description) (package--describe-add-library-links) (goto-char start-of-description) (browse-url-add-buttons)))
  describe-package-1(gtags-mode)
  (save-current-buffer (set-buffer standard-output) (describe-package-1 package))
  (closure ((package . gtags-mode)) nil (save-current-buffer (set-buffer standard-output) (describe-package-1 package)))()
  help--window-setup("*Help*" (closure ((package . gtags-mode)) nil (save-current-buffer (set-buffer standard-output) (describe-package-1 package))))
  (if (not (or (let* ((cl-x package)) (progn (and (memq (type-of cl-x) cl-struct-package-desc-tags) t))) (and package (symbolp package)))) (message "No package specified") (help-setup-xref (list #'describe-package package) (called-interactively-p 'interactive)) (help--window-setup (help-buffer) #'(lambda nil (save-current-buffer (set-buffer standard-output) (describe-package-1 package)))))
  describe-package(gtags-mode)
  funcall-interactively(describe-package gtags-mode)
  command-execute(describe-package)
--8<---------------cut here---------------end--------------->8---

A simple solution would be

diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index 02691ff7aa5..b784b0b0434 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -3089,6 +3090,8 @@ package--print-email-button
   "Insert a button whose action will send an email to RECIPIENT.
 NAME should have the form (FULLNAME . EMAIL) where FULLNAME is
 either a full name or nil, and EMAIL is a valid email address."
+  (when (stringp recipient)
+    (setq recipient (cons recipient "[email-masked]")))
   (when (car recipient)
     (insert (car recipient)))
   (when (and (car recipient) (cdr recipient))

or perhaps not render any button at all?

In GNU Emacs 30.0.50 (build 7, x86_64-pc-linux-gnu, GTK+ Version
 3.24.37, cairo version 1.16.0) of 2023-09-13 built on quetzal
Repository revision: a8959f1b245540a2d0d158621dedf244ac133849
Repository branch: master
System Description: Debian GNU/Linux 12 (bookworm)

Configured using:
 'configure --with-pgtk --with-native-compilation --with-imagemagick
 --with-tree-sitter'

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ
IMAGEMAGICK JPEG JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 MODULES
NATIVE_COMP NOTIFY INOTIFY PDUMPER PGTK PNG RSVG SECCOMP SOUND SQLITE3
THREADS TIFF TOOLKIT_SCROLL_BARS TREE_SITTER WEBP XIM GTK3 ZLIB

Important settings:
  value of $EMACSLOADPATH: 
  value of $LC_MONETARY: en_US.UTF-8
  value of $LC_NUMERIC: en_US.UTF-8
  value of $LC_TIME: en_US.UTF-8
  value of $LANG: en_US.UTF-8
  value of $XMODIFIERS: @im=ibus
  locale-coding-system: utf-8-unix

Major mode: ELisp/l

Minor modes in effect:
  bug-reference-prog-mode: t
  gtags-mode: t
  flyspell-mode: t
  flymake-mode: t
  outline-minor-mode: t
  yas-minor-mode: t
  editorconfig-mode: t
  repeat-mode: t
  display-battery-mode: t
  display-time-mode: t
  diff-hl-flydiff-mode: t
  diff-hl-mode: t
  winner-mode: t
  windmove-mode: t
  electric-pair-mode: t
  recentf-mode: t
  save-place-mode: t
  savehist-mode: t
  pixel-scroll-precision-mode: t
  pixel-scroll-mode: t
  xterm-mouse-mode: t
  which-function-mode: t
  package-autosuggest-mode: message
  tooltip-mode: t
  global-eldoc-mode: t
  eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  tab-bar-mode: t
  file-name-shadow-mode: t
  context-menu-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  minibuffer-regexp-mode: t
  line-number-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
/home/philip/.config/emacs/site-lisp/company-coq/.auto-site hides ~/.config/emacs/site-lisp/.auto-site
/home/philip/.config/emacs/site-lisp/flymake-proselint/flymake-proselint hides /home/philip/.config/emacs/elpa/flymake-proselint-0.3.0/flymake-proselint
/home/philip/.config/emacs/site-lisp/shell-command+/shell-command+ hides /home/philip/.config/emacs/elpa/shell-command+/shell-command+
/home/philip/.config/emacs/site-lisp/shell-command+/shell-command+-tests hides /home/philip/.config/emacs/elpa/shell-command+/shell-command+-tests
/home/philip/.config/emacs/site-lisp/vc-backup/vc-backup hides /home/philip/.config/emacs/elpa/vc-backup-1.1.0/vc-backup
/home/philip/.config/emacs/elpa/transient-0.4.3/transient hides /home/philip/Source/emacs/lisp/transient
/home/philip/.config/emacs/elpa/sweeprolog/theme-loaddefs hides /home/philip/Source/emacs/lisp/theme-loaddefs
/home/philip/.config/emacs/elpa/compat-29.1.4.2/compat hides /home/philip/Source/emacs/lisp/emacs-lisp/compat

Features:
(shadow emacsbug apropos avy vc-annotate edebug whitespace bug-reference
help-fns cl-print finder cus-start gtags-mode gtags-mode-autoloads
url-cache url-http url-auth url-gw package-vc bash-completion
flymake-proselint markdown-mode do-at-point hi-lock help-at-pt eglot
jsonrpc ert debug backtrace find-func make-mode add-log vc-fossil vc-hg
vc-bzr vc-src vc-sccs vc-svn vc-cvs vc-rcs char-fold misearch
multi-isearch doc-view jka-compr image-mode exif flymake-cc pulse
cc-mode cc-fonts cc-guess cc-menus cc-cmds cc-styles cc-align cc-engine
cc-vars cc-defs grep xref typo vc-git dired-aux autorevert filenotify
gnus-dired vc-backup copyright buffer-env compat autocrypt-message
writegood-mode ecomplete dictionary external-completion
dictionary-connection ffap shr-color color qp ietf-drums-date mule-util
sort smiley gnus-cite flow-fill mm-archive mail-extr textsec uni-scripts
idna-mapping ucs-normalize uni-confusable textsec-check gnus-async
gnus-bcklg gnus-ml disp-table autocrypt-gnus autocrypt nndraft nnmh
utf-7 nnfolder epa-file network-stream nsm gnus-agent gnus-srvr
gnus-score score-mode nnvirtual gnus-msg gnus-art mm-uu mml2015 mm-view
mml-smime smime gnutls dig nntp gnus-cache gnus-sum shr pixel-fill
kinsoku url-file svg dom gnus-group gnus-undo gnus-start gnus-dbus
gnus-cloud nnimap nnmail mail-source utf7 nnoo gnus-spec gnus-int
gnus-range message yank-media puny dired-x dired dired-loaddefs rfc822
mml mml-sec epa mm-decode mm-bodies mm-encode mail-parse rfc2231
mailabbrev gmm-utils mailheader gnus-win flyspell ispell checkdoc
flymake-proc flymake yasnippet-snippets yasnippet noutline outline
editorconfig editorconfig-core editorconfig-core-handle
editorconfig-fnmatch init repeat project battery dbus comp comp-cstr
warnings xml shell-command+ thingatpt time sendmail rfc2047 rfc2045
ietf-drums gnus nnheader gnus-util mail-utils range mm-util mail-prsvr
diff-hl-flydiff diff diff-hl log-view pcvs-util vc-dir ewoc vc
vc-dispatcher diff-mode hippie-exp winner windmove elec-pair tramp-cache
time-stamp recentf tree-widget saveplace tramp-sh tramp trampver
tramp-integration files-x tramp-message tramp-compat xdg shell pcomplete
parse-time iso8601 time-date format-spec tramp-loaddefs savehist
pixel-scroll cua-base xt-mouse cus-edit pp wid-edit which-func imenu
cus-load .auto-site compile text-property-search comint ansi-osc
ansi-color ring loaddefs-gen lisp-mnt radix-tree site-lisp setup
finder-inf auctex-autoloads tex-site autocrypt-autoloads avy-autoloads
bash-completion-autoloads buffer-env-autoloads cdlatex-autoloads
comint-mime-autoloads crdt-autoloads debbugs-autoloads diff-hl-autoloads
do-at-point-autoloads editorconfig-autoloads flymake-proselint-autoloads
focus-autoloads geiser-chez-autoloads geiser-guile-autoloads
geiser-kawa-autoloads geiser-mit-autoloads geiser-racket-autoloads
geiser-autoloads gnu-apl-mode-autoloads go-mode-autoloads
haskell-mode-autoloads htmlize-autoloads magit-autoloads
git-commit-autoloads magit-section-autoloads dash-autoloads
markdown-mode-autoloads proof-general-autoloads proof-site
proof-autoloads rmsbolt-autoloads shell-command+-autoloads
site-lisp-autoloads sly-autoloads sml-mode-autoloads
sweeprolog-autoloads transient-autoloads tuareg-autoloads caml-autoloads
typo-autoloads vc-backup-autoloads vc-fossil-autoloads
visual-fill-column-autoloads with-editor-autoloads info compat-autoloads
writegood-mode-autoloads xr-autoloads yasnippet-snippets-autoloads
yasnippet-autoloads package easy-mmode let-alist derived rx pcase
browse-url url url-proxy url-privacy url-expand url-methods url-history
url-cookie generate-lisp-file url-domsuf url-util mailcap url-handlers
url-parse auth-source cl-seq eieio eieio-core cl-macs password-cache
json map byte-opt gv bytecomp byte-compile url-vars inline epg rfc6068
epg-config subr-x cl-extra help-mode icons cl-loaddefs cl-lib rmc
iso-transl tooltip cconv eldoc paren electric uniquify ediff-hook
vc-hooks lisp-float-type elisp-mode mwheel term/pgtk-win pgtk-win
term/common-win pgtk-dnd touch-screen tool-bar dnd fontset image
regexp-opt fringe tabulated-list replace newcomment text-mode lisp-mode
prog-mode register page tab-bar menu-bar rfn-eshadow isearch easymenu
timer select scroll-bar mouse jit-lock font-lock syntax font-core
term/tty-colors frame minibuffer nadvice seq simple cl-generic
indonesian philippine cham georgian utf-8-lang misc-lang vietnamese
tibetan thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek
romanian slovak czech european ethiopic indian cyrillic chinese
composite emoji-zwj charscript charprop case-table epa-hook
jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button loaddefs
theme-loaddefs faces cus-face macroexp files window text-properties
overlay sha1 md5 base64 format env code-pages mule custom widget keymap
hashtable-print-readable backquote threads dbusbind inotify
dynamic-setting system-font-setting font-render-setting cairo gtk pgtk
lcms2 multi-tty move-toolbar make-network-process native-compile emacs)

Memory information:
((conses 16 804204 147061) (symbols 48 35153 17)
 (strings 32 155109 11051) (string-bytes 1 4499390) (vectors 16 94170)
 (vector-slots 8 2386053 223181) (floats 8 739 4529)
 (intervals 56 26957 5731) (buffers 992 47))