veros_copy_setup.py 3.58 KB
Newer Older
mashun1's avatar
veros  
mashun1 committed
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/usr/bin/env python

import os
import shutil
import datetime
import functools
import textwrap
import importlib

import click
import entrypoints


SETUPDIR_ENVVAR = "VEROS_SETUP_DIR"
IGNORE_PATTERNS = ["__init__.py", "*.pyc", "__pycache__"]
SETUPS = {}

setup_dirs = []


for e in entrypoints.get_group_all("veros.setup_dirs"):
    # - hack to prevent actually importing plugins -
    # we can only find the location of top-level modules
    # so assume that foo.bar.baz always resolves to foo/bar/baz
    base_module, *submodules = e.module_name.split(".")

    modpath = os.path.join(os.path.dirname(importlib.util.find_spec(base_module).origin), *submodules)
    setup_dirs.append(modpath)

for setup_dir in os.environ.get(SETUPDIR_ENVVAR, "").split(";"):
    if os.path.isdir(setup_dir):
        setup_dirs.append(setup_dir)

# populate {setup_name: path} mapping
for setup_dir in setup_dirs:
    for setup in os.listdir(setup_dir):
        setup_path = os.path.join(setup_dir, setup)

        if not os.path.isdir(setup_path):
            continue

        if setup.startswith(("_", ".")):
            continue

        SETUPS[setup] = setup_path

SETUP_NAMES = sorted(SETUPS.keys())


def rewrite_main_file(target_file, setup_name):
    from veros import __version__ as veros_version

    current_date = datetime.datetime.utcnow()
    header_str = textwrap.dedent(
        f'''
        """
        This Veros setup file was generated by

           $ veros copy-setup {setup_name}

        on {current_date:%Y-%m-%d %H:%M:%S} UTC.
        """

        __VEROS_VERSION__ = {veros_version!r}

        if __name__ == "__main__":
            raise RuntimeError(
                "Veros setups cannot be executed directly. "
                f"Try `veros run {{__file__}}` instead."
            )

        # -- end of auto-generated header, original file below --
    '''
    ).strip()

    with open(target_file, "r") as f:
        orig_contents = f.readlines()

    shebang = None
    if orig_contents[0].startswith("#!"):
        shebang = orig_contents[0]
        orig_contents = orig_contents[1:]

    with open(target_file, "w") as f:
        if shebang is not None:
            f.write(shebang + "\n")

        f.write(header_str + "\n\n")
        f.writelines(orig_contents)


def copy_setup(setup, to=None):
    """Copy a standard setup to another directory.

    Available setups:

        {setups}

    Example:

        $ veros copy-setup global_4deg --to ~/veros-setups/4deg-lowfric

    Further directories containing setup templates can be added to this command
    via the {setup_envvar} environment variable.
    """
    if to is None:
        to = os.path.join(os.getcwd(), setup)

    if os.path.exists(to):
        raise RuntimeError("Target directory must not exist")

    to_parent = os.path.dirname(os.path.realpath(to))

    if not os.path.exists(to_parent):
        os.makedirs(to_parent)

    ignore = shutil.ignore_patterns(*IGNORE_PATTERNS)
    shutil.copytree(SETUPS[setup], to, ignore=ignore)

    main_setup_file = os.path.join(to, f"{setup}.py")
    rewrite_main_file(main_setup_file, setup)


copy_setup.__doc__ = copy_setup.__doc__.format(setups=", ".join(SETUP_NAMES), setup_envvar=SETUPDIR_ENVVAR)


@click.command("veros-copy-setup")
@click.argument("setup", type=click.Choice(SETUP_NAMES), metavar="SETUP")
@click.option(
    "--to",
    required=False,
    default=None,
    type=click.Path(dir_okay=False, file_okay=False, writable=True),
    help=("Target directory, must not exist " "(default: copy to current working directory)"),
)
@functools.wraps(copy_setup)
def cli(*args, **kwargs):
    copy_setup(*args, **kwargs)