aboutsummaryrefslogtreecommitdiff
path: root/venv/lib/python3.8/site-packages/dash/development/update_components.py
blob: 2789be22fd99f3a1c2037836b17f42f1da144ec3 (plain)
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import sys
import subprocess
import shlex
import os
import argparse
import shutil
import logging
import coloredlogs


class _CombinedFormatter(
    argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter
):
    pass


logger = logging.getLogger(__name__)
coloredlogs.install(
    fmt="%(asctime)s,%(msecs)03d %(levelname)s - %(message)s", datefmt="%H:%M:%S"
)

dest_dir_map = {
    "dash-core-components": "dcc",
    "dash-html-components": "html",
    "dash-table": "dash_table",
}


def status_print(msg, **kwargs):
    try:
        print(msg, **kwargs)
    except UnicodeEncodeError:
        print(msg.encode("ascii", "ignore"), **kwargs)


def bootstrap_components(components_source, concurrency, install_type):

    is_windows = sys.platform == "win32"

    source_glob = (
        components_source
        if components_source != "all"
        else "{dash-core-components,dash-html-components,dash-table}"
    )

    cmdstr = f"npx lerna exec --concurrency {concurrency} --scope='{source_glob}' -- npm {install_type}"
    cmd = shlex.split(cmdstr, posix=not is_windows)
    status_print(cmdstr)

    with subprocess.Popen(
        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=is_windows
    ) as proc:
        out, err = proc.communicate()
        status = proc.poll()

    if err:
        status_print(("🛑 " if status else "") + err.decode(), file=sys.stderr)

    if status or not out:
        status_print(
            f"🚨 Failed installing npm dependencies for component packages: {source_glob} (status={status}) 🚨",
            file=sys.stderr,
        )
        sys.exit(1)
    else:
        status_print(
            f"🟢 Finished installing npm dependencies for component packages: {source_glob} 🟢",
            file=sys.stderr,
        )


def build_components(components_source, concurrency):

    is_windows = sys.platform == "win32"

    source_glob = (
        components_source
        if components_source != "all"
        else "{dash-core-components,dash-html-components,dash-table}"
    )

    cmdstr = f"npx lerna exec --concurrency {concurrency} --scope='{source_glob}' -- npm run build"
    cmd = shlex.split(cmdstr, posix=not is_windows)
    status_print(cmdstr)

    with subprocess.Popen(
        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=is_windows
    ) as proc:
        out, err = proc.communicate()
        status = proc.poll()

    if err:
        status_print(("🛑 " if status else "") + err.decode(), file=sys.stderr)

    if status or not out:
        status_print(
            f"🚨 Finished updating component packages: {source_glob} (status={status}) 🚨",
            file=sys.stderr,
        )
        sys.exit(1)

    if "{" in source_glob:
        source_glob = source_glob.split("{")[1].split("}")[0]

    for package in source_glob.split(","):
        build_directory = os.path.join(
            "components", package, package.replace("-", "_").rstrip("/\\")
        )

        dest_dir = dest_dir_map.get(package) or package

        dest_path = os.path.join("dash", dest_dir)

        if not os.path.exists(dest_path):
            try:
                os.makedirs(dest_path)
            except OSError:
                logger.exception("🚨 Having issues manipulating %s", dest_path)
                sys.exit(1)

        if not os.path.exists(build_directory):
            status_print(
                "🚨 Could not locate build artifacts."
                + " Check that the npm build process completed"
                + f" successfully for package: {package} 🚨"
            )
            sys.exit(1)
        else:
            status_print(f"🚚 Moving build artifacts from {build_directory} to Dash 🚚")
            shutil.rmtree(dest_path)
            shutil.copytree(build_directory, dest_path)
            with open(os.path.join(dest_path, ".gitkeep"), "w", encoding="utf-8"):
                pass
            status_print(
                f"🟢 Finished moving build artifacts from {build_directory} to Dash 🟢"
            )


def cli():
    parser = argparse.ArgumentParser(
        prog="dash-update-components",
        formatter_class=_CombinedFormatter,
        description="Update the specified subcomponent libraries within Dash"
        " by copying over build artifacts, dependencies, and dependency metadata.",
    )
    parser.add_argument(
        "components_source",
        help="A glob string that matches the Dash component libraries to be updated"
        " (eg.'dash-table' // 'dash-core-components|dash-html-components' // 'all')."
        " The default argument is 'all'.",
        default="all",
    )
    parser.add_argument(
        "--concurrency",
        type=int,
        default=3,
        help="Maximum concurrent steps, up to 3 (ie all components in parallel)",
    )
    parser.add_argument(
        "--ci",
        help="For clean-install use '--ci True'",
        default="False",
    )

    args = parser.parse_args()

    if sys.platform == "win32":
        args.components_source = args.components_source.replace('"', "").replace(
            "'", ""
        )

    bootstrap_components(
        args.components_source, args.concurrency, "ci" if args.ci == "True" else "i"
    )
    build_components(args.components_source, args.concurrency)


if __name__ == "__main__":
    cli()