Skip to content

Commit 3e63626

Browse files
committed
Make @service only define a module once
Currently, `@service` defines a module every time it's called. That works fine but the downside is that you get module redefinition warnings if you happen to call `@service` again. The most plausible reason why one would want to redefine the module would be to enable/disable particular features in some region of code. To facilitate that use case while avoiding redefinition warnings, we can `@service` check for an existing module, define one if it can't find one, and if it can then modify the feature set associated with the module in place. With this change, `@service S3` (e.g.) effectively expands to: ```julia if isdefined(Main, :S3) AWS.set_features!(S3.SERVICE_FEATURE_SET) else module S3 # contents end end ``` The `set_features!` call uses the features specified in the macro call as keyword arguments. No arguments uses defaults. Notably this design requires that `FeatureSet` be made mutable, which seems fine IMO.
1 parent bdb518c commit 3e63626

File tree

2 files changed

+37
-3
lines changed

2 files changed

+37
-3
lines changed

src/AWS.jl

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const aws_config = Ref{AbstractAWSConfig}()
5050
"""
5151
FeatureSet
5252
53-
Allows end users to opt-in to new breaking behaviors prior before a major/breaking package
53+
Allows end users to opt-in to new breaking behaviors prior to a major/breaking package
5454
release. Each field of this struct contains a default which specifies uses the original
5555
non-breaking behavior.
5656
@@ -61,10 +61,21 @@ non-breaking behavior.
6161
call response typically be parsed but will vary depending on the following parameters:
6262
"return_headers", "return_stream", "return_raw", "response_dict_type".
6363
"""
64-
Base.@kwdef struct FeatureSet
64+
Base.@kwdef mutable struct FeatureSet
6565
use_response_type::Bool = false
6666
end
6767

68+
# Reset the given `FeatureSet` to have the features specified via keyword arguments.
69+
# Notably, if no keyword arguments are provided, the `FeatureSet` is reset to defaults.
70+
# This is used internally by `@service` when a module already exists.
71+
function set_features!(fs::FeatureSet; kwargs...)
72+
new = FeatureSet(; kwargs...)
73+
for field in fieldnames(FeatureSet)
74+
setfield!(fs, field, getfield(new, field))
75+
end
76+
return fs
77+
end
78+
6879
"""
6980
global_aws_config()
7081
@@ -168,6 +179,9 @@ AWS.Response: application/xml interpreted as:
168179
OrderedCollections.LittleDict{Union{String, Symbol}, Any, Vector{Union{String, Symbol}}, Vector{Any}} with 2 entries:
169180
"GetCallerIdentityResult" => LittleDict{Union{String, Symbol}, Any, Vector{Un…
170181
"ResponseMetadata" => LittleDict{Union{String, Symbol}, Any, Vector{Un…
182+
183+
julia> @service STS as SecurityTokenService # reset a feature to its default by calling `@service` again
184+
SecurityTokenService
171185
```
172186
173187
Service IDs are case insensitive:
@@ -225,8 +239,16 @@ macro service(exprs...)
225239
const SERVICE_FEATURE_SET = FeatureSet(; $(features...))
226240
include($service_file)
227241
end
242+
module_def = Expr(:toplevel, Expr(:module, true, esc(module_name), esc(module_block)))
243+
244+
service_module = :($__module__.$module_name)
245+
reset_block = quote
246+
$set_features!($service_module.SERVICE_FEATURE_SET; $(features...))
247+
$service_module
248+
end
249+
check = :($(Core.isdefined)($__module__, $(QuoteNode(module_name))))
228250

229-
return Expr(:toplevel, Expr(:module, true, esc(module_name), esc(module_block)))
251+
return Expr(:if, esc(check), esc(reset_block), module_def)
230252
end
231253

232254
abstract type Service end

test/unit/AWS.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,16 @@
110110
@test !(:STS in names(@__MODULE__; all=true))
111111
@test !(Symbol("STS.X") in names(@__MODULE__; all=true))
112112
end
113+
114+
@testset "module redefinition" begin
115+
#! format: off
116+
@eval baremodule __service_module_redefinition
117+
using AWS, Test
118+
@service S3 use_response_type = true
119+
features = S3.SERVICE_FEATURE_SET
120+
@service S3
121+
@test features === S3.SERVICE_FEATURE_SET # ensures module wasn't replaced
122+
end
123+
#! format: on
124+
end
113125
end

0 commit comments

Comments
 (0)