Skip to content

Commit

Permalink
Merge pull request #295 from JuliaIO/teh/rewrite
Browse files Browse the repository at this point in the history
Major rewrite for correctness, performance
  • Loading branch information
timholy authored Mar 3, 2021
2 parents 71bdffe + 412fdc6 commit b0932bc
Show file tree
Hide file tree
Showing 15 changed files with 744 additions and 635 deletions.
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
name = "FileIO"
uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
version = "1.5.0"
version = "1.6.0"

[deps]
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[compat]
julia = "0.7, 1"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# FileIO

[![Build status](https://github.com/JuliaIO/FileIO.jl/actions/workflows/test.yml/badge.svg)](https://github.com/JuliaIO/FileIO.jl/actions/workflows/test.yml)
[![Coverage Status](https://coveralls.io/repos/JuliaIO/FileIO.jl/badge.svg?branch=master&service=github)](https://coveralls.io/github/JuliaIO/FileIO.jl?branch=master)
[![codecov](https://codecov.io/gh/JuliaIO/FileIO.jl/branch/master/graph/badge.svg?token=I0NjrZpJKh)](https://codecov.io/gh/JuliaIO/FileIO.jl)

FileIO aims to provide a common framework for detecting file formats
and dispatching to appropriate readers/writers. The two core
Expand Down
9 changes: 6 additions & 3 deletions src/FileIO.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ export DataFormat,
metadata

import Base.showerror
using Base: RefValue
using Base: RefValue, PkgId
using Pkg
using UUIDs

include("types.jl")
include("registry_setup.jl")
Expand All @@ -42,7 +43,7 @@ include("registry.jl")
- `File{fmt}` and `Stream{fmt}`: types of objects that declare that a resource has a particular format `fmt`
- `load([filename|stream])`: read data in formatted file, inferring the format
- `load(File(format"PNG",filename))`: specify the format manually
- `load(File{format"PNG"}(filename))`: specify the format manually
- `loadstreaming([filename|stream])`: similar to `load`, except that it returns an object that can be read from
- `save(filename, data...)` for similar operations involving saving data
- `savestreaming([filename|stream])`: similar to `save`, except that it returns an object that can be written to
Expand All @@ -56,7 +57,7 @@ include("registry.jl")
- `magic(fmt)` returns the magic bytes for format `fmt`
- `info(fmt)` returns `(magic, extensions)` for format `fmt`
- `add_format(fmt, magic, extension)`: register a new format
- `add_format(fmt, magic, extension, libraries...)`: register a new format
- `add_loader(fmt, :Package)`: indicate that `Package` supports loading files of type `fmt`
- `add_saver(fmt, :Package)`: indicate that `Package` supports saving files of type `fmt`
"""
Expand All @@ -67,4 +68,6 @@ if VERSION >= v"1.4.2" # https://github.com/JuliaLang/julia/pull/35378
_precompile_()
end

include("deprecated.jl")

end
78 changes: 78 additions & 0 deletions src/deprecated.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Deprecations added in 1.5.0, March 2021

function File(fmt::Type{DataFormat{sym}}, filename) where {sym}
Base.depwarn("`File(format\"$sym\", filename)` is deprecated, please use `File{format\"$sym\"}(filename)` instead.", :File)
return File{fmt}(filename)
end
function Stream(fmt::Type{DataFormat{sym}}, args...) where {sym}
Base.depwarn("`Stream(format\"$sym\", filename)` is deprecated, please use `Stream{format\"$sym\"}(filename)` instead.", :Stream)
return Stream{fmt}(args...)
end

# These aren't used here, but old versions of ImageIO expect them

function _findmod(f::Symbol)
Base.depwarn("_findmod is deprecated and will be removed. Use `Base.require(::Base.PkgId)` instead.", :_findmod)
for (u,v) in Base.loaded_modules
(Symbol(v) == f) && return u
end
nothing
end
function topimport(modname)
Base.depwarn("topimport is deprecated and will be removed. Use `Base.require(::Base.PkgId)` instead.", :topimport)
@eval Base.__toplevel__ import $modname
u = _findmod(modname)
@eval $modname = Base.loaded_modules[$u]
end

# Legacy add_loader/add_saver
for add_ in (:add_loader, :add_saver)
@eval begin
function $add_(fmt, pkg)
# TODO: delete this method in FileIO v2
sym = isa(fmt, Symbol) ? fmt : formatname(fmt)::Symbol
Base.depwarn(string($add_) * "(fmt, pkg::$(typeof(pkg))) is deprecated, supply `pkg` as a Module or `name=>uuid`", Symbol($add_))
pkg === :MimeWriter && return $add_(sym, MimeWriter)
# Try to look it up in the caller's environment
pkgname = string(pkg)
id = Base.identify_package(pkgname)
if id === nothing
# See if it's in Main
pkgsym = Symbol(pkg)
if isdefined(Main, pkgsym)
id = getfield(Main, pkgsym)
if !isa(id, Module)
id = nothing
end
end
if id === nothing
# Look it up in the registries. The tricky part here is supporting different Julia versions
ctx = Pkg.API.Context()
uuids = UUID[]
@static if Base.VERSION >= v"1.2"
if hasfield(typeof(ctx), :registries)
for reg in ctx.registries
append!(uuids, Pkg.Registry.uuids_from_name(reg, pkgname))
end
else
ctx = Pkg.API.Context!(ctx)
if isdefined(Pkg.Types, :find_registered!) && hasmethod(Pkg.Types.find_registered!, (typeof(ctx.env), Vector{String}))
Pkg.Types.find_registered!(ctx.env, [pkgname])
elseif isdefined(Pkg.Types, :find_registered!) && hasmethod(Pkg.Types.find_registered!, (typeof(ctx), Vector{String}))
Pkg.Types.find_registered!(ctx, [pkgname])
end
append!(uuids, get(ctx.env.uuids, pkgname, UUID[]))
end
else
Pkg.Types.find_registered!(ctx.env)
append!(uuids, get(ctx.env.uuids, pkgname, UUID[]))
end
isempty(uuids) && throw(ArgumentError("no UUID found for $pkg"))
length(uuids) == 1 || throw(ArgumentError("multiple UUIDs found for $pkg"))
id = PkgId(uuids[1], pkgname)
end
end
$add_(sym, id)
end
end
end
36 changes: 4 additions & 32 deletions src/error_handling.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,35 +24,12 @@ Base.showerror(io::IO, e::WriterError) = println(
e.msg, "\n Will try next writer."
)

"""
`NotInstalledError` should be thrown when a library is currently not installed.
"""
struct NotInstalledError <: Exception
library::Symbol
message::String
end
Base.showerror(io::IO, e::NotInstalledError) = println(io, e.library, " is not installed.")

"""
`UnknownFormat` gets thrown when FileIO can't recognize the format of a file.
"""
struct UnknownFormat{T <: Formatted} <: Exception
format::T
struct SpecError <: Exception
mod::Module
call::Symbol
end
Base.showerror(io::IO, e::UnknownFormat) = println(io, e.format, " couldn't be recognized by FileIO.")


"""
Handles error as soon as they get thrown while doing IO
"""
function handle_current_error(e, library, islast::Bool)
bt = catch_backtrace()
bts = sprint(io->Base.show_backtrace(io, bt))
message = islast ? "" : "\nTrying next loading library! Please report this issue on the Github page for $library"
@warn string(e, bts, message)
end
handle_current_error(e::NotInstalledError) = @warn string("lib ", e.library, " not installed, trying next library")

Base.showerror(io::IO, e::SpecError) = print(io, e.mod, " is missing $(e.call) and fileio_$(e.call)")

"""
Handles a list of thrown errors after no IO library was found working
Expand Down Expand Up @@ -80,8 +57,3 @@ function handle_exceptions(exceptions::Vector, action)
end

handle_error(e, q) = throw(e)

function handle_error(e::NotInstalledError, q)
println("Library \"", e.library, "\" is not installed but is recommended as a library to load format: \"", file_extension(q), "\"")
rethrow(e)
end
Loading

0 comments on commit b0932bc

Please sign in to comment.