Skip to content

Commit c1e8cc8

Browse files
authored
Support s3 paths (#103)
1 parent f48ac83 commit c1e8cc8

File tree

7 files changed

+59
-39
lines changed

7 files changed

+59
-39
lines changed

.github/workflows/UnitTest.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ jobs:
5050
PackageSpec(name="Reexport", version="0.2"),
5151
PackageSpec(name="Plots", version="1.6"),
5252
])
53+
# Remove Minio from the test project
54+
write("Project.toml", read(`grep -v '^Minio =' Project.toml`, String))
55+
run(`sed -i -e 's/, "Minio"//' Project.toml`)
5356
shell: julia --project=. --startup=no --color=yes {0}
54-
5557
- uses: julia-actions/julia-runtest@latest
56-

Project.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ StatsBase = "0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33"
2020
julia = "1.3"
2121

2222
[extras]
23+
Minio = "4281f0d9-7ae0-406e-9172-b7277c1efa20"
2324
ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
2425
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
2526
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
@@ -33,4 +34,4 @@ ValueHistories = "98cad3c8-aec3-5f06-8e41-884608649ab7"
3334
WAV = "8149f6b0-98f6-5db9-b78f-408fbbb8ef88"
3435

3536
[targets]
36-
test = ["Test", "MLDatasets", "TestImages", "ImageMagick", "Logging", "LightGraphs", "Plots", "PyPlot", "WAV", "Tracker", "ValueHistories"]
37+
test = ["Test", "MLDatasets", "TestImages", "ImageMagick", "Logging", "LightGraphs", "Plots", "PyPlot", "WAV", "Tracker", "ValueHistories", "Minio"]

src/Deserialization/deserialization.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
export summary_iterator
22

33
"""
4-
is_valid_event(f::IOStream) => Bool
4+
is_valid_event(f::IO) => Bool
55
66
Returns true if the stream points to a valid TensorBoard event, false overwise.
77
This is accomplished by checeking the crc checksum on the header (first 8
88
bytes) of the event.
99
"""
10-
function is_valid_event(f::IOStream)
10+
function is_valid_event(f::IO)
1111
eof(f) && return false
1212

1313
header = read(f, 8)
@@ -23,13 +23,13 @@ end
2323

2424

2525
"""
26-
read_event(f::IOStream) => Event
26+
read_event(f::IO) => Event
2727
2828
Reads the stream `f`, assuming it's encoded according to TensorBoard format,
2929
and decodes a single event.
3030
This function assumes that `eof(f) == false`.
3131
"""
32-
function read_event(f::IOStream)
32+
function read_event(f::IO)
3333
header = read(f, 8)
3434
crc_header = read(f, 4)
3535

@@ -113,7 +113,7 @@ Iterator for iterating along a fstream.
113113
The optional argument `stop_at_step` tells at what step the iterator should stop.
114114
"""
115115
struct TBEventFileIterator
116-
fstream::IOStream
116+
fstream::IO
117117
stop_at_step::Int
118118
end
119119
TBEventFileIterator(fstream) = TBEventFileIterator(fstream, typemax(Int))

src/Loggers/LogEmbeddings.jl

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function log_embeddings(logger::TBLogger, name::AbstractString, mat::AbstractMat
3434
write_pbtext(name, logger.logdir, matrix_path, metadata, img_labels, step)
3535
end
3636

37-
function write_matrix(mat::AbstractMatrix, matrix_path::AbstractString)
37+
function write_matrix(mat::AbstractMatrix, matrix_path)
3838
matrix_path = joinpath(matrix_path, "tensor.tsv")
3939
mat = convert(Array{Float64,2}, mat)
4040
open(matrix_path, "w") do file
@@ -45,7 +45,7 @@ function write_matrix(mat::AbstractMatrix, matrix_path::AbstractString)
4545
end
4646
end
4747

48-
function write_metadata(metadata::AbstractArray, matrix_path::AbstractString)
48+
function write_metadata(metadata::AbstractArray, matrix_path)
4949
matrix_path = joinpath(matrix_path, "metadata.tsv")
5050
open(matrix_path, "w") do file
5151
for x in metadata
@@ -54,7 +54,7 @@ function write_metadata(metadata::AbstractArray, matrix_path::AbstractString)
5454
end
5555
end
5656

57-
function write_sprite(img_labels::AbstractArray, matrix_path::AbstractString)
57+
function write_sprite(img_labels::AbstractArray, matrix_path)
5858
n, _, _, w = size(img_labels)
5959
sqrt(n)*w <= 8192 || throw(ErrorException("the value √N * W must be less than or equal to 8192 because of tensorboard restrictions"))
6060
total_pixels = size(img_labels, 1)*size(img_labels, 3)*size(img_labels, 4)
@@ -65,7 +65,9 @@ function write_sprite(img_labels::AbstractArray, matrix_path::AbstractString)
6565
arranged_augment_square_CHW = zeros((3, sprite_size, sprite_size))
6666
arranged_augment_square_CHW[:, 1:size(arranged_img_CHW, 2), :] = arranged_img_CHW
6767
sprite_path = joinpath(matrix_path, "sprite.png")
68-
save(sprite_path, colorview(RGB, arranged_augment_square_CHW))
68+
open(sprite_path; write=true) do io
69+
save(Stream{format"PNG"}(io), colorview(RGB, arranged_augment_square_CHW))
70+
end
6971
end
7072

7173
function make_grid_of_images(img_labels::AbstractArray, ncols::Integer)
@@ -89,21 +91,22 @@ function make_grid_of_images(img_labels::AbstractArray, ncols::Integer)
8991
grid
9092
end
9193

92-
function write_pbtext(name::AbstractString, path::AbstractString, matrix_path::AbstractString, metadata, img_labels, step)
94+
function write_pbtext(name::AbstractString, path, matrix_path, metadata, img_labels, step)
9395
metadata_path = joinpath(matrix_path, "metadata.tsv")
9496
img_labels_path = joinpath(matrix_path, "sprite.png")
9597
matrix_path = joinpath(matrix_path, "tensor.tsv")
9698
path = joinpath(path, "projector_config.pbtxt")
99+
isfile(path) || write(path, "") # workaround https://github.com/JuliaCloud/AWSS3.jl/issues/173
97100
open(path, "a") do file
98101
write(file, "embeddings {\n")
99102
write(file, "tensor_name: \""*name*":"*repr(step)*"\"\n")
100-
write(file, "tensor_path: \""*matrix_path*"\"\n")
103+
write(file, string("tensor_path: \"", matrix_path, "\"\n"))
101104
if metadata != nothing
102-
write(file, "metadata_path: \""*metadata_path*"\"\n")
105+
write(file, string("metadata_path: \"", metadata_path, "\"\n"))
103106
end
104107
if img_labels != nothing
105108
write(file, "sprite {\n")
106-
write(file, "image_path: \""*img_labels_path*"\"\n")
109+
write(file, string("image_path: \"", img_labels_path, "\"\n"))
107110
write(file, "single_image_dim: "*string(size(img_labels, 4))*"\n")
108111
write(file, "single_image_dim: "*string(size(img_labels, 3))*"\n")
109112
write(file, "}\n")

src/TBLogger.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
mutable struct TBLogger <: AbstractLogger
2-
logdir::String
3-
file::IOStream
4-
all_files::Dict{String, IOStream}
1+
mutable struct TBLogger{P,S} <: AbstractLogger
2+
logdir::P
3+
file::S
4+
all_files::Dict{String, S}
55
global_step::Int
66
step_increment::Int
77
min_level::LogLevel
@@ -52,7 +52,7 @@ function TBLogger(logdir="tensorboard_logs/run", overwrite=tb_increment;
5252
all_files = Dict(fpath => evfile)
5353
start_step = something(purge_step, 0)
5454

55-
TBLogger(logdir, evfile, all_files, start_step, step_increment, min_level)
55+
TBLogger{typeof(logdir), typeof(evfile)}(logdir, evfile, all_files, start_step, step_increment, min_level)
5656
end
5757

5858
"""
@@ -89,7 +89,7 @@ function init_logdir(logdir, overwrite=tb_increment)
8989
end
9090

9191
"""
92-
create_eventfile(logdir, [purge_step=nothing; time=time()]) -> IOStream
92+
create_eventfile(logdir, [purge_step=nothing; time=time()]) -> IO
9393
9494
Creates a protobuffer events file in the logdir and returns the IO buffer for
9595
writing to it. If `purge_step::Int` is passed then a special event is written
@@ -144,14 +144,14 @@ logdir(lg::TBLogger) = lg.logdir
144144
"""
145145
get_file(lg::TBLogger) -> IOS
146146
147-
Returns the main `file` IOStream object of Logger `lg`.
147+
Returns the main `file` IO object of Logger `lg`.
148148
"""
149149
get_file(lg::TBLogger) = lg.file
150150

151151
"""
152152
get_file(lg, tags::String...) -> IOS
153153
154-
Returns the `file` IOStream object of Logger `lg` writing to the tag
154+
Returns the `file` IO object of Logger `lg` writing to the tag
155155
`tags1/tags2.../tagsN`.
156156
"""
157157
function get_file(lg::TBLogger, tags::String...)

src/event.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function make_event(logger::TBLogger, summary::GraphDef; step=TensorBoardLogger.
1616
end
1717

1818
"""
19-
write_event(out::IOStream, event::Event)
19+
write_event(out::IO, event::Event)
2020
2121
Serializes the Event `event` to the `out` stream according to the TensorBoard
2222
format. The format follows the following rule (in bytes)
@@ -26,7 +26,7 @@ format. The format follows the following rule (in bytes)
2626
#3 16...N - serialized `event` as protobuffer
2727
#4 N..N+8 UInt32 masked_CRC of #3
2828
"""
29-
function write_event(out::IOStream, event::Event)
29+
function write_event(out::IO, event::Event)
3030
data = PipeBuffer();
3131
_writeproto(data, event)
3232

test/runtests.jl

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,31 @@ using ImageCore
66
using FileIO
77
using LightGraphs
88

9-
test_log_dir = "test_logs/"
109
ENV["DATADEPS_ALWAYS_ACCEPT"] = true
1110
ENV["GKSwstype"] = "100"
12-
1311
ENV["DATADEPS_ALWAYS_ACCEPT"] = true
1412

15-
@testset "TensorBoardLogger" begin
13+
LOG_DIRS = Any["test_logs/"]
14+
15+
if VERSION >= v"1.5"
16+
using Minio
17+
# Setup Minio server to test s3 paths
18+
minio_server = Minio.Server(mktempdir(); address="localhost:9001")
19+
run(minio_server, wait=false)
20+
config = MinioConfig("http://localhost:9001")
21+
s3_create_bucket(config, "tensorboard-tests")
22+
s3_log_dir = S3Path("s3://tensorboard-tests/logdir/"; config=config)
23+
push!(LOG_DIRS, s3_log_dir)
24+
end
25+
26+
@testset "TensorBoardLogger with path $(test_log_dir)" for test_log_dir in LOG_DIRS
1627

1728
@testset "TBLogger" begin
1829
include("test_TBLogger.jl")
1930
end
2031

2132
@testset "Scalar Value Logger" begin
22-
logger = TBLogger(test_log_dir*"t", tb_overwrite)
33+
logger = TBLogger(joinpath(test_log_dir, "t/"), tb_overwrite)
2334
step = 1
2435

2536
ss = TensorBoardLogger.scalar_summary("test", 12.0)
@@ -49,7 +60,7 @@ ENV["DATADEPS_ALWAYS_ACCEPT"] = true
4960
end
5061

5162
@testset "Histogram Value Logger" begin
52-
logger = TBLogger(test_log_dir*"t", tb_overwrite)
63+
logger = TBLogger(joinpath(test_log_dir, "t/"), tb_overwrite)
5364
step = 1
5465

5566
x0 = 0.5+step/30; s0 = 0.5/(step/20);
@@ -96,7 +107,7 @@ ENV["DATADEPS_ALWAYS_ACCEPT"] = true
96107
end
97108

98109
@testset "Text Logger" begin
99-
logger = TBLogger(test_log_dir*"t", tb_overwrite)
110+
logger = TBLogger(joinpath(test_log_dir, "t/"), tb_overwrite)
100111
step = 1
101112

102113
ss = TensorBoardLogger.text_summary("test", "Hello World")
@@ -127,7 +138,7 @@ ENV["DATADEPS_ALWAYS_ACCEPT"] = true
127138
end
128139

129140
@testset "Image Logger" begin
130-
logger = TBLogger(test_log_dir*"t", tb_overwrite)
141+
logger = TBLogger(joinpath(test_log_dir, "t/"), tb_overwrite)
131142
step = 1
132143

133144
# The following tests are akin to @test_nothrow, which does not exist.
@@ -217,7 +228,7 @@ ENV["DATADEPS_ALWAYS_ACCEPT"] = true
217228
end
218229

219230
@testset "LogInterface" begin
220-
logger = TBLogger(test_log_dir*"t", tb_overwrite)
231+
logger = TBLogger(joinpath(test_log_dir, "t/"), tb_overwrite)
221232
woman = testimage("woman_blonde")
222233
mri = testimage("mri")
223234
with_logger(logger) do
@@ -238,7 +249,7 @@ ENV["DATADEPS_ALWAYS_ACCEPT"] = true
238249
end
239250

240251
@testset "Audio Logger" begin
241-
logger = TBLogger(test_log_dir*"t", tb_overwrite)
252+
logger = TBLogger(joinpath(test_log_dir, "t/"), tb_overwrite)
242253
step = 1
243254

244255
ss = TensorBoardLogger.audio_summary("test", rand(800), 800)
@@ -255,7 +266,7 @@ ENV["DATADEPS_ALWAYS_ACCEPT"] = true
255266
end
256267

257268
@testset "Graph Logger" begin
258-
logger = TBLogger(test_log_dir*"t", tb_overwrite)
269+
logger = TBLogger(joinpath(test_log_dir, "t/"), tb_overwrite)
259270
step = 1
260271
ss = TensorBoardLogger.graph_summary(DiGraph(1), ["1"], ["1"], ["cpu"], [nothing])
261272
@test isa(ss, TensorBoardLogger.GraphDef)
@@ -272,14 +283,14 @@ ENV["DATADEPS_ALWAYS_ACCEPT"] = true
272283
end
273284

274285
@testset "Embedding Logger" begin
275-
logger = TBLogger(test_log_dir*"t", tb_overwrite)
286+
logger = TBLogger(joinpath(test_log_dir, "t/"), tb_overwrite)
276287
step = 1
277288
mat = rand(4, 4)
278289
metadata = rand(4, 10)
279290
metadata_header = Array(collect(1:10))
280291
imgs = TBImages(rand(8, 8, 3, 4), HWCN)
281-
@test π != log_embeddings(logger, "random1", mat, metadata = metadata, metadata_header = metadata_header, img_labels = imgs, step = step)
282-
@test π != log_embeddings(logger, "random2", mat, step = step+1)
292+
@test π != log_embeddings(logger, "random1/", mat, metadata = metadata, metadata_header = metadata_header, img_labels = imgs, step = step)
293+
@test π != log_embeddings(logger, "random2/", mat, step = step+1)
283294

284295
close.(values(logger.all_files))
285296
end
@@ -318,3 +329,7 @@ ENV["DATADEPS_ALWAYS_ACCEPT"] = true
318329
rm(test_log_dir, force=true, recursive=true)
319330

320331
end
332+
333+
if VERSION >= v"1.5"
334+
kill(minio_server)
335+
end

0 commit comments

Comments
 (0)