##// END OF EJS Templates
Deterministic order for kernels in notebook UI
Thomas Kluyver -
Show More
@@ -1,50 +1,52 b''
1 """Tornado handlers for kernel specifications."""
1 """Tornado handlers for kernel specifications."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5 import json
5 import json
6 from tornado import web
6 from tornado import web
7
7
8 from ...base.handlers import IPythonHandler, json_errors
8 from ...base.handlers import IPythonHandler, json_errors
9
9
10 from IPython.kernel.kernelspec import _pythonfirst
11
10
12
11 class MainKernelSpecHandler(IPythonHandler):
13 class MainKernelSpecHandler(IPythonHandler):
12 SUPPORTED_METHODS = ('GET',)
14 SUPPORTED_METHODS = ('GET',)
13
15
14 @web.authenticated
16 @web.authenticated
15 @json_errors
17 @json_errors
16 def get(self):
18 def get(self):
17 ksm = self.kernel_spec_manager
19 ksm = self.kernel_spec_manager
18 results = []
20 results = []
19 for kernel_name in ksm.find_kernel_specs():
21 for kernel_name in sorted(ksm.find_kernel_specs(), key=_pythonfirst):
20 d = ksm.get_kernel_spec(kernel_name).to_dict()
22 d = ksm.get_kernel_spec(kernel_name).to_dict()
21 d['name'] = kernel_name
23 d['name'] = kernel_name
22 results.append(d)
24 results.append(d)
23
25
24 self.set_header("Content-Type", 'application/json')
26 self.set_header("Content-Type", 'application/json')
25 self.finish(json.dumps(results))
27 self.finish(json.dumps(results))
26
28
27
29
28 class KernelSpecHandler(IPythonHandler):
30 class KernelSpecHandler(IPythonHandler):
29 SUPPORTED_METHODS = ('GET',)
31 SUPPORTED_METHODS = ('GET',)
30
32
31 @web.authenticated
33 @web.authenticated
32 @json_errors
34 @json_errors
33 def get(self, kernel_name):
35 def get(self, kernel_name):
34 ksm = self.kernel_spec_manager
36 ksm = self.kernel_spec_manager
35 try:
37 try:
36 kernelspec = ksm.get_kernel_spec(kernel_name)
38 kernelspec = ksm.get_kernel_spec(kernel_name)
37 except KeyError:
39 except KeyError:
38 raise web.HTTPError(404, u'Kernel spec %s not found' % kernel_name)
40 raise web.HTTPError(404, u'Kernel spec %s not found' % kernel_name)
39 self.set_header("Content-Type", 'application/json')
41 self.set_header("Content-Type", 'application/json')
40 self.finish(kernelspec.to_json())
42 self.finish(kernelspec.to_json())
41
43
42
44
43 # URL to handler mappings
45 # URL to handler mappings
44
46
45 kernel_name_regex = r"(?P<kernel_name>\w+)"
47 kernel_name_regex = r"(?P<kernel_name>\w+)"
46
48
47 default_handlers = [
49 default_handlers = [
48 (r"/api/kernelspecs", MainKernelSpecHandler),
50 (r"/api/kernelspecs", MainKernelSpecHandler),
49 (r"/api/kernelspecs/%s" % kernel_name_regex, KernelSpecHandler),
51 (r"/api/kernelspecs/%s" % kernel_name_regex, KernelSpecHandler),
50 ]
52 ]
@@ -1,193 +1,202 b''
1 import io
1 import io
2 import json
2 import json
3 import os
3 import os
4 import shutil
4 import shutil
5 import sys
5 import sys
6
6
7 pjoin = os.path.join
7 pjoin = os.path.join
8
8
9 from IPython.utils.path import get_ipython_dir
9 from IPython.utils.path import get_ipython_dir
10 from IPython.utils.py3compat import PY3
10 from IPython.utils.py3compat import PY3
11 from IPython.utils.traitlets import HasTraits, List, Unicode, Dict
11 from IPython.utils.traitlets import HasTraits, List, Unicode, Dict
12
12
13 if os.name == 'nt':
13 if os.name == 'nt':
14 programdata = os.environ.get('PROGRAMDATA', None)
14 programdata = os.environ.get('PROGRAMDATA', None)
15 if programdata:
15 if programdata:
16 SYSTEM_KERNEL_DIRS = [pjoin(programdata, 'ipython', 'kernels')]
16 SYSTEM_KERNEL_DIRS = [pjoin(programdata, 'ipython', 'kernels')]
17 else: # PROGRAMDATA is not defined by default on XP.
17 else: # PROGRAMDATA is not defined by default on XP.
18 SYSTEM_KERNEL_DIRS = []
18 SYSTEM_KERNEL_DIRS = []
19 else:
19 else:
20 SYSTEM_KERNEL_DIRS = ["/usr/share/ipython/kernels",
20 SYSTEM_KERNEL_DIRS = ["/usr/share/ipython/kernels",
21 "/usr/local/share/ipython/kernels",
21 "/usr/local/share/ipython/kernels",
22 ]
22 ]
23
23
24 NATIVE_KERNEL_NAME = 'python3' if PY3 else 'python2'
24 NATIVE_KERNEL_NAME = 'python3' if PY3 else 'python2'
25
25
26 def _pythonfirst(s):
27 "Sort key function that will put strings starting with 'python' first."
28 if s == NATIVE_KERNEL_NAME:
29 return ' ' + s # Two spaces to sort this first of all
30 elif s.startswith('python'):
31 # Space is not valid in kernel names, so this should sort first
32 return ' ' + s
33 return s
34
26 class KernelSpec(HasTraits):
35 class KernelSpec(HasTraits):
27 argv = List()
36 argv = List()
28 display_name = Unicode()
37 display_name = Unicode()
29 language = Unicode()
38 language = Unicode()
30 codemirror_mode = None
39 codemirror_mode = None
31 env = Dict()
40 env = Dict()
32
41
33 resource_dir = Unicode()
42 resource_dir = Unicode()
34
43
35 def __init__(self, resource_dir, argv, display_name, language,
44 def __init__(self, resource_dir, argv, display_name, language,
36 codemirror_mode=None):
45 codemirror_mode=None):
37 super(KernelSpec, self).__init__(resource_dir=resource_dir, argv=argv,
46 super(KernelSpec, self).__init__(resource_dir=resource_dir, argv=argv,
38 display_name=display_name, language=language,
47 display_name=display_name, language=language,
39 codemirror_mode=codemirror_mode)
48 codemirror_mode=codemirror_mode)
40 if not self.codemirror_mode:
49 if not self.codemirror_mode:
41 self.codemirror_mode = self.language
50 self.codemirror_mode = self.language
42
51
43 @classmethod
52 @classmethod
44 def from_resource_dir(cls, resource_dir):
53 def from_resource_dir(cls, resource_dir):
45 """Create a KernelSpec object by reading kernel.json
54 """Create a KernelSpec object by reading kernel.json
46
55
47 Pass the path to the *directory* containing kernel.json.
56 Pass the path to the *directory* containing kernel.json.
48 """
57 """
49 kernel_file = pjoin(resource_dir, 'kernel.json')
58 kernel_file = pjoin(resource_dir, 'kernel.json')
50 with io.open(kernel_file, 'r', encoding='utf-8') as f:
59 with io.open(kernel_file, 'r', encoding='utf-8') as f:
51 kernel_dict = json.load(f)
60 kernel_dict = json.load(f)
52 return cls(resource_dir=resource_dir, **kernel_dict)
61 return cls(resource_dir=resource_dir, **kernel_dict)
53
62
54 def to_dict(self):
63 def to_dict(self):
55 return dict(argv=self.argv,
64 return dict(argv=self.argv,
56 env=self.env,
65 env=self.env,
57 display_name=self.display_name,
66 display_name=self.display_name,
58 language=self.language,
67 language=self.language,
59 codemirror_mode=self.codemirror_mode,
68 codemirror_mode=self.codemirror_mode,
60 )
69 )
61
70
62 def to_json(self):
71 def to_json(self):
63 return json.dumps(self.to_dict())
72 return json.dumps(self.to_dict())
64
73
65 def _is_kernel_dir(path):
74 def _is_kernel_dir(path):
66 """Is ``path`` a kernel directory?"""
75 """Is ``path`` a kernel directory?"""
67 return os.path.isdir(path) and os.path.isfile(pjoin(path, 'kernel.json'))
76 return os.path.isdir(path) and os.path.isfile(pjoin(path, 'kernel.json'))
68
77
69 def _list_kernels_in(dir):
78 def _list_kernels_in(dir):
70 """Return a mapping of kernel names to resource directories from dir.
79 """Return a mapping of kernel names to resource directories from dir.
71
80
72 If dir is None or does not exist, returns an empty dict.
81 If dir is None or does not exist, returns an empty dict.
73 """
82 """
74 if dir is None or not os.path.isdir(dir):
83 if dir is None or not os.path.isdir(dir):
75 return {}
84 return {}
76 return {f.lower(): pjoin(dir, f) for f in os.listdir(dir)
85 return {f.lower(): pjoin(dir, f) for f in os.listdir(dir)
77 if _is_kernel_dir(pjoin(dir, f))}
86 if _is_kernel_dir(pjoin(dir, f))}
78
87
79 class NoSuchKernel(KeyError):
88 class NoSuchKernel(KeyError):
80 def __init__(self, name):
89 def __init__(self, name):
81 self.name = name
90 self.name = name
82
91
83 class KernelSpecManager(HasTraits):
92 class KernelSpecManager(HasTraits):
84 ipython_dir = Unicode()
93 ipython_dir = Unicode()
85 def _ipython_dir_default(self):
94 def _ipython_dir_default(self):
86 return get_ipython_dir()
95 return get_ipython_dir()
87
96
88 user_kernel_dir = Unicode()
97 user_kernel_dir = Unicode()
89 def _user_kernel_dir_default(self):
98 def _user_kernel_dir_default(self):
90 return pjoin(self.ipython_dir, 'kernels')
99 return pjoin(self.ipython_dir, 'kernels')
91
100
92 kernel_dirs = List(
101 kernel_dirs = List(
93 help="List of kernel directories to search. Later ones take priority over earlier."
102 help="List of kernel directories to search. Later ones take priority over earlier."
94 )
103 )
95 def _kernel_dirs_default(self):
104 def _kernel_dirs_default(self):
96 return SYSTEM_KERNEL_DIRS + [
105 return SYSTEM_KERNEL_DIRS + [
97 self.user_kernel_dir,
106 self.user_kernel_dir,
98 ]
107 ]
99
108
100 def _make_native_kernel_dir(self):
109 def _make_native_kernel_dir(self):
101 """Makes a kernel directory for the native kernel.
110 """Makes a kernel directory for the native kernel.
102
111
103 The native kernel is the kernel using the same Python runtime as this
112 The native kernel is the kernel using the same Python runtime as this
104 process. This will put its informatino in the user kernels directory.
113 process. This will put its informatino in the user kernels directory.
105 """
114 """
106 path = pjoin(self.user_kernel_dir, NATIVE_KERNEL_NAME)
115 path = pjoin(self.user_kernel_dir, NATIVE_KERNEL_NAME)
107 os.makedirs(path, mode=0o755)
116 os.makedirs(path, mode=0o755)
108 with open(pjoin(path, 'kernel.json'), 'w') as f:
117 with open(pjoin(path, 'kernel.json'), 'w') as f:
109 json.dump({'argv':[NATIVE_KERNEL_NAME, '-c',
118 json.dump({'argv':[NATIVE_KERNEL_NAME, '-c',
110 'from IPython.kernel.zmq.kernelapp import main; main()',
119 'from IPython.kernel.zmq.kernelapp import main; main()',
111 '-f', '{connection_file}'],
120 '-f', '{connection_file}'],
112 'display_name': 'IPython (Python %d)' % (3 if PY3 else 2),
121 'display_name': 'IPython (Python %d)' % (3 if PY3 else 2),
113 'language': 'python',
122 'language': 'python',
114 'codemirror_mode': {'name': 'ipython',
123 'codemirror_mode': {'name': 'ipython',
115 'version': sys.version_info[0]},
124 'version': sys.version_info[0]},
116 },
125 },
117 f, indent=1)
126 f, indent=1)
118 # TODO: Copy icons into directory
127 # TODO: Copy icons into directory
119 return path
128 return path
120
129
121 def find_kernel_specs(self):
130 def find_kernel_specs(self):
122 """Returns a dict mapping kernel names to resource directories."""
131 """Returns a dict mapping kernel names to resource directories."""
123 d = {}
132 d = {}
124 for kernel_dir in self.kernel_dirs:
133 for kernel_dir in self.kernel_dirs:
125 d.update(_list_kernels_in(kernel_dir))
134 d.update(_list_kernels_in(kernel_dir))
126
135
127 if NATIVE_KERNEL_NAME not in d:
136 if NATIVE_KERNEL_NAME not in d:
128 d[NATIVE_KERNEL_NAME] = self._make_native_kernel_dir()
137 d[NATIVE_KERNEL_NAME] = self._make_native_kernel_dir()
129 return d
138 return d
130 # TODO: Caching?
139 # TODO: Caching?
131
140
132 def get_kernel_spec(self, kernel_name):
141 def get_kernel_spec(self, kernel_name):
133 """Returns a :class:`KernelSpec` instance for the given kernel_name.
142 """Returns a :class:`KernelSpec` instance for the given kernel_name.
134
143
135 Raises :exc:`NoSuchKernel` if the given kernel name is not found.
144 Raises :exc:`NoSuchKernel` if the given kernel name is not found.
136 """
145 """
137 if kernel_name == 'python':
146 if kernel_name == 'python':
138 kernel_name = NATIVE_KERNEL_NAME
147 kernel_name = NATIVE_KERNEL_NAME
139 d = self.find_kernel_specs()
148 d = self.find_kernel_specs()
140 try:
149 try:
141 resource_dir = d[kernel_name.lower()]
150 resource_dir = d[kernel_name.lower()]
142 except KeyError:
151 except KeyError:
143 raise NoSuchKernel(kernel_name)
152 raise NoSuchKernel(kernel_name)
144 return KernelSpec.from_resource_dir(resource_dir)
153 return KernelSpec.from_resource_dir(resource_dir)
145
154
146 def install_kernel_spec(self, source_dir, kernel_name=None, system=False,
155 def install_kernel_spec(self, source_dir, kernel_name=None, system=False,
147 replace=False):
156 replace=False):
148 """Install a kernel spec by copying its directory.
157 """Install a kernel spec by copying its directory.
149
158
150 If ``kernel_name`` is not given, the basename of ``source_dir`` will
159 If ``kernel_name`` is not given, the basename of ``source_dir`` will
151 be used.
160 be used.
152
161
153 If ``system`` is True, it will attempt to install into the systemwide
162 If ``system`` is True, it will attempt to install into the systemwide
154 kernel registry. If the process does not have appropriate permissions,
163 kernel registry. If the process does not have appropriate permissions,
155 an :exc:`OSError` will be raised.
164 an :exc:`OSError` will be raised.
156
165
157 If ``replace`` is True, this will replace an existing kernel of the same
166 If ``replace`` is True, this will replace an existing kernel of the same
158 name. Otherwise, if the destination already exists, an :exc:`OSError`
167 name. Otherwise, if the destination already exists, an :exc:`OSError`
159 will be raised.
168 will be raised.
160 """
169 """
161 if not kernel_name:
170 if not kernel_name:
162 kernel_name = os.path.basename(source_dir)
171 kernel_name = os.path.basename(source_dir)
163 kernel_name = kernel_name.lower()
172 kernel_name = kernel_name.lower()
164
173
165 if system:
174 if system:
166 if SYSTEM_KERNEL_DIRS:
175 if SYSTEM_KERNEL_DIRS:
167 destination = os.path.join(SYSTEM_KERNEL_DIRS[-1], kernel_name)
176 destination = os.path.join(SYSTEM_KERNEL_DIRS[-1], kernel_name)
168 else:
177 else:
169 raise EnvironmentError("No system kernel directory is available")
178 raise EnvironmentError("No system kernel directory is available")
170 else:
179 else:
171 destination = os.path.join(self.user_kernel_dir, kernel_name)
180 destination = os.path.join(self.user_kernel_dir, kernel_name)
172
181
173 if replace and os.path.isdir(destination):
182 if replace and os.path.isdir(destination):
174 shutil.rmtree(destination)
183 shutil.rmtree(destination)
175
184
176 shutil.copytree(source_dir, destination)
185 shutil.copytree(source_dir, destination)
177
186
178 def find_kernel_specs():
187 def find_kernel_specs():
179 """Returns a dict mapping kernel names to resource directories."""
188 """Returns a dict mapping kernel names to resource directories."""
180 return KernelSpecManager().find_kernel_specs()
189 return KernelSpecManager().find_kernel_specs()
181
190
182 def get_kernel_spec(kernel_name):
191 def get_kernel_spec(kernel_name):
183 """Returns a :class:`KernelSpec` instance for the given kernel_name.
192 """Returns a :class:`KernelSpec` instance for the given kernel_name.
184
193
185 Raises KeyError if the given kernel name is not found.
194 Raises KeyError if the given kernel name is not found.
186 """
195 """
187 return KernelSpecManager().get_kernel_spec(kernel_name)
196 return KernelSpecManager().get_kernel_spec(kernel_name)
188
197
189 def install_kernel_spec(source_dir, kernel_name=None, system=False, replace=False):
198 def install_kernel_spec(source_dir, kernel_name=None, system=False, replace=False):
190 return KernelSpecManager().install_kernel_spec(source_dir, kernel_name,
199 return KernelSpecManager().install_kernel_spec(source_dir, kernel_name,
191 system, replace)
200 system, replace)
192
201
193 install_kernel_spec.__doc__ = KernelSpecManager.install_kernel_spec.__doc__
202 install_kernel_spec.__doc__ = KernelSpecManager.install_kernel_spec.__doc__
@@ -1,121 +1,114 b''
1
1
2 # Copyright (c) IPython Development Team.
2 # Copyright (c) IPython Development Team.
3 # Distributed under the terms of the Modified BSD License.
3 # Distributed under the terms of the Modified BSD License.
4
4
5 import errno
5 import errno
6 import os.path
6 import os.path
7
7
8 from IPython.config.application import Application
8 from IPython.config.application import Application
9 from IPython.core.application import (
9 from IPython.core.application import (
10 BaseIPythonApplication, base_flags, base_aliases
10 BaseIPythonApplication, base_flags, base_aliases
11 )
11 )
12 from IPython.utils.traitlets import Instance, Dict, Unicode, Bool
12 from IPython.utils.traitlets import Instance, Dict, Unicode, Bool
13
13
14 from .kernelspec import KernelSpecManager
14 from .kernelspec import KernelSpecManager, _pythonfirst
15
16 def _pythonfirst(s):
17 "Sort key function that will put strings starting with 'python' first."
18 if s.startswith('python'):
19 # Space is not valid in kernel names, so this should sort first
20 return ' ' + s
21 return s
22
15
23 class ListKernelSpecs(BaseIPythonApplication):
16 class ListKernelSpecs(BaseIPythonApplication):
24 description = """List installed kernel specifications."""
17 description = """List installed kernel specifications."""
25 kernel_spec_manager = Instance(KernelSpecManager)
18 kernel_spec_manager = Instance(KernelSpecManager)
26
19
27 # Not all of the base aliases are meaningful (e.g. profile)
20 # Not all of the base aliases are meaningful (e.g. profile)
28 aliases = {k: base_aliases[k] for k in ['ipython-dir', 'log-level']}
21 aliases = {k: base_aliases[k] for k in ['ipython-dir', 'log-level']}
29 flags = {'debug': base_flags['debug'],}
22 flags = {'debug': base_flags['debug'],}
30
23
31 def _kernel_spec_manager_default(self):
24 def _kernel_spec_manager_default(self):
32 return KernelSpecManager(ipython_dir=self.ipython_dir)
25 return KernelSpecManager(ipython_dir=self.ipython_dir)
33
26
34 def start(self):
27 def start(self):
35 print("Available kernels:")
28 print("Available kernels:")
36 for kernelname in sorted(self.kernel_spec_manager.find_kernel_specs(),
29 for kernelname in sorted(self.kernel_spec_manager.find_kernel_specs(),
37 key=_pythonfirst):
30 key=_pythonfirst):
38 print(" %s" % kernelname)
31 print(" %s" % kernelname)
39
32
40
33
41 class InstallKernelSpec(BaseIPythonApplication):
34 class InstallKernelSpec(BaseIPythonApplication):
42 description = """Install a kernel specification directory."""
35 description = """Install a kernel specification directory."""
43 kernel_spec_manager = Instance(KernelSpecManager)
36 kernel_spec_manager = Instance(KernelSpecManager)
44
37
45 def _kernel_spec_manager_default(self):
38 def _kernel_spec_manager_default(self):
46 return KernelSpecManager(ipython_dir=self.ipython_dir)
39 return KernelSpecManager(ipython_dir=self.ipython_dir)
47
40
48 sourcedir = Unicode()
41 sourcedir = Unicode()
49 kernel_name = Unicode("", config=True,
42 kernel_name = Unicode("", config=True,
50 help="Install the kernel spec with this name"
43 help="Install the kernel spec with this name"
51 )
44 )
52 def _kernel_name_default(self):
45 def _kernel_name_default(self):
53 return os.path.basename(self.sourcedir)
46 return os.path.basename(self.sourcedir)
54
47
55 system = Bool(False, config=True,
48 system = Bool(False, config=True,
56 help="""
49 help="""
57 Try to install the kernel spec to the systemwide directory instead of
50 Try to install the kernel spec to the systemwide directory instead of
58 the per-user directory.
51 the per-user directory.
59 """
52 """
60 )
53 )
61 replace = Bool(False, config=True,
54 replace = Bool(False, config=True,
62 help="Replace any existing kernel spec with this name."
55 help="Replace any existing kernel spec with this name."
63 )
56 )
64
57
65 aliases = {'name': 'InstallKernelSpec.kernel_name'}
58 aliases = {'name': 'InstallKernelSpec.kernel_name'}
66 for k in ['ipython-dir', 'log-level']:
59 for k in ['ipython-dir', 'log-level']:
67 aliases[k] = base_aliases[k]
60 aliases[k] = base_aliases[k]
68
61
69 flags = {'system': ({'InstallKernelSpec': {'system': True}},
62 flags = {'system': ({'InstallKernelSpec': {'system': True}},
70 "Install to the systemwide kernel registry"),
63 "Install to the systemwide kernel registry"),
71 'replace': ({'InstallKernelSpec': {'replace': True}},
64 'replace': ({'InstallKernelSpec': {'replace': True}},
72 "Replace any existing kernel spec with this name."),
65 "Replace any existing kernel spec with this name."),
73 'debug': base_flags['debug'],
66 'debug': base_flags['debug'],
74 }
67 }
75
68
76 def parse_command_line(self, argv):
69 def parse_command_line(self, argv):
77 super(InstallKernelSpec, self).parse_command_line(argv)
70 super(InstallKernelSpec, self).parse_command_line(argv)
78 # accept positional arg as profile name
71 # accept positional arg as profile name
79 if self.extra_args:
72 if self.extra_args:
80 self.sourcedir = self.extra_args[0]
73 self.sourcedir = self.extra_args[0]
81 else:
74 else:
82 print("No source directory specified.")
75 print("No source directory specified.")
83 self.exit(1)
76 self.exit(1)
84
77
85 def start(self):
78 def start(self):
86 try:
79 try:
87 self.kernel_spec_manager.install_kernel_spec(self.sourcedir,
80 self.kernel_spec_manager.install_kernel_spec(self.sourcedir,
88 kernel_name=self.kernel_name,
81 kernel_name=self.kernel_name,
89 system=self.system,
82 system=self.system,
90 replace=self.replace,
83 replace=self.replace,
91 )
84 )
92 except OSError as e:
85 except OSError as e:
93 if e.errno == errno.EACCES:
86 if e.errno == errno.EACCES:
94 print("Permission denied")
87 print("Permission denied")
95 self.exit(1)
88 self.exit(1)
96 elif e.errno == errno.EEXIST:
89 elif e.errno == errno.EEXIST:
97 print("A kernel spec is already present at %s" % e.filename)
90 print("A kernel spec is already present at %s" % e.filename)
98 self.exit(1)
91 self.exit(1)
99 raise
92 raise
100
93
101 class KernelSpecApp(Application):
94 class KernelSpecApp(Application):
102 name = "ipython kernelspec"
95 name = "ipython kernelspec"
103 description = """Manage IPython kernel specifications."""
96 description = """Manage IPython kernel specifications."""
104
97
105 subcommands = Dict(dict(
98 subcommands = Dict(dict(
106 list = (ListKernelSpecs, ListKernelSpecs.description.splitlines()[0]),
99 list = (ListKernelSpecs, ListKernelSpecs.description.splitlines()[0]),
107 install = (InstallKernelSpec, InstallKernelSpec.description.splitlines()[0])
100 install = (InstallKernelSpec, InstallKernelSpec.description.splitlines()[0])
108 ))
101 ))
109
102
110 aliases = {}
103 aliases = {}
111 flags = {}
104 flags = {}
112
105
113 def start(self):
106 def start(self):
114 if self.subapp is None:
107 if self.subapp is None:
115 print("No subcommand specified. Must specify one of: %s"% list(self.subcommands))
108 print("No subcommand specified. Must specify one of: %s"% list(self.subcommands))
116 print()
109 print()
117 self.print_description()
110 self.print_description()
118 self.print_subcommands()
111 self.print_subcommands()
119 self.exit(1)
112 self.exit(1)
120 else:
113 else:
121 return self.subapp.start()
114 return self.subapp.start()
General Comments 0
You need to be logged in to leave comments. Login now