Skip to content

Commit 6fc06f6

Browse files
authored
Remove file stem constraints from venv install and lock (#35)
Closes #34
2 parents a23dd79 + 2438f17 commit 6fc06f6

File tree

8 files changed

+93
-53
lines changed

8 files changed

+93
-53
lines changed

src/venv-cli/venv.sh

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ venv::_check_if_help_requested() {
5252
venv::_check_install_requirements_file() {
5353
### Check whether the first argument matches the pattern for a requirements file.
5454
### If not, raises error (silently if called with '-q')
55-
local file_pattern="^.*?requirements\.(txt|lock)$"
55+
local file_pattern="^.+\.(txt|lock)$"
5656
if [[ ! "$1" =~ $file_pattern ]]; then
5757
local message=""
5858
if [ "$2" != "-q" ]; then
59-
message="Input file name must have format '*requirements.txt' or '*requirements.lock', was '$1'"
59+
message="Input file name must end with '.txt' or '.lock', was '$1'"
6060
fi
6161
venv::raise "${message}"
6262
return "$?"
@@ -66,11 +66,11 @@ venv::_check_install_requirements_file() {
6666
venv::_check_lock_requirements_file() {
6767
### Check whether the first argument matches the pattern for a lock file.
6868
### If not, raises error (silently if called with '-q')
69-
local file_pattern="^.*?requirements\.lock$"
69+
local file_pattern="^.+\.lock$"
7070
if [[ ! "$1" =~ $file_pattern ]]; then
7171
local message=""
7272
if [ "$2" != "-q" ]; then
73-
message="Lock file name must have format '*requirements.lock', was '$1'"
73+
message="Lock file name must end with '.lock', was '$1'"
7474
fi
7575
venv::raise "${message}"
7676
return "$?"
@@ -227,7 +227,7 @@ venv::install() {
227227
echo "This step is skipped if '--skip-lock' or '-s' is specified, or when installing"
228228
echo "directly from a .lock-file."
229229
echo
230-
echo "The <requirements file> must be in the form '*requirements.[txt|lock]'."
230+
echo "The <requirements file> must have file extension '.txt' or '.lock'."
231231
echo "If no arguments are passed, a default file name of 'requirements.txt'"
232232
echo "will be used."
233233
echo
@@ -238,7 +238,7 @@ venv::install() {
238238
echo
239239
echo "$ venv install dev-requirements.txt"
240240
echo
241-
echo "$ venv install requirements.txt --skip-lock|-s --no-cache"
241+
echo "$ venv install requirements/dev-all.txt --skip-lock|-s --no-cache"
242242
return "${_success}"
243243
fi
244244

@@ -291,7 +291,7 @@ venv::lock() {
291291
echo "venv lock [<lock file>|<lock file prefix>]"
292292
echo
293293
echo "Lock all installed package versions and write them to <lock file>."
294-
echo "The <lock file> must be in the form '*requirements.lock'."
294+
echo "The <lock file> must have the file extension '.lock'."
295295
echo
296296
echo "If <lock file prefix> is specified instead, locks the requirements to"
297297
echo "a file called '<lock file prefix>-requirements.lock', e.g."

tests/helpers.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from pathlib import Path
66
from typing import Callable, Optional
77

8-
from tests.types import P, RawFilesDict, RequirementsBase, RequirementsDict
8+
from tests.types import P, RawFilesDict, RequirementsDict, RequirementsStem
99

1010
RequirementFiles = dict[str, Path]
1111
current_python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
@@ -54,13 +54,13 @@ def run_command(
5454

5555

5656
def collect_requirements(
57-
func: Callable[P, tuple[RawFilesDict, RequirementsBase]]
58-
) -> Callable[P, tuple[RequirementsDict, RequirementsBase]]:
57+
func: Callable[P, tuple[RawFilesDict, RequirementsStem]]
58+
) -> Callable[P, tuple[RequirementsDict, RequirementsStem]]:
5959
@wraps(func)
60-
def wrapper(*args: P.args, **kwargs: P.kwargs) -> tuple[RequirementsDict, RequirementsBase]:
61-
files_dict, requirements_base = func(*args, **kwargs)
60+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> tuple[RequirementsDict, RequirementsStem]:
61+
files_dict, requirements_stem = func(*args, **kwargs)
6262
requirements_dict = {filename: "\n".join(requirements) for filename, requirements in files_dict.items()}
63-
return requirements_dict, requirements_base
63+
return requirements_dict, requirements_stem
6464

6565
return wrapper
6666

tests/test_venv_install.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from tests.helpers import run_command, write_files
1010
from tests.test_venv_install_cases import CasesVenvInstallRequirementstxt, CasesVenvInstallWithLock
11-
from tests.types import RequirementsBase, RequirementsDict
11+
from tests.types import RequirementsDict, RequirementsStem
1212

1313
_package_name_regex = re.compile(r"^([a-zA-Z0-9_-]+)\b")
1414

@@ -30,24 +30,24 @@ def test_venv_install_not_activated(tmp_path: Path, monkeypatch: pytest.MonkeyPa
3030

3131

3232
@pytest.mark.order(after="test_venv_activate.py::test_venv_activate")
33-
@parametrize_with_cases(argnames=["files", "requirements_base"], cases=CasesVenvInstallRequirementstxt)
33+
@parametrize_with_cases(argnames=["files", "requirements_stem"], cases=CasesVenvInstallRequirementstxt)
3434
@pytest.mark.parametrize("use_file_name", [True, False])
3535
def test_venv_install_requirements(
3636
files: RequirementsDict,
37-
requirements_base: RequirementsBase,
37+
requirements_stem: RequirementsStem,
3838
use_file_name: bool,
3939
tmp_path: Path,
4040
capfd: pytest.CaptureFixture,
4141
):
4242
write_files(files=files, dir=tmp_path)
4343

4444
# Install the requirements
45-
if not (requirements_base is RequirementsBase.requirements or use_file_name):
46-
pytest.skip(f"Empty file name case only valid for requirements.txt, not {requirements_base}")
45+
if not (requirements_stem is RequirementsStem.requirements or use_file_name):
46+
pytest.skip(f"Empty file name case only valid for requirements.txt, not {requirements_stem.value}.txt")
4747

4848
install_file_name = ""
4949
if use_file_name:
50-
install_file_name = f"{requirements_base.value}.txt"
50+
install_file_name = f"{requirements_stem.value}.txt"
5151

5252
run_command(
5353
f"venv install {install_file_name} --skip-lock",
@@ -57,7 +57,7 @@ def test_venv_install_requirements(
5757

5858
# Check pip install log output
5959
output: str = capfd.readouterr().out
60-
assert f"Installing requirements from {requirements_base.value}.txt" in output
60+
assert f"Installing requirements from {requirements_stem.value}.txt" in output
6161

6262
installed_line = [line for line in output.splitlines() if line.startswith("Successfully installed")][0]
6363
requirement_lines = chain.from_iterable(contents.splitlines() for contents in files.values())
@@ -70,16 +70,16 @@ def test_venv_install_requirements(
7070

7171

7272
@pytest.mark.order(after="test_venv_activate.py::test_venv_activate")
73-
@parametrize_with_cases(argnames=["files", "requirements_base"], cases=CasesVenvInstallWithLock)
73+
@parametrize_with_cases(argnames=["files", "requirements_stem"], cases=CasesVenvInstallWithLock)
7474
def test_venv_install_with_lock(
7575
files: RequirementsDict,
76-
requirements_base: RequirementsBase,
76+
requirements_stem: RequirementsStem,
7777
tmp_path: Path,
7878
):
7979
write_files(files=files, dir=tmp_path)
8080

8181
run_command(
82-
f"venv install {requirements_base}.txt",
82+
f"venv install {requirements_stem}.txt",
8383
cwd=tmp_path,
8484
activated=True,
8585
)

tests/test_venv_install_cases.py

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
from tests.helpers import collect_requirements
2-
from tests.types import RawFilesDict, RequirementsBase
2+
from tests.types import RawFilesDict, RequirementsStem
33

44

55
class CasesVenvInstallRequirementstxt:
66
@collect_requirements
7-
def case_pypi(self) -> tuple[RawFilesDict, RequirementsBase]:
7+
def case_pypi(self) -> tuple[RawFilesDict, RequirementsStem]:
88
requirements_txt = [
99
"python-json-logger==2.0.7",
1010
]
1111

1212
files = {"requirements.txt": requirements_txt}
13-
return files, RequirementsBase.requirements
13+
return files, RequirementsStem.requirements
1414

1515
@collect_requirements
16-
def case_git(self) -> tuple[RawFilesDict, RequirementsBase]:
16+
def case_git(self) -> tuple[RawFilesDict, RequirementsStem]:
1717
requirements_txt = [
1818
"python-json-logger @ git+https://github.com/madzak/[email protected]",
1919
]
2020

2121
files = {"requirements.txt": requirements_txt}
22-
return files, RequirementsBase.requirements
22+
return files, RequirementsStem.requirements
2323

2424
@collect_requirements
25-
def case_pypi_dev(self) -> tuple[RawFilesDict, RequirementsBase]:
25+
def case_pypi_dev(self) -> tuple[RawFilesDict, RequirementsStem]:
2626
requirements_txt = [
2727
"python-json-logger==2.0.7",
2828
]
@@ -36,10 +36,10 @@ def case_pypi_dev(self) -> tuple[RawFilesDict, RequirementsBase]:
3636
"requirements.txt": requirements_txt,
3737
"dev-requirements.txt": dev_requirements_txt,
3838
}
39-
return files, RequirementsBase.dev_requirements
39+
return files, RequirementsStem.dev_requirements
4040

4141
@collect_requirements
42-
def case_git_dev(self) -> tuple[RawFilesDict, RequirementsBase]:
42+
def case_git_dev(self) -> tuple[RawFilesDict, RequirementsStem]:
4343
requirements_txt = [
4444
"python-json-logger @ git+https://github.com/madzak/[email protected]",
4545
]
@@ -53,15 +53,44 @@ def case_git_dev(self) -> tuple[RawFilesDict, RequirementsBase]:
5353
"requirements.txt": requirements_txt,
5454
"dev-requirements.txt": dev_requirements_txt,
5555
}
56-
return files, RequirementsBase.dev_requirements
56+
return files, RequirementsStem.dev_requirements
57+
58+
@collect_requirements
59+
def case_pypi_several_nested(self) -> tuple[RawFilesDict, RequirementsStem]:
60+
core_txt = [
61+
"python-json-logger==2.0.7",
62+
]
63+
64+
test_txt = [
65+
"pytest",
66+
]
67+
68+
lint_txt = [
69+
"black",
70+
]
71+
72+
all_txt = [
73+
"-r core.txt",
74+
"-r test.txt",
75+
"-r lint.txt",
76+
"numpy==1.26.0",
77+
]
78+
79+
files = {
80+
"core.txt": core_txt,
81+
"test.txt": test_txt,
82+
"lint.txt": lint_txt,
83+
"all.txt": all_txt,
84+
}
85+
return files, RequirementsStem.all
5786

5887

5988
class CasesVenvInstallWithLock:
6089
@collect_requirements
61-
def case_pypi(self) -> tuple[RawFilesDict, RequirementsBase]:
90+
def case_pypi(self) -> tuple[RawFilesDict, RequirementsStem]:
6291
requirements_txt = [
6392
"python-json-logger==2.0.7",
6493
]
6594

6695
files = {"requirements.txt": requirements_txt}
67-
return files, RequirementsBase.requirements
96+
return files, RequirementsStem.requirements

tests/test_venv_internals.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,23 @@ def test_check_venv_activated_yes_env(tmp_path: Path):
2121
@pytest.mark.parametrize(
2222
["filename", "expected"],
2323
[
24+
("dev.txt", True),
25+
("dev.lock", True),
26+
("file.txt", True),
27+
("file.lock", True),
2428
("requirements.txt", True),
2529
("requirements.lock", True),
2630
("dev-requirements.txt", True),
2731
("dev-requirements.lock", True),
2832
("prod-requirements.txt", True),
2933
("prod-requirements.lock", True),
34+
("requirements/prod-requirements.txt", True),
35+
("requirements/prod-requirements.lock", True),
3036
("", False),
3137
(".", False),
3238
(".txt", False),
3339
(".lock", False),
3440
("asdf", False),
35-
("asdf.txt", False),
36-
("asdf.lock", False),
3741
("requirements", False),
3842
("dev-requirements", False),
3943
("requirements.asdf", False),
@@ -70,18 +74,20 @@ def test_venv_check_install_requirements_file_quiet(capfd: pytest.CaptureFixture
7074

7175
@pytest.mark.order("first")
7276
@pytest.mark.parametrize(
73-
["filename", "expected"],
77+
["filename", "expected_success"],
7478
[
79+
("dev.lock", True),
80+
("file.lock", True),
7581
("requirements.lock", True),
7682
("dev-requirements.lock", True),
7783
("prod-requirements.lock", True),
84+
("requirements/prod-requirements.lock", True),
7885
("", False),
7986
(".", False),
8087
(".txt", False),
8188
(".lock", False),
8289
("asdf", False),
8390
("asdf.txt", False),
84-
("asdf.lock", False),
8591
("requirements", False),
8692
("dev-requirements", False),
8793
("requirements.txt", False),
@@ -92,11 +98,11 @@ def test_venv_check_install_requirements_file_quiet(capfd: pytest.CaptureFixture
9298
("dev-requirements.asdf", False),
9399
],
94100
)
95-
def test_venv_check_lock_requirements_file(filename: str, expected: bool):
101+
def test_venv_check_lock_requirements_file(filename: str, expected_success: bool):
96102
"""Check that 'venv::_check_lock_requirements_file' works as expected"""
97103
command = f'venv::_check_lock_requirements_file "{filename}"'
98104

99-
if expected:
105+
if expected_success:
100106
run_command(command)
101107
else:
102108
with pytest.raises(subprocess.CalledProcessError):
@@ -124,8 +130,10 @@ def test_venv_check_lock_requirements_file_quiet(capfd: pytest.CaptureFixture):
124130
("requirements", "requirements"),
125131
("requirements.txt", "requirements.lock"),
126132
("dev-requirements.txt", "dev-requirements.lock"),
133+
("requirements/requirements.txt", "requirements/requirements.lock"),
127134
("requirements.lock", "requirements.lock"),
128135
("dev-requirements.lock", "dev-requirements.lock"),
136+
("requirements/requirements.lock", "requirements/requirements.lock"),
129137
],
130138
)
131139
def test_venv_get_lock_from_requirements(filename: str, expected: str, capfd: pytest.CaptureFixture):
@@ -141,8 +149,10 @@ def test_venv_get_lock_from_requirements(filename: str, expected: str, capfd: py
141149
("requirements", "requirements"),
142150
("requirements.lock", "requirements.txt"),
143151
("dev-requirements.lock", "dev-requirements.txt"),
152+
("requirements/requirements.lock", "requirements/requirements.txt"),
144153
("requirements.txt", "requirements.txt"),
145154
("dev-requirements.txt", "dev-requirements.txt"),
155+
("requirements/requirements.txt", "requirements/requirements.txt"),
146156
],
147157
)
148158
def test_venv_get_requirements_from_lock(filename: str, expected: str, capfd: pytest.CaptureFixture):

tests/test_venv_lock.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from tests.helpers import run_command, write_files
88
from tests.test_venv_lock_cases import CasesVenvLock
9-
from tests.types import RequirementsBase, RequirementsDict
9+
from tests.types import RequirementsDict, RequirementsStem
1010

1111

1212
@pytest.mark.order(
@@ -15,26 +15,26 @@
1515
"test_venv_fill_credentials.py::test_venv_fill_credentials",
1616
]
1717
)
18-
@parametrize_with_cases(argnames=["files", "requirements_base"], cases=CasesVenvLock)
18+
@parametrize_with_cases(argnames=["files", "requirements_stem"], cases=CasesVenvLock)
1919
@parametrize("use_short_name", [False, True])
2020
def test_venv_lock(
2121
files: RequirementsDict,
22-
requirements_base: RequirementsBase,
22+
requirements_stem: RequirementsStem,
2323
use_short_name: bool,
2424
tmp_path: Path,
2525
):
2626
"""Checks that we can lock requirements in an environment after installing them"""
2727
write_files(files=files, dir=tmp_path)
2828

29-
lock_file_path = f"{requirements_base}.lock"
29+
lock_file_path = f"{requirements_stem}.lock"
3030
if use_short_name:
31-
lock_file_arg = requirements_base.split("-")[0] if "-" in requirements_base else ""
31+
lock_file_arg = requirements_stem.split("-")[0] if "-" in requirements_stem else ""
3232
else:
3333
lock_file_arg = lock_file_path
3434

3535
run_command(
3636
commands=[
37-
f"venv install {requirements_base}.txt --skip-lock",
37+
f"venv install {requirements_stem}.txt --skip-lock",
3838
f"venv lock {lock_file_arg}",
3939
],
4040
cwd=tmp_path,
@@ -48,7 +48,7 @@ def test_venv_lock(
4848
@parametrize(
4949
"lock_arg",
5050
[
51-
"file.lock",
51+
"file.txt",
5252
"requirements.txt",
5353
"requirements.asd",
5454
],

0 commit comments

Comments
 (0)