Comments (35)
+1
from ijavascript.
I need to experiment further with this idea. The main "con" is that:
> Object.getOwnPropertyNames(util.inspect)
[ 'length',
'name',
'arguments',
'caller',
'prototype',
'colors',
'styles' ]
In the meantime, similar, experimental functionality is already available through
http://n-riesco.github.io/ijavascript/doc/mimer.ipynb.html
from ijavascript.
inspect
is something you define on your own objects though that works in Chrome too, you don't have to use Node's util.inspect
.
I must be missing what the con is.
from ijavascript.
> var obj = { inspect: function(){}}
undefined
> Object.getOwnPropertyNames(obj.inspect)
[ 'length',
'name',
'arguments',
'caller',
'prototype' ]
from ijavascript.
from ijavascript.
Ah, ok, that makes sense. How do you define these in a way that is natural for a JS programmer? I'm assuming we want these to be bound by the caller and I'm not sure how you do it for nested functions.
function X() {}
X.prototype.inspect.mime = {
'text/html': function html() {
// How does this get bound to the future `this`?
}
}
from ijavascript.
> X.prototype.inspect.mime['text/html'] = function html(){ return `<b>${this.x}</b>`}
[Function: html]
> new X(12)
ITS AN X: 12
> y = new X(12)
ITS AN X: 12
> y.inspect.mime['text/html']
[Function: html]
> y.inspect.mime['text/html']()
'<b>undefined</b>'
from ijavascript.
@rgbkrk How urgent is this issue for you? I was working on #58 , but if we agree on what to do for #59 , I could work on it right away.
from ijavascript.
This is not urgent, working on #58 is great. @jdfreder was interested in the JavaScript kernel and since I saw the thread going along I wanted to experiment.
from ijavascript.
Currently there's a compare and contrast with tonic dev and IJavascript in Plotly's plotly-notebook-js and I'd like to make IJavascript take off in the same way that IPython did via libraries like pandas
.
from ijavascript.
Here's an alternative using a getter:
function X(x) {
this.x = x;
}
X.prototype.inspect = function inspect() {
return "X = " + this.x;
};
X.prototype.inspect.mime = {
get "text/html"() {
return "<b>X = " + this.x + "</b>";
},
};
var x = new X(1);
x.inspect.mime["text/html"]; // output: '<b>X = undefined</b>'
The main con of using a getter is that we would give up passing arguments. For example, obj.inspect.mime["image/jpg"]("lossless")
(admittedly, not a terribly good example).
from ijavascript.
In Python at least, we never pass arguments to the _repr_html_
. It's supposed to be a function that takes nothing and returns a string that we can send over the wire.
from ijavascript.
Uhhhh, appears that getter doesn't work for us since it comes out undefined
. Tested locally as well.
from ijavascript.
Oh well... using X.prototype.inspect.mime
has an undesired side-effect. this
gets assigned to X.prototype.inspect.mime
instead of the X instance:
function X(x) {
this.x = x;
}
X.prototype.inspect = function inspect() {
return "X = " + this.x;
}
X.prototype.inspect.mime = {};
Object.defineProperties(X.prototype.inspect.mime, {
"text/html": { get: function html() {
return this === X.prototype.inspect.mime;
},
},
});
var x = new X(1);
x; // output: X = 1
Object.getOwnPropertyNames(x.inspect.mime); // output: [ 'text/html' ]
x.inspect.mime["text/html"]; // output: true
from ijavascript.
Something like the following would be more in the spirit of the global $$html$$
:
function X(x) {
this.x = x;
}
X.prototype.inspect = function inspect() {
return "X = " + this.x;
}
Object.defineProperty(X.prototype, "$$html$$", {
get: function html() {
return "<b>" + this.inspect() + "</b>";
},
});
var x = new X(1);
x; // output: X = 1
x.$$html$$; // output:<b>X = 1</b>
from ijavascript.
Considering how you have to send these across the wire, what if the interface is just:
X.prototype.inspectAsMimeBundle = function inspectAsMimeBundle() {
return {
'text/html': `<b>${this.x}</b>`,
'text/plain': this.inspect(),
}
}
from ijavascript.
Alternatively, or in addition to (since these are for your own use in calling from ijavascript) they can implement inspectAsMimetype
:
X.prototype.inspectAsMimetype = function inspectAsMimetype(mimetype) {
switch(mimetype) {
case 'text/html':
return `<b>X = ${this.x}</b>`
case 'text/plain':
default:
return this.inspect();
}
}
var x = new X(1);
console.log(x.inspectAsMimetype('text/html'));
from ijavascript.
In IJavascript, we also have the global $$mime$$
:
Object.defineProperty(X.prototype, "$$mime$$", {
get: function mime() {
return {
"text/plain": this.inspect(),
"text/html": this.$$html$$,
};
},
});
x.$$mime$$; //output: { 'text/plain': 'X = 1', 'text/html': '<b>X = 1</b>' }
from ijavascript.
The main reason I'm aiming for something similar to inspect is that it's a built in. My big repulsion to the global
from ijavascript.
Alternatively we could model the API closer to IPython's. The way this is done in IPython isvia reprs. Python itself uses x.__repr__
for the string representation of an object. IPython extends this by also looking for x._repr_*_
, i.e. x._repr_html_
...
In Javascript the equivalent of __repr__
is toString
, right? So why not toHTML
, toJavascript
etc. Or toMimeBundle
?
from ijavascript.
function A() {}
A.prototype.toString = function() { return 'b'; }
a = new A();
String(a)
"b"
from ijavascript.
At least in node and v8 (which are what this kernel is), inspect
is the equivalent to __repr__
(the analogue to toString
is __str__
). inspect
has been around at least as far as 0.8 (was easy to launch a docker image with it):
$ docker run -it node:0.8
> X = function(x) { this.x = x; }
[Function]
> X.prototype.inspect = function inspect(){ return 'x = ' + this.x; }
[Function: inspect]
> trial = new X(23)
x = 23
> trial
x = 23
from ijavascript.
I've put together a document with all the display conventions mentioned here. I think it'll help understand the current conventions in IJavascript:
Display Conventions
1. Javascript `toString()`
2. Node.js `inspect()`
3. Python `__repr__()`
4. IPython `__repr_*__()`
5. IJavascript
5.1. Using `util.inspect()` (except for functions)
5.2. Using globals: `$$mime$$`, `$$html$$`, ...
5.3. Extensions:
5.3.1. Using global `$$mimer$$()`
5.3.2. Using Node.js `inspect()`
5.3.3. Using IPython's `__repr_*__()`
5.3.4. Using getters: `$$mime$$`, `$$html$$`, ...
Display Conventions
Javascript toString()
In Javascript, a class can define its string representation by defining the method toString()
.
$ node
> function F() {}
undefined
> F.prototype.toString = function toString() { return "World!"; };
[Function: toString]
> "Hello, " + new F()
'Hello, World!'
See documentation here.
Node.js inspect()
Node.js introduced another convention. The string representation of an object in a Node.js shell is determined by the method inspect([depth])
.
$ node
> function F() {}
undefined
> F.prototype.inspect = function inspect() { return "Hello, World!"; };
[Function: inspect]
> new F()
Hello, World!
See documentation here.
Python __repr__()
Similarly to Javascript, Python uses the method __repr__()
to determine the string representation of an object.
See documentation here.
IPython __repr_*__()
And similarly to Node.js, the IPython shell introduces the methods __repr_*__()
to define rich representations of an object.
See documentation here.
IJavascript
The sections below describe how IJavascript displays objects, and discuss potential extensions.
Using util.inspect()
(except for functions)
With the exception of functions, IJavascript displays objects like Node.js does; i.e. by invoking util.inspect()
, which, in turn, invokes the method inspect()
of the object.
Functions, however, are displayed using the method toString()
. See the difference:
$ node
> function f() { return "Hello, World!"; }
undefined
> f.toString()
'function f() { return "Hello, World!"; }'
> util.inspect(f)
'[Function: f]'
Thus, in IJavascript, it is possible to define an alternative string representation of an object by defining the method inspect()
(or the method toString()
in case of functions).
Using globals: $$mime$$
, $$html$$
, ...
The Jupyter notebook accepts object representations in a number of MIME formats. To produce these MIME representations, IJavascript defines a convention in terms of the global variables: $$html$$
, $$svg$$
, $$png$$
, $$jpeg$$
and $$mime$$
.
See documentation here.
The choice of names was guided by:
- convenience: use of short names
- avoid collisions with global variables defined by other libraries: mangle names with
$$
Extensions:
In this thread, we're discussing new conventions so that it is possible to customise an object representation based on its class.
Using global $$mimer$$
The global $$mimer$$
was experimentally introduced in [email protected].
$$mimer$$
is the function that IJavascript uses to generate the MIME representation of an object. By making this function accessible, a library can redefine $$mimer$$
to produce a custom representation.
See here for some examples of use.
I want to deprecate this convention because of its cons:
- it isn't robust: a buggy custom
$$mimer$$
can break the custom$$mimer$$
of other packages - it is inefficient: it creates a chain of
$$mimer$$
, each checking whether an object is an instance of a given class.
Using Node.js inspect()
All the ideas we've discussed so far that extend the inspect()
convention have serious drawbacks:
X.prototype.inspect["text/html"]
:this
is set toX.prototype.inspect
- by default
X.prototype.inspect
defines property names other than "text/html"
X.prototype.mime["text/html"]
this
is set toX.prototype.inspect
Using getters: $$mime$$
, $$html$$
, ...
From the experience with X.prototype.inspect["text/html"]
and X.prototype.mime["text/html"]
, it is clear that for this
to be properly defined a method or a getter must be used to generate a rich representation of an object. Once this is agreed, the issue becomes a naming issue. The first choice that came to my mind was to use the names already used in the global variables. The pros are:
- this isn't a new convention; it's already used in the global variables.
- convenience: use of short names
- avoid naming collisions
Using IPython __repr_*__()
Jonathan suggested an alternative naming convention, inspired by IPython. The advantage I see is that:
- it is a convention familiar to IPython/Jupyter developers
The cons are:
- that to be consistent, we should rename the global variables too
- the names are long
from ijavascript.
Thank you so much for putting this together @n-riesco, this helped me understand the whole picture quite a bit more.
from ijavascript.
The major con to me of using _repr_*_
is that it should appeal to and be idiomatic for the kernel's language.
It's worth pointing out that the global variables do have an analogue to IPython as well, IPython.display.display(X)
and helpers like IPython.display.HTML('<b>test</b>')
.
from ijavascript.
This may be a silly question, but what's the issue with just using a prototype method that takes an argument?
$ node
> function X(n){
this.y = n; return this;
}
> X.prototype.inspectMime = function(t){
var types = {
'text/html': '<h1>' + this.y + '</h1>'
};
return types[t];
}
> var x = new X(100);
undefined
> x
X { y: 100 }
> x.inspectMime('text/html')
<h1>100</h1>
from ijavascript.
I like inspectMime
from ijavascript.
@mdtusz Kyle suggested something very similar here.
I feel wary of using unmangled names like inspectAs*()
. inspect()
is fine because it's a Node.js convention.
I think it's easier for IJavascript to introduce a convention with mangled names, that are unlikely to clash with other naming conventions.
from ijavascript.
I'm preparing a new release in the async-stdio
branch, here and here.
Apart from improving the handling of asynchronous output, it also includes an initial implementation of a convention to fix this issue: Class.prototype.inspect
, Class.prototype._toMime
, Class.prototype._toHtml
, Class.prototype._toSvg
, Class.prototype._toPng
and Class.prototype._toJpg
.
Here's an example:
In[1]:
function F(f) {
this.f = f;
}
F.prototype.inspect = function inspect() {
return "F { f: " + this.f + " }";
}
F.prototype._toHtml = function toHtml() {
return "<div style='background-color:yellow'>" + this.inspect() + "</div>";
}
new F("Hello, World!");
Out[1]:
{ mime:
{ 'text/plain': 'F { f: Hello, World! }',
'text/html': '<div style=\'background-color:yellow\'>F { f: Hello, World! }</div>' } }
from ijavascript.
Nice!
So to clarify, one of the mime
properties will be displayed as output then - presumably defaulting to text/html
if provided?
from ijavascript.
@mdtusz My understanding is that the frontend chooses amongst all the available. For example, the notebook prefers text/html
over text/plain
.
from ijavascript.
The frontend chooses once it gets the display_data
or execute_reply
message.
Example from Pandas:
The raw messages that the kernel (JavaScript in this case) sends over look like this:
{
content:
{ data:
{ 'text/plain': ' A B C D\n2000-01-01 1.153881 -0.462802 -1.304602 -0.112967\n2000-01-02 2.041548 0.193145 -2.154538 -0.026075\n2000-01-03 2.277393 -0.990363 -1.676727 0.463335\n2000-01-04 2.472088 0.376239 -2.691538 -0.329923\n2000-01-05 3.426450 -0.052412 -3.275203 -1.422521',
'text/html': '<div style="max-height:1000px;max-width:1500px;overflow:auto;">\n<table border="1" class="dataframe">\n <thead>\n <tr style="text-align: right;">\n <th></th>\n <th>A</th>\n <th>B</th>\n <th>C</th>\n <th>D</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>2000-01-01</th>\n <td> 1.153881</td>\n <td>-0.462802</td>\n <td>-1.304602</td>\n <td>-0.112967</td>\n </tr>\n <tr>\n <th>2000-01-02</th>\n <td> 2.041548</td>\n <td> 0.193145</td>\n <td>-2.154538</td>\n <td>-0.026075</td>\n </tr>\n <tr>\n <th>2000-01-03</th>\n <td> 2.277393</td>\n <td>-0.990363</td>\n <td>-1.676727</td>\n <td> 0.463335</td>\n </tr>\n <tr>\n <th>2000-01-04</th>\n <td> 2.472088</td>\n <td> 0.376239</td>\n <td>-2.691538</td>\n <td>-0.329923</td>\n </tr>\n <tr>\n <th>2000-01-05</th>\n <td> 3.426450</td>\n <td>-0.052412</td>\n <td>-3.275203</td>\n <td>-1.422521</td>\n </tr>\n </tbody>\n</table>\n</div>' },
execution_count: 21,
metadata: {} },
header:
{ username: 'kyle6475',
msg_id: 'e697b50a-9aa0-4d7a-a825-27ac99479679',
msg_type: 'execute_result',
version: '5.0',
date: '2015-07-26T15:52:59.473133',
session: 'c297c1c0-89bb-4ded-9720-d298c33b2595' },
parentHeader:
{ username: 'kyle6475',
msg_id: '841e2314-0af6-46c8-8083-d4b544f91e0e',
version: '5.0',
session: 'b4e75019-83e8-4bd4-99f5-e462d5203886',
date: '2015-07-26T15:52:59.468333',
msg_type: 'execute_request' },
metadata: {},
signature: '6c41e0ecbbb50010c26d8e04e8ff2c634816761f610b30ab1765539c216be591',
blobs: []
}
and a frontend will choose how to wrap those up accordingly
from ijavascript.
I've made a pre-release with the changes in the async-stdio
branch.
I will keep this issue open until I make the release.
If you have any more feedback that I can address before the release, please, keep posting here.
from ijavascript.
I've just released [email protected], which uses [email protected], and fixes this issue.
from ijavascript.
Awesome, I look forward to installing and using this.
from ijavascript.
Related Issues (20)
- Thoughts on how to run iJavaScript on mybinder? HOT 4
- No output when using %%javascript magic HOT 2
- Unable to run in Ubuntu WSL2 Visual Studio Code (`spawn ijskernel ENOENT`) HOT 2
- import / require statements break vscode code analysis HOT 2
- IJavaScript stopped working after upgrading the major version of node.js HOT 2
- Jupyter notebook (and Jupyter lab too) gives error when loading d3 library HOT 2
- Updated NVM version, got error "no such file or directory" ".../bin/node" HOT 2
- Add a rebuild command
- jupyterlite Support HOT 1
- Java script kernel stops working in Jupyter Lab HOT 3
- Is it still maintained HOT 4
- Cant install ijavascript (npm ERR! enoent spawn C:\Windows\system32\cmd.exe ENOENT) HOT 2
- ijsinstall: not working due to wrong use of ipython --user HOT 6
- SyntaxError: Cannot use import statement outside a module HOT 2
- install failed due to zeromq (Node v21) HOT 1
- npm install fails on macos Sonoma 14.4.1 HOT 3
- When I use npm to install the ijavascript kernel and switch to it in the Jupyter environment in VS Code, then switch back to the Python environment, the code suggestions are still those from the JS environment. How can I fix this? HOT 1
- npm warn deprecated [email protected]: Please upgrade to version 7 or higher. HOT 3
- JSON.stringify() wraps the output within ` (Grave Accent mark) HOT 1
- Instructions to install in macOS will fail when installing Python dependecies as described in the README HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ijavascript.