File: generate_requirements.py

package info (click to toggle)
skimage 0.26.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 32,720 kB
  • sloc: python: 61,600; cpp: 2,592; ansic: 1,591; xml: 1,342; javascript: 1,267; makefile: 135; sh: 16
file content (113 lines) | stat: -rwxr-xr-x 3,585 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#!/usr/bin/env python
"""Generate requirements/*.txt files from pyproject.toml.

Also builda a conda environment.yml
"""

import sys
import re
from pathlib import Path

try:  # standard module since Python 3.11
    import tomllib as toml
except ImportError:
    try:  # available for older Python via pip
        import tomli as toml
    except ImportError:
        sys.exit("Please install `tomli` first: `pip install tomli`")

script_pth = Path(__file__)
repo_dir = script_pth.parent.parent
script_relpth = script_pth.relative_to(repo_dir)
header = [
    f"# Generated via {script_relpth.as_posix()} and pre-commit hook.",
    "# Do not edit this file; modify pyproject.toml instead.",
]


def generate_requirement_file(name: str, req_list: list[str]) -> None:
    req_fname = repo_dir / "requirements" / f"{name}.txt"
    req_fname.write_text("\n".join(header + req_list) + "\n")


def generate_environment_yml(req_sections: dict[str, list[str]]) -> None:
    # Some packages in conda-forge have different names than they do on pypi
    # Also, some packages have a 'base' flavoured conda package which
    # doesn't install all their optional dependencies.
    rename_idx = {
        'build': 'python-build',
        'kaleido': 'python-kaleido',
        'sphinx_design': 'sphinx-design',
        'astropy': 'astropy-base',
        'matplotlib': 'matplotlib-base',
    }
    lines = ["name: skimage-dev", "channels:", "  - conda-forge", "dependencies:"]
    for section in req_sections:
        lines.append(f"  # {section}")
        for dep in req_sections[section]:
            # Remove optional specifiers such as `[parallel]`
            dep = re.sub('\\[.*?\\]', '', dep)

            # Remove platform specifiers such as `; sys_platform != "emscripten"`
            dep = re.sub('; .*', '', dep)

            pkgname = re.split('[>=]', dep)[0]
            dep = dep.replace(pkgname, rename_idx.get(pkgname, pkgname))
            if dep == "scikit-image":
                continue

            lines.append(f"  - {dep}")

    with open("environment.yml", "w") as f:
        f.writelines(f"{line}\n" for line in lines)


def expand_dependencies(
    dep_list: list[str],
    optional_dict: dict[str, list[str]],
    package_name: str = 'scikit-image',
) -> list[str]:
    """Explode dependencies with optional extras into a flat list.

    If `scikit-image[optional_group]` is used as a dependency itself, replace
    with the actual dependencies of `optional_group`.
    """
    exploded = []
    for dep in dep_list:
        if dep.startswith(package_name):
            extras = dep.split('[')[1].rstrip(']').split(',')
            exploded.extend(
                expand_dependencies(
                    optional_dict[extras[0]], optional_dict, package_name
                )
            )
        else:
            exploded.append(dep)
    return exploded


def main() -> None:
    pyproject = toml.loads((repo_dir / "pyproject.toml").read_text())

    generate_requirement_file("default", pyproject["project"]["dependencies"])

    for key, opt_list in pyproject["project"]["optional-dependencies"].items():
        generate_requirement_file(
            key,
            expand_dependencies(
                opt_list,
                pyproject["project"]["optional-dependencies"],
                package_name='scikit-image',
            ),
        )

    generate_environment_yml(
        {
            **{'base': pyproject["project"]["dependencies"]},
            **pyproject["project"]["optional-dependencies"],
        }
    )


if __name__ == "__main__":
    main()