1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

feat: Add "deno jupyter" subcommand (#20337)

This commit adds "deno jupyter" subcommand which
provides a Deno kernel for Jupyter notebooks.

The implementation is mostly based on Deno's REPL and
reuses large parts of it (though there's some clean up that
needs to happen in follow up PRs). Not all functionality of
Jupyter kernel is implemented and some message type
are still not implemented (eg. "inspect_request") but
the kernel is fully working and provides all the capatibilities
that the Deno REPL has; including TypeScript transpilation
and npm packages support.

Closes https://github.com/denoland/deno/issues/13016

---------

Co-authored-by: Adam Powers <apowers@ato.ms>
Co-authored-by: Kyle Kelley <rgbkrk@gmail.com>
This commit is contained in:
Bartek Iwańczuk 2023-09-16 02:42:09 +02:00 committed by GitHub
parent 5a1505db67
commit bf07604113
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 2364 additions and 106 deletions

4
.gitignore vendored
View file

@ -32,3 +32,7 @@ gclient_config.py_entries
# JUnit files produced by deno test --junit
junit.xml
# Jupyter files
.ipynb_checkpoints/
Untitled*.ipynb

418
Cargo.lock generated
View file

@ -57,7 +57,7 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cipher",
"cpufeatures",
]
@ -85,13 +85,22 @@ dependencies = [
"aes",
]
[[package]]
name = "ahash"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
dependencies = [
"const-random",
]
[[package]]
name = "ahash"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"once_cell",
"version_check",
]
@ -301,6 +310,19 @@ dependencies = [
"syn 2.0.33",
]
[[package]]
name = "asynchronous-codec"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182"
dependencies = [
"bytes",
"futures-sink",
"futures-util",
"memchr",
"pin-project-lite",
]
[[package]]
name = "auto_impl"
version = "0.5.0"
@ -339,7 +361,7 @@ checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"cfg-if 1.0.0",
"libc",
"miniz_oxide",
"object",
@ -525,6 +547,12 @@ dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -645,6 +673,28 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
[[package]]
name = "const-random"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f590d95d011aa80b063ffe3253422ed5aa462af4e9867d43ce8337562bac77c4"
dependencies = [
"const-random-macro",
"proc-macro-hack",
]
[[package]]
name = "const-random-macro"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "615f6e27d000a2bffbc7f2f6a8669179378fa27ee4d0a509e985dfc0a7defb40"
dependencies = [
"getrandom 0.2.10",
"lazy_static",
"proc-macro-hack",
"tiny-keccak",
]
[[package]]
name = "convert_case"
version = "0.4.0"
@ -697,28 +747,107 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
name = "crossbeam"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e"
dependencies = [
"cfg-if 0.1.10",
"crossbeam-channel 0.4.4",
"crossbeam-deque",
"crossbeam-epoch",
"crossbeam-queue",
"crossbeam-utils 0.7.2",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.8"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
dependencies = [
"cfg-if",
"crossbeam-utils",
"crossbeam-utils 0.7.2",
"maybe-uninit",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils 0.8.10",
]
[[package]]
name = "crossbeam-deque"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils 0.7.2",
"maybe-uninit",
]
[[package]]
name = "crossbeam-epoch"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [
"autocfg",
"cfg-if 0.1.10",
"crossbeam-utils 0.7.2",
"lazy_static",
"maybe-uninit",
"memoffset 0.5.6",
"scopeguard",
]
[[package]]
name = "crossbeam-queue"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
dependencies = [
"cfg-if 0.1.10",
"crossbeam-utils 0.7.2",
"maybe-uninit",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"cfg-if",
"autocfg",
"cfg-if 0.1.10",
"lazy_static",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83"
dependencies = [
"cfg-if 1.0.0",
"once_cell",
]
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "crypto-bigint"
version = "0.4.9"
@ -782,7 +911,7 @@ version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622178105f911d937a42cdb140730ba4a3ed2becd8ae6ce39c7d28b5d75d4588"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cpufeatures",
"curve25519-dalek-derive",
"fiat-crypto",
@ -803,13 +932,24 @@ dependencies = [
"syn 2.0.33",
]
[[package]]
name = "dashmap"
version = "3.11.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f260e2fc850179ef410018660006951c1b55b79e8087e87111a2c388994b9b5"
dependencies = [
"ahash 0.3.8",
"cfg-if 0.1.10",
"num_cpus",
]
[[package]]
name = "dashmap"
version = "5.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"hashbrown 0.14.0",
"lock_api",
"once_cell",
@ -835,7 +975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
dependencies = [
"serde",
"uuid",
"uuid 1.4.1",
]
[[package]]
@ -846,12 +986,14 @@ dependencies = [
"base32",
"base64 0.13.1",
"bincode",
"bytes",
"cache_control",
"chrono",
"clap",
"clap_complete",
"clap_complete_fig",
"console_static_text",
"data-encoding",
"data-url",
"deno_ast",
"deno_bench_util",
@ -903,7 +1045,7 @@ dependencies = [
"pin-project",
"pretty_assertions",
"quick-junit",
"rand",
"rand 0.8.5",
"regex",
"ring",
"rustyline",
@ -919,16 +1061,17 @@ dependencies = [
"text_lines",
"thiserror",
"tokio",
"tokio-util",
"tokio-util 0.7.8",
"tower-lsp",
"trust-dns-client",
"trust-dns-server",
"twox-hash",
"typed-arena",
"uuid",
"uuid 1.4.1",
"walkdir",
"winapi",
"winres",
"zeromq",
"zstd",
]
@ -1012,7 +1155,7 @@ dependencies = [
"async-trait",
"deno_core",
"tokio",
"uuid",
"uuid 1.4.1",
]
[[package]]
@ -1117,7 +1260,7 @@ dependencies = [
"once_cell",
"p256 0.11.1",
"p384 0.11.2",
"rand",
"rand 0.8.5",
"ring",
"rsa",
"sec1 0.3.0",
@ -1128,7 +1271,7 @@ dependencies = [
"signature 1.6.4",
"spki 0.6.0",
"tokio",
"uuid",
"uuid 1.4.1",
"x25519-dalek",
]
@ -1138,7 +1281,7 @@ version = "0.66.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cc42f49e0aa338e438f59b8367c0ca73c789e9321bd6e1ee086d57733826190"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"deno_ast",
"deno_graph",
"futures",
@ -1180,7 +1323,7 @@ dependencies = [
"reqwest",
"serde",
"tokio",
"tokio-util",
"tokio-util 0.7.8",
]
[[package]]
@ -1211,7 +1354,7 @@ dependencies = [
"libc",
"log",
"nix 0.26.2",
"rand",
"rand 0.8.5",
"serde",
"tokio",
"winapi",
@ -1266,14 +1409,14 @@ dependencies = [
"percent-encoding",
"phf",
"pin-project",
"rand",
"rand 0.8.5",
"ring",
"serde",
"slab",
"smallvec",
"thiserror",
"tokio",
"tokio-util",
"tokio-util 0.7.8",
]
[[package]]
@ -1304,7 +1447,7 @@ dependencies = [
"num-bigint",
"prost",
"prost-build",
"rand",
"rand 0.8.5",
"reqwest",
"rusqlite",
"serde",
@ -1312,7 +1455,7 @@ dependencies = [
"termcolor",
"tokio",
"url",
"uuid",
"uuid 1.4.1",
]
[[package]]
@ -1422,7 +1565,7 @@ dependencies = [
"p384 0.13.0",
"path-clean",
"pbkdf2",
"rand",
"rand 0.8.5",
"regex",
"reqwest",
"ring",
@ -1529,7 +1672,7 @@ dependencies = [
"test_util",
"tokio",
"tokio-metrics",
"uuid",
"uuid 1.4.1",
"which",
"winapi",
"winres",
@ -1561,7 +1704,7 @@ dependencies = [
"os_pipe",
"path-dedot",
"tokio",
"tokio-util",
"tokio-util 0.7.8",
]
[[package]]
@ -1624,7 +1767,7 @@ dependencies = [
"futures",
"serde",
"tokio",
"uuid",
"uuid 1.4.1",
"windows-sys",
]
@ -1993,7 +2136,7 @@ version = "0.8.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -2014,6 +2157,17 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "enum-primitive-derive"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c375b9c5eadb68d0a6efee2999fef292f45854c3444c86f09d8ab086ba942b0e"
dependencies = [
"num-traits",
"quote 1.0.33",
"syn 1.0.109",
]
[[package]]
name = "env_logger"
version = "0.10.0"
@ -2142,7 +2296,7 @@ dependencies = [
"base64 0.21.4",
"hyper 0.14.27",
"pin-project",
"rand",
"rand 0.8.5",
"sha1",
"simdutf8",
"thiserror",
@ -2156,7 +2310,7 @@ version = "3.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"rustix",
"windows-sys",
]
@ -2193,7 +2347,7 @@ version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"redox_syscall 0.3.5",
"windows-sys",
@ -2420,7 +2574,7 @@ version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
@ -2431,7 +2585,7 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
]
@ -2504,7 +2658,7 @@ dependencies = [
"indexmap 1.9.3",
"slab",
"tokio",
"tokio-util",
"tokio-util 0.7.8",
"tracing",
]
@ -2520,7 +2674,7 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
dependencies = [
"ahash",
"ahash 0.8.3",
"allocator-api2",
]
@ -2764,7 +2918,7 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632089ec08bd62e807311104122fb26d5c911ab172e2b9864be154a575979e29"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"indexmap 1.9.3",
"log",
"serde",
@ -2830,7 +2984,7 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -3010,7 +3164,7 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"winapi",
]
@ -3118,6 +3272,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "md-5"
version = "0.10.5"
@ -3157,6 +3317,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15"
[[package]]
name = "memoffset"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
dependencies = [
"autocfg",
]
[[package]]
name = "memoffset"
version = "0.7.1"
@ -3275,7 +3444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"cfg-if 1.0.0",
"libc",
]
@ -3286,9 +3455,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"cfg-if 1.0.0",
"libc",
"memoffset",
"memoffset 0.7.1",
"pin-utils",
"static_assertions",
]
@ -3310,7 +3479,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2c66da08abae1c024c01d635253e402341b4060a12e99b31c7594063bf490a"
dependencies = [
"bitflags 1.3.2",
"crossbeam-channel",
"crossbeam-channel 0.5.5",
"filetime",
"fsevent-sys",
"inotify",
@ -3339,7 +3508,7 @@ dependencies = [
"autocfg",
"num-integer",
"num-traits",
"rand",
"rand 0.8.5",
"serde",
]
@ -3355,7 +3524,7 @@ dependencies = [
"num-integer",
"num-iter",
"num-traits",
"rand",
"rand 0.8.5",
"serde",
"smallvec",
"zeroize",
@ -3548,7 +3717,7 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall 0.2.16",
@ -3562,7 +3731,7 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"redox_syscall 0.3.5",
"smallvec",
@ -3663,7 +3832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
dependencies = [
"phf_shared",
"rand",
"rand 0.8.5",
]
[[package]]
@ -3782,7 +3951,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cpufeatures",
"opaque-debug",
"universal-hash",
@ -3978,7 +4147,7 @@ dependencies = [
"nextest-workspace-hack",
"quick-xml",
"thiserror",
"uuid",
"uuid 1.4.1",
]
[[package]]
@ -4024,6 +4193,19 @@ dependencies = [
"nibble_vec",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom 0.1.16",
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc",
]
[[package]]
name = "rand"
version = "0.8.5"
@ -4031,10 +4213,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core 0.5.1",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
@ -4063,6 +4255,15 @@ dependencies = [
"getrandom 0.2.10",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
@ -4148,7 +4349,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"tokio-socks",
"tokio-util",
"tokio-util 0.7.8",
"tower-service",
"url",
"wasm-bindgen",
@ -4357,7 +4558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1cd5ae51d3f7bf65d7969d579d502168ef578f289452bd8ccc91de28fda20e"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"cfg-if 1.0.0",
"clipboard-win",
"fd-lock",
"libc",
@ -4484,7 +4685,7 @@ version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f"
dependencies = [
"rand",
"rand 0.8.5",
"secp256k1-sys",
]
@ -4637,7 +4838,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cpufeatures",
"digest 0.10.7",
]
@ -4648,7 +4849,7 @@ version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cpufeatures",
"digest 0.10.7",
]
@ -4659,7 +4860,7 @@ version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cpufeatures",
"digest 0.10.7",
]
@ -4828,7 +5029,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce"
dependencies = [
"cc",
"cfg-if",
"cfg-if 1.0.0",
"libc",
"psm",
"winapi",
@ -4971,7 +5172,7 @@ checksum = "39cb7fcd56655c8ae7dcf2344f0be6cbff4d9c7cb401fe3ec8e56e1de8dfe582"
dependencies = [
"ast_node",
"better_scoped_tls",
"cfg-if",
"cfg-if 1.0.0",
"either",
"from_variant",
"new_debug_unreachable",
@ -5165,7 +5366,7 @@ version = "0.192.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "880fd2a588ac88a61cd1d21b10203bbabe31d7adacbd22de3bb4d702bf2c42b4"
dependencies = [
"dashmap",
"dashmap 5.5.3",
"indexmap 1.9.3",
"once_cell",
"petgraph",
@ -5210,7 +5411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "675b5c755b0448268830e85e59429095d3423c0ce4a850b209c6f0eeab069f63"
dependencies = [
"base64 0.13.1",
"dashmap",
"dashmap 5.5.3",
"indexmap 1.9.3",
"once_cell",
"serde",
@ -5410,7 +5611,7 @@ version = "3.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"fastrand",
"redox_syscall 0.3.5",
"rustix",
@ -5548,6 +5749,15 @@ dependencies = [
"time-core",
]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
@ -5638,6 +5848,21 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507"
dependencies = [
"bytes",
"futures-core",
"futures-io",
"futures-sink",
"log",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.8"
@ -5707,7 +5932,7 @@ dependencies = [
"async-trait",
"auto_impl 0.5.0",
"bytes",
"dashmap",
"dashmap 5.5.3",
"futures",
"httparse",
"log",
@ -5716,7 +5941,7 @@ dependencies = [
"serde",
"serde_json",
"tokio",
"tokio-util",
"tokio-util 0.7.8",
"tower",
"tower-lsp-macros",
]
@ -5744,7 +5969,7 @@ version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@ -5786,13 +6011,13 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c408c32e6a9dbb38037cece35740f2cf23c875d8ca134d33631cec83f74d3fe"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"data-encoding",
"futures-channel",
"futures-util",
"lazy_static",
"radix_trie",
"rand",
"rand 0.8.5",
"thiserror",
"time",
"tokio",
@ -5807,7 +6032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26"
dependencies = [
"async-trait",
"cfg-if",
"cfg-if 1.0.0",
"data-encoding",
"enum-as-inner",
"futures-channel",
@ -5816,7 +6041,7 @@ dependencies = [
"idna 0.2.3",
"ipnet",
"lazy_static",
"rand",
"rand 0.8.5",
"serde",
"smallvec",
"thiserror",
@ -5832,7 +6057,7 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"futures-util",
"ipconfig",
"lazy_static",
@ -5855,7 +6080,7 @@ checksum = "99022f9befa6daec2a860be68ac28b1f0d9d7ccf441d8c5a695e35a58d88840d"
dependencies = [
"async-trait",
"bytes",
"cfg-if",
"cfg-if 1.0.0",
"enum-as-inner",
"futures-executor",
"futures-util",
@ -5881,8 +6106,8 @@ version = "1.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
dependencies = [
"cfg-if",
"rand",
"cfg-if 1.0.0",
"rand 0.8.5",
"static_assertions",
]
@ -6052,6 +6277,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uuid"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
dependencies = [
"getrandom 0.2.10",
]
[[package]]
name = "uuid"
version = "1.4.1"
@ -6151,7 +6385,7 @@ version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"wasm-bindgen-macro",
]
@ -6176,7 +6410,7 @@ version = "0.4.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
@ -6389,7 +6623,7 @@ version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"windows-sys",
]
@ -6466,6 +6700,32 @@ dependencies = [
"syn 2.0.33",
]
[[package]]
name = "zeromq"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "667ece59294ccaf617fcf2e5decc9114a06427c1f68990028b9f12d322686bdc"
dependencies = [
"async-trait",
"asynchronous-codec",
"bytes",
"crossbeam",
"dashmap 3.11.10",
"enum-primitive-derive",
"futures",
"futures-util",
"lazy_static",
"log",
"num-traits",
"parking_lot 0.11.2",
"rand 0.7.3",
"regex",
"thiserror",
"tokio",
"tokio-util 0.6.10",
"uuid 0.8.2",
]
[[package]]
name = "zstd"
version = "0.12.4"

View file

@ -85,6 +85,7 @@ cbc = { version = "=0.1.2", features = ["alloc"] }
chrono = { version = "0.4", default-features = false, features = ["std", "serde", "clock"] }
console_static_text = "=0.8.1"
data-url = "=0.3.0"
data-encoding = "2.3.3"
dlopen = "0.1.8"
encoding_rs = "=0.8.33"
ecb = "=0.1.2"

View file

@ -65,12 +65,14 @@ async-trait.workspace = true
base32 = "=0.4.0"
base64.workspace = true
bincode = "=1.3.3"
bytes.workspace = true
cache_control.workspace = true
chrono.workspace = true
clap = { version = "=4.3.3", features = ["string"] }
clap_complete = "=4.3.1"
clap_complete_fig = "=4.3.1"
console_static_text.workspace = true
data-encoding.workspace = true
data-url.workspace = true
dissimilar = "=1.0.4"
dprint-plugin-json = "=0.17.4"
@ -120,6 +122,7 @@ twox-hash = "=1.6.3"
typed-arena = "=2.0.1"
uuid = { workspace = true, features = ["serde"] }
walkdir = "=2.3.2"
zeromq = { version = "=0.3.3", default-features = false, features = ["tcp-transport", "tokio-runtime"] }
zstd.workspace = true
[target.'cfg(windows)'.dependencies]

View file

@ -158,6 +158,13 @@ pub struct InstallFlags {
pub force: bool,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct JupyterFlags {
pub install: bool,
pub kernel: bool,
pub conn_file: Option<PathBuf>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct UninstallFlags {
pub name: String,
@ -276,6 +283,7 @@ pub enum DenoSubcommand {
Init(InitFlags),
Info(InfoFlags),
Install(InstallFlags),
Jupyter(JupyterFlags),
Uninstall(UninstallFlags),
Lsp,
Lint(LintFlags),
@ -678,7 +686,8 @@ impl Flags {
std::env::current_dir().ok()
}
Bundle(_) | Completions(_) | Doc(_) | Fmt(_) | Init(_) | Install(_)
| Uninstall(_) | Lsp | Lint(_) | Types | Upgrade(_) | Vendor(_) => None,
| Uninstall(_) | Jupyter(_) | Lsp | Lint(_) | Types | Upgrade(_)
| Vendor(_) => None,
}
}
@ -818,6 +827,7 @@ pub fn flags_from_vec(args: Vec<String>) -> clap::error::Result<Flags> {
"init" => init_parse(&mut flags, &mut m),
"info" => info_parse(&mut flags, &mut m),
"install" => install_parse(&mut flags, &mut m),
"jupyter" => jupyter_parse(&mut flags, &mut m),
"lint" => lint_parse(&mut flags, &mut m),
"lsp" => lsp_parse(&mut flags, &mut m),
"repl" => repl_parse(&mut flags, &mut m),
@ -919,6 +929,7 @@ fn clap_root() -> Command {
.subcommand(init_subcommand())
.subcommand(info_subcommand())
.subcommand(install_subcommand())
.subcommand(jupyter_subcommand())
.subcommand(uninstall_subcommand())
.subcommand(lsp_subcommand())
.subcommand(lint_subcommand())
@ -1613,6 +1624,33 @@ These must be added to the path manually if required.")
)
}
fn jupyter_subcommand() -> Command {
Command::new("jupyter")
.arg(
Arg::new("install")
.long("install")
.help("Installs kernelspec, requires 'jupyter' command to be available.")
.conflicts_with("kernel")
.action(ArgAction::SetTrue)
)
.arg(
Arg::new("kernel")
.long("kernel")
.help("Start the kernel")
.conflicts_with("install")
.requires("conn")
.action(ArgAction::SetTrue)
)
.arg(
Arg::new("conn")
.long("conn")
.help("Path to JSON file describing connection parameters, provided by Jupyter")
.value_parser(value_parser!(PathBuf))
.value_hint(ValueHint::FilePath)
.conflicts_with("install"))
.about("Deno kernel for Jupyter notebooks")
}
fn uninstall_subcommand() -> Command {
Command::new("uninstall")
.about("Uninstall a script previously installed with deno install")
@ -3166,6 +3204,18 @@ fn install_parse(flags: &mut Flags, matches: &mut ArgMatches) {
});
}
fn jupyter_parse(flags: &mut Flags, matches: &mut ArgMatches) {
let conn_file = matches.remove_one::<PathBuf>("conn");
let kernel = matches.get_flag("kernel");
let install = matches.get_flag("install");
flags.subcommand = DenoSubcommand::Jupyter(JupyterFlags {
install,
kernel,
conn_file,
});
}
fn uninstall_parse(flags: &mut Flags, matches: &mut ArgMatches) {
let root = matches.remove_one::<PathBuf>("root");
@ -7829,4 +7879,69 @@ mod tests {
}
);
}
#[test]
fn jupyter() {
let r = flags_from_vec(svec!["deno", "jupyter", "--unstable"]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Jupyter(JupyterFlags {
install: false,
kernel: false,
conn_file: None,
}),
unstable: true,
..Flags::default()
}
);
let r = flags_from_vec(svec!["deno", "jupyter", "--unstable", "--install"]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Jupyter(JupyterFlags {
install: true,
kernel: false,
conn_file: None,
}),
unstable: true,
..Flags::default()
}
);
let r = flags_from_vec(svec![
"deno",
"jupyter",
"--unstable",
"--kernel",
"--conn",
"path/to/conn/file"
]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Jupyter(JupyterFlags {
install: false,
kernel: true,
conn_file: Some(PathBuf::from("path/to/conn/file")),
}),
unstable: true,
..Flags::default()
}
);
let r = flags_from_vec(svec![
"deno",
"jupyter",
"--install",
"--conn",
"path/to/conn/file"
]);
r.unwrap_err();
let r = flags_from_vec(svec!["deno", "jupyter", "--kernel",]);
r.unwrap_err();
let r = flags_from_vec(svec!["deno", "jupyter", "--install", "--kernel",]);
r.unwrap_err();
}
}

View file

@ -134,6 +134,9 @@ async fn run_subcommand(flags: Flags) -> Result<i32, AnyError> {
DenoSubcommand::Install(install_flags) => spawn_subcommand(async {
tools::installer::install_command(flags, install_flags).await
}),
DenoSubcommand::Jupyter(jupyter_flags) => spawn_subcommand(async {
tools::jupyter::kernel(flags, jupyter_flags).await
}),
DenoSubcommand::Uninstall(uninstall_flags) => spawn_subcommand(async {
tools::installer::uninstall(uninstall_flags.name, uninstall_flags.root)
}),

View file

@ -330,7 +330,10 @@ impl CliModuleLoaderFactory {
lib_window: options.ts_type_lib_window(),
lib_worker: options.ts_type_lib_worker(),
is_inspecting: options.is_inspecting(),
is_repl: matches!(options.sub_command(), DenoSubcommand::Repl(_)),
is_repl: matches!(
options.sub_command(),
DenoSubcommand::Repl(_) | DenoSubcommand::Jupyter(_)
),
prepared_module_loader: PreparedModuleLoader {
emitter,
graph_container: graph_container.clone(),

View file

@ -0,0 +1,620 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"id": "182aef1d",
"metadata": {},
"source": [
"# Integration Tests for Deno Jupyter\n",
"This notebook contains a number of tests to ensure that Jupyter is working as expected. You should be able to select \"Kernel -> Restart Kernel and Run All\" in Jupyter's notebook UI to run the tests."
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d7705d88",
"metadata": {},
"source": [
"## Passing Tests"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "669f972e",
"metadata": {
"heading_collapsed": true
},
"source": [
"### Simple Tests"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "e7e8a512",
"metadata": {
"hidden": true
},
"source": [
"#### This test should print \"hi\".\n",
"If this doesn't work, everything else will probably fail :)"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "a5d38758",
"metadata": {
"hidden": true
},
"outputs": [
{
"data": {},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"hi\n"
]
}
],
"source": [
"console.log(\"hi\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "bc5ce8e3",
"metadata": {
"hidden": true
},
"source": [
"#### Top-level await"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "f7fa885a",
"metadata": {
"hidden": true
},
"outputs": [
{
"data": {},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"x is \u001b[33m42\u001b[39m\n"
]
}
],
"source": [
"let x = await Promise.resolve(42);\n",
"console.log(\"x is\", x);"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "c21455ae",
"metadata": {
"hidden": true
},
"source": [
"#### TypeScript transpiling\n",
"Credit to [typescriptlang.org](https://www.typescriptlang.org/docs/handbook/interfaces.html) for this code"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "08a17340",
"metadata": {
"hidden": true
},
"outputs": [
{
"data": {
"text/plain": [
"{ color: \u001b[32m\"red\"\u001b[39m, area: \u001b[33m10000\u001b[39m }"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"interface SquareConfig {\n",
" color?: string;\n",
" width?: number;\n",
"}\n",
" \n",
"function createSquare(config: SquareConfig): { color: string; area: number } {\n",
" return {\n",
" color: config.color || \"red\",\n",
" area: config.width ? config.width * config.width : 20,\n",
" };\n",
"}\n",
" \n",
"createSquare({ colour: \"red\", width: 100 });"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "eaa0ebc0",
"metadata": {
"heading_collapsed": true
},
"source": [
"### Return Values"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "52876276",
"metadata": {
"hidden": true
},
"source": [
"#### undefined should not return a value"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "bbf2c09b",
"metadata": {
"hidden": true
},
"outputs": [
{
"data": {},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"undefined"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "e175c803",
"metadata": {
"hidden": true
},
"source": [
"#### null should return \"null\""
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "d9801d80",
"metadata": {
"hidden": true
},
"outputs": [
{
"data": {
"text/plain": [
"\u001b[1mnull\u001b[22m"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"null"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "a2a716dc",
"metadata": {
"hidden": true
},
"source": [
"#### boolean should return the boolean"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "cfaac330",
"metadata": {
"hidden": true
},
"outputs": [
{
"data": {
"text/plain": [
"\u001b[33mtrue\u001b[39m"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"true"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "8d9f1aba",
"metadata": {
"hidden": true
},
"source": [
"#### number should return the number"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "ec3be2da",
"metadata": {
"hidden": true
},
"outputs": [
{
"data": {
"text/plain": [
"\u001b[33m42\u001b[39m"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"42"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "60965915",
"metadata": {
"hidden": true
},
"source": [
"#### string should return the string"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "997cf2d7",
"metadata": {
"hidden": true
},
"outputs": [
{
"data": {
"text/plain": [
"\u001b[32m\"this is a test of the emergency broadcast system\"\u001b[39m"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"\"this is a test of the emergency broadcast system\""
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "fe38dc27",
"metadata": {
"hidden": true
},
"source": [
"#### bigint should return the bigint in literal format"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "44b63807",
"metadata": {
"hidden": true
},
"outputs": [
{
"data": {
"text/plain": [
"\u001b[33m31337n\u001b[39m"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"31337n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "843ccb6c",
"metadata": {
"hidden": true
},
"source": [
"#### symbol should return a string describing the symbol"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "e10c0d31",
"metadata": {
"hidden": true
},
"outputs": [
{
"data": {
"text/plain": [
"\u001b[32mSymbol(foo)\u001b[39m"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Symbol(\"foo\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "171b817f",
"metadata": {
"hidden": true
},
"source": [
"#### object should describe the object inspection"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "81c99233",
"metadata": {
"hidden": true
},
"outputs": [
{
"data": {
"text/plain": [
"{ foo: \u001b[32m\"bar\"\u001b[39m }"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"{foo: \"bar\"}"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "6caeb583",
"metadata": {
"hidden": true
},
"source": [
"#### resolve returned promise"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "43c1581b",
"metadata": {
"hidden": true
},
"outputs": [
{
"data": {
"text/plain": [
"Promise { \u001b[32m\"it worked!\"\u001b[39m }"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Promise.resolve(\"it worked!\")"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "9a34b725",
"metadata": {
"hidden": true
},
"outputs": [
{
"data": {
"text/plain": [
"Promise {\n",
" \u001b[36m<rejected>\u001b[39m Error: it failed!\n",
" at <anonymous>:2:16\n",
"}"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Promise.reject(new Error(\"it failed!\"));"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "b5c7b819",
"metadata": {
"scrolled": true
},
"outputs": [
{
"ename": "Error: this is a test\n at foo (<anonymous>:3:9)\n at <anonymous>:4:3",
"evalue": "",
"output_type": "error",
"traceback": []
}
],
"source": [
"(function foo() {\n",
" throw new Error(\"this is a test\")\n",
"})()"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "72d01fdd",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Promise {\n",
" \u001b[36m<rejected>\u001b[39m TypeError: Expected string at position 0\n",
" at Object.readFile (ext:deno_fs/30_fs.js:716:29)\n",
" at <anonymous>:2:6\n",
"}"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Deno.readFile(1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "28cf59d0-6908-4edc-bb10-c325beeee362",
"metadata": {},
"outputs": [],
"source": [
"console.log(\"Hello from Deno!\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8d5485c3-0da3-43fe-8ef5-a61e672f5e81",
"metadata": {},
"outputs": [],
"source": [
"console.log(\"%c Hello Deno \", \"background-color: #15803d; color: white;\");"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1401d9d5-6994-4c7b-b55a-db3c16a1e2dc",
"metadata": {},
"outputs": [],
"source": [
"\"Cool 🫡\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7afdaa0a-a2a0-4f52-8c7d-b6c5f237aa0d",
"metadata": {},
"outputs": [],
"source": [
"console.table([1, 2, 3])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8e93df23-06eb-414b-98d4-51fbebb53d1f",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Deno",
"language": "typescript",
"name": "deno"
},
"language_info": {
"file_extension": ".ts",
"mimetype": "text/x.typescript",
"name": "typescript",
"nb_converter": "script",
"pygments_lexer": "typescript",
"version": "5.2.2"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": true
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View file

@ -0,0 +1,95 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_core::serde_json::json;
use std::env::current_exe;
use std::io::Write;
use std::path::Path;
use tempfile::TempDir;
const DENO_ICON_32: &[u8] = include_bytes!("./resources/deno-logo-32x32.png");
const DENO_ICON_64: &[u8] = include_bytes!("./resources/deno-logo-64x64.png");
pub fn status() -> Result<(), AnyError> {
let output = std::process::Command::new("jupyter")
.args(["kernelspec", "list", "--json"])
.output()
.context("Failed to get list of installed kernelspecs")?;
let json_output: serde_json::Value =
serde_json::from_slice(&output.stdout)
.context("Failed to parse JSON from kernelspec list")?;
if let Some(specs) = json_output.get("kernelspecs") {
if let Some(specs_obj) = specs.as_object() {
if specs_obj.contains_key("deno") {
println!("✅ Deno kernel already installed");
return Ok(());
}
}
}
println!(" Deno kernel is not yet installed, run `deno jupyter --unstable --install` to set it up");
Ok(())
}
fn install_icon(
dir_path: &Path,
filename: &str,
icon_data: &[u8],
) -> Result<(), AnyError> {
let path = dir_path.join(filename);
let mut file = std::fs::File::create(path)?;
file.write_all(icon_data)?;
Ok(())
}
pub fn install() -> Result<(), AnyError> {
let temp_dir = TempDir::new().unwrap();
let kernel_json_path = temp_dir.path().join("kernel.json");
// TODO(bartlomieju): add remaining fields as per
// https://jupyter-client.readthedocs.io/en/stable/kernels.html#kernel-specs
// FIXME(bartlomieju): replace `current_exe` before landing?
let json_data = json!({
"argv": [current_exe().unwrap().to_string_lossy(), "--unstable", "jupyter", "--kernel", "--conn", "{connection_file}"],
"display_name": "Deno",
"language": "typescript",
});
let f = std::fs::File::create(kernel_json_path)?;
serde_json::to_writer_pretty(f, &json_data)?;
install_icon(temp_dir.path(), "icon-32x32.png", DENO_ICON_32)?;
install_icon(temp_dir.path(), "icon-64x64.png", DENO_ICON_64)?;
let child_result = std::process::Command::new("jupyter")
.args([
"kernelspec",
"install",
"--user",
"--name",
"deno",
&temp_dir.path().to_string_lossy(),
])
.spawn();
if let Ok(mut child) = child_result {
let wait_result = child.wait();
match wait_result {
Ok(status) => {
if !status.success() {
bail!("Failed to install kernelspec, try again.");
}
}
Err(err) => {
bail!("Failed to install kernelspec: {}", err);
}
}
}
let _ = std::fs::remove_dir(temp_dir);
println!("✅ Deno kernelspec installed successfully.");
Ok(())
}

View file

@ -0,0 +1,268 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This file is forked/ported from <https://github.com/evcxr/evcxr>
// Copyright 2020 The Evcxr Authors. MIT license.
use bytes::Bytes;
use chrono::Utc;
use data_encoding::HEXLOWER;
use deno_core::anyhow::anyhow;
use deno_core::anyhow::bail;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_core::serde_json::json;
use ring::hmac;
use std::fmt;
use uuid::Uuid;
pub(crate) struct Connection<S> {
pub(crate) socket: S,
/// Will be None if our key was empty (digest authentication disabled).
pub(crate) mac: Option<hmac::Key>,
}
impl<S: zeromq::Socket> Connection<S> {
pub(crate) fn new(socket: S, key: &str) -> Self {
let mac = if key.is_empty() {
None
} else {
Some(hmac::Key::new(hmac::HMAC_SHA256, key.as_bytes()))
};
Connection { socket, mac }
}
}
struct RawMessage {
zmq_identities: Vec<Bytes>,
jparts: Vec<Bytes>,
}
impl RawMessage {
pub(crate) async fn read<S: zeromq::SocketRecv>(
connection: &mut Connection<S>,
) -> Result<RawMessage, AnyError> {
Self::from_multipart(connection.socket.recv().await?, connection)
}
pub(crate) fn from_multipart<S>(
multipart: zeromq::ZmqMessage,
connection: &Connection<S>,
) -> Result<RawMessage, AnyError> {
let delimiter_index = multipart
.iter()
.position(|part| &part[..] == DELIMITER)
.ok_or_else(|| anyhow!("Missing delimiter"))?;
let mut parts = multipart.into_vec();
let jparts: Vec<_> = parts.drain(delimiter_index + 2..).collect();
let expected_hmac = parts.pop().unwrap();
// Remove delimiter, so that what's left is just the identities.
parts.pop();
let zmq_identities = parts;
let raw_message = RawMessage {
zmq_identities,
jparts,
};
if let Some(key) = &connection.mac {
let sig = HEXLOWER.decode(&expected_hmac)?;
let mut msg = Vec::new();
for part in &raw_message.jparts {
msg.extend(part);
}
if let Err(err) = hmac::verify(key, msg.as_ref(), sig.as_ref()) {
bail!("{}", err);
}
}
Ok(raw_message)
}
async fn send<S: zeromq::SocketSend>(
self,
connection: &mut Connection<S>,
) -> Result<(), AnyError> {
let hmac = if let Some(key) = &connection.mac {
let ctx = self.digest(key);
let tag = ctx.sign();
HEXLOWER.encode(tag.as_ref())
} else {
String::new()
};
let mut parts: Vec<bytes::Bytes> = Vec::new();
for part in &self.zmq_identities {
parts.push(part.to_vec().into());
}
parts.push(DELIMITER.into());
parts.push(hmac.as_bytes().to_vec().into());
for part in &self.jparts {
parts.push(part.to_vec().into());
}
// ZmqMessage::try_from only fails if parts is empty, which it never
// will be here.
let message = zeromq::ZmqMessage::try_from(parts).unwrap();
connection.socket.send(message).await?;
Ok(())
}
fn digest(&self, mac: &hmac::Key) -> hmac::Context {
let mut hmac_ctx = hmac::Context::with_key(mac);
for part in &self.jparts {
hmac_ctx.update(part);
}
hmac_ctx
}
}
#[derive(Clone)]
pub(crate) struct JupyterMessage {
zmq_identities: Vec<Bytes>,
header: serde_json::Value,
parent_header: serde_json::Value,
metadata: serde_json::Value,
content: serde_json::Value,
}
const DELIMITER: &[u8] = b"<IDS|MSG>";
impl JupyterMessage {
pub(crate) async fn read<S: zeromq::SocketRecv>(
connection: &mut Connection<S>,
) -> Result<JupyterMessage, AnyError> {
Self::from_raw_message(RawMessage::read(connection).await?)
}
fn from_raw_message(
raw_message: RawMessage,
) -> Result<JupyterMessage, AnyError> {
if raw_message.jparts.len() < 4 {
bail!("Insufficient message parts {}", raw_message.jparts.len());
}
Ok(JupyterMessage {
zmq_identities: raw_message.zmq_identities,
header: serde_json::from_slice(&raw_message.jparts[0])?,
parent_header: serde_json::from_slice(&raw_message.jparts[1])?,
metadata: serde_json::from_slice(&raw_message.jparts[2])?,
content: serde_json::from_slice(&raw_message.jparts[3])?,
})
}
pub(crate) fn message_type(&self) -> &str {
self.header["msg_type"].as_str().unwrap_or("")
}
pub(crate) fn code(&self) -> &str {
self.content["code"].as_str().unwrap_or("")
}
pub(crate) fn cursor_pos(&self) -> usize {
self.content["cursor_pos"].as_u64().unwrap_or(0) as usize
}
pub(crate) fn comm_id(&self) -> &str {
self.content["comm_id"].as_str().unwrap_or("")
}
// Creates a new child message of this message. ZMQ identities are not transferred.
pub(crate) fn new_message(&self, msg_type: &str) -> JupyterMessage {
let mut header = self.header.clone();
header["msg_type"] = serde_json::Value::String(msg_type.to_owned());
header["username"] = serde_json::Value::String("kernel".to_owned());
header["msg_id"] = serde_json::Value::String(Uuid::new_v4().to_string());
header["date"] = serde_json::Value::String(Utc::now().to_rfc3339());
JupyterMessage {
zmq_identities: Vec::new(),
header,
parent_header: self.header.clone(),
metadata: json!({}),
content: json!({}),
}
}
// Creates a reply to this message. This is a child with the message type determined
// automatically by replacing "request" with "reply". ZMQ identities are transferred.
pub(crate) fn new_reply(&self) -> JupyterMessage {
let mut reply =
self.new_message(&self.message_type().replace("_request", "_reply"));
reply.zmq_identities = self.zmq_identities.clone();
reply
}
#[must_use = "Need to send this message for it to have any effect"]
pub(crate) fn comm_close_message(&self) -> JupyterMessage {
self.new_message("comm_close").with_content(json!({
"comm_id": self.comm_id()
}))
}
pub(crate) fn with_content(
mut self,
content: serde_json::Value,
) -> JupyterMessage {
self.content = content;
self
}
pub(crate) async fn send<S: zeromq::SocketSend>(
&self,
connection: &mut Connection<S>,
) -> Result<(), AnyError> {
// If performance is a concern, we can probably avoid the clone and to_vec calls with a bit
// of refactoring.
let raw_message = RawMessage {
zmq_identities: self.zmq_identities.clone(),
jparts: vec![
serde_json::to_string(&self.header)
.unwrap()
.as_bytes()
.to_vec()
.into(),
serde_json::to_string(&self.parent_header)
.unwrap()
.as_bytes()
.to_vec()
.into(),
serde_json::to_string(&self.metadata)
.unwrap()
.as_bytes()
.to_vec()
.into(),
serde_json::to_string(&self.content)
.unwrap()
.as_bytes()
.to_vec()
.into(),
],
};
raw_message.send(connection).await
}
}
impl fmt::Debug for JupyterMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"\nHeader: {}",
serde_json::to_string_pretty(&self.header).unwrap()
)?;
writeln!(
f,
"Parent header: {}",
serde_json::to_string_pretty(&self.parent_header).unwrap()
)?;
writeln!(
f,
"Metadata: {}",
serde_json::to_string_pretty(&self.metadata).unwrap()
)?;
writeln!(
f,
"Content: {}\n",
serde_json::to_string_pretty(&self.content).unwrap()
)?;
Ok(())
}
}

139
cli/tools/jupyter/mod.rs Normal file
View file

@ -0,0 +1,139 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use crate::args::Flags;
use crate::args::JupyterFlags;
use crate::tools::repl;
use crate::util::logger;
use crate::CliFactory;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::futures::channel::mpsc;
use deno_core::op;
use deno_core::resolve_url_or_path;
use deno_core::serde::Deserialize;
use deno_core::serde_json;
use deno_core::Op;
use deno_core::OpState;
use deno_runtime::permissions::Permissions;
use deno_runtime::permissions::PermissionsContainer;
mod install;
mod jupyter_msg;
mod server;
pub async fn kernel(
flags: Flags,
jupyter_flags: JupyterFlags,
) -> Result<(), AnyError> {
if !flags.unstable {
eprintln!(
"Unstable subcommand 'deno jupyter'. The --unstable flag must be provided."
);
std::process::exit(70);
}
if !jupyter_flags.install && !jupyter_flags.kernel {
install::status()?;
return Ok(());
}
if jupyter_flags.install {
install::install()?;
return Ok(());
}
let connection_filepath = jupyter_flags.conn_file.unwrap();
// This env var might be set by notebook
if std::env::var("DEBUG").is_ok() {
logger::init(Some(log::Level::Debug));
}
let factory = CliFactory::from_flags(flags).await?;
let cli_options = factory.cli_options();
let main_module =
resolve_url_or_path("./$deno$jupyter.ts", cli_options.initial_cwd())
.unwrap();
// TODO(bartlomieju): should we run with all permissions?
let permissions = PermissionsContainer::new(Permissions::allow_all());
let npm_resolver = factory.npm_resolver().await?.clone();
let resolver = factory.resolver().await?.clone();
let worker_factory = factory.create_cli_main_worker_factory().await?;
let (stdio_tx, stdio_rx) = mpsc::unbounded();
let conn_file =
std::fs::read_to_string(&connection_filepath).with_context(|| {
format!("Couldn't read connection file: {:?}", connection_filepath)
})?;
let spec: ConnectionSpec =
serde_json::from_str(&conn_file).with_context(|| {
format!(
"Connection file is not a valid JSON: {:?}",
connection_filepath
)
})?;
let mut worker = worker_factory
.create_custom_worker(
main_module.clone(),
permissions,
vec![deno_jupyter::init_ops(stdio_tx)],
Default::default(),
)
.await?;
worker.setup_repl().await?;
let worker = worker.into_main_worker();
let repl_session =
repl::ReplSession::initialize(cli_options, npm_resolver, resolver, worker)
.await?;
server::JupyterServer::start(spec, stdio_rx, repl_session).await?;
Ok(())
}
deno_core::extension!(deno_jupyter,
options = {
sender: mpsc::UnboundedSender<server::StdioMsg>,
},
middleware = |op| match op.name {
"op_print" => op_print::DECL,
_ => op,
},
state = |state, options| {
state.put(options.sender);
},
);
#[op]
pub fn op_print(
state: &mut OpState,
msg: String,
is_err: bool,
) -> Result<(), AnyError> {
let sender = state.borrow_mut::<mpsc::UnboundedSender<server::StdioMsg>>();
if is_err {
if let Err(err) = sender.unbounded_send(server::StdioMsg::Stderr(msg)) {
eprintln!("Failed to send stderr message: {}", err);
}
return Ok(());
}
if let Err(err) = sender.unbounded_send(server::StdioMsg::Stdout(msg)) {
eprintln!("Failed to send stdout message: {}", err);
}
Ok(())
}
#[derive(Debug, Deserialize)]
pub struct ConnectionSpec {
ip: String,
transport: String,
control_port: u32,
shell_port: u32,
stdin_port: u32,
hb_port: u32,
iopub_port: u32,
key: String,
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

724
cli/tools/jupyter/server.rs Normal file
View file

@ -0,0 +1,724 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This file is forked/ported from <https://github.com/evcxr/evcxr>
// Copyright 2020 The Evcxr Authors. MIT license.
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::Arc;
use crate::tools::repl;
use crate::tools::repl::cdp;
use deno_core::error::AnyError;
use deno_core::futures;
use deno_core::futures::channel::mpsc;
use deno_core::futures::StreamExt;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::CancelFuture;
use deno_core::CancelHandle;
use tokio::sync::Mutex;
use zeromq::SocketRecv;
use zeromq::SocketSend;
use super::jupyter_msg::Connection;
use super::jupyter_msg::JupyterMessage;
use super::ConnectionSpec;
pub enum StdioMsg {
Stdout(String),
Stderr(String),
}
pub struct JupyterServer {
execution_count: usize,
last_execution_request: Rc<RefCell<Option<JupyterMessage>>>,
// This is Arc<Mutex<>>, so we don't hold RefCell borrows across await
// points.
iopub_socket: Arc<Mutex<Connection<zeromq::PubSocket>>>,
repl_session: repl::ReplSession,
}
impl JupyterServer {
pub async fn start(
spec: ConnectionSpec,
mut stdio_rx: mpsc::UnboundedReceiver<StdioMsg>,
repl_session: repl::ReplSession,
) -> Result<(), AnyError> {
let mut heartbeat =
bind_socket::<zeromq::RepSocket>(&spec, spec.hb_port).await?;
let shell_socket =
bind_socket::<zeromq::RouterSocket>(&spec, spec.shell_port).await?;
let control_socket =
bind_socket::<zeromq::RouterSocket>(&spec, spec.control_port).await?;
let _stdin_socket =
bind_socket::<zeromq::RouterSocket>(&spec, spec.stdin_port).await?;
let iopub_socket =
bind_socket::<zeromq::PubSocket>(&spec, spec.iopub_port).await?;
let iopub_socket = Arc::new(Mutex::new(iopub_socket));
let last_execution_request = Rc::new(RefCell::new(None));
let cancel_handle = CancelHandle::new_rc();
let cancel_handle2 = CancelHandle::new_rc();
let mut server = Self {
execution_count: 0,
iopub_socket: iopub_socket.clone(),
last_execution_request: last_execution_request.clone(),
repl_session,
};
let handle1 = deno_core::unsync::spawn(async move {
if let Err(err) = Self::handle_heartbeat(&mut heartbeat).await {
eprintln!("Heartbeat error: {}", err);
}
});
let handle2 = deno_core::unsync::spawn(async move {
if let Err(err) =
Self::handle_control(control_socket, cancel_handle2).await
{
eprintln!("Control error: {}", err);
}
});
let handle3 = deno_core::unsync::spawn(async move {
if let Err(err) = server.handle_shell(shell_socket).await {
eprintln!("Shell error: {}", err);
}
});
let handle4 = deno_core::unsync::spawn(async move {
while let Some(stdio_msg) = stdio_rx.next().await {
Self::handle_stdio_msg(
iopub_socket.clone(),
last_execution_request.clone(),
stdio_msg,
)
.await;
}
});
let join_fut =
futures::future::try_join_all(vec![handle1, handle2, handle3, handle4]);
if let Ok(result) = join_fut.or_cancel(cancel_handle).await {
result?;
}
Ok(())
}
async fn handle_stdio_msg<S: zeromq::SocketSend>(
iopub_socket: Arc<Mutex<Connection<S>>>,
last_execution_request: Rc<RefCell<Option<JupyterMessage>>>,
stdio_msg: StdioMsg,
) {
let maybe_exec_result = last_execution_request.borrow().clone();
if let Some(exec_request) = maybe_exec_result {
let (name, text) = match stdio_msg {
StdioMsg::Stdout(text) => ("stdout", text),
StdioMsg::Stderr(text) => ("stderr", text),
};
let result = exec_request
.new_message("stream")
.with_content(json!({
"name": name,
"text": text
}))
.send(&mut *iopub_socket.lock().await)
.await;
if let Err(err) = result {
eprintln!("Output {} error: {}", name, err);
}
}
}
async fn handle_heartbeat(
connection: &mut Connection<zeromq::RepSocket>,
) -> Result<(), AnyError> {
loop {
connection.socket.recv().await?;
connection
.socket
.send(zeromq::ZmqMessage::from(b"ping".to_vec()))
.await?;
}
}
async fn handle_control(
mut connection: Connection<zeromq::RouterSocket>,
cancel_handle: Rc<CancelHandle>,
) -> Result<(), AnyError> {
loop {
let msg = JupyterMessage::read(&mut connection).await?;
match msg.message_type() {
"kernel_info_request" => {
msg
.new_reply()
.with_content(kernel_info())
.send(&mut connection)
.await?;
}
"shutdown_request" => {
cancel_handle.cancel();
}
"interrupt_request" => {
eprintln!("Interrupt request currently not supported");
}
_ => {
eprintln!(
"Unrecognized control message type: {}",
msg.message_type()
);
}
}
}
}
async fn handle_shell(
&mut self,
mut connection: Connection<zeromq::RouterSocket>,
) -> Result<(), AnyError> {
loop {
let msg = JupyterMessage::read(&mut connection).await?;
self.handle_shell_message(msg, &mut connection).await?;
}
}
async fn handle_shell_message(
&mut self,
msg: JupyterMessage,
connection: &mut Connection<zeromq::RouterSocket>,
) -> Result<(), AnyError> {
msg
.new_message("status")
.with_content(json!({"execution_state": "busy"}))
.send(&mut *self.iopub_socket.lock().await)
.await?;
match msg.message_type() {
"kernel_info_request" => {
msg
.new_reply()
.with_content(kernel_info())
.send(connection)
.await?;
}
"is_complete_request" => {
msg
.new_reply()
.with_content(json!({"status": "complete"}))
.send(connection)
.await?;
}
"execute_request" => {
self
.handle_execution_request(msg.clone(), connection)
.await?;
}
"comm_open" => {
msg
.comm_close_message()
.send(&mut *self.iopub_socket.lock().await)
.await?;
}
"complete_request" => {
let user_code = msg.code();
let cursor_pos = msg.cursor_pos();
let lsp_completions = self
.repl_session
.language_server
.completions(user_code, cursor_pos)
.await;
if !lsp_completions.is_empty() {
let matches: Vec<String> = lsp_completions
.iter()
.map(|item| item.new_text.clone())
.collect();
let cursor_start = lsp_completions
.first()
.map(|item| item.range.start)
.unwrap_or(cursor_pos);
let cursor_end = lsp_completions
.last()
.map(|item| item.range.end)
.unwrap_or(cursor_pos);
msg
.new_reply()
.with_content(json!({
"status": "ok",
"matches": matches,
"cursor_start": cursor_start,
"cursor_end": cursor_end,
"metadata": {},
}))
.send(connection)
.await?;
} else {
let expr = get_expr_from_line_at_pos(user_code, cursor_pos);
// check if the expression is in the form `obj.prop`
let (completions, cursor_start) = if let Some(index) = expr.rfind('.')
{
let sub_expr = &expr[..index];
let prop_name = &expr[index + 1..];
let candidates =
get_expression_property_names(&mut self.repl_session, sub_expr)
.await
.into_iter()
.filter(|n| {
!n.starts_with("Symbol(")
&& n.starts_with(prop_name)
&& n != &*repl::REPL_INTERNALS_NAME
})
.collect();
(candidates, cursor_pos - prop_name.len())
} else {
// combine results of declarations and globalThis properties
let mut candidates = get_expression_property_names(
&mut self.repl_session,
"globalThis",
)
.await
.into_iter()
.chain(get_global_lexical_scope_names(&mut self.repl_session).await)
.filter(|n| n.starts_with(expr) && n != &*repl::REPL_INTERNALS_NAME)
.collect::<Vec<_>>();
// sort and remove duplicates
candidates.sort();
candidates.dedup(); // make sure to sort first
(candidates, cursor_pos - expr.len())
};
msg
.new_reply()
.with_content(json!({
"status": "ok",
"matches": completions,
"cursor_start": cursor_start,
"cursor_end": cursor_pos,
"metadata": {},
}))
.send(connection)
.await?;
}
}
"comm_msg" | "comm_info_request" | "history_request" => {
// We don't handle these messages
}
_ => {
eprintln!("Unrecognized shell message type: {}", msg.message_type());
}
}
msg
.new_message("status")
.with_content(json!({"execution_state": "idle"}))
.send(&mut *self.iopub_socket.lock().await)
.await?;
Ok(())
}
async fn handle_execution_request(
&mut self,
msg: JupyterMessage,
connection: &mut Connection<zeromq::RouterSocket>,
) -> Result<(), AnyError> {
self.execution_count += 1;
*self.last_execution_request.borrow_mut() = Some(msg.clone());
msg
.new_message("execute_input")
.with_content(json!({
"execution_count": self.execution_count,
"code": msg.code()
}))
.send(&mut *self.iopub_socket.lock().await)
.await?;
let result = self
.repl_session
.evaluate_line_with_object_wrapping(msg.code())
.await;
let evaluate_response = match result {
Ok(eval_response) => eval_response,
Err(err) => {
msg
.new_message("error")
.with_content(json!({
"ename": err.to_string(),
"evalue": "",
"traceback": [],
}))
.send(&mut *self.iopub_socket.lock().await)
.await?;
msg
.new_reply()
.with_content(json!({
"status": "error",
"execution_count": self.execution_count,
}))
.send(connection)
.await?;
return Ok(());
}
};
let repl::cdp::EvaluateResponse {
result,
exception_details,
} = evaluate_response.value;
if exception_details.is_none() {
let output =
get_jupyter_display_or_eval_value(&mut self.repl_session, &result)
.await?;
msg
.new_message("execute_result")
.with_content(json!({
"execution_count": self.execution_count,
"data": output,
"metadata": {},
}))
.send(&mut *self.iopub_socket.lock().await)
.await?;
msg
.new_reply()
.with_content(json!({
"status": "ok",
"execution_count": self.execution_count,
}))
.send(connection)
.await?;
// Let's sleep here for a few ms, so we give a chance to the task that is
// handling stdout and stderr streams to receive and flush the content.
// Otherwise, executing multiple cells one-by-one might lead to output
// from various cells be grouped together in another cell result.
tokio::time::sleep(std::time::Duration::from_millis(5)).await;
} else {
let exception_details = exception_details.unwrap();
let name = if let Some(exception) = exception_details.exception {
if let Some(description) = exception.description {
description
} else if let Some(value) = exception.value {
value.to_string()
} else {
"undefined".to_string()
}
} else {
"Unknown exception".to_string()
};
// TODO(bartlomieju): fill all the fields
msg
.new_message("error")
.with_content(json!({
"ename": name,
"evalue": "",
"traceback": [],
}))
.send(&mut *self.iopub_socket.lock().await)
.await?;
msg
.new_reply()
.with_content(json!({
"status": "error",
"execution_count": self.execution_count,
}))
.send(connection)
.await?;
}
Ok(())
}
}
async fn bind_socket<S: zeromq::Socket>(
config: &ConnectionSpec,
port: u32,
) -> Result<Connection<S>, AnyError> {
let endpoint = format!("{}://{}:{}", config.transport, config.ip, port);
let mut socket = S::new();
socket.bind(&endpoint).await?;
Ok(Connection::new(socket, &config.key))
}
fn kernel_info() -> serde_json::Value {
json!({
"status": "ok",
"protocol_version": "5.3",
"implementation_version": crate::version::deno(),
"implementation": "Deno kernel",
"language_info": {
"name": "typescript",
"version": crate::version::TYPESCRIPT,
"mimetype": "text/x.typescript",
"file_extension": ".ts",
"pygments_lexer": "typescript",
"nb_converter": "script"
},
"help_links": [{
"text": "Visit Deno manual",
"url": "https://deno.land/manual"
}],
"banner": "Welcome to Deno kernel",
})
}
async fn get_jupyter_display(
session: &mut repl::ReplSession,
evaluate_result: &cdp::RemoteObject,
) -> Result<Option<HashMap<String, serde_json::Value>>, AnyError> {
let mut data = HashMap::default();
let response = session
.call_function_on_args(
r#"function (object) {{
return object[Symbol.for("Jupyter.display")]();
}}"#
.to_string(),
&[evaluate_result.clone()],
)
.await?;
if response.exception_details.is_some() {
return Ok(None);
}
let object_id = response.result.object_id.unwrap();
let get_properties_response_result = session
.post_message_with_event_loop(
"Runtime.getProperties",
Some(cdp::GetPropertiesArgs {
object_id,
own_properties: Some(true),
accessor_properties_only: None,
generate_preview: None,
non_indexed_properties_only: Some(true),
}),
)
.await;
let Ok(get_properties_response) = get_properties_response_result else {
return Ok(None);
};
let get_properties_response: cdp::GetPropertiesResponse =
serde_json::from_value(get_properties_response).unwrap();
for prop in get_properties_response.result.into_iter() {
if let Some(value) = &prop.value {
data.insert(
prop.name.to_string(),
value
.value
.clone()
.unwrap_or_else(|| serde_json::Value::Null),
);
}
}
if !data.is_empty() {
return Ok(Some(data));
}
Ok(None)
}
async fn get_jupyter_display_or_eval_value(
session: &mut repl::ReplSession,
evaluate_result: &cdp::RemoteObject,
) -> Result<HashMap<String, serde_json::Value>, AnyError> {
// Printing "undefined" generates a lot of noise, so let's skip
// these.
if evaluate_result.kind == "undefined" {
return Ok(HashMap::default());
}
if let Some(data) = get_jupyter_display(session, evaluate_result).await? {
return Ok(data);
}
let response = session
.call_function_on_args(
format!(
r#"function (object) {{
try {{
return {0}.inspectArgs(["%o", object], {{ colors: !{0}.noColor }});
}} catch (err) {{
return {0}.inspectArgs(["%o", err]);
}}
}}"#,
*repl::REPL_INTERNALS_NAME
),
&[evaluate_result.clone()],
)
.await?;
let mut data = HashMap::default();
if let Some(value) = response.result.value {
data.insert("text/plain".to_string(), value);
}
Ok(data)
}
// TODO(bartlomieju): dedup with repl::editor
fn get_expr_from_line_at_pos(line: &str, cursor_pos: usize) -> &str {
let start = line[..cursor_pos].rfind(is_word_boundary).unwrap_or(0);
let end = line[cursor_pos..]
.rfind(is_word_boundary)
.map(|i| cursor_pos + i)
.unwrap_or(cursor_pos);
let word = &line[start..end];
let word = word.strip_prefix(is_word_boundary).unwrap_or(word);
let word = word.strip_suffix(is_word_boundary).unwrap_or(word);
word
}
// TODO(bartlomieju): dedup with repl::editor
fn is_word_boundary(c: char) -> bool {
if matches!(c, '.' | '_' | '$') {
false
} else {
char::is_ascii_whitespace(&c) || char::is_ascii_punctuation(&c)
}
}
// TODO(bartlomieju): dedup with repl::editor
async fn get_global_lexical_scope_names(
session: &mut repl::ReplSession,
) -> Vec<String> {
let evaluate_response = session
.post_message_with_event_loop(
"Runtime.globalLexicalScopeNames",
Some(cdp::GlobalLexicalScopeNamesArgs {
execution_context_id: Some(session.context_id),
}),
)
.await
.unwrap();
let evaluate_response: cdp::GlobalLexicalScopeNamesResponse =
serde_json::from_value(evaluate_response).unwrap();
evaluate_response.names
}
// TODO(bartlomieju): dedup with repl::editor
async fn get_expression_property_names(
session: &mut repl::ReplSession,
expr: &str,
) -> Vec<String> {
// try to get the properties from the expression
if let Some(properties) = get_object_expr_properties(session, expr).await {
return properties;
}
// otherwise fall back to the prototype
let expr_type = get_expression_type(session, expr).await;
let object_expr = match expr_type.as_deref() {
// possibilities: https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#type-RemoteObject
Some("object") => "Object.prototype",
Some("function") => "Function.prototype",
Some("string") => "String.prototype",
Some("boolean") => "Boolean.prototype",
Some("bigint") => "BigInt.prototype",
Some("number") => "Number.prototype",
_ => return Vec::new(), // undefined, symbol, and unhandled
};
get_object_expr_properties(session, object_expr)
.await
.unwrap_or_default()
}
// TODO(bartlomieju): dedup with repl::editor
async fn get_expression_type(
session: &mut repl::ReplSession,
expr: &str,
) -> Option<String> {
evaluate_expression(session, expr)
.await
.map(|res| res.result.kind)
}
// TODO(bartlomieju): dedup with repl::editor
async fn get_object_expr_properties(
session: &mut repl::ReplSession,
object_expr: &str,
) -> Option<Vec<String>> {
let evaluate_result = evaluate_expression(session, object_expr).await?;
let object_id = evaluate_result.result.object_id?;
let get_properties_response = session
.post_message_with_event_loop(
"Runtime.getProperties",
Some(cdp::GetPropertiesArgs {
object_id,
own_properties: None,
accessor_properties_only: None,
generate_preview: None,
non_indexed_properties_only: Some(true),
}),
)
.await
.ok()?;
let get_properties_response: cdp::GetPropertiesResponse =
serde_json::from_value(get_properties_response).ok()?;
Some(
get_properties_response
.result
.into_iter()
.map(|prop| prop.name)
.collect(),
)
}
// TODO(bartlomieju): dedup with repl::editor
async fn evaluate_expression(
session: &mut repl::ReplSession,
expr: &str,
) -> Option<cdp::EvaluateResponse> {
let evaluate_response = session
.post_message_with_event_loop(
"Runtime.evaluate",
Some(cdp::EvaluateArgs {
expression: expr.to_string(),
object_group: None,
include_command_line_api: None,
silent: None,
context_id: Some(session.context_id),
return_by_value: None,
generate_preview: None,
user_gesture: None,
await_promise: None,
throw_on_side_effect: Some(true),
timeout: Some(200),
disable_breaks: None,
repl_mode: None,
allow_unsafe_eval_blocked_by_csp: None,
unique_context_id: None,
}),
)
.await
.ok()?;
let evaluate_response: cdp::EvaluateResponse =
serde_json::from_value(evaluate_response).ok()?;
if evaluate_response.exception_details.is_some() {
None
} else {
Some(evaluate_response)
}
}

View file

@ -10,6 +10,7 @@ pub mod fmt;
pub mod info;
pub mod init;
pub mod installer;
pub mod jupyter;
pub mod lint;
pub mod repl;
pub mod run;

View file

@ -13,7 +13,7 @@ use deno_runtime::permissions::Permissions;
use deno_runtime::permissions::PermissionsContainer;
use rustyline::error::ReadlineError;
mod cdp;
pub(crate) mod cdp;
mod channel;
mod editor;
mod session;
@ -24,8 +24,9 @@ use channel::RustylineSyncMessageHandler;
use channel::RustylineSyncResponse;
use editor::EditorHelper;
use editor::ReplEditor;
use session::EvaluationOutput;
use session::ReplSession;
pub use session::EvaluationOutput;
pub use session::ReplSession;
pub use session::REPL_INTERNALS_NAME;
#[allow(clippy::await_holding_refcell_ref)]
async fn read_line_and_poll(

View file

@ -116,9 +116,10 @@ pub fn result_to_evaluation_output(
}
}
struct TsEvaluateResponse {
ts_code: String,
value: cdp::EvaluateResponse,
#[derive(Debug)]
pub struct TsEvaluateResponse {
pub ts_code: String,
pub value: cdp::EvaluateResponse,
}
pub struct ReplSession {
@ -305,7 +306,7 @@ impl ReplSession {
result_to_evaluation_output(result)
}
async fn evaluate_line_with_object_wrapping(
pub async fn evaluate_line_with_object_wrapping(
&mut self,
line: &str,
) -> Result<TsEvaluateResponse, AnyError> {
@ -395,29 +396,24 @@ impl ReplSession {
Ok(())
}
pub async fn get_eval_value(
pub async fn call_function_on_args(
&mut self,
evaluate_result: &cdp::RemoteObject,
) -> Result<String, AnyError> {
// TODO(caspervonb) we should investigate using previews here but to keep things
// consistent with the previous implementation we just get the preview result from
// Deno.inspectArgs.
function_declaration: String,
args: &[cdp::RemoteObject],
) -> Result<cdp::CallFunctionOnResponse, AnyError> {
let arguments: Option<Vec<cdp::CallArgument>> = if args.is_empty() {
None
} else {
Some(args.iter().map(|a| a.into()).collect())
};
let inspect_response = self
.post_message_with_event_loop(
"Runtime.callFunctionOn",
Some(cdp::CallFunctionOnArgs {
function_declaration: format!(
r#"function (object) {{
try {{
return {0}.inspectArgs(["%o", object], {{ colors: !{0}.noColor }});
}} catch (err) {{
return {0}.inspectArgs(["%o", err]);
}}
}}"#,
*REPL_INTERNALS_NAME
),
function_declaration,
object_id: None,
arguments: Some(vec![evaluate_result.into()]),
arguments,
silent: None,
return_by_value: None,
generate_preview: None,
@ -432,6 +428,31 @@ impl ReplSession {
let response: cdp::CallFunctionOnResponse =
serde_json::from_value(inspect_response)?;
Ok(response)
}
pub async fn get_eval_value(
&mut self,
evaluate_result: &cdp::RemoteObject,
) -> Result<String, AnyError> {
// TODO(caspervonb) we should investigate using previews here but to keep things
// consistent with the previous implementation we just get the preview result from
// Deno.inspectArgs.
let response = self
.call_function_on_args(
format!(
r#"function (object) {{
try {{
return {0}.inspectArgs(["%o", object], {{ colors: !{0}.noColor }});
}} catch (err) {{
return {0}.inspectArgs(["%o", err]);
}}
}}"#,
*REPL_INTERNALS_NAME
),
&[evaluate_result.clone()],
)
.await?;
let value = response.result.value.unwrap();
let s = value.as_str().unwrap();

View file

@ -19,7 +19,7 @@ aes.workspace = true
brotli.workspace = true
bytes.workspace = true
cbc.workspace = true
data-encoding = "2.3.3"
data-encoding.workspace = true
deno_core.workspace = true
deno_fetch.workspace = true
deno_fs.workspace = true