File: julia.py

package info (click to toggle)
pre-commit 4.5.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,452 kB
  • sloc: python: 14,482; sh: 87; makefile: 4
file content (133 lines) | stat: -rw-r--r-- 4,432 bytes parent folder | download
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
from __future__ import annotations

import contextlib
import os
import shutil
from collections.abc import Generator
from collections.abc import Sequence

from pre_commit import lang_base
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import UNSET
from pre_commit.prefix import Prefix
from pre_commit.util import cmd_output_b

ENVIRONMENT_DIR = 'juliaenv'
health_check = lang_base.basic_health_check
get_default_version = lang_base.basic_get_default_version


def run_hook(
        prefix: Prefix,
        entry: str,
        args: Sequence[str],
        file_args: Sequence[str],
        *,
        is_local: bool,
        require_serial: bool,
        color: bool,
) -> tuple[int, bytes]:
    # `entry` is a (hook-repo relative) file followed by (optional) args, e.g.
    # `bin/id.jl` or `bin/hook.jl --arg1 --arg2` so we
    # 1) shell parse it and join with args with hook_cmd
    # 2) prepend the hooks prefix path to the first argument (the file), unless
    #    it is a local script
    # 3) prepend `julia` as the interpreter

    cmd = lang_base.hook_cmd(entry, args)
    script = cmd[0] if is_local else prefix.path(cmd[0])
    cmd = ('julia', '--startup-file=no', script, *cmd[1:])
    return lang_base.run_xargs(
        cmd,
        file_args,
        require_serial=require_serial,
        color=color,
    )


def get_env_patch(target_dir: str, version: str) -> PatchesT:
    return (
        ('JULIA_LOAD_PATH', target_dir),
        # May be set, remove it to not interfer with LOAD_PATH
        ('JULIA_PROJECT', UNSET),
    )


@contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None]:
    envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
    with envcontext(get_env_patch(envdir, version)):
        yield


def install_environment(
        prefix: Prefix,
        version: str,
        additional_dependencies: Sequence[str],
) -> None:
    envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
    with in_env(prefix, version):
        # TODO: Support language_version with juliaup similar to rust via
        # rustup
        # if version != 'system':
        #     ...

        # Copy Project.toml to hook env if it exist
        os.makedirs(envdir, exist_ok=True)
        project_names = ('JuliaProject.toml', 'Project.toml')
        project_found = False
        for project_name in project_names:
            project_file = prefix.path(project_name)
            if not os.path.isfile(project_file):
                continue
            shutil.copy(project_file, envdir)
            project_found = True
            break

        # If no project file was found we create an empty one so that the
        # package manager doesn't error
        if not project_found:
            open(os.path.join(envdir, 'Project.toml'), 'a').close()

        # Copy Manifest.toml to hook env if it exists
        manifest_names = ('JuliaManifest.toml', 'Manifest.toml')
        for manifest_name in manifest_names:
            manifest_file = prefix.path(manifest_name)
            if not os.path.isfile(manifest_file):
                continue
            shutil.copy(manifest_file, envdir)
            break

        # Julia code to instantiate the hook environment
        julia_code = """
        @assert length(ARGS) > 0
        hook_env = ARGS[1]
        deps = join(ARGS[2:end], " ")

        # We prepend @stdlib here so that we can load the package manager even
        # though `get_env_patch` limits `JULIA_LOAD_PATH` to just the hook env.
        pushfirst!(LOAD_PATH, "@stdlib")
        using Pkg
        popfirst!(LOAD_PATH)

        # Instantiate the environment shipped with the hook repo. If we have
        # additional dependencies we disable precompilation in this step to
        # avoid double work.
        precompile = isempty(deps) ? "1" : "0"
        withenv("JULIA_PKG_PRECOMPILE_AUTO" => precompile) do
            Pkg.instantiate()
        end

        # Add additional dependencies (with precompilation)
        if !isempty(deps)
            withenv("JULIA_PKG_PRECOMPILE_AUTO" => "1") do
                Pkg.REPLMode.pkgstr("add " * deps)
            end
        end
        """
        cmd_output_b(
            'julia', '--startup-file=no', '-e', julia_code, '--', envdir,
            *additional_dependencies,
            cwd=prefix.prefix_dir,
        )