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

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

7 

8from .scanner import _normalize_package_name 23b4b5b6b7b8b9b!b#b

9 

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

14 

15 

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. 

18 

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

24 

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 ; =

26 

27 

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

32 

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 ; =

45 

46 

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 ; =

54 

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

60 

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 ) 

71 

72 

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; =

78 

79 return get_node_top_level_deps_from_files([package_json]) 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b

80 

81 

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

88 

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

99 

100 return deps if found else None 2a b CbDbEbFbGbg RbUbh XbYbc d HbIbJbKbLbi SbVbj Zb0be f MbNbObPbQbk TbWbl 1b2b

101 

102 

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

108 

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

111 

112 return data 2a b CbDbEbFbGbg Ubh XbYbc d HbIbJbKbLbi Vbj Zb0be f MbNbObPbQbk Wbl 1b2b

113 

114 

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

127 

128 

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 ; =

142 

143 

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

147 

148 

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

154 

155 

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 ; =

164 

165 

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

176 

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

180 

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 )