Compare commits

..

15 Commits

Author SHA1 Message Date
ManuW 19c292f9a2 Improve let-expression examples (#680)
Because I did not understand what a `Let Expression` should be and the example code was more confusing as explanatory, I changed the format of the examples a little bit and changed the result.

- It should be clear that the example is just one code-line
- I tried out the example (`pkl eval...`) and get a little bit different result
2024-10-14 12:21:08 +01:00
Josh B d720f21149 [docs] Add documentation for new keyword (#624) 2024-08-29 21:32:12 -07:00
Josh B b28cdcd631 [docs] Improve searchability of "Methods" section (#625) 2024-08-29 21:32:12 -07:00
LamTrinh.Dev 825135a0f8 Correct the link for ANTLR 4 Documentation (#623) 2024-08-29 21:32:12 -07:00
Josh B 63b92ec72b [docs] Document the class-as-a-function pattern (#614) 2024-08-29 21:32:12 -07:00
Josh B 3125fc4678 [docs] Document hidden equality/hashing behavior (#618)
* [docs] Document `hidden` equality/hashing behavior

* Update docs/modules/language-reference/pages/index.adoc

---------

Co-authored-by: Daniel Chao <daniel.h.chao@gmail.com>
2024-08-29 21:32:12 -07:00
Josh B 4bb6890621 [docs] Add mention of "optional" to nullably types section (#613) 2024-08-29 21:32:12 -07:00
Dan Chao 8e15556201 Prepare 0.26.3 release 2024-08-06 09:58:31 -07:00
Daniel Chao 2fb17fd283 Add release notes for 0.26.3 (#612) 2024-08-06 09:56:00 -07:00
Daniel Chao f4983c51be Fix: globbing--read extra storage from owner instead of receiver (#607)
This fixes an issue where a PklBugException is thrown when a globbed
read/import is amended.
2024-08-06 09:53:35 -07:00
Islon Scherer 2cd2712589 Fix property parsing bug in the cli (#596) 2024-08-06 09:53:28 -07:00
Daniel Chao f9a3fc88fd Fix usage of file() notation with Pkl Gradle plugin on Windows (#611)
This is a port of a fix that was included in https://github.com/apple/pkl/pull/403.
2024-08-06 07:54:52 -07:00
Dan Chao d097341abd Prepare 0.26.2 release 2024-07-18 09:13:58 -07:00
Daniel Chao 0eab5fb552 Add release notes for 0.26.2 (#586) 2024-07-18 09:13:11 -07:00
Daniel Chao 7c3787396e Fix race condition when concurrently downloading packages (#584)
This fixes a possible race condition where multiple processes download
the same package into the same temp dir.
2024-07-18 09:12:03 -07:00
19 changed files with 309 additions and 35 deletions
+1 -1
View File
@@ -81,7 +81,7 @@ For automated build setup examples see our https://github.com/apple/pkl/blob/mai
=== ANTLR === ANTLR
* https://github.com/antlr/antlr4/blob/main/doc/index.md[Documentation] * https://github.com/antlr/antlr4/blob/master/doc/index.md[Documentation]
* https://groups.google.com/forum/#!forum/antlr-discussion[Forums] * https://groups.google.com/forum/#!forum/antlr-discussion[Forums]
* https://github.com/mobileink/lab.clj.antlr/tree/main/doc[Some third-party docs] * https://github.com/mobileink/lab.clj.antlr/tree/main/doc[Some third-party docs]
+1 -1
View File
@@ -1,6 +1,6 @@
name: main name: main
title: Main Project title: Main Project
version: 0.26.1 version: 0.26.3
prerelease: false prerelease: false
nav: nav:
- nav.adoc - nav.adoc
@@ -3,7 +3,7 @@
// the following attributes must be updated immediately before a release // the following attributes must be updated immediately before a release
// pkl version corresponding to current git commit without -dev suffix or git hash // pkl version corresponding to current git commit without -dev suffix or git hash
:pkl-version-no-suffix: 0.26.1 :pkl-version-no-suffix: 0.26.3
// tells whether pkl version corresponding to current git commit // tells whether pkl version corresponding to current git commit
// is a release version (:is-release-version: '') or dev version (:!is-release-version:) // is a release version (:is-release-version: '') or dev version (:!is-release-version:)
:is-release-version: '' :is-release-version: ''
+195 -13
View File
@@ -915,6 +915,7 @@ pigeon = new Dynamic { // <1>
==== Hidden Properties ==== Hidden Properties
A property with the modifier `hidden` is omitted from the rendered output and object conversions. A property with the modifier `hidden` is omitted from the rendered output and object conversions.
Hidden properties are also ignored when evaluating equality or hashing (e.g. for `Mapping` or `Map` keys).
[source,{pkl}] [source,{pkl}]
---- ----
@@ -933,12 +934,19 @@ pigeon = new Bird { // <3>
pigeonInIndex = pigeon.nameAndLifespanInIndex // <4> pigeonInIndex = pigeon.nameAndLifespanInIndex // <4>
pigeonDynamic = pigeon.toDynamic() // <5> pigeonDynamic = pigeon.toDynamic() // <5>
favoritePigeon = (pigeon) {
nameAndLifespanInIndex = "Bettie, \(lifespan)"
}
samePigeon = pigeon == favoritePigeon // <6>
---- ----
<1> Properties defined as `hidden` are accessible on any `Bird` instance, but not output by default. <1> Properties defined as `hidden` are accessible on any `Bird` instance, but not output by default.
<2> Non-hidden properties can refer to hidden properties as usual. <2> Non-hidden properties can refer to hidden properties as usual.
<3> `pigeon` is an object with _four_ properties, but is rendered with _three_ properties. <3> `pigeon` is an object with _four_ properties, but is rendered with _three_ properties.
<4> Accessing a `hidden` property from outside the class and object is like any other property. <4> Accessing a `hidden` property from outside the class and object is like any other property.
<5> Object conversions omit hidden properties, so the resulting `Dynamic` has three properties. <5> Object conversions omit hidden properties, so the resulting `Dynamic` has three properties.
<6> Objects that differ only in `hidden` property values are considered equal
Invoking Pkl on this file produces the following result. Invoking Pkl on this file produces the following result.
@@ -955,6 +963,12 @@ pigeonDynamic {
lifespan = 8 lifespan = 8
nameSignWidth = 9 nameSignWidth = 9
} }
favoritePigeon {
name = "Pigeon"
lifespan = 8
nameSignWidth = 9
}
samePigeon = true
---- ----
==== Local properties ==== Local properties
@@ -1992,7 +2006,8 @@ pigeon: ParentBird = new {
[[methods]] [[methods]]
== Methods == Methods
Modules and classes can define methods. Pkl methods can be defined on classes and modules using the `function` keyword.
Methods may access properties of their containing type.
Submodules and subclasses can override them. Submodules and subclasses can override them.
Like Java and most other object-oriented languages, Pkl uses _single dispatch_ -- methods are dynamically dispatched based on the receiver's runtime type. Like Java and most other object-oriented languages, Pkl uses _single dispatch_ -- methods are dynamically dispatched based on the receiver's runtime type.
@@ -2021,6 +2036,12 @@ greeting2 = greetPigeon(parrot) // <4>
<3> Call instance method on `pigeon`. <3> Call instance method on `pigeon`.
<4> Call module method (on `this`). <4> Call module method (on `this`).
NOTE: Methods do not support named parameters or default parameter values.
The xref:blog:ROOT:class-as-a-function.adoc[Class-as-a-function] pattern may be a suitable replacement.
TIP: In most cases, methods without parameters should not be defined.
Instead, use <<fixed-properties,`fixed` properties>> on the module or class.
[[modules]] [[modules]]
== Modules == Modules
@@ -3156,6 +3177,7 @@ pkl: TRACE: num1 * num2 = 672 (at file:///some/module.pkl, line 42)
This section discusses language features that are generally more relevant to template and library authors than template consumers. This section discusses language features that are generally more relevant to template and library authors than template consumers.
<<meaning-of-new,Meaning of `new`>> +
<<let-expressions,Let Expressions>> + <<let-expressions,Let Expressions>> +
<<type-tests,Type Tests>> + <<type-tests,Type Tests>> +
<<type-casts,Type Casts>> + <<type-casts,Type Casts>> +
@@ -3179,6 +3201,162 @@ This section discusses language features that are generally more relevant to tem
<<blank-identifiers,Blank Identifiers>> + <<blank-identifiers,Blank Identifiers>> +
<<projects,Projects>> <<projects,Projects>>
[[meaning-of-new]]
=== Meaning of `new`
Objects in Pkl always <<amending-objects, amends>> _some_ value.
The `new` keyword is a special case of amending where a contextual value is amended.
In Pkl, there are two forms of `new` objects:
* `new` with explicit type information, for example, `new Foo {}`.
* `new` without type information, for example, `new {}`.
==== Type defaults
To understand instantiation cases without explicit parent or type information, it's important to first understand implicit default values.
When a property is declared in a module or class but is not provided an explicit default value, the property's default value becomes the type's default value.
Similarly, when `Listing` and `Mapping` types are declared with explicit type arguments for their element or value, their `default` property amends that declared type.
When `Listing` and `Mapping` types are declared without type arguments, their `default` property amends an empty `Dynamic` object.
Some types, including `Pair` and primitives like `String`, `Number`, and `Boolean` have no default value; attempting to render such a property results in the error "Tried to read property `<name>` but its value is undefined".
[source,{pkl}]
----
class Bird {
name: String = "polly"
}
bird: Bird // <1>
birdListing: Listing<Bird> // <2>
birdMapping: Mapping<String, Bird> // <3>
----
<1> Without an explicit default value, this property has default value `new Bird { name = "polly" }`
<2> With an explicit element type argument, this property's default value is equivalent to `new Listing<Bird> { default = (_) -> new Bird { name = "polly" } }`
<3> With an explicit value type argument, this property's default value is equivalent to `new Mapping<String, Bird> { default = (_) -> new Bird { name = "polly" } }`
==== Explicitly Typed `new`
Instantiating an object with `new <type>` results in a value that amends the specified type's default value.
Notably, creating a `Listing` element or assigning a `Mapping` entry value with an explicitly typed `new` ignores the object's `default` value.
[source,{pkl}]
----
class Bird {
/// The name of the bird
name: String
/// Whether this is a bird of prey or not.
isPredatory: Boolean?
}
newProperty = new Bird { // <1>
name = "Warbler"
}
someListing = new Listing<Bird> {
default {
isPredatory = true
}
new Bird { // <2>
name = "Sand Piper"
}
}
someMapping = new Mapping<String, Bird> {
default {
isPredatory = true
}
["Penguin"] = new Bird { // <3>
name = "Penguin"
}
}
----
<1> Assigning a `new` explicitly-typed value to a property.
<2> Adding an `new` explicitly-typed `Listing` element.
The value will not have property `isPredatory = true` as the `default` property of the `Listing` is not used.
<3> Assigning a `new` explicitly-typed value to a `Mapping` entry.
The value will not have property `isPredatory = true` as the `default` property of the `Mapping` is not used.
==== Implicitly Typed `new`
When using the implicitly typed `new` invocation, there is no explicit parent value to amend.
In these cases, Pkl infers the amend operation's parent value based on context:
* When assigning to a declared property, the property's default value is amended (<<amend-null, including `null`>>).
If there is no type associated with the property, an empty `Dynamic` object is amended.
* When assigning to an entry (e.g. a `Mapping` member) or element (e.g. a `Listing` member), the enclosing object's `default` property is applied to the corresponding index or key, respectively, to produce the value to be amended.
* In other cases, evaluation fails with the error message "Cannot tell which parent to amend".
The type annotation of a <<methods,method>> parameter is not used for inference.
In this case, the argument's type should be specified explicitly.
[source,{pkl}]
----
class Bird {
name: String
function listHatchlings(items: Listing<String>): Listing<String> = new {
for (item in items) {
"\(name):\(item)"
}
}
}
typedProperty: Bird = new { // <1>
name = "Swift"
}
untypedProperty = new { // <2>
hello = "world"
}
typedListing: Listing<Bird> = new {
new { // <3>
name = "Kite"
}
}
untypedListing: Listing = new {
new { // <4>
hello = "there"
}
}
typedMapping: Mapping<String, Bird> = new {
default { entryKey ->
name = entryKey
}
["Saltmarsh Sparrow"] = new { // <5>
name = "Sharp-tailed Sparrow"
}
}
amendedMapping = (typedMapping) {
["Saltmarsh Sparrow"] = new {} // <6>
}
class Aviary {
birds: Listing<Bird> = new {
new { name = "Osprey" }
}
}
aviary: Aviary = new {
birds = new { // <7>
new { name = "Kiwi" }
}
}
swiftHatchlings = typedProperty.listHatchlings(new { "Poppy"; "Chirpy" }) // <8>
----
<1> Assignment to a property with an explicitly declared type, amending `new Bird {}`.
<2> Assignment to an undeclared property in module context, amending `new Dynamic {}`.
<3> `Listing` element creation, amending implicit `default`, `new Bird {}`.
<4> `Listing` element creation, amending implicit `default`, `new Dynamic {}`.
<5> `Mapping` value assignment, amdending the result of applying `default` to `"Saltmarsh Sparrow"`, `new Bird { name = "Saltmarsh Sparrow" }`.
<6> `Mapping` value assignment _replacing_ the parent's entry, amending the result of applying `default` to `"Saltmarsh Sparrow"`, `new Bird { name = "Saltmarsh Sparrow" }`.
<7> Admending the property default value `new Listing { new Bird { name = "Osprey" } }`; the result contains both birds.
<8> Error: Cannot tell which parent to amend.
[[let-expressions]] [[let-expressions]]
=== Let Expressions === Let Expressions
@@ -3200,10 +3378,11 @@ Here is an example:
[source%tested,{pkl}] [source%tested,{pkl}]
---- ----
birdDiets = let (diets = List("Seeds", "Berries", "Mice")) birdDiets =
List(diets[2], diets[0]) // <1> let (diets = List("Seeds", "Berries", "Mice"))
List(diets[2], diets[0]) // <1>
---- ----
<1> result: `List("Mice", "Seeds")` <1> result: `birdDiets = List("Mice", "Seeds")`
`let` expressions serve two purposes: `let` expressions serve two purposes:
@@ -3214,20 +3393,22 @@ List(diets[2], diets[0]) // <1>
[source%tested,{pkl}] [source%tested,{pkl}]
---- ----
birdDiets = let (diets: List<String> = List("Seeds", "Berries", "Mice")) birdDiets =
diets[2] + diets[0] // <1> let (diets: List<String> = List("Seeds", "Berries", "Mice"))
diets[2] + diets[0] // <1>
---- ----
<1> result: `List("Mice", "Seeds")` <1> result: `birdDiets = List("Mice", "Seeds")`
`let` expressions can be stacked: `let` expressions can be stacked:
[source%tested,{pkl}] [source%tested,{pkl}]
---- ----
birdDiets = let (birds = List("Pigeon", "Barn owl", "Parrot")) birdDiets =
let (diet = List("Seeds", "Mice", "Berries")) let (birds = List("Pigeon", "Barn owl", "Parrot"))
birds.zip(diet) // <1> let (diet = List("Seeds", "Mice", "Berries"))
birds.zip(diet) // <1>
---- ----
<1> result: `List(Pair("Pigeon", "Seeds"), Pair("Barn owl", "Mice"), Pair("Parrot", "Berries"))` <1> result: `birdDiets = List(Pair("Pigeon", "Seeds"), Pair("Barn owl", "Mice"), Pair("Parrot", "Berries"))`
[[type-tests]] [[type-tests]]
=== Type Tests === Type Tests
@@ -3721,6 +3902,7 @@ bird2: Bird? = null // <2>
The only class types that admit `null` values despite not ending in `?` are `Any` and `Null`. The only class types that admit `null` values despite not ending in `?` are `Any` and `Null`.
(`Null` is not very useful as a type because it _only_ admits `null` values.) (`Null` is not very useful as a type because it _only_ admits `null` values.)
`Any?` and `Null?` are equivalent to `Any` and `Null`, respectively. `Any?` and `Null?` are equivalent to `Any` and `Null`, respectively.
In some languages, nullable types are also known as _optional types_.
[[generic-types]] [[generic-types]]
==== Generic Types ==== Generic Types
@@ -5146,8 +5328,8 @@ It is defined by the presence of a `PklProject` file that amends the standard li
Defining a project serves the following purposes: Defining a project serves the following purposes:
1. It allows defining common evaluator settings for Pkl modules within a logical project. 1. It allows defining common evaluator settings for Pkl modules within a logical project.
2. It helps with managing <<packages,package>> dependencies for Pkl modules within a logical project. 2. It helps with managing <<package-asset-uri,package>> dependencies for Pkl modules within a logical project.
3. It enables packaging and sharing the contents of the project as a <<packages,package>>. 3. It enables packaging and sharing the contents of the project as a <<package-asset-uri,package>>.
4. It allows importing packages via dependency notation. 4. It allows importing packages via dependency notation.
[[project-dependencies]] [[project-dependencies]]
+1 -1
View File
@@ -1,6 +1,6 @@
= Pkl 0.26 Release Notes = Pkl 0.26 Release Notes
:version: 0.26 :version: 0.26
:version-minor: 0.26.1 :version-minor: 0.26.3
:release-date: June 17th, 2024 :release-date: June 17th, 2024
include::ROOT:partial$component-attributes.adoc[] include::ROOT:partial$component-attributes.adoc[]
@@ -1,6 +1,22 @@
= Changelog = Changelog
include::ROOT:partial$component-attributes.adoc[] include::ROOT:partial$component-attributes.adoc[]
[[release-0.26.3]]
== 0.26.3 (2024-08-06)
=== Fixes
* Fixes an issue where CLI argument `--property foo=""` is effectively parsed as `--property foo="true"`. This is now parsed as an empty string (https://github.com/apple/pkl/pull/596[#596]).
* Fixes a regression where amending a globbed import or globbed read results in a PklBugException (https://github.com/apple/pkl/pull/607[#607]).
* Fixes an issue around using `file()` notation when using the pkl-gradle plugin on Windows (https://github.com/apple/pkl/pull/611[#611]).
[[release-0.26.2]]
== 0.26.2 (2024-07-18)
=== Fixes
* Fixes a possible race condition where multiple concurrent Pkl evaluations results in a thrown exception when downloading packages (https://github.com/apple/pkl/pull/584[#584]).
[[release-0.26.1]] [[release-0.26.1]]
== 0.26.1 (2024-06-28) == 0.26.1 (2024-06-28)
+1 -1
View File
@@ -1,7 +1,7 @@
# suppress inspection "UnusedProperty" for whole file # suppress inspection "UnusedProperty" for whole file
group=org.pkl-lang group=org.pkl-lang
version=0.26.1 version=0.26.3
# google-java-format requires jdk.compiler exports # google-java-format requires jdk.compiler exports
org.gradle.jvmargs= \ org.gradle.jvmargs= \
@@ -64,6 +64,16 @@ class BaseOptions : OptionGroup() {
throw CliException(message) throw CliException(message)
} }
} }
fun RawOption.associateProps():
OptionWithValues<Map<String, String>, Pair<String, String>, Pair<String, String>> {
return convert {
val parts = it.split("=")
if (parts.size <= 1) parts[0] to "true" else parts[0] to parts[1]
}
.multiple()
.toMap()
}
} }
private val defaults = CliBaseOptions() private val defaults = CliBaseOptions()
@@ -116,7 +126,7 @@ class BaseOptions : OptionGroup() {
metavar = "<name=value>", metavar = "<name=value>",
help = "External property to set (repeatable)." help = "External property to set (repeatable)."
) )
.associate() .associateProps()
val noCache: Boolean by val noCache: Boolean by
option(names = arrayOf("--no-cache"), help = "Disable caching of packages") option(names = arrayOf("--no-cache"), help = "Disable caching of packages")
@@ -215,7 +225,7 @@ class BaseOptions : OptionGroup() {
allowedModules = allowedModules.ifEmpty { null }, allowedModules = allowedModules.ifEmpty { null },
allowedResources = allowedResources.ifEmpty { null }, allowedResources = allowedResources.ifEmpty { null },
environmentVariables = envVars.ifEmpty { null }, environmentVariables = envVars.ifEmpty { null },
externalProperties = properties.mapValues { it.value.ifBlank { "true" } }.ifEmpty { null }, externalProperties = properties.ifEmpty { null },
modulePath = modulePath.ifEmpty { null }, modulePath = modulePath.ifEmpty { null },
workingDir = workingDir, workingDir = workingDir,
settings = settings, settings = settings,
@@ -45,10 +45,10 @@ class BaseCommandTest {
@Test @Test
fun `external properties without value default to 'true'`() { fun `external properties without value default to 'true'`() {
cmd.parse(arrayOf("-p", "flag1", "-p", "flag2", "-p", "FOO=bar")) cmd.parse(arrayOf("-p", "flag1", "-p", "flag2=", "-p", "FOO=bar"))
val props = cmd.baseOptions.baseOptions(emptyList()).externalProperties val props = cmd.baseOptions.baseOptions(emptyList()).externalProperties
assertThat(props).isEqualTo(mapOf("flag1" to "true", "flag2" to "true", "FOO" to "bar")) assertThat(props).isEqualTo(mapOf("flag1" to "true", "flag2" to "", "FOO" to "bar"))
} }
@Test @Test
@@ -45,7 +45,7 @@ public final class ImportGlobMemberBodyNode extends ExpressionNode {
@Override @Override
public Object executeGeneric(VirtualFrame frame) { public Object executeGeneric(VirtualFrame frame) {
var mapping = VmUtils.getObjectReceiver(frame); var mapping = VmUtils.getOwner(frame);
var path = (String) VmUtils.getMemberKey(frame); var path = (String) VmUtils.getMemberKey(frame);
return importModule(mapping, path); return importModule(mapping, path);
} }
@@ -33,7 +33,7 @@ public class ReadGlobMemberBodyNode extends ExpressionNode {
@Override @Override
public Object executeGeneric(VirtualFrame frame) { public Object executeGeneric(VirtualFrame frame) {
var mapping = VmUtils.getObjectReceiver(frame); var mapping = VmUtils.getOwner(frame);
var path = (String) VmUtils.getMemberKey(frame); var path = (String) VmUtils.getMemberKey(frame);
return readResource(mapping, path); return readResource(mapping, path);
} }
@@ -455,10 +455,9 @@ final class PackageResolvers {
private byte[] downloadUriToPathAndComputeChecksum(URI downloadUri, Path path) private byte[] downloadUriToPathAndComputeChecksum(URI downloadUri, Path path)
throws IOException, SecurityManagerException { throws IOException, SecurityManagerException {
Files.createDirectories(path.getParent());
var inputStream = openExternalUri(downloadUri); var inputStream = openExternalUri(downloadUri);
try (var digestInputStream = newDigestInputStream(inputStream)) { try (var digestInputStream = newDigestInputStream(inputStream)) {
Files.copy(digestInputStream, path); Files.copy(digestInputStream, path, StandardCopyOption.REPLACE_EXISTING);
return digestInputStream.getMessageDigest().digest(); return digestInputStream.getMessageDigest().digest();
} }
} }
@@ -472,7 +471,7 @@ final class PackageResolvers {
} }
try (var in = inputStream) { try (var in = inputStream) {
Files.createDirectories(path.getParent()); Files.createDirectories(path.getParent());
Files.copy(in, path); Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING);
if (checksums != null) { if (checksums != null) {
var digestInputStream = (DigestInputStream) inputStream; var digestInputStream = (DigestInputStream) inputStream;
var checksumBytes = digestInputStream.getMessageDigest().digest(); var checksumBytes = digestInputStream.getMessageDigest().digest();
@@ -490,7 +489,10 @@ final class PackageResolvers {
if (Files.exists(cachePath)) { if (Files.exists(cachePath)) {
return cachePath; return cachePath;
} }
var tmpPath = tmpDir.resolve(metadataRelativePath); Files.createDirectories(tmpDir);
var tmpPath =
Files.createTempFile(
tmpDir, IoUtils.encodePath(packageUri.toString().replaceAll("/", "-")), ".json");
try { try {
downloadMetadata(packageUri, requestUri, tmpPath, checksums); downloadMetadata(packageUri, requestUri, tmpPath, checksums);
Files.createDirectories(cachePath.getParent()); Files.createDirectories(cachePath.getParent());
@@ -539,7 +541,10 @@ final class PackageResolvers {
if (Files.exists(cachePath)) { if (Files.exists(cachePath)) {
return cachePath; return cachePath;
} }
var tmpPath = tmpDir.resolve(relativePath); Files.createDirectories(tmpDir);
var tmpPath =
Files.createTempFile(
tmpDir, IoUtils.encodePath(packageUri.toString().replaceAll("/", "-")), ".zip");
try { try {
var checksumBytes = var checksumBytes =
downloadUriToPathAndComputeChecksum(dependencyMetadata.getPackageZipUrl(), tmpPath); downloadUriToPathAndComputeChecksum(dependencyMetadata.getPackageZipUrl(), tmpPath);
@@ -33,7 +33,7 @@ import org.pkl.core.util.Nullable;
@TruffleLanguage.Registration( @TruffleLanguage.Registration(
id = "pkl", id = "pkl",
name = "Pkl", name = "Pkl",
version = "0.26.1", version = "0.26.3",
characterMimeTypes = VmLanguage.MIME_TYPE, characterMimeTypes = VmLanguage.MIME_TYPE,
contextPolicy = ContextPolicy.SHARED) contextPolicy = ContextPolicy.SHARED)
public final class VmLanguage extends TruffleLanguage<VmContext> { public final class VmLanguage extends TruffleLanguage<VmContext> {
@@ -16,6 +16,16 @@ examples {
import*("../../input-helper/globtest/**.pkl").keys.toListing() import*("../../input-helper/globtest/**.pkl").keys.toListing()
} }
["amended"] {
(import*("../../input-helper/globtest/**.pkl")) {
[[true]] {
output {
renderer = new YamlRenderer {}
}
}
}.toMap().values.map((it) -> it.output.text).join("\n---\n")
}
["globstar then up one level"] { ["globstar then up one level"] {
import*("../../input-helper/globtest/**/../*.pkl").keys.toListing() import*("../../input-helper/globtest/**/../*.pkl").keys.toListing()
} }
@@ -8,6 +8,14 @@ examples {
read*("globtest/*.txt") read*("globtest/*.txt")
} }
["amended"] {
(read*("../../input-helper/globtest/**.pkl")) {
[[true]] {
text = "hi"
}
}
}
["env:"] { ["env:"] {
// doesn't match names that include slashes // doesn't match names that include slashes
read*("env:*") read*("env:*")
@@ -21,6 +21,21 @@ examples {
"../../input-helper/globtest/child/moduleC.pkl" "../../input-helper/globtest/child/moduleC.pkl"
} }
} }
["amended"] {
"""
{}
---
name: moduleA
---
name: moduleB
---
name: child/moduleC
"""
}
["globstar then up one level"] { ["globstar then up one level"] {
new { new {
"../../input-helper/globtest/child/../module with [weird] ~!characters.pkl" "../../input-helper/globtest/child/../module with [weird] ~!characters.pkl"
@@ -50,6 +50,30 @@ examples {
} }
} }
} }
["amended"] {
new {
["../../input-helper/globtest/module with [weird] ~!characters.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/module%20with%20%5Bweird%5D%20~!characters.pkl"
text = "hi"
base64 = ""
}
["../../input-helper/globtest/moduleA.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/moduleA.pkl"
text = "hi"
base64 = "bmFtZSA9ICJtb2R1bGVBIgo="
}
["../../input-helper/globtest/moduleB.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/moduleB.pkl"
text = "hi"
base64 = "bmFtZSA9ICJtb2R1bGVCIgo="
}
["../../input-helper/globtest/child/moduleC.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/child/moduleC.pkl"
text = "hi"
base64 = "bmFtZSA9ICJjaGlsZC9tb2R1bGVDIgo="
}
}
}
["env:"] { ["env:"] {
new { new {
["env:NAME1"] = "value1" ["env:NAME1"] = "value1"
@@ -2,10 +2,9 @@ package org.pkl.core.packages
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatCode import org.assertj.core.api.Assertions.assertThatCode
import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.*
import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.parallel.Execution
import org.junit.jupiter.api.Test import org.junit.jupiter.api.parallel.ExecutionMode
import org.junit.jupiter.api.assertThrows
import org.pkl.commons.deleteRecursively import org.pkl.commons.deleteRecursively
import org.pkl.commons.readString import org.pkl.commons.readString
import org.pkl.commons.test.FileTestUtils import org.pkl.commons.test.FileTestUtils
@@ -44,7 +43,9 @@ class PackageResolversTest {
} }
} }
@Test // execute test 3 times to check concurrent writes
@RepeatedTest(3)
@Execution(ExecutionMode.CONCURRENT)
fun `get module bytes`() { fun `get module bytes`() {
val expectedBirdModule = packageRoot.resolve("birds@0.5.0/package/Bird.pkl").readString(StandardCharsets.UTF_8) val expectedBirdModule = packageRoot.resolve("birds@0.5.0/package/Bird.pkl").readString(StandardCharsets.UTF_8)
val assetUri = PackageAssetUri("package://localhost:0/birds@0.5.0#/Bird.pkl") val assetUri = PackageAssetUri("package://localhost:0/birds@0.5.0#/Bird.pkl")
@@ -134,6 +134,9 @@ public abstract class ModulesTask extends BasePklTask {
*/ */
private URI parsedModuleNotationToUri(Object notation) { private URI parsedModuleNotationToUri(Object notation) {
if (notation instanceof File file) { if (notation instanceof File file) {
if (file.isAbsolute()) {
return file.toPath().toUri();
}
return IoUtils.createUri(IoUtils.toNormalizedPathString(file.toPath())); return IoUtils.createUri(IoUtils.toNormalizedPathString(file.toPath()));
} else if (notation instanceof URI uri) { } else if (notation instanceof URI uri) {
return uri; return uri;