From d20d0d0eae3d3c10a0d75e10c24000cd8ce0c616 Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Wed, 13 Jul 2022 13:00:41 +0100 Subject: [PATCH 1/6] Move `write_bytes` to be part of `Vector`. Add tests. --- .../src/Network/Http/Response/Body.enso | 2 +- .../Base/0.0.0-dev/src/System/File.enso | 78 ++++++++-------- .../main/java/org/enso/base/Array_Utils.java | 7 ++ test/Table_Tests/src/Delimited_Read_Spec.enso | 2 +- test/Tests/src/System/File_Spec.enso | 89 +++++++++++++++++-- 5 files changed, 131 insertions(+), 47 deletions(-) create mode 100644 std-bits/base/src/main/java/org/enso/base/Array_Utils.java diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http/Response/Body.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http/Response/Body.enso index 689ecc0521dd..c36d4bb4b3fd 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http/Response/Body.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http/Response/Body.enso @@ -49,6 +49,6 @@ type Body Examples.get_geo_data.to_file Examples.scratch_file to_file : File -> File to_file file = - file.write_bytes self.bytes + self.bytes.write_bytes file file diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso index 550b72f52551..1e074b7a74d7 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso @@ -12,6 +12,7 @@ from Standard.Base.Data.Time as Time_Module import Time export Standard.Base.System.File.Option +polyglot java import org.enso.base.Array_Utils polyglot java import org.enso.base.Encoding_Utils polyglot java import java.io.InputStream as Java_Input_Stream polyglot java import java.io.OutputStream as Java_Output_Stream @@ -289,41 +290,6 @@ type File bytes = self.read_bytes Text.from_bytes bytes encoding on_problems - ## Appends a number of bytes at the end of this file. - - Arguments: - - contents: A vector of bytes to append to the file. - - > Example - Append the bytes of the text "hello" to a file. - - import Standard.Examples - - example_append_bytes = Examples.scratch_file.append_bytes "hello".utf_8 - append_bytes : Vector.Vector -> Nothing ! File_Error - append_bytes contents = - opts = [Option.Append, Option.Create] - self.with_output_stream opts (_.write_bytes contents) - - ## Writes a number of bytes into this file, replacing any existing contents. - - Arguments: - - contents: The vector of bytes to write into the file. - - If the file does not exist, it will be created. - - > Example - Write the bytes of the text "hello" to a file. - - import Standard.Examples - - example_write_bytes = Examples.scratch_file.write_bytes "hello".utf_8 - write_bytes : Vector.Vector -> Nothing ! File_Error - write_bytes contents = - opts = [Option.Write, Option.Create, Option.Truncate_Existing] - self.with_output_stream opts (_.write_bytes contents) - Nothing - ## Join two path segments together. Arguments: @@ -1066,9 +1032,9 @@ get_cwd = @Builtin_Method "File.get_cwd" get_file : Text -> File get_file path = @Builtin_Method "File.get_file" -## Writes (or appends) the specified bytes to the specified file. - The behavior specified in the `existing_file` parameter will be used if the - file exists. +## Writes (or appends) the text to the specified file using the supplied + encoding. The behavior specified in the `existing_file` parameter will be + used if the file exists. Arguments: - path: The path to the target file. @@ -1090,3 +1056,39 @@ Text.write path encoding=Encoding.utf_8 on_existing_file=Existing_File_Behavior. file = new path on_existing_file.write file stream-> stream.write_bytes bytes + +## Writes (or appends) the Vector of bytes into the specified file. The behavior + specified in the `existing_file` parameter will be used if the file exists. + + Arguments: + - path: The path to the target file. + - on_existing_file: Specifies how to proceed if the file already exists. + + If the Vector contains any item which is not a `Byte`, an + `Illegal_Argument_Error` will be raised. + If the path to the parent location cannot be found or the filename is + invalid, a `File_Not_Found` is raised. + If another error occurs, such as access denied, an `Io_Error` is raised. + Otherwise, the file is created with the encoded text written to it. + + > Example + Write the bytes of the text "hello" to a file. + + import Standard.Examples + + example_write_bytes = "hello".utf_8.write_bytes Examples.scratch_file + > Example + Append the bytes of the text "hello" to a file. + + import Standard.Examples + + example_append_bytes = "hello".utf_8.write_bytes Examples.scratch_file Existing_File_Behavior.Append +Vector.Vector.write_bytes : (File|Text) -> Existing_File_Behavior -> Nothing ! Illegal_Argument_Error | File_Not_Found | Io_Error | File_Already_Exists_Error +Vector.Vector.write_bytes path on_existing_file=Existing_File_Behavior.Backup = + Panic.catch Unsupported_Argument_Types handler=(Error.throw (Illegal_Argument_Error "Can only write Vector's of bytes.")) <| + ## Convert to a byte array before writing - and fail early if a problem. + byte_array = Array_Utils.toByteArray self.to_array + + file = new path + on_existing_file.write file stream-> + stream.write_bytes (Vector.Vector byte_array) diff --git a/std-bits/base/src/main/java/org/enso/base/Array_Utils.java b/std-bits/base/src/main/java/org/enso/base/Array_Utils.java new file mode 100644 index 000000000000..dbf1dcfbf13d --- /dev/null +++ b/std-bits/base/src/main/java/org/enso/base/Array_Utils.java @@ -0,0 +1,7 @@ +package org.enso.base; + +public class Array_Utils { + public static byte[] toByteArray(byte[] input) { + return input; + } +} diff --git a/test/Table_Tests/src/Delimited_Read_Spec.enso b/test/Table_Tests/src/Delimited_Read_Spec.enso index 5097a5a9cd08..bfc7b71e3f61 100644 --- a/test/Table_Tests/src/Delimited_Read_Spec.enso +++ b/test/Table_Tests/src/Delimited_Read_Spec.enso @@ -148,7 +148,7 @@ spec = Test.specify "should report errors when encountering malformed characters" <| utf8_file = (enso_project.data / "transient" / "utf8_invalid.csv") utf8_bytes = [97, 44, 98, 44, 99, 10, -60, -123, 44, -17, -65, -65, 44, -61, 40, -61, 40, 10] - utf8_file.write_bytes utf8_bytes + utf8_bytes.write_bytes utf8_file action_1 on_problems = utf8_file.read (Delimited "," headers=True) on_problems tester_1 table = diff --git a/test/Tests/src/System/File_Spec.enso b/test/Tests/src/System/File_Spec.enso index 87650f3dd586..02ea518eddf4 100644 --- a/test/Tests/src/System/File_Spec.enso +++ b/test/Tests/src/System/File_Spec.enso @@ -120,14 +120,89 @@ spec = contents_2.should .start_with "Cupcake ipsum dolor sit amet." Test.group "write operations" <| + data = [32, 127, -128, 0] + data_2 = [10, 15, 20, 30] + transient = enso_project.data / "transient" - Test.specify "should allow to append to files" <| + Test.specify "should allow to writing bytes to a new file" <| + f = transient / "new_file.dat" + f.delete_if_exists + f.exists.should_be_false + data.write_bytes f + f.exists.should_be_true + f.read_bytes.should_equal data + f.delete_if_exists + + Test.specify "should backup a file when overwriting with new bytes" <| + f = transient / "work.txt" + f.delete_if_exists + f_bak = transient / "work.txt.bak" + f_bak.delete_if_exists + data.write_bytes f + f.exists.should_be_true + data_2.write_bytes f + f.read_bytes.should_equal data_2 + f_bak.exists.should_be_true + f.delete_if_exists + f_bak.delete_if_exists + + Test.specify "should allow to overwriting a file with new bytes" <| f = transient / "work.txt" f.delete_if_exists + f_bak = transient / "work.txt.bak" + f_bak.delete_if_exists + data.write_bytes f + f.exists.should_be_true + data_2.write_bytes f on_existing_file=Existing_File_Behavior.Overwrite + f.read_bytes.should_equal data_2 + f_bak.exists.should_be_false + f.delete_if_exists + + Test.specify "should allow to appending bytes to a new file" <| + f = transient / "new_file.dat" + f.delete_if_exists f.exists.should_be_false - "line 1!".write f on_existing_file=Existing_File_Behavior.Append + data.write_bytes f + data_2.write_bytes f on_existing_file=Existing_File_Behavior.Append + f.read_bytes.should_equal (data + data_2) + f.delete_if_exists + + Test.specify "should fail will Illegal_Argument_Error when trying to write invalid byte vector" <| + f = transient / "work.txt" + f.delete_if_exists + f.exists.should_be_false + [0, 1, 256].write_bytes f . should_fail_with Illegal_Argument_Error + [0, 1, Nothing].write_bytes f . should_fail_with Illegal_Argument_Error + + Test.specify "should not change the file when invalid byte vector" <| + f = transient / "work.txt" + f.delete_if_exists + f_bak = transient / "work.txt.bak" + f_bak.delete_if_exists + data.write_bytes f + [0, 1, 256].write_bytes f . should_fail_with Illegal_Argument_Error + f.read_bytes.should_equal data + f_bak.exists.should_be_false + [0, 1, 256].write_bytes f on_existing_file=Existing_File_Behavior.Overwrite . should_fail_with Illegal_Argument_Error + f.read_bytes.should_equal data + [0, 1, 256].write_bytes f on_existing_file=Existing_File_Behavior.Append . should_fail_with Illegal_Argument_Error + f.read_bytes.should_equal data + f.delete_if_exists + + Test.specify "should allow to writing text to a new file" <| + f = transient / "work.txt" + f.delete_if_exists + f.exists.should_be_false + "line 1!".write f f.exists.should_be_true f.read_text.should_equal "line 1!" + f.delete + f.exists.should_be_false + + Test.specify "should allow to appending text to a file" <| + f = transient / "work.txt" + f.delete_if_exists + "line 1!".write f on_existing_file=Existing_File_Behavior.Append '\nline 2!'.write f on_existing_file=Existing_File_Behavior.Append f.read_text.should_equal 'line 1!\nline 2!' f.delete @@ -140,8 +215,8 @@ spec = "line 1!".write f on_existing_file=Existing_File_Behavior.Overwrite . should_equal Nothing f.exists.should_be_true f.read_text.should_equal "line 1!" - 'line 2!'.write f on_existing_file=Existing_File_Behavior.Overwrite . should_equal Nothing - f.read_text.should_equal 'line 2!' + "line 2!".write f on_existing_file=Existing_File_Behavior.Overwrite . should_equal Nothing + f.read_text.should_equal "line 2!" f.delete f.exists.should_be_false @@ -153,7 +228,7 @@ spec = f.exists.should_be_true f.read_text.should_equal "line 1!" "line 2!".write f on_existing_file=Existing_File_Behavior.Error . should_fail_with File_Already_Exists_Error - f.read_text.should_equal 'line 1!' + f.read_text.should_equal "line 1!" f.delete f.exists.should_be_false @@ -180,8 +255,8 @@ spec = n3.delete_if_exists "line 2!".write f . should_equal Nothing - f.read_text.should_equal 'line 2!' - bak.read_text.should_equal 'line 1!' + f.read_text.should_equal "line 2!" + bak.read_text.should_equal "line 1!" if n3.exists then Test.fail "The temporary file should have been cleaned up." written_news.each n-> From 1d81f175e17b45e78eecaa4e6345f4036f368ad3 Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Wed, 13 Jul 2022 13:57:07 +0100 Subject: [PATCH 2/6] PR comments. --- .../lib/Standard/Base/0.0.0-dev/src/System/File.enso | 9 +++++---- .../base/src/main/java/org/enso/base/Array_Utils.java | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso index 1e074b7a74d7..f47f156e4350 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso @@ -1065,7 +1065,8 @@ Text.write path encoding=Encoding.utf_8 on_existing_file=Existing_File_Behavior. - on_existing_file: Specifies how to proceed if the file already exists. If the Vector contains any item which is not a `Byte`, an - `Illegal_Argument_Error` will be raised. + `Illegal_Argument_Error` will be raised. Enso follows the Java convention, + that a `Byte` is between -128 and 127. If the path to the parent location cannot be found or the filename is invalid, a `File_Not_Found` is raised. If another error occurs, such as access denied, an `Io_Error` is raised. @@ -1076,18 +1077,18 @@ Text.write path encoding=Encoding.utf_8 on_existing_file=Existing_File_Behavior. import Standard.Examples - example_write_bytes = "hello".utf_8.write_bytes Examples.scratch_file + example_write_bytes = [32, 63, 95, -120, -43].write_bytes Examples.scratch_file > Example Append the bytes of the text "hello" to a file. import Standard.Examples - example_append_bytes = "hello".utf_8.write_bytes Examples.scratch_file Existing_File_Behavior.Append + example_append_bytes = [32, 63, 95, -120, -43].write_bytes Examples.scratch_file Existing_File_Behavior.Append Vector.Vector.write_bytes : (File|Text) -> Existing_File_Behavior -> Nothing ! Illegal_Argument_Error | File_Not_Found | Io_Error | File_Already_Exists_Error Vector.Vector.write_bytes path on_existing_file=Existing_File_Behavior.Backup = Panic.catch Unsupported_Argument_Types handler=(Error.throw (Illegal_Argument_Error "Can only write Vector's of bytes.")) <| ## Convert to a byte array before writing - and fail early if a problem. - byte_array = Array_Utils.toByteArray self.to_array + byte_array = Array_Utils.ensureByteArray self.to_array file = new path on_existing_file.write file stream-> diff --git a/std-bits/base/src/main/java/org/enso/base/Array_Utils.java b/std-bits/base/src/main/java/org/enso/base/Array_Utils.java index dbf1dcfbf13d..c8d942bb7dd4 100644 --- a/std-bits/base/src/main/java/org/enso/base/Array_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/Array_Utils.java @@ -1,7 +1,7 @@ package org.enso.base; public class Array_Utils { - public static byte[] toByteArray(byte[] input) { + public static byte[] ensureByteArray(byte[] input) { return input; } } From fbb7dc359c17e4eeb3f6dbdeec25ceec7d7cc761 Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Wed, 13 Jul 2022 14:10:14 +0100 Subject: [PATCH 3/6] Update distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Radosław Waśko --- distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso index f47f156e4350..d71d44ba751e 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso @@ -1086,8 +1086,8 @@ Text.write path encoding=Encoding.utf_8 on_existing_file=Existing_File_Behavior. example_append_bytes = [32, 63, 95, -120, -43].write_bytes Examples.scratch_file Existing_File_Behavior.Append Vector.Vector.write_bytes : (File|Text) -> Existing_File_Behavior -> Nothing ! Illegal_Argument_Error | File_Not_Found | Io_Error | File_Already_Exists_Error Vector.Vector.write_bytes path on_existing_file=Existing_File_Behavior.Backup = - Panic.catch Unsupported_Argument_Types handler=(Error.throw (Illegal_Argument_Error "Can only write Vector's of bytes.")) <| - ## Convert to a byte array before writing - and fail early if a problem. + Panic.catch Unsupported_Argument_Types handler=(Error.throw (Illegal_Argument_Error "Only Vectors consisting of bytes (integers in the range from -128 to 127) are supported by the `write_bytes` method.")) <| + ## Convert to a byte array before writing - and fail early if there is any problem. byte_array = Array_Utils.ensureByteArray self.to_array file = new path From ee72aeef6cad58b8642c6b7d98ccc9c2b19ef6bf Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Wed, 13 Jul 2022 14:21:35 +0100 Subject: [PATCH 4/6] Last couple of PR comments. --- std-bits/base/src/main/java/org/enso/base/Array_Utils.java | 6 ++++++ test/Tests/src/System/File_Spec.enso | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/std-bits/base/src/main/java/org/enso/base/Array_Utils.java b/std-bits/base/src/main/java/org/enso/base/Array_Utils.java index c8d942bb7dd4..be43f52bf325 100644 --- a/std-bits/base/src/main/java/org/enso/base/Array_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/Array_Utils.java @@ -1,6 +1,12 @@ package org.enso.base; public class Array_Utils { + /** + * This function forces the polyglot conversion of an Enso array into a `byte[]`. + * This allows for asserting that it is a valid `byte[]`. + * @param input the converted array. + * @return the `input` unchanged. + */ public static byte[] ensureByteArray(byte[] input) { return input; } diff --git a/test/Tests/src/System/File_Spec.enso b/test/Tests/src/System/File_Spec.enso index 02ea518eddf4..96b8a6cb11a2 100644 --- a/test/Tests/src/System/File_Spec.enso +++ b/test/Tests/src/System/File_Spec.enso @@ -158,7 +158,7 @@ spec = f_bak.exists.should_be_false f.delete_if_exists - Test.specify "should allow to appending bytes to a new file" <| + Test.specify "should allow appending bytes to a new file" <| f = transient / "new_file.dat" f.delete_if_exists f.exists.should_be_false @@ -174,7 +174,7 @@ spec = [0, 1, 256].write_bytes f . should_fail_with Illegal_Argument_Error [0, 1, Nothing].write_bytes f . should_fail_with Illegal_Argument_Error - Test.specify "should not change the file when invalid byte vector" <| + Test.specify "should not change the file when trying to write an invalid byte vector" <| f = transient / "work.txt" f.delete_if_exists f_bak = transient / "work.txt.bak" From db7d8638c008205acc04bc0aa0ee9f73b523fc66 Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Thu, 14 Jul 2022 08:58:48 +0100 Subject: [PATCH 5/6] Formatting and examples --- .../lib/Standard/Base/0.0.0-dev/src/System/File.enso | 8 ++++---- .../base/src/main/java/org/enso/base/Array_Utils.java | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso index d71d44ba751e..9c82b6d664f0 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso @@ -1073,17 +1073,17 @@ Text.write path encoding=Encoding.utf_8 on_existing_file=Existing_File_Behavior. Otherwise, the file is created with the encoded text written to it. > Example - Write the bytes of the text "hello" to a file. + Write the UTF-8 bytes of the text "$£§€¢" to a file. import Standard.Examples - example_write_bytes = [32, 63, 95, -120, -43].write_bytes Examples.scratch_file + [36, -62, -93, -62, -89, -30, -126, -84, -62, -94].write_bytes Examples.scratch_file > Example - Append the bytes of the text "hello" to a file. + Append the UTF-8 bytes of the text "$£§€¢" to a file. import Standard.Examples - example_append_bytes = [32, 63, 95, -120, -43].write_bytes Examples.scratch_file Existing_File_Behavior.Append + [36, -62, -93, -62, -89, -30, -126, -84, -62, -94].write_bytes Examples.scratch_file.write_bytes Examples.scratch_file Existing_File_Behavior.Append Vector.Vector.write_bytes : (File|Text) -> Existing_File_Behavior -> Nothing ! Illegal_Argument_Error | File_Not_Found | Io_Error | File_Already_Exists_Error Vector.Vector.write_bytes path on_existing_file=Existing_File_Behavior.Backup = Panic.catch Unsupported_Argument_Types handler=(Error.throw (Illegal_Argument_Error "Only Vectors consisting of bytes (integers in the range from -128 to 127) are supported by the `write_bytes` method.")) <| diff --git a/std-bits/base/src/main/java/org/enso/base/Array_Utils.java b/std-bits/base/src/main/java/org/enso/base/Array_Utils.java index be43f52bf325..4933aa251591 100644 --- a/std-bits/base/src/main/java/org/enso/base/Array_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/Array_Utils.java @@ -2,8 +2,9 @@ public class Array_Utils { /** - * This function forces the polyglot conversion of an Enso array into a `byte[]`. - * This allows for asserting that it is a valid `byte[]`. + * This function forces the polyglot conversion of an Enso array into a `byte[]`. This allows for + * asserting that it is a valid `byte[]`. + * * @param input the converted array. * @return the `input` unchanged. */ From ad0a7af42704be41db6aacd1274fd1bd4c6afa5b Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Thu, 14 Jul 2022 12:05:31 +0100 Subject: [PATCH 6/6] Changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eaabbff335c..2b3726c39094 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -152,6 +152,7 @@ - [Added support for custom encodings in `File_Format.Delimited` writing.][3564] - [Allow filtering caught error type in `Error.catch`.][3574] - [Implemented `Append` mode for `File_Format.Delimited`.][3573] +- [Added `Vector.write_bytes` function and removed old `File.write_bytes`][3583] [debug-shortcuts]: https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug @@ -243,6 +244,7 @@ [3564]: https://github.com/enso-org/enso/pull/3564 [3574]: https://github.com/enso-org/enso/pull/3574 [3573]: https://github.com/enso-org/enso/pull/3573 +[3583]: https://github.com/enso-org/enso/pull/3583 #### Enso Compiler