diff --git a/base/dict.jl b/base/dict.jl index 5a5ebb79c6ec8..ab5ef3785b1d5 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -763,15 +763,6 @@ length(t::WeakKeyDict) = length(t.ht) # TODO: # - take care of hash collisions when tidying up WeakRef(nothing) -const secret_table_token = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5902__ - -import Base: KeyIterator, ValueIterator, haskey, get, getkey, delete!, - pop!, empty!, filter!, setindex!, getindex, similar, - sizehint, length, filter, isempty, start, next, done, - keys, values, _tablesz, skip_deleted, serialize, deserialize, serialize_type, - convert - - type WeakObjectIdDict{K,V} <: Associative{K,V} slots::Array{Uint8,1} keys::Array{WeakRef,1} @@ -851,9 +842,15 @@ function deserialize{K,V}(s, T::Type{WeakObjectIdDict{K,V}}) return t end -# have two hashindex. For setindex: -hashindex(::WeakObjectIdDict, key::WeakRef, sz) = error("asdf") #(int(hash(object_id(key.value))) & (sz-1)) + 1 -# For getindex: +# use these functions to access the h.keys array. Does conversion to +# and from WeakRef. +gkey(h::WeakObjectIdDict, ind) = h.keys[ind].value + +skey!(h::WeakObjectIdDict, val, ind) = (h.keys[ind] = WeakRef(val)) +skey!(ar::Array{WeakRef,1}, val, ind) = (ar[ind] = WeakRef(val)) + +numslots(h::WeakObjectIdDict) = length(h.slots) + hashindex(::WeakObjectIdDict, key, sz) = (int(hash(object_id(key))) & (sz-1)) + 1 isslotempty(h::WeakObjectIdDict, i::Int) = h.slots[i] == 0x0 @@ -889,7 +886,7 @@ function rehash{K,V}(h::WeakObjectIdDict{K,V}, newsz) for i = 1:sz if olds[i] == 0x1 - k = getkey(h, i) + k = gkey(h, i) if k==wnothing #&& i!=indexnothing_old # TODO: do something about hash collisions! # a gc'ed immutable was here continue @@ -900,7 +897,7 @@ function rehash{K,V}(h::WeakObjectIdDict{K,V}, newsz) index = (index & (newsz-1)) + 1 end slots[index] = 0x1 - setkey!(keys, k, index) + skey!(keys, k, index) vals[index] = v count += 1 @@ -942,16 +939,6 @@ function empty!{K,V}(h::WeakObjectIdDict{K,V}) return h end -# use these functions to access the h.keys array. Does conversion to -# and from WeakRef. -getkey(h::WeakObjectIdDict, ind) = h.keys[ind].value -getkey(ar::Array{WeakRef,1}, val, ind) = ar[ind].value - -setkey!(h::WeakObjectIdDict, val, ind) = (h.keys[ind] = WeakRef(val)) -setkey!(ar::Array{WeakRef,1}, val, ind) = (ar[ind] = WeakRef(val)) - -numslots(h::WeakObjectIdDict) = length(h.slots) - # get the index where a key is stored, or -1 if not present function ht_keyindex{K,V}(h::WeakObjectIdDict{K,V}, key) sz = numslots(h) @@ -963,7 +950,7 @@ function ht_keyindex{K,V}(h::WeakObjectIdDict{K,V}, key) if isslotempty(h,index) break end - if !isslotmissing(h,index) && isequal(key, getkey(h, index)) + if !isslotmissing(h,index) && isequal(key, gkey(h, index)) return index end @@ -998,7 +985,7 @@ function ht_keyindex2{K,V}(h::WeakObjectIdDict{K,V}, key) # in case "key" already exists in a later collided slot. avail = -index end - elseif isequal(key, getkey(h, index)) + elseif isequal(key, gkey(h, index)) return index end @@ -1016,7 +1003,7 @@ end function _setindex!(h::WeakObjectIdDict, v, key, index) h.slots[index] = 0x1 - setkey!(h, key, index) + skey!(h, key, index) h.vals[index] = v h.count += 1 @@ -1030,7 +1017,7 @@ end function weak_key_delete!(t::WeakObjectIdDict, k) # when a weak key is finalized, remove from dictionary if it is still there - wk = getkey(t, k, secret_table_token) # getkey returns the WeakRef.value + wk = getkey(t, k, secret_table_token) # gkey returns the WeakRef.value if !is(wk,secret_table_token) && is(wk, k) delete!(t, k) end @@ -1050,7 +1037,7 @@ function setindex!{K,V}(h::WeakObjectIdDict{K,V}, v0, key0) index = ht_keyindex2(h, key0) if index > 0 - setkey!(h, key0, index) + skey!(h, key0, index) h.vals[index] = v else _setindex!(h, v, key0, -index) diff --git a/base/help.jl b/base/help.jl index 6cd57273424b8..9a0b6dce03c5d 100644 --- a/base/help.jl +++ b/base/help.jl @@ -6,57 +6,69 @@ import Base.WeakObjectIdDict # - switch off when not running interactively (but what about tests?) # - make @doc work for methods # - make @doc work for modules -# - make APROPOS_DICT = WeakObjectIdDict(), currently this make julia very unstable +# - make an APROPOS_DICT mapping keywords to objects + -typealias HelpDict Dict{Symbol,Any} -# keys -# :desc : help text # :mod : module which contains this object/concept/keyword (if applicable) +type HelpEntry + desc::String # description + mod::Union(Module,String,Nothing) # module of entry. This is useful for type instances + # for which figuring out the defining module is hard. + HelpEntry() = new("", nothing) + HelpEntry(desc) = new(desc, nothing) + HelpEntry(desc, mod) = new(desc, mod) +end # internal data -NOOBJ_DICT = Dict{String, HelpDict}() # To keep documentation for non-object entities, like keywords, ccall, &&, ||, ", ', etc. +NOOBJ_DICT = Dict{String, HelpEntry}() # To keep documentation for non-object entities, like keywords, ccall, &&, ||, ", ', etc. # And also for macros as they cannot be used as dict-keys directly. -APROPOS_DICT = ObjectIdDict() # this is used in apropos search. Maps objects to their help text [:desc]. -APROPOS_DICT[NOOBJ_DICT] = Dict{String, String}() # to hold the apropos for stuff in NOOBJ_DICT INIT_OLD_HELP = true # flag # low level functions ## function hasdoc(obj) - if hasmeta(obj, :doc) - true - elseif haskey(NOOBJ_DICT, obj) + if hasmeta(obj, :doc) || haskey(NOOBJ_DICT, obj) true else false end end +function getdoc(obj) + doc = getdoc(obj, nothing) + doc==nothing ? error("No documentation associated with $obj") : doc +end function getdoc(obj, default) - doc = getmeta(obj, :doc, nothing) - if doc!=nothing - doc[:doc] + if hasmeta(obj, :doc) + getmeta(obj, :doc) elseif haskey(NOOBJ_DICT, obj) NOOBJ_DICT[obj] else default end end -function getdoc(obj) - doc = getdoc(obj, nothing) - doc==nothing ? error("No documentation associated with $obj") : doc +function getdoc!(obj; string_into_meta=false) + # If string_into_meta==true then add the doc to the metadata of the + # string-object, otherwise is goes into the NOOBJ_DICT (default). + if hasdoc(obj) + return getdoc(obj) + else + he = HelpEntry() + setdoc!(obj, he; string_into_meta=string_into_meta) + return he + end end -function setdoc!(obj, doc::HelpDict; string_into_meta=false) + +function setdoc!(obj, doc::HelpEntry; string_into_meta=false) # If string_into_meta==true then add the doc to the metadata of the # string-object, otherwise is goes into the NOOBJ_DICT (default). if !isa(obj, String) || string_into_meta setmeta!(obj, :doc, doc) - APROPOS_DICT[obj] = doc[:desc] else NOOBJ_DICT[obj] = doc - APROPOS_DICT[NOOBJ_DICT][obj] = doc[:desc] end + nothing end # making docs from helpdb.jl @@ -82,8 +94,8 @@ function init_help() INIT_OLD_HELP = false println("Loading help data...") helpdb = evalfile(helpdb_filename()) - for hd in helpdb - mod_,obj,desc = hd + for he in helpdb + mod_,obj,desc = he # split objects up between what goes into META and what # goes into NOOBJ_DICT if obj[1]=='@' # a macro @@ -105,14 +117,9 @@ function init_help() catch mod_ = mod_ # keep as string end - if !hasdoc(obj) # do not overwrite existing doc - hdnew = HelpDict() - hdnew[:desc]= desc; hdnew[:mod]=mod_; - setdoc!(obj, hdnew) - else # append to it - hdnew = getdoc(obj) - hdnew[:desc] *= "\n$(string(mod_)).$desc" - end + henew = getdoc!(obj) + henew.desc *= desc + henew.mod = mod_ end end end @@ -121,10 +128,7 @@ end ## function doc(obj, docstr::String; mod=nothing, string_into_meta=false) # the module cannot be set automatically with this function, use the macro instead - hd = HelpDict() - hd[:desc] = docstr - hd[:mod] = mod - setdoc!(obj, hd; string_into_meta=string_into_meta) + setdoc!(obj, HelpEntry(docstr, mod); string_into_meta=string_into_meta) end macro doc(args...) @@ -134,8 +138,6 @@ macro doc(args...) """ if length(args)==2 docstr, obj = args - elseif length(args)==3 - docstr, obj = args else error(errmsg) end @@ -223,9 +225,9 @@ end function help(io::IO, obj) init_help() - docdict = getdoc(obj, nothing) - if docdict!=nothing - out = _decor_help_desc(obj, docdict[:mod], docdict[:desc]) + if hasdoc(obj) + he = getdoc(obj) + out = _decor_help_desc(obj, he.mod, he.desc) print(io, out) else if isa(obj, DataType) # try to print something generic: @@ -254,29 +256,30 @@ end help(args...) = help(STDOUT, args...) -apropos(io::IO, s::String) = apropos(io, s) +apropos(s::String) = apropos(STDOUT, s) function apropos(io::IO, txt::String) - # This could be done better. init_help() n = 0 r = Regex("\\Q$txt", Base.PCRE.CASELESS) - for (obj, desc) in APROPOS_DICT - if isequal(obj,NOOBJ_DICT) - for (objj, desc) in obj - if ismatch(r, string(obj)) || ismatch(r, desc) - println(io, "Object: \"$objj\"") - n = 1 - end - end - else + println(io, "The string '$txt' appears in the documentation of following objects:") + for (obj, cont) in Base._META + if hasdoc(obj) + desc = getdoc(obj).desc if ismatch(r, string(obj)) || ismatch(r, desc) - println(io, "Object: \"$obj\"") - n = 1 + println(io, string(obj)) end end + n+=1 + end + for (obj, cont) in NOOBJ_DICT + desc = cont.desc + if ismatch(r, obj) || ismatch(r, desc) + println(io, obj) + end + n+=1 end if n == 0 - println(io, "No help information found.") + println(io, "Non occurrence found.") end end diff --git a/base/metadata.jl b/base/metadata.jl index 4cfe612324014..a141d53418a08 100644 --- a/base/metadata.jl +++ b/base/metadata.jl @@ -11,42 +11,46 @@ # Each metadata entry is just a dict Any=>Any: typealias MetaData Dict{Any,Any} -# The Dict for _META: ideally this would be a typed, weak-key -# ObjectIdDict. --> make one... - +# The Dict for META: +typealias MetaDict WeakObjectIdDict{Any, MetaData} #typealias MetaDict WeakKeyDict{Any, MetaData} # Problem: does not work for immutables #typealias MetaDict Dict{Any, MetaData} # Problem: if a mutable changes the hash changes -#typealias MetaDict ObjectIdDict -typealias MetaDict WeakObjectIdDict{Any, MetaData} +#typealias MetaDict ObjectIdDict # Problem: does not free references + +META = MetaDict() # this holds all the metadata -const secret_default = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5903__ +## metadata interface: +# (If defining type which also holds metadata, implement the "#*" +# methods for it) -_META = MetaDict() # this holds all the metadata +hasmeta(obj) = haskey(META, obj) #* +hasmeta(obj, key) = hasmeta(obj) && haskey(getmeta(obj), key) -getmeta(obj) = _META[obj] -getmeta(obj, key) = _META[obj][key] +getmeta(obj) = META[obj] #* +getmeta(obj, key) = getmeta(obj)[key] function getmeta(obj, key, default) - out = get(_META, obj, secret_default) - out==secret_default ? default : out + if hasmeta(obj) + return get!(getmeta(obj), key, default) + else + default + end end -getmeta!(obj) = get!(_META, obj, MetaData()) +getmeta!(obj) = get!(META, obj, MetaData()) #* function getmeta!(obj, key, default) md = getmeta!(obj) return get!(md, key, default) end -setmeta!(obj, md::MetaData) = ( _META[obj] = md ) +setmeta!(obj, md::MetaData) = ( META[obj] = md ) #* function setmeta!(obj, key, value) md = getmeta!(obj) md[key] = value end -hasmeta(obj) = haskey(_META, obj) -hasmeta(obj, key) = hasmeta(obj) && haskey(getmeta(obj), key) - -# note: this only clears the central Base._META dict, meta-storage in +# This only clears the central Base.META dict, meta-storage in # used-defined types will not be affected: -emptymeta!() = (global _META = MetaDict(); nothing) -deletemeta!(obj) = (delete!(_META, obj); nothing) -deletemeta!(obj, key) = (delete!(_META[obj], key); nothing) +emptymeta!() = (global META = MetaDict(); nothing) + +deletemeta!(obj) = (delete!(META, obj); nothing) #* +deletemeta!(obj, key) = (delete!(getmeta(obj), key); nothing) diff --git a/test/help.jl b/test/help.jl index e546bee49343b..79a9273cdf0a4 100644 --- a/test/help.jl +++ b/test/help.jl @@ -2,32 +2,82 @@ getdoc = Base.Help.getdoc setdoc! = Base.Help.setdoc! hasdoc = Base.Help.hasdoc -function makeHelpDict() - hd = Base.Help.HelpDict() - hd[:desc] = "" - hd[:mod] = nothing - return hd +## Old Help: +# Tests that old-help from helpdb.jl is correct. These tests are +# quite fragile/temperamental, I'm not sure why. Running in parallel +# may be one of the problems? + +# initialise old-help +Base.Help.init_help() +helpdb = evalfile(Base.Help.helpdb_filename()) + +# For these the help is broken, sometimes. +broken_help = {"eval", "MS_SYNC", "CPU_CORES", "BoundingBox", "isinside", "ENDIAN_BOM", "C_NULL", + "im" ,"Inf16" ,"NaN16" ,"NaN" ,"NaN32" ,"ccall" ,"Inf32" ,"Inf", "cglobal"} +# - eval does not work with help.jl machinery +# - MS_SYNC, CPU_CORES are both Int64 +# - "BoundingBox", "isinside" don't exist anymore +# - ENDIAN_BOM + +mod_,obj,desc = 1,2,3 +missingdoc = 0 +for hd in helpdb + mod_,obj,desc = hd + if obj in broken_help + continue + end + str = "" + if obj[1]=='@' # a macro + obj = obj # keep as string + str = obj + else + try + obj = eval(parse(obj)) + str = string(obj) + catch + try + obj = eval(parse(mod_ * "." * obj)) + str = mod_ * "." * string(obj) + catch + obj = obj # keep as string + end + end + end + # @show obj, typeof(obj), desc + # @test hasdoc(obj) + # @test contains(getdoc(obj).desc,desc) + if hasdoc(obj) + @test contains(getdoc(obj).desc,desc) + else + missingdoc += 1 + end +end +if missingdoc>0 + println("Docs missing from $missingdoc objects") end -## test low-level functions getdoc, setdoc!, hasdoc + +## New-style doc + +## test low-level functions getdoc, getdoc!, setdoc!, hasdoc a = [1,2] @test !hasdoc(a) @test_throws ErrorException getdoc(a) @test getdoc(a,nothing)==nothing -hd = makeHelpDict() -setdoc!(a, hd) +he = Base.Help.HelpEntry() +setdoc!(a, he) @test hasdoc(a) -@test isequal(getdoc(a),hd) -@test isequal(getdoc(a,nothing), hd) +@test isequal(getdoc(a),he) +@test isequal(getdoc(a,nothing), he) # strings are special st1 = randstring(5) st2 = randstring(5) -setdoc!(st1, hd) -setdoc!(st2, hd, string_into_meta=true) +setdoc!(st1, he) +setdoc!(st2, he, string_into_meta=true) for st in [st1,st2] @test hasdoc(st) - @test isequal(getdoc(a),hd) - @test isequal(getdoc(a,nothing), hd) + @test isequal(getdoc(a),he) + @test isequal(getdoc(a,nothing), he) end @test !hasmeta(st1) @test hasmeta(st2) @@ -36,34 +86,34 @@ end b = Dict() st = randstring(5); st2 = randstring(5) doc(b, st) -@test getdoc(b)[:desc]==st -@test getdoc(b)[:mod]==nothing +@test getdoc(b).desc==st +@test getdoc(b).mod==nothing @test hasmeta(b) doc(b, st; mod=Base) -@test getdoc(b)[:desc]==st -@test getdoc(b)[:mod]==Base +@test getdoc(b).desc==st +@test getdoc(b).mod==Base @test hasmeta(b) ## test @doc-macro st = randstring(5) @doc st type MyType9798 end -@test getdoc(MyType9798)[:desc]==st +@test getdoc(MyType9798).desc==st st = randstring(5) @doc st abstract MyA -@test getdoc(MyA)[:desc]==st +@test getdoc(MyA).desc==st st = randstring(5) @doc st ffff(x) = 5x -@test getdoc(ffff)[:desc]==st +@test getdoc(ffff).desc==st st = randstring(5) @doc st function gggg(x) 5x end -@test getdoc(gggg)[:desc]==st +@test getdoc(gggg).desc==st st = randstring(5) @doc st macro MyM(ex) end -@test getdoc("@MyM")[:desc]==st +@test getdoc("@MyM").desc==st # This equivalent one-liner gives an error for me: # @doc st macro MyM(ex) end @@ -76,62 +126,3 @@ end # @test_throws @doc st module MyMod end -## Old Help: -# Tests that old-help from helpdb.jl is correct. These tests are -# quite fragile/temperamental, I'm not sure why. Running in parallel -# may be one of the problems? - -# initialise old-help -Base.Help.init_help() -helpdb = evalfile(Base.Help.helpdb_filename()) - -# For these the help is broken, sometimes. -broken_help = {eval, MS_SYNC, CPU_CORES, "BoundingBox", "isinside", ENDIAN_BOM, C_NULL} -# sometimes_brocken = {im, Inf16, Inf32, Inf} -# append!(broken_help, sometimes_brocken) -# - eval does not work with help.jl machinery -# - MS_SYNC, CPU_CORES are both Int64 -# - "BoundingBox", "isinside" don't exist anymore -# - ENDIAN_BOM - -mod_,obj,desc = 1,2,3 -for hd in helpdb - @show hd - mod_,obj,desc = hd - if obj=="im" || obj=="Inf16" || obj==="NaN16" || obj==="NaN" || obj==="NaN32" || obj=="ccall" - continue - # otherwise I get this error?! -# LLVM ERROR: Cannot select: 0x2f42fa0: i64 = bitcast 0x9425f40 [ORD=88214] [ID=9] dbg:operators.jl:9:1 -# 0x9425f40: i8 = truncate 0x2f4d780 [ORD=88214] [ID=8] -# 0x2f4d780: i32,ch = CopyFromReg 0x15779e8, 0x9321120 [ORD=88214] [ID=7] -# 0x9321120: i32 = Register %vreg1 [ORD=88214] [ID=2] -# In function: julia_==;19624 - end - - os = obj - @show obj, typeof(obj), desc - str = "" - if obj[1]=='@' # a macro - obj = obj # keep as string - str = obj - else - try - obj = eval(parse(obj)) - str = string(obj) - catch - try - obj = eval(parse(mod_ * "." * obj)) - str = mod_ * "." * string(obj) - catch - obj = obj # keep as string - end - end - end - @show obj, typeof(obj), desc - if !(obj in broken_help) -# @show obj, desc - @test hasdoc(obj) - @test contains(getdoc(obj)[:desc],desc) - end -end - diff --git a/test/metadata.jl b/test/metadata.jl index 1678bd970d5c9..78481ea729f54 100644 --- a/test/metadata.jl +++ b/test/metadata.jl @@ -25,21 +25,11 @@ type MyType6498 MyType6498(a) = new(a, MetaData()) end hasmeta(obj::MyType6498) = !isempty(obj._metadata) -hasmeta(obj::MyType6498, key) = hasmeta(obj) && haskey(obj._metadata, key) - getmeta(obj::MyType6498) = hasmeta(obj) ? obj._metadata : throw(KeyError(obj)) -getmeta(obj::MyType6498, key) = haskey(obj._metadata, key) ? obj._metadata[key] : throw(KeyError(key)) -getmeta(obj::MyType6498, key, default) = haskey(obj._metadata, key) ? obj._metadata[key] : default - getmeta!(obj::MyType6498) = obj._metadata -getmeta!(obj::MyType6498, key, default) = haskey(obj._metadata, key) ? obj._metadata[key] : (obj._metadata[key] = default) - -setmeta!(obj::MyType6498, md::MetaData) = obj._metadata = md -setmeta!(obj::MyType6498, key, value) = (obj._metadata[key]=value) +setmeta!(obj::MyType6498, md::MetaData) = obj._metadata = md deletemeta!(obj::MyType6498) = (obj._metadata = MetaData(); nothing) -deletemeta!(obj::MyType6498, key) = (delete!(obj._metadata, key); nothing) - # other objects fn12(x) = x+1 @@ -147,7 +137,7 @@ setmeta!(numb, md) # this works @test isequal(getmeta(5),md) -# These tests only pass for isa(META, ObjectIdDict): +# These tests only pass for isa(META, (Weak)ObjectIdDict): @test_throws KeyError getmeta("asdf") di = {"a"=>5} setmeta!(di, md) diff --git a/test/runtests.jl b/test/runtests.jl index 56fe2ab744423..cdd17e12a9c9d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,7 +1,7 @@ -# linalg tests take the longest - start them off first -# help test only works reliably if it is near the front of the list! +# - linalg tests take the longest - start them off first +# - metadata needs to be finished before help-test runs testnames = [ - "linalg", "core", "help", "keywordargs", "numbers", "strings", + "linalg", "core", "keywordargs", "numbers", "strings", "metadata", "collections", "hashing", "remote", "iobuffer", "arrayops", "reduce", "reducedim", "simdloop", "blas", "fft", "dsp", "sparse", "bitarray", "random", "math", "functional", "bigint", "sorting", "statistics", "spawn", @@ -9,7 +9,7 @@ testnames = [ "resolve", "pollfd", "mpfr", "broadcast", "complex", "socket", "floatapprox", "readdlm", "regex", "float16", "combinatorics", "sysinfo", "rounding", "ranges", "mod2pi", "euler", "show", - "lineedit", "replcompletions", "repl", "test", "metadata" + "lineedit", "replcompletions", "repl", "test", "help" ] @unix_only push!(testnames, "unicode") @@ -35,7 +35,7 @@ end n = 1 if net_on - n = 1 #min(8, CPU_CORES, length(tests)) + n = min(8, CPU_CORES, length(tests)) n > 1 && addprocs(n) blas_set_num_threads(1) else