Add Execution Context control to Text.write (#6459)

- Adjusted `Context.is_enabled` to support default argument (moved built in so can have defaults).
- Made `environment` case-insensitive.
- Bug fix for play button.
- Short hand to execute within an enabled context.
- Forbid file writing if the Output context is disabled with a `Forbidden_Operation` error.
- Add temporary file support via `File.create_temporary_file` which is deleted on exit of JVM.
- Execution Context first pass in `Text.write`.
- Added dry run warning.
- Writes to a temporary file if disabled.
- Created a `DryRunFileManager` which will create and manage the temporary files.
- Added `format` dropdown to `File.read` and `Data.read`.
- Renamed `JSON_File` to `JSON_Format` to be consistent.

(still to unit test).
This commit is contained in:
James Dunkerley 2023-04-29 09:39:18 +01:00 committed by GitHub
parent cdd0065800
commit 6b0c682b08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 295 additions and 109 deletions

View File

@ -398,6 +398,8 @@
`Time_Of_Day`, `Time_Zone`, and `URI` to `Text`.][6404]
- [Implemented `create_database_table` allowing upload of in-memory
tables.][6429]
- [Added execution context control to writing files and dry run capabilities to
`Text.write`.][6459]
[debug-shortcuts]:
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
@ -602,6 +604,7 @@
[6404]: https://github.com/enso-org/enso/pull/6404
[6347]: https://github.com/enso-org/enso/pull/6347
[6429]: https://github.com/enso-org/enso/pull/6429
[6459]: https://github.com/enso-org/enso/pull/6459
#### Enso Compiler

View File

@ -157,7 +157,7 @@ trait API {
/// Restart the program execution.
#[MethodInput=RecomputeInput, rpc_name="executionContext/recompute"]
fn recompute(&self, context_id: ContextId, invalidated_expressions: InvalidatedExpressions, mode: Option<ExecutionEnvironment>) -> ();
fn recompute(&self, context_id: ContextId, invalidated_expressions: InvalidatedExpressions, execution_environment: Option<ExecutionEnvironment>) -> ();
/// Obtain the full suggestions database.
#[MethodInput=GetSuggestionsDatabaseInput, rpc_name="search/getSuggestionsDatabase"]

View File

@ -16,10 +16,9 @@ import project.Network.HTTP.Request.Request
import project.Network.HTTP.Request_Error
import project.Nothing.Nothing
import project.System.File.File
import project.System.File_Format.Auto_Detect
import project.System.File_Format.File_Format
from project.Data.Boolean import Boolean, True, False
from project.System.File_Format import Auto_Detect, File_Format, format_widget
## ALIAS Load, Open
Reads a file into Enso.
@ -56,6 +55,7 @@ from project.Data.Boolean import Boolean, True, False
import Standard.Examples
example_xls_to_table = Data.read Examples.xls (Excel (Worksheet 'Dates'))
@format format_widget
read : Text | File -> File_Format -> Problem_Behavior -> Any ! File_Error
read path format=Auto_Detect (on_problems=Problem_Behavior.Report_Warning) =
File.new path . read format on_problems
@ -81,6 +81,7 @@ read path format=Auto_Detect (on_problems=Problem_Behavior.Report_Warning) =
import Standard.Examples
example_read = Data.read_text Examples.csv_path
@encoding Encoding.default_widget
read_text : (Text | File) -> Encoding -> Problem_Behavior -> Text
read_text path (encoding=Encoding.utf_8) (on_problems=Problem_Behavior.Report_Warning) =
File.new path . read_text encoding on_problems

View File

@ -9,8 +9,19 @@ polyglot java import java.nio.charset.Charset
polyglot java import java.nio.charset.UnsupportedCharsetException
polyglot java import org.enso.base.Text_Utils
from project.Metadata.Widget import Single_Choice
from project.Metadata.Choice import Option
import project.Metadata.Display
## Represents a character encoding.
type Encoding
## PRIVATE
Gets the default drop down option for this encoding.
default_widget : Single_Choice
default_widget =
values = [Option "UTF-8" "Encoding.utf_8", Option "ASCII" "Encoding.ascii", Option "UTF-16LE" "Encoding.utf_16_le", Option "UTF-16BE" "Encoding.utf_16_be", Option "UTF-32LE" "Encoding.utf_32_le", Option "UTF-32BE" "Encoding.utf_32_be", Option "Windows-1250" "Encoding.windows_1250", Option "Windows-1251" "Encoding.windows_1251", Option "Windows-1252" "Encoding.windows_1252", Option "Windows-1253" "Encoding.windows_1253", Option "Windows-1254" "Encoding.windows_1254", Option "Windows-1255" "Encoding.windows_1255", Option "Windows-1256" "Encoding.windows_1256", Option "Windows-1257" "Encoding.windows_1257", Option "Windows-1258" "Encoding.windows_1258"]
Single_Choice values=values display=Display.When_Modified
## PRIVATE
ADVANCED
Get all available character sets from Java as Encodings.

View File

@ -644,6 +644,7 @@ Text.is_whitespace self =
Get the ASCII bytes of the text "Hello".
"Hello".bytes (Encoding.ascii)
@encoding Encoding.default_widget
Text.bytes : Encoding -> Problem_Behavior -> Vector Integer
Text.bytes self encoding on_problems=Problem_Behavior.Report_Warning =
result = Encoding_Utils.get_bytes self (encoding . to_java_charset)
@ -664,6 +665,7 @@ Text.bytes self encoding on_problems=Problem_Behavior.Report_Warning =
Get the ASCII bytes of the text "Hello".
"Hello".bytes (Encoding.ascii)
@encoding Encoding.default_widget
Text.from_bytes : Vector Integer -> Encoding -> Problem_Behavior -> Text
Text.from_bytes bytes encoding on_problems=Problem_Behavior.Report_Error =
result = Encoding_Utils.from_bytes bytes.to_array (encoding . to_java_charset)

View File

@ -336,3 +336,16 @@ type Forbidden_Operation
Convert the Forbidden_Operation error to a human-readable format.
to_display_text : Text
to_display_text self = "Forbidden operation: "+self.operation+"."
type Dry_Run_Operation
## PRIVATE
A warning that the operation has only been performed in a test mode.
Arguments:
- message: The message to be displayed.
Warning message
## PRIVATE
Convert the Dry_Run_Operation to a human-readable format.
to_display_text : Text
to_display_text self = self.message

View File

@ -63,7 +63,7 @@ export project.Warning.Warning
from project.Data.Boolean export Boolean, True, False
from project.Function export all
from project.Data.Numbers export Number, Integer, Decimal
from project.System.File_Format export File_Format, Plain_Text_Format, Plain_Text, Bytes, Infer, Auto_Detect, JSON_File
from project.System.File_Format export File_Format, Plain_Text_Format, Plain_Text, Bytes, Infer, Auto_Detect, JSON_Format
import project.Data
import project.Data.Filter_Condition.Filter_Condition

View File

@ -1,6 +1,8 @@
import project.Any.Any
import project.Data.Array.Array
import project.Data.Boolean.Boolean
import project.Data.Text.Case.Case
import project.Data.Text.Extensions
import project.Data.Text.Text
import project.Data.Vector.Vector
import project.Errors.Common.Forbidden_Operation
@ -159,7 +161,18 @@ type Context
- environment: Name of the execution environment.
- context: The context to enable.
is_enabled : Text -> Boolean
is_enabled self environment=Runtime.current_execution_environment = @Builtin_Method "Context.is_enabled"
is_enabled self environment=Runtime.current_execution_environment =
self.is_enabled_builtin (environment.to_case Case.Lower)
## PRIVATE
is_enabled_builtin : Text -> Boolean
is_enabled_builtin self environment = @Builtin_Method "Context.is_enabled_builtin"
## PRIVATE
Run an action with the Context enabled.
with_enabled : Function -> Any
with_enabled self ~action =
with_enabled_context self Runtime.current_execution_environment action
## PRIVATE
@ -179,7 +192,8 @@ current_execution_environment = @Builtin_Method "Runtime.current_execution_envir
- context: The context to enable.
- action: Action to be performed with the context enabled.
with_enabled_context : Context -> Text -> Function -> Any
with_enabled_context context environment=Runtime.current_execution_environment ~action = with_enabled_context_builtin context environment action
with_enabled_context context environment=Runtime.current_execution_environment ~action =
with_enabled_context_builtin context (environment.to_case Case.Lower) action
## PRIVATE
ADVANCED
@ -205,7 +219,8 @@ with_enabled_context_builtin context environment ~action = @Builtin_Method "Runt
- context: The context to disable.
- action: Action to be performed with the context disabled.
with_disabled_context : Context -> Text -> Function -> Any
with_disabled_context context environment=Runtime.current_execution_environment ~action = with_disabled_context_builtin context environment action
with_disabled_context context environment=Runtime.current_execution_environment ~action =
with_disabled_context_builtin context (environment.to_case Case.Lower) action
## PRIVATE
ADVANCED

View File

@ -11,6 +11,7 @@ import project.Data.Text.Text
import project.Data.Time.Time_Of_Day.Time_Of_Day
import project.Data.Vector.Vector
import project.Error.Error
import project.Errors.Common.Forbidden_Operation
import project.Errors.Encoding_Error.Encoding_Error
import project.Errors.File_Error.File_Error
import project.Errors.Illegal_Argument.Illegal_Argument
@ -18,21 +19,23 @@ import project.Errors.Problem_Behavior.Problem_Behavior
import project.Meta
import project.Nothing.Nothing
import project.Panic.Panic
import project.Runtime.Context
import project.Runtime.Managed_Resource.Managed_Resource
import project.System.File.File_Access.File_Access
import project.System.File_Format.Auto_Detect
import project.System.File_Format.File_Format
import project.System.File.File_Permissions.File_Permissions
from project.Data.Boolean import Boolean, True, False
from project.System.File_Format import Auto_Detect, File_Format, format_widget
import project.Metadata.Widget
from project.Metadata.Choice import Option
import project.Metadata.Display
polyglot java import org.enso.base.DryRunFileManager
polyglot java import org.enso.base.Encoding_Utils
polyglot java import org.enso.base.encoding.ReportingStreamDecoder
polyglot java import org.enso.base.encoding.ReportingStreamEncoder
polyglot java import java.io.File as Java_File
polyglot java import java.io.InputStream as Java_Input_Stream
polyglot java import java.io.OutputStream as Java_Output_Stream
polyglot java import java.nio.file.FileSystems
@ -64,6 +67,28 @@ type File
_ : File -> path
_ -> Error.throw (Illegal_Argument.Error "new file should be either a File or a Text")
## Creates a temporary file which will be deleted when Enso exits.
create_temporary_file : Text -> Text -> File
create_temporary_file prefix="temp" suffix=".tmp" =
java_file = Java_File.createTempFile prefix suffix
java_file.deleteOnExit
File.new java_file.getAbsolutePath
## PRIVATE
Create a dry run temporary file which will be deleted when Enso exits.
For an absolute path the same temporary file is returned.
If this file is a temporary file that was generated by
`create_dry_run_file` on another file, it is returned as-is.
create_dry_run_file : Boolean -> File ! File_Error
create_dry_run_file self copy_original=False =
temp_path = DryRunFileManager.getTemporaryFile self.absolute.path
if temp_path.is_nothing then Error.throw (File_Error.IO_Error "Unable to create a temporary file.") else
temp = File.new temp_path
if self.exists && copy_original then
self.copy_to temp replace_existing=True
temp
## ALIAS Current Directory
Returns the current working directory (CWD) of the current program.
@ -116,7 +141,27 @@ type File
file.with_output_stream [File_Access.Create, File_Access.Write] action
with_output_stream : Vector File_Access -> (Output_Stream -> Any ! File_Error) -> Any ! File_Error
with_output_stream self open_options action =
Managed_Resource.bracket (self.new_output_stream open_options) (_.close) action
new_output_stream : File -> Vector File_Access -> Output_Stream ! File_Error
new_output_stream file open_options =
opts = open_options . map (_.to_java) . to_array
stream = File_Error.handle_java_exceptions file <|
file.output_stream_builtin opts
## We re-wrap the File Not Found error to return the parent directory
instead of the file itself - because the file that is being written
may not exist and it will not be an error, it is the parent directory
that does not exist is what prevents the write operation from
succeeding.
## Until #5792 properly fixes catch, we cannot catch
`File_Error.Not_Found` specifically, so instead we catch all
`File_Error`s and match the needed one.
stream_2 = stream.catch File_Error error-> case error of
File_Error.Not_Found file_path -> Error.throw (File_Error.Not_Found file_path.parent)
_ -> stream
resource = Managed_Resource.register stream_2 close_stream
Output_Stream.Value file resource
if Context.Output.is_enabled.not then Error.throw (Forbidden_Operation.Error "File writing is forbidden as the Output context is disabled") else
Managed_Resource.bracket (new_output_stream self open_options) (_.close) action
## PRIVATE
Creates a new output stream for this file. Recommended to use
@ -125,8 +170,8 @@ type File
Arguments:
- options: A vector of `File_Access` objects determining how to open
the stream. These options set the access properties of the stream.
output_stream : Vector File_Access -> Output_Stream
output_stream self options = @Builtin_Method "File.output_stream"
output_stream_builtin : Vector File_Access -> Output_Stream
output_stream_builtin self options = @Builtin_Method "File.output_stream_builtin"
## PRIVATE
Creates a new input stream for this file. Recommended to use
@ -196,6 +241,7 @@ type File
import Standard.Examples
example_xls_to_table = Examples.xls.read (Excel (Worksheet 'Dates'))
@format format_widget
read : File_Format -> Problem_Behavior -> Any ! File_Error
read self format=Auto_Detect (on_problems=Problem_Behavior.Report_Warning) =
format.read self on_problems
@ -231,6 +277,7 @@ type File
import Standard.Examples
example_read = Examples.csv.read
@encoding Encoding.default_widget
read_text : Encoding -> Problem_Behavior -> Text ! File_Error
read_text self (encoding=Encoding.utf_8) (on_problems=Problem_Behavior.Report_Warning) =
file = File.new self
@ -366,7 +413,15 @@ type File
example_is_directory =
(Examples.data_dir / "my_directory") . create_directory
create_directory : Nothing
create_directory self = @Builtin_Method "File.create_directory"
create_directory self =
if Context.Output.is_enabled.not then Error.throw (Forbidden_Operation.Error "Directory creation is forbidden as the Output context is disabled") else
self.create_directory_builtin
## PRIVATE
Creates the directory represented by this file if it did not exist.
create_directory_builtin : Nothing
create_directory_builtin self = @Builtin_Method "File.create_directory_builtin"
## Checks whether the file exists and is a regular file.
@ -491,7 +546,8 @@ type File
file.delete
delete : Nothing ! File_Error
delete self =
File_Error.handle_java_exceptions self self.delete_builtin
if Context.Output.is_enabled.not then Error.throw (Forbidden_Operation.Error "File deleting is forbidden as the Output context is disabled") else
File_Error.handle_java_exceptions self self.delete_builtin
## Moves the file to the specified destination.
@ -501,11 +557,12 @@ type File
destination file already exists. Defaults to `False`.
copy_to : File -> Boolean -> Nothing ! File_Error
copy_to self destination replace_existing=False =
File_Error.handle_java_exceptions self <| case replace_existing of
True ->
copy_options = [StandardCopyOption.REPLACE_EXISTING].to_array
self.copy_builtin destination copy_options
False -> self.copy_builtin destination Array.empty
if Context.Output.is_enabled.not then Error.throw (Forbidden_Operation.Error "File copying is forbidden as the Output context is disabled") else
File_Error.handle_java_exceptions self <| case replace_existing of
True ->
copy_options = [StandardCopyOption.REPLACE_EXISTING].to_array
self.copy_builtin destination copy_options
False -> self.copy_builtin destination Array.empty
## Moves the file to the specified destination.
@ -515,11 +572,12 @@ type File
destination file already exists. Defaults to `False`.
move_to : File -> Boolean -> Nothing ! File_Error
move_to self destination replace_existing=False =
File_Error.handle_java_exceptions self <| case replace_existing of
True ->
copy_options = [StandardCopyOption.REPLACE_EXISTING].to_array
self.move_builtin destination copy_options
False -> self.move_builtin destination Array.empty
if Context.Output.is_enabled.not then Error.throw (Forbidden_Operation.Error "File moving is forbidden as the Output context is disabled") else
File_Error.handle_java_exceptions self <| case replace_existing of
True ->
copy_options = [StandardCopyOption.REPLACE_EXISTING].to_array
self.move_builtin destination copy_options
False -> self.move_builtin destination Array.empty
## Deletes the file if it exists on disk.
@ -553,35 +611,6 @@ type File
resource = Managed_Resource.register stream close_stream
Input_Stream.Value self resource
## PRIVATE
ADVANCED
Returns a new output stream for this file.
Arguments:
- open_options: A vector of `File_Access` objects determining how to open
the stream. These options set the access properties of the stream.
The returned stream should be closed as soon as it is not used anymore.
The `with_output_stream` method should be preferred whenever possible.
new_output_stream : Vector File_Access -> Output_Stream ! File_Error
new_output_stream self open_options =
opts = open_options . map (_.to_java) . to_array
stream = File_Error.handle_java_exceptions self <|
self.output_stream opts
## We re-wrap the File Not Found error to return the parent directory
instead of the file itself - because the file that is being written
may not exist and it will not be an error, it is the parent directory
that does not exist is what prevents the write operation from
succeeding.
## Until #5792 properly fixes catch, we cannot catch
`File_Error.Not_Found` specifically, so instead we catch all
`File_Error`s and match the needed one.
stream_2 = stream.catch File_Error error-> case error of
File_Error.Not_Found file_path -> Error.throw (File_Error.Not_Found file_path.parent)
_ -> stream
resource = Managed_Resource.register stream_2 close_stream
Output_Stream.Value self resource
## PRIVATE
Reads last `n` bytes from the file (or less if the file is too small) and
returns a vector of bytes.
@ -711,9 +740,9 @@ type Output_Stream
example_write_bytes =
file = Examples.scratch_file
out_stream = file.new_output_stream [File_Access.Create, File_Access.Write]
out_stream.write_bytes "hello".utf_8
out_stream.close
file.with_output_stream [File_Access.Create, File_Access.Write] out_stream->
out_stream.write_bytes "hello".utf_8
out_stream.close
write_bytes : Vector File_Access -> Nothing ! File_Error
write_bytes self contents = self.stream_resource . with java_stream->
File_Error.handle_java_exceptions self.file <|
@ -737,8 +766,8 @@ type Output_Stream
example_write_bytes =
file = Examples.scratch_file
out_stream = file.new_output_stream [File_Access.Create]
out_stream.close
file.with_output_stream [File_Access.Create] out_stream->
out_stream.close
close : Nothing
close self = self.stream_resource . finalize

View File

@ -2,17 +2,21 @@ import project.Data.Text.Text
import project.Data.Text.Encoding.Encoding
import project.Data.Text.Extensions
import project.Data.Vector.Vector
import project.Errors.Common.Unsupported_Argument_Types
import project.Error.Error
import project.Errors.Common.Dry_Run_Operation
import project.Errors.Common.Unsupported_Argument_Types
import project.Errors.Encoding_Error.Encoding_Error
import project.Errors.File_Error.File_Error
import project.Errors.Illegal_Argument.Illegal_Argument
import project.Errors.Problem_Behavior.Problem_Behavior
import project.Nothing.Nothing
import project.Panic.Panic
import project.Runtime.Context
import project.System.File.Existing_File_Behavior.Existing_File_Behavior
import project.System.File.File
import project.Warning.Warning
from project.Data.Boolean import Boolean, True, False
polyglot java import org.enso.base.Array_Builder
@ -37,14 +41,39 @@ polyglot java import org.enso.base.Array_Builder
Otherwise, the file is created with the encoded text written to it.
The method returns a `File` object for the written file.
? Dry Run
If writing to Output context is not enabled (such as in "Design" mode),
then this function will write to a temporary file. This temporary file will
be automatically deleted on exit of the Enso process.
This allows for building the workflow without affecting the real files.
@encoding Encoding.default_widget
Text.write : (File|Text) -> Encoding -> Existing_File_Behavior -> Problem_Behavior -> File ! Encoding_Error | Illegal_Argument | File_Error
Text.write self path encoding=Encoding.utf_8 on_existing_file=Existing_File_Behavior.Backup on_problems=Problem_Behavior.Report_Warning =
bytes = self.bytes encoding on_problems
file = File.new path
r = on_existing_file.write file stream->
bytes.if_not_error <|
stream.write_bytes bytes
r.if_not_error file
actual = File.new path
is_enabled = Context.Output.is_enabled
effective_existing_behaviour = if is_enabled then on_existing_file else
case on_existing_file of
Existing_File_Behavior.Backup -> Existing_File_Behavior.Overwrite
Existing_File_Behavior.Error -> if actual.exists then Error.throw (File_Error.Already_Exists actual) else Existing_File_Behavior.Overwrite
_ -> on_existing_file
file = if is_enabled then actual else actual.create_dry_run_file copy_original=on_existing_file==Existing_File_Behavior.Append
Context.Output.with_enabled <|
r = effective_existing_behaviour.write file stream->
bytes.if_not_error <|
stream.write_bytes bytes
r.if_not_error <|
if is_enabled then file else
warning = Dry_Run_Operation.Warning "Only a dry run has occurred, with data written to a temporary file."
Warning.attach warning file
## 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.

View File

@ -9,6 +9,7 @@ import project.Errors.File_Error.File_Error
import project.Errors.Problem_Behavior.Problem_Behavior
import project.Errors.Unimplemented.Unimplemented
import project.Function.Function
import project.Meta
import project.Network.HTTP.Response.Response
import project.Network.URI.URI
import project.Nothing.Nothing
@ -17,6 +18,10 @@ import project.System.File.File
from project.Data.Boolean import Boolean, True, False
from project.Data.Json import Invalid_JSON
from project.Metadata.Widget import Single_Choice
from project.Metadata.Choice import Option
import project.Metadata.Display
polyglot java import org.enso.base.file_format.FileFormatSPI
## PRIVATE
@ -35,6 +40,21 @@ get_format callback =
@Tail_Call reader (idx + 1)
reader 0
## PRIVATE
format_widget : Single_Choice
format_widget =
all_types = [Auto_Detect] + format_types
make_ctor type_obj =
type_name = Meta.get_qualified_type_name type_obj
## Temporary work around to work out if need to add the constructor name
is_singleton_type = type_obj==JSON_Format || (type_name.ends_with "_Format" . not)
if is_singleton_type then type_name else
simple_name = Meta.get_simple_type_name type_obj
"(" + type_name + "." + (simple_name.replace "_Format" "") + ")"
make_name type_obj = type_obj.to_text.replace "_Format" "" . replace "_" " "
Single_Choice display=Display.Always values=(all_types.map n->(Option (make_name n) (make_ctor n)))
type Auto_Detect
## PRIVATE
Implements the `File.read` for this `File_Format`
@ -127,23 +147,23 @@ type Bytes
read self file _ =
file.read_bytes
type JSON_File
type JSON_Format
## PRIVATE
If the File_Format supports reading from the file, return a configured instance.
for_file : File -> JSON_File | Nothing
for_file : File -> JSON_Format | Nothing
for_file file =
case file.extension of
".json" -> JSON_File
".geojson" -> JSON_File
".json" -> JSON_Format
".geojson" -> JSON_Format
_ -> Nothing
## PRIVATE
If the File_Format supports reading from the web response, return a configured instance.
for_web : Text -> URI -> JSON_File | Nothing
for_web : Text -> URI -> JSON_Format | Nothing
for_web content_type _ =
first = content_type.split ';' . first . trim
case first of
"application/json" -> JSON_File
"application/json" -> JSON_Format
_ -> Nothing
## PRIVATE

View File

@ -4,6 +4,7 @@ import Standard.Base.Data.Text.Regex.Match.Match
import Standard.Base.Errors.Common.No_Such_Method
import Standard.Base.Network.HTTP.Response.Response
import Standard.Base.Network.HTTP.Response_Body.Response_Body
import Standard.Base.Runtime.Context
from Standard.Table import Table, Column
@ -40,7 +41,7 @@ xls =
url = "https://enso-data-samples.s3.us-west-1.amazonaws.com/spreadsheet.xls"
file = enso_project.data / 'spreadsheet.xls'
if file.exists.not then
HTTP.fetch url . to_file file
Context.Output.with_enabled <| HTTP.fetch url . to_file file
file
## An example XLSX file for experimenting with Table and its APIs.
@ -55,14 +56,15 @@ xlsx =
url = "https://enso-data-samples.s3.us-west-1.amazonaws.com/spreadsheet.xlsx"
file = enso_project.data / 'spreadsheet.xlsx'
if file.exists.not then
HTTP.fetch url . to_file file
Context.Output.with_enabled <| HTTP.fetch url . to_file file
file
## A file that is used for writing temporary data as part of tests.
scratch_file : File
scratch_file =
file = enso_project.data / "scratch_file"
if file.exists then file.delete else Nothing
if file.exists.not then Nothing else
Context.Output.with_enabled <| file.delete
file
## An example duration for experimenting with duration APIs.
@ -172,7 +174,7 @@ image_file =
url = "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e9/Hue_alpha_falloff.png/320px-Hue_alpha_falloff.png"
file = enso_project.data / "image.png"
if file.exists.not then
HTTP.fetch url . to_file file
Context.Output.with_enabled <| HTTP.fetch url . to_file file
file
## A PNG image.

View File

@ -128,8 +128,8 @@ Text.parse_to_table self pattern="." case_sensitivity=Case_Sensitivity.Sensitive
- match_columns: How to match columns between the table and the file.
Not used for JSON.
- on_problems: What to do if there are problems reading the file.
JSON_File.write_table : File -> Table -> Existing_File_Behavior -> Match_Columns -> Problem_Behavior -> File
JSON_File.write_table self file table on_existing_file match_columns on_problems =
JSON_Format.write_table : File -> Table -> Existing_File_Behavior -> Match_Columns -> Problem_Behavior -> File
JSON_Format.write_table self file table on_existing_file match_columns on_problems =
_ = [match_columns, on_problems]
if file.exists.not then table.to_json.write file else
case on_existing_file of

View File

@ -1,4 +1,5 @@
from Standard.Base import all
import Standard.Base.Runtime.Context
import project.Suite_Config.Suite_Config
import project.Test.Test
@ -18,8 +19,10 @@ wrap_junit_testsuites config builder ~action =
if config.should_output_junit then
builder.append '</testsuites>\n'
config.output_path.parent.create_directory
builder.toString.write config.output_path
Context.Output.with_enabled <|
config.output_path.parent.create_directory
builder.toString.write config.output_path
result

View File

@ -11,7 +11,7 @@ import org.enso.interpreter.runtime.state.State;
@BuiltinMethod(
type = "Context",
name = "is_enabled",
name = "is_enabled_builtin",
description = "Check if the context is enabled in the provided execution environment.")
public class ContextIsEnabledNode extends Node {
private @Child ExpectStringNode expectStringNode = ExpectStringNode.build();

View File

@ -42,7 +42,7 @@ public final class EnsoFile implements TruffleObject {
this.truffleFile = truffleFile;
}
@Builtin.Method
@Builtin.Method(name = "output_stream_builtin")
@Builtin.WrapException(from = IOException.class)
@Builtin.ReturningGuestObject
@CompilerDirectives.TruffleBoundary
@ -155,7 +155,7 @@ public final class EnsoFile implements TruffleObject {
return this.truffleFile.isDirectory();
}
@Builtin.Method(name = "create_directory")
@Builtin.Method(name = "create_directory_builtin")
@CompilerDirectives.TruffleBoundary
public void createDirectories() {
try {

View File

@ -0,0 +1,42 @@
package org.enso.base;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class DryRunFileManager {
static final Map<String, String> files = new HashMap<>();
/**
* Creates a temporary file for the given path. If the path is already a dry run temporary file,
* the same path will be returned.
*
* @param path the path to the file to make a temporary file of.
* @return the path to the temporary file.
*/
public static String getTemporaryFile(String path) {
return files.computeIfAbsent(
path,
k -> {
if (files.containsValue(k)) {
// Existing temporary file so return this.
return k;
}
var filename = new File(k).getName();
var lastDot = filename.lastIndexOf('.');
var prefix = lastDot == -1 ? filename : filename.substring(0, lastDot);
prefix = prefix + "_ensodryrun";
var extension = lastDot == -1 ? "" : filename.substring(lastDot);
try {
var temp = File.createTempFile(prefix, extension);
temp.deleteOnExit();
return temp.getAbsolutePath();
} catch (IOException e) {
return null;
}
});
}
}

View File

@ -9,6 +9,6 @@ public class JSONFormatSPI extends FileFormatSPI {
@Override
protected String getTypeName() {
return "JSON_File";
return "JSON_Format";
}
}

View File

@ -1,5 +1,6 @@
from Standard.Base import all
import Standard.Base.Runtime.Ref.Ref
import Standard.Base.Runtime.Context
import Standard.Table.Data.Type.Value_Type.Bits
from Standard.Table import Table, Value_Type
@ -154,11 +155,11 @@ sqlite_spec connection prefix =
spec =
enso_project.data.create_directory
file = enso_project.data / "sqlite_test.db"
file.delete_if_exists
Context.Output.with_enabled <| file.delete_if_exists
in_file_prefix = "[SQLite File] "
sqlite_spec (Database.connect (SQLite file)) in_file_prefix
Upload_Spec.spec (_ -> Database.connect (SQLite file)) in_file_prefix
file.delete
Context.Output.with_enabled <| file.delete
in_memory_prefix = "[SQLite In-Memory] "
sqlite_spec (Database.connect (SQLite In_Memory)) in_memory_prefix
@ -166,7 +167,7 @@ spec =
SQLite_Type_Mapping_Spec.spec
Test.group "SQLite_Format should allow connecting to SQLite files" <|
Test.group "SQLite_Format should allow connecting to SQLite files" <| Context.Output.with_enabled <|
file.delete_if_exists
connection = Database.connect (SQLite file)

View File

@ -1,4 +1,5 @@
from Standard.Base import all
import Standard.Base.Runtime.Context
from Standard.Table import Table, Column, Delimited
import Standard.Table.Main as Table_Module
@ -91,7 +92,7 @@ spec =
res.should_equal expected
Test.specify 'should write CSV to a file' <|
Test.specify 'should write CSV to a file' <| Context.Output.with_enabled <|
varied_column = (enso_project.data / "varied_column.csv") . read
out = enso_project.data / "transient" / "out.csv"
out.delete_if_exists
@ -109,7 +110,7 @@ spec =
out.read_text.should_equal exp
out.delete_if_exists
Test.group "Integration" <|
Test.group "Integration" <| Context.Output.with_enabled <|
Test.specify "should be able to round-trip a table with all kinds of weird characters to CSV and back" <|
names = ['Śłąęźż");DROP TABLE Students;--', 'This;Name;;Is""Strange', 'Marcin,,', '\'', 'a\n\nb', 'a\tc', Nothing, Nothing, Nothing, '42', '💁👌🎍😍', '', 'null?\0?', 'FFFD', '\uFFFD', '\r\n', 'a\r\nb\n\rc\rd\ne', 'what about these # ?? // /* hmm */ is it included?', 'and the rare \v vertical tab?']
d = Date_Time.new 2015 10 29 23 55 49

View File

@ -2,6 +2,7 @@ from Standard.Base import all
import Standard.Base.Errors.Encoding_Error.Encoding_Error
import Standard.Base.Errors.File_Error.File_Error
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
import Standard.Base.Runtime.Context
from Standard.Table import Table, Column, Data_Formatter, Quote_Style, Delimited
import Standard.Table.Data.Table_Conversions
@ -102,7 +103,7 @@ spec =
r2.should_fail_with File_Error
r2.catch.should_be_a File_Error.IO_Error
Test.specify "should work with all kinds of line endings" <|
Test.specify "should work with all kinds of line endings" <| Context.Output.with_enabled <|
path name = enso_project.data / 'transient' / name
create_file name ending_style =
lines = ['a,b,c', 'd,e,f', '1,2,3']
@ -129,7 +130,7 @@ spec =
['crlf.csv', 'lf.csv', 'cr.csv', 'mixed.csv'].each (path >> .delete)
Test.specify "should allow to override line endings style" <|
Test.specify "should allow to override line endings style" <| Context.Output.with_enabled <|
file = enso_project.data / "transient" / "lf.csv"
lines = ['a,b,c', 'd,e,f', '1,2,3']
text = lines.join '\n'
@ -170,7 +171,7 @@ spec =
table.at '🚀b' . to_vector . should_equal ['✨🚀🚧😍😃😍😎😙😉☺']
table.at 'ć😎' . to_vector . should_equal ['แมวมีสี่ขา']
Test.specify "should report errors when encountering malformed characters" <|
Test.specify "should report errors when encountering malformed characters" <| Context.Output.with_enabled <|
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_bytes.write_bytes utf8_file

View File

@ -2,6 +2,7 @@ from Standard.Base import all
import Standard.Base.Errors.Encoding_Error.Encoding_Error
import Standard.Base.Errors.File_Error.File_Error
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
import Standard.Base.Runtime.Context
from Standard.Table import Table, Column, Data_Formatter, Quote_Style, Match_Columns, Delimited
from Standard.Table.Errors import all
@ -24,7 +25,7 @@ join_lines lines trailing_newline=True =
spec =
line_ending_pairs = [[Line_Ending_Style.Unix, '\n'], [Line_Ending_Style.Windows, '\r\n'], [Line_Ending_Style.Mac_Legacy, '\r']]
Test.group "Delimited File Writing" <|
Test.group "Delimited File Writing" <| Context.Output.with_enabled <|
Test.specify "should correctly write a simple table and return the written file object on success" <|
table = Table.new [["A", [1,2,3]], ["B", [1.0,1.5,2.2]], ["C", ["x","y","z"]], ["D", ["a", 2, My_Type.Value 10]]]
file = (enso_project.data / "transient" / "written.csv")

View File

@ -1,6 +1,7 @@
from Standard.Base import all
import Standard.Base.Errors.File_Error.File_Error
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
import Standard.Base.Runtime.Context
from Standard.Table import Table, Match_Columns, Excel, Excel_Range, Data_Formatter, Sheet_Names, Range_Names, Worksheet, Cell_Range, Delimited, Excel_Workbook
@ -67,7 +68,7 @@ spec_fmt header file read_method sheet_count=5 =
t_3.at 'C' . to_vector . should_equal [43.2, 54]
spec_write suffix test_sheet_name =
Test.group ("Write " + suffix + " Files") <|
Test.group ("Write " + suffix + " Files") <| Context.Output.with_enabled <|
out = enso_project.data / ('out.' + suffix)
out_bak = enso_project.data / ('out.' + suffix + '.bak')
table = enso_project.data/'varied_column.csv' . read
@ -678,7 +679,7 @@ spec =
r2.should_fail_with File_Error
r2.catch.should_be_a File_Error.Corrupted_Format
Test.specify "should handle malformed XLS files gracefully" <|
Test.specify "should handle malformed XLS files gracefully" <| Context.Output.with_enabled <|
bad_file = enso_project.data / "transient" / "malformed.xls"
"not really an XLS file contents...".write bad_file on_existing_file=Existing_File_Behavior.Overwrite

View File

@ -1,5 +1,6 @@
from Standard.Base import all
import Standard.Base.Errors.File_Error.File_Error
import Standard.Base.Runtime.Context
from Standard.Table import all
import Standard.Table.Errors.Invalid_JSON_Format
@ -10,7 +11,7 @@ import Standard.Test.Extensions
import project.Util
spec = Test.group 'Various File Format support on Table' <|
spec = Test.group 'Various File Format support on Table' <| Context.Output.with_enabled <|
t1 = Table.new [["X", [1, 2, 3]]]
transient = enso_project.data / "transient"
simple_empty = enso_project.data/'simple_empty.csv' . read

View File

@ -53,7 +53,7 @@ spec =
Problems.expect_only_warning Encoding_Error <|
windows_log.read (Plain_Text Encoding.ascii)
Test.group "JSON_File" <|
Test.group "JSON_Format" <|
Test.specify "should be able to read a file as Json" <|
f1 = enso_project.data / "sample.json"
j1 = f1.read
@ -62,7 +62,7 @@ spec =
j1.at "not" . should_equal Nothing
f2 = enso_project.data / "sample-json.weird-extension"
j2 = f2.read JSON_File
j2 = f2.read JSON_Format
j2.at 0 . at "foo" . should_equal "bar"
j2.at 1 . should_equal 42
j2.at 2 . should_equal Nothing

View File

@ -3,6 +3,7 @@ import Standard.Base.Errors.Encoding_Error.Encoding_Error
import Standard.Base.Errors.File_Error.File_Error
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
import Standard.Base.Errors.Illegal_State.Illegal_State
import Standard.Base.Runtime.Context
polyglot java import org.enso.base_test_helpers.FileSystemHelper
@ -18,7 +19,7 @@ spec =
windows_file = enso_project.data / "windows.txt"
non_existent_file = File.new "does_not_exist.txt"
Test.group "File Operations" <|
Test.group "File Operations" <| Context.Output.with_enabled <|
Test.specify "should allow creating a new file" <|
path = sample_file.path
File.new path
@ -132,13 +133,14 @@ spec =
Test.specify "should allow to read last n bytes from a file" <|
file = enso_project.data / "transient" / "bytes.txt"
data = [1, 0, 0, 1, 2, 100, 20]
data.write_bytes file
file.read_last_bytes 0 . should_equal []
file.read_last_bytes 1 . should_equal [20]
file.read_last_bytes 2 . should_equal [100, 20]
file.read_last_bytes 5 . should_equal [0, 1, 2, 100, 20]
file.read_last_bytes 1000 . should_equal data
file.delete
Context.Output.with_enabled <|
data.write_bytes file
file.read_last_bytes 0 . should_equal []
file.read_last_bytes 1 . should_equal [20]
file.read_last_bytes 2 . should_equal [100, 20]
file.read_last_bytes 5 . should_equal [0, 1, 2, 100, 20]
file.read_last_bytes 1000 . should_equal data
file.delete
Test.specify "should handle exceptions when reading a non-existent file" <|
file = File.new "does_not_exist.txt"
@ -203,7 +205,7 @@ spec =
contents_2 = Data.read_text file
contents_2.should_start_with "Cupcake ipsum dolor sit amet."
Test.group "write operations" <|
Test.group "write operations" <| Context.Output.with_enabled <|
data = [32, 127, -128, 0]
data_2 = [10, 15, 20, 30]

View File

@ -1,5 +1,6 @@
from Standard.Base import all
import Standard.Base.Errors.Encoding_Error.Encoding_Error
import Standard.Base.Runtime.Context
polyglot java import java.nio.CharBuffer
@ -24,7 +25,7 @@ spec =
f.delete
f.exists.should_be_false
Test.specify "should work correctly when reading chunks of varying sizes" <|
Test.specify "should work correctly when reading chunks of varying sizes" <| Context.Output.with_enabled <|
f = enso_project.data / "transient" / "varying_chunks.txt"
fragment = 'Hello 😎🚀🚧!'
contents = 1.up_to 1000 . map _->fragment . join '\n'

View File

@ -1,6 +1,7 @@
from Standard.Base import all
import Standard.Base.Errors.Encoding_Error.Encoding_Error
import Standard.Base.Errors.Illegal_State.Illegal_State
import Standard.Base.Runtime.Context
polyglot java import org.enso.base.Encoding_Utils
polyglot java import java.nio.CharBuffer
@ -9,7 +10,7 @@ from Standard.Test import Test, Test_Suite, Problems
import Standard.Test.Extensions
spec =
Test.group "ReportingStreamEncoder" <|
Test.group "ReportingStreamEncoder" <| Context.Output.with_enabled <|
Test.specify "should allow writing a file codepoint by codepoint" <|
f = enso_project.data / "transient" / "char-by-char.txt"
f.delete_if_exists

View File

@ -1,4 +1,5 @@
from Standard.Base import all
import Standard.Base.Runtime.Context
from Standard.Table import Table, Aggregate_Column, Value_Type
@ -99,7 +100,7 @@ visualization_spec connection =
Visualization.prepare_visualization Value_Type.Char . should_equal (make_json Value_Type.Char)
Visualization.prepare_visualization Value_Type.Unsupported_Data_Type . should_equal (make_json Value_Type.Unsupported_Data_Type)
spec =
spec = Context.Output.with_enabled <|
enso_project.data.create_directory
file = enso_project.data / "sqlite_test.db"
file.delete_if_exists

View File

@ -23,7 +23,12 @@ type Context
if self.is_enabled environment then action else Panic.throw (Forbidden_Operation.Error self.name)
is_enabled : Text -> Boolean
is_enabled self environment="design" = @Builtin_Method "Context.is_enabled"
is_enabled self environment="design" =
self.is_enabled_builtin environment
## PRIVATE
is_enabled_builtin : Text -> Boolean
is_enabled_builtin self environment = @Builtin_Method "Context.is_enabled_builtin"
current_execution_environment : Text
current_execution_environment = @Builtin_Method "Runtime.current_execution_environment"