Skip to content

Commit 6788b44

Browse files
committed
Fix setting the version for python projects using poetry and PEP 621
Ensure `project.version` is preferred over `tool.poetry.version` but also update both versions if both are already set in the `pyproject.toml` file. Don't create `tool.poetry` section if it doesn't exist and no poetry.lock file exists or if the `project` section exists already.
1 parent 77bc540 commit 6788b44

2 files changed

Lines changed: 173 additions & 3 deletions

File tree

pontos/version/commands/_python.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ def _update_pyproject_version(
7575
)
7676

7777
poetry_lock_file_path = self.project_file_path.parent / "poetry.lock"
78-
if poetry_lock_file_path.exists():
78+
if poetry_lock_file_path.exists() and "project" not in pyproject_toml:
79+
# only create [tool.poetry] section if poetry.lock exists and [project] section does not exist
80+
# otherwise use [project] section as it is the standard for PEP 621
7981
if "tool" not in pyproject_toml:
8082
tool_table = tomlkit.table()
8183
pyproject_toml["tool"] = tool_table
@@ -84,13 +86,32 @@ def _update_pyproject_version(
8486
poetry_table = tomlkit.table()
8587
pyproject_toml["tool"].add("poetry", poetry_table) # type: ignore
8688

87-
pyproject_toml["tool"]["poetry"]["version"] = str(new_version) # type: ignore
89+
if (
90+
pyproject_toml.get("tool", {}).get("poetry", {}).get("version")
91+
is None
92+
and pyproject_toml.get("project", {}).get("version") is None
93+
):
94+
# ensure the version is available in the [tool.poetry] section
95+
pyproject_toml["tool"]["poetry"]["version"] = str(new_version)
8896
else:
97+
# create [project] section if it does not exist and set the version
8998
if "project" not in pyproject_toml:
9099
project_table = tomlkit.table()
91100
pyproject_toml["project"] = project_table
92101

93-
pyproject_toml["project"]["version"] = str(new_version) # type: ignore
102+
# ensure the version is available in the [project] section
103+
pyproject_toml["project"]["version"] = str(new_version)
104+
105+
if (
106+
pyproject_toml.get("tool", {}).get("poetry", {}).get("version")
107+
is not None
108+
):
109+
# update the version in the [tool.poetry] section if it exists
110+
pyproject_toml["tool"]["poetry"]["version"] = str(new_version)
111+
112+
if pyproject_toml.get("project", {}).get("version") is not None:
113+
# update the version in the [project] section if it exists
114+
pyproject_toml["project"]["version"] = str(new_version)
94115

95116
self.project_file_path.write_text(
96117
tomlkit.dumps(pyproject_toml), encoding="utf-8"

tests/version/commands/test_python.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,155 @@ def test_empty_project_section(self):
228228

229229
self.assertEqual(toml["project"]["version"], "22.2")
230230

231+
def test_update_both_versions(self):
232+
content = "__version__ = '1.2.3'"
233+
with temp_python_module(content, name="foo", change_into=True) as temp:
234+
tmp_poetry_lock = temp.parent / "poetry.lock"
235+
tmp_poetry_lock.touch()
236+
tmp_file = temp.parent / "pyproject.toml"
237+
tmp_file.write_text(
238+
'[project]\nversion = "1.2.3"\n'
239+
'[tool.poetry]\nversion = "1.2.4"\n'
240+
'[tool.pontos.version]\nversion-module-file = "foo.py"',
241+
encoding="utf8",
242+
)
243+
cmd = PythonVersionCommand(PEP440VersioningScheme)
244+
new_version = PEP440VersioningScheme.parse_version("22.2")
245+
previous_version = PEP440VersioningScheme.parse_version("1.2.3")
246+
updated = cmd.update_version(new_version)
247+
248+
self.assertEqual(updated.new, new_version)
249+
self.assertEqual(updated.previous, previous_version)
250+
self.assertEqual(
251+
updated.changed_files, [Path("foo.py"), tmp_file.resolve()]
252+
)
253+
254+
text = tmp_file.read_text(encoding="utf8")
255+
256+
toml = tomlkit.parse(text)
257+
258+
self.assertEqual(toml["project"]["version"], "22.2")
259+
self.assertEqual(toml["tool"]["poetry"]["version"], "22.2")
260+
261+
def test_should_prefer_project_version_if_poetry_version_is_not_set(self):
262+
content = "__version__ = '1.2.3'"
263+
with temp_python_module(content, name="foo", change_into=True) as temp:
264+
tmp_poetry_lock = temp.parent / "poetry.lock"
265+
tmp_poetry_lock.touch()
266+
tmp_file = temp.parent / "pyproject.toml"
267+
tmp_file.write_text(
268+
'[project]\nversion = "1.2.3"\n'
269+
"[tool.poetry]\n"
270+
'[tool.pontos.version]\nversion-module-file = "foo.py"',
271+
encoding="utf8",
272+
)
273+
cmd = PythonVersionCommand(PEP440VersioningScheme)
274+
new_version = PEP440VersioningScheme.parse_version("22.2")
275+
previous_version = PEP440VersioningScheme.parse_version("1.2.3")
276+
updated = cmd.update_version(new_version)
277+
278+
self.assertEqual(updated.new, new_version)
279+
self.assertEqual(updated.previous, previous_version)
280+
self.assertEqual(
281+
updated.changed_files, [Path("foo.py"), tmp_file.resolve()]
282+
)
283+
284+
text = tmp_file.read_text(encoding="utf8")
285+
286+
toml = tomlkit.parse(text)
287+
288+
self.assertEqual(toml["project"]["version"], "22.2")
289+
self.assertIsNone(
290+
toml.get("tool", {}).get("poetry", {}).get("version")
291+
)
292+
293+
def test_create_project_version_if_poetry_lock_does_not_exist(self):
294+
content = "__version__ = '1.2.3'"
295+
with temp_python_module(content, name="foo", change_into=True) as temp:
296+
tmp_file = temp.parent / "pyproject.toml"
297+
tmp_file.write_text(
298+
'[tool.pontos.version]\nversion-module-file = "foo.py"',
299+
encoding="utf8",
300+
)
301+
cmd = PythonVersionCommand(PEP440VersioningScheme)
302+
new_version = PEP440VersioningScheme.parse_version("22.2")
303+
previous_version = PEP440VersioningScheme.parse_version("1.2.3")
304+
updated = cmd.update_version(new_version)
305+
306+
self.assertEqual(updated.new, new_version)
307+
self.assertEqual(updated.previous, previous_version)
308+
self.assertEqual(
309+
updated.changed_files, [Path("foo.py"), tmp_file.resolve()]
310+
)
311+
312+
text = tmp_file.read_text(encoding="utf8")
313+
314+
toml = tomlkit.parse(text)
315+
316+
self.assertEqual(toml["project"]["version"], "22.2")
317+
self.assertIsNone(
318+
toml.get("tool", {}).get("poetry", {}).get("version")
319+
)
320+
321+
def test_not_create_poetry_version_if_project_version_exists(self):
322+
content = "__version__ = '1.2.3'"
323+
with temp_python_module(content, name="foo", change_into=True) as temp:
324+
tmp_poetry_lock = temp.parent / "poetry.lock"
325+
tmp_poetry_lock.touch()
326+
tmp_file = temp.parent / "pyproject.toml"
327+
tmp_file.write_text(
328+
'[project]\nversion = "1.2.3"\n'
329+
'[tool.pontos.version]\nversion-module-file = "foo.py"',
330+
encoding="utf8",
331+
)
332+
cmd = PythonVersionCommand(PEP440VersioningScheme)
333+
new_version = PEP440VersioningScheme.parse_version("22.2")
334+
previous_version = PEP440VersioningScheme.parse_version("1.2.3")
335+
updated = cmd.update_version(new_version)
336+
337+
self.assertEqual(updated.new, new_version)
338+
self.assertEqual(updated.previous, previous_version)
339+
self.assertEqual(
340+
updated.changed_files, [Path("foo.py"), tmp_file.resolve()]
341+
)
342+
343+
text = tmp_file.read_text(encoding="utf8")
344+
345+
toml = tomlkit.parse(text)
346+
347+
self.assertEqual(toml["project"]["version"], "22.2")
348+
self.assertIsNone(
349+
toml.get("tool", {}).get("poetry", {}).get("version")
350+
)
351+
352+
def test_create_poetry_version_if_project_version_does_not_exist(self):
353+
content = "__version__ = '1.2.3'"
354+
with temp_python_module(content, name="foo", change_into=True) as temp:
355+
tmp_poetry_lock = temp.parent / "poetry.lock"
356+
tmp_poetry_lock.touch()
357+
tmp_file = temp.parent / "pyproject.toml"
358+
tmp_file.write_text(
359+
'[tool.pontos.version]\nversion-module-file = "foo.py"',
360+
encoding="utf8",
361+
)
362+
cmd = PythonVersionCommand(PEP440VersioningScheme)
363+
new_version = PEP440VersioningScheme.parse_version("22.2")
364+
previous_version = PEP440VersioningScheme.parse_version("1.2.3")
365+
updated = cmd.update_version(new_version)
366+
367+
self.assertEqual(updated.new, new_version)
368+
self.assertEqual(updated.previous, previous_version)
369+
self.assertEqual(
370+
updated.changed_files, [Path("foo.py"), tmp_file.resolve()]
371+
)
372+
373+
text = tmp_file.read_text(encoding="utf8")
374+
375+
toml = tomlkit.parse(text)
376+
377+
self.assertEqual(toml["tool"]["poetry"]["version"], "22.2")
378+
self.assertIsNone(toml.get("project", {}).get("version"))
379+
231380
def test_override_existing_version(self):
232381
content = "__version__ = '1.2.3'"
233382
with temp_python_module(content, name="foo", change_into=True) as temp:

0 commit comments

Comments
 (0)