2
2
3
3
; ; Author: wouter bolsterlee <[email protected] >
4
4
; ; Version: 3.3.0
5
- ; ; Package-Requires: ((emacs "24.4") (dash "2.18.0") (transient "0.3.7") (projectile "0.14.0") ( s "1.12.0"))
5
+ ; ; Package-Requires: ((emacs "24.4") (dash "2.18.0") (transient "0.3.7") (s "1.12.0"))
6
6
; ; Keywords: pytest, test, python, languages, processes, tools
7
7
; ; URL: https://github.com/wbolster/emacs-python-pytest
8
8
; ;
25
25
26
26
(require 'dash )
27
27
(require 'transient )
28
- (require 'projectile )
29
28
(require 's )
30
29
30
+ (require 'projectile nil t )
31
+ (require 'project nil t )
32
+
31
33
(defgroup python-pytest nil
32
34
" pytest integration"
33
35
:group 'python
@@ -106,6 +108,25 @@ When non-nil only ‘test_foo()’ will match, and nothing else."
106
108
(const :tag " Save current buffer" save-current)
107
109
(const :tag " Ignore" nil )))
108
110
111
+ (defcustom python-pytest-preferred-project-manager 'auto
112
+ " Override `projectile' or `project' auto-discovery to set preference if using both."
113
+ :group 'python-pytest
114
+ :type '(choice (const :tag " Projectile" projectile)
115
+ (const :tag " Project" project)
116
+ (const :tag " Automatically selected" auto))
117
+ :set (lambda (symbol value )
118
+ (cond
119
+ ((and (eq value 'projectile )
120
+ (not (featurep 'projectile )))
121
+ (user-error " Projectile preferred for python-pytest.el, but not available." ))
122
+ ((and (eq value 'project )
123
+ (not (fboundp 'project-root )))
124
+ (user-error (concat " Project.el preferred for python-pytest.el, "
125
+ " but need a newer version of Project (28.1+) to use." )))
126
+ (t
127
+ (set-default symbol value)
128
+ value))))
129
+
109
130
(defvar python-pytest--history nil
110
131
" History for pytest invocations." )
111
132
@@ -508,6 +529,11 @@ When present ON-REPLACEMENT is substituted, else OFF-REPLACEMENT is appended."
508
529
:argument " --numprocesses="
509
530
:choices '(" auto" " 0" " 1" " 2" " 4" " 8" " 16" ))
510
531
532
+ (defun python-pytest--using-projectile ()
533
+ " Returns t if projectile being used for project management."
534
+ (or (eq python-pytest-preferred-project-manager 'projectile )
535
+ (and (eq python-pytest-preferred-project-manager 'auto )
536
+ (bound-and-true-p projectile-mode))))
511
537
512
538
; ; python helpers
513
539
@@ -551,12 +577,26 @@ Example: ‘MyABCThingy.__repr__’ becomes ‘test_my_abc_thingy_repr’."
551
577
552
578
(defun python-pytest--project-name ()
553
579
" Find the project name."
554
- (projectile-project-name))
580
+ (if (python-pytest--using-projectile)
581
+ (projectile-project-name)
582
+ (if (fboundp 'project-name )
583
+ (project-name (project-current ))
584
+ ; ; older emacs...
585
+ (file-name-nondirectory
586
+ (directory-file-name (car (project-roots (project-current ))))))))
555
587
556
588
(defun python-pytest--project-root ()
557
- " Find the project root directory."
558
- (let ((projectile-require-project-root nil ))
559
- (projectile-compilation-dir)))
589
+ " Find the project root directory, for project.el can manually set your own
590
+ `project-compilation-dir' variable to override `project-root' being used."
591
+ (if (python-pytest--using-projectile)
592
+ (let ((projectile-require-project-root nil ))
593
+ (projectile-compilation-dir))
594
+ (or (and (bound-and-true-p project-compilation-dir)
595
+ project-compilation-dir)
596
+ (if (fboundp 'project-root )
597
+ (project-root (project-current ))
598
+ ; ; pre-emacs "28.1"
599
+ (car (project-roots (project-current )))))))
560
600
561
601
(defun python-pytest--relative-file-name (file )
562
602
" Make FILE relative to the project root."
@@ -567,11 +607,29 @@ Example: ‘MyABCThingy.__repr__’ becomes ‘test_my_abc_thingy_repr’."
567
607
568
608
(defun python-pytest--test-file-p (file )
569
609
" Tell whether FILE is a test file."
570
- (projectile-test-file-p file))
610
+ (if (python-pytest--using-projectile)
611
+ (projectile-test-file-p file)
612
+ (let ((base-name (file-name-nondirectory file)))
613
+ (or (string-prefix-p " test_" base-name)
614
+ (string-suffix-p " _test.py" base-name)))))
571
615
572
616
(defun python-pytest--find-test-file (file )
573
617
" Find a test file associated to FILE, if any."
574
- (let ((test-file (projectile-find-matching-test file)))
618
+ (let ((test-file))
619
+ (if (python-pytest--using-projectile)
620
+ (setq test-file (projectile-find-matching-test file))
621
+ (let* ((base-name (file-name-sans-extension (file-name-nondirectory file)))
622
+ (test-file-regex (concat " \\ `test_"
623
+ base-name " \\ .py\\ '\\ |\\ `"
624
+ base-name " _test\\ .py\\ '" )))
625
+ (setq test-file
626
+ (car (cl-delete-if
627
+ (lambda (full-file )
628
+ (let ((file (file-name-nondirectory full-file)))
629
+ (not (string-match-p
630
+ test-file-regex
631
+ file))))
632
+ (project-files (project-current t )))))))
575
633
(unless test-file
576
634
(user-error " No test file found" ))
577
635
test-file))
@@ -585,11 +643,34 @@ Example: ‘MyABCThingy.__repr__’ becomes ‘test_my_abc_thingy_repr’."
585
643
(cl-defun python-pytest--select-test-files (&key type )
586
644
" Interactively choose test files."
587
645
(let* ((test-files
588
- (->> (projectile-project-files (python-pytest--project-root))
589
- (-sort 'string< )
590
- (projectile-sort-by-recentf-first)
591
- ; ; show test files if any found, otherwise show everything
592
- (funcall (-orfn #'projectile-test-files #'identity ))))
646
+ (if (python-pytest--using-projectile)
647
+ (->> (projectile-project-files (python-pytest--project-root))
648
+ (-sort 'string< )
649
+ (projectile-sort-by-recentf-first)
650
+ ; ; show test files if any found, otherwise show everything
651
+ (funcall (-orfn #'projectile-test-files #'identity )))
652
+ (let* ((vc-directory-exclusion-list
653
+ (append vc-directory-exclusion-list '(" venv" " .venv" )))
654
+ (sorted-test-files
655
+ (sort (cl-delete-if
656
+ (lambda (file )
657
+ (not (python-pytest--test-file-p file)))
658
+ (project-files (project-current t )))
659
+ #'string< ))
660
+ (recentf-test-files '())
661
+ (test-files-prj
662
+ (when (fboundp 'recentf )
663
+ (dolist (file recentf-list
664
+ (progn
665
+ (setq sorted-test-files
666
+ (append (nreverse recentf-test-files)
667
+ sorted-test-files))
668
+ (cl-delete-duplicates sorted-test-files
669
+ :test 'equal )))
670
+ (when (and (file-exists-p file)
671
+ (python-pytest--test-file-p file))
672
+ (push (expand-file-name file) recentf-test-files))))))
673
+ test-files-prj)))
593
674
(test-directories
594
675
(->> test-files
595
676
(-map 'file-name-directory )
@@ -615,7 +696,9 @@ Example: ‘MyABCThingy.__repr__’ becomes ‘test_my_abc_thingy_repr’."
615
696
; ; check all project buffers
616
697
(-when-let*
617
698
((buffers
618
- (projectile-buffers-with-file (projectile-project-buffers)))
699
+ (if (python-pytest--using-projectile)
700
+ (projectile-buffers-with-file (projectile-project-buffers))
701
+ (-filter 'buffer-file-name (project-buffers (project-current t )))))
619
702
(modified-buffers
620
703
(-filter 'buffer-modified-p buffers))
621
704
(confirmed
0 commit comments