Coverage for src / library_skills / workspace.py: 100%

141 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-03 20:19 +0000

1import fnmatch 15*+6,-7./

2import json 15*+6,-7./

3import sys 15*+6,-7./

4from dataclasses import dataclass 15*+6,-7./

5from pathlib import Path 15*+6,-7./

6from typing import cast 15*+6,-7./

7 

8if sys.version_info >= (3, 11): # pragma: no cover 15*+6,-7./

9 import tomllib 15*+6,-./

10else: 

11 import tomli as tomllib 17

12 

13 

14@dataclass(frozen=True) 15*+6,-7./

15class UvWorkspace: 15*+6,-7./

16 root: Path 1567

17 members: tuple[Path, ...] 1567

18 current_member: Path | None 1567

19 

20 

21@dataclass(frozen=True) 15*+6,-7./

22class NodeWorkspace: 15*+6,-7./

23 root: Path 1567

24 members: tuple[Path, ...] 1567

25 current_member: Path | None 1567

26 

27 

28def find_uv_workspace(cwd: Path) -> UvWorkspace | None: 15*+6,-7./

29 """Find uv workspace metadata for cwd, if any.""" 

30 for directory in [cwd, *cwd.parents]: 2? @ [ ] a d xc^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbB C T W D obpbqbrbsbtb8 ycubg m p h vbwbxbyb: zcAcBczbv w CcDcEcAbE q 2 BbCbDbEbFbGbb e FcHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1bF G U X H 2b3b4b5b6b7b9 Gc8bi n r j 9b!b#b$b; HcIcJc%bx y KcLcMc'bI s 3 (b)b*b+b,b-bc f Nc.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecJ K V Y L fcgchcicjckc! Oclck o t l mcncocpc= PcQcRcqcz A ScTcUcrcM u 4 sctc

31 pyproject = directory / "pyproject.toml" 2? @ [ ] a d xc^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbB C T W D obpbqbrbsbtb8 ycubg m p h vbwbxbyb: zcAcBczbv w CcDcEcAbE q 2 BbCbDbEbFbGbb e FcHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1bF G U X H 2b3b4b5b6b7b9 Gc8bi n r j 9b!b#b$b; HcIcJc%bx y KcLcMc'bI s 3 (b)b*b+b,b-bc f Nc.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecJ K V Y L fcgchcicjckc! Oclck o t l mcncocpc= PcQcRcqcz A ScTcUcrcM u 4 sctc

32 if not pyproject.is_file(): 2? @ [ ] a d xc^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbB C T W D obpbqbrbsbtb8 ycubg m p h vbwbxbyb: zcAcBczbv w CcDcEcAbE q 2 BbCbDbEbFbGbb e FcHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1bF G U X H 2b3b4b5b6b7b9 Gc8bi n r j 9b!b#b$b; HcIcJc%bx y KcLcMc'bI s 3 (b)b*b+b,b-bc f Nc.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecJ K V Y L fcgchcicjckc! Oclck o t l mcncocpc= PcQcRcqcz A ScTcUcrcM u 4 sctc

33 continue 2? @ [ ] a xc^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbB C T W D obpbqbrbsbtb8 ycubg m h vbwbxbyb: zcAcBczbv w CcDcEcAbE q 2 BbCbDbEbFbGbb FcHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1bF G U X H 2b3b4b5b6b7b9 Gc8bi n j 9b!b#b$b; HcIcJc%bx y KcLcMc'bI s 3 (b)b*b+b,b-bc Nc.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecJ K V Y L fcgchcicjckc! Oclck o l mcncocpc= PcQcRcqcz A ScTcUcrcM u 4 sctc

34 data = _read_pyproject(pyproject) 2? @ [ ] a d ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtb8 ubg m p h vbwbxbybzbv w Abq 2 BbCbDbEbFbGbb e HbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b9 8bi n r j 9b!b#b$b%bx y 'bs 3 (b)b*b+b,b-bc f .b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjckc! lck o t l mcncocpcqcz A rcu 4 sctc

35 if not _has_uv_workspace(data): 2? @ [ ] a d ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtb8 ubg m p h vbwbxbybzbv w Abq 2 BbCbDbEbFbGbb e HbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b9 8bi n r j 9b!b#b$b%bx y 'bs 3 (b)b*b+b,b-bc f .b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjckc! lck o t l mcncocpcqcz A rcu 4 sctc

36 continue 2? @ [ ] a ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtb8 ubg h vbwbxbybzbv w Abq 2 BbCbDbEbFbGbb HbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b9 8bi j 9b!b#b$b%bx y 'bs 3 (b)b*b+b,b-bc .b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjckc! lck l mcncocpcqcz A rcu 4 sctc

37 members = _find_workspace_members(directory, data) 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

38 return UvWorkspace( 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

39 root=directory, 

40 members=tuple(members), 

41 current_member=_find_current_member(cwd, members), 

42 ) 

43 return None 2? @ [ ] xc^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbB C T W D obpbqbrbsbtb8 ycubvbwbxbyb: zcAcBczbCcDcEcAbE 2 BbCbDbEbFbGbFcHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1bF G U X H 2b3b4b5b6b7b9 Gc8b9b!b#b$b; HcIcJc%bKcLcMc'bI 3 (b)b*b+b,b-bNc.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecJ K V Y L fcgchcicjckc! Oclcmcncocpc= PcQcRcqcScTcUcrcM 4 sctc

44 

45 

46def find_node_workspace(cwd: Path) -> NodeWorkspace | None: 15*+6,-7./

47 """Find npm/Bun package.json workspace metadata for cwd, if any.""" 

48 for directory in [cwd, *cwd.parents]: 2? @ [ ] a d xc^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbB C T W D obpbqbrbsbtb8 ycubg m p h vbwbxbyb: zcAcBczbCcDcEcAbE ucZ BbCbDbEbFbGbb e FcHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1bF G U X H 2b3b4b5b6b7b9 Gc8bi n r j 9b!b#b$b; HcIcJc%bKcLcMc'bI vc0 (b)b*b+b,b-bc f Nc.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecJ K V Y L fcgchcicjckc! Oclck o t l mcncocpc= PcQcRcqcScTcUcrcM wc1 sctc

49 package_json = directory / "package.json" 2? @ [ ] a d xc^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbB C T W D obpbqbrbsbtb8 ycubg m p h vbwbxbyb: zcAcBczbCcDcEcAbE ucZ BbCbDbEbFbGbb e FcHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1bF G U X H 2b3b4b5b6b7b9 Gc8bi n r j 9b!b#b$b; HcIcJc%bKcLcMc'bI vc0 (b)b*b+b,b-bc f Nc.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecJ K V Y L fcgchcicjckc! Oclck o t l mcncocpc= PcQcRcqcScTcUcrcM wc1 sctc

50 if not package_json.is_file(): 2? @ [ ] a d xc^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbB C T W D obpbqbrbsbtb8 ycubg m p h vbwbxbyb: zcAcBczbCcDcEcAbE ucZ BbCbDbEbFbGbb e FcHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1bF G U X H 2b3b4b5b6b7b9 Gc8bi n r j 9b!b#b$b; HcIcJc%bKcLcMc'bI vc0 (b)b*b+b,b-bc f Nc.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecJ K V Y L fcgchcicjckc! Oclck o t l mcncocpc= PcQcRcqcScTcUcrcM wc1 sctc

51 continue 2? @ [ ] a xc^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbB C T D obpbqbrbsbtb8 ycubg m p h vbwbxbyb: zcAcBczbCcDcEcAbE ucZ BbCbDbEbFbGbb FcHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1bF G U H 2b3b4b5b6b7b9 Gc8bi n r j 9b!b#b$b; HcIcJc%bKcLcMc'bI vc0 (b)b*b+b,b-bc Nc.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecJ K V L fcgchcicjckc! Oclck o t l mcncocpc= PcQcRcqcScTcUcrcM wc1 sctc

52 data = _read_package_json(package_json) 2a d B C T W D 8 : E ucZ # $ b e F G U X H 9 ; I vc0 % ' c f J K V Y L ! = M wc1 ( )

53 member_globs = _get_node_workspace_globs(data) 2a d B C T W D 8 : E ucZ # $ b e F G U X H 9 ; I vc0 % ' c f J K V Y L ! = M wc1 ( )

54 if member_globs is None: 2a d B C T W D 8 : E ucZ # $ b e F G U X H 9 ; I vc0 % ' c f J K V Y L ! = M wc1 ( )

55 continue 2a B C D 8 : E ucZ # $ b F G H 9 ; I vc0 % ' c J K L ! = M wc1 ( )

56 members = _find_node_workspace_members(directory, member_globs) 1adBCTWDEZ#$beFGUXHI0%'cfJKVYLM1()

57 return NodeWorkspace( 1adBCTWDEZ#$beFGUXHI0%'cfJKVYLM1()

58 root=directory, 

59 members=tuple(members), 

60 current_member=_find_current_member(cwd, members), 

61 ) 

62 return None 2? @ [ ] xc^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtb8 ycubg m p h vbwbxbyb: zcAcBczbCcDcEcAbucBbCbDbEbFbGbFcHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b9 Gc8bi n r j 9b!b#b$b; HcIcJc%bKcLcMc'bvc(b)b*b+b,b-bNc.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjckc! Oclck o t l mcncocpc= PcQcRcqcScTcUcrcwcsctc

63 

64 

65def workspace_dependency_files(workspace: UvWorkspace) -> list[Path]: 15*+6,-7./

66 """Return pyproject files used for default dependency filtering.""" 

67 if workspace.current_member is not None: 1adgmphqNObeinrjsPQcfkotluRS

68 return [workspace.current_member / "pyproject.toml"] 1aghqNObijsPQckluRS

69 return [ 2d m p %c'ce n r (c)cf o t *c+c

70 path 

71 for path in [workspace.root / "pyproject.toml"] 

72 + [member / "pyproject.toml" for member in workspace.members] 

73 if path.is_file() 

74 ] 

75 

76 

77def node_workspace_dependency_files(workspace: NodeWorkspace) -> list[Path]: 15*+6,-7./

78 """Return package.json files used for default dependency filtering.""" 

79 if workspace.current_member is not None: 1adBCTWDE#$beFGUXHI%'cfJKVYLM()

80 return [workspace.current_member / "package.json"] 1aBCDE#$bFGHI%'cJKLM()

81 return [ 2d T W _c`ce U X {c|cf V Y }c~c

82 path 

83 for path in [workspace.root / "package.json"] 

84 + [member / "package.json" for member in workspace.members] 

85 if path.is_file() 

86 ] 

87 

88 

89def _read_pyproject(path: Path) -> dict[str, object]: 15*+6,-7./

90 try: 2? @ [ ] a d ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtb8 ubg m p h vbwbxbybzbv w Abq 2 BbCbDbEbFbGbb e HbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b9 8bi n r j 9b!b#b$b%bx y 'bs 3 (b)b*b+b,b-bc f .b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjckc! lck o t l mcncocpcqcz A rcu 4 sctc

91 data = tomllib.loads(path.read_text(encoding="utf-8")) 2? @ [ ] a d ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtb8 ubg m p h vbwbxbybzbv w Abq 2 BbCbDbEbFbGbb e HbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b9 8bi n r j 9b!b#b$b%bx y 'bs 3 (b)b*b+b,b-bc f .b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjckc! lck o t l mcncocpcqcz A rcu 4 sctc

92 except (OSError, tomllib.TOMLDecodeError): 22 VcWc3 XcYc4 Zc0c

93 return {} 22 VcWc3 XcYc4 Zc0c

94 return data if isinstance(data, dict) else {} 2? @ [ ] a d ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtb8 ubg m p h vbwbxbybzbv w Abq BbCbDbEbFbGbb e HbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b9 8bi n r j 9b!b#b$b%bx y 'bs (b)b*b+b,b-bc f .b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjckc! lck o t l mcncocpcqcz A rcu sctc

95 

96 

97def _read_package_json(path: Path) -> dict[str, object]: 15*+6,-7./

98 try: 2a d B C T W D 8 : E ucZ # $ b e F G U X H 9 ; I vc0 % ' c f J K V Y L ! = M wc1 ( )

99 data = json.loads(path.read_text(encoding="utf-8")) 2a d B C T W D 8 : E ucZ # $ b e F G U X H 9 ; I vc0 % ' c f J K V Y L ! = M wc1 ( )

100 except (OSError, json.JSONDecodeError): 2uc,c-cvc.c/cwc:c;c

101 return {} 2uc,c-cvc.c/cwc:c;c

102 return data if isinstance(data, dict) else {} 2a d B C T W D 8 : E ucZ # $ b e F G U X H 9 ; I vc0 % ' c f J K V Y L ! = M wc1 ( )

103 

104 

105def _has_uv_workspace(data: dict[str, object]) -> bool: 15*+6,-7./

106 tool = data.get("tool") 2? @ [ ] a d ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtb8 ubg m p h vbwbxbybzbv w Abq 2 BbCbDbEbFbGbb e HbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b9 8bi n r j 9b!b#b$b%bx y 'bs 3 (b)b*b+b,b-bc f .b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjckc! lck o t l mcncocpcqcz A rcu 4 sctc

107 if not isinstance(tool, dict): 2? @ [ ] a d ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtb8 ubg m p h vbwbxbybzbv w Abq 2 BbCbDbEbFbGbb e HbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b9 8bi n r j 9b!b#b$b%bx y 'bs 3 (b)b*b+b,b-bc f .b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjckc! lck o t l mcncocpcqcz A rcu 4 sctc

108 return False 2? @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtb8 ubvbwbxbybzbv w Abq 2 BbCbDbEbFbGbHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b9 8b9b!b#b$b%bx y 'bs 3 (b)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjckc! lcmcncocpcqcz A rcu 4 sctc

109 tool_data = cast("dict[str, object]", tool) 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

110 uv = tool_data.get("uv") 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

111 if not isinstance(uv, dict): 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

112 return False 22 VcWc3 XcYc4 Zc0c

113 uv_data = cast("dict[str, object]", uv) 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

114 return isinstance(uv_data.get("workspace"), dict) 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

115 

116 

117def _find_workspace_members(root: Path, data: dict[str, object]) -> list[Path]: 15*+6,-7./

118 workspace = _get_workspace_table(data) 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

119 if workspace is None: 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

120 return [] 22 VcWc3 XcYc4 Zc0c

121 member_globs = workspace.get("members") 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

122 if not isinstance(member_globs, list): 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

123 return [] 22 VcWc3 XcYc4 Zc0c

124 exclude_globs = workspace.get("exclude") 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

125 excludes = ( 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

126 [item for item in exclude_globs if isinstance(item, str)] 

127 if isinstance(exclude_globs, list) 

128 else [] 

129 ) 

130 

131 members: set[Path] = set() 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

132 for member_glob in member_globs: 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

133 if not isinstance(member_glob, str): 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

134 continue 2q 7c8cs 9c!cu #c$c

135 for member in root.glob(member_glob): 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

136 if not member.is_dir(): 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

137 continue 2q 7c8cs 9c!cu #c$c

138 relative = member.relative_to(root).as_posix() 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

139 if _is_excluded(relative, excludes): 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

140 continue 2q 7c8cs 9c!cu #c$c

141 if (member / "pyproject.toml").is_file(): 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

142 members.add(member.resolve()) 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

143 return sorted(members) 1adgmphvwqNObeinrjxysPQcfkotlzAuRS

144 

145 

146def _get_node_workspace_globs(data: dict[str, object]) -> list[str] | None: 15*+6,-7./

147 workspaces = data.get("workspaces") 2a d B C T W D 8 : E ucZ # $ b e F G U X H 9 ; I vc0 % ' c f J K V Y L ! = M wc1 ( )

148 if isinstance(workspaces, list): 2a d B C T W D 8 : E ucZ # $ b e F G U X H 9 ; I vc0 % ' c f J K V Y L ! = M wc1 ( )

149 return [item for item in workspaces if isinstance(item, str)] 1adBCTWDE#$beFGUXHI%'cfJKVYLM()

150 if isinstance(workspaces, dict): 2a B C D 8 : E ucZ # $ b F G H 9 ; I vc0 % ' c J K L ! = M wc1 ( )

151 workspace_data = cast("dict[str, object]", workspaces) 2Z 1c2c0 3c4c1 5c6c

152 packages = workspace_data.get("packages") 2Z 1c2c0 3c4c1 5c6c

153 if isinstance(packages, list): 2Z 1c2c0 3c4c1 5c6c

154 return [item for item in packages if isinstance(item, str)] 2Z 1c2c0 3c4c1 5c6c

155 return None 2a B C D 8 : E ucZ # $ b F G H 9 ; I vc0 % ' c J K L ! = M wc1 ( )

156 

157 

158def _find_node_workspace_members(root: Path, member_globs: list[str]) -> list[Path]: 15*+6,-7./

159 includes = [pattern for pattern in member_globs if not pattern.startswith("!")] 1adBCTWDEZ#$beFGUXHI0%'cfJKVYLM1()

160 excludes = [pattern[1:] for pattern in member_globs if pattern.startswith("!")] 1adBCTWDEZ#$beFGUXHI0%'cfJKVYLM1()

161 

162 members: set[Path] = set() 1adBCTWDEZ#$beFGUXHI0%'cfJKVYLM1()

163 for member_glob in includes: 1adBCTWDEZ#$beFGUXHI0%'cfJKVYLM1()

164 for member in root.glob(member_glob): 1adBCTWDEZ#$beFGUXHI0%'cfJKVYLM1()

165 if not member.is_dir(): 1adBCTWDEZ#$beFGUXHI0%'cfJKVYLM1()

166 continue 2E =c?cI @c[cM ]c^c

167 relative = member.relative_to(root).as_posix() 1adBCTWDEZ#$beFGUXHI0%'cfJKVYLM1()

168 if _is_excluded(relative, excludes): 1adBCTWDEZ#$beFGUXHI0%'cfJKVYLM1()

169 continue 2E =c?cI @c[cM ]c^c

170 if (member / "package.json").is_file(): 1adBCTWDEZ#$beFGUXHI0%'cfJKVYLM1()

171 members.add(member.resolve()) 1adBCTWDEZ#$beFGUXHI0%'cfJKVYLM1()

172 return sorted(members) 1adBCTWDEZ#$beFGUXHI0%'cfJKVYLM1()

173 

174 

175def _get_workspace_table(data: dict[str, object]) -> dict[str, object] | None: 15*+6,-7./

176 tool = data.get("tool") 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

177 if not isinstance(tool, dict): 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

178 return None 22 VcWc3 XcYc4 Zc0c

179 tool_data = cast("dict[str, object]", tool) 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

180 uv = tool_data.get("uv") 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

181 if not isinstance(uv, dict): 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

182 return None 22 VcWc3 XcYc4 Zc0c

183 uv_data = cast("dict[str, object]", uv) 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

184 workspace = uv_data.get("workspace") 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

185 return cast("dict[str, object]", workspace) if isinstance(workspace, dict) else None 1adgmphvwq2NObeinrjxys3PQcfkotlzAu4RS

186 

187 

188def _is_excluded(relative_path: str, exclude_globs: list[str]) -> bool: 15*+6,-7./

189 return any( 1adBCTWDgmphvwEZqNObeFGUXHinrjxyI0sPQcfJKVYLkotlzAM1uRS

190 fnmatch.fnmatch(relative_path, pattern.rstrip("/")) 

191 or fnmatch.fnmatch(f"{relative_path}/", pattern) 

192 for pattern in exclude_globs 

193 ) 

194 

195 

196def _find_current_member(cwd: Path, members: list[Path]) -> Path | None: 15*+6,-7./

197 cwd = cwd.resolve() 1adBCTWDgmphvwEZqNObeFGUXHinrjxyI0sPQcfJKVYLkotlzAM1uRS

198 matching_members = [ 1adBCTWDgmphvwEZqNObeFGUXHinrjxyI0sPQcfJKVYLkotlzAM1uRS

199 member for member in members if _is_relative_to(cwd, member.resolve()) 

200 ] 

201 if not matching_members: 1adBCTWDgmphvwEZqNObeFGUXHinrjxyI0sPQcfJKVYLkotlzAM1uRS

202 return None 2d T W m p %c'ce U X n r (c)cf V Y o t *c+c

203 return max(matching_members, key=lambda path: len(path.parts)) 1aBCDghvwEZqNObFGHijxyI0sPQcJKLklzAM1uRS

204 

205 

206def _is_relative_to(path: Path, parent: Path) -> bool: 15*+6,-7./

207 try: 1adBCTWDgmphvwEZqNObeFGUXHinrjxyI0sPQcfJKVYLkotlzAM1uRS

208 path.relative_to(parent) 1adBCTWDgmphvwEZqNObeFGUXHinrjxyI0sPQcfJKVYLkotlzAM1uRS

209 except ValueError: 1adBCTWDgmphNObeFGUXHinrjPQcfJKVYLkotlRS

210 return False 1adBCTWDgmphNObeFGUXHinrjPQcfJKVYLkotlRS

211 return True 1aBCDghvwEZqNObFGHijxyI0sPQcJKLklzAM1uRS