770 lines
26 KiB
Julia
770 lines
26 KiB
Julia
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
|
|
|
# editing files
|
|
|
|
"""
|
|
editor()
|
|
|
|
Determines the editor to use when running functions like `edit`. Returns an Array compatible
|
|
for use within backticks. You can change the editor by setting `JULIA_EDITOR`, `VISUAL` or
|
|
`EDITOR` as an environment variable.
|
|
"""
|
|
function editor()
|
|
if is_windows() || is_apple()
|
|
default_editor = "open"
|
|
elseif isfile("/etc/alternatives/editor")
|
|
default_editor = "/etc/alternatives/editor"
|
|
else
|
|
default_editor = "emacs"
|
|
end
|
|
# Note: the editor path can include spaces (if escaped) and flags.
|
|
args = shell_split(get(ENV,"JULIA_EDITOR", get(ENV,"VISUAL", get(ENV,"EDITOR", default_editor))))
|
|
isempty(args) && error("editor is empty")
|
|
return args
|
|
end
|
|
|
|
"""
|
|
edit(path::AbstractString, line::Integer=0)
|
|
|
|
Edit a file or directory optionally providing a line number to edit the file at.
|
|
Returns to the `julia` prompt when you quit the editor. The editor can be changed
|
|
by setting `JULIA_EDITOR`, `VISUAL` or `EDITOR` as an environment variable.
|
|
"""
|
|
function edit(path::AbstractString, line::Integer=0)
|
|
command = editor()
|
|
name = basename(first(command))
|
|
issrc = length(path)>2 && path[end-2:end] == ".jl"
|
|
if issrc
|
|
f = find_source_file(path)
|
|
f !== nothing && (path = f)
|
|
end
|
|
background = true
|
|
line_unsupported = false
|
|
if startswith(name, "emacs") || name == "gedit"
|
|
cmd = line != 0 ? `$command +$line $path` : `$command $path`
|
|
elseif startswith(name, "vim.") || name == "vi" || name == "vim" || name == "nvim" || name == "mvim" || name == "nano"
|
|
cmd = line != 0 ? `$command +$line $path` : `$command $path`
|
|
background = false
|
|
elseif name == "textmate" || name == "mate" || name == "kate"
|
|
cmd = line != 0 ? `$command $path -l $line` : `$command $path`
|
|
elseif startswith(name, "subl") || startswith(name, "atom")
|
|
cmd = line != 0 ? `$command $path:$line` : `$command $path`
|
|
elseif name == "code" || (is_windows() && uppercase(name) == "CODE.EXE")
|
|
cmd = line != 0 ? `$command -g $path:$line` : `$command -g $path`
|
|
elseif startswith(name, "notepad++")
|
|
cmd = line != 0 ? `$command $path -n$line` : `$command $path`
|
|
elseif is_apple() && name == "open"
|
|
cmd = `open -t $path`
|
|
line_unsupported = true
|
|
else
|
|
cmd = `$command $path`
|
|
background = false
|
|
line_unsupported = true
|
|
end
|
|
|
|
if is_windows() && name == "open"
|
|
@static is_windows() && # don't emit this ccall on other platforms
|
|
systemerror(:edit, ccall((:ShellExecuteW, "shell32"), stdcall, Int,
|
|
(Ptr{Void}, Cwstring, Cwstring, Ptr{Void}, Ptr{Void}, Cint),
|
|
C_NULL, "open", path, C_NULL, C_NULL, 10) ≤ 32)
|
|
elseif background
|
|
spawn(pipeline(cmd, stderr=STDERR))
|
|
else
|
|
run(cmd)
|
|
end
|
|
line != 0 && line_unsupported && println("Unknown editor: no line number information passed.\nThe method is defined at line $line.")
|
|
|
|
nothing
|
|
end
|
|
|
|
"""
|
|
edit(function, [types])
|
|
|
|
Edit the definition of a function, optionally specifying a tuple of types to
|
|
indicate which method to edit. The editor can be changed by setting `JULIA_EDITOR`,
|
|
`VISUAL` or `EDITOR` as an environment variable.
|
|
"""
|
|
edit(f) = edit(functionloc(f)...)
|
|
edit(f, t::ANY) = edit(functionloc(f,t)...)
|
|
edit(file, line::Integer) = error("could not find source file for function")
|
|
|
|
# terminal pager
|
|
|
|
if is_windows()
|
|
function less(file::AbstractString, line::Integer)
|
|
pager = get(ENV, "PAGER", "more")
|
|
g = pager == "more" ? "" : "g"
|
|
run(Cmd(`$pager +$(line)$(g) \"$file\"`, windows_verbatim = true))
|
|
end
|
|
else
|
|
function less(file::AbstractString, line::Integer)
|
|
pager = get(ENV, "PAGER", "less")
|
|
run(`$pager +$(line)g $file`)
|
|
end
|
|
end
|
|
|
|
"""
|
|
less(file::AbstractString, [line::Integer])
|
|
|
|
Show a file using the default pager, optionally providing a starting line number. Returns to
|
|
the `julia` prompt when you quit the pager.
|
|
"""
|
|
less(file::AbstractString) = less(file, 1)
|
|
|
|
"""
|
|
less(function, [types])
|
|
|
|
Show the definition of a function using the default pager, optionally specifying a tuple of
|
|
types to indicate which method to see.
|
|
"""
|
|
less(f) = less(functionloc(f)...)
|
|
less(f, t::ANY) = less(functionloc(f,t)...)
|
|
less(file, line::Integer) = error("could not find source file for function")
|
|
|
|
# clipboard copy and paste
|
|
|
|
if is_apple()
|
|
function clipboard(x)
|
|
open(pipeline(`pbcopy`, stderr=STDERR), "w") do io
|
|
print(io, x)
|
|
end
|
|
end
|
|
clipboard() = readstring(`pbpaste`)
|
|
|
|
elseif is_linux() || Sys.KERNEL === :FreeBSD
|
|
_clipboardcmd = nothing
|
|
const _clipboardcmds = Dict(
|
|
:copy => Dict(
|
|
:xsel => is_linux() ?
|
|
`xsel --nodetach --input --clipboard` : `xsel -c`,
|
|
:xclip => `xclip -silent -in -selection clipboard`,
|
|
),
|
|
:paste => Dict(
|
|
:xsel => is_linux() ?
|
|
`xsel --nodetach --output --clipboard` : `xsel -p`,
|
|
:xclip => `xclip -quiet -out -selection clipboard`,
|
|
)
|
|
)
|
|
function clipboardcmd()
|
|
global _clipboardcmd
|
|
_clipboardcmd !== nothing && return _clipboardcmd
|
|
for cmd in (:xclip, :xsel)
|
|
success(pipeline(`which $cmd`, DevNull)) && return _clipboardcmd = cmd
|
|
end
|
|
pkgs = @static if is_linux()
|
|
"xsel or xclip"
|
|
elseif Sys.KERNEL === :FreeBSD
|
|
"x11/xsel or x11/xclip"
|
|
end
|
|
error("no clipboard command found, please install $pkgs")
|
|
end
|
|
function clipboard(x)
|
|
c = clipboardcmd()
|
|
cmd = get(_clipboardcmds[:copy], c, nothing)
|
|
if cmd === nothing
|
|
error("unexpected clipboard command: $c")
|
|
end
|
|
open(pipeline(cmd, stderr=STDERR), "w") do io
|
|
print(io, x)
|
|
end
|
|
end
|
|
function clipboard()
|
|
c = clipboardcmd()
|
|
cmd = get(_clipboardcmds[:paste], c, nothing)
|
|
if cmd === nothing
|
|
error("unexpected clipboard command: $c")
|
|
end
|
|
readstring(pipeline(cmd, stderr=STDERR))
|
|
end
|
|
|
|
elseif is_windows()
|
|
# TODO: these functions leak memory and memory locks if they throw an error
|
|
function clipboard(x::AbstractString)
|
|
if containsnul(x)
|
|
throw(ArgumentError("Windows clipboard strings cannot contain NUL character"))
|
|
end
|
|
systemerror(:OpenClipboard, 0==ccall((:OpenClipboard, "user32"), stdcall, Cint, (Ptr{Void},), C_NULL))
|
|
systemerror(:EmptyClipboard, 0==ccall((:EmptyClipboard, "user32"), stdcall, Cint, ()))
|
|
x_u16 = cwstring(x)
|
|
# copy data to locked, allocated space
|
|
p = ccall((:GlobalAlloc, "kernel32"), stdcall, Ptr{UInt16}, (UInt16, Int32), 2, sizeof(x_u16))
|
|
systemerror(:GlobalAlloc, p==C_NULL)
|
|
plock = ccall((:GlobalLock, "kernel32"), stdcall, Ptr{UInt16}, (Ptr{UInt16},), p)
|
|
systemerror(:GlobalLock, plock==C_NULL)
|
|
ccall(:memcpy, Ptr{UInt16}, (Ptr{UInt16},Ptr{UInt16},Int), plock, x_u16, sizeof(x_u16))
|
|
systemerror(:GlobalUnlock, 0==ccall((:GlobalUnlock, "kernel32"), stdcall, Cint, (Ptr{Void},), plock))
|
|
pdata = ccall((:SetClipboardData, "user32"), stdcall, Ptr{UInt16}, (UInt32, Ptr{UInt16}), 13, p)
|
|
systemerror(:SetClipboardData, pdata!=p)
|
|
ccall((:CloseClipboard, "user32"), stdcall, Void, ())
|
|
end
|
|
clipboard(x) = clipboard(sprint(print, x)::String)
|
|
function clipboard()
|
|
systemerror(:OpenClipboard, 0==ccall((:OpenClipboard, "user32"), stdcall, Cint, (Ptr{Void},), C_NULL))
|
|
pdata = ccall((:GetClipboardData, "user32"), stdcall, Ptr{UInt16}, (UInt32,), 13)
|
|
systemerror(:SetClipboardData, pdata==C_NULL)
|
|
systemerror(:CloseClipboard, 0==ccall((:CloseClipboard, "user32"), stdcall, Cint, ()))
|
|
plock = ccall((:GlobalLock, "kernel32"), stdcall, Ptr{UInt16}, (Ptr{UInt16},), pdata)
|
|
systemerror(:GlobalLock, plock==C_NULL)
|
|
# find NUL terminator (0x0000 16-bit code unit)
|
|
len = 0
|
|
while unsafe_load(plock, len+1) != 0; len += 1; end
|
|
# get Vector{UInt16}, transcode data to UTF-8, make a String of it
|
|
s = transcode(String, unsafe_wrap(Array, plock, len))
|
|
systemerror(:GlobalUnlock, 0==ccall((:GlobalUnlock, "kernel32"), stdcall, Cint, (Ptr{UInt16},), plock))
|
|
return s
|
|
end
|
|
|
|
else
|
|
clipboard(x="") = error("`clipboard` function not implemented for $(Sys.KERNEL)")
|
|
end
|
|
|
|
|
|
"""
|
|
clipboard(x)
|
|
|
|
Send a printed form of `x` to the operating system clipboard ("copy").
|
|
"""
|
|
clipboard(x)
|
|
|
|
"""
|
|
clipboard() -> AbstractString
|
|
|
|
Return a string with the contents of the operating system clipboard ("paste").
|
|
"""
|
|
clipboard()
|
|
|
|
|
|
# system information
|
|
|
|
# used by sysinfo.jl
|
|
function _show_cpuinfo(io::IO, info::Sys.CPUinfo, header::Bool=true, prefix::AbstractString=" ")
|
|
tck = Sys.SC_CLK_TCK
|
|
if header
|
|
println(io, info.model, ": ")
|
|
print(io, " "^length(prefix))
|
|
if tck > 0
|
|
@printf(io, " %5s %9s %9s %9s %9s %9s\n",
|
|
"speed", "user", "nice", "sys", "idle", "irq")
|
|
else
|
|
@printf(io, " %5s %9s %9s %9s %9s %9s ticks\n",
|
|
"speed", "user", "nice", "sys", "idle", "irq")
|
|
end
|
|
end
|
|
print(io, prefix)
|
|
if tck > 0
|
|
@printf(io, "%5d MHz %9d s %9d s %9d s %9d s %9d s",
|
|
info.speed, info.cpu_times!user / tck, info.cpu_times!nice / tck,
|
|
info.cpu_times!sys / tck, info.cpu_times!idle / tck, info.cpu_times!irq / tck)
|
|
else
|
|
@printf(io, "%5d MHz %9d %9d %9d %9d %9d ticks",
|
|
info.speed, info.cpu_times!user, info.cpu_times!nice,
|
|
info.cpu_times!sys, info.cpu_times!idle, info.cpu_times!irq)
|
|
end
|
|
end
|
|
|
|
|
|
"""
|
|
versioninfo(io::IO=STDOUT, verbose::Bool=false)
|
|
|
|
Print information about the version of Julia in use. If the `verbose` argument is `true`,
|
|
detailed system information is shown as well.
|
|
"""
|
|
function versioninfo(io::IO=STDOUT, verbose::Bool=false)
|
|
println(io, "Julia Version $VERSION")
|
|
if !isempty(GIT_VERSION_INFO.commit_short)
|
|
println(io, "Commit $(GIT_VERSION_INFO.commit_short) ($(GIT_VERSION_INFO.date_string))")
|
|
end
|
|
if ccall(:jl_is_debugbuild, Cint, ())!=0
|
|
println(io, "DEBUG build")
|
|
end
|
|
println(io, "Platform Info:")
|
|
println(io, " OS: ", is_windows() ? "Windows" : is_apple() ?
|
|
"macOS" : Sys.KERNEL, " (", Sys.MACHINE, ")")
|
|
|
|
cpu = Sys.cpu_info()
|
|
println(io, " CPU: ", cpu[1].model)
|
|
println(io, " WORD_SIZE: ", Sys.WORD_SIZE)
|
|
if verbose
|
|
lsb = ""
|
|
if is_linux()
|
|
try lsb = readchomp(pipeline(`lsb_release -ds`, stderr=DevNull)) end
|
|
end
|
|
if is_windows()
|
|
try lsb = strip(readstring(`$(ENV["COMSPEC"]) /c ver`)) end
|
|
end
|
|
if !isempty(lsb)
|
|
println(io, " ", lsb)
|
|
end
|
|
if is_unix()
|
|
println(io, " uname: ", readchomp(`uname -mprsv`))
|
|
end
|
|
println(io, "Memory: $(Sys.total_memory()/2^30) GB ($(Sys.free_memory()/2^20) MB free)")
|
|
try println(io, "Uptime: $(Sys.uptime()) sec") end
|
|
print(io, "Load Avg: ")
|
|
print_matrix(io, Sys.loadavg()')
|
|
println(io )
|
|
Sys.cpu_summary(io)
|
|
println(io )
|
|
end
|
|
if Base.libblas_name == "libopenblas" || BLAS.vendor() == :openblas || BLAS.vendor() == :openblas64
|
|
openblas_config = BLAS.openblas_get_config()
|
|
println(io, " BLAS: libopenblas (", openblas_config, ")")
|
|
else
|
|
println(io, " BLAS: ",libblas_name)
|
|
end
|
|
println(io, " LAPACK: ",liblapack_name)
|
|
println(io, " LIBM: ",libm_name)
|
|
println(io, " LLVM: libLLVM-",libllvm_version," (", Sys.JIT, ", ", Sys.cpu_name, ")")
|
|
if verbose
|
|
println(io, "Environment:")
|
|
for (k,v) in ENV
|
|
if match(r"JULIA|PATH|FLAG|^TERM$|HOME", String(k)) !== nothing
|
|
println(io, " $(k) = $(v)")
|
|
end
|
|
end
|
|
println(io )
|
|
println(io, "Package Directory: ", Pkg.dir())
|
|
Pkg.status(io)
|
|
end
|
|
end
|
|
versioninfo(verbose::Bool) = versioninfo(STDOUT,verbose)
|
|
|
|
# displaying type-ambiguity warnings
|
|
|
|
|
|
"""
|
|
code_warntype([io::IO], f, types)
|
|
|
|
Prints lowered and type-inferred ASTs for the methods matching the given generic function
|
|
and type signature to `io` which defaults to `STDOUT`. The ASTs are annotated in such a way
|
|
as to cause "non-leaf" types to be emphasized (if color is available, displayed in red).
|
|
This serves as a warning of potential type instability. Not all non-leaf types are particularly
|
|
problematic for performance, so the results need to be used judiciously.
|
|
See [`@code_warntype`](@ref man-code-warntype) for more information.
|
|
"""
|
|
function code_warntype(io::IO, f, t::ANY)
|
|
function slots_used(ci, slotnames)
|
|
used = falses(length(slotnames))
|
|
scan_exprs!(used, ci.code)
|
|
return used
|
|
end
|
|
|
|
function scan_exprs!(used, exprs)
|
|
for ex in exprs
|
|
if isa(ex, Slot)
|
|
used[ex.id] = true
|
|
elseif isa(ex, Expr)
|
|
scan_exprs!(used, ex.args)
|
|
end
|
|
end
|
|
end
|
|
|
|
emph_io = IOContext(io, :TYPEEMPHASIZE => true)
|
|
for (src, rettype) in code_typed(f, t)
|
|
println(emph_io, "Variables:")
|
|
slotnames = sourceinfo_slotnames(src)
|
|
used_slotids = slots_used(src, slotnames)
|
|
for i = 1:length(slotnames)
|
|
print(emph_io, " ", slotnames[i])
|
|
if used_slotids[i]
|
|
if isa(src.slottypes, Array)
|
|
show_expr_type(emph_io, src.slottypes[i], true)
|
|
end
|
|
else
|
|
print(emph_io, " <optimized out>")
|
|
end
|
|
print(emph_io, '\n')
|
|
end
|
|
print(emph_io, "\nBody:\n ")
|
|
body = Expr(:body)
|
|
body.args = src.code
|
|
body.typ = rettype
|
|
# Fix slot names and types in function body
|
|
show_unquoted(IOContext(IOContext(emph_io, :SOURCEINFO => src),
|
|
:SOURCE_SLOTNAMES => slotnames),
|
|
body, 2)
|
|
print(emph_io, '\n')
|
|
end
|
|
nothing
|
|
end
|
|
code_warntype(f, t::ANY) = code_warntype(STDOUT, f, t)
|
|
|
|
typesof(args...) = Tuple{map(a->(isa(a,Type) ? Type{a} : typeof(a)), args)...}
|
|
|
|
gen_call_with_extracted_types(fcn, ex0::Symbol) = Expr(:call, fcn, Meta.quot(ex0))
|
|
function gen_call_with_extracted_types(fcn, ex0)
|
|
if isa(ex0, Expr)
|
|
if any(a->(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args)
|
|
# remove keyword args, but call the kwfunc
|
|
args = filter(a->!(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args)
|
|
return quote
|
|
local arg1 = $(esc(args[1]))
|
|
$(fcn)(Core.kwfunc(arg1),
|
|
Tuple{Vector{Any}, Core.Typeof(arg1),
|
|
$(typesof)($(map(esc, args[2:end])...)).parameters...})
|
|
end
|
|
elseif ex0.head == :call
|
|
return Expr(:call, fcn, esc(ex0.args[1]),
|
|
Expr(:call, typesof, map(esc, ex0.args[2:end])...))
|
|
end
|
|
end
|
|
exret = Expr(:none)
|
|
is_macro = false
|
|
ex = expand(ex0)
|
|
if isa(ex0, Expr) && ex0.head == :macrocall # Make @edit @time 1+2 edit the macro
|
|
is_macro = true
|
|
exret = Expr(:call, fcn, esc(ex0.args[1]), typesof(ex0.args[2:end]...))
|
|
elseif !isa(ex, Expr)
|
|
exret = Expr(:call, :error, "expression is not a function call or symbol")
|
|
elseif ex.head == :call
|
|
if any(e->(isa(e, Expr) && e.head==:(...)), ex0.args) &&
|
|
(ex.args[1] === GlobalRef(Core,:_apply) ||
|
|
ex.args[1] === GlobalRef(Base,:_apply))
|
|
# check for splatting
|
|
exret = Expr(:call, ex.args[1], fcn,
|
|
Expr(:tuple, esc(ex.args[2]),
|
|
Expr(:call, typesof, map(esc, ex.args[3:end])...)))
|
|
else
|
|
exret = Expr(:call, fcn, esc(ex.args[1]),
|
|
Expr(:call, typesof, map(esc, ex.args[2:end])...))
|
|
end
|
|
elseif ex.head == :body
|
|
a1 = ex.args[1]
|
|
if isa(a1, Expr) && a1.head == :call
|
|
a11 = a1.args[1]
|
|
if a11 == :setindex!
|
|
exret = Expr(:call, fcn, a11,
|
|
Expr(:call, typesof, map(esc, a1.args[2:end])...))
|
|
end
|
|
end
|
|
end
|
|
if (!is_macro && ex.head == :thunk) || exret.head == :none
|
|
exret = Expr(:call, :error, "expression is not a function call, "
|
|
* "or is too complex for @$fcn to analyze; "
|
|
* "break it down to simpler parts if possible")
|
|
end
|
|
exret
|
|
end
|
|
|
|
for fname in [:which, :less, :edit, :functionloc, :code_warntype,
|
|
:code_llvm, :code_llvm_raw, :code_native]
|
|
@eval begin
|
|
macro ($fname)(ex0)
|
|
gen_call_with_extracted_types($(Expr(:quote,fname)), ex0)
|
|
end
|
|
end
|
|
end
|
|
|
|
for fname in [:code_typed, :code_lowered]
|
|
@eval begin
|
|
macro ($fname)(ex0)
|
|
thecall = gen_call_with_extracted_types($(Expr(:quote,fname)), ex0)
|
|
quote
|
|
results = $thecall
|
|
length(results) == 1 ? results[1] : results
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
"""
|
|
@which
|
|
|
|
Applied to a function or macro call, it evaluates the arguments to the specified call, and
|
|
returns the `Method` object for the method that would be called for those arguments. Applied
|
|
to a variable, it returns the module in which the variable was bound. It calls out to the
|
|
`which` function.
|
|
"""
|
|
:@which
|
|
|
|
"""
|
|
@less
|
|
|
|
Evaluates the arguments to the function or macro call, determines their types, and calls the `less`
|
|
function on the resulting expression.
|
|
"""
|
|
:@less
|
|
|
|
"""
|
|
@edit
|
|
|
|
Evaluates the arguments to the function or macro call, determines their types, and calls the `edit`
|
|
function on the resulting expression.
|
|
"""
|
|
:@edit
|
|
|
|
"""
|
|
@functionloc
|
|
|
|
Applied to a function or macro call, it evaluates the arguments to the specified call, and
|
|
returns a tuple `(filename,line)` giving the location for the method that would be called for those arguments.
|
|
It calls out to the `functionloc` function.
|
|
"""
|
|
:@functionloc
|
|
|
|
"""
|
|
@code_typed
|
|
|
|
Evaluates the arguments to the function or macro call, determines their types, and calls
|
|
[`code_typed`](@ref) on the resulting expression.
|
|
"""
|
|
:@code_typed
|
|
|
|
"""
|
|
@code_warntype
|
|
|
|
Evaluates the arguments to the function or macro call, determines their types, and calls
|
|
[`code_warntype`](@ref) on the resulting expression.
|
|
"""
|
|
:@code_warntype
|
|
|
|
"""
|
|
@code_lowered
|
|
|
|
Evaluates the arguments to the function or macro call, determines their types, and calls
|
|
[`code_lowered`](@ref) on the resulting expression.
|
|
"""
|
|
:@code_lowered
|
|
|
|
"""
|
|
@code_llvm
|
|
|
|
Evaluates the arguments to the function or macro call, determines their types, and calls
|
|
[`code_llvm`](@ref) on the resulting expression.
|
|
"""
|
|
:@code_llvm
|
|
|
|
"""
|
|
@code_native
|
|
|
|
Evaluates the arguments to the function or macro call, determines their types, and calls
|
|
[`code_native`](@ref) on the resulting expression.
|
|
"""
|
|
:@code_native
|
|
|
|
function type_close_enough(x::ANY, t::ANY)
|
|
x == t && return true
|
|
return (isa(x,DataType) && isa(t,DataType) && x.name === t.name &&
|
|
!isleaftype(t) && x <: t) ||
|
|
(isa(x,Union) && isa(t,DataType) && (type_close_enough(x.a, t) || type_close_enough(x.b, t)))
|
|
end
|
|
|
|
# `methodswith` -- shows a list of methods using the type given
|
|
"""
|
|
methodswith(typ[, module or function][, showparents::Bool=false])
|
|
|
|
Return an array of methods with an argument of type `typ`.
|
|
|
|
The optional second argument restricts the search to a particular module or function
|
|
(the default is all modules, starting from Main).
|
|
|
|
If optional `showparents` is `true`, also return arguments with a parent type of `typ`,
|
|
excluding type `Any`.
|
|
"""
|
|
function methodswith(t::Type, f::Function, showparents::Bool=false, meths = Method[])
|
|
for d in methods(f)
|
|
if any(function (x)
|
|
let x = rewrap_unionall(x, d.sig)
|
|
(type_close_enough(x, t) ||
|
|
(showparents ? (t <: x && (!isa(x,TypeVar) || x.ub != Any)) :
|
|
(isa(x,TypeVar) && x.ub != Any && t == x.ub)) &&
|
|
x != Any && x != ANY)
|
|
end
|
|
end,
|
|
unwrap_unionall(d.sig).parameters)
|
|
push!(meths, d)
|
|
end
|
|
end
|
|
return meths
|
|
end
|
|
|
|
function methodswith(t::Type, m::Module, showparents::Bool=false)
|
|
meths = Method[]
|
|
for nm in names(m)
|
|
if isdefined(m, nm)
|
|
f = getfield(m, nm)
|
|
if isa(f, Function)
|
|
methodswith(t, f, showparents, meths)
|
|
end
|
|
end
|
|
end
|
|
return unique(meths)
|
|
end
|
|
|
|
function methodswith(t::Type, showparents::Bool=false)
|
|
meths = Method[]
|
|
mainmod = Main
|
|
# find modules in Main
|
|
for nm in names(mainmod)
|
|
if isdefined(mainmod, nm)
|
|
mod = getfield(mainmod, nm)
|
|
if isa(mod, Module)
|
|
append!(meths, methodswith(t, mod, showparents))
|
|
end
|
|
end
|
|
end
|
|
return unique(meths)
|
|
end
|
|
|
|
# file downloading
|
|
|
|
downloadcmd = nothing
|
|
if is_windows()
|
|
function download(url::AbstractString, filename::AbstractString)
|
|
res = ccall((:URLDownloadToFileW,:urlmon),stdcall,Cuint,
|
|
(Ptr{Void},Cwstring,Cwstring,Cuint,Ptr{Void}),C_NULL,url,filename,0,C_NULL)
|
|
if res != 0
|
|
error("automatic download failed (error: $res): $url")
|
|
end
|
|
filename
|
|
end
|
|
else
|
|
function download(url::AbstractString, filename::AbstractString)
|
|
global downloadcmd
|
|
if downloadcmd === nothing
|
|
for checkcmd in (:curl, :wget, :fetch)
|
|
if success(pipeline(`which $checkcmd`, DevNull))
|
|
downloadcmd = checkcmd
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if downloadcmd == :wget
|
|
try
|
|
run(`wget -O $filename $url`)
|
|
catch
|
|
rm(filename) # wget always creates a file
|
|
rethrow()
|
|
end
|
|
elseif downloadcmd == :curl
|
|
run(`curl -L -f -o $filename $url`)
|
|
elseif downloadcmd == :fetch
|
|
run(`fetch -f $filename $url`)
|
|
else
|
|
error("no download agent available; install curl, wget, or fetch")
|
|
end
|
|
filename
|
|
end
|
|
end
|
|
function download(url::AbstractString)
|
|
filename = tempname()
|
|
download(url, filename)
|
|
end
|
|
|
|
"""
|
|
download(url::AbstractString, [localfile::AbstractString])
|
|
|
|
Download a file from the given url, optionally renaming it to the given local file name.
|
|
Note that this function relies on the availability of external tools such as `curl`, `wget`
|
|
or `fetch` to download the file and is provided for convenience. For production use or
|
|
situations in which more options are needed, please use a package that provides the desired
|
|
functionality instead.
|
|
"""
|
|
download(url, filename)
|
|
|
|
# workspace management
|
|
|
|
"""
|
|
workspace()
|
|
|
|
Replace the top-level module (`Main`) with a new one, providing a clean workspace. The
|
|
previous `Main` module is made available as `LastMain`. A previously-loaded package can be
|
|
accessed using a statement such as `using LastMain.Package`.
|
|
|
|
This function should only be used interactively.
|
|
"""
|
|
function workspace()
|
|
last = Core.Main
|
|
b = last.Base
|
|
ccall(:jl_new_main_module, Any, ())
|
|
m = Core.Main
|
|
ccall(:jl_add_standard_imports, Void, (Any,), m)
|
|
eval(m,
|
|
Expr(:toplevel,
|
|
:(const Base = $(Expr(:quote, b))),
|
|
:(const LastMain = $(Expr(:quote, last)))))
|
|
empty!(package_locks)
|
|
nothing
|
|
end
|
|
|
|
# testing
|
|
|
|
"""
|
|
runtests([tests=["all"] [, numcores=ceil(Int, Sys.CPU_CORES / 2) ]])
|
|
|
|
Run the Julia unit tests listed in `tests`, which can be either a string or an array of
|
|
strings, using `numcores` processors. (not exported)
|
|
"""
|
|
function runtests(tests = ["all"], numcores = ceil(Int, Sys.CPU_CORES / 2))
|
|
if isa(tests,AbstractString)
|
|
tests = split(tests)
|
|
end
|
|
ENV2 = copy(ENV)
|
|
ENV2["JULIA_CPU_CORES"] = "$numcores"
|
|
try
|
|
run(setenv(`$(julia_cmd()) $(joinpath(JULIA_HOME,
|
|
Base.DATAROOTDIR, "julia", "test", "runtests.jl")) $tests`, ENV2))
|
|
catch
|
|
buf = PipeBuffer()
|
|
versioninfo(buf)
|
|
error("A test has failed. Please submit a bug report (https://github.com/JuliaLang/julia/issues)\n" *
|
|
"including error messages above and the output of versioninfo():\n$(readstring(buf))")
|
|
end
|
|
end
|
|
|
|
# testing
|
|
|
|
|
|
"""
|
|
whos(io::IO=STDOUT, m::Module=current_module(), pattern::Regex=r"")
|
|
|
|
Print information about exported global variables in a module, optionally restricted to those matching `pattern`.
|
|
|
|
The memory consumption estimate is an approximate lower bound on the size of the internal structure of the object.
|
|
"""
|
|
function whos(io::IO=STDOUT, m::Module=current_module(), pattern::Regex=r"")
|
|
maxline = displaysize(io)[2]
|
|
line = zeros(UInt8, maxline)
|
|
head = PipeBuffer(maxline + 1)
|
|
for v in sort!(names(m))
|
|
s = string(v)
|
|
if isdefined(m, v) && ismatch(pattern, s)
|
|
value = getfield(m, v)
|
|
@printf head "%30s " s
|
|
try
|
|
if value ∈ (Base, Main, Core)
|
|
print(head, " ")
|
|
else
|
|
bytes = summarysize(value)
|
|
if bytes < 10_000
|
|
@printf(head, "%6d bytes ", bytes)
|
|
else
|
|
@printf(head, "%6d KB ", bytes ÷ (1024))
|
|
end
|
|
end
|
|
print(head, summary(value))
|
|
catch e
|
|
print(head, "#=ERROR: unable to show value=#")
|
|
end
|
|
newline = search(head, UInt8('\n')) - 1
|
|
if newline < 0
|
|
newline = nb_available(head)
|
|
end
|
|
if newline > maxline
|
|
newline = maxline - 1 # make space for ...
|
|
end
|
|
line = resize!(line, newline)
|
|
line = read!(head, line)
|
|
|
|
write(io, line)
|
|
if nb_available(head) > 0 # more to read? replace with ...
|
|
print(io, '\u2026') # hdots
|
|
end
|
|
println(io)
|
|
seekend(head) # skip the rest of the text
|
|
end
|
|
end
|
|
end
|
|
whos(m::Module, pat::Regex=r"") = whos(STDOUT, m, pat)
|
|
whos(pat::Regex) = whos(STDOUT, current_module(), pat)
|