summaryrefslogtreecommitdiff
path: root/guix/import/pypi.scm
diff options
context:
space:
mode:
authorLars-Dominik Braun <lars@6xq.net>2024-12-15 13:22:00 +0100
committerSharlatan Hellseher <sharlatanus@gmail.com>2024-12-16 19:27:43 +0000
commit8bb3bb19c2cabdbea9ece2358c57809c2c4b1561 (patch)
treed6aa6b50562de231c50a374b57c9a9d59b518604 /guix/import/pypi.scm
parentf2b7e8f762cc6eb00b797a171adbb422a6d2a3be (diff)
import: pypi: Support extracting dependencies from pyproject.toml.
* guix/import/pypi.scm (guess-requirements): Support extracting dependencies from pyproject.toml. * tests/pypi.scm: ("pypi->guix-package, no requires.txt, but wheel."): Renamed from "pypi->guix-package, wheels", remove requires.txt file, because the current implementation cannot detect invalid files. ("pypi->guix-package, no usable requirement file, no wheel."): Renamed from "pypi->guix-package, no usable requirement file.". (test-pyproject.toml): New variable. ("pypi->guix-package, no wheel, no requires.txt, but pyproject.toml"): New test. ("pypi->guix-package, no wheel, but requires.txt and pyproject.toml"): Ditto. Change-Id: Ib525750eb6ff4139a8209420042b28ae3c850764 Reviewed-by: Ludovic Courtès <ludo@gnu.org> Signed-off-by: Sharlatan Hellseher <sharlatanus@gmail.com>
Diffstat (limited to 'guix/import/pypi.scm')
-rw-r--r--guix/import/pypi.scm72
1 files changed, 55 insertions, 17 deletions
diff --git a/guix/import/pypi.scm b/guix/import/pypi.scm
index 479b4684a3..1fd3481a09 100644
--- a/guix/import/pypi.scm
+++ b/guix/import/pypi.scm
@@ -57,6 +57,7 @@
#:use-module (guix import utils)
#:use-module (guix import json)
#:use-module (json)
+ #:use-module (guix build toml)
#:use-module (guix packages)
#:use-module (guix upstream)
#:use-module ((guix licenses) #:prefix license:)
@@ -386,7 +387,42 @@ be extracted in a temporary directory."
(if wheel-url
(and (url-fetch wheel-url temp)
(read-wheel-metadata temp))
- #f))))
+ (list '() '())))))
+
+ (define (guess-requirements-from-pyproject.toml dir)
+ (let* ((pyproject.toml-files (find-files dir (lambda (abs-file-name _)
+ (string-match "/pyproject.toml$"
+ abs-file-name))))
+ (pyproject.toml (match pyproject.toml-files
+ (()
+ (warning (G_ "Cannot guess requirements from \
+pyproject.toml file, because it does not exist.~%"))
+ '())
+ (else (parse-toml-file (first pyproject.toml-files)))))
+ (pyproject-build-requirements
+ (or (recursive-assoc-ref pyproject.toml '("build-system" "requires")) '()))
+ (pyproject-dependencies
+ (or (recursive-assoc-ref pyproject.toml '("project" "dependencies")) '()))
+ ;; This is more of a convention, since optional-dependencies is a table of arbitrary values.
+ (pyproject-test-dependencies
+ (or (recursive-assoc-ref pyproject.toml '("project" "optional-dependencies" "test")) '())))
+ (if (null? pyproject.toml)
+ #f
+ (list (map specification->requirement-name pyproject-dependencies)
+ (map specification->requirement-name
+ (append pyproject-build-requirements
+ pyproject-test-dependencies))))))
+
+ (define (guess-requirements-from-requires.txt dir)
+ (let ((requires.txt-files (find-files dir (lambda (abs-file-name _)
+ (string-match "\\.egg-info/requires.txt$"
+ abs-file-name)))))
+ (match requires.txt-files
+ (()
+ (warning (G_ "Cannot guess requirements from source archive: \
+no requires.txt file found.~%"))
+ #f)
+ (else (parse-requires.txt (first requires.txt-files))))))
(define (guess-requirements-from-source)
;; Return the package's requirements by guessing them from the source.
@@ -398,27 +434,29 @@ be extracted in a temporary directory."
(if (string=? "zip" (file-extension source-url))
(invoke "unzip" archive "-d" dir)
(invoke "tar" "xf" archive "-C" dir)))
- (let ((requires.txt-files
- (find-files dir (lambda (abs-file-name _)
- (string-match "\\.egg-info/requires.txt$"
- abs-file-name)))))
- (match requires.txt-files
- (()
- (warning (G_ "Cannot guess requirements from source archive:\
- no requires.txt file found.~%"))
- (list '() '()))
- (else (parse-requires.txt (first requires.txt-files)))))))
+ (list (guess-requirements-from-pyproject.toml dir)
+ (guess-requirements-from-requires.txt dir))))
(begin
(warning (G_ "Unsupported archive format; \
cannot determine package dependencies from source archive: ~a~%")
(basename source-url))
- (list '() '()))))
+ (list #f #f))))
+
+ (define (merge a b)
+ "Given lists A and B with two iteams each, combine A1 and B1, as well as A2 and B2."
+ (match (list a b)
+ (((first-propagated first-native) (second-propagated second-native))
+ (list (append first-propagated second-propagated) (append first-native second-native)))))
- ;; First, try to compute the requirements using the wheel, else, fallback to
- ;; reading the "requires.txt" from the egg-info directory from the source
- ;; archive.
- (or (guess-requirements-from-wheel)
- (guess-requirements-from-source)))
+ ;; requires.txt and the metadata of a wheel contain redundant information,
+ ;; so fetch only one of them, preferring requires.txt from the source
+ ;; distribution, which we always fetch, since the source tarball also
+ ;; contains pyproject.toml.
+ (match (guess-requirements-from-source)
+ ((from-pyproject.toml #f)
+ (merge (or from-pyproject.toml '(() ())) (or (guess-requirements-from-wheel) '(() ()))))
+ ((from-pyproject.toml from-requires.txt)
+ (merge (or from-pyproject.toml '(() ())) from-requires.txt))))
(define (compute-inputs source-url wheel-url archive)
"Given the SOURCE-URL and WHEEL-URL of an already downloaded ARCHIVE, return