From 03c29ef8890162bbf9a0bb66d8607aa80be0634e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A1n=20Ezequiel=20Gallina?= Date: Sat, 14 Dec 2013 23:41:28 -0300 Subject: [PATCH 1/6] New success/error handling for callbacks The new `python-django-qmgmt-process-status-ok' buffer local variable is set for process buffers and it's non-nil on success and nil on errors. This allows for more adequate error handling for failed commands. --- python-django.el | 224 +++++++++++++++++++++++++---------------------- 1 file changed, 120 insertions(+), 104 deletions(-) diff --git a/python-django.el b/python-django.el index 41f1da8..745c949 100644 --- a/python-django.el +++ b/python-django.el @@ -1128,6 +1128,15 @@ When called with universal argument you can filter the COMMAND to kill." ;;; Management shortcuts +(defvar python-django-qmgmt-process-status-ok nil + "Non-nil if management command process ended successfully. +This variable is set automatically and locally to the management +command process buffer after the process finishes, making it +available for user defined callbacks. See +`python-django-qmgmt-kill-and-msg-callback' as an example for how +to use this variable to execute callback code only if process +ended successfully.") + (defmacro python-django-qmgmt-define (name doc-or-args &optional args &rest iswitches) "Define a quick management command. @@ -1358,9 +1367,10 @@ example of a callback." process (apply-partially #'(lambda (cb process status) - (when (string= status "finished\n") - (set-buffer (process-buffer process)) - (funcall cb))) + (set-buffer (process-buffer process)) + (set (make-local-variable 'python-django-qmgmt-process-status-ok) + (string= status "finished\n")) + (funcall cb)) (apply-partially ',callback (append @@ -1411,13 +1421,14 @@ example of a callback." "Kill the process buffer and show message or output. Argument ARGS is an alist with the arguments passed to the management command." - (let ((msg (or (cdr (assq :msg args)) - (buffer-substring-no-properties - (point-min) (point-max)))) - (buffer-name (buffer-name))) - (kill-buffer) - (python-django-mgmt-restore-window-configuration) - (display-message-or-buffer msg buffer-name))) + (when python-django-qmgmt-process-status-ok + (let ((msg (or (cdr (assq :msg args)) + (buffer-substring-no-properties + (point-min) (point-max)))) + (buffer-name (buffer-name))) + (kill-buffer) + (python-django-mgmt-restore-window-configuration) + (display-message-or-buffer msg buffer-name)))) (python-django-qmgmt-define collectstatic "Collect static files." @@ -1442,33 +1453,34 @@ management command." (defun python-django-qmgmt-create_command-callback (args) "Callback for create_command quick management command. Optional argument ARGS args for it." - (let* ((appname (cdr (assoc 'app args))) - (manage-directory - (file-name-directory - (with-current-buffer - python-django-mgmt-parent-buffer - python-django-project-manage.py))) - (default-app-dir - (expand-file-name appname manage-directory)) - (default-create-dir - (expand-file-name "management" default-app-dir)) - (delete-safe - (and (file-exists-p default-app-dir) - (equal (directory-files default-app-dir) - '("." ".." "management"))))) - (when (y-or-n-p - (format "Created in app %s. Move it? " default-app-dir)) - (let ((newdir - (read-directory-name - "Move app to: " manage-directory nil t))) - (if (not (file-exists-p - (expand-file-name "management" newdir))) - (rename-file default-create-dir newdir) - (message - "Directory structure already exists in %s" appname)) - (and delete-safe (delete-directory default-app-dir t))))) - (kill-buffer) - (python-django-mgmt-restore-window-configuration)) + (when python-django-qmgmt-process-status-ok + (let* ((appname (cdr (assoc 'app args))) + (manage-directory + (file-name-directory + (with-current-buffer + python-django-mgmt-parent-buffer + python-django-project-manage.py))) + (default-app-dir + (expand-file-name appname manage-directory)) + (default-create-dir + (expand-file-name "management" default-app-dir)) + (delete-safe + (and (file-exists-p default-app-dir) + (equal (directory-files default-app-dir) + '("." ".." "management"))))) + (when (y-or-n-p + (format "Created in app %s. Move it? " default-app-dir)) + (let ((newdir + (read-directory-name + "Move app to: " manage-directory nil t))) + (if (not (file-exists-p + (expand-file-name "management" newdir))) + (rename-file default-create-dir newdir) + (message + "Directory structure already exists in %s" appname)) + (and delete-safe (delete-directory default-app-dir t))))) + (kill-buffer) + (python-django-mgmt-restore-window-configuration))) (python-django-qmgmt-define startapp "Create new Django app for current project." @@ -1478,22 +1490,23 @@ Optional argument ARGS args for it." (defun python-django-qmgmt-startapp-callback (args) "Callback for clean_pyc quick management command. Optional argument ARGS args for it." - (let ((appname (cdr (assoc 'app args))) - (manage-directory - (file-name-directory - (with-current-buffer - python-django-mgmt-parent-buffer - python-django-project-manage.py)))) - (when (y-or-n-p - (format - "App created in %s. Do you want to move it? " - manage-directory)) - (rename-file - (expand-file-name appname manage-directory) - (read-directory-name - "Move app to: " manage-directory nil t)))) - (kill-buffer) - (python-django-mgmt-restore-window-configuration)) + (when python-django-qmgmt-process-status-ok + (let ((appname (cdr (assoc 'app args))) + (manage-directory + (file-name-directory + (with-current-buffer + python-django-mgmt-parent-buffer + python-django-project-manage.py)))) + (when (y-or-n-p + (format + "App created in %s. Do you want to move it? " + manage-directory)) + (rename-file + (expand-file-name appname manage-directory) + (read-directory-name + "Move app to: " manage-directory nil t)))) + (kill-buffer) + (python-django-mgmt-restore-window-configuration))) ;; Shell @@ -1571,43 +1584,44 @@ Optional argument ARGS args for it." "Callback executed after dumpdata finishes. ARGS is an alist containing arguments passed to the quick management command." - (let ((file-name - (catch 'file-name - (while t - (let ((file-name - (read-file-name - "Save fixture to file: " - (expand-file-name - (with-current-buffer - python-django-mgmt-parent-buffer - python-django-project-root)) nil nil nil))) - (if (not (file-exists-p file-name)) - (throw 'file-name file-name) - (when (y-or-n-p - (format "File `%s' exists; overwrite? " file-name)) - (throw 'file-name file-name))))))) - (output-buffer (buffer-substring-no-properties - (point-min) (point-max)))) - (with-temp-buffer - (set (make-local-variable 'require-final-newline) t) - (insert output-buffer) - ;; Ensure there's a final newline - (and (> (point-max) (point-min)) - (not (= (char-after (1- (point-max))) ?\n)) - (insert "\n")) - (write-region - (progn - ;; Remove possible logs from output. - (goto-char (point-min)) - (re-search-forward - "^\\[\\|^<\\?xml +version=\"\\|^- +fields: " nil t) - (beginning-of-line 1) - (point)) - (point-max) - file-name)) - (kill-buffer) - (python-django-mgmt-restore-window-configuration) - (message "Fixture saved to file `%s'." file-name))) + (when python-django-qmgmt-process-status-ok + (let ((file-name + (catch 'file-name + (while t + (let ((file-name + (read-file-name + "Save fixture to file: " + (expand-file-name + (with-current-buffer + python-django-mgmt-parent-buffer + python-django-project-root)) nil nil nil))) + (if (not (file-exists-p file-name)) + (throw 'file-name file-name) + (when (y-or-n-p + (format "File `%s' exists; overwrite? " file-name)) + (throw 'file-name file-name))))))) + (output-buffer (buffer-substring-no-properties + (point-min) (point-max)))) + (with-temp-buffer + (set (make-local-variable 'require-final-newline) t) + (insert output-buffer) + ;; Ensure there's a final newline + (and (> (point-max) (point-min)) + (not (= (char-after (1- (point-max))) ?\n)) + (insert "\n")) + (write-region + (progn + ;; Remove possible logs from output. + (goto-char (point-min)) + (re-search-forward + "^\\[\\|^<\\?xml +version=\"\\|^- +fields: " nil t) + (beginning-of-line 1) + (point)) + (point-max) + file-name)) + (kill-buffer) + (python-django-mgmt-restore-window-configuration) + (message "Fixture saved to file `%s'." file-name)))) (defalias 'python-django-qmgmt-dumpdata-app-callback 'python-django-qmgmt-dumpdata-callback) @@ -1666,11 +1680,12 @@ management command." (defun python-django-qmgmt-graph_models-callback (args) "Callback for graph_model quick management command. Optional argument ARGS args for it." - (let ((open (y-or-n-p "Open generated graph? "))) - (kill-buffer) - (python-django-mgmt-restore-window-configuration) - (and open - (find-file (cdr (assoc 'filename args)))))) + (when python-django-qmgmt-process-status-ok + (let ((open (y-or-n-p "Open generated graph? "))) + (kill-buffer) + (python-django-mgmt-restore-window-configuration) + (and open + (find-file (cdr (assoc 'filename args))))))) (defalias 'python-django-qmgmt-graph_models-all-callback 'python-django-qmgmt-graph_models-callback) @@ -1761,15 +1776,16 @@ Optional argument ARGS args for it." "Callback for commands that create migrations. Argument ARGS is an alist with the arguments passed to the management command." - (let ((app (cdr (assq 'app args)))) - (python-django-qmgmt-kill-and-msg-callback args) - (and (y-or-n-p "Open the created migration? ") - (find-file - (expand-file-name - (car (last (python-django-info-get-app-migrations app))) - (expand-file-name - "migrations" - (python-django-info-get-app-path app))))))) + (when python-django-qmgmt-process-status-ok + (let ((app (cdr (assq 'app args)))) + (python-django-qmgmt-kill-and-msg-callback args) + (and (y-or-n-p "Open the created migration? ") + (find-file + (expand-file-name + (car (last (python-django-info-get-app-migrations app))) + (expand-file-name + "migrations" + (python-django-info-get-app-path app)))))))) (python-django-qmgmt-define convert_to_south "Convert given app to South." From 7b772ac771244d86383ca9f0a32ad3276b518480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A1n=20Ezequiel=20Gallina?= Date: Sat, 14 Jun 2014 22:55:40 -0300 Subject: [PATCH 2/6] Better error reporting. * python-django.el: (python-django-util-shell-command-to-string): New function. (python-django-util-shell-command-or-error): New function. (python-django--help-get): (python-django-info-get-version): (python-django-info-get-settings): (python-django-info-get-setting): (python-django-info-get-app-paths): (python-django-info-module-path): Use it. (python-django-open-project): Print user errors to project buffer. --- python-django.el | 101 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 69 insertions(+), 32 deletions(-) diff --git a/python-django.el b/python-django.el index 745c949..4064f4d 100644 --- a/python-django.el +++ b/python-django.el @@ -292,6 +292,37 @@ Many Django faces inherit from this one by default." (1 'font-lock-keyword-face) (2 'font-lock-function-name-face)))) + +;;; Error logging + +(defvar python-django-error-log-formatter + #'python-django-error-default-formatter) + +(defun python-django-error-default-formatter (error-string) + "Formats ERROR-STRING to be placed in the error log." + (format + (concat + "An error occurred retrieving project information.\n" + "Check your project settings and try again:\n\n" + "Current values:\n" + " + python-django-project-root: %s\n" + " + python-django-project-settings: %s\n" + " + python-shell-interpreter: %s\n" + " - found in %s\n\n" + "Details: \n\n%s\n") + python-django-project-root + python-django-project-settings + python-shell-interpreter + (let* ((process-environment + (python-django-info-calculate-process-environment)) + (exec-path (python-shell-calculate-exec-path))) + (executable-find python-shell-interpreter)) + error-string)) + +(defun python-django-error-log (error-string) + "Log ERROR-STRING by calling `user-error'." + (user-error "%s" (funcall python-django-error-log-formatter error-string))) + ;;; Utility functions @@ -367,6 +398,28 @@ the same variables of python files." (and (bufferp key) (setq key (buffer-name key))) (cdr (assoc key alist))) +(defun python-django-util-shell-command-to-string (command) + "Execute shell COMMAND and return its output as a string. +Returns a cons cell where the car is the exit status and the cdr +is the captured output." + (with-temp-buffer + (cons + (apply 'call-process shell-file-name + nil t nil (list shell-command-switch command)) + (buffer-string)))) + +(defun python-django-util-shell-command-or-error (command) + "Execute shell COMMAND and return its output as a string. +If the exit status is an error `python-django-error-log' is used +to display command output." + (let* ((result (python-django-util-shell-command-to-string command)) + (status (car result)) + (output (cdr result))) + (if (zerop status) + output + (python-django-error-log + (concat "Error executing: " command "\n\n" output))))) + (defun python-django-util-shorten-settings (&optional settings) "Return a shorter SETTINGS module string. Optional Argument SETTINGS defaults to the value of @@ -387,7 +440,7 @@ Optional Argument SETTINGS defaults to the value of (let* ((process-environment (python-django-info-calculate-process-environment)) (exec-path (python-shell-calculate-exec-path))) - (shell-command-to-string + (python-django-util-shell-command-or-error (format "%s %s help%s" (executable-find python-shell-interpreter) python-django-project-manage.py @@ -469,7 +522,7 @@ non-nil the cached value is invalidated." (let* ((process-environment (python-django-info-calculate-process-environment)) (exec-path (python-shell-calculate-exec-path))) - (shell-command-to-string + (python-django-util-shell-command-or-error (format "%s -c \"%s\"" (executable-find python-shell-interpreter) @@ -514,7 +567,7 @@ non-nil the cached value is invalidated." (exec-path (python-shell-calculate-exec-path)) (value (json-read-from-string - (shell-command-to-string + (python-django-util-shell-command-or-error (format "%s -c \"%s %s\"" (executable-find python-shell-interpreter) python-django-info-imports-code @@ -554,7 +607,7 @@ non-nil the cached value is invalidated." (exec-path (python-shell-calculate-exec-path)) (value (json-read-from-string - (shell-command-to-string + (python-django-util-shell-command-or-error (format "%s -c \"%s %s\"" (executable-find python-shell-interpreter) @@ -587,7 +640,7 @@ non-nil the cached value is invalidated." (python-django-info-calculate-process-environment)) (exec-path (python-shell-calculate-exec-path))) (json-read-from-string - (shell-command-to-string + (python-django-util-shell-command-or-error (format "%s -c \"%s %s\"" (executable-find python-shell-interpreter) python-django-info-imports-code @@ -624,7 +677,7 @@ non-nil the cached value is invalidated." (let* ((process-environment (python-django-info-calculate-process-environment)) (exec-path (python-shell-calculate-exec-path))) - (shell-command-to-string + (python-django-util-shell-command-or-error (format "%s -c \"%s %s %s\"" (executable-find python-shell-interpreter) @@ -2410,37 +2463,21 @@ settings module (the same happens when called with two or more (file-name-directory python-django-project-manage.py)) (python-django-util-clone-local-variables) - (python-django-ui-insert-header) (set (make-local-variable 'tree-widget-image-enable) python-django-ui-image-enable) (tree-widget-set-theme python-django-ui-theme) (condition-case err - (mapc (lambda (section) - (python-django-ui-tree-section-insert - (car section) (cdr section)) - (insert "\n")) - (python-django-ui-build-section-alist)) - (error + (progn + (python-django-ui-insert-header) + (mapc (lambda (section) + (python-django-ui-tree-section-insert + (car section) (cdr section)) + (insert "\n")) + (python-django-ui-build-section-alist))) + (user-error (setq success nil) - (insert - (format - (concat - "An error occurred retrieving project information.\n" - "Check your project settings and try again:\n\n" - "Current values:\n" - " + python-django-project-root: %s\n" - " + python-django-project-settings: %s\n" - " + python-shell-interpreter: %s\n" - " - found in %s\n\n\n" - "Error: %s \n") - python-django-project-root - python-django-project-settings - python-shell-interpreter - (let* ((process-environment - (python-django-info-calculate-process-environment)) - (exec-path (python-shell-calculate-exec-path))) - (executable-find python-shell-interpreter)) - (error-message-string err)))))) + (insert (error-message-string err)) + (goto-char (point-min))))) (when success (add-hook 'kill-buffer-hook #'python-django-mode-on-kill-buffer nil t) From 072ad4d17b5479485c69a7b8c95cf9edb956c9ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A1n=20Ezequiel=20Gallina?= Date: Sat, 14 Jun 2014 23:28:08 -0300 Subject: [PATCH 3/6] Generate readable code for python interactions --- python-django.el | 63 ++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/python-django.el b/python-django.el index 4064f4d..f08134d 100644 --- a/python-django.el +++ b/python-django.el @@ -527,20 +527,22 @@ non-nil the cached value is invalidated." "%s -c \"%s\"" (executable-find python-shell-interpreter) (concat - "from __future__ import print_function;" - "import django; print(django.get_version(), end='')")))))) + "from __future__ import print_function\n" + "import django\n" + "print(django.get_version(), end='')")))))) (defvar python-django-info-imports-code - (concat "from __future__ import print_function;" - "import sys; import os.path;" - "from os.path import dirname, abspath;" - "stdout = sys.stdout; stderr = sys.stderr;" - "sys.stdout = sys.stderr = open(os.devnull, 'w');" - "from django.conf import settings;" - "from django.utils import simplejson;" - ;; Force settings loading so all output is sent to devnull. - "settings.DEBUG;" - "sys.stdout = stdout; sys.stderr = stderr;") + (concat "\n" + "from __future__ import print_function\n" + "import sys\n" + "from os.path import dirname, abspath\n" + "stdout = sys.stdout; stderr = sys.stderr\n" + "sys.stdout = sys.stderr = open(os.devnull, 'w')\n" + "from django.conf import settings\n" + "from django.utils import simplejson\n" + "# Force settings loading so all output is sent to devnull.\n" + "settings.DEBUG\n" + "sys.stdout = stdout; sys.stderr = stderr\n\n") "All imports code used to get info. It contains output redirecting features so settings import doesn't break the JSON output.") @@ -565,21 +567,24 @@ non-nil the cached value is invalidated." (let* ((process-environment (python-django-info-calculate-process-environment)) (exec-path (python-shell-calculate-exec-path)) + (settings-list-string + (concat "[" + (mapconcat + #'(lambda (str) (concat "'" str "'")) + python-django-info-prefetched-settings + ", ") + "]")) (value (json-read-from-string (python-django-util-shell-command-or-error - (format "%s -c \"%s %s\"" + (format "%s -c \"%s%s\"" (executable-find python-shell-interpreter) python-django-info-imports-code (concat - "print(simplejson.dumps(" - "dict([(name, getattr(settings, name, None)) " - "for name in (" - (mapconcat - #'(lambda (str) (concat "'" str "'")) - python-django-info-prefetched-settings - ", ") - ")])), end='')")))))) + "acc = {}\n" + "for name in " settings-list-string ":\n" + " acc[name] = getattr(settings, name, None)\n" + "print(simplejson.dumps(acc), end='')")))))) (mapc (lambda (elt) (let ((cached-val @@ -609,7 +614,7 @@ non-nil the cached value is invalidated." (json-read-from-string (python-django-util-shell-command-or-error (format - "%s -c \"%s %s\"" + "%s -c \"%s%s\"" (executable-find python-shell-interpreter) python-django-info-imports-code (format @@ -641,7 +646,7 @@ non-nil the cached value is invalidated." (exec-path (python-shell-calculate-exec-path))) (json-read-from-string (python-django-util-shell-command-or-error - (format "%s -c \"%s %s\"" + (format "%s -c \"%s%s\"" (executable-find python-shell-interpreter) python-django-info-imports-code (concat @@ -678,12 +683,12 @@ non-nil the cached value is invalidated." (python-django-info-calculate-process-environment)) (exec-path (python-shell-calculate-exec-path))) (python-django-util-shell-command-or-error - (format - "%s -c \"%s %s %s\"" - (executable-find python-shell-interpreter) - "from __future__ import print_function;" - (format "import os.path; import %s;" module) - (format "print(%s.__file__.replace('.pyc', '.py'), end='')" module))))) + (format "%s -c \"%s%s%s\"" + (executable-find python-shell-interpreter) + python-django-info-imports-code + (format "import %s\n" module) + (format + "print(%s.__file__.replace('.pyc', '.py'), end='')" module))))) (defun python-django-info-directory-basename (&optional dir) "Get innermost directory name for given DIR." From 7283e85f80b528a99547c2eb1299e4e143467d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A1n=20Ezequiel=20Gallina?= Date: Sat, 14 Jun 2014 23:52:11 -0300 Subject: [PATCH 4/6] Fix allowed file extension checks (Bug#12) * python-django.el: (python-django-util-file-name-extension): New function. (python-django-ui-tree-dir-widget-expand): Use it. --- python-django.el | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/python-django.el b/python-django.el index f08134d..20f342b 100644 --- a/python-django.el +++ b/python-django.el @@ -398,6 +398,15 @@ the same variables of python files." (and (bufferp key) (setq key (buffer-name key))) (cdr (assoc key alist))) +;; Based on `file-name-extension' +(defun python-django-util-file-name-extension (filename) + "Return FILENAME's final \"extension\" sans dot." + (save-match-data + (let ((file (file-name-nondirectory filename))) + (if (and (string-match "\\.[^.]*\\'" file) + (not (eq 0 (match-beginning 0)))) + (substring file (+ (match-beginning 0) 1)))))) + (defun python-django-util-shell-command-to-string (command) "Execute shell COMMAND and return its output as a string. Returns a cons cell where the car is the exit status and the cdr @@ -2203,7 +2212,7 @@ Optional argument IGNORE is there for compatibility." (if (file-directory-p file) (when (not (member basename python-django-ui-ignored-dirs)) (setq dir-list (cons basename dir-list))) - (when (member (file-name-extension file) + (when (member (python-django-util-file-name-extension file) python-django-ui-allowed-extensions) (setq file-list (cons basename file-list)))))) (setq dir-list (sort dir-list 'string<)) From 0b26e28e179e538154dd991876b380eb121d1f13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A1n=20Ezequiel=20Gallina?= Date: Sun, 15 Jun 2014 00:08:36 -0300 Subject: [PATCH 5/6] Add os to `python-django-info-imports-code` --- python-django.el | 1 + 1 file changed, 1 insertion(+) diff --git a/python-django.el b/python-django.el index 20f342b..97851b5 100644 --- a/python-django.el +++ b/python-django.el @@ -543,6 +543,7 @@ non-nil the cached value is invalidated." (defvar python-django-info-imports-code (concat "\n" "from __future__ import print_function\n" + "import os\n" "import sys\n" "from os.path import dirname, abspath\n" "stdout = sys.stdout; stderr = sys.stderr\n" From 698c6fc3384e035de98fd44a13e00fbbefe7281f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A1n=20Ezequiel=20Gallina?= Date: Sun, 15 Jun 2014 00:14:38 -0300 Subject: [PATCH 6/6] Import JSON properly for newer Django versions --- python-django.el | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/python-django.el b/python-django.el index 97851b5..945cbaf 100644 --- a/python-django.el +++ b/python-django.el @@ -549,7 +549,11 @@ non-nil the cached value is invalidated." "stdout = sys.stdout; stderr = sys.stderr\n" "sys.stdout = sys.stderr = open(os.devnull, 'w')\n" "from django.conf import settings\n" - "from django.utils import simplejson\n" + "# Try to import json really hard\n" + "try:\n" + " import json\n" + "except ImportError:\n" + " from django.utils import simplejson as json\n" "# Force settings loading so all output is sent to devnull.\n" "settings.DEBUG\n" "sys.stdout = stdout; sys.stderr = stderr\n\n") @@ -594,7 +598,7 @@ non-nil the cached value is invalidated." "acc = {}\n" "for name in " settings-list-string ":\n" " acc[name] = getattr(settings, name, None)\n" - "print(simplejson.dumps(acc), end='')")))))) + "print(json.dumps(acc), end='')")))))) (mapc (lambda (elt) (let ((cached-val @@ -629,7 +633,7 @@ non-nil the cached value is invalidated." python-django-info-imports-code (format (concat - "print(simplejson.dumps(" + "print(json.dumps(" "getattr(settings, '%s', None)), end='')") setting))))) (already-cached (assq (intern setting) @@ -667,7 +671,7 @@ non-nil the cached value is invalidated." " for sub in app.split('.')[1:]:\n" " mod = getattr(mod, sub)\n" " app_paths[app] = dirname(abspath(mod.__file__))\n" - "print(simplejson.dumps(app_paths), end='')")))))) + "print(json.dumps(app_paths), end='')")))))) python-django-info--get-app-paths-cache)) (defun python-django-info-get-app-path (app &optional force)