Skip to content

Commit ed43d94

Browse files
committed
locker: refactor to reduce code complexity
1 parent 733736c commit ed43d94

File tree

1 file changed

+97
-77
lines changed

1 file changed

+97
-77
lines changed

poetry/packages/locker.py

+97-77
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55

66
from copy import deepcopy
77
from hashlib import sha256
8+
from typing import Dict
89
from typing import Iterable
910
from typing import Iterator
1011
from typing import List
1112
from typing import Optional
1213
from typing import Sequence
14+
from typing import Set
15+
from typing import Tuple
1316
from typing import Union
1417

1518
from tomlkit import array
@@ -185,36 +188,107 @@ def locked_repository(
185188

186189
return packages
187190

191+
@staticmethod
192+
def __get_locked_package(
193+
_dependency, packages_by_name
194+
): # type: (Dependency, Dict[str, List[Package]]) -> Optional[Package]
195+
"""
196+
Internal helper to identify corresponding locked package using dependency
197+
version constraints.
198+
"""
199+
for _package in packages_by_name.get(_dependency.name, []):
200+
if _dependency.constraint.allows(_package.version):
201+
return _package
202+
return None
203+
204+
@classmethod
205+
def __walk_dependency_level(
206+
cls,
207+
dependencies,
208+
level,
209+
pinned_versions,
210+
packages_by_name,
211+
project_level_dependencies,
212+
nested_dependencies,
213+
): # type: (List[Dependency], int, bool, Dict[str, List[Package]], Set[str], Dict[Tuple[str, str], Dependency]) -> Dict[Tuple[str, str], Dependency]
214+
if not dependencies:
215+
return nested_dependencies
216+
217+
next_level_dependencies = []
218+
219+
for requirement in dependencies:
220+
locked_package = cls.__get_locked_package(requirement, packages_by_name)
221+
222+
if locked_package:
223+
for require in locked_package.requires:
224+
if require.marker.is_empty():
225+
require.marker = requirement.marker
226+
else:
227+
require.marker = require.marker.intersect(requirement.marker)
228+
229+
require.marker = require.marker.intersect(locked_package.marker)
230+
next_level_dependencies.append(require)
231+
232+
if requirement.name in project_level_dependencies and level == 0:
233+
# project level dependencies take precedence
234+
continue
235+
236+
if locked_package:
237+
# create dependency from locked package to retain dependency metadata
238+
# if this is not done, we can end-up with incorrect nested dependencies
239+
marker = requirement.marker
240+
requirement = locked_package.to_dependency()
241+
requirement.marker = requirement.marker.intersect(marker)
242+
else:
243+
# we make a copy to avoid any side-effects
244+
requirement = deepcopy(requirement)
245+
246+
if pinned_versions:
247+
requirement.set_constraint(
248+
cls.__get_locked_package(requirement, packages_by_name)
249+
.to_dependency()
250+
.constraint
251+
)
252+
253+
# dependencies use extra to indicate that it was activated via parent
254+
# package's extras, this is not required for nested exports as we assume
255+
# the resolver already selected this dependency
256+
requirement.marker = requirement.marker.without_extras()
257+
258+
key = (requirement.name, requirement.pretty_constraint)
259+
if key not in nested_dependencies:
260+
nested_dependencies[key] = requirement
261+
else:
262+
nested_dependencies[key].marker = nested_dependencies[
263+
key
264+
].marker.intersect(requirement.marker)
265+
266+
return cls.__walk_dependency_level(
267+
dependencies=next_level_dependencies,
268+
level=level + 1,
269+
pinned_versions=pinned_versions,
270+
packages_by_name=packages_by_name,
271+
project_level_dependencies=project_level_dependencies,
272+
nested_dependencies=nested_dependencies,
273+
)
274+
188275
@classmethod
189276
def get_project_dependencies(
190277
cls, project_requires, locked_packages, pinned_versions=False, with_nested=False
191278
): # type: (List[Dependency], List[Package], bool, bool) -> Iterable[Dependency]
192-
# group packages entries by name, this is required because requirement might use
193-
# different constraints
279+
# group packages entries by name, this is required because requirement might use different constraints
194280
packages_by_name = {}
195281
for pkg in locked_packages:
196282
if pkg.name not in packages_by_name:
197283
packages_by_name[pkg.name] = []
198284
packages_by_name[pkg.name].append(pkg)
199285

200-
def __get_locked_package(
201-
_dependency,
202-
): # type: (Dependency) -> Optional[Package]
203-
"""
204-
Internal helper to identify corresponding locked package using dependency
205-
version constraints.
206-
"""
207-
for _package in packages_by_name.get(_dependency.name, []):
208-
if _dependency.constraint.allows(_package.version):
209-
return _package
210-
return None
211-
212286
project_level_dependencies = set()
213287
dependencies = []
214288

215289
for dependency in project_requires:
216290
dependency = deepcopy(dependency)
217-
locked_package = __get_locked_package(dependency)
291+
locked_package = cls.__get_locked_package(dependency, packages_by_name)
218292
if locked_package:
219293
locked_dependency = locked_package.to_dependency()
220294
locked_dependency.marker = dependency.marker.intersect(
@@ -233,68 +307,14 @@ def __get_locked_package(
233307
# return only with project level dependencies
234308
return dependencies
235309

236-
nested_dependencies = dict()
237-
238-
def __walk_level(
239-
__dependencies, __level
240-
): # type: (List[Dependency], int) -> None
241-
if not __dependencies:
242-
return
243-
244-
__next_level = []
245-
246-
for requirement in __dependencies:
247-
__locked_package = __get_locked_package(requirement)
248-
249-
if __locked_package:
250-
for require in __locked_package.requires:
251-
if require.marker.is_empty():
252-
require.marker = requirement.marker
253-
else:
254-
require.marker = require.marker.intersect(
255-
requirement.marker
256-
)
257-
258-
require.marker = require.marker.intersect(
259-
__locked_package.marker
260-
)
261-
__next_level.append(require)
262-
263-
if requirement.name in project_level_dependencies and __level == 0:
264-
# project level dependencies take precedence
265-
continue
266-
267-
if __locked_package:
268-
# create dependency from locked package to retain dependency metadata
269-
# if this is not done, we can end-up with incorrect nested dependencies
270-
marker = requirement.marker
271-
requirement = __locked_package.to_dependency()
272-
requirement.marker = requirement.marker.intersect(marker)
273-
else:
274-
# we make a copy to avoid any side-effects
275-
requirement = deepcopy(requirement)
276-
277-
if pinned_versions:
278-
requirement.set_constraint(
279-
__get_locked_package(requirement).to_dependency().constraint
280-
)
281-
282-
# dependencies use extra to indicate that it was activated via parent
283-
# package's extras, this is not required for nested exports as we assume
284-
# the resolver already selected this dependency
285-
requirement.marker = requirement.marker.without_extras()
286-
287-
key = (requirement.name, requirement.pretty_constraint)
288-
if key not in nested_dependencies:
289-
nested_dependencies[key] = requirement
290-
else:
291-
nested_dependencies[key].marker = nested_dependencies[
292-
key
293-
].marker.intersect(requirement.marker)
294-
295-
return __walk_level(__next_level, __level + 1)
296-
297-
__walk_level(dependencies, 0)
310+
nested_dependencies = cls.__walk_dependency_level(
311+
dependencies=dependencies,
312+
level=0,
313+
pinned_versions=pinned_versions,
314+
packages_by_name=packages_by_name,
315+
project_level_dependencies=project_level_dependencies,
316+
nested_dependencies=dict(),
317+
)
298318

299319
# Merge same dependencies using marker union
300320
for requirement in dependencies:

0 commit comments

Comments
 (0)