├── .devcontainer
└── devcontainer.json
├── .github
└── workflows
│ ├── devskim.yml
│ └── gh-sync.yml
├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── NOTICE.txt
├── README.md
├── SECURITY.md
├── ci
├── ci.yml
├── signed.yml
├── steps-build-core.yml
├── steps-build-samples.yml
├── steps-sdl-tools.yml
└── steps-sign.yml
├── docfx.json
├── docs
├── .gitignore
├── articles
│ ├── intro.md
│ └── toc.yml
├── index.md
└── toc.yml
├── examples
├── echo-kernel
│ ├── Echo Kernel.ipynb
│ ├── EchoEngine.cs
│ ├── KernelProperties.cs
│ ├── Program.cs
│ ├── README.md
│ ├── echo-kernel.csproj
│ └── res
│ │ └── logo-64x64.png
└── moon-kernel
│ ├── DynValueConverter.cs
│ ├── KernelProperties.cs
│ ├── MoonEngine.cs
│ ├── MoonScript Kernel.ipynb
│ ├── Program.cs
│ └── moon-kernel.csproj
├── jupyter-core.sln
├── src
├── Data
│ ├── ConnectionInfo.cs
│ ├── Enumerations.cs
│ ├── KernelContext.cs
│ ├── Metadata.cs
│ ├── Protocol.cs
│ └── Serialization.cs
├── Engines
│ ├── BaseEngine.cs
│ ├── CompleteRequestHandler.cs
│ ├── CompletionResult.cs
│ ├── ExecuteRequestHandler.cs
│ ├── ExecutionResult.cs
│ ├── IChannel.cs
│ ├── IExecutionEngine.cs
│ └── InputParser.cs
├── Extensions
│ ├── ChannelWithNewLines.cs
│ ├── Collections.cs
│ ├── Conversions.cs
│ ├── Cryptography.cs
│ ├── Extensions.cs
│ ├── Messages.cs
│ └── ServiceCollection.cs
├── KernelApplication.cs
├── Properties
│ ├── 267DevDivSNKey2048.snk
│ ├── AssemblyInfo.cs
│ └── DelaySign.cs
├── ResultEncoding
│ ├── BasicEncoders.cs
│ ├── DisplayData.cs
│ ├── SymbolEncoder.cs
│ └── Table.cs
├── Servers
│ ├── HeartbeatServer.cs
│ ├── IHeartbeatServer.cs
│ ├── IShellServer.cs
│ └── ShellServer.cs
├── ShellRouting
│ ├── CommsRouter.cs
│ ├── ICommsRouter.cs
│ ├── IShellHandler.cs
│ ├── IShellRouter.cs
│ ├── OrderedShellHandler.cs
│ └── ShellRouter.cs
├── Symbols
│ ├── ISymbolResolver.cs
│ └── Magic.cs
└── jupyter-core.csproj
├── tests
├── core
│ ├── EncoderTests.cs
│ ├── InputParserTests.cs
│ ├── Properties
│ │ └── 267DevDivSNKey2048.snk
│ └── core.csproj
└── protocol
│ ├── test_iecho.py
│ └── test_imoon.py
└── tutorial.md
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "extensions": [
3 | "ms-dotnettools.csharp"
4 | ],
5 | "features": {
6 | "ghcr.io/devcontainers/features/dotnet:1": {
7 | "version": "3.1.423"
8 | },
9 | "ghcr.io/devcontainers/features/python:1": {
10 | "version": "3.8"
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.github/workflows/devskim.yml:
--------------------------------------------------------------------------------
1 | name: DevSkim
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 | workflow_dispatch:
9 | schedule:
10 | # set schedule to run at 2AM PT on Saturdays
11 | - cron: '0 9 * * Sat'
12 |
13 | jobs:
14 | lint:
15 | name: DevSkim
16 | runs-on: ubuntu-latest
17 | permissions:
18 | actions: read
19 | contents: read
20 | security-events: write
21 | steps:
22 | - name: Checkout code
23 | uses: actions/checkout@v3
24 |
25 | - name: Run DevSkim scanner
26 | uses: microsoft/DevSkim-Action@v1
27 |
28 | - name: Upload DevSkim scan results to GitHub Security tab
29 | uses: github/codeql-action/upload-sarif@v2
30 | with:
31 | sarif_file: devskim-results.sarif
32 |
--------------------------------------------------------------------------------
/.github/workflows/gh-sync.yml:
--------------------------------------------------------------------------------
1 | name: Sync GitHub with ADO
2 |
3 | on:
4 | issues:
5 | types: [closed, edited, deleted, reopened, assigned, unassigned, labeled, unlabeled]
6 | issue_comment:
7 |
8 | concurrency:
9 | group: ${{ github.event.issue.number }}
10 | cancel-in-progress: true
11 |
12 | jobs:
13 | sync-issues:
14 | name: Run gh-sync from GitHub action
15 | if: ${{ github.event.label.name == 'tracking' || contains(github.event.issue.labels.*.name, 'tracking') }}
16 | runs-on: ubuntu-latest
17 | steps:
18 | - name: Login to Azure
19 | uses: Azure/login@v1
20 | with:
21 | creds: ${{ secrets.AZURE_CREDENTIALS }}
22 |
23 | - id: AzureKeyVault
24 | uses: Azure/get-keyvault-secrets@v1
25 | with:
26 | keyvault: 'kv-qdk-build'
27 | secrets: 'ghSyncBuildPAT'
28 |
29 | - name: 'Trigger gh-sync'
30 | uses: microsoft/gh-sync@main
31 | with:
32 | ado-organization-url: ${{ secrets.ADO_URL }}
33 | ado-project: ${{ secrets.ADO_PROJECT }}
34 | ado-area-path: ${{ secrets.ADO_AREA_PATH }}
35 | github-repo: 'microsoft/jupyter-core'
36 | issue-number: ${{ github.event.issue.number }}
37 | ado-token: ${{ steps.AzureKeyVault.outputs.ghSyncBuildPAT }}
38 | github-token: ${{ secrets.GITHUB_TOKEN }}
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .ipynb_checkpoints/
2 | ## Ignore Visual Studio temporary files, build results, and
3 | ## files generated by popular Visual Studio add-ons.
4 | ##
5 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
6 |
7 | # User-specific files
8 | *.rsuser
9 | *.suo
10 | *.user
11 | *.userosscache
12 | *.sln.docstates
13 |
14 | # User-specific files (MonoDevelop/Xamarin Studio)
15 | *.userprefs
16 |
17 | # Build results
18 | [Dd]ebug/
19 | [Dd]ebugPublic/
20 | [Rr]elease/
21 | [Rr]eleases/
22 | x64/
23 | x86/
24 | [Aa][Rr][Mm]/
25 | [Aa][Rr][Mm]64/
26 | bld/
27 | [Bb]in/
28 | [Oo]bj/
29 | [Ll]og/
30 |
31 | # Visual Studio 2015/2017 cache/options directory
32 | .vs/
33 | # Uncomment if you have tasks that create the project's static files in wwwroot
34 | #wwwroot/
35 |
36 | # Visual Studio 2017 auto generated files
37 | Generated\ Files/
38 |
39 | # MSTest test Results
40 | [Tt]est[Rr]esult*/
41 | [Bb]uild[Ll]og.*
42 |
43 | # NUNIT
44 | *.VisualState.xml
45 | TestResult.xml
46 |
47 | # Build Results of an ATL Project
48 | [Dd]ebugPS/
49 | [Rr]eleasePS/
50 | dlldata.c
51 |
52 | # Benchmark Results
53 | BenchmarkDotNet.Artifacts/
54 |
55 | # .NET Core
56 | project.lock.json
57 | project.fragment.lock.json
58 | artifacts/
59 | **/Properties/launchSettings.json
60 |
61 | # StyleCop
62 | StyleCopReport.xml
63 |
64 | # Files built by Visual Studio
65 | *_i.c
66 | *_p.c
67 | *_i.h
68 | *_h.h
69 | *.ilk
70 | *.meta
71 | *.obj
72 | *.iobj
73 | *.pch
74 | *.pdb
75 | *.ipdb
76 | *.pgc
77 | *.pgd
78 | *.rsp
79 | *.sbr
80 | *.tlb
81 | *.tli
82 | *.tlh
83 | *.tmp
84 | *.tmp_proj
85 | *_wpftmp.csproj
86 | *.log
87 | *.vspscc
88 | *.vssscc
89 | .builds
90 | *.pidb
91 | *.svclog
92 | *.scc
93 |
94 | # Chutzpah Test files
95 | _Chutzpah*
96 |
97 | # Visual C++ cache files
98 | ipch/
99 | *.aps
100 | *.ncb
101 | *.opendb
102 | *.opensdf
103 | *.sdf
104 | *.cachefile
105 | *.VC.db
106 | *.VC.VC.opendb
107 |
108 | # Visual Studio profiler
109 | *.psess
110 | *.vsp
111 | *.vspx
112 | *.sap
113 |
114 | # Visual Studio Trace Files
115 | *.e2e
116 |
117 | # TFS 2012 Local Workspace
118 | $tf/
119 |
120 | # Guidance Automation Toolkit
121 | *.gpState
122 |
123 | # ReSharper is a .NET coding add-in
124 | _ReSharper*/
125 | *.[Rr]e[Ss]harper
126 | *.DotSettings.user
127 |
128 | # JustCode is a .NET coding add-in
129 | .JustCode
130 |
131 | # TeamCity is a build add-in
132 | _TeamCity*
133 |
134 | # DotCover is a Code Coverage Tool
135 | *.dotCover
136 |
137 | # AxoCover is a Code Coverage Tool
138 | .axoCover/*
139 | !.axoCover/settings.json
140 |
141 | # Visual Studio code coverage results
142 | *.coverage
143 | *.coveragexml
144 |
145 | # NCrunch
146 | _NCrunch_*
147 | .*crunch*.local.xml
148 | nCrunchTemp_*
149 |
150 | # MightyMoose
151 | *.mm.*
152 | AutoTest.Net/
153 |
154 | # Web workbench (sass)
155 | .sass-cache/
156 |
157 | # Installshield output folder
158 | [Ee]xpress/
159 |
160 | # DocProject is a documentation generator add-in
161 | DocProject/buildhelp/
162 | DocProject/Help/*.HxT
163 | DocProject/Help/*.HxC
164 | DocProject/Help/*.hhc
165 | DocProject/Help/*.hhk
166 | DocProject/Help/*.hhp
167 | DocProject/Help/Html2
168 | DocProject/Help/html
169 |
170 | # Click-Once directory
171 | publish/
172 |
173 | # Publish Web Output
174 | *.[Pp]ublish.xml
175 | *.azurePubxml
176 | # Note: Comment the next line if you want to checkin your web deploy settings,
177 | # but database connection strings (with potential passwords) will be unencrypted
178 | *.pubxml
179 | *.publishproj
180 |
181 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
182 | # checkin your Azure Web App publish settings, but sensitive information contained
183 | # in these scripts will be unencrypted
184 | PublishScripts/
185 |
186 | # NuGet Packages
187 | *.nupkg
188 | # The packages folder can be ignored because of Package Restore
189 | **/[Pp]ackages/*
190 | # except build/, which is used as an MSBuild target.
191 | !**/[Pp]ackages/build/
192 | # Uncomment if necessary however generally it will be regenerated when needed
193 | #!**/[Pp]ackages/repositories.config
194 | # NuGet v3's project.json files produces more ignorable files
195 | *.nuget.props
196 | *.nuget.targets
197 |
198 | # Microsoft Azure Build Output
199 | csx/
200 | *.build.csdef
201 |
202 | # Microsoft Azure Emulator
203 | ecf/
204 | rcf/
205 |
206 | # Windows Store app package directories and files
207 | AppPackages/
208 | BundleArtifacts/
209 | Package.StoreAssociation.xml
210 | _pkginfo.txt
211 | *.appx
212 |
213 | # Visual Studio cache files
214 | # files ending in .cache can be ignored
215 | *.[Cc]ache
216 | # but keep track of directories ending in .cache
217 | !*.[Cc]ache/
218 |
219 | # Others
220 | ClientBin/
221 | ~$*
222 | *~
223 | *.dbmdl
224 | *.dbproj.schemaview
225 | *.jfm
226 | *.pfx
227 | *.publishsettings
228 | orleans.codegen.cs
229 |
230 | # Including strong name files can present a security risk
231 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
232 | #*.snk
233 |
234 | # Since there are multiple workflows, uncomment next line to ignore bower_components
235 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
236 | #bower_components/
237 | # ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
238 | **/wwwroot/lib/
239 |
240 | # RIA/Silverlight projects
241 | Generated_Code/
242 |
243 | # Backup & report files from converting an old project file
244 | # to a newer Visual Studio version. Backup files are not needed,
245 | # because we have git ;-)
246 | _UpgradeReport_Files/
247 | Backup*/
248 | UpgradeLog*.XML
249 | UpgradeLog*.htm
250 | ServiceFabricBackup/
251 | *.rptproj.bak
252 |
253 | # SQL Server files
254 | *.mdf
255 | *.ldf
256 | *.ndf
257 |
258 | # Business Intelligence projects
259 | *.rdl.data
260 | *.bim.layout
261 | *.bim_*.settings
262 | *.rptproj.rsuser
263 |
264 | # Microsoft Fakes
265 | FakesAssemblies/
266 |
267 | # GhostDoc plugin setting file
268 | *.GhostDoc.xml
269 |
270 | # Node.js Tools for Visual Studio
271 | .ntvs_analysis.dat
272 | node_modules/
273 |
274 | # Visual Studio 6 build log
275 | *.plg
276 |
277 | # Visual Studio 6 workspace options file
278 | *.opt
279 |
280 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
281 | *.vbw
282 |
283 | # Visual Studio LightSwitch build output
284 | **/*.HTMLClient/GeneratedArtifacts
285 | **/*.DesktopClient/GeneratedArtifacts
286 | **/*.DesktopClient/ModelManifest.xml
287 | **/*.Server/GeneratedArtifacts
288 | **/*.Server/ModelManifest.xml
289 | _Pvt_Extensions
290 |
291 | # Paket dependency manager
292 | .paket/paket.exe
293 | paket-files/
294 |
295 | # FAKE - F# Make
296 | .fake/
297 |
298 | # JetBrains Rider
299 | .idea/
300 | *.sln.iml
301 |
302 | # CodeRush
303 | .cr/
304 | # CodeRush personal settings
305 | .cr/personal
306 |
307 | # Python Tools for Visual Studio (PTVS)
308 | __pycache__/
309 | *.pyc
310 |
311 | # Cake - Uncomment if you are using it
312 | # tools/**
313 | # !tools/packages.config
314 |
315 | # Tabs Studio
316 | *.tss
317 |
318 | # Telerik's JustMock configuration file
319 | *.jmconfig
320 |
321 | # BizTalk build output
322 | *.btp.cs
323 | *.btm.cs
324 | *.odx.cs
325 | *.xsd.cs
326 |
327 | # OpenCover UI analysis results
328 | OpenCover/
329 |
330 | # Azure Stream Analytics local run output
331 | ASALocalRun/
332 |
333 | # MSBuild Binary and Structured Log
334 | *.binlog
335 |
336 | # NVidia Nsight GPU debugger configuration file
337 | *.nvuser
338 |
339 | # MFractors (Xamarin productivity tool) working folder
340 | .mfractor/
341 |
342 | # Local History for Visual Studio
343 | .localhistory/
344 |
345 | # Byte-compiled / optimized / DLL files
346 | __pycache__/
347 | *.py[cod]
348 | *$py.class
349 |
350 | # C extensions
351 | *.so
352 |
353 | # Distribution / packaging
354 | .Python
355 | build/
356 | develop-eggs/
357 | dist/
358 | downloads/
359 | eggs/
360 | .eggs/
361 | lib/
362 | lib64/
363 | parts/
364 | sdist/
365 | var/
366 | wheels/
367 | share/python-wheels/
368 | *.egg-info/
369 | .installed.cfg
370 | *.egg
371 | MANIFEST
372 |
373 | # PyInstaller
374 | # Usually these files are written by a python script from a template
375 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
376 | *.manifest
377 | *.spec
378 |
379 | # Installer logs
380 | pip-log.txt
381 | pip-delete-this-directory.txt
382 |
383 | # Unit test / coverage reports
384 | htmlcov/
385 | .tox/
386 | .nox/
387 | .coverage
388 | .coverage.*
389 | .cache
390 | nosetests.xml
391 | coverage.xml
392 | *.cover
393 | .hypothesis/
394 | .pytest_cache/
395 |
396 | # Translations
397 | *.mo
398 | *.pot
399 |
400 | # Django stuff:
401 | *.log
402 | local_settings.py
403 | db.sqlite3
404 |
405 | # Flask stuff:
406 | instance/
407 | .webassets-cache
408 |
409 | # Scrapy stuff:
410 | .scrapy
411 |
412 | # Sphinx documentation
413 | docs/_build/
414 |
415 | # PyBuilder
416 | target/
417 |
418 | # Jupyter Notebook
419 | .ipynb_checkpoints
420 |
421 | # IPython
422 | profile_default/
423 | ipython_config.py
424 |
425 | # pyenv
426 | .python-version
427 |
428 | # celery beat schedule file
429 | celerybeat-schedule
430 |
431 | # SageMath parsed files
432 | *.sage.py
433 |
434 | # Environments
435 | .env
436 | .venv
437 | env/
438 | venv/
439 | ENV/
440 | env.bak/
441 | venv.bak/
442 |
443 | # Spyder project settings
444 | .spyderproject
445 | .spyproject
446 |
447 | # Rope project settings
448 | .ropeproject
449 |
450 | # mkdocs documentation
451 | /site
452 |
453 | # mypy
454 | .mypy_cache/
455 | .dmypy.json
456 | dmypy.json
457 |
458 | # Pyre type checker
459 | .pyre/
460 |
461 | # DocFX
462 | docs/api
463 | _site/
464 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.associations": {
3 | "**/ci/*.yml": "azure-pipelines"
4 | }
5 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation. All rights reserved.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DEPRECATION NOTICE #
2 |
3 | **This repository is deprecated.**
4 |
5 | ## Microsoft.Jupyter.Core Preview ##
6 |
7 | The **Microsoft.Jupyter.Core** library makes it easier to write language kernels for Jupyter using .NET Core languages like C# and F#.
8 | This library uses .NET Core technologies such as the [ASP.NET Core Dependency Injection Framework](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2) to help make it straightforward to develop for the Jupyter platform.
9 |
10 | Kernels developed using **Microsoft.Jupyter.Core** can be installed as [.NET Core Global Tools](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools-how-to-create).
11 | This makes it easy to package, distribute, and install language kernels.
12 | For instance, the [**IEcho**](examples/echo-kernel/) sample kernel can be installed into a user's Jupyter environment with two commands:
13 |
14 | ```
15 | cd examples/echo-kernel/
16 | dotnet run -- install
17 | ```
18 |
19 | Once installed, the IEcho example can then be used like any other Jupyter kernel by running your favorite client:
20 |
21 | ```
22 | jupyter notebook
23 | ```
24 |
25 | After a language kernel has been published as a NuGet package, it can be installed into a user's Jupyter environment with two commands. For example, if the IEcho kernel were published as a NuGet package named `Microsoft.Jupyter.Example.IEcho`, it could be installed via the following commands:
26 |
27 | ```
28 | dotnet tool install -g Microsoft.Jupyter.Example.IEcho
29 | dotnet iecho install
30 | ````
31 |
32 | ## Making New Language Kernels ##
33 |
34 | Using **Microsoft.Jupyter.Core** to make a new language kernel follows in several steps:
35 |
36 | - Create a new console application project in your favorite .NET Core language.
37 | - Add properties to your project to enable packaging as a [.NET Core Global Tool](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools-how-to-create).
38 | - Add `Microsoft.Jupyter.Core` as a package reference to your new project.
39 | - Add metadata properties for your new kernel.
40 | - Subclass the `BaseEngine` class.
41 | - Pass your metadata and engine to the `KernelApplication` class.
42 |
43 | Each of these steps has been demonstrated in the example kernels provided with the library:
44 |
45 | - [**IEcho**](examples/echo-kernel/): A simple language kernel that echos its input back as output.
46 | - [**IMoon**](examples/moon-kernel/): A language kernel for the [MoonSharp](http://moonsharp.org/) dialect of Lua.
47 |
48 | For more details, see the [provided tutorial](tutorial.md).
49 |
50 | ## Contributing ##
51 |
52 | This project welcomes contributions and suggestions. Most contributions require you to agree to a
53 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
54 | the rights to use your contribution. For details, visit https://cla.microsoft.com.
55 |
56 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
57 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
58 | provided by the bot. You will only need to do this once across all repos using our CLA.
59 |
60 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
61 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
62 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
63 |
64 |
65 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Security
4 |
5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
6 |
7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
8 |
9 | ## Reporting Security Issues
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues.**
12 |
13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
14 |
15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
16 |
17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
18 |
19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
20 |
21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22 | * Full paths of source file(s) related to the manifestation of the issue
23 | * The location of the affected source code (tag/branch/commit or direct URL)
24 | * Any special configuration required to reproduce the issue
25 | * Step-by-step instructions to reproduce the issue
26 | * Proof-of-concept or exploit code (if possible)
27 | * Impact of the issue, including how an attacker might exploit the issue
28 |
29 | This information will help us triage your report more quickly.
30 |
31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
32 |
33 | ## Preferred Languages
34 |
35 | We prefer all communications to be in English.
36 |
37 | ## Policy
38 |
39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
40 |
41 |
42 |
--------------------------------------------------------------------------------
/ci/ci.yml:
--------------------------------------------------------------------------------
1 | name: $(Build.Major).$(Build.Minor).$(BuildId)
2 | trigger:
3 | - master
4 |
5 | schedules:
6 | - cron: "0 9 * * Sat"
7 | displayName: 'Build for Component Governance'
8 | branches:
9 | include:
10 | - master
11 | always: true
12 |
13 | resources:
14 | repositories:
15 | - repository: self
16 | variables:
17 | Build.Major: 3
18 | Build.Minor: 1
19 | Build.Patch: $(Build.BuildId)
20 | Build.Configuration: 'Release'
21 | VersioningScheme: 'byPrereleaseNumber'
22 |
23 |
24 | jobs:
25 | - job: Windows
26 | pool:
27 | vmImage: 'windows-2022'
28 | steps:
29 | - template: steps-build-core.yml
30 | - template: steps-build-samples.yml
31 | - task: PublishBuildArtifacts@1
32 | condition: succeededOrFailed()
33 |
34 |
--------------------------------------------------------------------------------
/ci/signed.yml:
--------------------------------------------------------------------------------
1 | name: $(Build.Major).$(Build.Minor).$(BuildId)
2 | trigger:
3 | - master
4 | resources:
5 | repositories:
6 | - repository: self
7 | variables:
8 | Build.Major: 3
9 | Build.Minor: 1
10 | Build.Patch: $(Build.BuildId)
11 | Build.Configuration: 'Release'
12 | Assembly.Constants: 'SIGNED'
13 | VersioningScheme: 'byBuildNumber'
14 | DllsToScan: 'src\bin\Release\*\*.dll'
15 |
16 | jobs:
17 | - job: Windows
18 | pool:
19 | vmImage: 'windows-2022'
20 | steps:
21 | - template: steps-build-core.yml
22 | - template: steps-build-samples.yml
23 | - template: steps-sign.yml
24 | - template: steps-sdl-tools.yml
25 | - task: PublishBuildArtifacts@1
26 | condition: succeededOrFailed()
27 |
--------------------------------------------------------------------------------
/ci/steps-build-core.yml:
--------------------------------------------------------------------------------
1 | ##
2 | # Builds, tests & packs the Jupyter Core project
3 | ##
4 |
5 | steps:
6 | - task: DotNetCoreInstaller@1
7 | displayName: 'Install .NET Core SDK 3.1.300'
8 | inputs:
9 | version: 3.1.100
10 |
11 | - task: DotNetCoreCLI@2
12 | displayName: "Build and run unit tests on Core library"
13 | inputs:
14 | command: test
15 | arguments: '-c $(Build.Configuration) -v n /p:DefineConstants=$(Assembly.Constants)'
16 | projects: tests/core/core.csproj
17 |
18 | - task: DotNetCoreCLI@2
19 | displayName: "Pack Core library"
20 | inputs:
21 | command: pack
22 | configuration: $(Build.Configuration)
23 | packagesToPack: src/jupyter-core.csproj
24 | versioningScheme: $(VersioningScheme)
25 | majorVersion: $(Build.Major)
26 | minorVersion: $(Build.Minor)
27 | patchVersion: $(Build.Patch)
--------------------------------------------------------------------------------
/ci/steps-build-samples.yml:
--------------------------------------------------------------------------------
1 | ##
2 | # Runs sanity tests on the core and the sample kernels.
3 | ##
4 |
5 | steps:
6 | ##
7 | # The conda task is deprecated, so conda must be used inline.
8 | ##
9 | - pwsh: |
10 | $(CONDA)/scripts/conda.exe create --yes --quiet --name core_tests
11 | displayName: Create Anaconda environment
12 |
13 | - script: |
14 | $(CONDA)/scripts/conda.exe install --yes --quiet --name core_tests python=3.8 notebook jupyter_client pytest nose pip>=18.1
15 | displayName: Install Anaconda packages
16 |
17 | ##
18 | # Conda must be activated for every subsequent task.
19 | # The first line will execute shell functions that are dynamcally generated by conda and are a substitute for conda init.
20 | ##
21 | - pwsh: |
22 | (& "C:\Miniconda\scripts\conda.exe" "shell.powershell" "hook") | Out-String | Invoke-Expression
23 | conda activate core_tests
24 | conda info
25 | pip install jupyter_kernel_test
26 | displayName: 'Install additional dependencies from pip'
27 |
28 | # The version of pyzmq on Anaconda does not seem to work in Windows agents
29 | # as of November 2018: https://github.com/zeromq/pyzmq/issues/852
30 | - pwsh: |
31 | (& "C:\Miniconda\scripts\conda.exe" "shell.powershell" "hook") | Out-String | Invoke-Expression
32 | conda activate core_tests
33 | pip uninstall -y pyzmq
34 | pip install pyzmq
35 | displayName: 'Fix pyzmq on Windows'
36 | condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
37 |
38 | - pwsh: |
39 | (& "C:\Miniconda\scripts\conda.exe" "shell.powershell" "hook") | Out-String | Invoke-Expression
40 | conda activate core_tests
41 | dotnet run -c $(Build.Configuration) -v n -- install --develop
42 | displayName: "Add IEcho kernel to Jupyter."
43 | workingDirectory: examples/echo-kernel
44 |
45 | - pwsh: |
46 | (& "C:\Miniconda\scripts\conda.exe" "shell.powershell" "hook") | Out-String | Invoke-Expression
47 | conda activate core_tests
48 | dotnet run -c $(Build.Configuration) -v n -- install --develop
49 | displayName: "Add IMoon kernel to Jupyter."
50 | workingDirectory: examples/moon-kernel
51 |
52 | - pwsh: |
53 | (& "C:\Miniconda\scripts\conda.exe" "shell.powershell" "hook") | Out-String | Invoke-Expression
54 | conda activate core_tests
55 | py.test tests/protocol/
56 | displayName: "Run Jupyter protocol tests."
57 | workingDirectory: .
58 |
59 |
--------------------------------------------------------------------------------
/ci/steps-sdl-tools.yml:
--------------------------------------------------------------------------------
1 | ##
2 | # Runs scanning tools from security team.
3 | # See https://www.1eswiki.com/wiki/Secure_Development_Tools_Extension_For_Azure_DevOps
4 | ##
5 | steps:
6 | - task: ComponentGovernanceComponentDetection@0
7 | displayName: 'Components Detection'
8 | inputs:
9 | snapshotForceEnabled: true
10 | condition: and(succeededOrFailed(), variables.SDLScan)
11 |
12 |
13 | - task: securedevelopmentteam.vss-secure-development-tools.build-task-antimalware.AntiMalware@3
14 | displayName: 'SDL: Anti-Malware scan of build sources and/or artifacts'
15 | continueOnError: true
16 | condition: and(succeededOrFailed(), variables.SDLScan)
17 |
18 | - task: securedevelopmentteam.vss-secure-development-tools.build-task-autoapplicability.AutoApplicability@1
19 | displayName: 'SDL: Run AutoApplicability'
20 | inputs:
21 | ExternalRelease: true
22 | continueOnError: true
23 | condition: and(succeededOrFailed(), variables.SDLScan)
24 |
25 | - task: securedevelopmentteam.vss-secure-development-tools.build-task-binskim.BinSkim@3
26 | displayName: 'SDL: Analyze managed and unmanaged binaries (exe, dll) for security vulnerabilities (BinSkim)'
27 | inputs:
28 | InputType: Basic
29 | AnalyzeTarget: $(DllsToScan)
30 | AnalyzeVerbose: true
31 | AnalyzeHashes: true
32 | continueOnError: true
33 | condition: and(succeededOrFailed(), variables.SDLScan)
34 |
35 | - task: securedevelopmentteam.vss-secure-development-tools.build-task-codemetrics.CodeMetrics@1
36 | displayName: 'SDL: Analyze complexity of managed C# code (CodeMetrics)'
37 | inputs:
38 | Files: $(DllsToScan)
39 | continueOnError: true
40 | condition: and(succeededOrFailed(), variables.SDLScan)
41 |
42 | - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2
43 | displayName: 'SDL: Analyze source and build output text files for credentials (CredScan)'
44 | inputs:
45 | debugMode: false
46 | continueOnError: true
47 | condition: and(succeededOrFailed(), variables.SDLScan)
48 |
49 | - task: securedevelopmentteam.vss-secure-development-tools.build-task-fxcop.FxCop@2
50 | displayName: 'SDL: Analyze C# code (.NET framework only) for security vulnerabilities (FxCop)'
51 | inputs:
52 | inputType: Basic
53 | targets: $(DllsToScan)
54 | continueOnError: true
55 | condition: and(succeededOrFailed(), variables.SDLScan)
56 |
57 | - task: securedevelopmentteam.vss-secure-development-tools.build-task-prefast.SDLNativeRules@2
58 | displayName: 'SDL: Run the PREfast SDL Native Rules for MSBuild'
59 | continueOnError: true
60 | condition: and(succeededOrFailed(), variables.SDLScan)
61 |
62 | - task: securedevelopmentteam.vss-secure-development-tools.build-task-vulnerabilityassessment.VulnerabilityAssessment@0
63 | displayName: 'SDL: Create Vulnerability Assessment'
64 | continueOnError: true
65 | condition: and(succeededOrFailed(), variables.SDLScan)
66 |
67 | - task: securedevelopmentteam.vss-secure-development-tools.build-task-publishsecurityanalysislogs.PublishSecurityAnalysisLogs@2
68 | displayName: 'SDL: Publish Security Analysis Logs'
69 | continueOnError: true
70 | condition: and(succeededOrFailed(), variables.SDLScan)
71 |
72 | - task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@1
73 | displayName: 'SDL: Post Analysis'
74 | inputs:
75 | BinSkim: true
76 | CredScan: true
77 | SDLNativeRules: true
78 | continueOnError: true
79 | condition: and(succeededOrFailed(), variables.SDLScan)
80 |
--------------------------------------------------------------------------------
/ci/steps-sign.yml:
--------------------------------------------------------------------------------
1 | ##
2 | # Signing dlls & packages.
3 | ##
4 | steps:
5 |
6 | - task: DotNetCoreInstaller@1
7 | displayName: 'Install .NET Core SDK 2.2.401'
8 | inputs:
9 | version: 2.2.401
10 |
11 | - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
12 | displayName: 'Signing: strong name'
13 | inputs:
14 | ConnectedServiceName: CodeSign
15 | FolderPath: src\bin\$(Build.Configuration)
16 | CertificateId: 267
17 | OpusName: 'Microsoft Jupyter Core'
18 | OpusInfo: 'https://github.com/Microsoft/jupyter-core'
19 | SessionTimeout: 120
20 |
21 |
22 | - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
23 | displayName: 'Signing: authenticode'
24 | inputs:
25 | ConnectedServiceName: CodeSign
26 | FolderPath: src\bin\$(Build.Configuration)
27 | CertificateId: 400
28 | OpusName: 'Microsoft Jupyter Core'
29 | OpusInfo: 'https://github.com/Microsoft/jupyter-core'
30 | SessionTimeout: 120
31 |
32 |
33 | - task: DotNetCoreCLI@2
34 | displayName: "Pack signed version of Core library"
35 | inputs:
36 | command: pack
37 | configuration: $(Build.Configuration)
38 | nobuild: true
39 | packagesToPack: src/jupyter-core.csproj
40 | versioningScheme: $(VersioningScheme)
41 | majorVersion: $(Build.Major)
42 | minorVersion: $(Build.Minor)
43 | patchVersion: $(Build.Patch)
44 |
45 |
46 | - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
47 | displayName: 'Signing: Nugets'
48 | inputs:
49 | ConnectedServiceName: CodeSign
50 | FolderPath: '$(Build.ArtifactStagingDirectory)'
51 | Pattern: '*.nupkg'
52 | signConfigType: inlineSignParams
53 | inlineOperation: |
54 | [
55 | {
56 | "keyCode": "CP-401405",
57 | "operationSetCode": "NuGetSign",
58 | "parameters": [ ],
59 | "toolName": "sign",
60 | "toolVersion": "1.0"
61 | },
62 | {
63 | "keyCode": "CP-401405",
64 | "operationSetCode": "NuGetVerify",
65 | "parameters": [ ],
66 | "toolName": "sign",
67 | "toolVersion": "1.0"
68 | }
69 | ]
70 | SessionTimeout: 120
71 |
72 |
73 |
--------------------------------------------------------------------------------
/docfx.json:
--------------------------------------------------------------------------------
1 | {
2 | "metadata": [
3 | {
4 | "src": [
5 | {
6 | "files": [
7 | "src/**.csproj"
8 | ]
9 | }
10 | ],
11 | "dest": "docs/api",
12 | "disableGitFeatures": false,
13 | "disableDefaultFilter": false
14 | }
15 | ],
16 | "build": {
17 | "content": [
18 | {
19 | "files": [
20 | "docs/api/**.yml",
21 | "docs/api/index.md"
22 | ]
23 | },
24 | {
25 | "files": [
26 | "docs/articles/**.md",
27 | "docs/articles/**/toc.yml",
28 | "docs/toc.yml",
29 | "docs/*.md"
30 | ]
31 | }
32 | ],
33 | "resource": [
34 | {
35 | "files": [
36 | "docs/images/**"
37 | ]
38 | }
39 | ],
40 | "overwrite": [
41 | {
42 | "files": [
43 | "apidoc/**.md"
44 | ],
45 | "exclude": [
46 | "obj/**",
47 | "_site/**"
48 | ]
49 | }
50 | ],
51 | "dest": "_site",
52 | "globalMetadataFiles": [],
53 | "fileMetadataFiles": [],
54 | "template": [
55 | "default"
56 | ],
57 | "postProcessors": [],
58 | "markdownEngineName": "markdig",
59 | "noLangKeyword": false,
60 | "keepFileLink": false,
61 | "cleanupCacheHistory": false,
62 | "disableGitFeatures": false
63 | }
64 | }
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | ###############
2 | # folder #
3 | ###############
4 | /**/DROP/
5 | /**/TEMP/
6 | /**/packages/
7 | /**/bin/
8 | /**/obj/
9 | _site
10 |
--------------------------------------------------------------------------------
/docs/articles/intro.md:
--------------------------------------------------------------------------------
1 | # Add your introductions here!
2 |
--------------------------------------------------------------------------------
/docs/articles/toc.yml:
--------------------------------------------------------------------------------
1 | - name: Introduction
2 | href: intro.md
3 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # This is the **HOMEPAGE**.
2 | Refer to [Markdown](http://daringfireball.net/projects/markdown/) for how to write markdown files.
3 | ## Quick Start Notes:
4 | 1. Add images to the *images* folder if the file is referencing an image.
5 |
--------------------------------------------------------------------------------
/docs/toc.yml:
--------------------------------------------------------------------------------
1 | - name: Articles
2 | href: articles/
3 | - name: Api Documentation
4 | href: api/
5 |
--------------------------------------------------------------------------------
/examples/echo-kernel/Echo Kernel.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "metadata": {},
7 | "outputs": [
8 | {
9 | "data": {
10 | "text/plain": [
11 | "%dne"
12 | ]
13 | },
14 | "execution_count": 1,
15 | "metadata": {},
16 | "output_type": "execute_result"
17 | }
18 | ],
19 | "source": [
20 | "%dne"
21 | ]
22 | },
23 | {
24 | "cell_type": "code",
25 | "execution_count": 2,
26 | "metadata": {},
27 | "outputs": [
28 | {
29 | "data": {
30 | "text/plain": [
31 | "bar"
32 | ]
33 | },
34 | "execution_count": 2,
35 | "metadata": {},
36 | "output_type": "execute_result"
37 | }
38 | ],
39 | "source": [
40 | "bar"
41 | ]
42 | },
43 | {
44 | "cell_type": "code",
45 | "execution_count": 3,
46 | "metadata": {},
47 | "outputs": [
48 | {
49 | "data": {
50 | "text/plain": [
51 | "baz"
52 | ]
53 | },
54 | "execution_count": 3,
55 | "metadata": {},
56 | "output_type": "execute_result"
57 | }
58 | ],
59 | "source": [
60 | "baz"
61 | ]
62 | },
63 | {
64 | "cell_type": "code",
65 | "execution_count": 4,
66 | "metadata": {},
67 | "outputs": [
68 | {
69 | "data": {
70 | "text/html": [
71 | "
"
72 | ],
73 | "text/plain": [
74 | "%dne, bar, baz, %history"
75 | ]
76 | },
77 | "execution_count": 4,
78 | "metadata": {},
79 | "output_type": "execute_result"
80 | }
81 | ],
82 | "source": [
83 | "%history"
84 | ]
85 | },
86 | {
87 | "cell_type": "code",
88 | "execution_count": 5,
89 | "metadata": {},
90 | "outputs": [
91 | {
92 | "data": {
93 | "text/html": [
94 | "Component | Version |
---|
iecho | 1.0.0.0 |
Jupyter Core | 1.0.0.0 |
.NET Runtime | .NETCoreApp,Version=v3.0 |
"
95 | ],
96 | "text/plain": [
97 | "Component Version\r\n",
98 | "------------ ------------------------\r\n",
99 | "iecho 1.0.0.0\r\n",
100 | "Jupyter Core 1.0.0.0\r\n",
101 | ".NET Runtime .NETCoreApp,Version=v3.0\r\n"
102 | ]
103 | },
104 | "metadata": {},
105 | "output_type": "display_data"
106 | }
107 | ],
108 | "source": [
109 | "%version"
110 | ]
111 | },
112 | {
113 | "cell_type": "code",
114 | "execution_count": 6,
115 | "metadata": {},
116 | "outputs": [
117 | {
118 | "data": {
119 | "text/html": [
120 | " %history
Displays a list of commands run so far this session.
"
121 | ],
122 | "text/plain": [
123 | "%history:\n",
124 | "Displays a list of commands run so far this session."
125 | ]
126 | },
127 | "execution_count": 6,
128 | "metadata": {},
129 | "output_type": "execute_result"
130 | }
131 | ],
132 | "source": [
133 | "%history?"
134 | ]
135 | },
136 | {
137 | "cell_type": "code",
138 | "execution_count": null,
139 | "metadata": {},
140 | "outputs": [],
141 | "source": []
142 | }
143 | ],
144 | "metadata": {
145 | "kernelspec": {
146 | "display_name": "IEcho",
147 | "language": "Echo",
148 | "name": "iecho"
149 | },
150 | "language_info": {
151 | "file_extension": ".txt",
152 | "mimetype": "text/plain",
153 | "name": "Echo",
154 | "version": "0.1"
155 | }
156 | },
157 | "nbformat": 4,
158 | "nbformat_minor": 2
159 | }
160 |
--------------------------------------------------------------------------------
/examples/echo-kernel/EchoEngine.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | using System;
5 | using System.Linq;
6 | using System.Collections.Generic;
7 | using Microsoft.Extensions.Options;
8 | using Microsoft.Extensions.Logging;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 |
12 | namespace Microsoft.Jupyter.Core
13 | {
14 |
15 | public class EchoEngine : BaseEngine
16 | {
17 | public EchoEngine(
18 | IShellServer shell,
19 | IShellRouter router,
20 | IOptions context,
21 | ILogger logger,
22 | IServiceProvider serviceProvider
23 | ) : base(shell, router, context, logger, serviceProvider) { }
24 |
25 | public override async Task ExecuteMundane(string input, IChannel channel) =>
26 | (Program.ShoutOption.HasValue() ? input.ToUpper() : input).ToExecutionResult();
27 |
28 | public override async Task> CompleteMundane(string code, int cursorPos)
29 | {
30 | Logger.LogInformation("Got completion request inside the echo kernel!");
31 | return new List
32 | {
33 | new Completion
34 | {
35 | CursorStart = cursorPos,
36 | CursorEnd = cursorPos,
37 | Text = "foo"
38 | },
39 | new Completion
40 | {
41 | CursorStart = cursorPos,
42 | CursorEnd = cursorPos,
43 | Text = "bar"
44 | }
45 | };
46 | }
47 |
48 | [MagicCommand(
49 | "%tick",
50 | "Writes some ticks to demonstrate updatable display data."
51 | )]
52 | public async Task ExecuteTick(string code, IChannel channel)
53 | {
54 | var tickMessage = "Tick.";
55 | var updatable = channel.DisplayUpdatable(tickMessage);
56 |
57 | foreach (var idx in Enumerable.Range(0, 10))
58 | {
59 | tickMessage += ".";
60 | Thread.Sleep(1000);
61 | updatable.Update(tickMessage);
62 | }
63 |
64 | return ExecuteStatus.Ok.ToExecutionResult();
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/examples/echo-kernel/KernelProperties.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using System.Collections.Generic;
5 |
6 | namespace Microsoft.Jupyter.Core
7 | {
8 | internal static class Constants
9 | {
10 | internal static KernelProperties PROPERTIES = new KernelProperties
11 | {
12 | // Name of the kernel as it appears outside of code.
13 | FriendlyName = "IEcho",
14 | // A short name for the kernel to be used in code, such as when
15 | // calling from jupyter_client.
16 | KernelName = "iecho",
17 | // The version of the kernel.
18 | KernelVersion = typeof(Program).Assembly.GetName().Version.ToString(),
19 | // Name of the kernel as it should appear in lists of available kernels.
20 | DisplayName = "Echo",
21 |
22 | // Name of the language implemented by the kernel.
23 | // Note that this property is used to set the syntax highlighting
24 | // mode in some clients, such as Jupyter Notebook.
25 | LanguageName = "Echo",
26 | // Version of the language implemeted by the kernel.
27 | LanguageVersion = "0.1",
28 | // The MIME type of the language implemented by the kernel.
29 | // This property is used mainly for providing "plain" downloads from
30 | // Jupyter clients.
31 | LanguageMimeType = MimeTypes.PlainText,
32 | // The file extension for source files written in the language
33 | // implemented by the kernel.
34 | // This property is used mainly for providing "plain" downloads from
35 | // Jupyter clients.
36 | LanguageFileExtension = ".txt",
37 |
38 | // An extended description of the kernel.
39 | Description = "A simple kernel that echos its input."
40 | };
41 | }
42 | }
--------------------------------------------------------------------------------
/examples/echo-kernel/Program.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using System;
5 | using System.IO;
6 | using System.Diagnostics;
7 | using System.ComponentModel.DataAnnotations;
8 | using Microsoft.Extensions.Logging;
9 | using Newtonsoft.Json;
10 | using System.Collections.Generic;
11 | using static Microsoft.Jupyter.Core.Constants;
12 | using Microsoft.Extensions.DependencyInjection;
13 | using McMaster.Extensions.CommandLineUtils;
14 |
15 | namespace Microsoft.Jupyter.Core
16 | {
17 | class Program
18 | {
19 | public static void Init(ServiceCollection serviceCollection) =>
20 | serviceCollection
21 | // Start a new service for the ReplEngine.
22 | .AddSingleton();
23 |
24 | internal static CommandOption ShoutOption;
25 |
26 | public static int Main(string[] args) {
27 | var app = new KernelApplication(
28 | PROPERTIES,
29 | Init
30 | );
31 | CommandOption shoutInstallOption = null;
32 |
33 | return app
34 | .AddInstallCommand(
35 | installCmd => {
36 | shoutInstallOption = installCmd.Option(
37 | "--shout",
38 | "Shout back when echoing input.",
39 | CommandOptionType.NoValue
40 | );
41 | }
42 | )
43 | .AddKernelCommand(
44 | kernelCmd => {
45 | ShoutOption = kernelCmd.Option(
46 | "--shout",
47 | "Shout back when echoing input.",
48 | CommandOptionType.NoValue
49 | );
50 | }
51 | )
52 | .WithKernelArguments(
53 | () => shoutInstallOption.HasValue() ? new[] {"--shout"} : new string[] {}
54 | )
55 | .WithKernelSpecResources(
56 | new Dictionary
57 | {
58 | ["logo-64x64.png"] = "echo-kernel.res.logo-64x64.png"
59 | }
60 | )
61 | .Execute(args);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/examples/echo-kernel/README.md:
--------------------------------------------------------------------------------
1 | # IEcho Example Kernel #
2 |
3 | This project provides an example of how to write your own Jupyter kernels using Microsoft.Jupyter.Core.
4 | The kernel implemented by this project is a simple "echo" kernel that repeats its input as output.
5 |
6 | ## Installing from NuGet.org ##
7 |
8 | ```
9 | dotnet tool install -g Microsoft.Jupyter.Example.IEcho
10 | dotnet iecho install
11 | ```
12 |
13 | ## Installing from a Local Build ##
14 |
15 | ```
16 | cd echo-kernel
17 | dotnet pack
18 | dotnet tool install -g Microsoft.Jupyter.Example.IEcho --add-source .\bin\Debug\
19 | dotnet iecho install
20 | ```
21 |
22 | ## Installing for Local Development ##
23 |
24 | ```
25 | cd echo-kernel
26 | dotnet run -- install --develop
27 | ```
28 |
--------------------------------------------------------------------------------
/examples/echo-kernel/echo-kernel.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | True
5 | Exe
6 | netcoreapp3.1
7 |
8 |
12 | dotnet-iecho
13 | Microsoft.Jupyter.Example.IEcho
14 | 1.0
15 |
16 | true
17 | DotnetCliTool
18 | Microsoft
19 | An example Jupyter kernel that echoes its input back as output.
20 | © Microsoft Corporation. All rights reserved.
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/examples/echo-kernel/res/logo-64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/jupyter-core/15a08a57215b0683b59bb04298c58a4f2bbb6aa0/examples/echo-kernel/res/logo-64x64.png
--------------------------------------------------------------------------------
/examples/moon-kernel/DynValueConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using MoonSharp.Interpreter;
4 | using Newtonsoft.Json;
5 | using Newtonsoft.Json.Linq;
6 |
7 | namespace Microsoft.Jupyter.Core
8 | {
9 | public class DynValueConverter : JsonConverter
10 | {
11 | public override DynValue ReadJson(JsonReader reader, Type objectType, DynValue existingValue, bool hasExistingValue, JsonSerializer serializer)
12 | {
13 | throw new NotImplementedException();
14 | }
15 |
16 | public override void WriteJson(JsonWriter writer, DynValue value, JsonSerializer serializer)
17 | {
18 | var obj = value.ToObject();
19 | JToken token;
20 | switch (obj)
21 | {
22 | case MoonSharp.Interpreter.Closure closure:
23 | token = JToken.FromObject(new Dictionary
24 | {
25 | ["@closure"] = closure.ToString()
26 | });
27 | token.WriteTo(writer);
28 | return;
29 |
30 | default:
31 | // See https://github.com/JamesNK/Newtonsoft.Json/issues/386#issuecomment-421161191
32 | // for why this works to pass through.
33 | token = JToken.FromObject(obj);
34 | token.WriteTo(writer);
35 | return;
36 | }
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/examples/moon-kernel/KernelProperties.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using MoonSharp.Interpreter.REPL;
5 |
6 | namespace Microsoft.Jupyter.Core
7 | {
8 |
9 | internal static class Constants
10 | {
11 | internal static KernelProperties PROPERTIES = new KernelProperties
12 | {
13 | FriendlyName = "IMoon",
14 | KernelName = "imoon",
15 | KernelVersion = typeof(Program).Assembly.GetName().Version.ToString(),
16 | DisplayName = "Lua (MoonScript)",
17 |
18 | LanguageName = "Lua",
19 | LanguageVersion = "0.1",
20 | LanguageMimeType = "text/plain",
21 | LanguageFileExtension = ".lua",
22 |
23 | Description = "Runs Lua using the MoonScript interpreter."
24 | }
25 | .WithAdditionalVersion("MoonScript");
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/moon-kernel/MoonEngine.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using System;
5 | using System.Linq;
6 | using System.Collections.Generic;
7 | using MoonSharp.Interpreter;
8 | using MoonSharp.Interpreter.REPL;
9 | using Microsoft.Extensions.Options;
10 | using Microsoft.Extensions.Logging;
11 | using Newtonsoft.Json;
12 | using System.Threading.Tasks;
13 |
14 | namespace Microsoft.Jupyter.Core
15 | {
16 |
17 | public class MoonEngine : BaseEngine
18 | {
19 | private ReplInterpreter interp;
20 | private Action printFn = null;
21 |
22 | public MoonEngine(
23 | IShellServer shell,
24 | IShellRouter router,
25 | IOptions context,
26 | ILogger logger,
27 | IServiceProvider serviceProvider
28 | ) : base(shell, router, context, logger, serviceProvider)
29 | {
30 | RegisterJsonEncoder(
31 | new DynValueConverter()
32 | );
33 | RegisterDisplayEncoder(
34 | MimeTypes.Markdown,
35 | displayable => {
36 | if (displayable is IEnumerable list)
37 | {
38 | return String.Join(
39 | "\n",
40 | list.Select(item => $"- {item}")
41 | );
42 | }
43 | else return $"`{displayable}`";
44 | }
45 | );
46 | var script = new Script();
47 | script.Options.DebugPrint = str => printFn?.Invoke(str);
48 | interp = new ReplInterpreter(script);
49 | }
50 |
51 | public override async Task ExecuteMundane(string input, IChannel channel)
52 | {
53 | var oldAction = printFn;
54 | printFn = channel.Stdout;
55 | try
56 | {
57 | var result = interp.Evaluate(input);
58 | if (result == null)
59 | {
60 | channel.Stderr("Interpreter returned null, this is typically due to incomplete input.");
61 | return ExecuteStatus.Error.ToExecutionResult();
62 | }
63 | else if (result.ToObject() != null)
64 | {
65 | return result.ToExecutionResult();
66 | }
67 | else
68 | {
69 | return ExecuteStatus.Ok.ToExecutionResult();
70 | }
71 | }
72 | catch (Exception ex)
73 | {
74 | channel.Stderr(ex.ToString());
75 | return ExecuteStatus.Error.ToExecutionResult();
76 | }
77 | finally
78 | {
79 | printFn = oldAction;
80 | }
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/examples/moon-kernel/MoonScript Kernel.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "metadata": {},
7 | "outputs": [
8 | {
9 | "data": {
10 | "application/json": "4.0",
11 | "text/markdown": [
12 | "`4`"
13 | ],
14 | "text/plain": [
15 | "4"
16 | ]
17 | },
18 | "execution_count": 1,
19 | "metadata": {},
20 | "output_type": "execute_result"
21 | }
22 | ],
23 | "source": [
24 | "return 1 + 3"
25 | ]
26 | },
27 | {
28 | "cell_type": "code",
29 | "execution_count": 2,
30 | "metadata": {},
31 | "outputs": [
32 | {
33 | "data": {
34 | "application/json": "120.0",
35 | "text/markdown": [
36 | "`120`"
37 | ],
38 | "text/plain": [
39 | "120"
40 | ]
41 | },
42 | "execution_count": 2,
43 | "metadata": {},
44 | "output_type": "execute_result"
45 | }
46 | ],
47 | "source": [
48 | "-- defines a factorial function\n",
49 | " function fact (n)\n",
50 | " if (n == 0) then\n",
51 | " return 1\n",
52 | " else\n",
53 | " return n*fact(n - 1)\n",
54 | " end\n",
55 | " end\n",
56 | "\n",
57 | " return fact(5)"
58 | ]
59 | },
60 | {
61 | "cell_type": "code",
62 | "execution_count": 3,
63 | "metadata": {},
64 | "outputs": [
65 | {
66 | "data": {
67 | "application/json": "{\"@closure\":\"MoonSharp.Interpreter.Closure\"}",
68 | "text/markdown": [
69 | "`(Function 00000074)`"
70 | ],
71 | "text/plain": [
72 | "(Function 00000074)"
73 | ]
74 | },
75 | "execution_count": 3,
76 | "metadata": {},
77 | "output_type": "execute_result"
78 | }
79 | ],
80 | "source": [
81 | "return fact"
82 | ]
83 | },
84 | {
85 | "cell_type": "code",
86 | "execution_count": 4,
87 | "metadata": {},
88 | "outputs": [
89 | {
90 | "data": {
91 | "application/json": "[\"return 1 + 3\",\"-- defines a factorial function\\n function fact (n)\\n if (n == 0) then\\n return 1\\n else\\n return n*fact(n - 1)\\n end\\n end\\n\\n return fact(5)\",\"return fact\",\"%history\"]",
92 | "text/html": [
93 | "- return 1 + 3
- -- defines a factorial function\n",
94 | " function fact (n)\n",
95 | " if (n == 0) then\n",
96 | " return 1\n",
97 | " else\n",
98 | " return n*fact(n - 1)\n",
99 | " end\n",
100 | " end\n",
101 | "\n",
102 | " return fact(5)
- return fact
- %history
"
103 | ],
104 | "text/markdown": [
105 | "- return 1 + 3\n",
106 | "- -- defines a factorial function\n",
107 | " function fact (n)\n",
108 | " if (n == 0) then\n",
109 | " return 1\n",
110 | " else\n",
111 | " return n*fact(n - 1)\n",
112 | " end\n",
113 | " end\n",
114 | "\n",
115 | " return fact(5)\n",
116 | "- return fact\n",
117 | "- %history"
118 | ],
119 | "text/plain": [
120 | "return 1 + 3, -- defines a factorial function\n",
121 | " function fact (n)\n",
122 | " if (n == 0) then\n",
123 | " return 1\n",
124 | " else\n",
125 | " return n*fact(n - 1)\n",
126 | " end\n",
127 | " end\n",
128 | "\n",
129 | " return fact(5), return fact, %history"
130 | ]
131 | },
132 | "execution_count": 4,
133 | "metadata": {},
134 | "output_type": "execute_result"
135 | }
136 | ],
137 | "source": [
138 | "%history"
139 | ]
140 | },
141 | {
142 | "cell_type": "code",
143 | "execution_count": 5,
144 | "metadata": {},
145 | "outputs": [
146 | {
147 | "data": {
148 | "application/json": "720.0",
149 | "text/markdown": [
150 | "`720`"
151 | ],
152 | "text/plain": [
153 | "720"
154 | ]
155 | },
156 | "execution_count": 5,
157 | "metadata": {},
158 | "output_type": "execute_result"
159 | }
160 | ],
161 | "source": [
162 | "return fact(6)"
163 | ]
164 | },
165 | {
166 | "cell_type": "code",
167 | "execution_count": 6,
168 | "metadata": {},
169 | "outputs": [
170 | {
171 | "name": "stdout",
172 | "output_type": "stream",
173 | "text": [
174 | "Hello, world"
175 | ]
176 | }
177 | ],
178 | "source": [
179 | "print('Hello, world')"
180 | ]
181 | },
182 | {
183 | "cell_type": "code",
184 | "execution_count": 7,
185 | "metadata": {},
186 | "outputs": [
187 | {
188 | "data": {
189 | "application/json": "{\"Name\":\"%history\",\"Kind\":0,\"Documentation\":{\"Full\":null,\"Summary\":\"Displays a list of commands run so far this session.\"}}",
190 | "text/html": [
191 | " %history
Displays a list of commands run so far this session.
"
192 | ],
193 | "text/markdown": [
194 | "`Microsoft.Jupyter.Core.MagicSymbol`"
195 | ],
196 | "text/plain": [
197 | "%history:\n",
198 | "Displays a list of commands run so far this session."
199 | ]
200 | },
201 | "execution_count": 7,
202 | "metadata": {},
203 | "output_type": "execute_result"
204 | }
205 | ],
206 | "source": [
207 | "%history?"
208 | ]
209 | },
210 | {
211 | "cell_type": "code",
212 | "execution_count": 8,
213 | "metadata": {},
214 | "outputs": [
215 | {
216 | "data": {
217 | "application/json": "{\"rows\":[{\"Item1\":\"imoon\",\"Item2\":\"1.0.0.0\"},{\"Item1\":\"Jupyter Core\",\"Item2\":\"1.0.0.0\"},{\"Item1\":\".NET Runtime\",\"Item2\":\".NETCoreApp,Version=v3.0\"},{\"Item1\":\"MoonScript\",\"Item2\":\"2.0.0.0\"}]}",
218 | "text/html": [
219 | "Component | Version |
---|
imoon | 1.0.0.0 |
Jupyter Core | 1.0.0.0 |
.NET Runtime | .NETCoreApp,Version=v3.0 |
MoonScript | 2.0.0.0 |
"
220 | ],
221 | "text/markdown": [
222 | "`Microsoft.Jupyter.Core.Table`1[System.ValueTuple`2[System.String,System.String]]`"
223 | ],
224 | "text/plain": [
225 | "Component Version\r\n",
226 | "------------ ------------------------\r\n",
227 | "imoon 1.0.0.0\r\n",
228 | "Jupyter Core 1.0.0.0\r\n",
229 | ".NET Runtime .NETCoreApp,Version=v3.0\r\n",
230 | "MoonScript 2.0.0.0\r\n"
231 | ]
232 | },
233 | "metadata": {},
234 | "output_type": "display_data"
235 | }
236 | ],
237 | "source": [
238 | "%version"
239 | ]
240 | },
241 | {
242 | "cell_type": "code",
243 | "execution_count": null,
244 | "metadata": {},
245 | "outputs": [],
246 | "source": []
247 | }
248 | ],
249 | "metadata": {
250 | "kernelspec": {
251 | "display_name": "IMoon",
252 | "language": "Lua",
253 | "name": "imoon"
254 | },
255 | "language_info": {
256 | "file_extension": ".lua",
257 | "mimetype": "text/plain",
258 | "name": "Lua",
259 | "version": "0.1"
260 | }
261 | },
262 | "nbformat": 4,
263 | "nbformat_minor": 2
264 | }
265 |
--------------------------------------------------------------------------------
/examples/moon-kernel/Program.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using System;
5 | using System.IO;
6 | using System.Diagnostics;
7 | using System.ComponentModel.DataAnnotations;
8 | using Microsoft.Extensions.Logging;
9 | using Newtonsoft.Json;
10 | using System.Collections.Generic;
11 | using static Microsoft.Jupyter.Core.Constants;
12 | using Microsoft.Extensions.DependencyInjection;
13 |
14 | namespace Microsoft.Jupyter.Core
15 | {
16 | class Program
17 | {
18 | public static void Init(ServiceCollection serviceCollection) =>
19 | serviceCollection
20 | // Start a new service for the ReplEngine.
21 | .AddSingleton();
22 |
23 | public static int Main(string[] args) {
24 | var app = new KernelApplication(
25 | PROPERTIES,
26 | Init
27 | )
28 | .ConfigureLogging(loggingBuilder =>
29 | {
30 | loggingBuilder.AddFile(
31 | Path.Join(Directory.GetCurrentDirectory(), "imoon.log"),
32 | minimumLevel: LogLevel.Debug
33 | );
34 | });
35 |
36 | return app.WithDefaultCommands().Execute(args);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/examples/moon-kernel/moon-kernel.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | dotnet-imoon
5 | True
6 | Exe
7 | netcoreapp3.1
8 | 1.0
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/jupyter-core.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26124.0
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "jupyter-core", "src\jupyter-core.csproj", "{D1C7D204-B6C5-4789-8035-ED8B594DCEB3}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{EF78B627-9418-4608-A773-349165C55BFC}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "moon-kernel", "examples\moon-kernel\moon-kernel.csproj", "{C5D59812-BFC0-4AFB-8D81-9D7F0C362079}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "echo-kernel", "examples\echo-kernel\echo-kernel.csproj", "{95C93365-CD6B-46E7-8CFC-2F10D4E42536}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{99727866-8D93-42FC-9E35-7A7ED15EFB46}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "core", "tests\core\core.csproj", "{551FFA2A-BE5F-4DB0-92C5-654F5EBAD214}"
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Debug|x64 = Debug|x64
22 | Debug|x86 = Debug|x86
23 | Release|Any CPU = Release|Any CPU
24 | Release|x64 = Release|x64
25 | Release|x86 = Release|x86
26 | EndGlobalSection
27 | GlobalSection(SolutionProperties) = preSolution
28 | HideSolutionNode = FALSE
29 | EndGlobalSection
30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
31 | {D1C7D204-B6C5-4789-8035-ED8B594DCEB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32 | {D1C7D204-B6C5-4789-8035-ED8B594DCEB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
33 | {D1C7D204-B6C5-4789-8035-ED8B594DCEB3}.Debug|x64.ActiveCfg = Debug|Any CPU
34 | {D1C7D204-B6C5-4789-8035-ED8B594DCEB3}.Debug|x64.Build.0 = Debug|Any CPU
35 | {D1C7D204-B6C5-4789-8035-ED8B594DCEB3}.Debug|x86.ActiveCfg = Debug|Any CPU
36 | {D1C7D204-B6C5-4789-8035-ED8B594DCEB3}.Debug|x86.Build.0 = Debug|Any CPU
37 | {D1C7D204-B6C5-4789-8035-ED8B594DCEB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
38 | {D1C7D204-B6C5-4789-8035-ED8B594DCEB3}.Release|Any CPU.Build.0 = Release|Any CPU
39 | {D1C7D204-B6C5-4789-8035-ED8B594DCEB3}.Release|x64.ActiveCfg = Release|Any CPU
40 | {D1C7D204-B6C5-4789-8035-ED8B594DCEB3}.Release|x64.Build.0 = Release|Any CPU
41 | {D1C7D204-B6C5-4789-8035-ED8B594DCEB3}.Release|x86.ActiveCfg = Release|Any CPU
42 | {D1C7D204-B6C5-4789-8035-ED8B594DCEB3}.Release|x86.Build.0 = Release|Any CPU
43 | {C5D59812-BFC0-4AFB-8D81-9D7F0C362079}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
44 | {C5D59812-BFC0-4AFB-8D81-9D7F0C362079}.Debug|Any CPU.Build.0 = Debug|Any CPU
45 | {C5D59812-BFC0-4AFB-8D81-9D7F0C362079}.Debug|x64.ActiveCfg = Debug|Any CPU
46 | {C5D59812-BFC0-4AFB-8D81-9D7F0C362079}.Debug|x64.Build.0 = Debug|Any CPU
47 | {C5D59812-BFC0-4AFB-8D81-9D7F0C362079}.Debug|x86.ActiveCfg = Debug|Any CPU
48 | {C5D59812-BFC0-4AFB-8D81-9D7F0C362079}.Debug|x86.Build.0 = Debug|Any CPU
49 | {C5D59812-BFC0-4AFB-8D81-9D7F0C362079}.Release|Any CPU.ActiveCfg = Release|Any CPU
50 | {C5D59812-BFC0-4AFB-8D81-9D7F0C362079}.Release|Any CPU.Build.0 = Release|Any CPU
51 | {C5D59812-BFC0-4AFB-8D81-9D7F0C362079}.Release|x64.ActiveCfg = Release|Any CPU
52 | {C5D59812-BFC0-4AFB-8D81-9D7F0C362079}.Release|x64.Build.0 = Release|Any CPU
53 | {C5D59812-BFC0-4AFB-8D81-9D7F0C362079}.Release|x86.ActiveCfg = Release|Any CPU
54 | {C5D59812-BFC0-4AFB-8D81-9D7F0C362079}.Release|x86.Build.0 = Release|Any CPU
55 | {95C93365-CD6B-46E7-8CFC-2F10D4E42536}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
56 | {95C93365-CD6B-46E7-8CFC-2F10D4E42536}.Debug|Any CPU.Build.0 = Debug|Any CPU
57 | {95C93365-CD6B-46E7-8CFC-2F10D4E42536}.Debug|x64.ActiveCfg = Debug|Any CPU
58 | {95C93365-CD6B-46E7-8CFC-2F10D4E42536}.Debug|x64.Build.0 = Debug|Any CPU
59 | {95C93365-CD6B-46E7-8CFC-2F10D4E42536}.Debug|x86.ActiveCfg = Debug|Any CPU
60 | {95C93365-CD6B-46E7-8CFC-2F10D4E42536}.Debug|x86.Build.0 = Debug|Any CPU
61 | {95C93365-CD6B-46E7-8CFC-2F10D4E42536}.Release|Any CPU.ActiveCfg = Release|Any CPU
62 | {95C93365-CD6B-46E7-8CFC-2F10D4E42536}.Release|Any CPU.Build.0 = Release|Any CPU
63 | {95C93365-CD6B-46E7-8CFC-2F10D4E42536}.Release|x64.ActiveCfg = Release|Any CPU
64 | {95C93365-CD6B-46E7-8CFC-2F10D4E42536}.Release|x64.Build.0 = Release|Any CPU
65 | {95C93365-CD6B-46E7-8CFC-2F10D4E42536}.Release|x86.ActiveCfg = Release|Any CPU
66 | {95C93365-CD6B-46E7-8CFC-2F10D4E42536}.Release|x86.Build.0 = Release|Any CPU
67 | {551FFA2A-BE5F-4DB0-92C5-654F5EBAD214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
68 | {551FFA2A-BE5F-4DB0-92C5-654F5EBAD214}.Debug|Any CPU.Build.0 = Debug|Any CPU
69 | {551FFA2A-BE5F-4DB0-92C5-654F5EBAD214}.Debug|x64.ActiveCfg = Debug|Any CPU
70 | {551FFA2A-BE5F-4DB0-92C5-654F5EBAD214}.Debug|x64.Build.0 = Debug|Any CPU
71 | {551FFA2A-BE5F-4DB0-92C5-654F5EBAD214}.Debug|x86.ActiveCfg = Debug|Any CPU
72 | {551FFA2A-BE5F-4DB0-92C5-654F5EBAD214}.Debug|x86.Build.0 = Debug|Any CPU
73 | {551FFA2A-BE5F-4DB0-92C5-654F5EBAD214}.Release|Any CPU.ActiveCfg = Release|Any CPU
74 | {551FFA2A-BE5F-4DB0-92C5-654F5EBAD214}.Release|Any CPU.Build.0 = Release|Any CPU
75 | {551FFA2A-BE5F-4DB0-92C5-654F5EBAD214}.Release|x64.ActiveCfg = Release|Any CPU
76 | {551FFA2A-BE5F-4DB0-92C5-654F5EBAD214}.Release|x64.Build.0 = Release|Any CPU
77 | {551FFA2A-BE5F-4DB0-92C5-654F5EBAD214}.Release|x86.ActiveCfg = Release|Any CPU
78 | {551FFA2A-BE5F-4DB0-92C5-654F5EBAD214}.Release|x86.Build.0 = Release|Any CPU
79 | EndGlobalSection
80 | GlobalSection(NestedProjects) = preSolution
81 | {C5D59812-BFC0-4AFB-8D81-9D7F0C362079} = {EF78B627-9418-4608-A773-349165C55BFC}
82 | {95C93365-CD6B-46E7-8CFC-2F10D4E42536} = {EF78B627-9418-4608-A773-349165C55BFC}
83 | {551FFA2A-BE5F-4DB0-92C5-654F5EBAD214} = {99727866-8D93-42FC-9E35-7A7ED15EFB46}
84 | EndGlobalSection
85 | EndGlobal
86 |
--------------------------------------------------------------------------------
/src/Data/ConnectionInfo.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using System.Net;
5 | using System.Text;
6 | using System.Linq;
7 | using NetMQ;
8 | using Newtonsoft.Json;
9 | using System.Collections.Generic;
10 | using System.Security.Cryptography;
11 | using System;
12 | using Microsoft.Jupyter.Core.Protocol;
13 |
14 | namespace Microsoft.Jupyter.Core
15 | {
16 | ///
17 | /// Represents the information stored in a Jupyter connection file.
18 | /// See https://jupyter-client.readthedocs.io/en/stable/kernels.html#connection-files
19 | /// for details.
20 | ///
21 | [JsonObject(MemberSerialization.OptIn)]
22 | public class ConnectionInfo
23 | {
24 | // As per documentation at https://jupyter-client.readthedocs.io/en/stable/kernels.html#connection-files,
25 | // an example connection file looks like the following:
26 | //
27 | // {
28 | // "control_port": 50160,
29 | // "shell_port": 57503,
30 | // "transport": "tcp",
31 | // "signature_scheme": "hmac-sha256",
32 | // "stdin_port": 52597,
33 | // "hb_port": 42540,
34 | // "ip": "127.0.0.1",
35 | // "iopub_port": 40885,
36 | // "key": "a0436f6c-1916-498b-8eb9-e81ab9368e84"
37 | // }
38 |
39 | #region Port Information
40 |
41 | [JsonProperty("control_port")]
42 | public int ControlPort { get; set; }
43 |
44 | [JsonProperty("shell_port")]
45 | public int ShellPort { get; set; }
46 |
47 | [JsonProperty("hb_port")]
48 | public int HeartbeatPort { get; set; }
49 |
50 | [JsonProperty("iopub_port")]
51 | public int IoPubPort { get; set; }
52 |
53 | [JsonProperty("stdin_port")]
54 | public int StdInPort { get; set; }
55 |
56 | #endregion
57 |
58 | #region Transport Information
59 |
60 | [JsonProperty("transport")]
61 | public Transport Transport { get; set; }
62 |
63 | [JsonProperty("ip")]
64 | [JsonConverter(typeof(IpAddressConverter))]
65 | public IPAddress IpAddress { get; set; }
66 |
67 | #endregion
68 |
69 | #region Authentication Information
70 |
71 | [JsonProperty("key")]
72 | public string Key { get; set; }
73 |
74 | [JsonProperty("signature_scheme")]
75 | public SignatureScheme SignatureScheme { get; set; }
76 |
77 | #endregion
78 |
79 | #region Metadata
80 |
81 | [JsonProperty("kernel_name")]
82 | public string KernelName { get; set; }
83 |
84 | #endregion
85 |
86 | #region ZeroMQ Configuration
87 | // ZeroMQ uses a notion of "address" that combines both IP addresses
88 | // and ports, similar to a URL. Thus, we put some logic here to
89 | // construct ZeroMQ addresses from the rest of the connection info.
90 |
91 | // FIXME: consolidate address logic here.
92 | public string HeartbeatZmqAddress {
93 | get {
94 | var protocol = Enum.GetName(typeof(Transport), Transport).ToLower();
95 | return $"{protocol}://{IpAddress}:{HeartbeatPort}";
96 | }
97 | }
98 |
99 | public string ShellZmqAddress {
100 | get {
101 | var protocol = Enum.GetName(typeof(Transport), Transport).ToLower();
102 | return $"{protocol}://{IpAddress}:{ShellPort}";
103 | }
104 | }
105 |
106 | public string ControlZmqAddress {
107 | get {
108 | var protocol = Enum.GetName(typeof(Transport), Transport).ToLower();
109 | return $"{protocol}://{IpAddress}:{ControlPort}";
110 | }
111 | }
112 |
113 | public string IoPubZmqAddress {
114 | get {
115 | var protocol = Enum.GetName(typeof(Transport), Transport).ToLower();
116 | return $"{protocol}://{IpAddress}:{IoPubPort}";
117 | }
118 | }
119 | #endregion
120 |
121 | #region Diagnostic Support
122 |
123 | public override string ToString() =>
124 | JsonConvert.SerializeObject(this, Formatting.Indented);
125 |
126 | #endregion
127 |
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/Data/Enumerations.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using Newtonsoft.Json;
5 | using Newtonsoft.Json.Converters;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Collections.Immutable;
9 | using System.Linq;
10 | using System.Net;
11 | using System.Runtime.Serialization;
12 | using System.Security.Cryptography;
13 | using System.Text;
14 |
15 | namespace Microsoft.Jupyter.Core
16 | {
17 |
18 | [JsonConverter(typeof(StringEnumConverter))]
19 | public enum ExecuteStatus
20 | {
21 | [EnumMember(Value="ok")]
22 | Ok,
23 |
24 | [EnumMember(Value="error")]
25 | Error,
26 |
27 | [EnumMember(Value="abort")]
28 | Abort
29 | }
30 |
31 | [JsonConverter(typeof(StringEnumConverter))]
32 | public enum CompleteStatus
33 | {
34 | [EnumMember(Value="ok")]
35 | Ok,
36 |
37 | [EnumMember(Value="error")]
38 | Error
39 | }
40 |
41 | [JsonConverter(typeof(StringEnumConverter))]
42 | public enum ExecutionState
43 | {
44 | [EnumMember(Value="busy")]
45 | Busy,
46 |
47 | [EnumMember(Value="idle")]
48 | Idle,
49 |
50 | [EnumMember(Value="starting")]
51 | Starting
52 | }
53 |
54 | [JsonConverter(typeof(StringEnumConverter))]
55 | public enum StreamName
56 | {
57 | [EnumMember(Value="stdin")]
58 | StandardIn,
59 |
60 | [EnumMember(Value="stdout")]
61 | StandardOut,
62 |
63 | [EnumMember(Value="stderr")]
64 | StandardError
65 | }
66 |
67 | public enum Transport
68 | {
69 | [EnumMember(Value="tcp")]
70 | Tcp
71 | }
72 |
73 | public enum SignatureScheme
74 | {
75 | [EnumMember(Value="hmac-sha256")]
76 | HmacSha256
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/src/Data/KernelContext.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using System;
5 | using System.IO;
6 | using System.Security.Cryptography;
7 | using System.Text;
8 | using Microsoft.Extensions.Logging;
9 | using Newtonsoft.Json;
10 |
11 | namespace Microsoft.Jupyter.Core
12 | {
13 | ///
14 | /// Describes all information needed to identify a kernel at runtime,
15 | /// including how to connect to clients, and a description of the
16 | /// kernel's supported language.
17 | ///
18 | public class KernelContext
19 | {
20 | public ConnectionInfo ConnectionInfo { get; private set; }
21 | public KernelProperties Properties { get; set; }
22 |
23 | ///
24 | /// Populates the context using a connection file, typically
25 | /// provided by Jupyter when instantiating a kernel.
26 | ///
27 | ///
28 | /// A path to the connection file to be loaded.
29 | ///
30 | ///
31 | /// A logger object used to report debugging information from the
32 | /// connection file.
33 | ///
34 | public void LoadConnectionFile(string connectionFile, ILogger logger = null)
35 | {
36 | logger?.LogDebug("Loading kernel context from connection file: {connectionFile}.", connectionFile);
37 | this.ConnectionInfo = JsonConvert.DeserializeObject(File.ReadAllText(connectionFile));
38 | logger?.LogDebug("Loaded connection information:\n{connectionInfo}", this.ConnectionInfo);
39 | }
40 |
41 | internal HMAC NewHmac() {
42 | // TODO: ensure that this HMAC algorithm agrees with that specified
43 | // in ConnectionInfo.
44 | return new HMACSHA256(Encoding.ASCII.GetBytes(ConnectionInfo.Key));
45 | }
46 | }
47 |
48 | }
--------------------------------------------------------------------------------
/src/Data/Metadata.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using Newtonsoft.Json;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Net;
8 | using System.Runtime.Serialization;
9 | using Microsoft.Jupyter.Core.Protocol;
10 | using System.IO;
11 | using System.Linq;
12 | using System.Reflection;
13 | using System.Runtime.Versioning;
14 |
15 | namespace Microsoft.Jupyter.Core
16 | {
17 |
18 | [JsonObject(MemberSerialization.OptIn)]
19 | public class HelpLinks
20 | {
21 | [JsonProperty("text")]
22 | public string Text { get; set; }
23 |
24 | [JsonProperty("url")]
25 | public Uri Url { get; set; }
26 | }
27 |
28 | [JsonObject(MemberSerialization.OptIn)]
29 | public class KernelSpec
30 | {
31 | [JsonProperty("argv")]
32 | public List Arguments;
33 |
34 | [JsonProperty("display_name")]
35 | public string DisplayName;
36 |
37 | [JsonProperty("language")]
38 | public string LanguageName;
39 |
40 | [JsonProperty("interrupt_mode")]
41 | public string InterruptMode = "message";
42 | }
43 |
44 | ///
45 | /// Specifies the metadata for a particular language kernel, including
46 | /// details about the kernel, the language supported by the kernel,
47 | /// and relevant version information.
48 | ///
49 | ///
50 | ///
51 | /// var properties = new KernelProperties
52 | /// {
53 | /// FriendlyName = "IEcho",
54 | /// KernelName = "iecho",
55 | /// KernelVersion = typeof(Program).Assembly.GetName().Version.ToString(),
56 | /// DisplayName = "Echo",
57 | ///
58 | /// LanguageName = "Echo",
59 | /// LanguageVersion = "0.1",
60 | /// LanguageMimeType = MimeTypes.PlainText,
61 | /// LanguageFileExtension = ".txt",
62 | ///
63 | /// Description = "A simple kernel that echos its input."
64 | /// };
65 | ///
66 | ///
67 | public class KernelProperties
68 | {
69 | private IList<(string, string)> additionalVersions = new List<(string, string)>();
70 |
71 | ///
72 | /// A user-friendly name for the kernel, typically used in menus.
73 | ///
74 | public string FriendlyName { get; set; }
75 |
76 | ///
77 | /// The name for the kernel, this name will be used to register the kernel with Jupyter
78 | /// and to identify the kernel programmatically.
79 | ///
80 | public string KernelName { get; set; }
81 |
82 | ///
83 | /// A string describing the version of the kernel.
84 | ///
85 | ///
86 | /// Note that this property does not refer to the version of the
87 | /// language supported by the kernel.
88 | ///
89 | public string KernelVersion { get; set; }
90 |
91 | public string DisplayName { get; set; }
92 |
93 |
94 | ///
95 | /// The name of the language supported by the kernel.
96 | ///
97 | public string LanguageName { get; set; }
98 | ///
99 | /// A string describing the version of the language supported by
100 | /// the kernel.
101 | ///
102 | public string LanguageVersion { get; set; }
103 |
104 | ///
105 | /// The MIME type used for source files in the supported language.
106 | ///
107 | ///
108 | /// This property is typically used by clients to offer exports of
109 | /// notebooks as plain script or source files.
110 | ///
111 | public string LanguageMimeType { get; set; }
112 |
113 | ///
114 | /// The file extension used for source files in the supported
115 | /// language.
116 | ///
117 | ///
118 | /// This property is typically used by clients to offer exports of
119 | /// notebooks as plain script or source files.
120 | ///
121 | public string LanguageFileExtension { get; set; }
122 |
123 | ///
124 | /// An extended description of the kernel.
125 | ///
126 | public string Description { get; set; }
127 |
128 | ///
129 | /// A collection of links to more information on this kernel and
130 | /// its supported language.
131 | ///
132 | public HelpLinks[] HelpLinks { get; set; }
133 |
134 | ///
135 | /// Returns a list of versions for the various components used by
136 | /// this kernel.
137 | ///
138 | ///
139 | /// Note that versions are represented as strings rather than
140 | /// System.Version instances in order to represent version
141 | /// numbers that may not conform to .NET versioning standards.
142 | ///
143 | public virtual IEnumerable<(string, string)> VersionTable {
144 | get {
145 | yield return (KernelName, KernelVersion);
146 | yield return ("Jupyter Core", typeof(KernelProperties).Assembly.GetName().Version.ToString());
147 | yield return (
148 | ".NET Runtime",
149 | // Use the technique documented at
150 | // https://weblog.west-wind.com/posts/2018/Apr/12/Getting-the-NET-Core-Runtime-Version-in-a-Running-Application
151 | // to get the target framework moniker of the entry point for the kernel,
152 | // so that we can report that as a version.
153 | Assembly
154 | .GetEntryAssembly()
155 | ?.GetCustomAttribute()
156 | ?.FrameworkName ?? ""
157 | );
158 | foreach (var version in additionalVersions) yield return version;
159 | }
160 | }
161 |
162 | public LanguageInfo AsLanguageInfo()
163 | {
164 | return new LanguageInfo
165 | {
166 | Name = LanguageName,
167 | LanguageVersion = LanguageVersion,
168 | MimeType = LanguageMimeType,
169 | FileExtension = LanguageFileExtension
170 | };
171 | }
172 |
173 | internal KernelInfoReplyContent AsKernelInfoReply()
174 | {
175 | return new KernelInfoReplyContent
176 | {
177 | Implementation = KernelName,
178 | ImplementationVersion = KernelVersion,
179 | LanguageInfo = AsLanguageInfo(),
180 | HelpLinks = new HelpLinks[0],
181 | ExecuteStatus = ExecuteStatus.Ok,
182 | Banner = ""
183 | };
184 | }
185 |
186 | ///
187 | /// Registers an additional component in the version table reported
188 | /// by this kernel.
189 | ///
190 | ///
191 | /// The name of the component as should be reported to the user.
192 | ///
193 | ///
194 | /// The version of the component as should be reported to the user.
195 | ///
196 | public KernelProperties WithAdditionalVersion(string component, string version)
197 | {
198 | this.additionalVersions.Add((component, version));
199 | return this;
200 | }
201 |
202 | ///
203 | /// Registers an additional component in the version table reported
204 | /// by this kernel, using the version information provided by the
205 | /// assembly for a given type.
206 | ///
207 | ///
208 | /// A type from the assembly whose version should be registered as
209 | /// the version of this component.
210 | ///
211 | ///
212 | /// The name of the component as should be reported to the user.
213 | ///
214 | public KernelProperties WithAdditionalVersion(string component)
215 | {
216 | this.additionalVersions.Add((component, typeof(T).Assembly.GetName().Version.ToString()));
217 | return this;
218 | }
219 | }
220 |
221 | [JsonObject(MemberSerialization.OptIn)]
222 | public class LanguageInfo
223 | {
224 | [JsonProperty("name")]
225 | public string Name { get; set; }
226 |
227 | [JsonProperty("version")]
228 | public string LanguageVersion {get; set;}
229 |
230 | [JsonProperty("mimetype")]
231 | public string MimeType {get; set;}
232 |
233 | [JsonProperty("file_extension")]
234 | public string FileExtension {get; set;}
235 | }
236 |
237 | }
238 |
--------------------------------------------------------------------------------
/src/Data/Serialization.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using Newtonsoft.Json;
5 | using System;
6 | using System.Net;
7 | using System.Runtime.Serialization;
8 |
9 | namespace Microsoft.Jupyter.Core
10 | {
11 |
12 | public class IpAddressConverter : JsonConverter
13 | {
14 | public override IPAddress ReadJson(JsonReader reader, Type objectType, IPAddress existingValue, bool hasExistingValue, JsonSerializer serializer)
15 | {
16 | if (reader.TokenType == JsonToken.Null)
17 | {
18 | return null;
19 | }
20 | else if (reader.TokenType == JsonToken.String)
21 | {
22 | return IPAddress.Parse((string) serializer.Deserialize(reader, typeof(string)));
23 | }
24 | else
25 | {
26 | throw new NotImplementedException();
27 | }
28 | }
29 |
30 | public override void WriteJson(JsonWriter writer, IPAddress value, JsonSerializer serializer) =>
31 | writer.WriteValue(value.ToString());
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/Engines/CompleteRequestHandler.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | #nullable enable
5 |
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Collections.Immutable;
9 | using System.Linq;
10 | using System.Reflection;
11 | using Microsoft.Extensions.Logging;
12 | using Microsoft.Extensions.Options;
13 | using Newtonsoft.Json;
14 | using Microsoft.Jupyter.Core.Protocol;
15 | using System.Diagnostics;
16 | using System.Threading.Tasks;
17 | using System.Threading;
18 | using Microsoft.Extensions.DependencyInjection;
19 |
20 | namespace Microsoft.Jupyter.Core
21 | {
22 | internal class CompleteRequestHandler : IShellHandler
23 | {
24 | private BaseEngine engine;
25 | private IShellServer shellServer;
26 | private ILogger logger;
27 |
28 | public CompleteRequestHandler(IExecutionEngine engine, IShellServer shellServer, ILogger logger)
29 | {
30 | if (engine is BaseEngine baseEngine)
31 | {
32 | this.engine = baseEngine;
33 | }
34 | else throw new Exception("The CompleteRequestHandler requires that the IExecutionEngine service inherits from BaseEngine.");
35 | this.shellServer = shellServer;
36 | this.logger = logger;
37 | }
38 |
39 | public string MessageType => "complete_request";
40 |
41 | public async Task HandleAsync(Message message)
42 | {
43 | await engine.Initialized;
44 |
45 | var request = message.Content as CompleteRequestContent;
46 | if (request == null)
47 | {
48 | logger.LogError("Expected completion result content, but got {Type} instead.", message.Content.GetType());
49 | return;
50 | }
51 | this.logger.LogDebug("Ask to complete code at cursor position {CursorPos}:\n{Code}", request.CursorPos, request.Code);
52 |
53 | shellServer.NotifyBusyStatus(message, ExecutionState.Busy);
54 | try
55 | {
56 | var completion = await engine.Complete(request.Code, request.CursorPos);
57 | // If we got a completion back from the engine that was anything
58 | // other than null, respond with it here. Note that unlike execute_request,
59 | // it's ok to just ignore a complete request that we don't know
60 | // how to handle.
61 | if (completion != null)
62 | {
63 | this.shellServer.SendShellMessage(
64 | new Message
65 | {
66 | Content = new CompleteReplyContent
67 | {
68 | CompleteStatus = completion.Value.Status,
69 | Matches = completion.Value.Matches.ToList(),
70 | CursorStart = completion.Value.CursorStart ?? request.CursorPos,
71 | CursorEnd = completion.Value.CursorEnd ?? request.CursorPos
72 | },
73 | Header = new MessageHeader
74 | {
75 | MessageType = "complete_reply"
76 | }
77 | }
78 | .AsReplyTo(message)
79 | );
80 | }
81 | return;
82 | }
83 | catch (TaskCanceledException tce)
84 | {
85 | this.logger?.LogDebug(tce, "Task cancelled.");
86 | return;
87 | }
88 | catch (Exception e)
89 | {
90 | this.logger?.LogError(e, "Unable to process CompleteRequest.");
91 | return;
92 | }
93 | finally
94 | {
95 | shellServer.NotifyBusyStatus(message, ExecutionState.Idle);
96 | }
97 | }
98 | }
99 | }
--------------------------------------------------------------------------------
/src/Engines/CompletionResult.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | using System;
5 | using System.Collections;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 | using Microsoft.Extensions.Logging;
10 | using Newtonsoft.Json;
11 | using Newtonsoft.Json.Linq;
12 |
13 | namespace Microsoft.Jupyter.Core
14 | {
15 |
16 | // From the Jupyter messaging protocol documentation
17 | // at https://jupyter-client.readthedocs.io/en/stable/messaging.html#completion:
18 | //
19 | // content = {
20 | // # status should be 'ok' unless an exception was raised during the request,
21 | // # in which case it should be 'error', along with the usual error message content
22 | // # in other messages.
23 | // 'status' : 'ok'
24 | //
25 | // # The list of all matches to the completion request, such as
26 | // # ['a.isalnum', 'a.isalpha'] for the above example.
27 | // 'matches' : list,
28 | //
29 | // # The range of text that should be replaced by the above matches when a completion is accepted.
30 | // # typically cursor_end is the same as cursor_pos in the request.
31 | // 'cursor_start' : int,
32 | // 'cursor_end' : int,
33 | //
34 | // # Information that frontend plugins might use for extra display information about completions.
35 | // 'metadata' : dict,
36 | // }
37 |
38 | ///
39 | /// Represents a list of completion results returned by an execution
40 | /// engine.
41 | ///
42 | public struct CompletionResult
43 | {
44 | public CompleteStatus Status;
45 | public IList? Matches;
46 | public int? CursorStart;
47 | public int? CursorEnd;
48 | public IDictionary Metadata;
49 |
50 | public static CompletionResult Failed => new CompletionResult
51 | {
52 | Status = CompleteStatus.Ok,
53 | Matches = null,
54 | CursorStart = null,
55 | CursorEnd = null,
56 | Metadata = new Dictionary()
57 | };
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/Engines/ExecuteRequestHandler.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | #nullable enable
5 |
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Collections.Immutable;
9 | using System.Linq;
10 | using System.Reflection;
11 | using Microsoft.Extensions.Logging;
12 | using Microsoft.Extensions.Options;
13 | using Newtonsoft.Json;
14 | using Microsoft.Jupyter.Core.Protocol;
15 | using System.Diagnostics;
16 | using System.Threading.Tasks;
17 | using System.Threading;
18 | using Microsoft.Extensions.DependencyInjection;
19 |
20 | namespace Microsoft.Jupyter.Core
21 | {
22 | internal class ExecuteRequestHandler : OrderedShellHandler
23 | {
24 |
25 | private BaseEngine engine;
26 | private IShellServer shellServer;
27 |
28 | private Task? previousTask = null;
29 | private ILogger? logger = null;
30 | private ICommsRouter router;
31 |
32 | protected override ILogger? Logger => logger;
33 |
34 | ///
35 | /// The number of cells that have been executed since the start of
36 | /// this engine. Used by clients to typeset cell numbers, e.g.:
37 | /// In[12]:.
38 | ///
39 | public int ExecutionCount { get; protected set; } = 0;
40 |
41 | public ExecuteRequestHandler(IExecutionEngine engine, IShellServer shellServer, ICommsRouter router, ILogger logger)
42 | {
43 | if (engine is BaseEngine baseEngine)
44 | {
45 | this.engine = baseEngine;
46 | }
47 | else throw new Exception("The ExecuteRequestHandler requires that the IExecutionEngine service inherits from BaseEngine.");
48 | this.shellServer = shellServer;
49 | this.logger = logger;
50 | this.router = router;
51 | }
52 |
53 | public override string MessageType => "execute_request";
54 |
55 | protected async virtual Task ExecutionTaskForMessage(Message message, int executionCount, Action onHandled)
56 | {
57 | var engineResponse = ExecutionResult.Aborted;
58 |
59 | var code = (message.Content as ExecuteRequestContent)?.Code ?? string.Empty;
60 |
61 | this.shellServer.SendIoPubMessage(
62 | new Message
63 | {
64 | ZmqIdentities = message.ZmqIdentities,
65 | ParentHeader = message.Header,
66 | Metadata = new Dictionary(),
67 | Content = new ExecuteInputContent
68 | {
69 | Code = code,
70 | ExecutionCount = executionCount
71 | },
72 | Header = new MessageHeader
73 | {
74 | MessageType = "execute_input"
75 | }
76 | }
77 | );
78 |
79 | using (var cancellationTokenSource = new CancellationTokenSource())
80 | {
81 | Action onInterruptRequest = (message) => cancellationTokenSource.Cancel();
82 |
83 | var shellServerSupportsInterrupt = this.shellServer as IShellServerSupportsInterrupt;
84 | if (shellServerSupportsInterrupt != null)
85 | {
86 | shellServerSupportsInterrupt.InterruptRequest += onInterruptRequest;
87 | }
88 |
89 | // Make sure that the engine is fully initialized.
90 | await engine.Initialized;
91 |
92 | try
93 | {
94 | engineResponse = await engine.Execute(
95 | code,
96 | new BaseEngine.ExecutionChannel(engine, message, router),
97 | cancellationTokenSource.Token
98 | );
99 | }
100 | finally
101 | {
102 | if (shellServerSupportsInterrupt != null)
103 | {
104 | shellServerSupportsInterrupt.InterruptRequest -= onInterruptRequest;
105 | }
106 | }
107 | }
108 |
109 | // Send the engine's output as an execution result.
110 | if (engineResponse.Output != null)
111 | {
112 | var serialized = engine.EncodeForDisplay(engineResponse.Output);
113 | this.shellServer.SendIoPubMessage(
114 | new Message
115 | {
116 | ZmqIdentities = message.ZmqIdentities,
117 | ParentHeader = message.Header,
118 | Metadata = new Dictionary(),
119 | Content = new ExecuteResultContent
120 | {
121 | ExecutionCount = executionCount,
122 | Data = serialized.Data,
123 | Metadata = serialized.Metadata
124 | },
125 | Header = new MessageHeader
126 | {
127 | MessageType = "execute_result"
128 | }
129 | }
130 | );
131 | }
132 |
133 | // Invoke the onHandled callback prior to sending the execute_reply message.
134 | // This guarantees that the OrderedShellHandler correctly processes the completion
135 | // of this task *before* any processing new task that the client may submit for
136 | // execution immediately upon receiving the execute_reply message.
137 | onHandled();
138 |
139 | // Handle the message.
140 | this.shellServer.SendShellMessage(
141 | new Message
142 | {
143 | ZmqIdentities = message.ZmqIdentities,
144 | ParentHeader = message.Header,
145 | Metadata = new Dictionary(),
146 | Content = new ExecuteReplyContent
147 | {
148 | ExecuteStatus = engineResponse.Status,
149 | ExecutionCount = executionCount,
150 | UserExpressions = new Dictionary()
151 | },
152 | Header = new MessageHeader
153 | {
154 | MessageType = "execute_reply"
155 | }
156 | }
157 | );
158 |
159 | return engineResponse;
160 | }
161 |
162 | protected async Task SendAbortMessage(Message message)
163 | {
164 | // The previous call failed, so abort here and let the
165 | // shell server know.
166 | this.shellServer.SendShellMessage(
167 | new Message
168 | {
169 | ZmqIdentities = message.ZmqIdentities,
170 | ParentHeader = message.Header,
171 | Metadata = new Dictionary(),
172 | Content = new ExecuteReplyContent
173 | {
174 | ExecuteStatus = ExecuteStatus.Abort,
175 | ExecutionCount = null,
176 | UserExpressions = new Dictionary()
177 | },
178 | Header = new MessageHeader
179 | {
180 | MessageType = "execute_reply"
181 | }
182 | }
183 | );
184 |
185 | // Finish by telling the client that we're free again.
186 | this.shellServer.SendIoPubMessage(
187 | new Message
188 | {
189 | Header = new MessageHeader
190 | {
191 | MessageType = "status"
192 | },
193 | Content = new KernelStatusContent
194 | {
195 | ExecutionState = ExecutionState.Idle
196 | }
197 | }.AsReplyTo(message)
198 | );
199 | }
200 |
201 | private int IncrementExecutionCount()
202 | {
203 | lock (this)
204 | {
205 | return ++this.ExecutionCount;
206 | }
207 | }
208 |
209 | public override async Task HandleAsync(Message message, ExecutionResult? previousResult) =>
210 | await HandleAsync(message, previousResult, () => {});
211 |
212 | public override async Task HandleAsync(Message message, ExecutionResult? previousResult, Action onHandled)
213 | {
214 | this.logger.LogDebug($"Asked to execute code:\n{((ExecuteRequestContent)message.Content).Code}");
215 |
216 | if (previousResult != null && previousResult.Value.Status != ExecuteStatus.Ok)
217 | {
218 | this.logger.LogDebug("Aborting due to previous execution result indicating failure: {PreviousResult}", previousResult.Value);
219 | onHandled();
220 | await SendAbortMessage(message);
221 | return ExecutionResult.Aborted;
222 | }
223 |
224 | var executionCount = IncrementExecutionCount();
225 | shellServer.NotifyBusyStatus(message, ExecutionState.Busy);
226 |
227 | try
228 | {
229 | var result = await ExecutionTaskForMessage(message, executionCount, onHandled);
230 | return result;
231 | }
232 | catch (TaskCanceledException tce)
233 | {
234 | this.logger?.LogDebug(tce, "Task cancelled.");
235 | return new ExecutionResult
236 | {
237 | Output = null,
238 | Status = ExecuteStatus.Abort
239 | };
240 | }
241 | catch (Exception e)
242 | {
243 | this.logger?.LogError(e, "Unable to process ExecuteRequest");
244 | return new ExecutionResult
245 | {
246 | Output = e,
247 | Status = ExecuteStatus.Error
248 | };
249 | }
250 | finally
251 | {
252 | shellServer.NotifyBusyStatus(message, ExecutionState.Idle);
253 | }
254 | }
255 |
256 | }
257 |
258 | }
259 |
--------------------------------------------------------------------------------
/src/Engines/ExecutionResult.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using System;
5 | using System.Collections;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 | using Microsoft.Extensions.Logging;
10 | using Newtonsoft.Json;
11 | using Newtonsoft.Json.Linq;
12 |
13 | namespace Microsoft.Jupyter.Core
14 | {
15 | public struct ExecutionResult
16 | {
17 | public ExecuteStatus Status;
18 | public object Output;
19 |
20 | public static ExecutionResult Failed => new ExecutionResult
21 | {
22 | Status = ExecuteStatus.Error,
23 | Output = null
24 | };
25 |
26 | public static ExecutionResult Aborted => new ExecutionResult
27 | {
28 | Status = ExecuteStatus.Abort,
29 | Output = null
30 | };
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/Engines/IChannel.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | #nullable enable
5 |
6 | using System;
7 | using Microsoft.Jupyter.Core.Protocol;
8 |
9 | namespace Microsoft.Jupyter.Core
10 | {
11 | ///
12 | /// Represents a display output that can be updated after it is first
13 | /// rendered (e.g.: for displaying progress of long-running tasks,
14 | /// or for providing interactivity with the user).
15 | ///
16 | public interface IUpdatableDisplay
17 | {
18 | ///
19 | /// Replaces any previous content rendered to this display with a
20 | /// new displayable object.
21 | ///
22 | ///
23 | /// The object to be displayed. Cannot be null.
24 | ///
25 | void Update(object displayable);
26 | }
27 |
28 | ///
29 | /// Provided as a backwards compatability shim for implementations of
30 | /// IChannel that do not support updatable display.
31 | ///
32 | internal class UpdatableDisplayFallback : IUpdatableDisplay
33 | {
34 | private readonly IChannel channel;
35 |
36 | public UpdatableDisplayFallback(IChannel channel)
37 | {
38 | this.channel = channel;
39 | }
40 |
41 | public void Update(object displayable)
42 | {
43 | channel.Display(displayable);
44 | }
45 | }
46 |
47 | ///
48 | /// Specifies a display channel between a Jupyter kernel and its clients
49 | /// that can be used for printing to streams, displaying rich data, and
50 | /// sending messages.
51 | ///
52 | public interface IChannel
53 | {
54 | ///
55 | /// Writes a message to this channel's standard output stream.
56 | ///
57 | /// The message to be written. Cannot be null.
58 | void Stdout(string message);
59 |
60 | ///
61 | /// Writes a message to this channel's standard error stream.
62 | ///
63 | /// The message to be written. Cannot be null.
64 | void Stderr(string message);
65 |
66 | ///
67 | /// Displays an object using this display channel.
68 | ///
69 | /// The object to be displayed. Cannot be null.
70 | void Display(object displayable);
71 |
72 | ///
73 | /// Displays an object using this display channel and allows for the
74 | /// object to be updated with future calls.
75 | ///
76 | /// The object to be displayed. Cannot be null.
77 | /// An object that can be used to update the display.
78 | public IUpdatableDisplay DisplayUpdatable(object displayable)
79 | {
80 | this.Display(displayable);
81 | return new UpdatableDisplayFallback(this);
82 | }
83 |
84 | ///
85 | /// Sends an iopub message to the client associated with this channel.
86 | ///
87 | /// The message to send. Cannot be null.
88 | void SendIoPubMessage(Message message) => throw new NotImplementedException();
89 |
90 | ///
91 | /// Gets the comms router associated with this channel, if any,
92 | /// or null if no comms router is available.
93 | ///
94 | ///
95 | /// Engines should ensure that this value is not null. In
96 | /// a future version, this property will no longer be nullable.
97 | ///
98 | public ICommsRouter? CommsRouter => null;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/Engines/IExecutionEngine.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading.Tasks;
5 |
6 | namespace Microsoft.Jupyter.Core
7 | {
8 | public interface IExecutionEngine
9 | {
10 | void Start();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Engines/InputParser.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | #nullable enable
5 |
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 |
10 | namespace Microsoft.Jupyter.Core
11 | {
12 | internal class InputParser
13 | {
14 | internal enum CommandType
15 | {
16 | Mundane,
17 | Magic,
18 | Help,
19 | MagicHelp
20 | }
21 |
22 | public ISymbolResolver Resolver;
23 |
24 | public InputParser(ISymbolResolver resolver)
25 | {
26 | this.Resolver = resolver;
27 | }
28 |
29 | public CommandType GetNextCommand(string input, out ISymbol? symbol, out string commandInput, out string remainingInput)
30 | {
31 | if (input == null) { throw new ArgumentNullException("input"); }
32 |
33 | symbol = null;
34 | commandInput = input;
35 | remainingInput = string.Empty;
36 |
37 | var inputLines = input.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None).ToList();
38 | if (inputLines == null || inputLines.Count == 0)
39 | {
40 | return CommandType.Mundane;
41 | }
42 |
43 | // Find the first non-whitespace line and see if it starts with a magic symbol.
44 | bool isHelp = false;
45 | int firstLineIndex = inputLines.FindIndex(s => !string.IsNullOrWhiteSpace(s));
46 | if (firstLineIndex < 0 || !StartsWithMagic(inputLines[firstLineIndex], out symbol, out isHelp))
47 | {
48 | // No magic symbol found.
49 | return isHelp ? CommandType.Help : CommandType.Mundane;
50 | }
51 |
52 | // Look through the remaining lines until we find one that
53 | // starts with a magic symbol.
54 | string? _commandInput = null;
55 | for (int lineIndex = firstLineIndex + 1; lineIndex < inputLines.Count; lineIndex++)
56 | {
57 | if (StartsWithMagic(inputLines[lineIndex], out _, out _))
58 | {
59 | _commandInput = string.Join(Environment.NewLine, inputLines.SkipLast(inputLines.Count - lineIndex));
60 | remainingInput = string.Join(Environment.NewLine, inputLines.Skip(lineIndex));
61 | break;
62 | }
63 | }
64 |
65 | // If we didn't find another magic symbol, use the full input
66 | // as the command input.
67 | commandInput = _commandInput ?? input;
68 |
69 | return isHelp ? CommandType.MagicHelp : CommandType.Magic;
70 | }
71 |
72 | private bool StartsWithMagic(string input, out ISymbol? symbol, out bool isHelp)
73 | {
74 | symbol = null;
75 | isHelp = false;
76 |
77 | var inputParts = input.Trim().Split(null, 2);
78 | var symbolName = inputParts[0].Trim();
79 | if (symbolName.StartsWith("?"))
80 | {
81 | symbolName = symbolName.Substring(1, symbolName.Length - 1);
82 | isHelp = true;
83 | }
84 | else if (symbolName.EndsWith("?"))
85 | {
86 | symbolName = symbolName.Substring(0, symbolName.Length - 1);
87 | isHelp = true;
88 | }
89 |
90 | if (!string.IsNullOrEmpty(symbolName))
91 | {
92 | symbol = this.Resolver.Resolve(symbolName);
93 | }
94 |
95 | return (symbol as MagicSymbol) != null;
96 | }
97 | }
98 | }
--------------------------------------------------------------------------------
/src/Extensions/ChannelWithNewLines.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | #nullable enable
5 |
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Text;
9 | using Microsoft.Jupyter.Core.Protocol;
10 |
11 | namespace Microsoft.Jupyter.Core
12 | {
13 | ///
14 | /// Provides extension methods used throughout the Jupyter Core library.
15 | ///
16 | public static partial class Extensions
17 | {
18 | ///
19 | /// Creates a wrapper of an IChannel that adds new lines to every message
20 | /// sent to stdout and stderr.
21 | ///
22 | ///
23 | /// If original is already a ChannelWithNewLines, this method
24 | /// simply returns original unmodified.
25 | ///
26 | public static ChannelWithNewLines WithNewLines(this IChannel original) =>
27 | (original is ChannelWithNewLines ch) ? ch : new ChannelWithNewLines(original);
28 | }
29 |
30 | ///
31 | /// This is a Jupyter Core IChannel that wraps an existing IChannel and
32 | /// adds NewLine symbols (Environment.NewLine)
33 | /// to every message that gets logged to Stdout and Stderror.
34 | ///
35 | public class ChannelWithNewLines : IChannel
36 | {
37 | ///
38 | /// The existing channel that this channel wraps with new lines.
39 | ///
40 | public IChannel BaseChannel { get; }
41 |
42 | ///
43 | /// Constructs a new channel, given a base channel to be wrapped
44 | /// with newlines.
45 | ///
46 | public ChannelWithNewLines(IChannel original) => BaseChannel = original;
47 |
48 | ///
49 | /// Formats a given message for display to stdout or stderr.
50 | ///
51 | /// The message to be formatted.
52 | ///
53 | /// , formatted with a trailing newline
54 | /// (Environment.NewLine).
55 | ///
56 | public static string Format(string msg) => $"{msg}{Environment.NewLine}";
57 |
58 | ///
59 | /// Writes a given message to the base channel's standard output,
60 | /// but with a trailing newline appended.
61 | ///
62 | /// The message to be written.
63 | public void Stdout(string message) => BaseChannel?.Stdout(Format(message));
64 |
65 | ///
66 | /// Writes a given message to the base channel's standard error,
67 | /// but with a trailing newline appended.
68 | ///
69 | /// The message to be written.
70 | public void Stderr(string message) => BaseChannel?.Stderr(Format(message));
71 |
72 | ///
73 | /// Displays a given object using the base channel.
74 | ///
75 | /// The object to be displayed.
76 | ///
77 | /// Note that no newline is appended by this method, as the
78 | /// displayable object need not be a string.
79 | ///
80 | public void Display(object displayable) => BaseChannel?.Display(displayable);
81 |
82 | ///
83 | /// Displays a given object using the base channel, allowing for
84 | /// future updates.
85 | ///
86 | /// The object to be displayed.
87 | ///
88 | /// Note that no newline is appended by this method, as the
89 | /// displayable object need not be a string.
90 | ///
91 | ///
92 | /// An object that can be used to update the display in the future.
93 | ///
94 | public IUpdatableDisplay DisplayUpdatable(object displayable) => BaseChannel?.DisplayUpdatable(displayable);
95 |
96 | ///
97 | public void SendIoPubMessage(Message message) => BaseChannel?.SendIoPubMessage(message);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/Extensions/Collections.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using System.Net;
5 | using System.Text;
6 | using System.Linq;
7 | using NetMQ;
8 | using Newtonsoft.Json;
9 | using System.Collections.Generic;
10 | using System.Security.Cryptography;
11 | using System;
12 |
13 | namespace Microsoft.Jupyter.Core
14 | {
15 | public static partial class Extensions
16 | {
17 |
18 | // NB: This is a polyfill for the equivalent .NET Core 2.0 method, not available in .NET Standard 2.0.
19 | public static TValue GetValueOrDefault(this IDictionary dict, TKey key, TValue @default)
20 | {
21 | var success = dict.TryGetValue(key, out var value);
22 | return success ? value : @default;
23 | }
24 |
25 | public static bool IsEqual(this T[] actual, T[] expected)
26 | where T: IEquatable
27 | {
28 | return Enumerable
29 | .Zip(actual, expected, (actualElement, expectedElement) => actualElement.Equals(expectedElement))
30 | .Aggregate((acc, nextBool) => (acc && nextBool));
31 | }
32 |
33 | public static IEnumerable AsEnumerable(this Nullable nullable)
34 | where T : struct
35 | {
36 | if (nullable.HasValue)
37 | {
38 | yield return nullable.Value;
39 | }
40 | }
41 | public static Dictionary Update(this Dictionary dict, Dictionary other)
42 | {
43 | foreach (var item in other)
44 | {
45 | dict[item.Key] = item.Value;
46 | }
47 |
48 | return dict;
49 | }
50 |
51 | public static void Deconstruct(this KeyValuePair entry, out TKey key, out TValue value)
52 | {
53 | key = entry.Key;
54 | value = entry.Value;
55 | }
56 |
57 | public static IEnumerable EnumerateInReverse(this IList source)
58 | {
59 | foreach (var idx in Enumerable.Range(1, source.Count))
60 | {
61 | yield return source[source.Count - idx];
62 | }
63 | }
64 |
65 | }
66 | }
--------------------------------------------------------------------------------
/src/Extensions/Conversions.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using System.Net;
5 | using System.Text;
6 | using System.Linq;
7 | using NetMQ;
8 | using Newtonsoft.Json;
9 | using System.Collections.Generic;
10 | using System.Security.Cryptography;
11 | using System;
12 |
13 | namespace Microsoft.Jupyter.Core
14 | {
15 | public static partial class Extensions
16 | {
17 |
18 | ///
19 | /// Converts a string containing hexadecimal digits to an array of
20 | /// bytes representing the same data.
21 | ///
22 | ///
23 | /// A string containing an even number of hexadecimal characters
24 | /// (0-f).
25 | ///
26 | /// An array of bytes representing the same data.
27 | public static byte[] HexToBytes(this string hex)
28 | {
29 | var bytes = new byte[hex.Length / 2];
30 | foreach (var idxHexPair in Enumerable.Range(0, hex.Length / 2))
31 | {
32 | bytes[idxHexPair] = Convert.ToByte(hex.Substring(2 * idxHexPair, 2), 16);
33 | }
34 | return bytes;
35 | }
36 |
37 | ///
38 | /// Encapsulates encoded data into an
39 | /// value without metadata.
40 | ///
41 | public static EncodedData ToEncodedData(this string data) =>
42 | new EncodedData
43 | {
44 | Data = data,
45 | Metadata = new Dictionary()
46 | };
47 |
48 | ///
49 | /// Encapsulates an execution status as a result object without
50 | /// output. This is useful when an input being executed has
51 | /// completed, but has not produced output; e.g. after a print
52 | /// statement or function.
53 | ///
54 | ///
55 | /// The status to be encapsulated as the result of an execution.
56 | ///
57 | public static ExecutionResult ToExecutionResult(this ExecuteStatus status) =>
58 | new ExecutionResult
59 | {
60 | Status = status,
61 | Output = null
62 | };
63 |
64 | ///
65 | /// Encapsulates a given output as the result of an execution.
66 | /// By default, this method denotes that an execution completed
67 | /// successfully.
68 | ///
69 | ///
70 | /// The output from an execution.
71 | ///
72 | ///
73 | /// The status to be encapsulated as the result of an execution.
74 | ///
75 | public static ExecutionResult ToExecutionResult(this object output, ExecuteStatus status = ExecuteStatus.Ok) =>
76 | new ExecutionResult
77 | {
78 | Status = status,
79 | Output = output
80 | };
81 |
82 | }
83 | }
--------------------------------------------------------------------------------
/src/Extensions/Cryptography.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using System.Net;
5 | using System.Text;
6 | using System.Linq;
7 | using NetMQ;
8 | using Newtonsoft.Json;
9 | using System.Collections.Generic;
10 | using System.Security.Cryptography;
11 | using System;
12 |
13 | namespace Microsoft.Jupyter.Core
14 | {
15 | public static partial class Extensions
16 | {
17 | public static byte[] ComputeHash(this HMAC hmac, params byte[][] blobs)
18 | {
19 | hmac.Initialize();
20 | // TODO: generalize to allow encodings other than UTF-8.
21 | foreach (var blob in blobs.Take(blobs.Length - 1))
22 | {
23 | hmac.TransformBlock(blob, 0, blob.Length, null, 0);
24 | }
25 | var lastBlob = blobs[blobs.Length - 1];
26 | hmac.TransformFinalBlock(lastBlob, 0, lastBlob.Length);
27 | return hmac.Hash;
28 | }
29 |
30 | public static byte[] ComputeHash(this HMAC hmac, params object[] blobs)
31 | {
32 | return hmac.ComputeHash(
33 | blobs
34 | .Select(blob => Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(blob)))
35 | .ToArray()
36 | );
37 | }
38 |
39 | }
40 | }
--------------------------------------------------------------------------------
/src/Extensions/Extensions.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using System.Net;
5 | using System.Text;
6 | using System.Linq;
7 | using NetMQ;
8 | using Newtonsoft.Json;
9 | using System.Collections.Generic;
10 | using System.Security.Cryptography;
11 | using System;
12 | using Newtonsoft.Json.Linq;
13 | using Microsoft.Jupyter.Core.Protocol;
14 |
15 | namespace Microsoft.Jupyter.Core
16 | {
17 | ///
18 | /// Provides extension methods used throughout the Jupyter Core library.
19 | ///
20 | public static partial class Extensions
21 | {
22 | ///
23 | /// Given some raw data, attempts to decode it into an object of
24 | /// a given type, returning false if the deserialization
25 | /// fails.
26 | ///
27 | public static bool TryAs(this JToken rawData, out T converted)
28 | where T: class
29 | {
30 | try
31 | {
32 | converted = rawData.ToObject();
33 | return true;
34 | }
35 | catch
36 | {
37 | converted = null;
38 | return false;
39 | }
40 | }
41 |
42 | internal static void NotifyBusyStatus(this IShellServer shellServer, Message message, ExecutionState state)
43 | {
44 | // Begin by sending that we're busy.
45 | shellServer.SendIoPubMessage(
46 | new Message
47 | {
48 | Header = new MessageHeader
49 | {
50 | MessageType = "status"
51 | },
52 | Content = new KernelStatusContent
53 | {
54 | ExecutionState = state
55 | },
56 | Metadata = new Dictionary()
57 | }.AsReplyTo(message)
58 | );
59 | }
60 |
61 | internal static CompletionResult AsCompletionResult(this IEnumerable completions, string code, int cursorPos)
62 | {
63 | // Since Jupyter's messaging protocol assumes a single cursor start and end for all completions, we need
64 | // to make a common cursor start from the minimum cursor start, and similarly for the cursor end.
65 | var minCursor = cursorPos;
66 | var maxCursor = cursorPos;
67 | if (completions.Any())
68 | {
69 | minCursor = completions.Min(completion => completion.CursorStart);
70 | maxCursor = completions.Max(completion => completion.CursorEnd);
71 | }
72 |
73 | return new CompletionResult
74 | {
75 | Status = CompleteStatus.Ok,
76 | CursorStart = minCursor,
77 | CursorEnd = maxCursor,
78 | Matches = completions
79 | .Select(completion =>
80 | {
81 | var prefix = code.Substring(minCursor, completion.CursorStart - minCursor);
82 | var suffix = code.Substring(maxCursor, completion.CursorEnd - maxCursor);
83 | return prefix + completion.Text + suffix;
84 | })
85 | .ToList()
86 | };
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/Extensions/Messages.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using System.Net;
5 | using System.Text;
6 | using System.Linq;
7 | using NetMQ;
8 | using Newtonsoft.Json;
9 | using System.Collections.Generic;
10 | using System.Security.Cryptography;
11 | using System;
12 | using Microsoft.Jupyter.Core.Protocol;
13 | using Newtonsoft.Json.Linq;
14 |
15 | namespace Microsoft.Jupyter.Core
16 | {
17 | public static partial class Extensions
18 | {
19 |
20 | ///
21 | /// Receives an entire Jupyter wire protocol message from a given
22 | /// ZeroMQ socket and deserializes it to a Message object for
23 | /// further processing.
24 | ///
25 | public static Message ReceiveMessage(
26 | this NetMQSocket socket,
27 | KernelContext context,
28 | Encoding encoding = null
29 | )
30 | {
31 | encoding = encoding ?? Encoding.UTF8;
32 |
33 | // Get all the relevant message frames.
34 | var rawFrames = socket
35 | .ReceiveMultipartBytes();
36 | var frames = rawFrames
37 | .Select(frame => encoding.GetString(frame))
38 | .ToList();
39 |
40 | // We know that one of the frames should be the special delimiter
41 | // . If we don't find it, time to throw an exception.
42 | var idxDelimiter = frames.IndexOf("");
43 | if (idxDelimiter < 0)
44 | {
45 | throw new ProtocolViolationException("Expected delimiter, but none was present.");
46 | }
47 |
48 | // At this point, we know that everything before idxDelimter is
49 | // a ZMQ identity, and that everything after follows the Jupyter
50 | // wire protocol. In particular, the next five blobs after
51 | // are as follows:
52 | // • An HMAC signature for the entire message.
53 | // • A serialized header for this message.
54 | // • A serialized header for the previous message in sequence.
55 | // • A serialized metadata dictionary.
56 | // • A serialized content dictionary.
57 | // Any remaining blobs are extra raw data buffers.
58 |
59 | // We start by computing the digest, since that is much, much easier
60 | // to do given the raw frames than trying to unambiguously
61 | // reserialize everything.
62 | // To compute the digest and verify the message, we start by pulling
63 | // out the claimed signature. This is by default a string of
64 | // hexadecimal characters, so we convert to a byte[] for comparing
65 | // with the HMAC output.
66 | var signature = frames[idxDelimiter + 1].HexToBytes();
67 | // Next, we take the four frames after the delimeter, since
68 | // those are the subject of the digest.
69 | var toDigest = rawFrames.Skip(idxDelimiter + 2).Take(4).ToArray();
70 | var digest = context.NewHmac().ComputeHash(toDigest);
71 |
72 | if (!signature.IsEqual(digest))
73 | {
74 | var digestStr = Convert.ToBase64String(digest);
75 | var signatureStr = Convert.ToBase64String(signature);
76 | throw new ProtocolViolationException(
77 | $"HMAC {digestStr} did not agree with {signatureStr}.");
78 | }
79 |
80 | // If we made it this far, we can unpack the content of the message
81 | // into the right subclass of MessageContent.
82 | var header = JsonConvert.DeserializeObject(frames[idxDelimiter + 2]);
83 | var content = MessageContent.Deserializers.GetValueOrDefault(
84 | header.MessageType,
85 | data =>
86 | new UnknownContent
87 | {
88 | RawData = JToken.Parse(data)
89 | }
90 | )(frames[idxDelimiter + 5]);
91 |
92 | var message = new Message
93 | {
94 | ZmqIdentities = rawFrames.Take(idxDelimiter).ToList(),
95 | Signature = signature,
96 | Header = header,
97 | ParentHeader = JsonConvert.DeserializeObject(frames[idxDelimiter + 3]),
98 | Metadata = JsonConvert.DeserializeObject>(frames[idxDelimiter + 4]),
99 | Content = content
100 | };
101 |
102 | return message;
103 | }
104 |
105 | public static void SendMessage(this IOutgoingSocket socket, KernelContext context, Message message)
106 | {
107 | // FIXME: need to handle parents for messages which are handled
108 | // sequentially.
109 |
110 | // Conceptually, sending a message consists of three steps:
111 | // • Convert the message to four frames.
112 | // • Digest the four frames.
113 | // • Send the identities, the delimeter, the digest, and the
114 | // message frames.
115 |
116 | var zmqMessage = new NetMQMessage();
117 | var frames = new object[]
118 | {
119 | message.Header,
120 | message.ParentHeader,
121 | message.Metadata,
122 | message.Content
123 | }
124 | .Select(frame => JsonConvert.SerializeObject(frame))
125 | .Select(str => Encoding.UTF8.GetBytes(str))
126 | .ToList();
127 | var digest = context.NewHmac().ComputeHash(frames.ToArray());
128 |
129 | message.ZmqIdentities?.ForEach(ident => zmqMessage.Append(ident));
130 | zmqMessage.Append("");
131 | zmqMessage.Append(BitConverter.ToString(digest).Replace("-", "").ToLowerInvariant());
132 | frames.ForEach(ident => zmqMessage.Append(ident));
133 |
134 | socket.SendMultipartMessage(zmqMessage);
135 | }
136 | }
137 |
138 | }
--------------------------------------------------------------------------------
/src/Extensions/ServiceCollection.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | using System.Net;
5 | using System.Text;
6 | using System.Linq;
7 | using NetMQ;
8 | using Newtonsoft.Json;
9 | using System.Collections.Generic;
10 | using System.Security.Cryptography;
11 | using System;
12 | using Microsoft.Extensions.DependencyInjection;
13 |
14 | namespace Microsoft.Jupyter.Core
15 | {
16 | public static partial class Extensions
17 | {
18 | ///
19 | /// Adds the core kernel servers (heartbeat and shell) to the service collection.
20 | ///
21 | public static IServiceCollection AddKernelServers(this IServiceCollection serviceCollection)
22 | {
23 | serviceCollection
24 | .AddSingleton()
25 | .AddSingleton()
26 | .AddSingleton()
27 | .AddSingleton();
28 |
29 | return serviceCollection;
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/Properties/267DevDivSNKey2048.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/jupyter-core/15a08a57215b0683b59bb04298c58a4f2bbb6aa0/src/Properties/267DevDivSNKey2048.snk
--------------------------------------------------------------------------------
/src/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("Microsoft.Jupyter.Core.Tests" + SigningConstants.PUBLIC_KEY)]
4 |
--------------------------------------------------------------------------------
/src/Properties/DelaySign.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | // Attributes for delay-signing
4 | #if SIGNED
5 | [assembly:AssemblyKeyFile(SigningConstants.KEY_FILE)]
6 | [assembly:AssemblyDelaySign(true)]
7 | #endif
8 |
9 | internal static partial class SigningConstants
10 | {
11 | #if SIGNED
12 | public const string KEY_FILE = @"Properties\267DevDivSNKey2048.snk";
13 | public const string PUBLIC_KEY = ", PublicKey=" +
14 | "002400000c800000140100000602000000240000525341310008000001000100613399aff18ef1" +
15 | "a2c2514a273a42d9042b72321f1757102df9ebada69923e2738406c21e5b801552ab8d200a65a2" +
16 | "35e001ac9adc25f2d811eb09496a4c6a59d4619589c69f5baf0c4179a47311d92555cd006acc8b" +
17 | "5959f2bd6e10e360c34537a1d266da8085856583c85d81da7f3ec01ed9564c58d93d713cd0172c" +
18 | "8e23a10f0239b80c96b07736f5d8b022542a4e74251a5f432824318b3539a5a087f8e53d2f135f" +
19 | "9ca47f3bb2e10aff0af0849504fb7cea3ff192dc8de0edad64c68efde34c56d302ad55fd6e80f3" +
20 | "02d5efcdeae953658d3452561b5f36c542efdbdd9f888538d374cef106acf7d93a4445c3c73cd9" +
21 | "11f0571aaf3d54da12b11ddec375b3";
22 | #else
23 | public const string PUBLIC_KEY = "";
24 | #endif
25 | }
26 |
--------------------------------------------------------------------------------
/src/ResultEncoding/BasicEncoders.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using Microsoft.Extensions.Logging;
6 | using Newtonsoft.Json;
7 |
8 | namespace Microsoft.Jupyter.Core
9 | {
10 |
11 | public class JsonResultEncoder : IResultEncoder
12 | {
13 | private readonly ILogger logger;
14 | private readonly JsonConverter[] converters;
15 | private readonly string mimeType;
16 |
17 | public string MimeType => mimeType;
18 |
19 | public JsonResultEncoder(ILogger logger = null, JsonConverter[] converters = null, string mimeType = null)
20 | {
21 | this.logger = logger;
22 | this.converters = converters ?? new JsonConverter[] {};
23 | this.mimeType = mimeType ?? MimeTypes.Json;
24 | }
25 |
26 | public EncodedData? Encode(object displayable)
27 | {
28 | if (displayable == null) return null;
29 |
30 | try
31 | {
32 | var serialized = JsonConvert.SerializeObject(displayable, converters);
33 | return serialized.ToEncodedData();
34 | }
35 | catch (Exception ex)
36 | {
37 | logger?.LogWarning(ex, "Failed to serialize display data of type {Type}.", displayable.GetType().ToString());
38 | return null;
39 | }
40 | }
41 | }
42 |
43 | public class PlainTextResultEncoder : IResultEncoder
44 | {
45 | public string MimeType => MimeTypes.PlainText;
46 | public EncodedData? Encode(object displayable) =>
47 | Extensions.ToEncodedData(displayable?.ToString());
48 | }
49 |
50 | public class ListToTextResultEncoder : IResultEncoder
51 | {
52 | public string MimeType => MimeTypes.PlainText;
53 | public EncodedData? Encode(object displayable)
54 | {
55 | if (displayable == null) return null;
56 |
57 | if (displayable is string)
58 | {
59 | return null;
60 | }
61 | else if (displayable is IEnumerable enumerable)
62 | {
63 | return String.Join(", ",
64 | enumerable.Cast