Coverage for src / library_skills / deps.py: 100%
111 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-03 20:19 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-03 20:19 +0000
1import json 23b4b5b6b7b8b9b!b#b
2import re 23b4b5b6b7b8b9b!b#b
3import sys 23b4b5b6b7b8b9b!b#b
4from collections.abc import Sequence 23b4b5b6b7b8b9b!b#b
5from pathlib import Path 23b4b5b6b7b8b9b!b#b
6from typing import cast 23b4b5b6b7b8b9b!b#b
8from .scanner import _normalize_package_name 23b4b5b6b7b8b9b!b#b
10if sys.version_info >= (3, 11): # pragma: no cover 23b4b5b6b7b8b9b!b#b
11 import tomllib 23b4b5b6b7b8b!b#b
12else:
13 import tomli as tomllib 29b
16def get_python_top_level_deps(project_root: Path) -> set[str] | None: 23b4b5b6b7b8b9b!b#b
17 """Parse pyproject.toml to get top-level dependency names.
19 Returns normalized package names, or None if no pyproject.toml found.
20 """
21 pyproject = project_root / "pyproject.toml" 2B ? @ [ a b @bC D E F ] ^ G H I _ ` J K { L M | CbDbEbFbGbN g [b} p q r s ~ O P zbm ab$b.b;bh Q R S bbcbdbc d ]bT U V W ebfbX Y Z gbhb0 1 ib2 3 jbHbIbJbKbLb4 i ^bkbt u v w lb5 6 Abn mb%b/b=bj 7 8 9 nbobpbe f _b! # $ % qbrb' ( ) sbtb* + ub, - vbMbNbObPbQb. k `bwbx y z A xb/ : Bbo yb'b:b?bl ; =
22 if not pyproject.is_file(): 2B ? @ [ a b @bC D E F ] ^ G H I _ ` J K { L M | CbDbEbFbGbN g [b} p q r s ~ O P zbm ab$b.b;bh Q R S bbcbdbc d ]bT U V W ebfbX Y Z gbhb0 1 ib2 3 jbHbIbJbKbLb4 i ^bkbt u v w lb5 6 Abn mb%b/b=bj 7 8 9 nbobpbe f _b! # $ % qbrb' ( ) sbtb* + ub, - vbMbNbObPbQb. k `bwbx y z A xb/ : Bbo yb'b:b?bl ; =
23 return None 2@bCbDbEbFbGb[b;bucvc]bHbIbJbKbLb^b=bwcxc_bMbNbObPbQb`b?byczc
25 return get_python_top_level_deps_from_files([pyproject]) 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$b.bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%b/bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'b:bl ; =
28def get_python_top_level_deps_from_files(pyprojects: list[Path]) -> set[str] | None: 23b4b5b6b7b8b9b!b#b
29 """Parse one or more pyproject.toml files to get top-level dependencies."""
30 if not pyprojects: 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$b.b;bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%b/b=bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'b:b?bl ; =
31 return None 2;bicjc=bkclc?bmcnc
33 deps: set[str] = set() 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$b.b;bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%b/b=bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'b:b?bl ; =
34 found = False 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$b.b;bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%b/b=bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'b:b?bl ; =
35 for pyproject in pyprojects: 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$b.b;bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%b/b=bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'b:b?bl ; =
36 if not pyproject.is_file(): 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$b.b;bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%b/b=bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'b:b?bl ; =
37 continue 2;bicjc=bkclc?bmcnc
38 try: 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$b.bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%b/bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'b:bl ; =
39 data = tomllib.loads(pyproject.read_text(encoding="utf-8")) 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$b.bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%b/bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'b:bl ; =
40 except (OSError, tomllib.TOMLDecodeError): 2.bocpc/bqcrc:bsctc
41 return None 2.bocpc/bqcrc:bsctc
42 found = True 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'bl ; =
43 _extract_python_top_level_deps(data, deps) 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'bl ; =
44 return deps if found else None 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$b;bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%b=bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'b?bl ; =
47def _extract_python_top_level_deps(data: dict[str, object], deps: set[str]) -> None: 23b4b5b6b7b8b9b!b#b
48 project = data.get("project", {}) 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'bl ; =
49 if isinstance(project, dict): 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'bl ; =
50 project_data = cast("dict[str, object]", project) 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm abh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mbj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo ybl ; =
51 dependencies = project_data.get("dependencies", []) 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm abh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mbj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo ybl ; =
52 if isinstance(dependencies, list): 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm abh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mbj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo ybl ; =
53 _extract_deps_from_specs(dependencies, deps) 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm abh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mbj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo ybl ; =
55 optional_dependencies = project_data.get("optional-dependencies", {}) 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm abh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mbj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo ybl ; =
56 if isinstance(optional_dependencies, dict): 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm abh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mbj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo ybl ; =
57 for group_dependencies in optional_dependencies.values(): 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm abh Q ccR dcS bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mbj 7 ec8 fc9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo ybl ; gc= hc
58 if isinstance(group_dependencies, list): 2abccdcmbecfcybgchc
59 _extract_deps_from_specs(group_dependencies, deps) 2abccdcmbecfcybgchc
61 dependency_groups = data.get("dependency-groups", {}) 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'bl ; =
62 if isinstance(dependency_groups, dict): 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'bl ; =
63 dependency_group_data = cast("dict[str, object]", dependency_groups) 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$bh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%bj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'bl ; =
64 for group_name in dependency_group_data: 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm ab$bh Q (bR )bS bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mb%bj 7 *b8 +b9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo yb'bl ; ,b= -b
65 _extract_deps_from_dependency_group( 2m (b)bn *b+bo ,b-b
66 group_name,
67 dependency_group_data,
68 deps,
69 visited=set(),
70 )
73def get_node_top_level_deps(project_root: Path) -> set[str] | None: 23b4b5b6b7b8b9b!b#b
74 """Parse package.json to get top-level dependency names."""
75 package_json = project_root / "package.json" 2B ? @ [ a b @bC D E F ] ^ G H I _ ` J K { L M | CbDbEbFbGbN g [b} p q r s ~ O P RbUbh Q R S bbcbdbc d ]bT U V W ebfbX Y Z gbhb0 1 ib2 3 jbHbIbJbKbLb4 i ^bkbt u v w lb5 6 SbVbj 7 8 9 nbobpbe f _b! # $ % qbrb' ( ) sbtb* + ub, - vbMbNbObPbQb. k `bwbx y z A xb/ : TbWbl ; =
76 if not package_json.is_file(): 2B ? @ [ a b @bC D E F ] ^ G H I _ ` J K { L M | CbDbEbFbGbN g [b} p q r s ~ O P RbUbh Q R S bbcbdbc d ]bT U V W ebfbX Y Z gbhb0 1 ib2 3 jbHbIbJbKbLb4 i ^bkbt u v w lb5 6 SbVbj 7 8 9 nbobpbe f _b! # $ % qbrb' ( ) sbtb* + ub, - vbMbNbObPbQb. k `bwbx y z A xb/ : TbWbl ; =
77 return None 2B ? @ [ @bC D E F ] ^ G H I _ ` J K { L M | N [b} p q r s ~ O P RbQ R S bbcbdb]bT U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 ^bkbt u v w lb5 6 Sb7 8 9 nbobpb_b! # $ % qbrb' ( ) sbtb* + ub, - vb. `bwbx y z A xb/ : Tb; =
79 return get_node_top_level_deps_from_files([package_json]) 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
82def get_node_top_level_deps_from_files( 23b4b5b6b7b8b9b!b#b
83 package_json_files: list[Path],
84) -> set[str] | None:
85 """Parse one or more package.json files to get top-level dependencies."""
86 if not package_json_files: 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
87 return None 2Rb{b|bSb}b~bTbacbc
89 deps: set[str] = set() 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
90 found = False 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
91 for package_json in package_json_files: 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
92 if not package_json.is_file(): 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
93 continue 2Rb{b|bSb}b~bTbacbc
94 data = _read_package_json(package_json) 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
95 if data is None: 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
96 return None 2Rb{b|bSb}b~bTbacbc
97 found = True 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
98 _extract_node_top_level_deps(data, deps) 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
100 return deps if found else None 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
103def _read_package_json(package_json: Path) -> dict[str, object] | None: 23b4b5b6b7b8b9b!b#b
104 try: 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
105 data = json.loads(package_json.read_text(encoding="utf-8")) 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
106 except (OSError, json.JSONDecodeError): 2Rb{b|bSb}b~bTbacbc
107 return None 2Rb{b|bSb}b~bTbacbc
109 if not isinstance(data, dict): 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
110 return {} 2Rb{b|bSb}b~bTbacbc
112 return data 2a b CbDbEbFbGbg Ubh XbYbc d HbIbJbKbLbi Vbj Zb0be f MbNbObPbQbk Wbl 1b2b
115def _extract_node_top_level_deps(data: dict[str, object], deps: set[str]) -> None: 23b4b5b6b7b8b9b!b#b
116 for field in ( 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
117 "dependencies",
118 "devDependencies",
119 "optionalDependencies",
120 "peerDependencies",
121 ):
122 dependencies = data.get(field) 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
123 if isinstance(dependencies, dict): 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b
124 for package_name in dependencies: 2a b CbDbEbFbGbg Ubh XbYbc d HbIbJbKbLbi Vbj Zb0be f MbNbObPbQbk Wbl 1b2b
125 if isinstance(package_name, str): 2a b CbDbEbFbGbg Ubh XbYbc d HbIbJbKbLbi Vbj Zb0be f MbNbObPbQbk Wbl 1b2b
126 deps.add(_normalize_package_name(package_name)) 2a b CbDbEbFbGbg Ubh XbYbc d HbIbJbKbLbi Vbj Zb0be f MbNbObPbQbk Wbl 1b2b
129def get_top_level_deps(project_root: Path) -> set[str] | None: 23b4b5b6b7b8b9b!b#b
130 """Parse project metadata to get top-level Python and Node dependency names."""
131 dependency_sets = [ 2B ? @ [ a b @bC D E F ] ^ G H I _ ` J K { L M | CbDbEbFbGbN g [b} p q r s ~ O P h Q R S bbcbdbc d ]bT U V W ebfbX Y Z gbhb0 1 ib2 3 jbHbIbJbKbLb4 i ^bkbt u v w lb5 6 j 7 8 9 nbobpbe f _b! # $ % qbrb' ( ) sbtb* + ub, - vbMbNbObPbQb. k `bwbx y z A xb/ : l ; =
132 deps
133 for deps in (
134 get_python_top_level_deps(project_root),
135 get_node_top_level_deps(project_root),
136 )
137 if deps is not None
138 ]
139 if not dependency_sets: 2B ? @ [ a b @bC D E F ] ^ G H I _ ` J K { L M | CbDbEbFbGbN g [b} p q r s ~ O P h Q R S bbcbdbc d ]bT U V W ebfbX Y Z gbhb0 1 ib2 3 jbHbIbJbKbLb4 i ^bkbt u v w lb5 6 j 7 8 9 nbobpbe f _b! # $ % qbrb' ( ) sbtb* + ub, - vbMbNbObPbQb. k `bwbx y z A xb/ : l ; =
140 return None 2@b[bAcBc]b^bCcDc_b`bEcFc
141 return set().union(*dependency_sets) 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | CbDbEbFbGbN g } p q r s ~ O P h Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jbHbIbJbKbLb4 i kbt u v w lb5 6 j 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vbMbNbObPbQb. k wbx y z A xb/ : l ; =
144def get_workspace_top_level_deps(pyprojects: list[Path]) -> set[str] | None: 23b4b5b6b7b8b9b!b#b
145 """Parse Python dependency metadata for selected uv workspace files."""
146 return get_python_top_level_deps_from_files(pyprojects) 2a b p q r s GcHcc d t u v w IcJce f x y z A KcLc
149def get_node_workspace_top_level_deps( 23b4b5b6b7b8b9b!b#b
150 package_json_files: list[Path],
151) -> set[str] | None:
152 """Parse Node dependency metadata for selected npm/Bun workspace files."""
153 return get_node_top_level_deps_from_files(package_json_files) 2a b CbDbEbFbGbXbYbc d HbIbJbKbLbZb0be f MbNbObPbQb1b2b
156def _extract_deps_from_specs(dep_specs: Sequence[object], deps: set[str]) -> None: 23b4b5b6b7b8b9b!b#b
157 """Extract package names from dependency spec strings."""
158 for dep_spec in dep_specs: 2B ? @ [ a b C D E F ] ^ G H I _ ` J K { L M | N g } p q r s ~ O P zbm abh Q R S bbcbdbc d T U V W ebfbX Y Z gbhb0 1 ib2 3 jb4 i kbt u v w lb5 6 Abn mbj 7 8 9 nbobpbe f ! # $ % qbrb' ( ) sbtb* + ub, - vb. k wbx y z A xb/ : Bbo ybl ; =
159 if not isinstance(dep_spec, str): 2B a b C D E F G H I J K L M N g p q r s O P zbm abh Q R S c d T U V W X Y Z 0 1 2 3 4 i t u v w 5 6 Abn mbj 7 8 9 e f ! # $ % ' ( ) * + , - . k x y z A / : Bbo ybl ; =
160 continue 2zbm (b)bAbn *b+bBbo ,b-b
161 pkg_name = re.split(r"[~>=<!\[;,\s]", dep_spec)[0].strip() 2B a b C D E F G H I J K L M N g p q r s O P zbm abh Q R S c d T U V W X Y Z 0 1 2 3 4 i t u v w 5 6 Abn mbj 7 8 9 e f ! # $ % ' ( ) * + , - . k x y z A / : Bbo ybl ; =
162 if pkg_name and not pkg_name.startswith("#"): 2B a b C D E F G H I J K L M N g p q r s O P zbm abh Q R S c d T U V W X Y Z 0 1 2 3 4 i t u v w 5 6 Abn mbj 7 8 9 e f ! # $ % ' ( ) * + , - . k x y z A / : Bbo ybl ; =
163 deps.add(_normalize_package_name(pkg_name)) 2B a b C D E F G H I J K L M N g p q r s O P zbm abh Q R S c d T U V W X Y Z 0 1 2 3 4 i t u v w 5 6 Abn mbj 7 8 9 e f ! # $ % ' ( ) * + , - . k x y z A / : Bbo ybl ; =
166def _extract_deps_from_dependency_group( 23b4b5b6b7b8b9b!b#b
167 group_name: str,
168 dependency_groups: dict[str, object],
169 deps: set[str],
170 visited: set[str],
171) -> None:
172 """Extract package names from a PEP 735 dependency group."""
173 if group_name in visited: 2m (b)bn *b+bo ,b-b
174 return 2m (b)bn *b+bo ,b-b
175 visited.add(group_name) 2m (b)bn *b+bo ,b-b
177 group_dependencies = dependency_groups.get(group_name) 2m (b)bn *b+bo ,b-b
178 if not isinstance(group_dependencies, list): 2m (b)bn *b+bo ,b-b
179 return 2m (b)bn *b+bo ,b-b
181 _extract_deps_from_specs(group_dependencies, deps) 2m (b)bn *b+bo ,b-b
182 for group_dependency in group_dependencies: 2m (b)bn *b+bo ,b-b
183 if not isinstance(group_dependency, dict): 2m (b)bn *b+bo ,b-b
184 continue 2m (b)bn *b+bo ,b-b
185 group_dependency_data = cast("dict[str, object]", group_dependency) 2m (b)bn *b+bo ,b-b
186 include_group = group_dependency_data.get("include-group") 2m (b)bn *b+bo ,b-b
187 if not isinstance(include_group, str): 2m (b)bn *b+bo ,b-b
188 continue 2m (b)bn *b+bo ,b-b
189 _extract_deps_from_dependency_group( 2m (b)bn *b+bo ,b-b
190 include_group,
191 dependency_groups,
192 deps,
193 visited,
194 )