diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c2df486e..b51417d1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,34 +17,34 @@ repos: - id: flake8 additional_dependencies: [flake8-typing-imports==1.6.0] - repo: https://github.com/pre-commit/mirrors-autopep8 - rev: v1.5 + rev: v1.5.1 hooks: - id: autopep8 - repo: https://github.com/pre-commit/pre-commit - rev: v2.1.1 + rev: v2.2.0 hooks: - id: validate_manifest - repo: https://github.com/asottile/pyupgrade - rev: v2.0.1 + rev: v2.1.0 hooks: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/asottile/reorder_python_imports - rev: v1.9.0 + rev: v2.1.0 hooks: - id: reorder-python-imports args: [--py3-plus] - repo: https://github.com/asottile/add-trailing-comma - rev: v1.5.0 + rev: v2.0.1 hooks: - id: add-trailing-comma args: [--py36-plus] - repo: https://github.com/asottile/setup-cfg-fmt - rev: v1.6.0 + rev: v1.8.2 hooks: - id: setup-cfg-fmt - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.761 + rev: v0.770 hooks: - id: mypy exclude: ^testing/resources/ diff --git a/pre_commit/languages/node.py b/pre_commit/languages/node.py index 79ff807a..9b636d30 100644 --- a/pre_commit/languages/node.py +++ b/pre_commit/languages/node.py @@ -1,4 +1,5 @@ import contextlib +import functools import os import sys from typing import Generator @@ -6,6 +7,7 @@ from typing import Sequence from typing import Tuple import pre_commit.constants as C +from pre_commit import parse_shebang from pre_commit.envcontext import envcontext from pre_commit.envcontext import PatchesT from pre_commit.envcontext import Var @@ -18,10 +20,22 @@ from pre_commit.util import cmd_output from pre_commit.util import cmd_output_b ENVIRONMENT_DIR = 'node_env' -get_default_version = helpers.basic_get_default_version healthy = helpers.basic_healthy +@functools.lru_cache(maxsize=1) +def get_default_version() -> str: + # nodeenv does not yet support `-n system` on windows + if sys.platform == 'win32': + return C.DEFAULT + # if node is already installed, we can save a bunch of setup time by + # using the installed version + elif all(parse_shebang.find_executable(exe) for exe in ('node', 'npm')): + return 'system' + else: + return C.DEFAULT + + def _envdir(prefix: Prefix, version: str) -> str: directory = helpers.environment_dir(ENVIRONMENT_DIR, version) return prefix.path(directory) diff --git a/tests/languages/node_test.py b/tests/languages/node_test.py new file mode 100644 index 00000000..fd300469 --- /dev/null +++ b/tests/languages/node_test.py @@ -0,0 +1,47 @@ +import sys +from unittest import mock + +import pytest + +import pre_commit.constants as C +from pre_commit import parse_shebang +from pre_commit.languages.node import get_default_version + + +ACTUAL_GET_DEFAULT_VERSION = get_default_version.__wrapped__ + + +@pytest.fixture +def is_linux(): + with mock.patch.object(sys, 'platform', 'linux'): + yield + + +@pytest.fixture +def is_win32(): + with mock.patch.object(sys, 'platform', 'win32'): + yield + + +@pytest.fixture +def find_exe_mck(): + with mock.patch.object(parse_shebang, 'find_executable') as mck: + yield mck + + +@pytest.mark.usefixtures('is_linux') +def test_sets_system_when_node_and_npm_are_available(find_exe_mck): + find_exe_mck.return_value = '/path/to/exe' + assert ACTUAL_GET_DEFAULT_VERSION() == 'system' + + +@pytest.mark.usefixtures('is_linux') +def test_uses_default_when_node_and_npm_are_not_available(find_exe_mck): + find_exe_mck.return_value = None + assert ACTUAL_GET_DEFAULT_VERSION() == C.DEFAULT + + +@pytest.mark.usefixtures('is_win32') +def test_sets_default_on_windows(find_exe_mck): + find_exe_mck.return_value = '/path/to/exe' + assert ACTUAL_GET_DEFAULT_VERSION() == C.DEFAULT diff --git a/tests/repository_test.py b/tests/repository_test.py index df7e7d1b..3c7a6372 100644 --- a/tests/repository_test.py +++ b/tests/repository_test.py @@ -131,9 +131,9 @@ def test_python_hook(tempdir_factory, store): def test_python_hook_default_version(tempdir_factory, store): # make sure that this continues to work for platforms where default # language detection does not work - with mock.patch.object( - python, 'get_default_version', return_value=C.DEFAULT, - ): + returns_default = mock.Mock(return_value=C.DEFAULT) + lang = languages['python']._replace(get_default_version=returns_default) + with mock.patch.dict(languages, python=lang): test_python_hook(tempdir_factory, store) @@ -243,6 +243,15 @@ def test_run_a_node_hook(tempdir_factory, store): ) +def test_run_a_node_hook_default_version(tempdir_factory, store): + # make sure that this continues to work for platforms where node is not + # installed at the system + returns_default = mock.Mock(return_value=C.DEFAULT) + lang = languages['node']._replace(get_default_version=returns_default) + with mock.patch.dict(languages, node=lang): + test_run_a_node_hook(tempdir_factory, store) + + def test_run_versioned_node_hook(tempdir_factory, store): _test_hook_repo( tempdir_factory, store, 'node_versioned_hooks_repo',