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

IPEP 23: Backbone.js Widgets #4374

Merged
merged 474 commits into from Jan 28, 2014

Conversation

@jdfreder
Copy link
Member

commented Oct 10, 2013

Description

Adds backbone.js layer on top of comm layer to enable the creation of modular widgets.
See IPEP 23 for a detailed description.

Organization

  • Widget manager /ipython/IPython/html/static/js/widgetmanager.js
  • Javascript models and associated views /ipython/IPython/html/static/js/widgets/widget*.js
  • Python widgets /ipython/IPython/html/widgets/widget*.py
  • Unit tests /ipython/IPython/html/tests/casperjs/test_cases/widgets*.js
  • Example and tutorial notebooks /ipython/examples/widgets/*.ipynb

Examples

Examples and tutorials are included in this PR's contents (as seen above) direct link

Included Widgets

  • CheckboxWidget
  • ToggleButtonWidget
  • ButtonWidget
  • ContainerWidget
  • PopupWidget
  • FloatSliderWidget
  • BoundedFloatTextWidget
  • FloatProgressWidget
  • FloatTextWidget
  • ImageWidget
  • IntSliderWidget
  • BoundedIntTextWidget
  • IntProgressWidget
  • IntTextWidget
  • AccordionWidget
  • TabWidget
  • ToggleButtonsWidget
  • RadioButtonsWidget
  • DropdownWidget
  • SelectWidget
  • HTMLWidget
  • LatexWidget
  • TextareaWidget
  • TextWidget
@jdfreder jdfreder referenced this pull request Oct 10, 2013
1 of 1 task complete
@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Oct 17, 2013

Uploaded some widgets to play with,
also updated example gist.

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Oct 17, 2013

PR description updated with some small examples and an index of the widget/view pairs was added.

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Oct 17, 2013

Made changes discussed in meeting and added ToggleButtonView for the SelectionWidget.

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Oct 31, 2013

NOTE: JS test will not pass until jupyter-attic/ipython-components#10 is merged

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 1, 2013

Continuing a discussion from HipChat,

@cekees This PR provides an MVC-like architecture on top of the comm stuff. It's useful for widgets that can be expressed via a list of properties. This PR synchronizes a python traitlet model in the backend with a backbone model in the front. Some of the things it automatically handles are

  • delta compression (only sending the state information that has changed)
  • wiring the message callbacks to the correct cells automatically
  • inter-view synchronization (handled by backbone)
  • message throttling (to avoid flooding the kernel)
  • parent/child relationships between views (which one can override to specify custom parent/child relationships)
  • ability to manipulate the widget view's DOM from python using CSS, $().addClass, and $().removeClass methods

That being said, if you're interested in lower level control (for example, implementing a renderer that streams images to the browser using webm compression or something similar), you'll want to use comms to implement your widgets.

EDIT 11/20/2013: Support for custom messages was added a couple of days ago.

@jasongrout

This comment has been minimized.

Copy link
Member

commented Nov 2, 2013

Sorry; I haven't had time to look at the code in this pull request much, but I'm curious: how much does this depend on the architecture of the notebook? I'd like to see about adapting this to the Sage cell server, but we don't have the same concept of 'cells' as in the notebook. It seems like (in principle) this could be more general than depending on details of the IPython notebook front end.

If your answer is: "Read the code, tell us what you think", that's fine and understandable. I apologize for not having time (yet) to go through this carefully.

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 3, 2013

Hi @jasongrout ,

I think I don't fully understand the context of where you want to implement this code. If you are planning on using it within a modified IPython environment where only one cell exists, that should work great (as long as the comm stuff is available). If you are planning on using it elsewhere, the coupling with IPython isn't horrible, but it will require a little bit of work. As of now it depends on

  • IPython traitlets
  • IPython Comm & CommManager
  • IPython.OutputArea (required to map outputs with a comm message)
  • JQuery, Require.js, bootstrap, underscore.js, and backbone.js

EDIT 11/4/13: Updated with new details

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 3, 2013

With regards to the msg_id mapping, see this JS function https://github.com/ipython/ipython/pull/4374/files#diff-309e41632e4bd9e130ed388abdd89575R294 . This is fairly recent code and not the only place where the mapping is (also the function that makes the comm callback dict). I think I'm going to try to do a better job of reducing the coupling with cell_index.

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 4, 2013

@jasongrout I just made changes that decoupled the cell_index from the mapping code. Now everything that you would need to modify for custom mapping to use the code outside of IPython is contained in the _get_output_area(msg_id) and _get_widget_area_element(output_area, show) functions. 😄

EDIT: 11/4/13: Added _get_widget_area_element(...) to post

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 4, 2013

^ That last commit seems to have broken something... Looking

@jasongrout

This comment has been minimized.

Copy link
Member

commented Nov 4, 2013

Thanks. Encapsulating all the IPython notebook-specific things, like what the output DOM looks like, into a function or two that I could override is what I was hoping for.

If I understand things correctly, there is only one widget area per cell? That seems kind of limiting. I'd love to print some text, have a widget, print some more text, have another widget, etc. Is there any way to embed widgets in the normal output flow? I mean, I can embed images, audio, and all sorts of other things in the normal output region of a cell, inline with the other output---why not widgets too?

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 4, 2013

That seems kind of limiting.

I totally agree with you. Ideally you would be able to place widgets wherever you want. This is one of the design questions that we talked about in one of the first dev meetings regarding this subject. @ellisonbg and I re-discussed the issue when I started implementing this.

The problem is that we don't want the widgets to be cleared along with the output. In other words, we want the user to be able to make a widget and hookup the value change event to a function that clears the output and plots. If the output clear event cleared the entire output with the widgets included, the widget on the front end and corresponding com would get closed. Then the user would have to re-show the widget with the output. Even if the clear output selectively cleared the output, the placement of the widgets would be ruined.

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 4, 2013

@jasongrout I fixed the bug, and updated my comments above with the changes.

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 4, 2013

@jasongrout Also with the new changes, you could implement multiple widget areas by just overriding _get_widget_area_element so that it created new widget areas on the fly within the output_area. The problems I mentioned above would still exist- You would still have to override/fix clear_output so it didn't clear widget areas.

@jasongrout

This comment has been minimized.

Copy link
Member

commented Nov 5, 2013

Sorry I haven't kept up on the design discussions. In the case you mentioned, couldn't the widget just be hooked up to an "output widget" containing the plot, and send the clear message just to that output widget?

We've done similar things for Sage interacts, and it seems to work well (nested interacts, for example, like http://sagecell.sagemath.org/?q=zkjyys -- change the numbers of rows and columns, and the inner interact updates accordingly). It nicely encapsulates the effects of a widget too. In fact, I suppose you could implement the standard output of a cell as an "output" widget itself, and have turtles all the way down.

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 5, 2013

In the case you mentioned, couldn't the widget just be hooked up to an "output widget" containing the plot, and send the clear message just to that output widget?

It could, but in that case why bother with interweaving the output area and widgets at all? The existing framework can behave like that already. There is already a LabelView (which lets you display arbitrary HTML in the widget area). The only thing that is missing is an ImageWidget or PlotWidget. Maybe that's something that should be included by default?

I think someone else is working on porting the matplot image data over a comm (unfortunately I don't know where that is being done). An image widget was on my todo list at one point, but @ellisonbg and I removed it.

@jasongrout

This comment has been minimized.

Copy link
Member

commented Nov 5, 2013

That was me working on porting matplotlib images over to the comm framework :). I had to leave it for the time being, but hope to pick it back up before or during the holiday season.

There's also a generic "output widget" missing, right? Suppose I want to execute a user function every time a button is clicked, and put whatever output the user did in that function in a box below the button (this is similar to how Sage interacts work right now).

But back to the other discussion---I'm just seeing if there is a way to combine the widget and general output regions. The point is not whether I can interleave widgets and output, but whether that should be the default. In the use-case you mentioned above, to me it seems more elegant to make the plot that would be cleared a specific region in the output, like this generic "output widget", that could be cleared independent of the rest of the output. Also, how would I actually clear/destroy the widget area?

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 5, 2013

That was me working on porting matplotlib images over to the comm framework :)

Awesome! Yeah I can't wait till that works. I think that would be much better than having a plot or image widget that tries to transmit all of that data through state.

There's also a generic "output widget" missing, right? Suppose I want to execute a user function every time a button is clicked, and put whatever output the user did in that function in a box below the button (this is similar to how Sage interacts work right now).

Maybe, I think redirecting output from the standard output area to a widget is not possible with the current callback system. I believe that would require the msg callbacks stored in the front-end (that direct output to the correct cell) to be modified in the middle of a cell execution in the back-end. I don't think there is a message that allows this to be done. @minrk would definitely know how to do this and if it could be done.

In the use-case you mentioned above, to me it seems more elegant to make the plot that would be cleared a specific region in the output

It may be more elegant, but I don't think it would be the simplest solution. Right now a user can create a widget and register a callback for it's value change. In that callback the user clears the output and draws a plot, just like he/she normally would for simple animations. The benefit is that the output area is predictable and behaves like it does without widgets.

Also, just to make sure we are on the same page, these widgets are not a form of interact. Interact can and will be built on top of these widgets, which is what @ellisonbg has shown and is still working on.

@cekees

This comment has been minimized.

Copy link

commented Nov 5, 2013

I've running this branch without any problems on my local installation, even doing a little bit of UI development for some computational models using the IPython widgets, but I can't seem to get them to work when I run ipython notebook from a sage math cloud instances. No errors, but also no widgets. @jasongrout have you tried these on SMC? Perhaps interestingly, @minrk's low level comm example with the range widget works perfectly. The IPython commit ID is 2cc3475.

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 5, 2013

@cekees is it because jupyter-attic/ipython-components#10 isn't checked out on the sage server too?

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 5, 2013

The JavaScript console in your web browser should mention missing backbone.js and underscore.js

@cekees

This comment has been minimized.

Copy link

commented Nov 5, 2013

ipython:
commit 2cc3475
Author: Jonathan Frederic jdfreder@calpoly.edu
Date: Mon Nov 4 19:16:34 2013 +0000

Fixed comment in widget.js

ipython/IPython/html/static/components:
commit 041a80ccbc2073eee9a2dceddf2cfcf9d9fa87a0
Author: Jonathan Frederic jdfreder@calpoly.edu
Date: Fri Oct 11 22:53:47 2013 +0000

Add backbone and underscore.

and:
~/ipython/IPython/html/static/components$ ls
backbone bower.json fabfile.py highlight.js jquery-ui marked
README.md underscore
bootstrap codemirror font-awesome jquery less.js
nonbower.json requirejs

But I still get the following in my js console:

  1. GET
    https://cloud.sagemath.com/static/notebook/js/widgets/int_range.js?_=1383690233765404
    (Not Found)
    jquery.min.js:6https://cloud.sagemath.com/6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/static/components/jquery/jquery.min.js
    1. x.support.cors.e.crossDomain.sendjquery.min.js:6https://cloud.sagemath.com/6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/static/components/jquery/jquery.min.js
    2. x.extend.ajaxjquery.min.js:6https://cloud.sagemath.com/6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/static/components/jquery/jquery.min.js
    3. x.(anonymous
      function)jquery.min.js:6https://cloud.sagemath.com/6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/static/components/jquery/jquery.min.js
    4. x.extend.getScriptjquery.min.js:6https://cloud.sagemath.com/6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/static/components/jquery/jquery.min.js
    5. (anonymous function)
    6. OutputArea.append_javascriptoutputarea.js:478https://cloud.sagemath.com/6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/static/notebook/js/outputarea.js
    7. OutputArea.append_mime_typeoutputarea.js:448https://cloud.sagemath.com/6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/static/notebook/js/outputarea.js
    8. OutputArea.append_display_dataoutputarea.js:427https://cloud.sagemath.com/6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/static/notebook/js/outputarea.js
    9. OutputArea.append_outputoutputarea.js:305https://cloud.sagemath.com/6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/static/notebook/js/outputarea.js
    10. OutputArea.handle_outputoutputarea.js:254https://cloud.sagemath.com/6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/static/notebook/js/outputarea.js
    11. x.isFunction.ijquery.min.js:4https://cloud.sagemath.com/6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/static/components/jquery/jquery.min.js
    12. Kernel._handle_output_messagekernel.js:544https://cloud.sagemath.com/6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/static/services/kernels/js/kernel.js
    13. x.isFunction.ijquery.min.js:4https://cloud.sagemath.com/6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/static/components/jquery/jquery.min.js
    14. Kernel._handle_iopub_messagekernel.js:555https://cloud.sagemath.com/6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/static/services/kernels/js/kernel.js
    15. x.isFunction.i

On Tue, Nov 5, 2013 at 4:10 PM, Jonathan Frederic
notifications@github.comwrote:

The JavaScript console in your web browser should mention missing
backbone.js and underscore.js


Reply to this email directly or view it on GitHubhttps://github.com//pull/4374#issuecomment-27818012
.

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 5, 2013

Ah! It looks like the path to the widget stuff is missing the /6dbbd03e-0125-46e8-acfd-335888c9464b/port/25043/ portion of the URL. This probably is because I load the files from an absolute path, I'll try changing that so it's relative. I think I just need to change where the widget js is loaded from using display. The require.js imports should work. I'll post back in a second with a fix attempt...

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 5, 2013

@cekees can you try the last commit? c6936ae

@cekees

This comment has been minimized.

Copy link

commented Nov 5, 2013

Works!

On Tue, Nov 5, 2013 at 4:40 PM, Jonathan Frederic
notifications@github.comwrote:

@cekees https://github.com/cekees can you try the last commit?


Reply to this email directly or view it on GitHubhttps://github.com//pull/4374#issuecomment-27820872
.

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 6, 2013

@cekees Could you do me a favor and pull the latest and test again when you get a chance (making sure to clear the browser cache)? I changed the JavaScript references yet again, but I am using baseProjectUrl now.

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 6, 2013

I uploaded the basic widget tutorial notebooks, parts 1-5. They are listed in the PR description and actually stored in this PR (ipython/examples/widgets).

@cekees

This comment has been minimized.

Copy link

commented Nov 6, 2013

Works for me, except Part 5 in the PR is giving me a 404 and on Part 4: Styles, In[5] I get the following error (there are also errors down further in the worksheet that may or may not be related.


TypeError Traceback (most recent call last)
in ()
6 # set_css used to set multiple CSS attributes.
7 container.set_css({'padding': '6px', # Add padding to the container
----> 8 'background': 'yellow'}) # Fill the container yellow
9
10 label = widgets.StringWidget(default_view_name="LabelView", parent=container)

TypeError: set_css() takes at least 3 arguments (2 given)

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Nov 6, 2013

TypeError: set_css() takes at least 3 arguments (2 given)

Looks like you don't have the latest widgets.py? Commit 42c6fc8 changes set_css so it can take between 1-3 arguments.

@ellisonbg ellisonbg referenced this pull request Jan 27, 2014
@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Jan 28, 2014

I think we should remove the inspection and only support one callback signature in each case

I think you are right. I merged your review PR, thanks for all the good input! Question: I noticed that, in the example notebooks, you returned after each period in the markdown cells. Is that something I should practice too?

@minrk

This comment has been minimized.

Copy link
Member

commented Jan 28, 2014

Is that something I should practice too?

No, don't worry about it, it's just my habit.

jdfreder added 4 commits Jan 28, 2014
Renamed widgets...
TextWidget, TextareaWidget, CheckboxWidget, and SelectWidget
@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Jan 28, 2014

@minrk and @ellisonbg this should be ready to merge after d661486 pleases our good friend Travis C. I.

@ellisonbg

This comment has been minimized.

Copy link
Member

commented Jan 28, 2014

Merging. Great work @jdfreder !!! and thanks everyone for the review help, especially @minrk and @jasongrout @cekees

ellisonbg added a commit that referenced this pull request Jan 28, 2014
Merge pull request #4374 from jdfreder/widget-msg
IPEP 23: Backbone.js Widgets

@ellisonbg ellisonbg merged commit 7eac82e into ipython:master Jan 28, 2014

1 check passed

default The Travis CI build passed
Details
@jasongrout

This comment has been minimized.

Copy link
Member

commented Jan 29, 2014

Awesome! Congrats, guys!

// elsewhere.
define(["underscore",
"backbone",
], function (Underscore, Backbone) {

This comment has been minimized.

Copy link
@jasongrout

jasongrout Jan 29, 2014

Member

This should be function(_, Backbone), since we are actually using underscore via _ below. This might also be a problem in other files.

@jasongrout

This comment has been minimized.

Copy link
Member

commented Jan 29, 2014

Also, since we changed view_name to be private, we probably also ought to change model_name to be private (i.e., _model_name or something---I even prefer the shorter _model and _view)

closed = Bool(False)

keys = List()
def _keys_default(self):

This comment has been minimized.

Copy link
@jasongrout

jasongrout Jan 29, 2014

Member

So just to double-check this design decision---widget.keys is still the authoritative source for the syncing attributes, and setting the sync=True parameter just provides a convenient default? But we could just set widget.keys directly to change this behavior? (I had thought we were totally moving to the sync=True framework, but I do like the flexibility of manually overriding that architecture if I need to.)

This comment has been minimized.

Copy link
@ellisonbg

ellisonbg Jan 29, 2014

Member

They keys attribute should always be constructed automatically by looking at the sync trait metadata. The problem with allowing the setting of keys manually is that it messes up subclassing.

This comment has been minimized.

Copy link
@takluyver

takluyver Jan 29, 2014

Member

In that case, should keys be a property, rather than a traitlet?

This comment has been minimized.

Copy link
@jdfreder

jdfreder Jan 29, 2014

Author Member

You can still set it manually, any classes that subclass that class won't be able to rely on sync = True.

This comment has been minimized.

Copy link
@ellisonbg

ellisonbg Jan 29, 2014

Member

I think that breaking subclassing is always wrong, so we should make it a read only property? Thoughts?

This comment has been minimized.

Copy link
@jdfreder

jdfreder Jan 29, 2014

Author Member

@minrk are you okay with using a readonly property for this?

This comment has been minimized.

Copy link
@minrk

minrk Jan 29, 2014

Member

Sure, property is fine. Just make sure it's not evaluated on every request. I don't actually see any benefit to property, since there is really no such thing as read-only in Python.

This comment has been minimized.

Copy link
@jdfreder

jdfreder Jan 29, 2014

Author Member

I don't actually see any benefit to property, since there is really no such thing as read-only in Python.

I agree. I'm indifferent to the property or traitlet approach.

This comment has been minimized.

Copy link
@takluyver

takluyver Jan 29, 2014

Member

I think a traitlet implies that it makes sense to set it - a property suggests that you shouldn't try that, even though it's still technically possible.

This comment has been minimized.

Copy link
@jasongrout

jasongrout Jan 30, 2014

Member

Can't you have a readonly traitlet? Basically, the validator just rejects any attempts to set it?

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Jan 29, 2014

Thanks for pointing the naming issues out @jasongrout , I opened a PR to resolve them - #4933

@ChakriCherukuri

This comment has been minimized.

Copy link

commented Jan 30, 2014

Hi,
I just got the latest code from master and I am trying to create my custom widgets. One thing I noticed is that List() traitlet is no longer being mapped to an array on JS side. It is now mapped
into an object something like {0: v1, 1: v2, 2: v3, ...} as opposed to an array like [v1, v2, v3, ...]. It would be great if someone can look into this issue.

Thanks a lot,
Chakri

PS: I am a first time poster so please pardon my lack of sophistication in my comments. I just started playing with GIT, IPython widgets, Backbone etc.

@jasongrout

This comment has been minimized.

Copy link
Member

commented Jan 30, 2014

It looks like the javascript unpacker should be checking to see if the value is an instanceof Array before checking to see if it is an object here: https://github.com/ipython/ipython/blob/master/IPython/html/static/notebook/js/widgets/widget.js#L225 and

@jasongrout

This comment has been minimized.

Copy link
Member

commented Jan 30, 2014

(perhaps using jquery isArray is safer than trying to deal with the javascript type system in lots of different browsers: http://api.jquery.com/jquery.isarray/)

@jdfreder

This comment has been minimized.

Copy link
Member Author

commented Jan 31, 2014

Thanks for pointing this out guys, I will be gone tomorrow. I'll try to open a PR to fix this over the weekend.

@jasongrout

This comment has been minimized.

Copy link
Member

commented Feb 4, 2014

@jdfreder: not to get impatient, but I'm curious if there is progress on this. I know you guys have been doing a ton of work lately, and we really appreciate it!

@ellisonbg

This comment has been minimized.

Copy link
Member

commented Feb 4, 2014

Jason, can you open a new issue for us to track this one on? Thanks.

@jasongrout

This comment has been minimized.

Copy link
Member

commented Feb 4, 2014

Done: #5020

@damianavila damianavila referenced this pull request Feb 12, 2014

@jdfreder jdfreder deleted the jdfreder:widget-msg branch Mar 10, 2014

@jasongrout

This comment has been minimized.

Copy link
Member

commented on IPython/html/widgets/widget_float.py in 3644592 Sep 4, 2014

How does this work? Above, _validate is called when value, min, or max changes, but the function here assumes that new is the value. Also, the self.value itself is set in this function; does that create loops?

This comment has been minimized.

Copy link
Member Author

replied Sep 7, 2014

I think the assumption new==value is a bug. Reassigning the value in the changed handler doesn't cause loops because traitlets checks new!=old before triggering the event.

This comment has been minimized.

Copy link
Member Author

replied Sep 16, 2014

I just got around to investigating this closely. This is working most of the time because of dumb luck:

on_trait_change is called after the trait has been changed (which makes me think on_trait_change should actually be named on_trait_changed). That means the condition if self.min > new or new > self.max: never evaluates to True since in the case of name == 'min' self.min == new and in the case of name == 'max' new == self.max. Only in the case of name=value or when the new max is less than the min or the new min is greater than the max will it evaluate to True.

a = widgets.IntSlider()
display(a)
a.max = -1

will cause a crash and

a = widgets.IntSlider()
display(a)
a.min = 101

will cause a crash.

This comment has been minimized.

Copy link
Member Author

replied Sep 16, 2014

mattvonrocketstein pushed a commit to mattvonrocketstein/ipython that referenced this pull request Nov 3, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
9 participants
You can’t perform that action at this time.