Skip to content

Commit a0d2340

Browse files
v-kbukum1Copilot
andcommitted
Handle Python requirements with no version specifier in the requirement file updater
A version specifier is optional per PEP 508 and the pip requirements format (bare names, URL/VCS/editable/local-path references, unpinned pip-tools inputs, PEP 621 / [tool.uv.sources]). The requirement file updater assumed every requirement carried one and crashed with a TypeError when it did not. Guard the nil at the single shared call site so a no-specifier requirement is left unchanged instead of forced through the replacer, matching the existing pip-compile nil-guard. uv reuses this updater, so the fix covers pip and uv. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent dc25739 commit a0d2340

3 files changed

Lines changed: 142 additions & 1 deletion

File tree

‎python/lib/dependabot/python/file_updater/requirement_file_updater.rb‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,18 @@ def unique_requirement_changes
107107
).returns(String)
108108
end
109109
def updated_requirement_or_setup_file_content(content, new_req, old_req)
110+
# A version specifier is optional in Python packaging (PEP 508, pip
111+
# requirements format, PEP 621 / [tool.uv.sources]). When the new
112+
# requirement carries none there is nothing to rewrite, so leave the
113+
# file untouched rather than forcing nil through the replacer.
114+
new_requirement = new_req.requirement_string
115+
return content unless new_requirement
116+
110117
RequirementReplacer.new(
111118
content: content,
112119
dependency_name: dependency.name,
113120
old_requirement: old_req&.requirement_string,
114-
new_requirement: T.must(new_req.requirement_string),
121+
new_requirement: new_requirement,
115122
new_hash_version: dependency.version,
116123
index_urls: @index_urls
117124
).updated_content

‎python/spec/dependabot/python/file_updater/requirement_file_updater_spec.rb‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,73 @@
9595
its(:content) { is_expected.to include "psycopg2==2.8.1 # Comment!\n" }
9696
end
9797

98+
context "when the new requirement has no version specifier" do
99+
# A version specifier is optional in Python packaging, so the updater
100+
# must leave such requirements untouched rather than crashing.
101+
subject(:updated_files) { updater.updated_dependency_files }
102+
103+
context "with a bare package name" do
104+
let(:requirements) do
105+
Dependabot::DependencyFile.new(
106+
content: "psycopg2\n",
107+
name: "requirements.txt"
108+
)
109+
end
110+
let(:dependency) do
111+
Dependabot::Dependency.new(
112+
name: "psycopg2",
113+
version: "2.8.1",
114+
requirements: [{
115+
file: "requirements.txt",
116+
requirement: nil,
117+
groups: [],
118+
source: nil
119+
}],
120+
previous_requirements: [],
121+
package_manager: "pip"
122+
)
123+
end
124+
125+
it "does not raise and leaves the file unchanged" do
126+
expect { updated_files }.not_to raise_error
127+
expect(updated_files).to be_empty
128+
end
129+
end
130+
131+
context "with a URL/VCS direct reference" do
132+
let(:requirements) do
133+
Dependabot::DependencyFile.new(
134+
content: "psycopg2 @ git+https://github.com/psycopg/psycopg2.git@2.6.1\n",
135+
name: "requirements.txt"
136+
)
137+
end
138+
let(:dependency) do
139+
Dependabot::Dependency.new(
140+
name: "psycopg2",
141+
version: "2.8.1",
142+
requirements: [{
143+
file: "requirements.txt",
144+
requirement: nil,
145+
groups: [],
146+
source: { type: "git", url: "https://github.com/psycopg/psycopg2.git", ref: "2.8.1" }
147+
}],
148+
previous_requirements: [{
149+
file: "requirements.txt",
150+
requirement: nil,
151+
groups: [],
152+
source: { type: "git", url: "https://github.com/psycopg/psycopg2.git", ref: "2.6.1" }
153+
}],
154+
package_manager: "pip"
155+
)
156+
end
157+
158+
it "does not raise and leaves the file unchanged" do
159+
expect { updated_files }.not_to raise_error
160+
expect(updated_files).to be_empty
161+
end
162+
end
163+
end
164+
98165
context "with an unknown package" do
99166
let(:dependency) do
100167
Dependabot::Dependency.new(

‎uv/spec/dependabot/uv/file_updater/requirement_file_updater_spec.rb‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,73 @@
9595
its(:content) { is_expected.to include "psycopg2==2.8.1 # Comment!\n" }
9696
end
9797

98+
context "when the new requirement has no version specifier" do
99+
# A version specifier is optional in Python packaging, so the shared
100+
# updater must leave such requirements untouched rather than crashing.
101+
subject(:updated_files) { updater.updated_dependency_files }
102+
103+
context "with a bare package name" do
104+
let(:requirements) do
105+
Dependabot::DependencyFile.new(
106+
content: "psycopg2\n",
107+
name: "requirements.txt"
108+
)
109+
end
110+
let(:dependency) do
111+
Dependabot::Dependency.new(
112+
name: "psycopg2",
113+
version: "2.8.1",
114+
requirements: [{
115+
file: "requirements.txt",
116+
requirement: nil,
117+
groups: [],
118+
source: nil
119+
}],
120+
previous_requirements: [],
121+
package_manager: "uv"
122+
)
123+
end
124+
125+
it "does not raise and leaves the file unchanged" do
126+
expect { updated_files }.not_to raise_error
127+
expect(updated_files).to be_empty
128+
end
129+
end
130+
131+
context "with a URL/VCS direct reference" do
132+
let(:requirements) do
133+
Dependabot::DependencyFile.new(
134+
content: "psycopg2 @ git+https://github.com/psycopg/psycopg2.git@2.6.1\n",
135+
name: "requirements.txt"
136+
)
137+
end
138+
let(:dependency) do
139+
Dependabot::Dependency.new(
140+
name: "psycopg2",
141+
version: "2.8.1",
142+
requirements: [{
143+
file: "requirements.txt",
144+
requirement: nil,
145+
groups: [],
146+
source: { type: "git", url: "https://github.com/psycopg/psycopg2.git", ref: "2.8.1" }
147+
}],
148+
previous_requirements: [{
149+
file: "requirements.txt",
150+
requirement: nil,
151+
groups: [],
152+
source: { type: "git", url: "https://github.com/psycopg/psycopg2.git", ref: "2.6.1" }
153+
}],
154+
package_manager: "uv"
155+
)
156+
end
157+
158+
it "does not raise and leaves the file unchanged" do
159+
expect { updated_files }.not_to raise_error
160+
expect(updated_files).to be_empty
161+
end
162+
end
163+
end
164+
98165
context "with an unknown package" do
99166
let(:dependency) do
100167
Dependabot::Dependency.new(

0 commit comments

Comments
 (0)