| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| 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', 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', '-e', julia_code, '--', envdir, *additional_dependencies, | ||
| cwd=prefix.prefix_dir, | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from pre_commit.languages import julia | ||
| from testing.language_helpers import run_language | ||
| from testing.util import cwd | ||
|
|
||
|
|
||
| def _make_hook(tmp_path, julia_code): | ||
| src_dir = tmp_path.joinpath('src') | ||
| src_dir.mkdir() | ||
| src_dir.joinpath('main.jl').write_text(julia_code) | ||
| tmp_path.joinpath('Project.toml').write_text( | ||
| '[deps]\n' | ||
| 'Example = "7876af07-990d-54b4-ab0e-23690620f79a"\n', | ||
| ) | ||
|
|
||
|
|
||
| def test_julia_hook(tmp_path): | ||
| code = """ | ||
| using Example | ||
| function main() | ||
| println("Hello, world!") | ||
| end | ||
| main() | ||
| """ | ||
| _make_hook(tmp_path, code) | ||
| expected = (0, b'Hello, world!\n') | ||
| assert run_language(tmp_path, julia, 'src/main.jl') == expected | ||
|
|
||
|
|
||
| def test_julia_hook_manifest(tmp_path): | ||
| code = """ | ||
| using Example | ||
| println(pkgversion(Example)) | ||
| """ | ||
| _make_hook(tmp_path, code) | ||
|
|
||
| tmp_path.joinpath('Manifest.toml').write_text( | ||
| 'manifest_format = "2.0"\n\n' | ||
| '[[deps.Example]]\n' | ||
| 'git-tree-sha1 = "11820aa9c229fd3833d4bd69e5e75ef4e7273bf1"\n' | ||
| 'uuid = "7876af07-990d-54b4-ab0e-23690620f79a"\n' | ||
| 'version = "0.5.4"\n', | ||
| ) | ||
| expected = (0, b'0.5.4\n') | ||
| assert run_language(tmp_path, julia, 'src/main.jl') == expected | ||
|
|
||
|
|
||
| def test_julia_hook_args(tmp_path): | ||
| code = """ | ||
| function main(argv) | ||
| foreach(println, argv) | ||
| end | ||
| main(ARGS) | ||
| """ | ||
| _make_hook(tmp_path, code) | ||
| expected = (0, b'--arg1\n--arg2\n') | ||
| assert run_language( | ||
| tmp_path, julia, 'src/main.jl --arg1 --arg2', | ||
| ) == expected | ||
|
|
||
|
|
||
| def test_julia_hook_additional_deps(tmp_path): | ||
| code = """ | ||
| using TOML | ||
| function main() | ||
| project_file = Base.active_project() | ||
| dict = TOML.parsefile(project_file) | ||
| for (k, v) in dict["deps"] | ||
| println(k, " = ", v) | ||
| end | ||
| end | ||
| main() | ||
| """ | ||
| _make_hook(tmp_path, code) | ||
| deps = ('TOML=fa267f1f-6049-4f14-aa54-33bafae1ed76',) | ||
| ret, out = run_language(tmp_path, julia, 'src/main.jl', deps=deps) | ||
| assert ret == 0 | ||
| assert b'Example = 7876af07-990d-54b4-ab0e-23690620f79a' in out | ||
| assert b'TOML = fa267f1f-6049-4f14-aa54-33bafae1ed76' in out | ||
|
|
||
|
|
||
| def test_julia_repo_local(tmp_path): | ||
| env_dir = tmp_path.joinpath('envdir') | ||
| env_dir.mkdir() | ||
| local_dir = tmp_path.joinpath('local') | ||
| local_dir.mkdir() | ||
| local_dir.joinpath('local.jl').write_text( | ||
| 'using TOML; foreach(println, ARGS)', | ||
| ) | ||
| with cwd(local_dir): | ||
| deps = ('TOML=fa267f1f-6049-4f14-aa54-33bafae1ed76',) | ||
| expected = (0, b'--local-arg1\n--local-arg2\n') | ||
| assert run_language( | ||
| env_dir, julia, 'local.jl --local-arg1 --local-arg2', | ||
| deps=deps, is_local=True, | ||
| ) == expected | ||
| Back | FazBrowse Home | New Git URL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Qualitymaybe I just don't know enough about julia but from a quick search it seems like TOML is a stdlib library? does it still need to be installed?
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityIt is a standard library, and it can be loaded at top level (e.g. in the Julia REPL) by default, since the @stdlib entry is included in the default LOAD_PATH. However, for Julia packages it is required (so people are used to explicitly adding also stdlibs), and for full reproducibility it is recommended. There is also ongoing work to separate stdlibs to make them less tied to the Julia version, so then they would behave more like normal packages that can be upgraded separately etc.
We can of course make the choice to include @stdlib in the load path for julia hooks, but I would argue that it is better to be explicit with the dependencies in this case.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.