mirror of
https://github.com/apple/pkl.git
synced 2026-04-19 15:01:26 +02:00
Improve handling of errors when analysis fails (#948)
Modules that cannot be loaded for one reason or another now cause a Pkl Exception, with the offending import highlighted as part of the stack trace.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,6 @@ package org.pkl.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -78,7 +77,6 @@ public class Analyzer {
|
||||
return VmImportAnalyzer.analyze(sources, vmContext);
|
||||
} catch (SecurityManagerException
|
||||
| IOException
|
||||
| URISyntaxException
|
||||
| PackageLoadError
|
||||
| HttpClientInitException e) {
|
||||
throw new PklException(e.getMessage(), e);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,9 +16,12 @@
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -31,22 +34,22 @@ import org.pkl.core.SecurityManagerException;
|
||||
import org.pkl.core.ast.builder.ImportsAndReadsParser;
|
||||
import org.pkl.core.ast.builder.ImportsAndReadsParser.Entry;
|
||||
import org.pkl.core.externalreader.ExternalReaderProcessException;
|
||||
import org.pkl.core.module.ResolvedModuleKey;
|
||||
import org.pkl.core.packages.PackageLoadError;
|
||||
import org.pkl.core.util.GlobResolver;
|
||||
import org.pkl.core.util.GlobResolver.InvalidGlobPatternException;
|
||||
import org.pkl.core.util.GlobResolver.ResolvedGlobElement;
|
||||
import org.pkl.core.util.IoUtils;
|
||||
|
||||
public class VmImportAnalyzer {
|
||||
@TruffleBoundary
|
||||
public static ImportGraph analyze(URI[] moduleUris, VmContext context)
|
||||
throws IOException,
|
||||
URISyntaxException,
|
||||
SecurityManagerException,
|
||||
ExternalReaderProcessException {
|
||||
throws IOException, SecurityManagerException {
|
||||
var imports = new TreeMap<URI, Set<ImportGraph.Import>>();
|
||||
var resolvedImports = new TreeMap<URI, URI>();
|
||||
for (var moduleUri : moduleUris) {
|
||||
analyzeSingle(moduleUri, context, imports, resolvedImports);
|
||||
var moduleKey = context.getModuleResolver().resolve(moduleUri);
|
||||
var resolvedModuleKey = moduleKey.resolve(context.getSecurityManager());
|
||||
analyzeSingle(moduleUri, resolvedModuleKey, context, imports, resolvedImports);
|
||||
}
|
||||
return new ImportGraph(imports, resolvedImports);
|
||||
}
|
||||
@@ -54,37 +57,33 @@ public class VmImportAnalyzer {
|
||||
@TruffleBoundary
|
||||
private static void analyzeSingle(
|
||||
URI moduleUri,
|
||||
ResolvedModuleKey resolvedModuleKey,
|
||||
VmContext context,
|
||||
Map<URI, Set<ImportGraph.Import>> imports,
|
||||
Map<URI, URI> resolvedImports)
|
||||
throws IOException,
|
||||
URISyntaxException,
|
||||
SecurityManagerException,
|
||||
ExternalReaderProcessException {
|
||||
Map<URI, URI> resolvedImports) {
|
||||
var moduleResolver = context.getModuleResolver();
|
||||
var securityManager = context.getSecurityManager();
|
||||
var importsInModule = collectImports(moduleUri, moduleResolver, securityManager);
|
||||
|
||||
var collectedImports = collectImports(resolvedModuleKey, moduleResolver, securityManager);
|
||||
var importsInModule = new TreeSet<Import>();
|
||||
for (var imprt : collectedImports) {
|
||||
importsInModule.add(imprt.toImport());
|
||||
}
|
||||
imports.put(moduleUri, importsInModule);
|
||||
resolvedImports.put(
|
||||
moduleUri, moduleResolver.resolve(moduleUri).resolve(securityManager).getUri());
|
||||
for (var imprt : importsInModule) {
|
||||
if (imports.containsKey(imprt.uri())) {
|
||||
resolvedImports.put(moduleUri, resolvedModuleKey.getUri());
|
||||
for (var imprt : collectedImports) {
|
||||
if (imports.containsKey(imprt.moduleUri)) {
|
||||
continue;
|
||||
}
|
||||
analyzeSingle(imprt.uri(), context, imports, resolvedImports);
|
||||
analyzeSingle(imprt.moduleUri, imprt.resolvedModuleKey, context, imports, resolvedImports);
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<ImportGraph.Import> collectImports(
|
||||
URI moduleUri, ModuleResolver moduleResolver, SecurityManager securityManager)
|
||||
throws IOException,
|
||||
URISyntaxException,
|
||||
SecurityManagerException,
|
||||
ExternalReaderProcessException {
|
||||
var moduleKey = moduleResolver.resolve(moduleUri);
|
||||
var resolvedModuleKey = moduleKey.resolve(securityManager);
|
||||
private static Set<ImportEntry> collectImports(
|
||||
ResolvedModuleKey resolvedModuleKey,
|
||||
ModuleResolver moduleResolver,
|
||||
SecurityManager securityManager) {
|
||||
List<Entry> importsAndReads;
|
||||
var moduleKey = resolvedModuleKey.getOriginal();
|
||||
try {
|
||||
importsAndReads = ImportsAndReadsParser.parse(moduleKey, resolvedModuleKey);
|
||||
} catch (VmException err) {
|
||||
@@ -92,19 +91,21 @@ public class VmImportAnalyzer {
|
||||
.evalError("cannotAnalyzeBecauseSyntaxError", moduleKey.getUri())
|
||||
.wrapping(err)
|
||||
.build();
|
||||
} catch (IOException err) {
|
||||
throw new VmExceptionBuilder().evalError("ioErrorLoadingModule", moduleKey.getUri()).build();
|
||||
}
|
||||
if (importsAndReads == null) {
|
||||
return Set.of();
|
||||
}
|
||||
var result = new TreeSet<ImportGraph.Import>();
|
||||
var result = new HashSet<ImportEntry>();
|
||||
for (var entry : importsAndReads) {
|
||||
if (!entry.isModule()) {
|
||||
continue;
|
||||
}
|
||||
if (entry.isGlob()) {
|
||||
var theModuleKey =
|
||||
moduleResolver.resolve(moduleKey.resolveUri(IoUtils.toUri(entry.stringValue())));
|
||||
try {
|
||||
try {
|
||||
if (entry.isGlob()) {
|
||||
var theModuleKey =
|
||||
moduleResolver.resolve(moduleKey.resolveUri(IoUtils.toUri(entry.stringValue())));
|
||||
var elements =
|
||||
GlobResolver.resolveGlob(
|
||||
securityManager,
|
||||
@@ -112,24 +113,60 @@ public class VmImportAnalyzer {
|
||||
moduleKey,
|
||||
moduleKey.getUri(),
|
||||
entry.stringValue());
|
||||
var globImports =
|
||||
elements.values().stream()
|
||||
.map(ResolvedGlobElement::uri)
|
||||
.map(ImportGraph.Import::new)
|
||||
.toList();
|
||||
result.addAll(globImports);
|
||||
} catch (InvalidGlobPatternException e) {
|
||||
throw new VmExceptionBuilder()
|
||||
.evalError("invalidGlobPattern", entry.stringValue())
|
||||
.withSourceSection(entry.sourceSection())
|
||||
.build();
|
||||
for (var globElement : elements.values()) {
|
||||
var moduleUri = globElement.uri();
|
||||
var mk = moduleResolver.resolve(moduleUri);
|
||||
var rmk = mk.resolve(securityManager);
|
||||
result.add(new ImportEntry(moduleUri, rmk));
|
||||
}
|
||||
} else {
|
||||
var resolvedUri =
|
||||
IoUtils.resolve(securityManager, moduleKey, IoUtils.toUri(entry.stringValue()));
|
||||
var mKey = moduleResolver.resolve(resolvedUri);
|
||||
var rmk = mKey.resolve(securityManager);
|
||||
result.add(new ImportEntry(resolvedUri, rmk));
|
||||
}
|
||||
} else {
|
||||
var resolvedUri =
|
||||
IoUtils.resolve(securityManager, moduleKey, IoUtils.toUri(entry.stringValue()));
|
||||
result.add(new Import(resolvedUri));
|
||||
} catch (InvalidGlobPatternException e) {
|
||||
throw new VmExceptionBuilder()
|
||||
.evalError("invalidGlobPattern", entry.stringValue())
|
||||
.withSourceSection(entry.sourceSection())
|
||||
.build();
|
||||
} catch (FileNotFoundException | NoSuchFileException e) {
|
||||
throw new VmExceptionBuilder()
|
||||
.evalError("cannotFindModule", entry.stringValue())
|
||||
.withSourceSection(entry.sourceSection())
|
||||
.build();
|
||||
} catch (URISyntaxException e) {
|
||||
throw new VmExceptionBuilder()
|
||||
.evalError("invalidModuleUri", entry.stringValue())
|
||||
.withHint(e.getReason())
|
||||
.withSourceSection(entry.sourceSection())
|
||||
.build();
|
||||
} catch (IOException e) {
|
||||
throw new VmExceptionBuilder()
|
||||
.evalError("ioErrorLoadingModule", entry.stringValue())
|
||||
.withCause(e)
|
||||
.withSourceSection(entry.sourceSection())
|
||||
.build();
|
||||
} catch (SecurityManagerException | PackageLoadError e) {
|
||||
throw new VmExceptionBuilder()
|
||||
.withSourceSection(entry.sourceSection())
|
||||
.withCause(e)
|
||||
.build();
|
||||
} catch (ExternalReaderProcessException e) {
|
||||
throw new VmExceptionBuilder()
|
||||
.withSourceSection(entry.sourceSection())
|
||||
.evalError("externalReaderFailure")
|
||||
.withCause(e)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private record ImportEntry(URI moduleUri, ResolvedModuleKey resolvedModuleKey) {
|
||||
private Import toImport() {
|
||||
return new Import(resolvedModuleKey.getOriginal().getUri());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,7 +23,6 @@ import java.net.URISyntaxException;
|
||||
import org.pkl.core.ImportGraph;
|
||||
import org.pkl.core.ImportGraph.Import;
|
||||
import org.pkl.core.SecurityManagerException;
|
||||
import org.pkl.core.externalreader.ExternalReaderProcessException;
|
||||
import org.pkl.core.packages.PackageLoadError;
|
||||
import org.pkl.core.runtime.AnalyzeModule;
|
||||
import org.pkl.core.runtime.VmContext;
|
||||
@@ -92,11 +91,7 @@ public final class AnalyzeNodes {
|
||||
try {
|
||||
var results = VmImportAnalyzer.analyze(uris, context);
|
||||
return importGraphFactory.create(results);
|
||||
} catch (IOException
|
||||
| URISyntaxException
|
||||
| SecurityManagerException
|
||||
| PackageLoadError
|
||||
| ExternalReaderProcessException e) {
|
||||
} catch (IOException | SecurityManagerException | PackageLoadError e) {
|
||||
throw exceptionBuilder().withCause(e).build();
|
||||
}
|
||||
}
|
||||
|
||||
1
pkl-core/src/test/files/LanguageSnippetTests/input-helper/analyze/cannotFindModule.pkl
vendored
Normal file
1
pkl-core/src/test/files/LanguageSnippetTests/input-helper/analyze/cannotFindModule.pkl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
import "bogusModule.pkl"
|
||||
1
pkl-core/src/test/files/LanguageSnippetTests/input-helper/analyze/invalidGlob.pkl
vendored
Normal file
1
pkl-core/src/test/files/LanguageSnippetTests/input-helper/analyze/invalidGlob.pkl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
import* ".../**.pkl"
|
||||
5
pkl-core/src/test/files/LanguageSnippetTests/input/errors/analyzeImportsCannotFindModule.pkl
vendored
Normal file
5
pkl-core/src/test/files/LanguageSnippetTests/input/errors/analyzeImportsCannotFindModule.pkl
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import "pkl:analyze"
|
||||
import "pkl:reflect"
|
||||
import ".../input-helper/analyze/cannotFindModule.pkl"
|
||||
|
||||
res = analyze.importGraph(Set(reflect.Module(cannotFindModule).uri))
|
||||
5
pkl-core/src/test/files/LanguageSnippetTests/input/errors/analyzeImportsInvalidGlob.pkl
vendored
Normal file
5
pkl-core/src/test/files/LanguageSnippetTests/input/errors/analyzeImportsInvalidGlob.pkl
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import "pkl:analyze"
|
||||
import "pkl:reflect"
|
||||
import ".../input-helper/analyze/invalidGlob.pkl"
|
||||
|
||||
res = analyze.importGraph(Set(reflect.Module(invalidGlob).uri))
|
||||
14
pkl-core/src/test/files/LanguageSnippetTests/output/errors/analyzeImportsCannotFindModule.err
vendored
Normal file
14
pkl-core/src/test/files/LanguageSnippetTests/output/errors/analyzeImportsCannotFindModule.err
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
–– Pkl Error ––
|
||||
Cannot find module `bogusModule.pkl`.
|
||||
|
||||
x | import "bogusModule.pkl"
|
||||
^^^^^^^^^^^^^^^^^
|
||||
at pkl.analyze#importGraph (file:///$snippetsDir/input-helper/analyze/cannotFindModule.pkl)
|
||||
|
||||
x | res = analyze.importGraph(Set(reflect.Module(cannotFindModule).uri))
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at analyzeImportsCannotFindModule#res (file:///$snippetsDir/input/errors/analyzeImportsCannotFindModule.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
14
pkl-core/src/test/files/LanguageSnippetTests/output/errors/analyzeImportsInvalidGlob.err
vendored
Normal file
14
pkl-core/src/test/files/LanguageSnippetTests/output/errors/analyzeImportsInvalidGlob.err
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
–– Pkl Error ––
|
||||
Cannot combine glob imports with triple-dot module URIs.
|
||||
|
||||
x | import* ".../**.pkl"
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
at analyzeImportsInvalidGlob#invalidGlob (file:///$snippetsDir/input-helper/analyze/invalidGlob.pkl)
|
||||
|
||||
x | res = analyze.importGraph(Set(reflect.Module(invalidGlob).uri))
|
||||
^^^^^^^^^^^
|
||||
at analyzeImportsInvalidGlob#res (file:///$snippetsDir/input/errors/analyzeImportsInvalidGlob.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
Reference in New Issue
Block a user