;;; Manifest to build the latest Python packages with Python 3.8.
(use-modules (guix packages)           ; for "package", "package-arguments"...
             (guix build-system python)
             (guix utils)
             (ice-9 match))

;; The list of Python packages (or rather specifications) that we want
;; to build with an older Python.
(define packages
  (list "python-numpy"
        "python-pytorch"
        "python-matplotlib"
        "python-scipy"
        "python-scikit-learn"
        "python-seaborn"))

(define old-python
  (specification->package "python@3.8"))

(define old-python-wrapper
  ;; We use wrap-python3 to create a "python" executable.  Python
  ;; itself only comes with "python3".  Python 3.8 is available in the
  ;; guix-past channel.
  ((@@ (gnu packages python) wrap-python3) old-python))

(define (python-package? package)
  (eq? python-build-system
       (package-build-system package)))

(define (replace-python-in-inputs inputs python)
  "Some packages have an explicit Python in the inputs, or use a
separate output of the Python package."
  (map (match-lambda
         (("python" package out) `("python" ,python ,out))
         (("python" package) python)
         (anything anything))
       inputs))

(define (package-with-different-python pkg python-wrapper python)
  "Return a new package based on PKG that uses PYTHON during the build."
  (package/inherit pkg
    (arguments
     (ensure-keyword-arguments
      (package-arguments pkg)
      `(#:python ,python-wrapper
        #:tests? #false)))  ;running tests is slow, so why bother?
    (native-inputs
     (replace-python-in-inputs (package-native-inputs pkg) python))
    (inputs
     (replace-python-in-inputs (package-inputs pkg) python))
    (propagated-inputs
     (replace-python-in-inputs (package-propagated-inputs pkg) python))))

;; This is a recursive package transformer.  When given a package
;; "pkg" it checks if it is a Python package by looking at its build
;; system; if that is the case, it will return a package variant that
;; is built with the old Python.  It does this recursively, so all
;; dependencies are also modified.
(define use-old-python
  (package-mapping
   (lambda (pkg)
     (if (python-package? pkg)
         (let ((modified (package-with-different-python pkg
                                                        old-python-wrapper
                                                        old-python)))
           (cond
            ;; This package also needs a newer version of setuptools;
            ;; Python 3.8 comes with an older version of setuptools.
            ((string=? (package-name pkg)
                       "python-importlib-metadata")
             (package/inherit modified
               (native-inputs
                (modify-inputs (package-native-inputs modified)
                  (prepend (specification->package "python-setuptools"))))))
            ;; The other packages don't need special treatment.
            (else modified)))
         pkg))
   ;; Stop recursion when we hit a package that is not considered a
   ;; Python package.  This could be a package that uses Python or
   ;; Python modules as inputs but does not itself produce a Python
   ;; module.  This is to avoid needlessly rebuilding big things like
   ;; GTK+.
   (negate python-package?)
   #:deep? #false))

;; Apply the transformer to the list of packages.
(define python-packages-with-old-python
  (map (compose use-old-python specification->package)
       packages))

;; Build a manifest from the list of modified packages and the old
;; Python variant itself.
(packages->manifest
 (cons old-python
       python-packages-with-old-python))

Generated by Ricardo Wurmus using scpaste at Tue May 31 13:08:23 2022. CEST. (original)