Skip to content

Commit a2c848e

Browse files
authored
Try #175:
2 parents ee0d59a + 1c5829d commit a2c848e

File tree

1 file changed

+24
-22
lines changed

1 file changed

+24
-22
lines changed

src/s3path.jl

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
struct S3Path{A<:AbstractAWSConfig} <: AbstractPath
1+
struct S3Path{A<:Union{Nothing, AbstractAWSConfig}} <: AbstractPath
22
segments::Tuple{Vararg{String}}
33
root::String
44
drive::String
@@ -8,13 +8,13 @@ end
88

99
# constructor that converts but does not require type parameter
1010
function S3Path(segments, root::AbstractString, drive::AbstractString, isdirectory::Bool,
11-
config::AbstractAWSConfig)
11+
config::Union{Nothing, AbstractAWSConfig})
1212
S3Path{typeof(config)}(segments, root, drive, isdirectory, config)
1313
end
1414

1515
"""
1616
S3Path()
17-
S3Path(str; config::AbstractAWSConfig=aws_config())
17+
S3Path(str; config::Union{Nothing, AbstractAWSConfig}=nothing)
1818
1919
Construct a new AWS S3 path type which should be of the form
2020
"s3://<bucket>/prefix/to/my/object".
@@ -31,28 +31,30 @@ NOTES:
3131
- On top of the standard path properties (e.g., `segments`, `root`, `drive`,
3232
`separator`), `S3Path`s also support `bucket` and `key` properties for your
3333
convenience.
34+
- if `config` is left at its default value of `nothing`, then the
35+
latest `global_aws_config()` will be used in any operations involving the
36+
path. To "freeze" the config at construction time, explicitly pass an
37+
`AbstractAWSConfig` to the `config` keyword argument.
3438
"""
3539
function S3Path()
36-
config = global_aws_config()
37-
account_id = aws_account_number(config)
38-
region = config.region
40+
config = nothing
3941

4042
return S3Path(
4143
(),
4244
"/",
43-
"s3://$account_id-$region",
45+
"",
4446
true,
4547
config,
4648
)
4749
end
4850
# below definition needed by FilePathsBase
49-
S3Path{A}() where {A<:AbstractAWSConfig} = S3Path()
51+
S3Path{A}() where {A<:Union{Nothing, AbstractAWSConfig}} = S3Path()
5052

5153
function S3Path(
5254
bucket::AbstractString,
5355
key::AbstractString;
5456
isdirectory::Bool=false,
55-
config::AbstractAWSConfig=global_aws_config(),
57+
config::Union{Nothing, AbstractAWSConfig}=nothing,
5658
)
5759
return S3Path(
5860
Tuple(filter!(!isempty, split(key, "/"))),
@@ -67,7 +69,7 @@ function S3Path(
6769
bucket::AbstractString,
6870
key::AbstractPath;
6971
isdirectory::Bool=false,
70-
config::AbstractAWSConfig=global_aws_config(),
72+
config::Union{Nothing, AbstractAWSConfig}=nothing,
7173
)
7274
return S3Path(
7375
key.segments,
@@ -79,18 +81,15 @@ function S3Path(
7981
end
8082

8183
# To avoid a breaking change.
82-
function S3Path(str::AbstractString; config::AbstractAWSConfig=global_aws_config())
84+
function S3Path(str::AbstractString; config::Union{Nothing, AbstractAWSConfig}=nothing)
8385
result = tryparse(S3Path, str; config=config)
8486
result !== nothing || throw(ArgumentError("Invalid s3 path string: $str"))
8587
return result
8688
end
8789

88-
# if config=nothing, will not try to talk to AWS until after string is confirmed to be an s3 path
8990
function Base.tryparse(::Type{<:S3Path}, str::AbstractString; config::Union{Nothing,AbstractAWSConfig}=nothing)
9091
str = String(str)
9192
startswith(str, "s3://") || return nothing
92-
# we do this here so that the `@p_str` macro only tries to call AWS if it actually has an S3 path
93-
(config nothing) && (config = global_aws_config())
9493
root = ""
9594
path = ()
9695
isdirectory = true
@@ -173,7 +172,10 @@ function FilePathsBase.parents(fp::S3Path)
173172
end
174173
end
175174

176-
FilePathsBase.exists(fp::S3Path) = s3_exists(fp.config, fp.bucket, fp.key)
175+
# Use `fp.config` unless it is nothing; in that case, get the latest `global_aws_config`
176+
get_config(fp::S3Path) = @something(fp.config, global_aws_config())
177+
178+
FilePathsBase.exists(fp::S3Path) = s3_exists(get_config(fp), fp.bucket, fp.key)
177179
Base.isfile(fp::S3Path) = !fp.isdirectory && exists(fp)
178180
function Base.isdir(fp::S3Path)
179181
if isempty(fp.segments)
@@ -184,7 +186,7 @@ function Base.isdir(fp::S3Path)
184186
return false
185187
end
186188

187-
objects = s3_list_objects(fp.config, fp.bucket, key; max_items=1)
189+
objects = s3_list_objects(get_config(fp), fp.bucket, key; max_items=1)
188190

189191
# `objects` is a `Channel`, so we call iterate to see if there are any objects that
190192
# match our directory key.
@@ -205,7 +207,7 @@ function Base.stat(fp::S3Path)
205207
last_modified = DateTime(0)
206208

207209
if exists(fp)
208-
resp = s3_get_meta(fp.config, fp.bucket, fp.key)
210+
resp = s3_get_meta(get_config(fp), fp.bucket, fp.key)
209211
# Example: "Thu, 03 Jan 2019 21:09:17 GMT"
210212
last_modified = DateTime(
211213
resp["Last-Modified"][1:end-4],
@@ -261,7 +263,7 @@ function Base.rm(fp::S3Path; recursive=false, kwargs...)
261263
end
262264

263265
@debug "delete: $fp"
264-
s3_delete(fp.config, fp.bucket, fp.key)
266+
s3_delete(get_config(fp), fp.bucket, fp.key)
265267
end
266268

267269
# We need to special case sync with S3Paths because of how directories
@@ -380,7 +382,7 @@ function Base.readdir(fp::S3Path; join=false, sort=true)
380382
if !isempty(token)
381383
params["continuation-token"] = token
382384
end
383-
S3.list_objects_v2(fp.bucket, params; aws_config=fp.config)
385+
S3.list_objects_v2(fp.bucket, params; aws_config=get_config(fp))
384386
catch e
385387
@delay_retry if ecode(e) in ["NoSuchBucket"] end
386388
end
@@ -401,7 +403,7 @@ function Base.readdir(fp::S3Path; join=false, sort=true)
401403
end
402404

403405
function Base.read(fp::S3Path; byte_range=nothing)
404-
return Vector{UInt8}(s3_get(fp.config, fp.bucket, fp.key; raw=true, byte_range=byte_range))
406+
return Vector{UInt8}(s3_get(get_config(fp), fp.bucket, fp.key; raw=true, byte_range=byte_range))
405407
end
406408

407409
Base.write(fp::S3Path, content::String; kwargs...) = Base.write(fp, Vector{UInt8}(content); kwargs...)
@@ -410,10 +412,10 @@ function Base.write(fp::S3Path, content::Vector{UInt8}; part_size_mb=50, multipa
410412
# avoid HTTPClientError('An HTTP Client raised an unhandled exception: string longer than 2147483647 bytes')
411413
MAX_HTTP_BYTES = 2147483647
412414
if !multipart || length(content) < MAX_HTTP_BYTES
413-
return s3_put(fp.config, fp.bucket, fp.key, content)
415+
return s3_put(get_config(fp), fp.bucket, fp.key, content)
414416
else
415417
io = IOBuffer(content)
416-
return s3_multipart_upload(fp.config, fp.bucket, fp.key, io, part_size_mb=part_size_mb; other_kwargs...)
418+
return s3_multipart_upload(get_config(fp), fp.bucket, fp.key, io, part_size_mb=part_size_mb; other_kwargs...)
417419
end
418420
end
419421

0 commit comments

Comments
 (0)