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

refactor py_str macro to allow passing custom global/locals #777

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Changes from 1 commit
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
15 changes: 10 additions & 5 deletions src/pyeval.jl
Original file line number Diff line number Diff line change
Expand Up @@ -202,32 +202,37 @@ pasted into the Python code. This allows you to evaluate code
where the code itself is generated by a Julia expression.
"""
macro py_str(code, options...)
m = :(pynamespace($__module__))
esc(macroexpand(__module__, :(PyCall.@_py_str($m, $m, $code, $(options...)))))
marius311 marked this conversation as resolved.
Show resolved Hide resolved
end

macro _py_str(pyglobals, pylocals, code, options...)
stevengj marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are going to use a function for this, it should take a named tuple (__module__=__module__, __source__=__source__) as the first argument so that all the information available within a usual macro is available. I think it's better to make it a single first argument rather than two arguments because future versions of julia may introduce some more information within a macro definition but it'd be bad to change the calling convention of this function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we should use options rather than options....

T = length(options) == 1 && 'o' in options[1] ? PyObject : PyAny
code, locals = interpolate_pycode(code)
input_type = '\n' in code ? Py_file_input : Py_eval_input
fname = make_fname(@__FILE__)
tkf marked this conversation as resolved.
Show resolved Hide resolved
assignlocals = Expr(:block, [(isa(v,String) ?
:(m[$v] = PyObject($(esc(ex)))) :
:(pylocals[$v] = PyObject($(esc(ex)))) :
nothing) for (v,ex) in locals]...)
code_expr = Expr(:call, esc(:(Base.string)))
code_expr = Expr(:call, Base.string)
i0 = firstindex(code)
for i in sort!(collect(filter(k -> isa(k,Integer), keys(locals))))
push!(code_expr.args, code[i0:prevind(code,i)], esc(locals[i]))
i0 = i
end
push!(code_expr.args, code[i0:lastindex(code)])
if input_type == Py_eval_input
removelocals = Expr(:block, [:(delete!(m, $v)) for v in keys(locals)]...)
removelocals = Expr(:block, [:(delete!(pylocals, $v)) for v in keys(locals)]...)
else
# if we are evaluating multi-line input, then it is not
# safe to remove the local variables, because they might be referred
# to in Python function definitions etc. that will be called later.
removelocals = nothing
end
quote
m = pynamespace($__module__)
pyglobals, pylocals = $pyglobals, $pylocals
$assignlocals
ret = $T(pyeval_($code_expr, m, m, $input_type, $fname))
ret = $T(pyeval_($code_expr, pyglobals, pylocals, $input_type, $fname))
$removelocals
ret
end
Expand Down