-
Notifications
You must be signed in to change notification settings - Fork 102
add variable interpolation #262
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
Conversation
Why |
It would require matching balanced parenthesis with regex (e.g. I'll also work on fixing the test errors. |
Actually I realize now |
I thought this PR was awesome until I saw this comment. Too bad... See also #205 (comment)
You can install pytest with |
From @tkf's comment in the other thread.
Ha, hadn't thought of that. Thinking about it more, in some sense that's the best syntactically-sound way to do this. The problem with either
where
where You could also in theory syntactically parse the expression, but actually that to me seems like exactly what using The limitation is that
I gather this will work the way people expect it to, be flexible enough to let people do what they want by splitting expressions between |
Ah, yes, that's a good point. Another idea: How about using
So, we don't have to do any PyJulia-specific parsing and we can just look at the AST. Julia parser can also do
|
The fact that it is parsable means that someone could be using it in their macors. So, |
I wanted to get feedback on the version I just force-pushed to this PR before finishing it up. What do you think of this approach? In this version, I got rid of
Some examples:
The main limitation is that since I'm using Julia to parse everything, including the interpolated Python code, some small number of valid Python expressions are non-valid Julia expressions and hence don't work, e.g. In any case, let me know if you have any major problems with this approach. |
Also, since it may not totally be clear what's going on, its all in this macro which parses the Julia/Python code and rewrites things so we can just call it from Python with a globals/locals dicts, e.g.:
|
I like the idea of using Julia's macro system.
Unfortunately, Julia also normalizes some valid Python code to invalid ones, for example:
This is why I suggested to use |
Ah, hadn't realized that, good catch. Maybe its worthwhile enough to correct a few spot-cases like that, and then directing people to use the |
I don't think it's a good idea to have multiple syntaxes to do one thing. If Regarding def f(x):
y = %julia py"x + 1" + 1
return y But if we treat it as an extension of PyCall's semantics, it probably is OK? |
Yea, I can understand this, but
This is a good point to bring up. Actually, in the current version of this PR its totally consistent with PyCall and is always the global scope, i.e. In [10]: x = "global"
...: def f():
...: x = "local"
...: ret = %julia py"x"
...: return ret
...: f()
Out[10]: 'global' I'd say lets keep it this way, and maybe at a later time we could introduce a |
Next up is tests and figuring out why Python exception crash the whole thing. Hopefully I didn't go down some doomed path with this |
I think I'll likely need some help here @stevengj @tkf, these are internals that are beyond me. Basically any python code which gives an error crashes the interpreter (MWE below). I get different stack traces python-jl vs. natively. Hopefully it wasn't a bad idea to have Julia call Python back from inside of Dynamically linked Python:
and python-jl:
|
I'm strongly against to have multiple options because
Using Julia's
Thinking about this more, I think this is not a useful behavior and is potentially a big pitfall for Python users. I started thinking |
Re: the error with |
That's fine, I can remove it.
I didn't understand this, sorry.
This is with this PR, so it seems here is the right place to discuss it? Otherwise I have julia 1.1.0, python 3.6.6, and PyCall v1.91.1. |
Which is the part you don't understand? I'm just saying that we can "hide" the current misbehavior of
Ah, sorry. I thought it happened without this PR. |
I can reproduce it. |
JuliaPy/PyCall.jl#675 fixed it for me |
Ah sorry I had thought PyCall also "misbehaved" like this but I realize now it works correctly. I fixed it here as well, see the updated doc which now returns "local". Trying to fix test failures now... |
Pull Request Test Coverage Report for Build 658
💛 - Coveralls |
Pull Request Test Coverage Report for Build 677
💛 - Coveralls |
You can use |
I think its ready from my end. |
After thinking about it I think you're right about
This means interpolation from Python via
This leads to things like
which I think is entirely natural. I added the logic to track the "quote depth" which you need to do this to the PR, it was really simple. I also turned off interpolation inside of macros entirely, I think you're right that there you just have to err on the safe side. And I removed |
This PR looks great now. Thanks a lot for implementing it!
This sounds good to me. Thanks for noticing the string case (which I forgot). The example looks good. Before merging, a bit of nitpicking of the wording... Doesn't the word "interpolation" suggest that the following code prints the same number five times? for _ in 1:5
println(py"numpy.random.rand()")
end Also, if we interpret "interpolation" as "pre-evaluation object injection" (as in In [2]: x = 1
In [3]: %%julia
println($x)
py"""
x = 2
"""
println($x) which print 1 and 2 in this PR but with "pre-evaluation object injection" semantics it would be 1 twice. This is fine by me (although I'm somewhat nervous a little bit) as we can do whatever we want do to define the semantics of depth-1 Maybe we can just say
instead of Python objects? Or is there a better phrase for it? |
Fair point, how about:
|
FWIW I find both Julia and PyCall behavior totally weird compared to what we have here. In Julia it looks like its always the final value of the interpolated thing (if it was going to be a fixed thing wouldn't the initial value make more sense?), e.g:
and PyCall is maybe trying to be that but I think there's got to be a bug?
|
Ah, this sounds more explicit and easy to understand. It'd be great if you can tweak the docs to avoid using the word "interpolation" like this. BTW the examples you brought up is interesting. I didn't know that's what Julia do. FYI this is what Julia evaluates:
But I have no idea why this happens. Maybe report a bug to Julia? OTOH, PyCall seems to not track the order (maybe because
|
I wasn't really thinking about this case when I implemented |
@marius311 Thanks for your awesome work! |
@stevengj Would it be fixed by just using |
Thanks for all the comments, this was way better because of them! |
Super minor but any idea what happened to the syntax highlighting on cells 6 & 7 in https://pyjulia.readthedocs.io/en/latest/usage.html#ipython-magic ? It looks right when I build the docs locally. Note those are marked language Julia bc the |
Hmm... Good point. I have no idea. Maybe the package versions are different? Though looking at some of the packages listed in https://readthedocs.org/projects/pyjulia/builds/8884519/ they seem to use fairly new ones. We can in principle freeze the version of all the packages by putting them in |
From the docs included in this PR:
Python variables can be passed to Julia using either
$foo
or{foo}
.The
{}
style supports passing arbitrary Python expressions inside.Variables are automatically converted between equivalent Python/Julia types (should they exist) using PyCall. Note below that
1
,x
, and the tuple itself are converted to JuliaInt64
,String
, andTuple
, respectively, although the functionabs
and the result oftypeof
are not.Closes #205
Happy to take any feedback or requests for changes. I didn't write any tests since I couldn't figure out how to make pytest work with python-jl (which my system needs) so maybe someone could help with creating tests, perhaps in a separate PR? (there we'll need to figure out how testing interacts with the fact that interpolation is using the IPython shell global namespace)