[IPython-dev] cell magics
Jason Grout
jason-sage at creativetrax.com
Sat Feb 16 17:07:04 EST 2013
On 2/16/13 12:47 AM, Fernando Perez wrote:
> Hi Jason,
>
>> PROPOSAL:
>>
>> Line and cell magics are normal python functions that take a string as
>> their first input. The string is either the rest of the line, or if
>> there is no discernible string on the rest of the line, the string is
>> taken as the rest of the cell. As an example:
>>
>> %timeit(runs=10) 2+3
>>
>> which gets translated to time("2+3", runs=10)
>>
>> A cell magic is exactly the same function, only it is invoked by putting
>> the string on the following lines, rather than on the same line, so the
>> string is taken as the remainder of the cell:
>>
>> %timeit(runs=10)
>> 2+3
>> 5+6
>>
>> which gets translated to timeit("2+3\n5+6", runs=10)
>>
>> Notice there is no distinction between line and cell magic
>> functions---the distinction entirely happens in how they are invoked
>> (whether the string is on the same line or on following lines). Also,
>> magic options are passed in exactly the same way that arguments are
>> always passed to python functions, instead of having a non-pythonic,
>> bash-like syntax for options.
I've spent all day (and a better part of a night :) writing and
rewriting this reply; hopefully it's clear. Like I mentioned before,
I'm still forming an opinion and working on seeing things clearly.
Special thanks goes to some private comments from William for helping me
to see more clearly what is a real difference between the two
philosophies and what is an implementation detail.
To summarize, I think the two positions are:
Current IPython:
1a. % is only a valid operator on registered string decorators
2a. Two different syntaxes for invoking line and cell string decorators
3a. optional arguments to string decorators should not have pythonic syntax
In our proposal, we say:
1b. % should be a valid operator on any string decorator
2b. The % cell/line invocation syntax should be unified
3b. optional arguments to string decorators should have pythonic syntax
Is that a fair characterization?
Let's take these in turn:
1. where % is a valid operator
==============================
I think your argument boils down to "let's make % only apply to
registered string decorators so that people will think in a certain way
when they use %". That may have held when you had tight control on
exactly what was registered, but now with everyone and their mother
implementing custom extensions doing who knows what, it's artificially
restrictive to insist that % means "modify the editor or go beyond
python". Thomas points out that already the scope of % directives has
massively increased beyond the original idea. With custom extensions,
the scope of what is accomplished and how a user thinks when they use %
blows wide open.
With our proposal, %<tab> could still just complete with registered
string decorators (with even the exact same registration system, so you
can register things outside of the user's namespace). The difference is
that if I wanted to run a string decorator that wasn't in the registered
namespace, but I had just defined in my user namespace, I wouldn't have
to pollute the registered namespace for my one invocation. I could just
run %my_function directly. I think this is particularly important
because the registered % namespace is a flat list of names, so it's not
a very organized namespace (which is exactly a problem you have with
Sage's philosophy, ironically :).
2. Unification of user syntax for cell/line decorators
======================================================
You only address the fact that you have special ways to make it easy for
a developer to define both a line and cell magic. That's an
implementation detail, and it's easier with our proposal anyway since
everything is automatically both. We see already that there are a
number of decorators that could be both, but are only defined as one,
like %ruby, for example.
But the real issue here is that IPython has two different syntaxes for
the users to invoke string decorators. That means that I, as a user,
constantly have to remember if a command is one, or the other, or both,
because the very first two characters are different between the two.
For example, just now I typed %ru<tab>, and I see from the completions
that ruby is only a cell decorator. So now I have to backspace and put
that extra % in there. It also means that if a string decorator can be
invoked as both, in order to change between the two, I have to not only
put the string in a different place, but I also have to go back and
modify the % part too. If I forget to modify the %, I get very
confusing results
With our proposal, these issues go away. All string decorators are
invoked with %. The difference between a line or cell decorator is
determined by whether or not there is a string on the line (just as the
difference between
if blah: something
and
if blah:
something
something else
is (partly) just seen by looking to see if the block is on the same
line). So to change between cell and line string decorators, all I have
to do is move the line. For example:
%time some_function
now I want to add a few more things to the time run. All I have to do
is change where the string is:
%time
some_function
some_other function
and the lack of a string after %time tells me that I should look below
%time for the string. I don't have to constantly keep adjusting the %
character(s).
P.S. Actually, after thinking about it more, since a single % is
ambiguous syntax, I think I would prefer all string decorators be
invoked with %%, which is invalid python syntax. Then we won't have
this problem:
cd=5
a=4\
%cd
or this problem:
cd=4
a="time: %d"\
%cd
3. format of optional arguments
===============================
It seems that there are two main arguments for this:
* backwards compatibility (with IPP and previous IPython versions, as
well as IDL and matlab apparently?). But I'll point out that we can
easily support this too, in almost exactly the same invocation:
%timeit('-r 5 -and -other -options') 2+3
* also, you made the argument that it should be command-line type syntax
because it is visually distinctive from python. I think this argument
presupposes that:
(a) users are comfortable with bash-like syntax. With the rising
popularity of IPython, and especially with the rising popularity of the
notebook, I think we're going to see more and more non-unix users that
see the bash-like syntax as one more *new* thing to learn, rather than
something that is already familiar in a different context. In fact, I
look at the %timeit syntax for example, and I have to try to remember
what the options are each time. Compare:
%timeit -r5 2+3
%timeit(runs=5) 2+3
I think it's pretty clear which statement is more readable. This focus
on readability over brevity (remember, "Readability counts") is part of
why python is so good in general.
I look at the %R option syntax, and I absolutely *have* to go to the
help to see what is going on. Supporting a pythonic syntax I think will
help users tremendously.
Of course, this readability stuff is just a convention. You could make
%timeit --runs=5 2+3
but the point is that encouraging a bash-like syntax is casting a vote
for brevity over readability. Considering that each extension will
implement its own short options, I think I vote for readability, so I
have a better chance of looking at a string decorator invocation and
having an idea of what it is doing
(b) the bash-like syntax is just as powerful. But it's certainly not.
With a bash-like syntax that isn't valid python code, but just a string,
I cannot pass in python objects directly. I can do that if my
parameters are valid python syntax. This is a lesson IPython learned
when it decided on a rich pyout/display_data/stream output system
instead of just passing on a stdout string. It makes sense to apply
that same lesson here to the string decorator inputs.
(c) we need visually distinctive syntax because % directives are very
different from python code. But like I mention above with custom user
extensions, I don't think this idea that % directives are command-line
sorts of things applies as much anymore because the field is wide open.
Regardless, if we *have* to have bash syntax for some extension, it's
easy to make an invocation like:
%script('-bg')
blah
I think I just convinced myself that the new proposal really is a better
plan.
Whew! Okay, so...comments?
Thanks,
Jason
More information about the IPython-dev
mailing list