11from typing import Any , Dict , Iterable , List
22
33from cleo .helpers import argument , option
4+ from packaging .version import parse as parse_version
5+ from poetry .__version__ import __version__ as poetry_version
46from poetry .console .commands .installer_command import InstallerCommand
7+ from poetry .core .constraints .version import parse_constraint
58from poetry .core .packages .dependency import Dependency
69from poetry .core .packages .dependency_group import DependencyGroup
710from poetry .core .packages .package import Package
811from poetry .version .version_selector import VersionSelector
9- from tomlkit import dumps
12+ from tomlkit import array , dumps , table
1013from tomlkit .toml_document import TOMLDocument
1114
1215
@@ -102,19 +105,18 @@ def handle(self) -> int:
102105 pyproject_content = self .poetry .file .read ()
103106 original_pyproject_content = self .poetry .file .read ()
104107
105- for group in self .get_groups ():
106- for dependency in group .dependencies :
107- self .handle_dependency (
108- dependency = dependency ,
109- latest = latest ,
110- pinned = pinned ,
111- only_packages = only_packages ,
112- pyproject_content = pyproject_content ,
113- selector = selector ,
114- exclude = exclude ,
115- exclude_zero_based_caret = exclude_zero_based_caret ,
116- preserve_wildcard = preserve_wildcard ,
117- )
108+ for dependency in self .get_dependencies ():
109+ self .handle_dependency (
110+ dependency = dependency ,
111+ latest = latest ,
112+ pinned = pinned ,
113+ only_packages = only_packages ,
114+ pyproject_content = pyproject_content ,
115+ selector = selector ,
116+ exclude = exclude ,
117+ exclude_zero_based_caret = exclude_zero_based_caret ,
118+ preserve_wildcard = preserve_wildcard ,
119+ )
118120
119121 if dry_run :
120122 self .line (dumps (pyproject_content ))
@@ -127,7 +129,10 @@ def handle(self) -> int:
127129 try :
128130 if no_install :
129131 # update lock file
130- self .call (name = "lock" )
132+ if poetry_version_above_2 ():
133+ self .call (name = "lock" , args = "--regenerate" )
134+ else :
135+ self .call (name = "lock" )
131136 else :
132137 # update dependencies
133138 self .call (name = "update" )
@@ -137,11 +142,10 @@ def handle(self) -> int:
137142 raise e
138143 return 0
139144
140- def get_groups (self ) -> Iterable [DependencyGroup ]:
145+ def get_dependencies (self ) -> Iterable [DependencyGroup ]:
141146 """Returns activated dependency groups"""
142-
143147 for group in self .activated_groups :
144- yield self .poetry .package .dependency_group (group )
148+ yield from self .poetry .package .dependency_group (group ). dependencies
145149
146150 def handle_dependency (
147151 self ,
@@ -156,7 +160,6 @@ def handle_dependency(
156160 preserve_wildcard : bool ,
157161 ) -> None :
158162 """Handles a dependency"""
159-
160163 if not self .is_bumpable (
161164 dependency ,
162165 only_packages ,
@@ -187,7 +190,10 @@ def handle_dependency(
187190 and "." in dependency .pretty_constraint
188191 ):
189192 new_version = "~" + candidate .pretty_version
190- elif dependency .pretty_constraint [:2 ] == ">=" :
193+ elif (
194+ dependency .pretty_constraint [:2 ] == ">="
195+ and len (dependency .pretty_constraint .split ("," )) == 1
196+ ):
191197 new_version = ">=" + candidate .pretty_version
192198 else :
193199 new_version = "^" + candidate .pretty_version
@@ -209,7 +215,6 @@ def is_bumpable(
209215 preserve_wildcard : bool ,
210216 ) -> bool :
211217 """Determines if a dependency can be bumped in pyproject.toml"""
212-
213218 if dependency .source_type in ["git" , "file" , "directory" ]:
214219 return False
215220 if dependency .name in ["python" ]:
@@ -265,8 +270,35 @@ def bump_version_in_pyproject_content(
265270 pyproject_content : TOMLDocument ,
266271 ) -> None :
267272 """Bumps versions in pyproject content (pyproject.toml)"""
268-
269- poetry_content : Dict [str , Any ] = pyproject_content ["tool" ]["poetry" ]
273+ project_content = pyproject_content .get ("project" , table ())
274+ if uses_pep508_style (pyproject_content ):
275+ for group in dependency .groups :
276+ sections = []
277+ if group == "main" :
278+ sections .append (
279+ project_content .get ("dependencies" , array ())
280+ )
281+ opt_deps = project_content .get ("optional-dependencies" , {})
282+ for section in opt_deps .values ():
283+ sections .append (section )
284+ elif group in (
285+ dep_groups := pyproject_content .get ("dependency-groups" , {})
286+ ):
287+ sections .append (dep_groups .get (group , array ()))
288+ for section in sections :
289+ for index , entry in enumerate (section ):
290+ entry_dependency = Dependency .create_from_pep_508 (entry )
291+ if (
292+ entry_dependency .name == dependency .name
293+ and entry_dependency .constraint
294+ != parse_constraint (new_version )
295+ ):
296+ entry_dependency .constraint = new_version
297+ section [index ] = entry_dependency .to_pep_508 ()
298+
299+ poetry_content : Dict [str , Any ] = pyproject_content .get ("tool" , {}).get (
300+ "poetry" , {}
301+ )
270302
271303 for group in dependency .groups :
272304 # find section to modify
@@ -293,3 +325,16 @@ def bump_version_in_pyproject_content(
293325def is_pinned (version : str ) -> bool :
294326 """Returns if `version` is an exact version."""
295327 return version [0 ].isdigit () or version [:2 ] == "=="
328+
329+
330+ def uses_pep508_style (pyproject_content : TOMLDocument ) -> bool :
331+ project_content = pyproject_content .get ("project" , table ())
332+ return (
333+ "dependencies" in project_content
334+ or "optional-dependencies" in project_content
335+ or "dependency-groups" in pyproject_content
336+ )
337+
338+
339+ def poetry_version_above_2 () -> bool :
340+ return parse_version (poetry_version ) >= parse_version ("2.0.0" )
0 commit comments