Show More
@@ -19,6 +19,7 b' import sys' | |||||
19 |
|
19 | |||
20 | from IPython.external import argparse |
|
20 | from IPython.external import argparse | |
21 | from IPython.utils.ipstruct import Struct |
|
21 | from IPython.utils.ipstruct import Struct | |
|
22 | from IPython.utils.genutils import filefind | |||
22 |
|
23 | |||
23 | #----------------------------------------------------------------------------- |
|
24 | #----------------------------------------------------------------------------- | |
24 | # Code |
|
25 | # Code | |
@@ -84,7 +85,7 b' class PyFileConfigLoader(FileConfigLoader):' | |||||
84 | that are all caps. These attribute are added to the config Struct. |
|
85 | that are all caps. These attribute are added to the config Struct. | |
85 | """ |
|
86 | """ | |
86 |
|
87 | |||
87 |
def __init__(self, filename, path= |
|
88 | def __init__(self, filename, path=None): | |
88 | """Build a config loader for a filename and path. |
|
89 | """Build a config loader for a filename and path. | |
89 |
|
90 | |||
90 | Parameters |
|
91 | Parameters | |
@@ -110,22 +111,7 b' class PyFileConfigLoader(FileConfigLoader):' | |||||
110 |
|
111 | |||
111 | def _find_file(self): |
|
112 | def _find_file(self): | |
112 | """Try to find the file by searching the paths.""" |
|
113 | """Try to find the file by searching the paths.""" | |
113 | if os.path.isfile(os.path.expanduser(self.filename)): |
|
114 | self.full_filename = filefind(self.filename, self.path) | |
114 | self.full_filename = os.path.expanduser(self.filename) |
|
|||
115 | return |
|
|||
116 | if self.path == '.': |
|
|||
117 | self.path = [os.getcwd()] |
|
|||
118 | if not isinstance(path, (list, tuple)): |
|
|||
119 | raise TypeError("path must be a list or tuple, got: %r" % self.path) |
|
|||
120 | for p in self.path: |
|
|||
121 | if p == '.': p = os.getcwd() |
|
|||
122 | full_filename = os.path.expanduser(os.path.join(p, self.filename)) |
|
|||
123 | if os.path.isfile(full_filename): |
|
|||
124 | self.full_filename = full_filename |
|
|||
125 | return |
|
|||
126 | raise IOError("Config file does not exist in any " |
|
|||
127 | "of the search paths: %r, %r" % \ |
|
|||
128 | (self.filename, self.path)) |
|
|||
129 |
|
115 | |||
130 | def _read_file_as_dict(self): |
|
116 | def _read_file_as_dict(self): | |
131 | self.data = {} |
|
117 | self.data = {} | |
@@ -175,6 +161,10 b' class ArgParseConfigLoader(CommandLineConfigLoader):' | |||||
175 | def _create_parser(self): |
|
161 | def _create_parser(self): | |
176 | self.parser = argparse.ArgumentParser(*self.args, **self.kw) |
|
162 | self.parser = argparse.ArgumentParser(*self.args, **self.kw) | |
177 | self._add_arguments() |
|
163 | self._add_arguments() | |
|
164 | self._add_other_arguments() | |||
|
165 | ||||
|
166 | def _add_other_arguments(): | |||
|
167 | pass | |||
178 |
|
168 | |||
179 | def _add_arguments(self): |
|
169 | def _add_arguments(self): | |
180 | for argument in self.arguments: |
|
170 | for argument in self.arguments: | |
@@ -196,3 +186,15 b' class ArgParseConfigLoader(CommandLineConfigLoader):' | |||||
196 | if v is not NoDefault: |
|
186 | if v is not NoDefault: | |
197 | setattr(self.config, k, v) |
|
187 | setattr(self.config, k, v) | |
198 |
|
188 | |||
|
189 | class IPythonArgParseConfigLoader(ArgParseConfigLoader): | |||
|
190 | ||||
|
191 | def _add_other_arguments(self): | |||
|
192 | self.parser.add_argument('--ipythondir',dest='IPYTHONDIR',type=str, | |||
|
193 | help='set to override default location of IPYTHONDIR', | |||
|
194 | default=NoDefault) | |||
|
195 | self.parser.add_argument('-p','--p',dest='PROFILE_NAME',type=str, | |||
|
196 | help='the string name of the ipython profile to be used', | |||
|
197 | default=None) | |||
|
198 | self.parser.add_argument('--debug',dest="DEBUG",action='store_true', | |||
|
199 | help='debug the application startup process', | |||
|
200 | default=NoDefault) |
@@ -10,41 +10,6 b' Authors:' | |||||
10 |
|
10 | |||
11 | Notes |
|
11 | Notes | |
12 | ----- |
|
12 | ----- | |
13 |
|
||||
14 | The following directories are relevant in the startup of an app: |
|
|||
15 |
|
||||
16 | * The ipythondir. This has a default, but can be set by IPYTHONDIR or at |
|
|||
17 | the command line. |
|
|||
18 | * The current working directory. |
|
|||
19 | * Another runtime directory. With some applications (engine, controller) we |
|
|||
20 | need the ability to have different cluster configs. Each of these needs |
|
|||
21 | to have its own config, security dir and log dir. We could simply treat |
|
|||
22 | these as regular ipython dirs. |
|
|||
23 |
|
||||
24 | There are number of ways in which these directories are used: |
|
|||
25 |
|
||||
26 | * For config files. |
|
|||
27 | * For other assets and resources needed to run. These include |
|
|||
28 | plugins, magics, furls files. |
|
|||
29 | * For writing various things created at runtime like logs, furl files, etc. |
|
|||
30 |
|
||||
31 | Questions: |
|
|||
32 |
|
||||
33 |
|
||||
34 | * Can we limit ourselves to 1 config file or do we want to have a sequence |
|
|||
35 | of them like IPYTHONDIR->RUNTIMEDIR->CWD? [1] |
|
|||
36 | * Do we need a debug mode that has custom exception handling and can drop |
|
|||
37 | into pdb upno startup? N |
|
|||
38 | * Do we need to use an OutputTrap to capture output and then present it |
|
|||
39 | to a user if startup fails? N |
|
|||
40 | * Do we want the location of the config file(s) to be independent of the |
|
|||
41 | ipython/runtime dir or coupled to it. In other words, can the user select |
|
|||
42 | a config file that is outside their runtime/ipython dir. One model is |
|
|||
43 | that we could have a very strict model of IPYTHONDIR=runtimed dir= |
|
|||
44 | dir used for all config. |
|
|||
45 | * Do we install default config files or not? N |
|
|||
46 |
|
||||
47 | * attempt needs to either clash or to die |
|
|||
48 | """ |
|
13 | """ | |
49 |
|
14 | |||
50 | #----------------------------------------------------------------------------- |
|
15 | #----------------------------------------------------------------------------- | |
@@ -58,9 +23,17 b' Questions:' | |||||
58 | # Imports |
|
23 | # Imports | |
59 | #----------------------------------------------------------------------------- |
|
24 | #----------------------------------------------------------------------------- | |
60 |
|
25 | |||
|
26 | import os | |||
61 | import sys |
|
27 | import sys | |
|
28 | import traceback | |||
|
29 | ||||
62 | from copy import deepcopy |
|
30 | from copy import deepcopy | |
63 | from IPython.utils.ipstruct import Struct |
|
31 | from IPython.utils.ipstruct import Struct | |
|
32 | from IPython.utils.genutils import get_ipython_dir, filefind | |||
|
33 | from IPython.config.loader import ( | |||
|
34 | IPythonArgParseConfigLoader, | |||
|
35 | PyFileConfigLoader | |||
|
36 | ) | |||
64 |
|
37 | |||
65 | #----------------------------------------------------------------------------- |
|
38 | #----------------------------------------------------------------------------- | |
66 | # Classes and functions |
|
39 | # Classes and functions | |
@@ -72,72 +45,157 b' class ApplicationError(Exception):' | |||||
72 |
|
45 | |||
73 |
|
46 | |||
74 | class Application(object): |
|
47 | class Application(object): | |
|
48 | """Load a config, construct an app and run it. | |||
|
49 | """ | |||
75 |
|
50 | |||
76 | runtime_dirs = [] |
|
51 | config_file_name = 'ipython_config.py' | |
77 | default_config = Struct() |
|
52 | name = 'ipython' | |
78 | runtime_dir = '' |
|
53 | debug = False | |
79 | config_file = '' |
|
|||
80 | name = '' |
|
|||
81 |
|
54 | |||
82 | def __init__(self): |
|
55 | def __init__(self): | |
83 | pass |
|
56 | pass | |
84 |
|
57 | |||
85 | def start(self): |
|
58 | def start(self): | |
86 | """Start the application.""" |
|
59 | """Start the application.""" | |
87 |
self.attempt(self.create_ |
|
60 | self.attempt(self.create_default_config) | |
88 |
self.attempt(self. |
|
61 | self.attempt(self.pre_load_command_line_config) | |
89 |
self.attempt(self. |
|
62 | self.attempt(self.load_command_line_config, action='exit') | |
90 |
self.attempt(self. |
|
63 | self.attempt(self.post_load_command_line_config) | |
91 |
self.attempt(self. |
|
64 | self.attempt(self.find_ipythondir) | |
|
65 | self.attempt(self.find_config_file_name) | |||
|
66 | self.attempt(self.find_config_file_paths) | |||
|
67 | self.attempt(self.pre_load_file_config) | |||
|
68 | self.attempt(self.load_file_config) | |||
|
69 | self.attempt(self.post_load_file_config) | |||
92 | self.attempt(self.merge_configs) |
|
70 | self.attempt(self.merge_configs) | |
|
71 | self.attempt(self.pre_construct) | |||
93 | self.attempt(self.construct) |
|
72 | self.attempt(self.construct) | |
94 |
self.attempt(self. |
|
73 | self.attempt(self.post_construct) | |
95 | self.attempt(self.start_app) |
|
74 | self.attempt(self.start_app) | |
96 |
|
75 | |||
97 | #------------------------------------------------------------------------- |
|
76 | #------------------------------------------------------------------------- | |
98 | # Various stages of Application creation |
|
77 | # Various stages of Application creation | |
99 | #------------------------------------------------------------------------- |
|
78 | #------------------------------------------------------------------------- | |
100 |
|
79 | |||
|
80 | def create_default_config(self): | |||
|
81 | """Create defaults that can't be set elsewhere.""" | |||
|
82 | self.default_config = Struct() | |||
|
83 | self.default_config.IPYTHONDIR = get_ipython_dir() | |||
|
84 | ||||
101 | def create_command_line_config(self): |
|
85 | def create_command_line_config(self): | |
102 |
""" |
|
86 | """Create and return a command line config loader.""" | |
103 | self.command_line_config = Struct() |
|
87 | return IPythonArgParseConfigLoader(description=self.name) | |
|
88 | ||||
|
89 | def pre_load_command_line_config(self): | |||
|
90 | """Do actions just before loading the command line config.""" | |||
|
91 | pass | |||
104 |
|
92 | |||
105 |
def |
|
93 | def load_command_line_config(self): | |
106 | """Find the runtime directory for this application. |
|
94 | """Load the command line config. | |
107 |
|
95 | |||
108 |
This |
|
96 | This method also sets ``self.debug``. | |
109 | """ |
|
97 | """ | |
110 | pass |
|
|||
111 |
|
98 | |||
112 | def create_runtime_dirs(self): |
|
99 | loader = self.create_command_line_config() | |
113 | """Create the runtime dirs if they don't exist.""" |
|
100 | self.command_line_config = loader.load_config() | |
|
101 | try: | |||
|
102 | self.debug = self.command_line_config.DEBUG | |||
|
103 | except AttributeError: | |||
|
104 | pass # use class default | |||
|
105 | self.log("Default config loaded:", self.default_config) | |||
|
106 | self.log("Command line config loaded:", self.command_line_config) | |||
|
107 | ||||
|
108 | def post_load_command_line_config(self): | |||
|
109 | """Do actions just after loading the command line config.""" | |||
114 | pass |
|
110 | pass | |
115 |
|
111 | |||
116 |
def find_ |
|
112 | def find_ipythondir(self): | |
117 | """Find the config file for this application.""" |
|
113 | """Set the IPython directory. | |
|
114 | ||||
|
115 | This sets ``self.ipythondir``, but the actual value that is passed | |||
|
116 | to the application is kept in either ``self.default_config`` or | |||
|
117 | ``self.command_line_config``. This also added ``self.ipythondir`` to | |||
|
118 | ``sys.path`` so config files there can be references by other config | |||
|
119 | files. | |||
|
120 | """ | |||
|
121 | ||||
|
122 | try: | |||
|
123 | self.ipythondir = self.command_line_config.IPYTHONDIR | |||
|
124 | except AttributeError: | |||
|
125 | self.ipythondir = self.default_config.IPYTHONDIR | |||
|
126 | sys.path.append(os.path.abspath(self.ipythondir)) | |||
|
127 | self.log("IPYTHONDIR set to: %s" % self.ipythondir) | |||
|
128 | ||||
|
129 | def find_config_file_name(self): | |||
|
130 | """Find the config file name for this application. | |||
|
131 | ||||
|
132 | If a profile has been set at the command line, this will resolve | |||
|
133 | it. The search paths for the config file are set in | |||
|
134 | :meth:`find_config_file_paths` and then passed to the config file | |||
|
135 | loader where they are resolved to an absolute path. | |||
|
136 | """ | |||
|
137 | ||||
|
138 | if self.command_line_config.PROFILE_NAME is not None: | |||
|
139 | self.profile_name = self.command_line_config.PROFILE_NAME | |||
|
140 | name_parts = self.config_file_name.split('.') | |||
|
141 | name_parts.insert(1, '_' + self.profile_name + '.') | |||
|
142 | self.config_file_name = ''.join(name_parts) | |||
|
143 | ||||
|
144 | def find_config_file_paths(self): | |||
|
145 | """Set the search paths for resolving the config file.""" | |||
|
146 | self.config_file_paths = (os.getcwd(), self.ipythondir) | |||
|
147 | ||||
|
148 | def pre_load_file_config(self): | |||
|
149 | """Do actions before the config file is loaded.""" | |||
118 | pass |
|
150 | pass | |
119 |
|
151 | |||
120 |
def |
|
152 | def load_file_config(self): | |
121 | self.file_configs = [Struct()] |
|
153 | """Load the config file. | |
|
154 | ||||
|
155 | This tries to load the config file from disk. If successful, the | |||
|
156 | ``CONFIG_FILE`` config variable is set to the resolved config file | |||
|
157 | location. If not successful, an empty config is used. | |||
|
158 | """ | |||
|
159 | loader = PyFileConfigLoader(self.config_file_name, | |||
|
160 | self.config_file_paths) | |||
|
161 | try: | |||
|
162 | self.file_config = loader.load_config() | |||
|
163 | self.file_config.CONFIG_FILE = loader.full_filename | |||
|
164 | except IOError: | |||
|
165 | self.log("Config file not found, skipping: %s" % \ | |||
|
166 | self.config_file_name) | |||
|
167 | self.file_config = Struct() | |||
|
168 | else: | |||
|
169 | self.log("Config file loaded: %s" % loader.full_filename) | |||
|
170 | ||||
|
171 | def post_load_file_config(self): | |||
|
172 | """Do actions after the config file is loaded.""" | |||
|
173 | pass | |||
122 |
|
174 | |||
123 | def merge_configs(self): |
|
175 | def merge_configs(self): | |
|
176 | """Merge the default, command line and file config objects.""" | |||
124 | config = Struct() |
|
177 | config = Struct() | |
125 | all_configs = self.file_configs + self.command_line_config |
|
178 | config.update(self.default_config) | |
126 | for c in all_configs: |
|
179 | config.update(self.command_line_config) | |
127 |
|
|
180 | config.update(self.file_config) | |
128 | self.master_config = config |
|
181 | self.master_config = config | |
|
182 | self.log("Master config created:", self.master_config) | |||
129 |
|
183 | |||
130 |
def construct(self |
|
184 | def pre_construct(self): | |
131 | """Construct the main components that make up this app.""" |
|
185 | """Do actions after the config has been built, but before construct.""" | |
132 | pass |
|
186 | pass | |
133 |
|
187 | |||
134 |
def st |
|
188 | def construct(self): | |
135 | """Start logging, if needed, at the last possible moment.""" |
|
189 | """Construct the main components that make up this app.""" | |
|
190 | self.log("Constructing components for application...") | |||
|
191 | ||||
|
192 | def post_construct(self): | |||
|
193 | """Do actions after construct, but before starting the app.""" | |||
136 | pass |
|
194 | pass | |
137 |
|
195 | |||
138 | def start_app(self): |
|
196 | def start_app(self): | |
139 | """Actually start the app.""" |
|
197 | """Actually start the app.""" | |
140 | pass |
|
198 | self.log("Starting application...") | |
141 |
|
199 | |||
142 | #------------------------------------------------------------------------- |
|
200 | #------------------------------------------------------------------------- | |
143 | # Utility methods |
|
201 | # Utility methods | |
@@ -148,14 +206,26 b' class Application(object):' | |||||
148 | print "Aborting application: ", self.name |
|
206 | print "Aborting application: ", self.name | |
149 | sys.exit(1) |
|
207 | sys.exit(1) | |
150 |
|
208 | |||
151 |
def |
|
209 | def exit(self): | |
|
210 | print "Exiting application: ", self.name | |||
|
211 | sys.exit(1) | |||
|
212 | ||||
|
213 | def attempt(self, func, action='abort'): | |||
152 | try: |
|
214 | try: | |
153 | func() |
|
215 | func() | |
154 | except: |
|
216 | except: | |
155 | self.handle_error() |
|
217 | if action == 'abort': | |
156 |
self. |
|
218 | self.print_traceback() | |
157 |
|
219 | self.abort() | ||
158 | def handle_error(self): |
|
220 | elif action == 'exit': | |
159 | print "I am dying!" |
|
221 | self.exit() | |
160 |
|
222 | |||
161 | No newline at end of file |
|
223 | def print_traceback(self): | |
|
224 | print "Error in appliction startup: ", self.name | |||
|
225 | ||||
|
226 | traceback.print_exc() | |||
|
227 | ||||
|
228 | def log(self, *args): | |||
|
229 | if self.debug: | |||
|
230 | for arg in args: | |||
|
231 | print "[%s] %s" % (self.name, arg) No newline at end of file |
@@ -196,7 +196,7 b' class Component(HasTraitlets):' | |||||
196 |
|
196 | |||
197 | def _config_changed(self, name, old, new): |
|
197 | def _config_changed(self, name, old, new): | |
198 | # Get all traitlets with a config_key metadata entry |
|
198 | # Get all traitlets with a config_key metadata entry | |
199 |
traitlets = self.traitlets(config_key |
|
199 | traitlets = self.traitlets('config_key') | |
200 | for k, v in traitlets.items(): |
|
200 | for k, v in traitlets.items(): | |
201 | try: |
|
201 | try: | |
202 | config_value = new[v.get_metadata('config_key')] |
|
202 | config_value = new[v.get_metadata('config_key')] |
@@ -93,7 +93,7 b' def make_IPython(argv=None,user_ns=None,user_global_ns=None,debug=1,' | |||||
93 | # Defaults and initialization |
|
93 | # Defaults and initialization | |
94 |
|
94 | |||
95 | # For developer debugging, deactivates crash handler and uses pdb. |
|
95 | # For developer debugging, deactivates crash handler and uses pdb. | |
96 |
DEVDEBUG = |
|
96 | DEVDEBUG = True | |
97 |
|
97 | |||
98 | if argv is None: |
|
98 | if argv is None: | |
99 | argv = sys.argv |
|
99 | argv = sys.argv | |
@@ -446,6 +446,8 b" object? -> Details about 'object'. ?object also works, ?? prints more." | |||||
446 | warn('Configuration file %s not found. Ignoring request.' |
|
446 | warn('Configuration file %s not found. Ignoring request.' | |
447 | % (opts_all.rcfile) ) |
|
447 | % (opts_all.rcfile) ) | |
448 |
|
448 | |||
|
449 | print opts_all.rcfile, opts_all.ipythondir | |||
|
450 | ||||
449 | # 'profiles' are a shorthand notation for config filenames |
|
451 | # 'profiles' are a shorthand notation for config filenames | |
450 | profile_handled_by_legacy = False |
|
452 | profile_handled_by_legacy = False | |
451 | if opts_all.profile: |
|
453 | if opts_all.profile: |
@@ -528,32 +528,55 b' def get_py_filename(name):' | |||||
528 | raise IOError,'File `%s` not found.' % name |
|
528 | raise IOError,'File `%s` not found.' % name | |
529 |
|
529 | |||
530 | #----------------------------------------------------------------------------- |
|
530 | #----------------------------------------------------------------------------- | |
531 | def filefind(fname,alt_dirs = None): |
|
|||
532 | """Return the given filename either in the current directory, if it |
|
|||
533 | exists, or in a specified list of directories. |
|
|||
534 |
|
531 | |||
535 | ~ expansion is done on all file and directory names. |
|
|||
536 |
|
532 | |||
537 | Upon an unsuccessful search, raise an IOError exception.""" |
|
533 | def filefind(filename, path_dirs=None): | |
|
534 | """Find a file by looking through a sequence of paths. | |||
538 |
|
|
535 | ||
539 | if alt_dirs is None: |
|
536 | This iterates through a sequence of paths looking for a file and returns | |
540 | try: |
|
537 | the full, absolute path of the first occurence of the file. If no set of | |
541 | alt_dirs = get_home_dir() |
|
538 | path dirs is given, the filename is tested as is, after running through | |
542 | except HomeDirError: |
|
539 | :func:`expandvars` and :func:`expanduser`. Thus a simple call:: | |
543 | alt_dirs = os.getcwd() |
|
540 | ||
544 | search = [fname] + list_strings(alt_dirs) |
|
541 | filefind('myfile.txt') | |
545 | search = map(os.path.expanduser,search) |
|
542 | ||
546 | #print 'search list for',fname,'list:',search # dbg |
|
543 | will find the file in the current working dir, but:: | |
547 | fname = search[0] |
|
544 | ||
548 | if os.path.isfile(fname): |
|
545 | filefind('~/myfile.txt') | |
549 | return fname |
|
546 | ||
550 | for direc in search[1:]: |
|
547 | Will find the file in the users home directory. This function does not | |
551 | testname = os.path.join(direc,fname) |
|
548 | automatically try any paths, such as the cwd or the user's home directory. | |
552 | #print 'testname',testname # dbg |
|
549 | ||
|
550 | Parameters | |||
|
551 | ---------- | |||
|
552 | filename : str | |||
|
553 | The filename to look for. | |||
|
554 | path_dirs : str, None or sequence of str | |||
|
555 | The sequence of paths to look for the file in. If None, the filename | |||
|
556 | need to be absolute or be in the cwd. If a string, the string is | |||
|
557 | put into a sequence and the searched. If a sequence, walk through | |||
|
558 | each element and join with ``filename``, calling :func:`expandvars` | |||
|
559 | and :func:`expanduser` before testing for existence. | |||
|
560 | ||||
|
561 | Returns | |||
|
562 | ------- | |||
|
563 | Raises :exc:`IOError` or returns absolute path to file. | |||
|
564 | """ | |||
|
565 | if path_dirs is None: | |||
|
566 | path_dirs = ("",) | |||
|
567 | elif isinstance(path_dirs, basestring): | |||
|
568 | path_dirs = (path_dirs,) | |||
|
569 | for path in path_dirs: | |||
|
570 | if path == '.': path = os.getcwd() | |||
|
571 | testname = os.path.expandvars( | |||
|
572 | os.path.expanduser( | |||
|
573 | os.path.join(path, filename))) | |||
553 | if os.path.isfile(testname): |
|
574 | if os.path.isfile(testname): | |
554 | return testname |
|
575 | return os.path.abspath(testname) | |
555 |
raise IOError |
|
576 | raise IOError("File does not exist in any " | |
556 | ' not found in current or supplied directories:' + `alt_dirs` |
|
577 | "of the search paths: %r, %r" % \ | |
|
578 | (filename, path_dirs)) | |||
|
579 | ||||
557 |
|
580 | |||
558 | #---------------------------------------------------------------------------- |
|
581 | #---------------------------------------------------------------------------- | |
559 | def file_read(filename): |
|
582 | def file_read(filename): |
@@ -354,10 +354,13 b' class TestHasTraitlets(TestCase):' | |||||
354 | i = Int(config_key='VALUE1', other_thing='VALUE2') |
|
354 | i = Int(config_key='VALUE1', other_thing='VALUE2') | |
355 | f = Float(config_key='VALUE3', other_thing='VALUE2') |
|
355 | f = Float(config_key='VALUE3', other_thing='VALUE2') | |
356 | a = A() |
|
356 | a = A() | |
357 | # traitlets = a.traitlets(config_key=lambda v: True) |
|
357 | self.assertEquals(a.traitlets(), dict(i=A.i, f=A.f)) | |
358 | # self.assertEquals(traitlets, dict(i=A.i, f=A.f)) |
|
358 | traitlets = a.traitlets(config_key=lambda v: True) | |
|
359 | self.assertEquals(traitlets, dict(i=A.i, f=A.f)) | |||
359 | traitlets = a.traitlets(config_key='VALUE1', other_thing='VALUE2') |
|
360 | traitlets = a.traitlets(config_key='VALUE1', other_thing='VALUE2') | |
360 | self.assertEquals(traitlets, dict(i=A.i)) |
|
361 | self.assertEquals(traitlets, dict(i=A.i)) | |
|
362 | traitlets = a.traitlets('config_key') | |||
|
363 | self.assertEquals(traitlets, dict(i=A.i, f=A.f)) | |||
361 |
|
364 | |||
362 | #----------------------------------------------------------------------------- |
|
365 | #----------------------------------------------------------------------------- | |
363 | # Tests for specific traitlet types |
|
366 | # Tests for specific traitlet types |
@@ -438,7 +438,7 b' class HasTraitlets(object):' | |||||
438 | """Get a list of all the names of this classes traitlets.""" |
|
438 | """Get a list of all the names of this classes traitlets.""" | |
439 | return self.traitlets(**metadata).keys() |
|
439 | return self.traitlets(**metadata).keys() | |
440 |
|
440 | |||
441 | def traitlets(self, **metadata): |
|
441 | def traitlets(self, *args, **metadata): | |
442 | """Get a list of all the traitlets of this class. |
|
442 | """Get a list of all the traitlets of this class. | |
443 |
|
443 | |||
444 | The TraitletTypes returned don't know anything about the values |
|
444 | The TraitletTypes returned don't know anything about the values | |
@@ -446,9 +446,12 b' class HasTraitlets(object):' | |||||
446 | """ |
|
446 | """ | |
447 | traitlets = dict([memb for memb in inspect.getmembers(self.__class__) if \ |
|
447 | traitlets = dict([memb for memb in inspect.getmembers(self.__class__) if \ | |
448 | isinstance(memb[1], TraitletType)]) |
|
448 | isinstance(memb[1], TraitletType)]) | |
449 | if len(metadata) == 0: |
|
449 | if len(metadata) == 0 and len(args) == 0: | |
450 | return traitlets |
|
450 | return traitlets | |
451 |
|
451 | |||
|
452 | for meta_name in args: | |||
|
453 | metadata[meta_name] = lambda _: True | |||
|
454 | ||||
452 | for meta_name, meta_eval in metadata.items(): |
|
455 | for meta_name, meta_eval in metadata.items(): | |
453 | if type(meta_eval) is not FunctionType: |
|
456 | if type(meta_eval) is not FunctionType: | |
454 | metadata[meta_name] = _SimpleTest(meta_eval) |
|
457 | metadata[meta_name] = _SimpleTest(meta_eval) |
General Comments 0
You need to be logged in to leave comments.
Login now