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
« 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./
8if sys.version_info >= (3, 11): # pragma: no cover 15*+6,-7./
9 import tomllib 15*+6,-./
10else:
11 import tomli as tomllib 17
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
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
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
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
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 ]
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 ]
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
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 ( )
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
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 )
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
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 ( )
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()
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()
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
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 )
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
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