2121import com .fasterxml .jackson .annotation .Nulls ;
2222import com .fasterxml .jackson .databind .DeserializationFeature ;
2323import com .fasterxml .jackson .dataformat .toml .TomlMapper ;
24+ import java .io .File ;
2425import java .io .IOException ;
26+ import java .nio .file .Files ;
2527import java .util .ArrayList ;
2628import java .util .LinkedHashSet ;
2729import java .util .List ;
2830import java .util .Map ;
2931import java .util .Set ;
3032import javax .annotation .Nonnull ;
3133import javax .annotation .Nullable ;
32- import org .sonar .api .batch .fs .InputFile ;
3334
3435/**
3536 * Extracts source root directories from pyproject.toml build system configurations.
3940 * <li>setuptools: {@code [tool.setuptools.packages.find] where = ["src"]}</li>
4041 * <li>Poetry: {@code [tool.poetry] packages = [{from = "src", include = "pkg"}]}</li>
4142 * <li>Hatchling: {@code [tool.hatch.build.targets.wheel] sources = ["src"]}</li>
42- * <li>uv_build: {@code [tool.uv.build-backend] module-root = "src"} and by default detect src by convention</li>
43+ * <li>uv_build: {@code [build-system] build-backend = "uv_build"} or {@code [ tool.uv.build-backend] module-root = "src"} - auto-detects src/ layout by convention</li>
4344 * <li>PDM: {@code [tool.pdm] package-dir = "src"}</li>
4445 * <li>Flit: auto-detects src/ layout by convention</li>
4546 * </ul>
@@ -71,37 +72,53 @@ public static List<String> extract(String tomlContent) {
7172 }
7273
7374 /**
74- * Extracts source root directories from a pyproject.toml InputFile .
75+ * Extracts source root directories from a pyproject.toml File .
7576 *
76- * @param inputFile the pyproject.toml file
77+ * @param file the pyproject.toml file
7778 * @return list of source root paths (relative), empty if none found or on parse error
7879 */
79- public static List <String > extract (InputFile inputFile ) {
80+ public static List <String > extract (File file ) {
8081 try {
81- return extract (inputFile . contents ( ));
82+ return extract (Files . readString ( file . toPath () ));
8283 } catch (IOException e ) {
8384 return List .of ();
8485 }
8586 }
8687
8788 private static List <String > extractFromConfig (PyProjectConfig config ) {
88- if (config .tool () == null ) {
89- return List .of ();
90- }
91-
9289 Set <String > sourceRoots = new LinkedHashSet <>();
93- Tool tool = config .tool ();
9490
95- sourceRoots .addAll (extractFromSetuptools (tool .setuptools ()));
96- sourceRoots .addAll (extractFromPoetry (tool .poetry ()));
97- sourceRoots .addAll (extractFromHatchling (tool .hatch ()));
98- sourceRoots .addAll (extractFromUvBuild (tool .uv ()));
99- sourceRoots .addAll (extractFromPdm (tool .pdm ()));
100- sourceRoots .addAll (extractFromFlit (tool .flit ()));
91+ // Check build-system.build-backend for uv_build
92+ sourceRoots .addAll (extractFromBuildSystem (config .buildSystem ()));
93+
94+ if (config .tool () != null ) {
95+ Tool tool = config .tool ();
96+ sourceRoots .addAll (extractFromSetuptools (tool .setuptools ()));
97+ sourceRoots .addAll (extractFromPoetry (tool .poetry ()));
98+ sourceRoots .addAll (extractFromHatchling (tool .hatch ()));
99+ sourceRoots .addAll (extractFromUvBuild (tool .uv ()));
100+ sourceRoots .addAll (extractFromPdm (tool .pdm ()));
101+ sourceRoots .addAll (extractFromFlit (tool .flit ()));
102+ }
101103
102104 return new ArrayList <>(sourceRoots );
103105 }
104106
107+ // === Build System ===
108+ // [build-system]
109+ // build-backend = "uv_build"
110+
111+ private static List <String > extractFromBuildSystem (@ Nullable BuildSystem buildSystem ) {
112+ if (buildSystem == null || buildSystem .buildBackend () == null ) {
113+ return List .of ();
114+ }
115+ // uv_build auto-detects src/ layout by convention
116+ if ("uv_build" .equals (buildSystem .buildBackend ())) {
117+ return List .of ("src" );
118+ }
119+ return List .of ();
120+ }
121+
105122 // === Setuptools ===
106123 // [tool.setuptools.packages.find]
107124 // where = ["src"]
@@ -135,7 +152,7 @@ private static List<String> extractFromPoetry(@Nullable Poetry poetry) {
135152
136153 private static List <String > extractFromHatchling (@ Nullable Hatch hatch ) {
137154 if (hatch == null || hatch .build () == null || hatch .build ().targets () == null
138- || hatch .build ().targets ().wheel () == null ) {
155+ || hatch .build ().targets ().wheel () == null ) {
139156 return List .of ();
140157 }
141158
@@ -220,7 +237,8 @@ private static List<String> extractFromFlit(@Nullable Flit flit) {
220237 private record PyProjectConfig (
221238 @ JsonProperty ("build-system" ) @ Nullable BuildSystem buildSystem ,
222239 @ Nullable Tool tool
223- ) {}
240+ ) {
241+ }
224242
225243 private record BuildSystem (
226244 @ JsonProperty ("build-backend" ) @ Nullable String buildBackend ,
@@ -238,16 +256,19 @@ private record Tool(
238256 @ Nullable Uv uv ,
239257 @ Nullable Pdm pdm ,
240258 @ Nullable Flit flit
241- ) {}
259+ ) {
260+ }
242261
243262 // Setuptools records
244263 private record Setuptools (
245264 @ Nullable SetuptoolsPackages packages
246- ) {}
265+ ) {
266+ }
247267
248268 private record SetuptoolsPackages (
249269 @ Nullable SetuptoolsFind find
250- ) {}
270+ ) {
271+ }
251272
252273 private record SetuptoolsFind (
253274 @ JsonSetter (nulls = Nulls .AS_EMPTY ) @ Nonnull List <String > where
@@ -271,20 +292,24 @@ private record Poetry(
271292 private record PoetryPackage (
272293 @ Nullable String include ,
273294 @ Nullable String from
274- ) {}
295+ ) {
296+ }
275297
276298 // Hatchling records
277299 private record Hatch (
278300 @ Nullable HatchBuild build
279- ) {}
301+ ) {
302+ }
280303
281304 private record HatchBuild (
282305 @ Nullable HatchTargets targets
283- ) {}
306+ ) {
307+ }
284308
285309 private record HatchTargets (
286310 @ Nullable HatchWheel wheel
287- ) {}
311+ ) {
312+ }
288313
289314 private record HatchWheel (
290315 @ JsonSetter (nulls = Nulls .AS_EMPTY ) @ Nonnull List <String > sources ,
@@ -299,24 +324,29 @@ private record HatchWheel(
299324 // uv_build records
300325 private record Uv (
301326 @ JsonProperty ("build-backend" ) @ Nullable UvBuildBackend buildBackend
302- ) {}
327+ ) {
328+ }
303329
304330 private record UvBuildBackend (
305331 @ JsonProperty ("module-root" ) @ Nullable String moduleRoot
306- ) {}
332+ ) {
333+ }
307334
308335 // PDM records
309336 private record Pdm (
310337 @ JsonProperty ("package-dir" ) @ Nullable String packageDir
311- ) {}
338+ ) {
339+ }
312340
313341 // Flit records
314342 private record Flit (
315343 @ Nullable FlitModule module
316- ) {}
344+ ) {
345+ }
317346
318347 private record FlitModule (
319348 @ Nullable String name
320- ) {}
349+ ) {
350+ }
321351}
322352
0 commit comments