Skip to content

Commit caf7f89

Browse files
tkfstevengj
authored andcommitted
WIP: Use property (#420)
* Use property * Define property methods for wrapper objects * updated requirements to PyPlot 1.90, dropped Compat since we now require julia 0.7, fixed getproperty in plot3d * eliminate unnecessary PyObject(f) calls * rm more obsolete compatibility code * rm 0.6 testing * need newer PyCall to import Base.hasproperty in Julia 1.2
1 parent f8f8681 commit caf7f89

File tree

9 files changed

+132
-138
lines changed

9 files changed

+132
-138
lines changed

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ language: julia
22
os:
33
- linux
44
julia:
5-
- 0.6
65
- 0.7
76
- 1.0
87
- nightly

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ version number is returned by `PyPlot.version`.
107107

108108
Only the currently documented `matplotlib.pyplot` API is exported. To use
109109
other functions in the module, you can also call `matplotlib.pyplot.foo(...)`
110-
as `plt[:foo](...)`. For example, `plt[:plot](x, y)` also works. (And
110+
as `plt.foo(...)`. For example, `plt.plot(x, y)` also works. (And
111111
the raw `PyObject` for the `matplotlib` modules is also accessible
112112
as `PyPlot.matplotlib`.)
113113

REQUIRE

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
julia 0.6
2-
PyCall 1.6.2
1+
julia 0.7
2+
PyCall 1.90.0
33
Colors
44
LaTeXStrings
5-
Compat 0.65.0
65
VersionParsing

appveyor.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
environment:
22
matrix:
3-
- julia_version: 0.6
4-
PYTHON: "Conda"
53
- julia_version: 0.7
64
PYTHON: "Conda"
75
- julia_version: 1

src/PyPlot.jl

Lines changed: 57 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,18 @@ module PyPlot
44

55
using PyCall
66
import PyCall: PyObject, pygui, pycall, pyexists
7+
import PyCall: hasproperty # Base.hasproperty in Julia 1.2
78
import Base: convert, ==, isequal, hash, getindex, setindex!, haskey, keys, show
9+
using Base: @deprecate
810
export Figure, plt, matplotlib, pygui, withfig
911

10-
using Compat
11-
import Base.show
12-
1312
###########################################################################
1413
# Julia 0.4 help system: define a documentation object
1514
# that lazily looks up help from a PyObject via zero or more keys.
1615
# This saves us time when loading PyPlot, since we don't have
1716
# to load up all of the documentation strings right away.
1817
struct LazyHelp
19-
o # a PyObject or similar object supporting getindex with a __doc__ key
18+
o # a PyObject or similar object supporting getindex with a __doc__ property
2019
keys::Tuple{Vararg{String}}
2120
LazyHelp(o) = new(o, ())
2221
LazyHelp(o, k::AbstractString) = new(o, (k,))
@@ -28,8 +27,8 @@ function show(io::IO, ::MIME"text/plain", h::LazyHelp)
2827
for k in h.keys
2928
o = o[k]
3029
end
31-
if haskey(o, "__doc__")
32-
print(io, convert(AbstractString, o["__doc__"]))
30+
if hasproperty(o, "__doc__")
31+
print(io, convert(AbstractString, o."__doc__"))
3332
else
3433
print(io, "no Python docstring found for ", h.k)
3534
end
@@ -53,46 +52,46 @@ include("init.jl")
5352
mutable struct Figure
5453
o::PyObject
5554
end
56-
PyObject(f::Figure) = f.o
55+
PyObject(f::Figure) = getfield(f, :o)
5756
convert(::Type{Figure}, o::PyObject) = Figure(o)
58-
==(f::Figure, g::Figure) = f.o == g.o
59-
==(f::Figure, g::PyObject) = f.o == g
60-
==(f::PyObject, g::Figure) = f == g.o
61-
hash(f::Figure) = hash(f.o)
62-
pycall(f::Figure, args...; kws...) = pycall(f.o, args...; kws...)
63-
(f::Figure)(args...; kws...) = pycall(f.o, PyAny, args...; kws...)
64-
Base.Docs.doc(f::Figure) = Base.Docs.doc(f.o)
65-
66-
getindex(f::Figure, x) = getindex(f.o, x)
67-
setindex!(f::Figure, v, x) = setindex!(f.o, v, x)
68-
haskey(f::Figure, x) = haskey(f.o, x)
69-
keys(f::Figure) = keys(f.o)
57+
==(f::Figure, g::Figure) = PyObject(f) == PyObject(g)
58+
==(f::Figure, g::PyObject) = PyObject(f) == g
59+
==(f::PyObject, g::Figure) = f == PyObject(g)
60+
hash(f::Figure) = hash(PyObject(f))
61+
pycall(f::Figure, args...; kws...) = pycall(PyObject(f), args...; kws...)
62+
(f::Figure)(args...; kws...) = pycall(PyObject(f), PyAny, args...; kws...)
63+
Base.Docs.doc(f::Figure) = Base.Docs.doc(PyObject(f))
64+
65+
# Note: using `Union{Symbol,String}` produces ambiguity.
66+
Base.getproperty(f::Figure, s::Symbol) = getproperty(PyObject(f), s)
67+
Base.getproperty(f::Figure, s::AbstractString) = getproperty(PyObject(f), s)
68+
Base.setproperty!(f::Figure, s::Symbol, x) = setproperty!(PyObject(f), s, x)
69+
Base.setproperty!(f::Figure, s::AbstractString, x) = setproperty!(PyObject(f), s, x)
70+
hasproperty(f::Figure, s::Symbol) = hasproperty(PyObject(f), s)
71+
Base.propertynames(f::Figure) = propertynames(PyObject(f))
72+
haskey(f::Figure, x) = haskey(PyObject(f), x)
73+
74+
@deprecate getindex(f::Figure, x) getproperty(f, x)
75+
@deprecate setindex!(f::Figure, v, x) setproperty!(f, v, x)
76+
@deprecate keys(f::Figure) propertynames(f)
7077

7178
for (mime,fmt) in aggformats
7279
@eval function show(io::IO, m::MIME{Symbol($mime)}, f::Figure)
73-
if !haskey(pycall(f.o["canvas"]["get_supported_filetypes"], PyDict),
80+
if !haskey(pycall(f."canvas"."get_supported_filetypes", PyDict),
7481
$fmt)
7582
throw(MethodError(show, (io, m, f)))
7683
end
77-
f.o["canvas"]["print_figure"](io, format=$fmt, bbox_inches="tight")
84+
f."canvas"."print_figure"(io, format=$fmt, bbox_inches="tight")
7885
end
7986
if fmt != "svg"
80-
if isdefined(Base, :showable)
81-
@eval Base.showable(::MIME{Symbol($mime)}, f::Figure) = !isempty(f) && haskey(pycall(f.o["canvas"]["get_supported_filetypes"], PyDict), $fmt)
82-
else
83-
@eval Base.mimewritable(::MIME{Symbol($mime)}, f::Figure) = !isempty(f) && haskey(pycall(f.o["canvas"]["get_supported_filetypes"], PyDict), $fmt)
84-
end
87+
@eval Base.showable(::MIME{Symbol($mime)}, f::Figure) = !isempty(f) && haskey(pycall(f."canvas"."get_supported_filetypes", PyDict), $fmt)
8588
end
8689
end
8790

8891
# disable SVG output by default, since displaying large SVGs (large datasets)
8992
# in IJulia is slow, and browser SVG display is buggy. (Similar to IPython.)
9093
const SVG = [false]
91-
if isdefined(Base, :showable)
92-
Base.showable(::MIME"image/svg+xml", f::Figure) = SVG[1] && !isempty(f) && haskey(pycall(f.o["canvas"]["get_supported_filetypes"], PyDict), "svg")
93-
else
94-
Base.mimewritable(::MIME"image/svg+xml", f::Figure) = SVG[1] && !isempty(f) && haskey(pycall(f.o["canvas"]["get_supported_filetypes"], PyDict), "svg")
95-
end
94+
Base.showable(::MIME"image/svg+xml", f::Figure) = SVG[1] && !isempty(f) && haskey(pycall(f."canvas"."get_supported_filetypes", PyDict), "svg")
9695
svg() = SVG[1]
9796
svg(b::Bool) = (SVG[1] = b)
9897

@@ -103,7 +102,7 @@ svg(b::Bool) = (SVG[1] = b)
103102
# since the user is keeping track of these in some other way,
104103
# e.g. for interactive widgets.
105104

106-
Base.isempty(f::Figure) = isempty(pycall(f["get_axes"], PyVector))
105+
Base.isempty(f::Figure) = isempty(pycall(f."get_axes", PyVector))
107106

108107
# We keep a set of figure numbers for the figures used in withfig, because
109108
# for these figures we don't want to auto-display or auto-close them
@@ -114,23 +113,23 @@ const withfig_fignums = Set{Int}()
114113

115114
function display_figs() # called after IJulia cell executes
116115
if isjulia_display[1]
117-
for manager in Gcf["get_all_fig_managers"]()
118-
f = manager["canvas"]["figure"]
119-
if f[:number] withfig_fignums
116+
for manager in Gcf."get_all_fig_managers"()
117+
f = manager."canvas"."figure"
118+
if f.number withfig_fignums
120119
fig = Figure(f)
121120
isempty(fig) || display(fig)
122-
pycall(plt["close"], PyAny, f)
121+
pycall(plt."close", PyAny, f)
123122
end
124123
end
125124
end
126125
end
127126

128127
function close_figs() # called after error in IJulia cell
129128
if isjulia_display[1]
130-
for manager in Gcf["get_all_fig_managers"]()
131-
f = manager["canvas"]["figure"]
132-
if f[:number] withfig_fignums
133-
pycall(plt["close"], PyAny, f)
129+
for manager in Gcf."get_all_fig_managers"()
130+
f = manager."canvas"."figure"
131+
if f.number withfig_fignums
132+
pycall(plt."close", PyAny, f)
134133
end
135134
end
136135
end
@@ -166,39 +165,39 @@ export acorr,annotate,arrow,autoscale,autumn,axhline,axhspan,axis,axvline,axvspa
166165
# overlap with standard Julia functions:
167166
# close, connect, fill, hist, xcorr
168167
import Base: close, fill, step
169-
import Compat.Sockets: connect
168+
import Sockets: connect
170169

171170
const plt_funcs = (:acorr,:annotate,:arrow,:autoscale,:autumn,:axes,:axhline,:axhspan,:axis,:axvline,:axvspan,:bar,:barbs,:barh,:bone,:box,:boxplot,:broken_barh,:cla,:clabel,:clf,:clim,:cohere,:colorbar,:colors,:contour,:contourf,:cool,:copper,:csd,:delaxes,:disconnect,:draw,:errorbar,:eventplot,:figaspect,:figimage,:figlegend,:figtext,:fill_between,:fill_betweenx,:findobj,:flag,:gca,:gci,:get_current_fig_manager,:get_figlabels,:get_fignums,:get_plot_commands,:ginput,:gray,:grid,:hexbin,:hlines,:hold,:hot,:hsv,:imread,:imsave,:imshow,:ioff,:ion,:ishold,:jet,:legend,:locator_params,:loglog,:margins,:matshow,:minorticks_off,:minorticks_on,:over,:pause,:pcolor,:pcolormesh,:pie,:pink,:plot,:plot_date,:plotfile,:polar,:prism,:psd,:quiver,:quiverkey,:rc,:rc_context,:rcdefaults,:rgrids,:savefig,:sca,:scatter,:sci,:semilogx,:semilogy,:set_cmap,:setp,:specgram,:spectral,:spring,:spy,:stackplot,:stem,:streamplot,:subplot,:subplot2grid,:subplot_tool,:subplots,:subplots_adjust,:summer,:suptitle,:table,:text,:thetagrids,:tick_params,:ticklabel_format,:tight_layout,:title,:tricontour,:tricontourf,:tripcolor,:triplot,:twinx,:twiny,:vlines,:waitforbuttonpress,:winter,:xkcd,:xlabel,:xlim,:xscale,:xticks,:ylabel,:ylim,:yscale,:yticks,:hist,:xcorr,:isinteractive)
172171

173172
for f in plt_funcs
174173
sf = string(f)
175174
@eval @doc LazyHelp(plt,$sf) function $f(args...; kws...)
176-
if !haskey(plt, $sf)
175+
if !hasproperty(plt, $sf)
177176
error("matplotlib ", version, " does not have pyplot.", $sf)
178177
end
179-
return pycall(plt[$sf], PyAny, args...; kws...)
178+
return pycall(plt.$sf, PyAny, args...; kws...)
180179
end
181180
end
182181

183-
@doc LazyHelp(plt,"step") step(x, y; kws...) = pycall(plt["step"], PyAny, x, y; kws...)
182+
@doc LazyHelp(plt,"step") step(x, y; kws...) = pycall(plt."step", PyAny, x, y; kws...)
184183

185-
Base.show(; kws...) = begin pycall(plt["show"], PyObject; kws...); nothing; end
184+
Base.show(; kws...) = begin pycall(plt."show", PyObject; kws...); nothing; end
186185

187-
close(f::Figure) = close(f[:number])
186+
close(f::Figure) = close(f.number)
188187
function close(f::Integer)
189188
pop!(withfig_fignums, f, f)
190-
pycall(plt["close"], PyAny, f)
189+
pycall(plt."close", PyAny, f)
191190
end
192-
close(f::Union{AbstractString,Symbol}) = pycall(plt["close"], PyAny, f)
193-
@doc LazyHelp(plt,"close") close() = pycall(plt["close"], PyAny)
191+
close(f::Union{AbstractString,Symbol}) = pycall(plt."close", PyAny, f)
192+
@doc LazyHelp(plt,"close") close() = pycall(plt."close", PyAny)
194193

195-
@doc LazyHelp(plt,"connect") connect(s::Union{AbstractString,Symbol}, f::Function) = pycall(plt["connect"], PyAny, s, f)
194+
@doc LazyHelp(plt,"connect") connect(s::Union{AbstractString,Symbol}, f::Function) = pycall(plt."connect", PyAny, s, f)
196195

197196
@doc LazyHelp(plt,"fill") fill(x::AbstractArray,y::AbstractArray, args...; kws...) =
198-
pycall(plt["fill"], PyAny, x, y, args...; kws...)
197+
pycall(plt."fill", PyAny, x, y, args...; kws...)
199198

200199
# consistent capitalization with mplot3d, avoid conflict with Base.hist2d
201-
@doc LazyHelp(plt,"hist2d") hist2D(args...; kws...) = pycall(plt["hist2d"], PyAny, args...; kws...)
200+
@doc LazyHelp(plt,"hist2d") hist2D(args...; kws...) = pycall(plt."hist2d", PyAny, args...; kws...)
202201

203202
include("colormaps.jl")
204203

@@ -212,9 +211,9 @@ function bar(x::AbstractVector{T}, y; kws...) where T<:AbstractString
212211
end
213212
p = bar(xi, y; kws...)
214213
ax = any(kw -> kw[1] == :orientation && lowercase(kw[2]) == "horizontal",
215-
kws) ? gca()["yaxis"] : gca()["xaxis"]
216-
ax["set_ticks"](xi)
217-
ax["set_ticklabels"](x)
214+
kws) ? gca()."yaxis" : gca()."xaxis"
215+
ax."set_ticks"(xi)
216+
ax."set_ticklabels"(x)
218217
return p
219218
end
220219

@@ -274,9 +273,9 @@ end
274273

275274
function withfig(actions::Function, f::Figure; clear=true)
276275
ax_save = gca()
277-
push!(withfig_fignums, f[:number])
278-
figure(f[:number])
279-
@compat finalizer(close, f)
276+
push!(withfig_fignums, f.number)
277+
figure(f.number)
278+
finalizer(close, f)
280279
try
281280
if clear && !isempty(f)
282281
clf()

src/colormaps.jl

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,31 @@ mutable struct ColorMap
1111
o::PyObject
1212
end
1313

14-
PyObject(c::ColorMap) = c.o
14+
PyObject(c::ColorMap) = getfield(c, :o)
1515
convert(::Type{ColorMap}, o::PyObject) = ColorMap(o)
16-
==(c::ColorMap, g::ColorMap) = c.o == g.o
17-
==(c::PyObject, g::ColorMap) = c == g.o
18-
==(c::ColorMap, g::PyObject) = c.o == g
19-
hash(c::ColorMap) = hash(c.o)
20-
pycall(c::ColorMap, args...; kws...) = pycall(c.o, args...; kws...)
21-
(c::ColorMap)(args...; kws...) = pycall(c.o, PyAny, args...; kws...)
22-
Base.Docs.doc(c::ColorMap) = Base.Docs.doc(c.o)
23-
24-
getindex(c::ColorMap, x) = getindex(c.o, x)
25-
setindex!(c::ColorMap, v, x) = setindex!(c.o, v, x)
26-
haskey(c::ColorMap, x) = haskey(c.o, x)
27-
keys(c::ColorMap) = keys(c.o)
16+
==(c::ColorMap, g::ColorMap) = PyObject(c) == PyObject(g)
17+
==(c::PyObject, g::ColorMap) = c == PyObject(g)
18+
==(c::ColorMap, g::PyObject) = PyObject(c) == g
19+
hash(c::ColorMap) = hash(PyObject(c))
20+
pycall(c::ColorMap, args...; kws...) = pycall(PyObject(c), args...; kws...)
21+
(c::ColorMap)(args...; kws...) = pycall(PyObject(c), PyAny, args...; kws...)
22+
Base.Docs.doc(c::ColorMap) = Base.Docs.doc(PyObject(c))
23+
24+
# Note: using `Union{Symbol,String}` produces ambiguity.
25+
Base.getproperty(c::ColorMap, s::Symbol) = getproperty(PyObject(c), s)
26+
Base.getproperty(c::ColorMap, s::AbstractString) = getproperty(PyObject(c), s)
27+
Base.setproperty!(c::ColorMap, s::Symbol, x) = setproperty!(PyObject(c), s, x)
28+
Base.setproperty!(c::ColorMap, s::AbstractString, x) = setproperty!(PyObject(c), s, x)
29+
Base.propertynames(c::ColorMap) = propertynames(PyObject(c))
30+
hasproperty(c::ColorMap, s::Union{Symbol,AbstractString}) = hasproperty(PyObject(c), s)
31+
haskey(c::ColorMap, x) = haskey(PyObject(c), x)
32+
33+
@deprecate getindex(c::ColorMap, x) getproperty(c, x)
34+
@deprecate setindex!(c::ColorMap, s, x) setproperty!(c, s, x)
35+
@deprecate keys(c::ColorMap) propertynames(c)
2836

2937
function show(io::IO, c::ColorMap)
30-
print(io, "ColorMap \"$(c[:name])\"")
38+
print(io, "ColorMap \"$(c.name)\"")
3139
end
3240

3341
# all Python dependencies must be initialized at runtime (not when precompiled)
@@ -42,15 +50,15 @@ function init_colormaps()
4250
copy!(colorsm, pyimport("matplotlib.colors"))
4351
copy!(cm, pyimport("matplotlib.cm"))
4452

45-
pytype_mapping(colorsm["Colormap"], ColorMap)
53+
pytype_mapping(colorsm."Colormap", ColorMap)
4654

47-
copy!(LinearSegmentedColormap, colorsm["LinearSegmentedColormap"])
55+
copy!(LinearSegmentedColormap, colorsm."LinearSegmentedColormap")
4856

49-
copy!(cm_get_cmap, cm["get_cmap"])
50-
copy!(cm_register_cmap, cm["register_cmap"])
57+
copy!(cm_get_cmap, cm."get_cmap")
58+
copy!(cm_register_cmap, cm."register_cmap")
5159

52-
copy!(ScalarMappable, cm["ScalarMappable"])
53-
copy!(Normalize01, pycall(colorsm["Normalize"],PyAny,vmin=0,vmax=1))
60+
copy!(ScalarMappable, cm."ScalarMappable")
61+
copy!(Normalize01, pycall(colorsm."Normalize",PyAny,vmin=0,vmax=1))
5462
end
5563

5664
########################################################################
@@ -149,7 +157,7 @@ register_cmap(n::Union{AbstractString,Symbol}, c::ColorMap) = pycall(cm_register
149157
get_cmaps() =
150158
ColorMap[get_cmap(c) for c in
151159
sort(filter!(c -> !endswith(c, "_r"),
152-
AbstractString[c for (c,v) in PyDict(PyPlot.cm["datad"])]),
160+
AbstractString[c for (c,v) in PyDict(PyPlot.cm."datad")]),
153161
by=lowercase)]
154162

155163
########################################################################
@@ -158,8 +166,8 @@ get_cmaps() =
158166
function show(io::IO, ::MIME"image/svg+xml", cs::AbstractVector{ColorMap})
159167
n = 256
160168
nc = length(cs)
161-
a = Compat.range(0; stop=1, length=n)
162-
namelen = mapreduce(c -> length(c[:name]), max, cs)
169+
a = range(0; stop=1, length=n)
170+
namelen = mapreduce(c -> length(c.name), max, cs)
163171
width = 0.5
164172
height = 5
165173
pad = 0.5
@@ -175,9 +183,9 @@ function show(io::IO, ::MIME"image/svg+xml", cs::AbstractVector{ColorMap})
175183
for j = 1:nc
176184
c = cs[j]
177185
y = (j-1) * (height+pad)
178-
write(io, """<text x="$(n*width+1)mm" y="$(y+3.8)mm" font-size="3mm">$(c[:name])</text>""")
186+
write(io, """<text x="$(n*width+1)mm" y="$(y+3.8)mm" font-size="3mm">$(c.name)</text>""")
179187
rgba = pycall(pycall(ScalarMappable, PyObject, cmap=c,
180-
norm=Normalize01)["to_rgba"], PyArray, a)
188+
norm=Normalize01)."to_rgba", PyArray, a)
181189
for i = 1:n
182190
write(io, """<rect x="$((i-1)*width)mm" y="$(y)mm" width="$(width)mm" height="$(height)mm" fill="#$(hex(RGB(rgba[i,1],rgba[i,2],rgba[i,3])))" stroke="none" />""")
183191
end

0 commit comments

Comments
 (0)