Skip to content

Commit daab523

Browse files
committed
add constraints tests and fix code
1 parent 0ab10a3 commit daab523

13 files changed

+400
-132
lines changed

python/uv/private/lock.bzl

+18-7
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,22 @@ def _args(ctx):
7272
else:
7373
args.add(arg)
7474

75-
def _add_all(all_args):
75+
def _add_all(name, all_args = None, **kwargs):
76+
if not all_args and type(name) == "list":
77+
all_args = name
78+
name = None
79+
80+
before_each = kwargs.get("before_each")
81+
if name:
82+
args.add_all(name, all_args, **kwargs)
83+
run_info.append(name)
84+
else:
85+
args.add_all(all_args, **kwargs)
86+
7687
for arg in all_args:
77-
_add_args(arg)
88+
if before_each:
89+
run_info.append(before_each)
90+
run_info.append(arg)
7891

7992
return struct(
8093
run_info = run_info,
@@ -108,10 +121,8 @@ def _lock_impl(ctx):
108121
args.add("--generate-hashes")
109122
if not ctx.attr.strip_extras:
110123
args.add("--no-strip-extras")
111-
for constraint in ctx.attr.build_constraints:
112-
args.add("--build-constraints", constraint)
113-
for constraint in ctx.attr.constraints:
114-
args.add("--constraints", constraint)
124+
args.add_all(ctx.files.build_constraints, before_each = "--build-constraints")
125+
args.add_all(ctx.files.constraints, before_each = "--constraints")
115126
args.add_all(ctx.attr.args)
116127

117128
python = py_runtime.interpreter if py_runtime.interpreter else py_runtime.interpreter_path
@@ -153,7 +164,7 @@ def _lock_impl(ctx):
153164
args = args.run_info,
154165
env = ctx.attr.env,
155166
srcs = depset(
156-
srcs + [uv],
167+
srcs + [uv] + ctx.files.build_constraints + ctx.files.constraints,
157168
transitive = [
158169
py_runtime.files,
159170
],

python/uv/private/lock_copier.py

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ def main():
5959
build_workspace = Path(environ["BUILD_WORKSPACE_DIRECTORY"])
6060

6161
dst_real_path = build_workspace / dst
62+
dst_real_path.parent.mkdir(parents=True, exist_ok=True)
6263
dst_real_path.write_text(src.read_text())
6364
print(f"OK: updated {dst_real_path}")
6465
return 0

tests/uv/BUILD.bazel

-13
Original file line numberDiff line numberDiff line change
@@ -1,13 +0,0 @@
1-
load("@bazel_skylib//rules:native_binary.bzl", "native_test")
2-
load("//python/uv:lock.bzl", "lock")
3-
4-
lock(
5-
name = "requirements",
6-
srcs = ["requirements.in"],
7-
out = "requirements.txt",
8-
)
9-
10-
native_test(
11-
name = "requirements_test",
12-
src = ":requirements.update",
13-
)

tests/uv/lock/BUILD.bazel

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
load(":lock_tests.bzl", "lock_test_suite")
2+
3+
lock_test_suite(
4+
name = "lock_tests",
5+
)

tests/uv/lock/lock_run_test.py

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import subprocess
2+
import sys
3+
import tempfile
4+
import unittest
5+
from pathlib import Path
6+
7+
from python import runfiles
8+
9+
rfiles = runfiles.Create()
10+
11+
12+
def _relative_rpath(path: str) -> Path:
13+
p = (Path("_main") / "tests" / "uv" / "lock" / path).as_posix()
14+
rpath = rfiles.Rlocation(p)
15+
if not rpath:
16+
raise ValueError(f"Could not find file: {p}")
17+
18+
return Path(rpath)
19+
20+
21+
class LockTests(unittest.TestCase):
22+
def test_requirements_updating_for_the_first_time(self):
23+
# Given
24+
copier_path = _relative_rpath("requirements_new_file.update")
25+
26+
# When
27+
with tempfile.TemporaryDirectory() as dir:
28+
workspace_dir = Path(dir)
29+
want_path = workspace_dir / "tests" / "uv" / "lock" / "does_not_exist.txt"
30+
31+
self.assertFalse(
32+
want_path.exists(), "The path should not exist after the test"
33+
)
34+
output = subprocess.run(
35+
copier_path,
36+
capture_output=True,
37+
env={
38+
"BUILD_WORKSPACE_DIRECTORY": f"{workspace_dir}",
39+
},
40+
)
41+
42+
# Then
43+
self.assertEqual(0, output.returncode, output.stderr)
44+
self.assertIn(
45+
"cp <bazel-sandbox>/tests/uv/lock/requirements_new_file.out",
46+
output.stdout.decode("utf-8"),
47+
)
48+
self.assertTrue(want_path.exists(), "The path should exist after the test")
49+
self.assertNotEqual(want_path.read_text(), "")
50+
51+
def test_requirements_updating(self):
52+
# Given
53+
copier_path = _relative_rpath("requirements.update")
54+
existing_file = _relative_rpath("testdata/requirements.txt")
55+
want_text = existing_file.read_text()
56+
57+
# When
58+
with tempfile.TemporaryDirectory() as dir:
59+
workspace_dir = Path(dir)
60+
want_path = (
61+
workspace_dir
62+
/ "tests"
63+
/ "uv"
64+
/ "lock"
65+
/ "testdata"
66+
/ "requirements.txt"
67+
)
68+
want_path.parent.mkdir(parents=True)
69+
want_path.write_text(
70+
want_text + "\n\n"
71+
) # Write something else to see that it is restored
72+
73+
output = subprocess.run(
74+
copier_path,
75+
capture_output=True,
76+
env={
77+
"BUILD_WORKSPACE_DIRECTORY": f"{workspace_dir}",
78+
},
79+
)
80+
81+
# Then
82+
self.assertEqual(0, output.returncode)
83+
self.assertIn(
84+
"cp <bazel-sandbox>/tests/uv/lock/requirements.out",
85+
output.stdout.decode("utf-8"),
86+
)
87+
self.assertEqual(want_path.read_text(), want_text)
88+
89+
def test_requirements_run_on_the_first_time(self):
90+
# Given
91+
copier_path = _relative_rpath("requirements_new_file.run")
92+
93+
# When
94+
with tempfile.TemporaryDirectory() as dir:
95+
workspace_dir = Path(dir)
96+
want_path = workspace_dir / "tests" / "uv" / "lock" / "does_not_exist.txt"
97+
# NOTE @aignas 2025-03-18: right now we require users to have the folder
98+
# there already
99+
want_path.parent.mkdir(parents=True)
100+
101+
self.assertFalse(
102+
want_path.exists(), "The path should not exist after the test"
103+
)
104+
output = subprocess.run(
105+
copier_path,
106+
capture_output=True,
107+
env={
108+
"BUILD_WORKSPACE_DIRECTORY": f"{workspace_dir}",
109+
},
110+
)
111+
112+
# Then
113+
self.assertEqual(0, output.returncode, output.stderr)
114+
self.assertTrue(want_path.exists(), "The path should exist after the test")
115+
got_contents = want_path.read_text()
116+
self.assertNotEqual(got_contents, "")
117+
self.assertIn(
118+
got_contents,
119+
output.stdout.decode("utf-8"),
120+
)
121+
122+
def test_requirements_run(self):
123+
# Given
124+
copier_path = _relative_rpath("requirements.run")
125+
existing_file = _relative_rpath("testdata/requirements.txt")
126+
want_text = existing_file.read_text()
127+
128+
# When
129+
with tempfile.TemporaryDirectory() as dir:
130+
workspace_dir = Path(dir)
131+
want_path = (
132+
workspace_dir
133+
/ "tests"
134+
/ "uv"
135+
/ "lock"
136+
/ "testdata"
137+
/ "requirements.txt"
138+
)
139+
140+
want_path.parent.mkdir(parents=True)
141+
want_path.write_text(
142+
want_text + "\n\n"
143+
) # Write something else to see that it is restored
144+
145+
output = subprocess.run(
146+
copier_path,
147+
capture_output=True,
148+
env={
149+
"BUILD_WORKSPACE_DIRECTORY": f"{workspace_dir}",
150+
},
151+
)
152+
153+
# Then
154+
self.assertEqual(0, output.returncode, output.stderr)
155+
self.assertTrue(want_path.exists(), "The path should exist after the test")
156+
got_contents = want_path.read_text()
157+
self.assertNotEqual(got_contents, "")
158+
self.assertIn(
159+
got_contents,
160+
output.stdout.decode("utf-8"),
161+
)
162+
163+
164+
if __name__ == "__main__":
165+
unittest.main()

tests/uv/lock/lock_tests.bzl

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Copyright 2025 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
""
16+
17+
load("@bazel_skylib//rules:native_binary.bzl", "native_test")
18+
load("//python/uv:lock.bzl", "lock")
19+
load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test")
20+
21+
def lock_test_suite(name):
22+
"""The test suite with various lock-related integration tests
23+
24+
Args:
25+
name: {type}`str` the name of the test suite
26+
"""
27+
lock(
28+
name = "requirements",
29+
srcs = ["testdata/requirements.in"],
30+
constraints = [
31+
"testdata/constraints.txt",
32+
"testdata/constraints2.txt",
33+
],
34+
build_constraints = [
35+
"testdata/build_constraints.txt",
36+
"testdata/build_constraints2.txt",
37+
],
38+
out = "testdata/requirements.txt",
39+
)
40+
41+
lock(
42+
name = "requirements_new_file",
43+
srcs = ["testdata/requirements.in"],
44+
out = "does_not_exist.txt",
45+
)
46+
47+
py_reconfig_test(
48+
name = "requirements_run_tests",
49+
env = {
50+
"BUILD_WORKSPACE_DIRECTORY": "foo",
51+
},
52+
srcs = ["lock_run_test.py"],
53+
deps = [
54+
"//python/runfiles",
55+
],
56+
data = [
57+
"requirements_new_file.update",
58+
"requirements_new_file.run",
59+
"requirements.update",
60+
"requirements.run",
61+
"testdata/requirements.txt",
62+
],
63+
main = "lock_run_test.py",
64+
)
65+
66+
# document and check that this actually works
67+
native_test(
68+
name = "requirements_test",
69+
src = ":requirements.update",
70+
)
71+
72+
native.test_suite(
73+
name = name,
74+
tests = [
75+
":requirements_test",
76+
":build_test",
77+
":requirements_run_tests",
78+
],
79+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
certifi==2025.1.31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
certifi==2025.1.31
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
charset-normalizer==3.4.0
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
charset-normalizer==3.4.0
File renamed without changes.

0 commit comments

Comments
 (0)