         content))))
 
 (defun python-pyproject-path ()
-  (when-let ((dir (locate-dominating-file default-directory "pyproject.toml")))
-    (expand-file-name (concat dir "pyproject.toml"))))
+  (find-file-from-project-root "pyproject.toml"))
 
 (defun python-use-poetry-p ()
-  (and (python-pyproject-path)
-       (with-temp-buffer
-         (insert-file-contents (python-pyproject-path))
-         (goto-char (point-min))
-         (re-search-forward "^\\[tool.poetry\\]$" nil t nil))
-       (executable-find "poetry")))
+  (when-let ((pyproject-path (python-pyproject-path)))
+    (and
+     (with-temp-buffer
+       (insert-file-contents pyproject-path)
+       (goto-char (point-min))
+       (re-search-forward "^\\[tool.poetry\\]$" nil t nil))
+     (executable-find "poetry"))))
 
 (defvar python-pyproject-cache nil)
 (defun python-parse-pyproject ()

      (let ((content
             (condition-case err
                 (with-temp-buffer
                   (funcall 'call-process "dasel" nil t nil "select" "-f" (python-pyproject-path) "-w" "json")
                   (goto-char (point-min))
                   (json-parse-buffer :object-type 'alist :array-type 'list))
               (error (error-message-string err) nil))))

      (flatten-list
       (cl-loop for req in requirements
                collect (if (string-match
                             (rx (or "-r" "-c" "--requirement" "--constraint")
                                 (+ space)
                                 (group (+ nonl)))
                             req)
                            (let ((file-path (match-string 1 req)))
                              (python-resolve-requirements
