From 54331dca6fcfff1a06c43defb29b395898c65ce8 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Mon, 17 Jan 2022 15:46:36 -0500 Subject: [PATCH] get lua version from luarocks itself --- pre_commit/languages/lua.py | 108 ++++++++---------------------------- tests/languages/lua_test.py | 55 ------------------ tests/repository_test.py | 10 ++-- 3 files changed, 28 insertions(+), 145 deletions(-) delete mode 100644 tests/languages/lua_test.py diff --git a/pre_commit/languages/lua.py b/pre_commit/languages/lua.py index ae322279b..f69993712 100644 --- a/pre_commit/languages/lua.py +++ b/pre_commit/languages/lua.py @@ -1,6 +1,6 @@ import contextlib import os -import re +import sys from typing import Generator from typing import Sequence from typing import Tuple @@ -11,7 +11,6 @@ from pre_commit.envcontext import Var from pre_commit.hook import Hook from pre_commit.languages import helpers -from pre_commit.parse_shebang import find_executable from pre_commit.prefix import Prefix from pre_commit.util import clean_path_on_failure from pre_commit.util import cmd_output @@ -21,95 +20,38 @@ healthy = helpers.basic_healthy -def _find_lua(language_version: str) -> str: # pragma: win32 no cover - """Find a lua executable. - - Lua doesn't always have a plain `lua` executable. - Some OS vendors will ship the binary as `lua#.#` (e.g., lua5.3) - so discovery is needed to find a valid executable. - """ - if language_version == C.DEFAULT: - choices = ['lua'] - for path in os.environ.get('PATH', '').split(os.pathsep): - try: - candidates = os.listdir(path) - except OSError: - # Invalid path on PATH or lacking permissions. - continue - - for candidate in candidates: - # The Lua executable might look like `lua#.#` or `lua-#.#`. - if re.search(r'^lua[-]?\d+\.\d+', candidate): - choices.append(candidate) - else: - # Prefer version specific executables first if available. - # This should avoid the corner case where a user requests a language - # version, gets a `lua` executable, but that executable is actually - # for a different version and package.path would patch LUA_PATH - # incorrectly. - choices = [f'lua{language_version}', 'lua-{language_version}', 'lua'] - - found_exes = [exe for exe in choices if find_executable(exe)] - if found_exes: - return found_exes[0] - - raise ValueError( - 'No lua executable found on the system paths ' - f'for {language_version} version.', - ) +def _get_lua_version() -> str: # pragma: win32 no cover + """Get the Lua version used in file paths.""" + _, stdout, _ = cmd_output('luarocks', 'config', '--lua-ver') + return stdout.strip() -def _get_lua_path_version( - lua_executable: str, -) -> str: # pragma: win32 no cover - """Get the Lua version used in file paths.""" - # This could sniff out from _VERSION, but checking package.path should - # provide an answer for *exactly* where lua is looking for packages. - _, stdout, _ = cmd_output(lua_executable, '-e', 'print(package.path)') - sep = os.sep if os.name != 'nt' else os.sep * 2 - match = re.search(fr'{sep}lua{sep}(.*?){sep}', stdout) - if match: - return match[1] - - raise ValueError('Cannot determine lua version for file paths.') - - -def get_env_patch( - env: str, language_version: str, -) -> PatchesT: # pragma: win32 no cover - lua = _find_lua(language_version) - version = _get_lua_path_version(lua) +def get_env_patch(d: str) -> PatchesT: # pragma: win32 no cover + version = _get_lua_version() + so_ext = 'dll' if sys.platform == 'win32' else 'so' return ( - ('PATH', (os.path.join(env, 'bin'), os.pathsep, Var('PATH'))), + ('PATH', (os.path.join(d, 'bin'), os.pathsep, Var('PATH'))), ( 'LUA_PATH', ( - os.path.join(env, 'share', 'lua', version, '?.lua;'), - os.path.join(env, 'share', 'lua', version, '?', 'init.lua;;'), + os.path.join(d, 'share', 'lua', version, '?.lua;'), + os.path.join(d, 'share', 'lua', version, '?', 'init.lua;;'), ), ), ( - 'LUA_CPATH', ( - os.path.join(env, 'lib', 'lua', version, '?.so;;'), - ), + 'LUA_CPATH', + (os.path.join(d, 'lib', 'lua', version, f'?.{so_ext};;'),), ), ) -def _envdir(prefix: Prefix, version: str) -> str: # pragma: win32 no cover - directory = helpers.environment_dir(ENVIRONMENT_DIR, version) +def _envdir(prefix: Prefix) -> str: # pragma: win32 no cover + directory = helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT) return prefix.path(directory) @contextlib.contextmanager # pragma: win32 no cover -def in_env( - prefix: Prefix, - language_version: str, -) -> Generator[None, None, None]: - with envcontext( - get_env_patch( - _envdir(prefix, language_version), language_version, - ), - ): +def in_env(prefix: Prefix) -> Generator[None, None, None]: + with envcontext(get_env_patch(_envdir(prefix))): yield @@ -120,19 +62,17 @@ def install_environment( ) -> None: # pragma: win32 no cover helpers.assert_version_default('lua', version) - envdir = _envdir(prefix, version) + envdir = _envdir(prefix) with clean_path_on_failure(envdir): - with in_env(prefix, version): + with in_env(prefix): # luarocks doesn't bootstrap a tree prior to installing # so ensure the directory exists. os.makedirs(envdir, exist_ok=True) - make_cmd = ['luarocks', '--tree', envdir, 'make'] - # Older luarocks (e.g., 2.4.2) expect the rockspec as an argument. - filenames = prefix.star('.rockspec') - make_cmd.extend(filenames[:1]) - - helpers.run_setup_cmd(prefix, tuple(make_cmd)) + # Older luarocks (e.g., 2.4.2) expect the rockspec as an arg + for rockspec in prefix.star('.rockspec'): + make_cmd = ('luarocks', '--tree', envdir, 'make', rockspec) + helpers.run_setup_cmd(prefix, make_cmd) # luarocks can't install multiple packages at once # so install them individually. @@ -146,5 +86,5 @@ def run_hook( file_args: Sequence[str], color: bool, ) -> Tuple[int, bytes]: # pragma: win32 no cover - with in_env(hook.prefix, hook.language_version): + with in_env(hook.prefix): return helpers.run_xargs(hook, hook.cmd, file_args, color=color) diff --git a/tests/languages/lua_test.py b/tests/languages/lua_test.py deleted file mode 100644 index fba23b22f..000000000 --- a/tests/languages/lua_test.py +++ /dev/null @@ -1,55 +0,0 @@ -import os -from unittest import mock - -import pytest - -import pre_commit.constants as C -from pre_commit.languages import lua -from testing.util import xfailif_windows - - -@pytest.mark.parametrize( - 'lua_name', ('lua', 'lua5.4', 'lua-5.4', 'lua5.4.exe'), -) -def test_find_lua(tmp_path, lua_name): - """The language support can find common lua executable names.""" - lua_file = tmp_path / lua_name - lua_file.touch(0o555) - with mock.patch.dict(os.environ, {'PATH': str(tmp_path)}): - lua_executable = lua._find_lua(C.DEFAULT) - assert lua_name in lua_executable - - -def test_find_lua_language_version(tmp_path): - """Language discovery can find a specific version.""" - lua_file = tmp_path / 'lua5.99' - lua_file.touch(0o555) - with mock.patch.dict(os.environ, {'PATH': str(tmp_path)}): - lua_executable = lua._find_lua('5.99') - assert 'lua5.99' in lua_executable - - -@pytest.mark.parametrize( - ('invalid', 'mode'), - ( - ('foobar', 0o555), - ('luac', 0o555), - # Windows doesn't respect the executable checking. - pytest.param('lua5.4', 0o444, marks=xfailif_windows), - ), -) -def test_find_lua_fail(tmp_path, invalid, mode): - """No lua executable on the system will fail.""" - non_lua_file = tmp_path / invalid - non_lua_file.touch(mode) - with mock.patch.dict(os.environ, {'PATH': str(tmp_path)}): - with pytest.raises(ValueError): - lua._find_lua(C.DEFAULT) - - -@mock.patch.object(lua, 'cmd_output') -def test_bad_package_path(mock_cmd_output): - """A package path missing path info returns an unknown version.""" - mock_cmd_output.return_value = (0, '', '') - with pytest.raises(ValueError): - lua._get_lua_path_version('lua') diff --git a/tests/repository_test.py b/tests/repository_test.py index 5f5e17e55..8569ba96c 100644 --- a/tests/repository_test.py +++ b/tests/repository_test.py @@ -17,7 +17,6 @@ from pre_commit.hook import Hook from pre_commit.languages import golang from pre_commit.languages import helpers -from pre_commit.languages import lua from pre_commit.languages import node from pre_commit.languages import python from pre_commit.languages import ruby @@ -1142,18 +1141,17 @@ def test_lua_hook(tempdir_factory, store): @skipif_cant_run_lua # pragma: win32 no cover def test_local_lua_additional_dependencies(store): - lua_entry = lua._find_lua(C.DEFAULT) config = { 'repo': 'local', 'hooks': [{ 'id': 'local-lua', 'name': 'local-lua', - 'entry': lua_entry, + 'entry': 'luacheck --version', 'language': 'lua', - 'args': ['-e', 'require "inspect"; print("hello world")'], - 'additional_dependencies': ['inspect'], + 'additional_dependencies': ['luacheck'], }], } hook = _get_hook(config, store, 'local-lua') ret, out = _hook_run(hook, (), color=False) - assert (ret, _norm_out(out)) == (0, b'hello world\n') + assert b'Luacheck' in out + assert ret == 0