Skip to content

Commit c6d00bb

Browse files
Download_Mode for File, S3_File and Enso_File (#12017)
1 parent 9f6fc3b commit c6d00bb

File tree

9 files changed

+139
-10
lines changed

9 files changed

+139
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@
167167
- [Added `add_group_number` to the in-memory database.[11818]
168168
- [The reload button clears the HTTP cache.][11673]
169169
- [SQL Server Support for Aggregate][11811]
170+
- [Added `Download_Mode` parameter to `Data.download`.][12017]
170171

171172
[11235]: https://github.com/enso-org/enso/pull/11235
172173
[11255]: https://github.com/enso-org/enso/pull/11255
@@ -178,6 +179,7 @@
178179
[11818]: https://github.com/enso-org/enso/pull/11818
179180
[11673]: https://github.com/enso-org/enso/pull/11673
180181
[11811]: https://github.com/enso-org/enso/pull/11811
182+
[12017]: https://github.com/enso-org/enso/pull/12017
181183

182184
#### Enso Language & Runtime
183185

distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import project.Any.Any
2+
import project.Data.Download.Download_Mode.Download_Mode
23
import project.Data.Pair.Pair
34
import project.Data.Read.Many_Files_List.Many_Files_List
45
import project.Data.Read.Return_As.Return_As
@@ -437,23 +438,33 @@ post (uri:(URI | Text)=(Missing_Argument.throw "uri")) (body:Request_Body=..Empt
437438
Arguments:
438439
- uri: The URI to fetch.
439440
- file: The file to write the response to.
441+
- replace_existing: Determines when `download` should proceed with the
442+
download, if the destination file already exists. (If the destination file
443+
does not exist, the download always proceeds).
444+
Options include:
445+
- `If_Not_Exists`: Download the file if it does not already exist on
446+
disk. (The default.)
447+
- `If_Older_Than age`: Download the file if the existing file is older
448+
than the specified age.
449+
- `Always`: Always download.
440450
- method: The HTTP method to use. Must be one of `HTTP_Method.Get`,
441451
`HTTP_Method.Head`, `HTTP_Method.Delete`, `HTTP_Method.Options`.
442452
Defaults to `HTTP_Method.Get`.
443453
- headers: The headers to send with the request. Defaults to an empty vector.
444454
@uri (Text_Input display=..Always)
445455
@headers Header.default_widget
446-
download : (URI | Text) -> Writable_File -> HTTP_Method -> Vector (Header | Pair Text Text) -> File ! Request_Error | HTTP_Error
447-
download (uri:(URI | Text)=(Missing_Argument.throw "uri")) file:Writable_File (method:HTTP_Method=..Get) (headers:(Vector (Header | Pair Text Text))=[]) =
456+
download : (URI | Text) -> Writable_File -> Download_Mode -> HTTP_Method -> Vector (Header | Pair Text Text) -> File ! Request_Error | HTTP_Error
457+
download (uri:(URI | Text)=(Missing_Argument.throw "uri")) file:Writable_File (replace_existing:Download_Mode=..If_Not_Exists) (method:HTTP_Method=..Get) (headers:(Vector (Header | Pair Text Text))=[]) =
448458
Context.Output.if_enabled disabled_message="As writing is disabled, cannot download to a file. Press the Write button ▶ to perform the operation." panic=False <|
449-
response = HTTP.fetch uri method headers cache_policy=Cache_Policy.No_Cache
450-
case Data_Link.is_data_link_from_metadata response.body.metadata of
451-
True ->
452-
# If the resource was a data link, we follow it, download the target data and try to write it to a file.
453-
data_link = Data_Link_Helpers.interpret_json_as_data_link response.decode_as_json
454-
Data_Link_Helpers.save_data_link_to_file data_link file
455-
False ->
456-
response.write file
459+
if replace_existing.should_download file then
460+
response = HTTP.fetch uri method headers cache_policy=Cache_Policy.No_Cache
461+
case Data_Link.is_data_link_from_metadata response.body.metadata of
462+
True ->
463+
# If the resource was a data link, we follow it, download the target data and try to write it to a file.
464+
data_link = Data_Link_Helpers.interpret_json_as_data_link response.decode_as_json
465+
Data_Link_Helpers.save_data_link_to_file data_link file
466+
False ->
467+
response.write file
457468

458469
## If the `format` is set to `Raw_Response`, a raw HTTP `Response` is returned
459470
that can be then processed further manually.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import project.Data.Time.Date_Time.Date_Time
2+
import project.Data.Time.Duration.Duration
3+
import project.Nothing.Nothing
4+
import project.System.File.Generic.Writable_File.Writable_File
5+
from project.Data.Boolean import Boolean, False, True
6+
7+
type Download_Mode
8+
## Download the file if it does not already exist on disk.
9+
If_Not_Exists
10+
11+
## Download the file if it is older than the specified age.
12+
If_Older_Than age:Duration
13+
14+
## Always download.
15+
Always
16+
17+
## PRIVATE
18+
Determine if a file should be downloaded, based on the file type,
19+
download mode, and file age.
20+
should_download self (file:Writable_File) -> Boolean =
21+
case self of
22+
Download_Mode.If_Not_Exists ->
23+
file.file.exists.not
24+
Download_Mode.If_Older_Than age ->
25+
file.file.exists.not || file.file.last_modified_time < (Date_Time.now - age)
26+
Download_Mode.Always ->
27+
True

distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export project.Data.Boolean.Boolean.True
88
export project.Data.Decimal.dec
99
export project.Data.Decimal.Decimal
1010
export project.Data.Dictionary.Dictionary
11+
export project.Data.Download.Download_Mode.Download_Mode
1112
export project.Data.Filter_Condition.Filter_Action
1213
export project.Data.Filter_Condition.Filter_Condition
1314
export project.Data.Hashset.Hashset

std-bits/table/src/main/java/org/enso/table/excel/ExcelConnectionPool.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,11 @@ private String getKeyForFile(File file) throws IOException {
201201
}
202202

203203
void release(ReadOnlyExcelConnection excelConnection) throws IOException {
204+
System.out.println("AAAo");
205+
System.err.println("AAAo");
206+
new Exception().printStackTrace();
207+
System.out.println("AAAo2");
208+
System.err.println("AAAo2");
204209
synchronized (this) {
205210
excelConnection.record.refCount--;
206211
if (excelConnection.record.refCount <= 0) {

test/AWS_Tests/src/S3_Spec.enso

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import enso_dev.Table_Tests.Util
1818

1919
from Standard.Test import all
2020

21+
import enso_dev.Base_Tests.Data.Data_Spec
2122
import enso_dev.Base_Tests.Network.Enso_Cloud.Cloud_Tests_Setup.Cloud_Tests_Setup
2223
import enso_dev.Base_Tests.Network.Enso_Cloud.Cloud_Tests_Setup.Temporary_Directory
2324
import enso_dev.Base_Tests.System.File_Spec as Local_File_Spec
@@ -70,6 +71,9 @@ replace_username_in_data_link base_file =
7071
past we had a bug precisely due to relying on the defaults: https://github.com/enso-org/enso/issues/9284
7172
add_specs suite_builder =
7273
cloud_setup = Cloud_Tests_Setup.prepare
74+
75+
add_data_specs suite_builder writable_root
76+
7377
suite_builder.group "S3 Path handling" group_builder->
7478
# Testing just path handling can be done on instances without any credentials:
7579
root = S3_File.new "s3://"+bucket_name+"/"
@@ -661,6 +665,10 @@ create_data_link_to_s3 credential:AWS_Credential link_location target:S3_File =
661665
. replace "<SECRET_KEY>" keys.second
662666
Data_Link.write_raw_config link_location new_content replace_existing=True
663667

668+
add_data_specs suite_builder root =
669+
file_maker path_element:Text = root / "data_spec" / (path_element+".txt")
670+
Data_Spec.add_specs "(S3_File) " suite_builder file_maker
671+
664672
main filter=Nothing =
665673
suite = Test.build suite_builder->
666674
add_specs suite_builder
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from Standard.Base import all
2+
3+
from project.Network.Http.Http_Test_Setup import base_url_with_slash, pending_has_url
4+
5+
from Standard.Test import all
6+
import Standard.Test.Test_Environment
7+
8+
polyglot java import java.lang.Thread
9+
10+
with_test_file f ~action =
11+
f.delete_if_exists
12+
Panic.with_finalizer (f.delete_if_exists) <|
13+
action f
14+
15+
## file_maker should take a path component and return a full file path, for example:
16+
"if_not_exist"
17+
=>
18+
(enso_project.data / "transient" / "if_not_exist.txt")
19+
add_specs prefix suite_builder file_maker =
20+
suite_builder.group prefix+"Download Mode" pending=pending_has_url group_builder->
21+
url_n_bytes n = base_url_with_slash+'test_download?length='+n.to_text
22+
23+
group_builder.specify prefix+"Will always download a file for mode Always" <|
24+
with_test_file (file_maker "always") file->
25+
file.exists . should_be_false
26+
Data.download (url_n_bytes 10) replace_existing=..Always file
27+
first_contents = file.read
28+
Data.download (url_n_bytes 11) replace_existing=..Always file
29+
second_contents = file.read
30+
first_contents . should_not_equal second_contents
31+
32+
group_builder.specify prefix+"Will download a file if it does not exist for default mode If_Not_Exists" <|
33+
with_test_file (file_maker "if_not_exist") file->
34+
file.exists . should_be_false
35+
Data.download (url_n_bytes 10) file
36+
first_contents = file.read
37+
Data.download (url_n_bytes 11) file
38+
second_contents = file.read
39+
first_contents . should_equal second_contents
40+
41+
# With retries because it relies on a short `sleep`
42+
group_builder.specify prefix+"Will download a file if it is older than a specified duration for mode If_Older_Than" <| Test.with_retries <|
43+
with_test_file (file_maker "if_older_than") file->
44+
sleep_duration_secs = 3
45+
46+
file.exists . should_be_false
47+
48+
Data.download (url_n_bytes 10) file
49+
first_contents = file.read
50+
51+
Data.download (url_n_bytes 11) (replace_existing=..If_Older_Than (Duration.new seconds=sleep_duration_secs)) file
52+
second_contents = file.read
53+
first_contents . should_equal second_contents
54+
55+
Thread.sleep ((sleep_duration_secs + 0.5) * 1000)
56+
57+
Data.download (url_n_bytes 12) (replace_existing=..If_Older_Than (Duration.new seconds=sleep_duration_secs)) file
58+
third_contents = file.read
59+
first_contents . should_not_equal third_contents

test/Base_Tests/src/Network/Enso_Cloud/Enso_File_Spec.enso

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Standard.Base.Runtime.Context
1111
from Standard.Test import all
1212
import Standard.Test.Test_Environment
1313

14+
import project.Data.Data_Spec
1415
import project.Network.Enso_Cloud.Cloud_Tests_Setup.Cloud_Tests_Setup
1516
import project.Network.Enso_Cloud.Cloud_Tests_Setup.Temporary_Directory
1617

@@ -26,6 +27,8 @@ add_specs suite_builder setup:Cloud_Tests_Setup = suite_builder.group "Enso Clou
2627
Panic.rethrow <| "Hello Another!".write (sub / "another.txt")
2728
group_builder.teardown test_root.cleanup
2829

30+
add_data_specs suite_builder test_root.get
31+
2932
group_builder.specify "should be able to list a directory" <|
3033
assets = Enso_File.home.list
3134
# We don't a priori know the contents, so we can only check very generic properties
@@ -394,6 +397,12 @@ add_specs suite_builder setup:Cloud_Tests_Setup = suite_builder.group "Enso Clou
394397
Local_File_Spec.add_create_and_delete_directory_specs group_builder test_root.get
395398
Local_File_Spec.add_copy_edge_cases_specs group_builder test_root.get
396399

400+
add_data_specs suite_builder root =
401+
my_dir = (root / "data_specs-").create_directory
402+
my_dir.should_succeed
403+
file_maker path_element:Text = my_dir / (path_element+".txt")
404+
Data_Spec.add_specs "(Enso_File) " suite_builder file_maker
405+
397406
main filter=Nothing =
398407
setup = Cloud_Tests_Setup.prepare
399408
suite = Test.build suite_builder->

test/Base_Tests/src/System/File_Spec.enso

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ polyglot java import org.enso.base_test_helpers.FileSystemHelper
1313
from Standard.Test import all
1414
import Standard.Test.Test_Environment
1515

16+
import project.Data.Data_Spec
1617
import project.Network.Enso_Cloud.Cloud_Tests_Setup.Cloud_Tests_Setup
1718

1819
## We rely on a less strict equality for `File` as it is fine if a relative file gets resolved to absolute.
@@ -38,6 +39,8 @@ add_specs suite_builder =
3839
Platform.OS.Windows -> Nothing
3940
_ -> "This test runs only on Windows."
4041

42+
add_data_specs suite_builder
43+
4144
suite_builder.group "File Operations" group_builder->
4245
group_builder.specify "should get name of the root" <|
4346
root = File.new "/"
@@ -1055,6 +1058,10 @@ add_create_and_delete_directory_specs group_builder ~parent_dir =
10551058

10561059
dir.delete_if_exists . should_equal Nothing
10571060

1061+
add_data_specs suite_builder =
1062+
file_maker path_element:Text = enso_project.data / "transient" / (path_element+".txt")
1063+
Data_Spec.add_specs "(File) " suite_builder file_maker
1064+
10581065
add_copy_edge_cases_specs group_builder ~parent_dir file_from_path=File.new =
10591066
group_builder.specify "should allow copy/move edge case with source=target" <|
10601067
file = parent_dir / "src-target-in-one.txt"

0 commit comments

Comments
 (0)