@@ -28,7 +28,7 @@ import { JupyterKernelSpec } from './jupyterKernelSpec';
2828import  {  LiveKernelModel  }  from  './types' ; 
2929
3030// tslint:disable-next-line: no-var-requires no-require-imports 
31- const  NamedRegexp  =  require ( 'named-js-regexp' ) ; 
31+ const  NamedRegexp  =  require ( 'named-js-regexp' )   as   typeof   import ( 'named-js-regexp' ) ; 
3232
3333/** 
3434 * Helper to ensure we can differentiate between two types in union types, keeping typing information. 
@@ -153,10 +153,10 @@ export class KernelService {
153153 // Check if kernel is `Python2` or `Python3` or a similar generic kernel. 
154154 const  regEx  =  NamedRegexp ( 'python\\s*(?<version>(\\d+))' ,  'g' ) ; 
155155 const  match  =  regEx . exec ( kernelSpec . name . toLowerCase ( ) ) ; 
156-  if  ( match )  { 
156+  if  ( match   &&   match . groups ( ) )  { 
157157 // 3. Look for interpreter with same major version 
158158
159-  const  majorVersion  =  parseInt ( match . groups ( ) . version ,  10 )  ||  0 ; 
159+  const  majorVersion  =  parseInt ( match . groups ( ) ! . version ,  10 )  ||  0 ; 
160160 // If the major versions match, that's sufficient. 
161161 if  ( ! majorVersion  ||  ( activeInterpreter ?. version  &&  activeInterpreter . version . major  ===  majorVersion ) )  { 
162162 traceInfo ( `Using current interpreter for kernel ${ kernelSpec . name } ${ kernelSpec . display_name }  ) ; 
@@ -219,6 +219,7 @@ export class KernelService {
219219 * @returns  {Promise<IJupyterKernelSpec> } 
220220 * @memberof  KernelService 
221221 */ 
222+  // tslint:disable-next-line: max-func-body-length 
222223 @captureTelemetry ( Telemetry . RegisterInterpreterAsKernel ,  undefined ,  true ) 
223224 @traceDecorators . error ( 'Failed to register an interpreter as a kernel' ) 
224225 public  async  registerKernel ( interpreter : PythonInterpreter ,  cancelToken ?: CancellationToken ) : Promise < IJupyterKernelSpec  |  undefined >  { 
@@ -269,6 +270,13 @@ export class KernelService {
269270 await  sleep ( 500 ) ; 
270271 kernel  =  await  this . findMatchingKernelSpec ( {  display_name : interpreter . displayName ,  name } ,  undefined ,  cancelToken ) ; 
271272 } 
273+  if  ( ! kernel )  { 
274+  // Possible user doesn't have kernelspec installed. 
275+  kernel  =  await  this . getKernelSpecFromStdOut ( output . stdout ) . catch ( ex  =>  { 
276+  traceError ( 'Failed to get kernelspec from stdout' ,  ex ) ; 
277+  return  undefined ; 
278+  } ) ; 
279+  } 
272280 if  ( ! kernel )  { 
273281 const  error  =  `Kernel not created with the name ${ name } ${ interpreter . displayName } ${ output . stdout }  ; 
274282 throw  new  Error ( error ) ; 
@@ -347,7 +355,47 @@ export class KernelService {
347355 return  `${ interpreter . displayName  ||  '' } ${ uuid ( ) }  . replace ( / [ ^ A - Z a - z 0 - 9 ] / g,  '' ) . toLowerCase ( ) ; 
348356 } 
349357
350-  private  enumerateSpecs  =  async  ( _cancelToken ?: CancellationToken ) : Promise < JupyterKernelSpec [ ] >  =>  { 
358+  /** 
359+  * Will scrape kernelspec info from the output when a new kernel is created. 
360+  * 
361+  * @private  
362+  * @param  {string } output 
363+  * @returns  {JupyterKernelSpec } 
364+  * @memberof  KernelService 
365+  */ 
366+  @traceDecorators . error ( 'Failed to parse kernel creation stdout' ) 
367+  private  async  getKernelSpecFromStdOut ( output : string ) : Promise < JupyterKernelSpec  |  undefined >  { 
368+  if  ( ! output )  { 
369+  return ; 
370+  } 
371+ 
372+  // Output should be of the form 
373+  // `Installed kernel <kernelname> in <path>` 
374+  const  regEx  =  NamedRegexp ( 'Installed\\skernelspec\\s(?<name>\\w*)\\sin\\s(?<path>.*)' ,  'g' ) ; 
375+  const  match  =  regEx . exec ( output ) ; 
376+  if  ( ! match  ||  ! match . groups ( ) )  { 
377+  return ; 
378+  } 
379+ 
380+  type  RegExGroup  =  {  name : string ;  path : string  } ; 
381+  const  groups  =  match . groups ( )  as  RegExGroup  |  undefined ; 
382+ 
383+  if  ( ! groups  ||  ! groups . name  ||  ! groups . path )  { 
384+  traceError ( 'Kernel Output not parsed' ,  output ) ; 
385+  throw  new  Error ( 'Unable to parse output to get the kernel info' ) ; 
386+  } 
387+ 
388+  const  specFile  =  path . join ( groups . path ,  'kernel.json' ) ; 
389+  if  ( ! ( await  this . fileSystem . fileExists ( specFile ) ) )  { 
390+  throw  new  Error ( 'KernelSpec file not found' ) ; 
391+  } 
392+ 
393+  const  kernelModel  =  JSON . parse ( await  this . fileSystem . readFile ( specFile ) ) ; 
394+  kernelModel . name  =  groups . name ; 
395+  return  new  JupyterKernelSpec ( kernelModel  as  Kernel . ISpecModel ,  specFile ) ; 
396+  } 
397+ 
398+  private  async  enumerateSpecs ( _cancelToken ?: CancellationToken ) : Promise < JupyterKernelSpec [ ] >  { 
351399 // Ignore errors if there are no kernels. 
352400 const  kernelSpecCommand  =  await  this . commandFinder . findBestCommand ( JupyterCommands . KernelSpecCommand ) . catch ( noop ) ; 
353401
@@ -387,5 +435,5 @@ export class KernelService {
387435 // This is failing for some folks. In that case return nothing 
388436 return  [ ] ; 
389437 } 
390-  } ; 
438+  } 
391439} 
0 commit comments