Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eliminate "global const foo = bar" from __init__ #279

Merged
merged 7 commits into from
May 24, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/PyCall.jl
Original file line number Diff line number Diff line change
Expand Up @@ -643,14 +643,14 @@ for (mime, method) in ((MIME"text/html", "_repr_html_"),
function writemime(io::IO, mime::$mime, o::PyObject)
if o.o != C_NULL && haskey(o, $method)
r = pycall(o[$method], PyObject)
r.o != pynothing && return write(io, convert($T, r))
r.o != pynothing[] && return write(io, convert($T, r))
end
throw(MethodError(writemime, (io, mime, o)))
end
mimewritable(::$mime, o::PyObject) =
o.o != C_NULL && haskey(o, $method) && let meth = o[$method]
meth.o != pynothing &&
pycall(meth, PyObject).o != pynothing
meth.o != pynothing[] &&
pycall(meth, PyObject).o != pynothing[]
end
end
end
Expand Down
10 changes: 5 additions & 5 deletions src/conversions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ PyObject(c::Complex) = PyObject(@pycheckn ccall((@pysym :PyComplex_FromDoubles),
PyPtr, (Cdouble,Cdouble),
real(c), imag(c)))

PyObject(n::Void) = pyerr_check("PyObject(nothing)", pyincref(pynothing))
PyObject(n::Void) = pyerr_check("PyObject(nothing)", pyincref(pynothing[]))

# conversions to Julia types from PyObject

Expand Down Expand Up @@ -110,7 +110,7 @@ end
#########################################################################
# Pointer conversions, using ctypes or PyCapsule

PyObject(p::Ptr) = py_void_p(p)
PyObject(p::Ptr) = pycall(c_void_p_Type, PyObject, UInt(p))

function convert(::Type{Ptr{Void}}, po::PyObject)
if pyisinstance(po, c_void_p_Type)
Expand Down Expand Up @@ -567,7 +567,7 @@ end
# Range: integer ranges are converted to xrange,
# while other ranges (<: AbstractVector) are converted to lists

xrange(start, stop, step) = pycall(pyxrange, PyObject,
xrange(start, stop, step) = pycall(pyxrange[], PyObject,
start, stop, step)

function PyObject{T<:Integer}(r::Range{T})
Expand Down Expand Up @@ -703,7 +703,7 @@ pystring_query(o::PyObject) = pyisinstance(o, @pyglobalobj PyString_Type) ? Abst
# we never automatically convert to Function.
pyfunction_query(o::PyObject) = Union{}

pynothing_query(o::PyObject) = o.o == pynothing ? Void : Union{}
pynothing_query(o::PyObject) = o.o == pynothing[] ? Void : Union{}

# we check for "items" attr since PyMapping_Check doesn't do this (it only
# checks for __getitem__) and PyMapping_Check returns true for some
Expand All @@ -720,7 +720,7 @@ function pysequence_query(o::PyObject)
if pyisinstance(o, @pyglobalobj :PyTuple_Type)
len = @pycheckz ccall((@pysym :PySequence_Size), Int, (PyPtr,), o)
return typetuple([pytype_query(PyObject(ccall((@pysym :PySequence_GetItem), PyPtr, (PyPtr,Int), o,i-1)), PyAny) for i = 1:len])
elseif pyisinstance(o, pyxrange)
elseif pyisinstance(o, pyxrange[])
return Range
elseif ispybytearray(o)
return Vector{UInt8}
Expand Down
10 changes: 5 additions & 5 deletions src/gc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# * When we wrap a Julia object jo inside a Python object po
# (e.g a numpy array), we add jo to the pycall_gc dictionary,
# keyed by a weak reference to po. The Python weak reference
# allows us to register a callback function that is called
# allows us to register a callback function that is called
# when po is deallocated, and this callback function removes
# jo from pycall_gc so that Julia can garbage-collect it.

Expand All @@ -16,8 +16,8 @@ const pycall_gc = Dict{PyPtr,Any}()
function weakref_callback(callback::PyPtr, wo::PyPtr)
delete!(pycall_gc, wo)
ccall((@pysym :Py_DecRef), Void, (PyPtr,), wo)
ccall((@pysym :Py_IncRef), Void, (PyPtr,), pynothing)
return pynothing
ccall((@pysym :Py_IncRef), Void, (PyPtr,), pynothing[])
return pynothing[]
end

const weakref_callback_obj = PyNULL() # weakref_callback Python method
Expand All @@ -34,8 +34,8 @@ function pyembed(po::PyObject, jo::Any)
"weakref_callback",
METH_O)).o
end
wo = @pycheckn ccall((@pysym :PyWeakref_NewRef), PyPtr, (PyPtr,PyPtr),
wo = @pycheckn ccall((@pysym :PyWeakref_NewRef), PyPtr, (PyPtr,PyPtr),
po, weakref_callback_obj)
pycall_gc[wo] = jo
return po
end
end
8 changes: 4 additions & 4 deletions src/gui.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pyexists(mod) = try

# Tkinter was renamed to tkinter in Python 3
function tkinter_name()
return pyversion < v"3" ? "Tkinter" : "tkinter"
return pyversion_build.major < 3 ? "Tkinter" : "tkinter"
end

pygui_works(gui::Symbol) = gui == :default ||
Expand Down Expand Up @@ -109,7 +109,7 @@ function qt_eventloop(QtModule="PyQt4", sec::Real=50e-3)
maxtime = PyObject(50)
@doevent begin
app = pycall(instance, PyObject)
if app.o != pynothing
if app.o != pynothing[]
app["_in_event_loop"] = true
pycall(processEvents, PyObject, AllEvents, maxtime)
end
Expand All @@ -125,7 +125,7 @@ function wx_eventloop(sec::Real=50e-3)
EventLoopActivator = wx["EventLoopActivator"]
@doevent begin
app = pycall(GetApp, PyObject)
if app.o != pynothing
if app.o != pynothing[]
app["_in_event_loop"] = true
evtloop = pycall(EventLoop, PyObject)
ea = pycall(EventLoopActivator, PyObject, evtloop)
Expand All @@ -146,7 +146,7 @@ function Tk_eventloop(sec::Real=50e-3)
Tk = pyimport(tkinter_name())
@doevent begin
root = Tk["_default_root"]
if root.o != pynothing
if root.o != pynothing[]
pycall(root["update"], PyObject)
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/numpy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function npyinitialize()
catch e
error("numpy.core.multiarray required for multidimensional Array conversions - ", e)
end
if pyversion < v"3.0"
if pyversion_build.major < 3
PyArray_API = @pycheck ccall((@pysym :PyCObject_AsVoidPtr),
Ptr{Ptr{Void}}, (PyPtr,),
npy_multiarray["_ARRAY_API"])
Expand Down
35 changes: 22 additions & 13 deletions src/pydates.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,34 +48,45 @@ end
const PyDate_HEAD = sizeof(Int)+sizeof(PyPtr)+sizeof(Py_hash_t)+1

# called from __init__
const DateType = Ref{PyPtr}()
const DateTimeType = Ref{PyPtr}()
const DeltaType = Ref{PyPtr}()
const Date_FromDate = Ref{Ptr{Void}}()
const DateTime_FromDateAndTime = Ref{Ptr{Void}}()
const Delta_FromDelta = Ref{Ptr{Void}}()
function init_datetime()
# emulate PyDateTime_IMPORT:
global const PyDateTimeAPI =
unsafe_load(@pycheckn ccall((@pysym :PyCapsule_Import),
PyDateTimeAPI = unsafe_load(@pycheckn ccall((@pysym :PyCapsule_Import),
Ptr{PyDateTime_CAPI}, (Ptr{UInt8}, Cint),
"datetime.datetime_CAPI", 0))
DateType[] = PyDateTimeAPI.DateType
DateTimeType[] = PyDateTimeAPI.DateTimeType
DeltaType[] = PyDateTimeAPI.DeltaType
Date_FromDate[] = PyDateTimeAPI.Date_FromDate
DateTime_FromDateAndTime[] = PyDateTimeAPI.DateTime_FromDateAndTime
Delta_FromDelta[] = PyDateTimeAPI.Delta_FromDelta
end

PyObject(d::Dates.Date) =
PyObject(@pycheckn ccall(PyDateTimeAPI.Date_FromDate, PyPtr,
PyObject(@pycheckn ccall(Date_FromDate[], PyPtr,
(Cint, Cint, Cint, PyPtr),
Dates.year(d), Dates.month(d), Dates.day(d),
PyDateTimeAPI.DateType))
DateType[]))

PyObject(d::Dates.DateTime) =
PyObject(@pycheckn ccall(PyDateTimeAPI.DateTime_FromDateAndTime, PyPtr,
PyObject(@pycheckn ccall(DateTime_FromDateAndTime[], PyPtr,
(Cint, Cint, Cint, Cint, Cint, Cint, Cint,
PyPtr, PyPtr),
Dates.year(d), Dates.month(d), Dates.day(d),
Dates.hour(d), Dates.minute(d), Dates.second(d),
Dates.millisecond(d) * 1000,
pynothing, PyDateTimeAPI.DateTimeType))
pynothing[], DateTimeType[]))

PyDelta_FromDSU(days, seconds, useconds) =
PyObject(@pycheckn ccall(PyDateTimeAPI.Delta_FromDelta, PyPtr,
PyObject(@pycheckn ccall(Delta_FromDelta[], PyPtr,
(Cint, Cint, Cint, Cint, PyPtr),
days, seconds, useconds,
1, PyDateTimeAPI.DeltaType))
1, DeltaType[]))

PyObject(p::Dates.Day) = PyDelta_FromDSU(Int(p), 0, 0)

Expand All @@ -97,11 +108,9 @@ function PyObject(p::Dates.Millisecond)
PyDelta_FromDSU(d, s, ms * 1000)
end

for T in (:Date, :DateTime, :Delta)
f = Symbol(string("Py", T, "_Check"))
t = Expr(:., :PyDateTimeAPI, QuoteNode(Symbol(string(T, "Type"))))
@eval $f(o::PyObject) = pyisinstance(o, $t)
end
PyDate_Check(o::PyObject) = pyisinstance(o, DateType[])
PyDateTime_Check(o::PyObject) = pyisinstance(o, DateTimeType[])
PyDelta_Check(o::PyObject) = pyisinstance(o, DeltaType[])

function pydate_query(o::PyObject)
if PyDate_Check(o)
Expand Down
57 changes: 18 additions & 39 deletions src/pyinit.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Initializing Python (surprisingly complicated; see also deps/build.jl)

#########################################################################
# global constants, initialized to NULL and then overwritten in __init__

# global PyObject constants, initialized to NULL and then overwritten in __init__
# (eventually, the ability to define global const in __init__ may go away,
# and in any case this is better for type inference during precompilation)

const inspect = PyNULL()
const builtin = PyNULL()
const BuiltinFunctionType = PyNULL()
Expand All @@ -15,6 +15,13 @@ const ufuncType = PyNULL()
const format_traceback = PyNULL()
const pyproperty = PyNULL()
const jlfun2pyfun = PyNULL()
const c_void_p_Type = PyNULL()

# other global constants initialized at runtime are defined via Ref
# or are simply left as non-const values
pyversion = pyversion_build # not a Ref since pyversion is exported
const pynothing = Ref{PyPtr}()
const pyxrange = Ref{PyPtr}()

#########################################################################

Expand Down Expand Up @@ -43,16 +50,15 @@ function __init__()
end

# cache the Python version as a Julia VersionNumber
global const pyversion = convert(VersionNumber,
split(bytestring(ccall(@pysym(:Py_GetVersion),
Ptr{UInt8}, ())))[1])
global pyversion = convert(VersionNumber, split(bytestring(ccall(@pysym(:Py_GetVersion),
Ptr{UInt8}, ())))[1])
if pyversion_build.major != pyversion.major
error("PyCall built with Python $pyversion_build, but now using Python $pyversion; ",
"you need to relaunch Julia and re-run Pkg.build(\"PyCall\")")
end

copy!(inspect, pyimport("inspect"))
copy!(builtin, pyimport(pyversion.major < 3 ? "__builtin__" : "builtins"))
copy!(builtin, pyimport(pyversion_build.major < 3 ? "__builtin__" : "builtins"))
copy!(pyproperty, pybuiltin(:property))

pyexc_initialize() # mappings from Julia Exception types to Python exceptions
Expand All @@ -62,47 +68,20 @@ function __init__()

# cache Python None -- PyPtr, not PyObject, to prevent it from
# being finalized prematurely on exit
global const pynothing = @pyglobalobj(:_Py_NoneStruct)
pynothing[] = @pyglobalobj(:_Py_NoneStruct)

# xrange type (or range in Python 3)
global const pyxrange = @pyglobalobj(:PyRange_Type)

# cache ctypes.c_void_p type and function if available
vpt, pvp = try
(pyimport("ctypes")["c_void_p"],
p::Ptr -> pycall(c_void_p_Type, PyObject, UInt(p)))
catch # fallback to CObject
(@pyglobalobj(:PyCObject_FromVoidPtr),
p::Ptr -> PyObject(ccall(pycobject_new, PyPtr, (Ptr{Void}, Ptr{Void}), p, C_NULL)))
end
global const c_void_p_Type = vpt
global const py_void_p = pvp
pyxrange[] = @pyglobalobj(:PyRange_Type)

# ctypes.c_void_p for Ptr types
copy!(c_void_p_Type, pyimport("ctypes")["c_void_p"])

# traceback.format_tb function, for show(PyError)
copy!(format_traceback, pyimport("traceback")["format_tb"])

# all cfunctions must be compiled at runtime
global const jl_Function_call_ptr =
cfunction(jl_Function_call, PyPtr, (PyPtr,PyPtr,PyPtr))
global const pyjlwrap_dealloc_ptr = cfunction(pyjlwrap_dealloc, Void, (PyPtr,))
global const pyjlwrap_repr_ptr = cfunction(pyjlwrap_repr, PyPtr, (PyPtr,))
global const pyjlwrap_hash_ptr = cfunction(pyjlwrap_hash, UInt, (PyPtr,))
global const pyjlwrap_hash32_ptr = cfunction(pyjlwrap_hash32, UInt32, (PyPtr,))

# PyMemberDef stores explicit pointers, hence must be initialized in __init__
global const pyjlwrap_members =
PyMemberDef[ PyMemberDef(pyjlwrap_membername,
T_PYSSIZET, sizeof_PyObject_HEAD, READONLY,
pyjlwrap_doc),
PyMemberDef(C_NULL,0,0,0,C_NULL) ]

init_datetime()
pyjlwrap_init()

global const jl_FunctionType = pyjlwrap_type("PyCall.jl_Function",
t -> t.tp_call =
jl_Function_call_ptr)

# jl_FunctionType is a class, and when assigning it to an object
# obj[:foo] = some_julia_function
# it won't behave like a regular Python method because it's not a Python
Expand All @@ -114,7 +93,7 @@ function __init__()

if !already_inited
# some modules (e.g. IPython) expect sys.argv to be set
if pyversion.major < 3
if pyversion_build.major < 3
argv_s = bytestring("")
argv = unsafe_convert(Ptr{UInt8}, argv_s)
ccall(@pysym(:PySys_SetArgvEx), Void, (Cint,Ptr{Ptr{UInt8}},Cint), 1, &argv, 0)
Expand Down
Loading