##// END OF EJS Templates
Semi-final Application and minor work on traitlets.
Brian Granger -
Show More
@@ -1,198 +1,200 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """A factory for creating configuration objects.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2009 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import os
18 18 import sys
19 19
20 20 from IPython.external import argparse
21 21 from IPython.utils.ipstruct import Struct
22 from IPython.utils.genutils import filefind
22 23
23 24 #-----------------------------------------------------------------------------
24 25 # Code
25 26 #-----------------------------------------------------------------------------
26 27
27 28
28 29 class ConfigLoaderError(Exception):
29 30 pass
30 31
31 32
32 33 class ConfigLoader(object):
33 34 """A object for loading configurations from just about anywhere.
34 35
35 36 The resulting configuration is packaged as a :class:`Struct`.
36 37
37 38 Notes
38 39 -----
39 40 A :class:`ConfigLoader` does one thing: load a config from a source
40 41 (file, command line arguments) and returns the data as a :class:`Struct`.
41 42 There are lots of things that :class:`ConfigLoader` does not do. It does
42 43 not implement complex logic for finding config files. It does not handle
43 44 default values or merge multiple configs. These things need to be
44 45 handled elsewhere.
45 46 """
46 47
47 48 def __init__(self):
48 49 """A base class for config loaders.
49 50
50 51 Examples
51 52 --------
52 53
53 54 >>> cl = ConfigLoader()
54 55 >>> config = cl.load_config()
55 56 >>> config
56 57 {}
57 58 """
58 59 self.clear()
59 60
60 61 def clear(self):
61 62 self.config = Struct()
62 63
63 64 def load_config(self):
64 65 """Load a config from somewhere, return a Struct.
65 66
66 67 Usually, this will cause self.config to be set and then returned.
67 68 """
68 69 return self.config
69 70
70 71
71 72 class FileConfigLoader(ConfigLoader):
72 73 """A base class for file based configurations.
73 74
74 75 As we add more file based config loaders, the common logic should go
75 76 here.
76 77 """
77 78 pass
78 79
79 80
80 81 class PyFileConfigLoader(FileConfigLoader):
81 82 """A config loader for pure python files.
82 83
83 84 This calls execfile on a plain python file and looks for attributes
84 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 89 """Build a config loader for a filename and path.
89 90
90 91 Parameters
91 92 ----------
92 93 filename : str
93 94 The file name of the config file.
94 95 path : str, list, tuple
95 96 The path to search for the config file on, or a sequence of
96 97 paths to try in order.
97 98 """
98 99 super(PyFileConfigLoader, self).__init__()
99 100 self.filename = filename
100 101 self.path = path
101 102 self.full_filename = ''
102 103 self.data = None
103 104
104 105 def load_config(self):
105 106 """Load the config from a file and return it as a Struct."""
106 107 self._find_file()
107 108 self._read_file_as_dict()
108 109 self._convert_to_struct()
109 110 return self.config
110 111
111 112 def _find_file(self):
112 113 """Try to find the file by searching the paths."""
113 if os.path.isfile(os.path.expanduser(self.filename)):
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))
114 self.full_filename = filefind(self.filename, self.path)
129 115
130 116 def _read_file_as_dict(self):
131 117 self.data = {}
132 118 execfile(self.full_filename, self.data)
133 119
134 120 def _convert_to_struct(self):
135 121 if self.data is None:
136 122 ConfigLoaderError('self.data does not exist')
137 123 for k, v in self.data.iteritems():
138 124 if k == k.upper():
139 125 self.config[k] = v
140 126
141 127
142 128 class CommandLineConfigLoader(ConfigLoader):
143 129 """A config loader for command line arguments.
144 130
145 131 As we add more command line based loaders, the common logic should go
146 132 here.
147 133 """
148 134
149 135
150 136 class NoDefault(object): pass
151 137 NoDefault = NoDefault()
152 138
153 139 class ArgParseConfigLoader(CommandLineConfigLoader):
154 140
155 141 # arguments = [(('-f','--file'),dict(type=str,dest='file'))]
156 142 arguments = ()
157 143
158 144 def __init__(self, *args, **kw):
159 145 """Create a config loader for use with argparse.
160 146
161 147 The args and kwargs arguments here are passed onto the constructor
162 148 of :class:`argparse.ArgumentParser`.
163 149 """
164 150 super(CommandLineConfigLoader, self).__init__()
165 151 self.args = args
166 152 self.kw = kw
167 153
168 154 def load_config(self, args=None):
169 155 """Parse command line arguments and return as a Struct."""
170 156 self._create_parser()
171 157 self._parse_args(args)
172 158 self._convert_to_struct()
173 159 return self.config
174 160
175 161 def _create_parser(self):
176 162 self.parser = argparse.ArgumentParser(*self.args, **self.kw)
177 163 self._add_arguments()
164 self._add_other_arguments()
165
166 def _add_other_arguments():
167 pass
178 168
179 169 def _add_arguments(self):
180 170 for argument in self.arguments:
181 171 if not argument[1].has_key('default'):
182 172 argument[1]['default'] = NoDefault
183 173 self.parser.add_argument(*argument[0],**argument[1])
184 174
185 175 def _parse_args(self, args=None):
186 176 """self.parser->self.parsed_data"""
187 177 if args is None:
188 178 self.parsed_data = self.parser.parse_args()
189 179 else:
190 180 self.parsed_data = self.parser.parse_args(args)
191 181
192 182 def _convert_to_struct(self):
193 183 """self.parsed_data->self.config"""
194 184 self.config = Struct()
195 185 for k, v in vars(self.parsed_data).items():
196 186 if v is not NoDefault:
197 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)
@@ -1,161 +1,231 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 An application for IPython
5 5
6 6 Authors:
7 7
8 8 * Brian Granger
9 9 * Fernando Perez
10 10
11 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 #-----------------------------------------------------------------------------
51 16 # Copyright (C) 2008-2009 The IPython Development Team
52 17 #
53 18 # Distributed under the terms of the BSD License. The full license is in
54 19 # the file COPYING, distributed as part of this software.
55 20 #-----------------------------------------------------------------------------
56 21
57 22 #-----------------------------------------------------------------------------
58 23 # Imports
59 24 #-----------------------------------------------------------------------------
60 25
26 import os
61 27 import sys
28 import traceback
29
62 30 from copy import deepcopy
63 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 39 # Classes and functions
67 40 #-----------------------------------------------------------------------------
68 41
69 42
70 43 class ApplicationError(Exception):
71 44 pass
72 45
73 46
74 47 class Application(object):
48 """Load a config, construct an app and run it.
49 """
75 50
76 runtime_dirs = []
77 default_config = Struct()
78 runtime_dir = ''
79 config_file = ''
80 name = ''
51 config_file_name = 'ipython_config.py'
52 name = 'ipython'
53 debug = False
81 54
82 55 def __init__(self):
83 56 pass
84 57
85 58 def start(self):
86 59 """Start the application."""
87 self.attempt(self.create_command_line_config)
88 self.attempt(self.find_runtime_dirs)
89 self.attempt(self.create_runtime_dirs)
90 self.attempt(self.find_config_files)
91 self.attempt(self.create_file_configs)
60 self.attempt(self.create_default_config)
61 self.attempt(self.pre_load_command_line_config)
62 self.attempt(self.load_command_line_config, action='exit')
63 self.attempt(self.post_load_command_line_config)
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 70 self.attempt(self.merge_configs)
71 self.attempt(self.pre_construct)
93 72 self.attempt(self.construct)
94 self.attempt(self.start_logging)
73 self.attempt(self.post_construct)
95 74 self.attempt(self.start_app)
96 75
97 76 #-------------------------------------------------------------------------
98 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 85 def create_command_line_config(self):
102 """Read the command line args and return its config object."""
103 self.command_line_config = Struct()
86 """Create and return a command line config loader."""
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 find_runtime_dirs(self):
106 """Find the runtime directory for this application.
93 def load_command_line_config(self):
94 """Load the command line config.
107 95
108 This should set self.runtime_dir.
96 This method also sets ``self.debug``.
109 97 """
110 pass
111 98
112 def create_runtime_dirs(self):
113 """Create the runtime dirs if they don't exist."""
99 loader = self.create_command_line_config()
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 110 pass
115 111
116 def find_config_files(self):
117 """Find the config file for this application."""
112 def find_ipythondir(self):
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 150 pass
119 151
120 def create_file_configs(self):
121 self.file_configs = [Struct()]
152 def load_file_config(self):
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 175 def merge_configs(self):
176 """Merge the default, command line and file config objects."""
124 177 config = Struct()
125 all_configs = self.file_configs + self.command_line_config
126 for c in all_configs:
127 config.update(c)
178 config.update(self.default_config)
179 config.update(self.command_line_config)
180 config.update(self.file_config)
128 181 self.master_config = config
182 self.log("Master config created:", self.master_config)
129 183
130 def construct(self, config):
131 """Construct the main components that make up this app."""
184 def pre_construct(self):
185 """Do actions after the config has been built, but before construct."""
132 186 pass
133 187
134 def start_logging(self):
135 """Start logging, if needed, at the last possible moment."""
188 def construct(self):
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 194 pass
137 195
138 196 def start_app(self):
139 197 """Actually start the app."""
140 pass
198 self.log("Starting application...")
141 199
142 200 #-------------------------------------------------------------------------
143 201 # Utility methods
144 202 #-------------------------------------------------------------------------
145 203
146 204 def abort(self):
147 205 """Abort the starting of the application."""
148 206 print "Aborting application: ", self.name
149 207 sys.exit(1)
150 208
151 def attempt(self, func):
209 def exit(self):
210 print "Exiting application: ", self.name
211 sys.exit(1)
212
213 def attempt(self, func, action='abort'):
152 214 try:
153 215 func()
154 216 except:
155 self.handle_error()
156 self.abort()
157
158 def handle_error(self):
159 print "I am dying!"
160
161 No newline at end of file
217 if action == 'abort':
218 self.print_traceback()
219 self.abort()
220 elif action == 'exit':
221 self.exit()
222
223 def print_traceback(self):
224 print "Error in appliction startup: ", self.name
225 print
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
@@ -1,225 +1,225 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 A lightweight component system for IPython.
5 5
6 6 Authors:
7 7
8 8 * Brian Granger
9 9 * Fernando Perez
10 10 """
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Copyright (C) 2008-2009 The IPython Development Team
14 14 #
15 15 # Distributed under the terms of the BSD License. The full license is in
16 16 # the file COPYING, distributed as part of this software.
17 17 #-----------------------------------------------------------------------------
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Imports
21 21 #-----------------------------------------------------------------------------
22 22
23 23 from copy import deepcopy
24 24 from weakref import WeakValueDictionary
25 25
26 26 from IPython.utils.ipstruct import Struct
27 27 from IPython.utils.traitlets import (
28 28 HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This
29 29 )
30 30
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Helper classes for Components
34 34 #-----------------------------------------------------------------------------
35 35
36 36
37 37 class ComponentError(Exception):
38 38 pass
39 39
40 40 class MetaComponentTracker(type):
41 41 """A metaclass that tracks instances of Components and its subclasses."""
42 42
43 43 def __init__(cls, name, bases, d):
44 44 super(MetaComponentTracker, cls).__init__(name, bases, d)
45 45 cls.__instance_refs = WeakValueDictionary()
46 46 cls.__numcreated = 0
47 47
48 48 def __call__(cls, *args, **kw):
49 49 """Called when *class* is called (instantiated)!!!
50 50
51 51 When a Component or subclass is instantiated, this is called and
52 52 the instance is saved in a WeakValueDictionary for tracking.
53 53 """
54 54
55 55 instance = super(MetaComponentTracker, cls).__call__(*args, **kw)
56 56 for c in cls.__mro__:
57 57 if issubclass(cls, c) and issubclass(c, Component):
58 58 c.__numcreated += 1
59 59 c.__instance_refs[c.__numcreated] = instance
60 60 return instance
61 61
62 62 def get_instances(cls, name=None, klass=None, root=None):
63 63 """Get all instances of cls and its subclasses.
64 64
65 65 Parameters
66 66 ----------
67 67 name : str
68 68 Limit to components with this name.
69 69 klass : class
70 70 Limit to components having isinstance(component, klass)
71 71 root : Component or subclass
72 72 Limit to components having this root.
73 73 """
74 74 instances = cls.__instance_refs.values()
75 75 if name is not None:
76 76 instances = [i for i in instances if i.name == name]
77 77 if klass is not None:
78 78 instances = [i for i in instances if isinstance(i, klass)]
79 79 if root is not None:
80 80 instances = [i for i in instances if i.root == root]
81 81 return instances
82 82
83 83 def get_instances_by_condition(cls, call, name=None, klass=None, root=None):
84 84 """Get all instances of cls, i such that call(i)==True.
85 85
86 86 This also takes the ``name``, ``klass`` and ``root`` arguments of
87 87 :meth:`get_instance`
88 88 """
89 89 return [i for i in cls.get_instances(name,klass,root) if call(i)]
90 90
91 91
92 92 class ComponentNameGenerator(object):
93 93 """A Singleton to generate unique component names."""
94 94
95 95 def __init__(self, prefix):
96 96 self.prefix = prefix
97 97 self.i = 0
98 98
99 99 def __call__(self):
100 100 count = self.i
101 101 self.i += 1
102 102 return "%s%s" % (self.prefix, count)
103 103
104 104
105 105 ComponentNameGenerator = ComponentNameGenerator('ipython.component')
106 106
107 107
108 108 class MetaComponent(MetaHasTraitlets, MetaComponentTracker):
109 109 pass
110 110
111 111
112 112 #-----------------------------------------------------------------------------
113 113 # Component implementation
114 114 #-----------------------------------------------------------------------------
115 115
116 116
117 117 class Component(HasTraitlets):
118 118
119 119 __metaclass__ = MetaComponent
120 120
121 121 # Traitlets are fun!
122 122 config = Instance(Struct,(),{})
123 123 parent = This()
124 124 root = This()
125 125
126 126 def __init__(self, parent, name=None, config=None):
127 127 """Create a component given a parent and possibly and name and config.
128 128
129 129 Parameters
130 130 ----------
131 131 parent : Component subclass
132 132 The parent in the component graph. The parent is used
133 133 to get the root of the component graph.
134 134 name : str
135 135 The unique name of the component. If empty, then a unique
136 136 one will be autogenerated.
137 137 config : Struct
138 138 If this is empty, self.config = parent.config, otherwise
139 139 self.config = config and root.config is ignored. This argument
140 140 should only be used to *override* the automatic inheritance of
141 141 parent.config. If a caller wants to modify parent.config
142 142 (not override), the caller should make a copy and change
143 143 attributes and then pass the copy to this argument.
144 144
145 145 Notes
146 146 -----
147 147 Subclasses of Component must call the :meth:`__init__` method of
148 148 :class:`Component` *before* doing anything else and using
149 149 :func:`super`::
150 150
151 151 class MyComponent(Component):
152 152 def __init__(self, parent, name=None, config=None):
153 153 super(MyComponent, self).__init__(parent, name, config)
154 154 # Then any other code you need to finish initialization.
155 155
156 156 This ensures that the :attr:`parent`, :attr:`name` and :attr:`config`
157 157 attributes are handled properly.
158 158 """
159 159 super(Component, self).__init__()
160 160 self._children = []
161 161 if name is None:
162 162 self.name = ComponentNameGenerator()
163 163 else:
164 164 self.name = name
165 165 self.root = self # This is the default, it is set when parent is set
166 166 self.parent = parent
167 167 if config is not None:
168 168 self.config = deepcopy(config)
169 169 else:
170 170 if self.parent is not None:
171 171 self.config = deepcopy(self.parent.config)
172 172
173 173 #-------------------------------------------------------------------------
174 174 # Static traitlet notifiations
175 175 #-------------------------------------------------------------------------
176 176
177 177 def _parent_changed(self, name, old, new):
178 178 if old is not None:
179 179 old._remove_child(self)
180 180 if new is not None:
181 181 new._add_child(self)
182 182
183 183 if new is None:
184 184 self.root = self
185 185 else:
186 186 self.root = new.root
187 187
188 188 def _root_changed(self, name, old, new):
189 189 if self.parent is None:
190 190 if not (new is self):
191 191 raise ComponentError("Root not self, but parent is None.")
192 192 else:
193 193 if not self.parent.root is new:
194 194 raise ComponentError("Error in setting the root attribute: "
195 195 "root != parent.root")
196 196
197 197 def _config_changed(self, name, old, new):
198 198 # Get all traitlets with a config_key metadata entry
199 traitlets = self.traitlets(config_key=lambda v: True)
199 traitlets = self.traitlets('config_key')
200 200 for k, v in traitlets.items():
201 201 try:
202 202 config_value = new[v.get_metadata('config_key')]
203 203 except KeyError:
204 204 pass
205 205 else:
206 206 setattr(self, k, config_value)
207 207
208 208 @property
209 209 def children(self):
210 210 """A list of all my child components."""
211 211 return self._children
212 212
213 213 def _remove_child(self, child):
214 214 """A private method for removing children componenets."""
215 215 if child in self._children:
216 216 index = self._children.index(child)
217 217 del self._children[index]
218 218
219 219 def _add_child(self, child):
220 220 """A private method for adding children componenets."""
221 221 if child not in self._children:
222 222 self._children.append(child)
223 223
224 224 def __repr__(self):
225 225 return "<Component('%s')>" % self.name
@@ -1,790 +1,792 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 IPython -- An enhanced Interactive Python
4 4
5 5 Requires Python 2.1 or better.
6 6
7 7 This file contains the main make_IPython() starter function.
8 8 """
9 9
10 10 #*****************************************************************************
11 11 # Copyright (C) 2008-2009 The IPython Development Team
12 12 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 16 #*****************************************************************************
17 17
18 18 try:
19 19 credits._Printer__data = """
20 20 Python: %s
21 21
22 22 IPython: The IPython Development Team.
23 23 See http://ipython.scipy.org for more information.""" \
24 24 % credits._Printer__data
25 25
26 26 copyright._Printer__data += """
27 27
28 28 Copyright (c) 2008-2009 The IPython Development Team.
29 29 Copyright (c) 2001-2007 Fernando Perez, Janko Hauser, Nathan Gray.
30 30 All Rights Reserved."""
31 31 except NameError:
32 32 # Can happen if ipython was started with 'python -S', so that site.py is
33 33 # not loaded
34 34 pass
35 35
36 36 #****************************************************************************
37 37 # Required modules
38 38
39 39 # From the standard library
40 40 import __main__
41 41 import __builtin__
42 42 import os
43 43 import sys
44 44 from pprint import pprint
45 45 import warnings
46 46
47 47 # Our own
48 48 from IPython.utils import DPyGetOpt
49 49 from IPython.core import release
50 50 from IPython.utils.ipstruct import Struct
51 51 from IPython.core.outputtrap import OutputTrap
52 52 from IPython.config.configloader import ConfigLoader
53 53 from IPython.core.iplib import InteractiveShell
54 54 from IPython.core.usage import cmd_line_usage, interactive_usage
55 55 from IPython.utils.genutils import *
56 56
57 57
58 58 def force_import(modname,force_reload=False):
59 59 if modname in sys.modules and force_reload:
60 60 info("reloading: %s" % modname)
61 61 reload(sys.modules[modname])
62 62 else:
63 63 __import__(modname)
64 64
65 65
66 66 def threaded_shell_warning():
67 67 msg = """
68 68
69 69 The IPython threaded shells and their associated command line
70 70 arguments (pylab/wthread/gthread/qthread/q4thread) have been
71 71 deprecated. See the %gui magic for information on the new interface.
72 72 """
73 73 warnings.warn(msg, category=DeprecationWarning, stacklevel=1)
74 74
75 75
76 76 #-----------------------------------------------------------------------------
77 77 def make_IPython(argv=None,user_ns=None,user_global_ns=None,debug=1,
78 78 rc_override=None,shell_class=InteractiveShell,
79 79 embedded=False,**kw):
80 80 """This is a dump of IPython into a single function.
81 81
82 82 Later it will have to be broken up in a sensible manner.
83 83
84 84 Arguments:
85 85
86 86 - argv: a list similar to sys.argv[1:]. It should NOT contain the desired
87 87 script name, b/c DPyGetOpt strips the first argument only for the real
88 88 sys.argv.
89 89
90 90 - user_ns: a dict to be used as the user's namespace."""
91 91
92 92 #----------------------------------------------------------------------
93 93 # Defaults and initialization
94 94
95 95 # For developer debugging, deactivates crash handler and uses pdb.
96 DEVDEBUG = False
96 DEVDEBUG = True
97 97
98 98 if argv is None:
99 99 argv = sys.argv
100 100
101 101 # __IP is the main global that lives throughout and represents the whole
102 102 # application. If the user redefines it, all bets are off as to what
103 103 # happens.
104 104
105 105 # __IP is the name of he global which the caller will have accessible as
106 106 # __IP.name. We set its name via the first parameter passed to
107 107 # InteractiveShell:
108 108
109 109 IP = shell_class('__IP',user_ns=user_ns,user_global_ns=user_global_ns,
110 110 embedded=embedded,**kw)
111 111
112 112 # Put 'help' in the user namespace
113 113 try:
114 114 from site import _Helper
115 115 IP.user_ns['help'] = _Helper()
116 116 except ImportError:
117 117 warn('help() not available - check site.py')
118 118
119 119 if DEVDEBUG:
120 120 # For developer debugging only (global flag)
121 121 from IPython.core import ultratb
122 122 sys.excepthook = ultratb.VerboseTB(call_pdb=1)
123 123
124 124 IP.BANNER_PARTS = ['Python %s\n'
125 125 'Type "copyright", "credits" or "license" '
126 126 'for more information.\n'
127 127 % (sys.version.split('\n')[0],),
128 128 "IPython %s -- An enhanced Interactive Python."
129 129 % (release.version,),
130 130 """\
131 131 ? -> Introduction and overview of IPython's features.
132 132 %quickref -> Quick reference.
133 133 help -> Python's own help system.
134 134 object? -> Details about 'object'. ?object also works, ?? prints more.
135 135 """ ]
136 136
137 137 IP.usage = interactive_usage
138 138
139 139 # Platform-dependent suffix.
140 140 if os.name == 'posix':
141 141 rc_suffix = ''
142 142 else:
143 143 rc_suffix = '.ini'
144 144
145 145 # default directory for configuration
146 146 ipythondir_def = get_ipython_dir()
147 147
148 148 sys.path.insert(0, '') # add . to sys.path. Fix from Prabhu Ramachandran
149 149
150 150 # we need the directory where IPython itself is installed
151 151 import IPython
152 152 IPython_dir = os.path.dirname(IPython.__file__)
153 153 del IPython
154 154
155 155 #-------------------------------------------------------------------------
156 156 # Command line handling
157 157
158 158 # Valid command line options (uses DPyGetOpt syntax, like Perl's
159 159 # GetOpt::Long)
160 160
161 161 # Any key not listed here gets deleted even if in the file (like session
162 162 # or profile). That's deliberate, to maintain the rc namespace clean.
163 163
164 164 # Each set of options appears twice: under _conv only the names are
165 165 # listed, indicating which type they must be converted to when reading the
166 166 # ipythonrc file. And under DPyGetOpt they are listed with the regular
167 167 # DPyGetOpt syntax (=s,=i,:f,etc).
168 168
169 169 # Make sure there's a space before each end of line (they get auto-joined!)
170 170 cmdline_opts = ('autocall=i autoindent! automagic! banner! cache_size|cs=i '
171 171 'c=s classic|cl color_info! colors=s confirm_exit! '
172 172 'debug! deep_reload! editor=s log|l messages! nosep '
173 173 'object_info_string_level=i pdb! '
174 174 'pprint! prompt_in1|pi1=s prompt_in2|pi2=s prompt_out|po=s '
175 175 'pydb! '
176 176 'pylab_import_all! '
177 177 'quick screen_length|sl=i prompts_pad_left=i '
178 178 'logfile|lf=s logplay|lp=s profile|p=s '
179 179 'readline! readline_merge_completions! '
180 180 'readline_omit__names! '
181 181 'rcfile=s separate_in|si=s separate_out|so=s '
182 182 'separate_out2|so2=s xmode=s wildcards_case_sensitive! '
183 183 'magic_docstrings system_verbose! '
184 184 'multi_line_specials! '
185 185 'term_title! wxversion=s '
186 186 'autoedit_syntax!')
187 187
188 188 # Options that can *only* appear at the cmd line (not in rcfiles).
189 189
190 190 cmdline_only = ('help interact|i ipythondir=s Version upgrade '
191 191 'gthread! qthread! q4thread! wthread! tkthread! pylab! tk! '
192 192 # 'twisted!' # disabled for now.
193 193 )
194 194
195 195 # Build the actual name list to be used by DPyGetOpt
196 196 opts_names = qw(cmdline_opts) + qw(cmdline_only)
197 197
198 198 # Set sensible command line defaults.
199 199 # This should have everything from cmdline_opts and cmdline_only
200 200 opts_def = Struct(autocall = 1,
201 201 autoedit_syntax = 0,
202 202 autoindent = 0,
203 203 automagic = 1,
204 204 autoexec = [],
205 205 banner = 1,
206 206 c = '',
207 207 cache_size = 1000,
208 208 classic = 0,
209 209 color_info = 0,
210 210 colors = 'NoColor',
211 211 confirm_exit = 1,
212 212 debug = 0,
213 213 deep_reload = 0,
214 214 editor = '0',
215 215 gthread = 0,
216 216 help = 0,
217 217 interact = 0,
218 218 ipythondir = ipythondir_def,
219 219 log = 0,
220 220 logfile = '',
221 221 logplay = '',
222 222 messages = 1,
223 223 multi_line_specials = 1,
224 224 nosep = 0,
225 225 object_info_string_level = 0,
226 226 pdb = 0,
227 227 pprint = 0,
228 228 profile = '',
229 229 prompt_in1 = 'In [\\#]: ',
230 230 prompt_in2 = ' .\\D.: ',
231 231 prompt_out = 'Out[\\#]: ',
232 232 prompts_pad_left = 1,
233 233 pydb = 0,
234 234 pylab = 0,
235 235 pylab_import_all = 1,
236 236 q4thread = 0,
237 237 qthread = 0,
238 238 quick = 0,
239 239 quiet = 0,
240 240 rcfile = 'ipythonrc' + rc_suffix,
241 241 readline = 1,
242 242 readline_merge_completions = 1,
243 243 readline_omit__names = 0,
244 244 screen_length = 0,
245 245 separate_in = '\n',
246 246 separate_out = '\n',
247 247 separate_out2 = '',
248 248 system_header = 'IPython system call: ',
249 249 system_verbose = 0,
250 250 term_title = 1,
251 251 tk = 0,
252 252 #twisted= 0, # disabled for now
253 253 upgrade = 0,
254 254 Version = 0,
255 255 wildcards_case_sensitive = 1,
256 256 wthread = 0,
257 257 wxversion = '0',
258 258 xmode = 'Context',
259 259 magic_docstrings = 0, # undocumented, for doc generation
260 260 )
261 261
262 262 # Things that will *only* appear in rcfiles (not at the command line).
263 263 # Make sure there's a space before each end of line (they get auto-joined!)
264 264 rcfile_opts = { qwflat: 'include import_mod import_all execfile ',
265 265 qw_lol: 'import_some ',
266 266 # for things with embedded whitespace:
267 267 list_strings:'execute alias readline_parse_and_bind ',
268 268 # Regular strings need no conversion:
269 269 None:'readline_remove_delims ',
270 270 }
271 271 # Default values for these
272 272 rc_def = Struct(include = [],
273 273 import_mod = [],
274 274 import_all = [],
275 275 import_some = [[]],
276 276 execute = [],
277 277 execfile = [],
278 278 alias = [],
279 279 readline_parse_and_bind = [],
280 280 readline_remove_delims = '',
281 281 )
282 282
283 283 # Build the type conversion dictionary from the above tables:
284 284 typeconv = rcfile_opts.copy()
285 285 typeconv.update(optstr2types(cmdline_opts))
286 286
287 287 # FIXME: the None key appears in both, put that back together by hand. Ugly!
288 288 typeconv[None] += ' ' + rcfile_opts[None]
289 289
290 290 # Remove quotes at ends of all strings (used to protect spaces)
291 291 typeconv[unquote_ends] = typeconv[None]
292 292 del typeconv[None]
293 293
294 294 # Build the list we'll use to make all config decisions with defaults:
295 295 opts_all = opts_def.copy()
296 296 opts_all.update(rc_def)
297 297
298 298 # Build conflict resolver for recursive loading of config files:
299 299 # - preserve means the outermost file maintains the value, it is not
300 300 # overwritten if an included file has the same key.
301 301 # - add_flip applies + to the two values, so it better make sense to add
302 302 # those types of keys. But it flips them first so that things loaded
303 303 # deeper in the inclusion chain have lower precedence.
304 304 conflict = {'preserve': ' '.join([ typeconv[int],
305 305 typeconv[unquote_ends] ]),
306 306 'add_flip': ' '.join([ typeconv[qwflat],
307 307 typeconv[qw_lol],
308 308 typeconv[list_strings] ])
309 309 }
310 310
311 311 # Now actually process the command line
312 312 getopt = DPyGetOpt.DPyGetOpt()
313 313 getopt.setIgnoreCase(0)
314 314
315 315 getopt.parseConfiguration(opts_names)
316 316
317 317 try:
318 318 getopt.processArguments(argv)
319 319 except DPyGetOpt.ArgumentError, exc:
320 320 print cmd_line_usage
321 321 warn('\nError in Arguments: "%s"' % exc)
322 322 sys.exit(1)
323 323
324 324 # convert the options dict to a struct for much lighter syntax later
325 325 opts = Struct(getopt.optionValues)
326 326 args = getopt.freeValues
327 327
328 328 # this is the struct (which has default values at this point) with which
329 329 # we make all decisions:
330 330 opts_all.update(opts)
331 331
332 332 # Options that force an immediate exit
333 333 if opts_all.help:
334 334 page(cmd_line_usage)
335 335 sys.exit()
336 336
337 337 if opts_all.Version:
338 338 print release.version
339 339 sys.exit()
340 340
341 341 if opts_all.magic_docstrings:
342 342 IP.magic_magic('-latex')
343 343 sys.exit()
344 344
345 345 # Display the deprecation warnings about threaded shells
346 346 if opts_all.pylab == 1: threaded_shell_warning()
347 347 if opts_all.wthread == 1: threaded_shell_warning()
348 348 if opts_all.qthread == 1: threaded_shell_warning()
349 349 if opts_all.q4thread == 1: threaded_shell_warning()
350 350 if opts_all.gthread == 1: threaded_shell_warning()
351 351
352 352 # add personal ipythondir to sys.path so that users can put things in
353 353 # there for customization
354 354 sys.path.append(os.path.abspath(opts_all.ipythondir))
355 355
356 356 # Create user config directory if it doesn't exist. This must be done
357 357 # *after* getting the cmd line options.
358 358 if not os.path.isdir(opts_all.ipythondir):
359 359 IP.user_setup(opts_all.ipythondir,rc_suffix,'install')
360 360
361 361 # upgrade user config files while preserving a copy of the originals
362 362 if opts_all.upgrade:
363 363 IP.user_setup(opts_all.ipythondir,rc_suffix,'upgrade')
364 364
365 365 # check mutually exclusive options in the *original* command line
366 366 mutex_opts(opts,[qw('log logfile'),qw('rcfile profile'),
367 367 qw('classic profile'),qw('classic rcfile')])
368 368
369 369 #---------------------------------------------------------------------------
370 370 # Log replay
371 371
372 372 # if -logplay, we need to 'become' the other session. That basically means
373 373 # replacing the current command line environment with that of the old
374 374 # session and moving on.
375 375
376 376 # this is needed so that later we know we're in session reload mode, as
377 377 # opts_all will get overwritten:
378 378 load_logplay = 0
379 379
380 380 if opts_all.logplay:
381 381 load_logplay = opts_all.logplay
382 382 opts_debug_save = opts_all.debug
383 383 try:
384 384 logplay = open(opts_all.logplay)
385 385 except IOError:
386 386 if opts_all.debug: IP.InteractiveTB()
387 387 warn('Could not open logplay file '+`opts_all.logplay`)
388 388 # restore state as if nothing had happened and move on, but make
389 389 # sure that later we don't try to actually load the session file
390 390 logplay = None
391 391 load_logplay = 0
392 392 del opts_all.logplay
393 393 else:
394 394 try:
395 395 logplay.readline()
396 396 logplay.readline();
397 397 # this reloads that session's command line
398 398 cmd = logplay.readline()[6:]
399 399 exec cmd
400 400 # restore the true debug flag given so that the process of
401 401 # session loading itself can be monitored.
402 402 opts.debug = opts_debug_save
403 403 # save the logplay flag so later we don't overwrite the log
404 404 opts.logplay = load_logplay
405 405 # now we must update our own structure with defaults
406 406 opts_all.update(opts)
407 407 # now load args
408 408 cmd = logplay.readline()[6:]
409 409 exec cmd
410 410 logplay.close()
411 411 except:
412 412 logplay.close()
413 413 if opts_all.debug: IP.InteractiveTB()
414 414 warn("Logplay file lacking full configuration information.\n"
415 415 "I'll try to read it, but some things may not work.")
416 416
417 417 #-------------------------------------------------------------------------
418 418 # set up output traps: catch all output from files, being run, modules
419 419 # loaded, etc. Then give it to the user in a clean form at the end.
420 420
421 421 msg_out = 'Output messages. '
422 422 msg_err = 'Error messages. '
423 423 msg_sep = '\n'
424 424 msg = Struct(config = OutputTrap('Configuration Loader',msg_out,
425 425 msg_err,msg_sep,debug,
426 426 quiet_out=1),
427 427 user_exec = OutputTrap('User File Execution',msg_out,
428 428 msg_err,msg_sep,debug),
429 429 logplay = OutputTrap('Log Loader',msg_out,
430 430 msg_err,msg_sep,debug),
431 431 summary = ''
432 432 )
433 433
434 434 #-------------------------------------------------------------------------
435 435 # Process user ipythonrc-type configuration files
436 436
437 437 # turn on output trapping and log to msg.config
438 438 # remember that with debug on, trapping is actually disabled
439 439 msg.config.trap_all()
440 440
441 441 # look for rcfile in current or default directory
442 442 try:
443 443 opts_all.rcfile = filefind(opts_all.rcfile,opts_all.ipythondir)
444 444 except IOError:
445 445 if opts_all.debug: IP.InteractiveTB()
446 446 warn('Configuration file %s not found. Ignoring request.'
447 447 % (opts_all.rcfile) )
448 448
449 print opts_all.rcfile, opts_all.ipythondir
450
449 451 # 'profiles' are a shorthand notation for config filenames
450 452 profile_handled_by_legacy = False
451 453 if opts_all.profile:
452 454
453 455 try:
454 456 opts_all.rcfile = filefind('ipythonrc-' + opts_all.profile
455 457 + rc_suffix,
456 458 opts_all.ipythondir)
457 459 profile_handled_by_legacy = True
458 460 except IOError:
459 461 if opts_all.debug: IP.InteractiveTB()
460 462 opts.profile = '' # remove profile from options if invalid
461 463 # We won't warn anymore, primary method is ipy_profile_PROFNAME
462 464 # which does trigger a warning.
463 465
464 466 # load the config file
465 467 rcfiledata = None
466 468 if opts_all.quick:
467 469 print 'Launching IPython in quick mode. No config file read.'
468 470 elif opts_all.rcfile:
469 471 try:
470 472 cfg_loader = ConfigLoader(conflict)
471 473 rcfiledata = cfg_loader.load(opts_all.rcfile,typeconv,
472 474 'include',opts_all.ipythondir,
473 475 purge = 1,
474 476 unique = conflict['preserve'])
475 477 except:
476 478 IP.InteractiveTB()
477 479 warn('Problems loading configuration file '+
478 480 `opts_all.rcfile`+
479 481 '\nStarting with default -bare bones- configuration.')
480 482 else:
481 483 warn('No valid configuration file found in either currrent directory\n'+
482 484 'or in the IPython config. directory: '+`opts_all.ipythondir`+
483 485 '\nProceeding with internal defaults.')
484 486
485 487 #------------------------------------------------------------------------
486 488 # Set exception handlers in mode requested by user.
487 489 otrap = OutputTrap(trap_out=1) # trap messages from magic_xmode
488 490 IP.magic_xmode(opts_all.xmode)
489 491 otrap.release_out()
490 492
491 493 #------------------------------------------------------------------------
492 494 # Execute user config
493 495
494 496 # Create a valid config structure with the right precedence order:
495 497 # defaults < rcfile < command line. This needs to be in the instance, so
496 498 # that method calls below that rely on it find it.
497 499 IP.rc = rc_def.copy()
498 500
499 501 # Work with a local alias inside this routine to avoid unnecessary
500 502 # attribute lookups.
501 503 IP_rc = IP.rc
502 504
503 505 IP_rc.update(opts_def)
504 506 if rcfiledata:
505 507 IP_rc.update(rcfiledata)
506 508 IP_rc.update(opts)
507 509 if rc_override is not None:
508 510 IP_rc.update(rc_override)
509 511
510 512 # Store the original cmd line for reference:
511 513 IP_rc.opts = opts
512 514 IP_rc.args = args
513 515
514 516 # create a *runtime* Struct like rc for holding parameters which may be
515 517 # created and/or modified by runtime user extensions.
516 518 IP.runtime_rc = Struct()
517 519
518 520 # from this point on, all config should be handled through IP_rc,
519 521 # opts* shouldn't be used anymore.
520 522
521 523
522 524 # update IP_rc with some special things that need manual
523 525 # tweaks. Basically options which affect other options. I guess this
524 526 # should just be written so that options are fully orthogonal and we
525 527 # wouldn't worry about this stuff!
526 528
527 529 if IP_rc.classic:
528 530 IP_rc.quick = 1
529 531 IP_rc.cache_size = 0
530 532 IP_rc.pprint = 0
531 533 IP_rc.prompt_in1 = '>>> '
532 534 IP_rc.prompt_in2 = '... '
533 535 IP_rc.prompt_out = ''
534 536 IP_rc.separate_in = IP_rc.separate_out = IP_rc.separate_out2 = '0'
535 537 IP_rc.colors = 'NoColor'
536 538 IP_rc.xmode = 'Plain'
537 539
538 540 IP.pre_config_initialization()
539 541 # configure readline
540 542
541 543 # update exception handlers with rc file status
542 544 otrap.trap_out() # I don't want these messages ever.
543 545 IP.magic_xmode(IP_rc.xmode)
544 546 otrap.release_out()
545 547
546 548 # activate logging if requested and not reloading a log
547 549 if IP_rc.logplay:
548 550 IP.magic_logstart(IP_rc.logplay + ' append')
549 551 elif IP_rc.logfile:
550 552 IP.magic_logstart(IP_rc.logfile)
551 553 elif IP_rc.log:
552 554 IP.magic_logstart()
553 555
554 556 # find user editor so that it we don't have to look it up constantly
555 557 if IP_rc.editor.strip()=='0':
556 558 try:
557 559 ed = os.environ['EDITOR']
558 560 except KeyError:
559 561 if os.name == 'posix':
560 562 ed = 'vi' # the only one guaranteed to be there!
561 563 else:
562 564 ed = 'notepad' # same in Windows!
563 565 IP_rc.editor = ed
564 566
565 567 # Keep track of whether this is an embedded instance or not (useful for
566 568 # post-mortems).
567 569 IP_rc.embedded = IP.embedded
568 570
569 571 # Recursive reload
570 572 try:
571 573 from IPython.lib import deepreload
572 574 if IP_rc.deep_reload:
573 575 __builtin__.reload = deepreload.reload
574 576 else:
575 577 __builtin__.dreload = deepreload.reload
576 578 del deepreload
577 579 except ImportError:
578 580 pass
579 581
580 582 # Save the current state of our namespace so that the interactive shell
581 583 # can later know which variables have been created by us from config files
582 584 # and loading. This way, loading a file (in any way) is treated just like
583 585 # defining things on the command line, and %who works as expected.
584 586
585 587 # DON'T do anything that affects the namespace beyond this point!
586 588 IP.internal_ns.update(__main__.__dict__)
587 589
588 590 #IP.internal_ns.update(locals()) # so our stuff doesn't show up in %who
589 591
590 592 # Now run through the different sections of the users's config
591 593 if IP_rc.debug:
592 594 print 'Trying to execute the following configuration structure:'
593 595 print '(Things listed first are deeper in the inclusion tree and get'
594 596 print 'loaded first).\n'
595 597 pprint(IP_rc.__dict__)
596 598
597 599 for mod in IP_rc.import_mod:
598 600 try:
599 601 exec 'import '+mod in IP.user_ns
600 602 except :
601 603 IP.InteractiveTB()
602 604 import_fail_info(mod)
603 605
604 606 for mod_fn in IP_rc.import_some:
605 607 if not mod_fn == []:
606 608 mod,fn = mod_fn[0],','.join(mod_fn[1:])
607 609 try:
608 610 exec 'from '+mod+' import '+fn in IP.user_ns
609 611 except :
610 612 IP.InteractiveTB()
611 613 import_fail_info(mod,fn)
612 614
613 615 for mod in IP_rc.import_all:
614 616 try:
615 617 exec 'from '+mod+' import *' in IP.user_ns
616 618 except :
617 619 IP.InteractiveTB()
618 620 import_fail_info(mod)
619 621
620 622 for code in IP_rc.execute:
621 623 try:
622 624 exec code in IP.user_ns
623 625 except:
624 626 IP.InteractiveTB()
625 627 warn('Failure executing code: ' + `code`)
626 628
627 629 # Execute the files the user wants in ipythonrc
628 630 for file in IP_rc.execfile:
629 631 try:
630 632 file = filefind(file,sys.path+[IPython_dir])
631 633 except IOError:
632 634 warn(itpl('File $file not found. Skipping it.'))
633 635 else:
634 636 IP.safe_execfile(os.path.expanduser(file),IP.user_ns)
635 637
636 638 # finally, try importing ipy_*_conf for final configuration
637 639 try:
638 640 import ipy_system_conf
639 641 except ImportError:
640 642 if opts_all.debug: IP.InteractiveTB()
641 643 warn("Could not import 'ipy_system_conf'")
642 644 except:
643 645 IP.InteractiveTB()
644 646 import_fail_info('ipy_system_conf')
645 647
646 648 # only import prof module if ipythonrc-PROF was not found
647 649 if opts_all.profile and not profile_handled_by_legacy:
648 650 profmodname = 'ipy_profile_' + opts_all.profile
649 651 try:
650 652 force_import(profmodname)
651 653 except:
652 654 IP.InteractiveTB()
653 655 print "Error importing",profmodname,\
654 656 "- perhaps you should run %upgrade?"
655 657 import_fail_info(profmodname)
656 658 else:
657 659 opts.profile = opts_all.profile
658 660 else:
659 661 force_import('ipy_profile_none')
660 662 # XXX - this is wrong: ipy_user_conf should not be loaded unconditionally,
661 663 # since the user could have specified a config file path by hand.
662 664 try:
663 665 force_import('ipy_user_conf')
664 666 except:
665 667 conf = opts_all.ipythondir + "/ipy_user_conf.py"
666 668 IP.InteractiveTB()
667 669 if not os.path.isfile(conf):
668 670 warn(conf + ' does not exist, please run %upgrade!')
669 671
670 672 import_fail_info("ipy_user_conf")
671 673
672 674 # Define the history file for saving commands in between sessions
673 675 try:
674 676 histfname = 'history-%s' % opts.profile
675 677 except AttributeError:
676 678 histfname = 'history'
677 679 IP.histfile = os.path.join(opts_all.ipythondir,histfname)
678 680
679 681 # finally, push the argv to options again to ensure highest priority
680 682 IP_rc.update(opts)
681 683
682 684 # release stdout and stderr and save config log into a global summary
683 685 msg.config.release_all()
684 686 if IP_rc.messages:
685 687 msg.summary += msg.config.summary_all()
686 688
687 689 #------------------------------------------------------------------------
688 690 # Setup interactive session
689 691
690 692 # Now we should be fully configured. We can then execute files or load
691 693 # things only needed for interactive use. Then we'll open the shell.
692 694
693 695 # Take a snapshot of the user namespace before opening the shell. That way
694 696 # we'll be able to identify which things were interactively defined and
695 697 # which were defined through config files.
696 698 IP.user_config_ns.update(IP.user_ns)
697 699
698 700 # Force reading a file as if it were a session log. Slower but safer.
699 701 if load_logplay:
700 702 print 'Replaying log...'
701 703 try:
702 704 if IP_rc.debug:
703 705 logplay_quiet = 0
704 706 else:
705 707 logplay_quiet = 1
706 708
707 709 msg.logplay.trap_all()
708 710 IP.safe_execfile(load_logplay,IP.user_ns,
709 711 islog = 1, quiet = logplay_quiet)
710 712 msg.logplay.release_all()
711 713 if IP_rc.messages:
712 714 msg.summary += msg.logplay.summary_all()
713 715 except:
714 716 warn('Problems replaying logfile %s.' % load_logplay)
715 717 IP.InteractiveTB()
716 718
717 719 # Load remaining files in command line
718 720 msg.user_exec.trap_all()
719 721
720 722 # Do NOT execute files named in the command line as scripts to be loaded
721 723 # by embedded instances. Doing so has the potential for an infinite
722 724 # recursion if there are exceptions thrown in the process.
723 725
724 726 # XXX FIXME: the execution of user files should be moved out to after
725 727 # ipython is fully initialized, just as if they were run via %run at the
726 728 # ipython prompt. This would also give them the benefit of ipython's
727 729 # nice tracebacks.
728 730
729 731 if (not embedded and IP_rc.args and
730 732 not IP_rc.args[0].lower().endswith('.ipy')):
731 733 name_save = IP.user_ns['__name__']
732 734 IP.user_ns['__name__'] = '__main__'
733 735 # Set our own excepthook in case the user code tries to call it
734 736 # directly. This prevents triggering the IPython crash handler.
735 737 old_excepthook,sys.excepthook = sys.excepthook, IP.excepthook
736 738
737 739 save_argv = sys.argv[1:] # save it for later restoring
738 740
739 741 sys.argv = args
740 742
741 743 try:
742 744 IP.safe_execfile(args[0], IP.user_ns)
743 745 finally:
744 746 # Reset our crash handler in place
745 747 sys.excepthook = old_excepthook
746 748 sys.argv[:] = save_argv
747 749 IP.user_ns['__name__'] = name_save
748 750
749 751 msg.user_exec.release_all()
750 752
751 753 if IP_rc.messages:
752 754 msg.summary += msg.user_exec.summary_all()
753 755
754 756 # since we can't specify a null string on the cmd line, 0 is the equivalent:
755 757 if IP_rc.nosep:
756 758 IP_rc.separate_in = IP_rc.separate_out = IP_rc.separate_out2 = '0'
757 759 if IP_rc.separate_in == '0': IP_rc.separate_in = ''
758 760 if IP_rc.separate_out == '0': IP_rc.separate_out = ''
759 761 if IP_rc.separate_out2 == '0': IP_rc.separate_out2 = ''
760 762 IP_rc.separate_in = IP_rc.separate_in.replace('\\n','\n')
761 763 IP_rc.separate_out = IP_rc.separate_out.replace('\\n','\n')
762 764 IP_rc.separate_out2 = IP_rc.separate_out2.replace('\\n','\n')
763 765
764 766 # Determine how many lines at the bottom of the screen are needed for
765 767 # showing prompts, so we can know wheter long strings are to be printed or
766 768 # paged:
767 769 num_lines_bot = IP_rc.separate_in.count('\n')+1
768 770 IP_rc.screen_length = IP_rc.screen_length - num_lines_bot
769 771
770 772 # configure startup banner
771 773 if IP_rc.c: # regular python doesn't print the banner with -c
772 774 IP_rc.banner = 0
773 775 if IP_rc.banner:
774 776 BANN_P = IP.BANNER_PARTS
775 777 else:
776 778 BANN_P = []
777 779
778 780 if IP_rc.profile: BANN_P.append('IPython profile: %s\n' % IP_rc.profile)
779 781
780 782 # add message log (possibly empty)
781 783 if msg.summary: BANN_P.append(msg.summary)
782 784 # Final banner is a string
783 785 IP.BANNER = '\n'.join(BANN_P)
784 786
785 787 # Finalize the IPython instance. This assumes the rc structure is fully
786 788 # in place.
787 789 IP.post_config_initialization()
788 790
789 791 return IP
790 792 #************************ end of file <ipmaker.py> **************************
@@ -1,2261 +1,2284 b''
1 1 # -*- coding: utf-8 -*-
2 2 """General purpose utilities.
3 3
4 4 This is a grab-bag of stuff I find useful in most programs I write. Some of
5 5 these things are also convenient when working at the command line.
6 6 """
7 7
8 8 #*****************************************************************************
9 9 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #*****************************************************************************
14 14
15 15 #****************************************************************************
16 16 # required modules from the Python standard library
17 17 import __main__
18 18 import commands
19 19 try:
20 20 import doctest
21 21 except ImportError:
22 22 pass
23 23 import os
24 24 import platform
25 25 import re
26 26 import shlex
27 27 import shutil
28 28 import subprocess
29 29 import sys
30 30 import tempfile
31 31 import time
32 32 import types
33 33 import warnings
34 34
35 35 # Curses and termios are Unix-only modules
36 36 try:
37 37 import curses
38 38 # We need termios as well, so if its import happens to raise, we bail on
39 39 # using curses altogether.
40 40 import termios
41 41 except ImportError:
42 42 USE_CURSES = False
43 43 else:
44 44 # Curses on Solaris may not be complete, so we can't use it there
45 45 USE_CURSES = hasattr(curses,'initscr')
46 46
47 47 # Other IPython utilities
48 48 import IPython
49 49 from IPython.external.Itpl import Itpl,itpl,printpl
50 50 from IPython.utils import platutils
51 51 from IPython.utils import DPyGetOpt
52 52 from IPython.utils.generics import result_display
53 53 from IPython.core import ipapi
54 54 from IPython.external.path import path
55 55 if os.name == "nt":
56 56 from IPython.utils.winconsole import get_console_size
57 57
58 58 try:
59 59 set
60 60 except:
61 61 from sets import Set as set
62 62
63 63
64 64 #****************************************************************************
65 65 # Exceptions
66 66 class Error(Exception):
67 67 """Base class for exceptions in this module."""
68 68 pass
69 69
70 70 #----------------------------------------------------------------------------
71 71 class IOStream:
72 72 def __init__(self,stream,fallback):
73 73 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
74 74 stream = fallback
75 75 self.stream = stream
76 76 self._swrite = stream.write
77 77 self.flush = stream.flush
78 78
79 79 def write(self,data):
80 80 try:
81 81 self._swrite(data)
82 82 except:
83 83 try:
84 84 # print handles some unicode issues which may trip a plain
85 85 # write() call. Attempt to emulate write() by using a
86 86 # trailing comma
87 87 print >> self.stream, data,
88 88 except:
89 89 # if we get here, something is seriously broken.
90 90 print >> sys.stderr, \
91 91 'ERROR - failed to write data to stream:', self.stream
92 92
93 93 def close(self):
94 94 pass
95 95
96 96
97 97 class IOTerm:
98 98 """ Term holds the file or file-like objects for handling I/O operations.
99 99
100 100 These are normally just sys.stdin, sys.stdout and sys.stderr but for
101 101 Windows they can can replaced to allow editing the strings before they are
102 102 displayed."""
103 103
104 104 # In the future, having IPython channel all its I/O operations through
105 105 # this class will make it easier to embed it into other environments which
106 106 # are not a normal terminal (such as a GUI-based shell)
107 107 def __init__(self,cin=None,cout=None,cerr=None):
108 108 self.cin = IOStream(cin,sys.stdin)
109 109 self.cout = IOStream(cout,sys.stdout)
110 110 self.cerr = IOStream(cerr,sys.stderr)
111 111
112 112 # Global variable to be used for all I/O
113 113 Term = IOTerm()
114 114
115 115 import IPython.utils.rlineimpl as readline
116 116 # Remake Term to use the readline i/o facilities
117 117 if sys.platform == 'win32' and readline.have_readline:
118 118
119 119 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
120 120
121 121
122 122 #****************************************************************************
123 123 # Generic warning/error printer, used by everything else
124 124 def warn(msg,level=2,exit_val=1):
125 125 """Standard warning printer. Gives formatting consistency.
126 126
127 127 Output is sent to Term.cerr (sys.stderr by default).
128 128
129 129 Options:
130 130
131 131 -level(2): allows finer control:
132 132 0 -> Do nothing, dummy function.
133 133 1 -> Print message.
134 134 2 -> Print 'WARNING:' + message. (Default level).
135 135 3 -> Print 'ERROR:' + message.
136 136 4 -> Print 'FATAL ERROR:' + message and trigger a sys.exit(exit_val).
137 137
138 138 -exit_val (1): exit value returned by sys.exit() for a level 4
139 139 warning. Ignored for all other levels."""
140 140
141 141 if level>0:
142 142 header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
143 143 print >> Term.cerr, '%s%s' % (header[level],msg)
144 144 if level == 4:
145 145 print >> Term.cerr,'Exiting.\n'
146 146 sys.exit(exit_val)
147 147
148 148 def info(msg):
149 149 """Equivalent to warn(msg,level=1)."""
150 150
151 151 warn(msg,level=1)
152 152
153 153 def error(msg):
154 154 """Equivalent to warn(msg,level=3)."""
155 155
156 156 warn(msg,level=3)
157 157
158 158 def fatal(msg,exit_val=1):
159 159 """Equivalent to warn(msg,exit_val=exit_val,level=4)."""
160 160
161 161 warn(msg,exit_val=exit_val,level=4)
162 162
163 163 #---------------------------------------------------------------------------
164 164 # Debugging routines
165 165 #
166 166 def debugx(expr,pre_msg=''):
167 167 """Print the value of an expression from the caller's frame.
168 168
169 169 Takes an expression, evaluates it in the caller's frame and prints both
170 170 the given expression and the resulting value (as well as a debug mark
171 171 indicating the name of the calling function. The input must be of a form
172 172 suitable for eval().
173 173
174 174 An optional message can be passed, which will be prepended to the printed
175 175 expr->value pair."""
176 176
177 177 cf = sys._getframe(1)
178 178 print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
179 179 eval(expr,cf.f_globals,cf.f_locals))
180 180
181 181 # deactivate it by uncommenting the following line, which makes it a no-op
182 182 #def debugx(expr,pre_msg=''): pass
183 183
184 184 #----------------------------------------------------------------------------
185 185 StringTypes = types.StringTypes
186 186
187 187 # Basic timing functionality
188 188
189 189 # If possible (Unix), use the resource module instead of time.clock()
190 190 try:
191 191 import resource
192 192 def clocku():
193 193 """clocku() -> floating point number
194 194
195 195 Return the *USER* CPU time in seconds since the start of the process.
196 196 This is done via a call to resource.getrusage, so it avoids the
197 197 wraparound problems in time.clock()."""
198 198
199 199 return resource.getrusage(resource.RUSAGE_SELF)[0]
200 200
201 201 def clocks():
202 202 """clocks() -> floating point number
203 203
204 204 Return the *SYSTEM* CPU time in seconds since the start of the process.
205 205 This is done via a call to resource.getrusage, so it avoids the
206 206 wraparound problems in time.clock()."""
207 207
208 208 return resource.getrusage(resource.RUSAGE_SELF)[1]
209 209
210 210 def clock():
211 211 """clock() -> floating point number
212 212
213 213 Return the *TOTAL USER+SYSTEM* CPU time in seconds since the start of
214 214 the process. This is done via a call to resource.getrusage, so it
215 215 avoids the wraparound problems in time.clock()."""
216 216
217 217 u,s = resource.getrusage(resource.RUSAGE_SELF)[:2]
218 218 return u+s
219 219
220 220 def clock2():
221 221 """clock2() -> (t_user,t_system)
222 222
223 223 Similar to clock(), but return a tuple of user/system times."""
224 224 return resource.getrusage(resource.RUSAGE_SELF)[:2]
225 225
226 226 except ImportError:
227 227 # There is no distinction of user/system time under windows, so we just use
228 228 # time.clock() for everything...
229 229 clocku = clocks = clock = time.clock
230 230 def clock2():
231 231 """Under windows, system CPU time can't be measured.
232 232
233 233 This just returns clock() and zero."""
234 234 return time.clock(),0.0
235 235
236 236 def timings_out(reps,func,*args,**kw):
237 237 """timings_out(reps,func,*args,**kw) -> (t_total,t_per_call,output)
238 238
239 239 Execute a function reps times, return a tuple with the elapsed total
240 240 CPU time in seconds, the time per call and the function's output.
241 241
242 242 Under Unix, the return value is the sum of user+system time consumed by
243 243 the process, computed via the resource module. This prevents problems
244 244 related to the wraparound effect which the time.clock() function has.
245 245
246 246 Under Windows the return value is in wall clock seconds. See the
247 247 documentation for the time module for more details."""
248 248
249 249 reps = int(reps)
250 250 assert reps >=1, 'reps must be >= 1'
251 251 if reps==1:
252 252 start = clock()
253 253 out = func(*args,**kw)
254 254 tot_time = clock()-start
255 255 else:
256 256 rng = xrange(reps-1) # the last time is executed separately to store output
257 257 start = clock()
258 258 for dummy in rng: func(*args,**kw)
259 259 out = func(*args,**kw) # one last time
260 260 tot_time = clock()-start
261 261 av_time = tot_time / reps
262 262 return tot_time,av_time,out
263 263
264 264 def timings(reps,func,*args,**kw):
265 265 """timings(reps,func,*args,**kw) -> (t_total,t_per_call)
266 266
267 267 Execute a function reps times, return a tuple with the elapsed total CPU
268 268 time in seconds and the time per call. These are just the first two values
269 269 in timings_out()."""
270 270
271 271 return timings_out(reps,func,*args,**kw)[0:2]
272 272
273 273 def timing(func,*args,**kw):
274 274 """timing(func,*args,**kw) -> t_total
275 275
276 276 Execute a function once, return the elapsed total CPU time in
277 277 seconds. This is just the first value in timings_out()."""
278 278
279 279 return timings_out(1,func,*args,**kw)[0]
280 280
281 281 #****************************************************************************
282 282 # file and system
283 283
284 284 def arg_split(s,posix=False):
285 285 """Split a command line's arguments in a shell-like manner.
286 286
287 287 This is a modified version of the standard library's shlex.split()
288 288 function, but with a default of posix=False for splitting, so that quotes
289 289 in inputs are respected."""
290 290
291 291 # XXX - there may be unicode-related problems here!!! I'm not sure that
292 292 # shlex is truly unicode-safe, so it might be necessary to do
293 293 #
294 294 # s = s.encode(sys.stdin.encoding)
295 295 #
296 296 # first, to ensure that shlex gets a normal string. Input from anyone who
297 297 # knows more about unicode and shlex than I would be good to have here...
298 298 lex = shlex.shlex(s, posix=posix)
299 299 lex.whitespace_split = True
300 300 return list(lex)
301 301
302 302 def system(cmd,verbose=0,debug=0,header=''):
303 303 """Execute a system command, return its exit status.
304 304
305 305 Options:
306 306
307 307 - verbose (0): print the command to be executed.
308 308
309 309 - debug (0): only print, do not actually execute.
310 310
311 311 - header (''): Header to print on screen prior to the executed command (it
312 312 is only prepended to the command, no newlines are added).
313 313
314 314 Note: a stateful version of this function is available through the
315 315 SystemExec class."""
316 316
317 317 stat = 0
318 318 if verbose or debug: print header+cmd
319 319 sys.stdout.flush()
320 320 if not debug: stat = os.system(cmd)
321 321 return stat
322 322
323 323 def abbrev_cwd():
324 324 """ Return abbreviated version of cwd, e.g. d:mydir """
325 325 cwd = os.getcwd().replace('\\','/')
326 326 drivepart = ''
327 327 tail = cwd
328 328 if sys.platform == 'win32':
329 329 if len(cwd) < 4:
330 330 return cwd
331 331 drivepart,tail = os.path.splitdrive(cwd)
332 332
333 333
334 334 parts = tail.split('/')
335 335 if len(parts) > 2:
336 336 tail = '/'.join(parts[-2:])
337 337
338 338 return (drivepart + (
339 339 cwd == '/' and '/' or tail))
340 340
341 341
342 342 # This function is used by ipython in a lot of places to make system calls.
343 343 # We need it to be slightly different under win32, due to the vagaries of
344 344 # 'network shares'. A win32 override is below.
345 345
346 346 def shell(cmd,verbose=0,debug=0,header=''):
347 347 """Execute a command in the system shell, always return None.
348 348
349 349 Options:
350 350
351 351 - verbose (0): print the command to be executed.
352 352
353 353 - debug (0): only print, do not actually execute.
354 354
355 355 - header (''): Header to print on screen prior to the executed command (it
356 356 is only prepended to the command, no newlines are added).
357 357
358 358 Note: this is similar to genutils.system(), but it returns None so it can
359 359 be conveniently used in interactive loops without getting the return value
360 360 (typically 0) printed many times."""
361 361
362 362 stat = 0
363 363 if verbose or debug: print header+cmd
364 364 # flush stdout so we don't mangle python's buffering
365 365 sys.stdout.flush()
366 366
367 367 if not debug:
368 368 platutils.set_term_title("IPy " + cmd)
369 369 os.system(cmd)
370 370 platutils.set_term_title("IPy " + abbrev_cwd())
371 371
372 372 # override shell() for win32 to deal with network shares
373 373 if os.name in ('nt','dos'):
374 374
375 375 shell_ori = shell
376 376
377 377 def shell(cmd,verbose=0,debug=0,header=''):
378 378 if os.getcwd().startswith(r"\\"):
379 379 path = os.getcwd()
380 380 # change to c drive (cannot be on UNC-share when issuing os.system,
381 381 # as cmd.exe cannot handle UNC addresses)
382 382 os.chdir("c:")
383 383 # issue pushd to the UNC-share and then run the command
384 384 try:
385 385 shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header)
386 386 finally:
387 387 os.chdir(path)
388 388 else:
389 389 shell_ori(cmd,verbose,debug,header)
390 390
391 391 shell.__doc__ = shell_ori.__doc__
392 392
393 393 def getoutput(cmd,verbose=0,debug=0,header='',split=0):
394 394 """Dummy substitute for perl's backquotes.
395 395
396 396 Executes a command and returns the output.
397 397
398 398 Accepts the same arguments as system(), plus:
399 399
400 400 - split(0): if true, the output is returned as a list split on newlines.
401 401
402 402 Note: a stateful version of this function is available through the
403 403 SystemExec class.
404 404
405 405 This is pretty much deprecated and rarely used,
406 406 genutils.getoutputerror may be what you need.
407 407
408 408 """
409 409
410 410 if verbose or debug: print header+cmd
411 411 if not debug:
412 412 output = os.popen(cmd).read()
413 413 # stipping last \n is here for backwards compat.
414 414 if output.endswith('\n'):
415 415 output = output[:-1]
416 416 if split:
417 417 return output.split('\n')
418 418 else:
419 419 return output
420 420
421 421 def getoutputerror(cmd,verbose=0,debug=0,header='',split=0):
422 422 """Return (standard output,standard error) of executing cmd in a shell.
423 423
424 424 Accepts the same arguments as system(), plus:
425 425
426 426 - split(0): if true, each of stdout/err is returned as a list split on
427 427 newlines.
428 428
429 429 Note: a stateful version of this function is available through the
430 430 SystemExec class."""
431 431
432 432 if verbose or debug: print header+cmd
433 433 if not cmd:
434 434 if split:
435 435 return [],[]
436 436 else:
437 437 return '',''
438 438 if not debug:
439 439 pin,pout,perr = os.popen3(cmd)
440 440 tout = pout.read().rstrip()
441 441 terr = perr.read().rstrip()
442 442 pin.close()
443 443 pout.close()
444 444 perr.close()
445 445 if split:
446 446 return tout.split('\n'),terr.split('\n')
447 447 else:
448 448 return tout,terr
449 449
450 450 # for compatibility with older naming conventions
451 451 xsys = system
452 452 bq = getoutput
453 453
454 454 class SystemExec:
455 455 """Access the system and getoutput functions through a stateful interface.
456 456
457 457 Note: here we refer to the system and getoutput functions from this
458 458 library, not the ones from the standard python library.
459 459
460 460 This class offers the system and getoutput functions as methods, but the
461 461 verbose, debug and header parameters can be set for the instance (at
462 462 creation time or later) so that they don't need to be specified on each
463 463 call.
464 464
465 465 For efficiency reasons, there's no way to override the parameters on a
466 466 per-call basis other than by setting instance attributes. If you need
467 467 local overrides, it's best to directly call system() or getoutput().
468 468
469 469 The following names are provided as alternate options:
470 470 - xsys: alias to system
471 471 - bq: alias to getoutput
472 472
473 473 An instance can then be created as:
474 474 >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ')
475 475 """
476 476
477 477 def __init__(self,verbose=0,debug=0,header='',split=0):
478 478 """Specify the instance's values for verbose, debug and header."""
479 479 setattr_list(self,'verbose debug header split')
480 480
481 481 def system(self,cmd):
482 482 """Stateful interface to system(), with the same keyword parameters."""
483 483
484 484 system(cmd,self.verbose,self.debug,self.header)
485 485
486 486 def shell(self,cmd):
487 487 """Stateful interface to shell(), with the same keyword parameters."""
488 488
489 489 shell(cmd,self.verbose,self.debug,self.header)
490 490
491 491 xsys = system # alias
492 492
493 493 def getoutput(self,cmd):
494 494 """Stateful interface to getoutput()."""
495 495
496 496 return getoutput(cmd,self.verbose,self.debug,self.header,self.split)
497 497
498 498 def getoutputerror(self,cmd):
499 499 """Stateful interface to getoutputerror()."""
500 500
501 501 return getoutputerror(cmd,self.verbose,self.debug,self.header,self.split)
502 502
503 503 bq = getoutput # alias
504 504
505 505 #-----------------------------------------------------------------------------
506 506 def mutex_opts(dict,ex_op):
507 507 """Check for presence of mutually exclusive keys in a dict.
508 508
509 509 Call: mutex_opts(dict,[[op1a,op1b],[op2a,op2b]...]"""
510 510 for op1,op2 in ex_op:
511 511 if op1 in dict and op2 in dict:
512 512 raise ValueError,'\n*** ERROR in Arguments *** '\
513 513 'Options '+op1+' and '+op2+' are mutually exclusive.'
514 514
515 515 #-----------------------------------------------------------------------------
516 516 def get_py_filename(name):
517 517 """Return a valid python filename in the current directory.
518 518
519 519 If the given name is not a file, it adds '.py' and searches again.
520 520 Raises IOError with an informative message if the file isn't found."""
521 521
522 522 name = os.path.expanduser(name)
523 523 if not os.path.isfile(name) and not name.endswith('.py'):
524 524 name += '.py'
525 525 if os.path.isfile(name):
526 526 return name
527 527 else:
528 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:
540 try:
541 alt_dirs = get_home_dir()
542 except HomeDirError:
543 alt_dirs = os.getcwd()
544 search = [fname] + list_strings(alt_dirs)
545 search = map(os.path.expanduser,search)
546 #print 'search list for',fname,'list:',search # dbg
547 fname = search[0]
548 if os.path.isfile(fname):
549 return fname
550 for direc in search[1:]:
551 testname = os.path.join(direc,fname)
552 #print 'testname',testname # dbg
536 This iterates through a sequence of paths looking for a file and returns
537 the full, absolute path of the first occurence of the file. If no set of
538 path dirs is given, the filename is tested as is, after running through
539 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
540
541 filefind('myfile.txt')
542
543 will find the file in the current working dir, but::
544
545 filefind('~/myfile.txt')
546
547 Will find the file in the users home directory. This function does not
548 automatically try any paths, such as the cwd or the user's home directory.
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 574 if os.path.isfile(testname):
554 return testname
555 raise IOError,'File' + `fname` + \
556 ' not found in current or supplied directories:' + `alt_dirs`
575 return os.path.abspath(testname)
576 raise IOError("File does not exist in any "
577 "of the search paths: %r, %r" % \
578 (filename, path_dirs))
579
557 580
558 581 #----------------------------------------------------------------------------
559 582 def file_read(filename):
560 583 """Read a file and close it. Returns the file source."""
561 584 fobj = open(filename,'r');
562 585 source = fobj.read();
563 586 fobj.close()
564 587 return source
565 588
566 589 def file_readlines(filename):
567 590 """Read a file and close it. Returns the file source using readlines()."""
568 591 fobj = open(filename,'r');
569 592 lines = fobj.readlines();
570 593 fobj.close()
571 594 return lines
572 595
573 596 #----------------------------------------------------------------------------
574 597 def target_outdated(target,deps):
575 598 """Determine whether a target is out of date.
576 599
577 600 target_outdated(target,deps) -> 1/0
578 601
579 602 deps: list of filenames which MUST exist.
580 603 target: single filename which may or may not exist.
581 604
582 605 If target doesn't exist or is older than any file listed in deps, return
583 606 true, otherwise return false.
584 607 """
585 608 try:
586 609 target_time = os.path.getmtime(target)
587 610 except os.error:
588 611 return 1
589 612 for dep in deps:
590 613 dep_time = os.path.getmtime(dep)
591 614 if dep_time > target_time:
592 615 #print "For target",target,"Dep failed:",dep # dbg
593 616 #print "times (dep,tar):",dep_time,target_time # dbg
594 617 return 1
595 618 return 0
596 619
597 620 #-----------------------------------------------------------------------------
598 621 def target_update(target,deps,cmd):
599 622 """Update a target with a given command given a list of dependencies.
600 623
601 624 target_update(target,deps,cmd) -> runs cmd if target is outdated.
602 625
603 626 This is just a wrapper around target_outdated() which calls the given
604 627 command if target is outdated."""
605 628
606 629 if target_outdated(target,deps):
607 630 xsys(cmd)
608 631
609 632 #----------------------------------------------------------------------------
610 633 def unquote_ends(istr):
611 634 """Remove a single pair of quotes from the endpoints of a string."""
612 635
613 636 if not istr:
614 637 return istr
615 638 if (istr[0]=="'" and istr[-1]=="'") or \
616 639 (istr[0]=='"' and istr[-1]=='"'):
617 640 return istr[1:-1]
618 641 else:
619 642 return istr
620 643
621 644 #----------------------------------------------------------------------------
622 645 def process_cmdline(argv,names=[],defaults={},usage=''):
623 646 """ Process command-line options and arguments.
624 647
625 648 Arguments:
626 649
627 650 - argv: list of arguments, typically sys.argv.
628 651
629 652 - names: list of option names. See DPyGetOpt docs for details on options
630 653 syntax.
631 654
632 655 - defaults: dict of default values.
633 656
634 657 - usage: optional usage notice to print if a wrong argument is passed.
635 658
636 659 Return a dict of options and a list of free arguments."""
637 660
638 661 getopt = DPyGetOpt.DPyGetOpt()
639 662 getopt.setIgnoreCase(0)
640 663 getopt.parseConfiguration(names)
641 664
642 665 try:
643 666 getopt.processArguments(argv)
644 667 except DPyGetOpt.ArgumentError, exc:
645 668 print usage
646 669 warn('"%s"' % exc,level=4)
647 670
648 671 defaults.update(getopt.optionValues)
649 672 args = getopt.freeValues
650 673
651 674 return defaults,args
652 675
653 676 #----------------------------------------------------------------------------
654 677 def optstr2types(ostr):
655 678 """Convert a string of option names to a dict of type mappings.
656 679
657 680 optstr2types(str) -> {None:'string_opts',int:'int_opts',float:'float_opts'}
658 681
659 682 This is used to get the types of all the options in a string formatted
660 683 with the conventions of DPyGetOpt. The 'type' None is used for options
661 684 which are strings (they need no further conversion). This function's main
662 685 use is to get a typemap for use with read_dict().
663 686 """
664 687
665 688 typeconv = {None:'',int:'',float:''}
666 689 typemap = {'s':None,'i':int,'f':float}
667 690 opt_re = re.compile(r'([\w]*)([^:=]*:?=?)([sif]?)')
668 691
669 692 for w in ostr.split():
670 693 oname,alias,otype = opt_re.match(w).groups()
671 694 if otype == '' or alias == '!': # simple switches are integers too
672 695 otype = 'i'
673 696 typeconv[typemap[otype]] += oname + ' '
674 697 return typeconv
675 698
676 699 #----------------------------------------------------------------------------
677 700 def read_dict(filename,type_conv=None,**opt):
678 701 r"""Read a dictionary of key=value pairs from an input file, optionally
679 702 performing conversions on the resulting values.
680 703
681 704 read_dict(filename,type_conv,**opt) -> dict
682 705
683 706 Only one value per line is accepted, the format should be
684 707 # optional comments are ignored
685 708 key value\n
686 709
687 710 Args:
688 711
689 712 - type_conv: A dictionary specifying which keys need to be converted to
690 713 which types. By default all keys are read as strings. This dictionary
691 714 should have as its keys valid conversion functions for strings
692 715 (int,long,float,complex, or your own). The value for each key
693 716 (converter) should be a whitespace separated string containing the names
694 717 of all the entries in the file to be converted using that function. For
695 718 keys to be left alone, use None as the conversion function (only needed
696 719 with purge=1, see below).
697 720
698 721 - opt: dictionary with extra options as below (default in parens)
699 722
700 723 purge(0): if set to 1, all keys *not* listed in type_conv are purged out
701 724 of the dictionary to be returned. If purge is going to be used, the
702 725 set of keys to be left as strings also has to be explicitly specified
703 726 using the (non-existent) conversion function None.
704 727
705 728 fs(None): field separator. This is the key/value separator to be used
706 729 when parsing the file. The None default means any whitespace [behavior
707 730 of string.split()].
708 731
709 732 strip(0): if 1, strip string values of leading/trailinig whitespace.
710 733
711 734 warn(1): warning level if requested keys are not found in file.
712 735 - 0: silently ignore.
713 736 - 1: inform but proceed.
714 737 - 2: raise KeyError exception.
715 738
716 739 no_empty(0): if 1, remove keys with whitespace strings as a value.
717 740
718 741 unique([]): list of keys (or space separated string) which can't be
719 742 repeated. If one such key is found in the file, each new instance
720 743 overwrites the previous one. For keys not listed here, the behavior is
721 744 to make a list of all appearances.
722 745
723 746 Example:
724 747
725 748 If the input file test.ini contains (we put it in a string to keep the test
726 749 self-contained):
727 750
728 751 >>> test_ini = '''\
729 752 ... i 3
730 753 ... x 4.5
731 754 ... y 5.5
732 755 ... s hi ho'''
733 756
734 757 Then we can use it as follows:
735 758 >>> type_conv={int:'i',float:'x',None:'s'}
736 759
737 760 >>> d = read_dict(test_ini)
738 761
739 762 >>> sorted(d.items())
740 763 [('i', '3'), ('s', 'hi ho'), ('x', '4.5'), ('y', '5.5')]
741 764
742 765 >>> d = read_dict(test_ini,type_conv)
743 766
744 767 >>> sorted(d.items())
745 768 [('i', 3), ('s', 'hi ho'), ('x', 4.5), ('y', '5.5')]
746 769
747 770 >>> d = read_dict(test_ini,type_conv,purge=True)
748 771
749 772 >>> sorted(d.items())
750 773 [('i', 3), ('s', 'hi ho'), ('x', 4.5)]
751 774 """
752 775
753 776 # starting config
754 777 opt.setdefault('purge',0)
755 778 opt.setdefault('fs',None) # field sep defaults to any whitespace
756 779 opt.setdefault('strip',0)
757 780 opt.setdefault('warn',1)
758 781 opt.setdefault('no_empty',0)
759 782 opt.setdefault('unique','')
760 783 if type(opt['unique']) in StringTypes:
761 784 unique_keys = qw(opt['unique'])
762 785 elif type(opt['unique']) in (types.TupleType,types.ListType):
763 786 unique_keys = opt['unique']
764 787 else:
765 788 raise ValueError, 'Unique keys must be given as a string, List or Tuple'
766 789
767 790 dict = {}
768 791
769 792 # first read in table of values as strings
770 793 if '\n' in filename:
771 794 lines = filename.splitlines()
772 795 file = None
773 796 else:
774 797 file = open(filename,'r')
775 798 lines = file.readlines()
776 799 for line in lines:
777 800 line = line.strip()
778 801 if len(line) and line[0]=='#': continue
779 802 if len(line)>0:
780 803 lsplit = line.split(opt['fs'],1)
781 804 try:
782 805 key,val = lsplit
783 806 except ValueError:
784 807 key,val = lsplit[0],''
785 808 key = key.strip()
786 809 if opt['strip']: val = val.strip()
787 810 if val == "''" or val == '""': val = ''
788 811 if opt['no_empty'] and (val=='' or val.isspace()):
789 812 continue
790 813 # if a key is found more than once in the file, build a list
791 814 # unless it's in the 'unique' list. In that case, last found in file
792 815 # takes precedence. User beware.
793 816 try:
794 817 if dict[key] and key in unique_keys:
795 818 dict[key] = val
796 819 elif type(dict[key]) is types.ListType:
797 820 dict[key].append(val)
798 821 else:
799 822 dict[key] = [dict[key],val]
800 823 except KeyError:
801 824 dict[key] = val
802 825 # purge if requested
803 826 if opt['purge']:
804 827 accepted_keys = qwflat(type_conv.values())
805 828 for key in dict.keys():
806 829 if key in accepted_keys: continue
807 830 del(dict[key])
808 831 # now convert if requested
809 832 if type_conv==None: return dict
810 833 conversions = type_conv.keys()
811 834 try: conversions.remove(None)
812 835 except: pass
813 836 for convert in conversions:
814 837 for val in qw(type_conv[convert]):
815 838 try:
816 839 dict[val] = convert(dict[val])
817 840 except KeyError,e:
818 841 if opt['warn'] == 0:
819 842 pass
820 843 elif opt['warn'] == 1:
821 844 print >>sys.stderr, 'Warning: key',val,\
822 845 'not found in file',filename
823 846 elif opt['warn'] == 2:
824 847 raise KeyError,e
825 848 else:
826 849 raise ValueError,'Warning level must be 0,1 or 2'
827 850
828 851 return dict
829 852
830 853 #----------------------------------------------------------------------------
831 854 def flag_calls(func):
832 855 """Wrap a function to detect and flag when it gets called.
833 856
834 857 This is a decorator which takes a function and wraps it in a function with
835 858 a 'called' attribute. wrapper.called is initialized to False.
836 859
837 860 The wrapper.called attribute is set to False right before each call to the
838 861 wrapped function, so if the call fails it remains False. After the call
839 862 completes, wrapper.called is set to True and the output is returned.
840 863
841 864 Testing for truth in wrapper.called allows you to determine if a call to
842 865 func() was attempted and succeeded."""
843 866
844 867 def wrapper(*args,**kw):
845 868 wrapper.called = False
846 869 out = func(*args,**kw)
847 870 wrapper.called = True
848 871 return out
849 872
850 873 wrapper.called = False
851 874 wrapper.__doc__ = func.__doc__
852 875 return wrapper
853 876
854 877 #----------------------------------------------------------------------------
855 878 def dhook_wrap(func,*a,**k):
856 879 """Wrap a function call in a sys.displayhook controller.
857 880
858 881 Returns a wrapper around func which calls func, with all its arguments and
859 882 keywords unmodified, using the default sys.displayhook. Since IPython
860 883 modifies sys.displayhook, it breaks the behavior of certain systems that
861 884 rely on the default behavior, notably doctest.
862 885 """
863 886
864 887 def f(*a,**k):
865 888
866 889 dhook_s = sys.displayhook
867 890 sys.displayhook = sys.__displayhook__
868 891 try:
869 892 out = func(*a,**k)
870 893 finally:
871 894 sys.displayhook = dhook_s
872 895
873 896 return out
874 897
875 898 f.__doc__ = func.__doc__
876 899 return f
877 900
878 901 #----------------------------------------------------------------------------
879 902 def doctest_reload():
880 903 """Properly reload doctest to reuse it interactively.
881 904
882 905 This routine:
883 906
884 907 - imports doctest but does NOT reload it (see below).
885 908
886 909 - resets its global 'master' attribute to None, so that multiple uses of
887 910 the module interactively don't produce cumulative reports.
888 911
889 912 - Monkeypatches its core test runner method to protect it from IPython's
890 913 modified displayhook. Doctest expects the default displayhook behavior
891 914 deep down, so our modification breaks it completely. For this reason, a
892 915 hard monkeypatch seems like a reasonable solution rather than asking
893 916 users to manually use a different doctest runner when under IPython.
894 917
895 918 Notes
896 919 -----
897 920
898 921 This function *used to* reload doctest, but this has been disabled because
899 922 reloading doctest unconditionally can cause massive breakage of other
900 923 doctest-dependent modules already in memory, such as those for IPython's
901 924 own testing system. The name wasn't changed to avoid breaking people's
902 925 code, but the reload call isn't actually made anymore."""
903 926
904 927 import doctest
905 928 doctest.master = None
906 929 doctest.DocTestRunner.run = dhook_wrap(doctest.DocTestRunner.run)
907 930
908 931 #----------------------------------------------------------------------------
909 932 class HomeDirError(Error):
910 933 pass
911 934
912 935 def get_home_dir():
913 936 """Return the closest possible equivalent to a 'home' directory.
914 937
915 938 We first try $HOME. Absent that, on NT it's $HOMEDRIVE\$HOMEPATH.
916 939
917 940 Currently only Posix and NT are implemented, a HomeDirError exception is
918 941 raised for all other OSes. """
919 942
920 943 isdir = os.path.isdir
921 944 env = os.environ
922 945
923 946 # first, check py2exe distribution root directory for _ipython.
924 947 # This overrides all. Normally does not exist.
925 948
926 949 if hasattr(sys, "frozen"): #Is frozen by py2exe
927 950 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
928 951 root, rest = IPython.__file__.lower().split('library.zip')
929 952 else:
930 953 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
931 954 root=os.path.abspath(root).rstrip('\\')
932 955 if isdir(os.path.join(root, '_ipython')):
933 956 os.environ["IPYKITROOT"] = root
934 957 return root
935 958 try:
936 959 homedir = env['HOME']
937 960 if not isdir(homedir):
938 961 # in case a user stuck some string which does NOT resolve to a
939 962 # valid path, it's as good as if we hadn't foud it
940 963 raise KeyError
941 964 return homedir
942 965 except KeyError:
943 966 if os.name == 'posix':
944 967 raise HomeDirError,'undefined $HOME, IPython can not proceed.'
945 968 elif os.name == 'nt':
946 969 # For some strange reason, win9x returns 'nt' for os.name.
947 970 try:
948 971 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
949 972 if not isdir(homedir):
950 973 homedir = os.path.join(env['USERPROFILE'])
951 974 if not isdir(homedir):
952 975 raise HomeDirError
953 976 return homedir
954 977 except KeyError:
955 978 try:
956 979 # Use the registry to get the 'My Documents' folder.
957 980 import _winreg as wreg
958 981 key = wreg.OpenKey(wreg.HKEY_CURRENT_USER,
959 982 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
960 983 homedir = wreg.QueryValueEx(key,'Personal')[0]
961 984 key.Close()
962 985 if not isdir(homedir):
963 986 e = ('Invalid "Personal" folder registry key '
964 987 'typically "My Documents".\n'
965 988 'Value: %s\n'
966 989 'This is not a valid directory on your system.' %
967 990 homedir)
968 991 raise HomeDirError(e)
969 992 return homedir
970 993 except HomeDirError:
971 994 raise
972 995 except:
973 996 return 'C:\\'
974 997 elif os.name == 'dos':
975 998 # Desperate, may do absurd things in classic MacOS. May work under DOS.
976 999 return 'C:\\'
977 1000 else:
978 1001 raise HomeDirError,'support for your operating system not implemented.'
979 1002
980 1003
981 1004 def get_ipython_dir():
982 1005 """Get the IPython directory for this platform and user.
983 1006
984 1007 This uses the logic in `get_home_dir` to find the home directory
985 1008 and the adds either .ipython or _ipython to the end of the path.
986 1009 """
987 1010 if os.name == 'posix':
988 1011 ipdir_def = '.ipython'
989 1012 else:
990 1013 ipdir_def = '_ipython'
991 1014 home_dir = get_home_dir()
992 1015 ipdir = os.path.abspath(os.environ.get('IPYTHONDIR',
993 1016 os.path.join(home_dir, ipdir_def)))
994 1017 return ipdir.decode(sys.getfilesystemencoding())
995 1018
996 1019 def get_security_dir():
997 1020 """Get the IPython security directory.
998 1021
999 1022 This directory is the default location for all security related files,
1000 1023 including SSL/TLS certificates and FURL files.
1001 1024
1002 1025 If the directory does not exist, it is created with 0700 permissions.
1003 1026 If it exists, permissions are set to 0700.
1004 1027 """
1005 1028 security_dir = os.path.join(get_ipython_dir(), 'security')
1006 1029 if not os.path.isdir(security_dir):
1007 1030 os.mkdir(security_dir, 0700)
1008 1031 else:
1009 1032 os.chmod(security_dir, 0700)
1010 1033 return security_dir
1011 1034
1012 1035 def get_log_dir():
1013 1036 """Get the IPython log directory.
1014 1037
1015 1038 If the log directory does not exist, it is created.
1016 1039 """
1017 1040 log_dir = os.path.join(get_ipython_dir(), 'log')
1018 1041 if not os.path.isdir(log_dir):
1019 1042 os.mkdir(log_dir, 0777)
1020 1043 return log_dir
1021 1044
1022 1045 #****************************************************************************
1023 1046 # strings and text
1024 1047
1025 1048 class LSString(str):
1026 1049 """String derivative with a special access attributes.
1027 1050
1028 1051 These are normal strings, but with the special attributes:
1029 1052
1030 1053 .l (or .list) : value as list (split on newlines).
1031 1054 .n (or .nlstr): original value (the string itself).
1032 1055 .s (or .spstr): value as whitespace-separated string.
1033 1056 .p (or .paths): list of path objects
1034 1057
1035 1058 Any values which require transformations are computed only once and
1036 1059 cached.
1037 1060
1038 1061 Such strings are very useful to efficiently interact with the shell, which
1039 1062 typically only understands whitespace-separated options for commands."""
1040 1063
1041 1064 def get_list(self):
1042 1065 try:
1043 1066 return self.__list
1044 1067 except AttributeError:
1045 1068 self.__list = self.split('\n')
1046 1069 return self.__list
1047 1070
1048 1071 l = list = property(get_list)
1049 1072
1050 1073 def get_spstr(self):
1051 1074 try:
1052 1075 return self.__spstr
1053 1076 except AttributeError:
1054 1077 self.__spstr = self.replace('\n',' ')
1055 1078 return self.__spstr
1056 1079
1057 1080 s = spstr = property(get_spstr)
1058 1081
1059 1082 def get_nlstr(self):
1060 1083 return self
1061 1084
1062 1085 n = nlstr = property(get_nlstr)
1063 1086
1064 1087 def get_paths(self):
1065 1088 try:
1066 1089 return self.__paths
1067 1090 except AttributeError:
1068 1091 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
1069 1092 return self.__paths
1070 1093
1071 1094 p = paths = property(get_paths)
1072 1095
1073 1096 def print_lsstring(arg):
1074 1097 """ Prettier (non-repr-like) and more informative printer for LSString """
1075 1098 print "LSString (.p, .n, .l, .s available). Value:"
1076 1099 print arg
1077 1100
1078 1101 print_lsstring = result_display.when_type(LSString)(print_lsstring)
1079 1102
1080 1103 #----------------------------------------------------------------------------
1081 1104 class SList(list):
1082 1105 """List derivative with a special access attributes.
1083 1106
1084 1107 These are normal lists, but with the special attributes:
1085 1108
1086 1109 .l (or .list) : value as list (the list itself).
1087 1110 .n (or .nlstr): value as a string, joined on newlines.
1088 1111 .s (or .spstr): value as a string, joined on spaces.
1089 1112 .p (or .paths): list of path objects
1090 1113
1091 1114 Any values which require transformations are computed only once and
1092 1115 cached."""
1093 1116
1094 1117 def get_list(self):
1095 1118 return self
1096 1119
1097 1120 l = list = property(get_list)
1098 1121
1099 1122 def get_spstr(self):
1100 1123 try:
1101 1124 return self.__spstr
1102 1125 except AttributeError:
1103 1126 self.__spstr = ' '.join(self)
1104 1127 return self.__spstr
1105 1128
1106 1129 s = spstr = property(get_spstr)
1107 1130
1108 1131 def get_nlstr(self):
1109 1132 try:
1110 1133 return self.__nlstr
1111 1134 except AttributeError:
1112 1135 self.__nlstr = '\n'.join(self)
1113 1136 return self.__nlstr
1114 1137
1115 1138 n = nlstr = property(get_nlstr)
1116 1139
1117 1140 def get_paths(self):
1118 1141 try:
1119 1142 return self.__paths
1120 1143 except AttributeError:
1121 1144 self.__paths = [path(p) for p in self if os.path.exists(p)]
1122 1145 return self.__paths
1123 1146
1124 1147 p = paths = property(get_paths)
1125 1148
1126 1149 def grep(self, pattern, prune = False, field = None):
1127 1150 """ Return all strings matching 'pattern' (a regex or callable)
1128 1151
1129 1152 This is case-insensitive. If prune is true, return all items
1130 1153 NOT matching the pattern.
1131 1154
1132 1155 If field is specified, the match must occur in the specified
1133 1156 whitespace-separated field.
1134 1157
1135 1158 Examples::
1136 1159
1137 1160 a.grep( lambda x: x.startswith('C') )
1138 1161 a.grep('Cha.*log', prune=1)
1139 1162 a.grep('chm', field=-1)
1140 1163 """
1141 1164
1142 1165 def match_target(s):
1143 1166 if field is None:
1144 1167 return s
1145 1168 parts = s.split()
1146 1169 try:
1147 1170 tgt = parts[field]
1148 1171 return tgt
1149 1172 except IndexError:
1150 1173 return ""
1151 1174
1152 1175 if isinstance(pattern, basestring):
1153 1176 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
1154 1177 else:
1155 1178 pred = pattern
1156 1179 if not prune:
1157 1180 return SList([el for el in self if pred(match_target(el))])
1158 1181 else:
1159 1182 return SList([el for el in self if not pred(match_target(el))])
1160 1183 def fields(self, *fields):
1161 1184 """ Collect whitespace-separated fields from string list
1162 1185
1163 1186 Allows quick awk-like usage of string lists.
1164 1187
1165 1188 Example data (in var a, created by 'a = !ls -l')::
1166 1189 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
1167 1190 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
1168 1191
1169 1192 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
1170 1193 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
1171 1194 (note the joining by space).
1172 1195 a.fields(-1) is ['ChangeLog', 'IPython']
1173 1196
1174 1197 IndexErrors are ignored.
1175 1198
1176 1199 Without args, fields() just split()'s the strings.
1177 1200 """
1178 1201 if len(fields) == 0:
1179 1202 return [el.split() for el in self]
1180 1203
1181 1204 res = SList()
1182 1205 for el in [f.split() for f in self]:
1183 1206 lineparts = []
1184 1207
1185 1208 for fd in fields:
1186 1209 try:
1187 1210 lineparts.append(el[fd])
1188 1211 except IndexError:
1189 1212 pass
1190 1213 if lineparts:
1191 1214 res.append(" ".join(lineparts))
1192 1215
1193 1216 return res
1194 1217 def sort(self,field= None, nums = False):
1195 1218 """ sort by specified fields (see fields())
1196 1219
1197 1220 Example::
1198 1221 a.sort(1, nums = True)
1199 1222
1200 1223 Sorts a by second field, in numerical order (so that 21 > 3)
1201 1224
1202 1225 """
1203 1226
1204 1227 #decorate, sort, undecorate
1205 1228 if field is not None:
1206 1229 dsu = [[SList([line]).fields(field), line] for line in self]
1207 1230 else:
1208 1231 dsu = [[line, line] for line in self]
1209 1232 if nums:
1210 1233 for i in range(len(dsu)):
1211 1234 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
1212 1235 try:
1213 1236 n = int(numstr)
1214 1237 except ValueError:
1215 1238 n = 0;
1216 1239 dsu[i][0] = n
1217 1240
1218 1241
1219 1242 dsu.sort()
1220 1243 return SList([t[1] for t in dsu])
1221 1244
1222 1245 def print_slist(arg):
1223 1246 """ Prettier (non-repr-like) and more informative printer for SList """
1224 1247 print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
1225 1248 if hasattr(arg, 'hideonce') and arg.hideonce:
1226 1249 arg.hideonce = False
1227 1250 return
1228 1251
1229 1252 nlprint(arg)
1230 1253
1231 1254 print_slist = result_display.when_type(SList)(print_slist)
1232 1255
1233 1256
1234 1257
1235 1258 #----------------------------------------------------------------------------
1236 1259 def esc_quotes(strng):
1237 1260 """Return the input string with single and double quotes escaped out"""
1238 1261
1239 1262 return strng.replace('"','\\"').replace("'","\\'")
1240 1263
1241 1264 #----------------------------------------------------------------------------
1242 1265 def make_quoted_expr(s):
1243 1266 """Return string s in appropriate quotes, using raw string if possible.
1244 1267
1245 1268 XXX - example removed because it caused encoding errors in documentation
1246 1269 generation. We need a new example that doesn't contain invalid chars.
1247 1270
1248 1271 Note the use of raw string and padding at the end to allow trailing
1249 1272 backslash.
1250 1273 """
1251 1274
1252 1275 tail = ''
1253 1276 tailpadding = ''
1254 1277 raw = ''
1255 1278 if "\\" in s:
1256 1279 raw = 'r'
1257 1280 if s.endswith('\\'):
1258 1281 tail = '[:-1]'
1259 1282 tailpadding = '_'
1260 1283 if '"' not in s:
1261 1284 quote = '"'
1262 1285 elif "'" not in s:
1263 1286 quote = "'"
1264 1287 elif '"""' not in s and not s.endswith('"'):
1265 1288 quote = '"""'
1266 1289 elif "'''" not in s and not s.endswith("'"):
1267 1290 quote = "'''"
1268 1291 else:
1269 1292 # give up, backslash-escaped string will do
1270 1293 return '"%s"' % esc_quotes(s)
1271 1294 res = raw + quote + s + tailpadding + quote + tail
1272 1295 return res
1273 1296
1274 1297
1275 1298 #----------------------------------------------------------------------------
1276 1299 def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):
1277 1300 """Take multiple lines of input.
1278 1301
1279 1302 A list with each line of input as a separate element is returned when a
1280 1303 termination string is entered (defaults to a single '.'). Input can also
1281 1304 terminate via EOF (^D in Unix, ^Z-RET in Windows).
1282 1305
1283 1306 Lines of input which end in \\ are joined into single entries (and a
1284 1307 secondary continuation prompt is issued as long as the user terminates
1285 1308 lines with \\). This allows entering very long strings which are still
1286 1309 meant to be treated as single entities.
1287 1310 """
1288 1311
1289 1312 try:
1290 1313 if header:
1291 1314 header += '\n'
1292 1315 lines = [raw_input(header + ps1)]
1293 1316 except EOFError:
1294 1317 return []
1295 1318 terminate = [terminate_str]
1296 1319 try:
1297 1320 while lines[-1:] != terminate:
1298 1321 new_line = raw_input(ps1)
1299 1322 while new_line.endswith('\\'):
1300 1323 new_line = new_line[:-1] + raw_input(ps2)
1301 1324 lines.append(new_line)
1302 1325
1303 1326 return lines[:-1] # don't return the termination command
1304 1327 except EOFError:
1305 1328 print
1306 1329 return lines
1307 1330
1308 1331 #----------------------------------------------------------------------------
1309 1332 def raw_input_ext(prompt='', ps2='... '):
1310 1333 """Similar to raw_input(), but accepts extended lines if input ends with \\."""
1311 1334
1312 1335 line = raw_input(prompt)
1313 1336 while line.endswith('\\'):
1314 1337 line = line[:-1] + raw_input(ps2)
1315 1338 return line
1316 1339
1317 1340 #----------------------------------------------------------------------------
1318 1341 def ask_yes_no(prompt,default=None):
1319 1342 """Asks a question and returns a boolean (y/n) answer.
1320 1343
1321 1344 If default is given (one of 'y','n'), it is used if the user input is
1322 1345 empty. Otherwise the question is repeated until an answer is given.
1323 1346
1324 1347 An EOF is treated as the default answer. If there is no default, an
1325 1348 exception is raised to prevent infinite loops.
1326 1349
1327 1350 Valid answers are: y/yes/n/no (match is not case sensitive)."""
1328 1351
1329 1352 answers = {'y':True,'n':False,'yes':True,'no':False}
1330 1353 ans = None
1331 1354 while ans not in answers.keys():
1332 1355 try:
1333 1356 ans = raw_input(prompt+' ').lower()
1334 1357 if not ans: # response was an empty string
1335 1358 ans = default
1336 1359 except KeyboardInterrupt:
1337 1360 pass
1338 1361 except EOFError:
1339 1362 if default in answers.keys():
1340 1363 ans = default
1341 1364 print
1342 1365 else:
1343 1366 raise
1344 1367
1345 1368 return answers[ans]
1346 1369
1347 1370 #----------------------------------------------------------------------------
1348 1371 def marquee(txt='',width=78,mark='*'):
1349 1372 """Return the input string centered in a 'marquee'."""
1350 1373 if not txt:
1351 1374 return (mark*width)[:width]
1352 1375 nmark = (width-len(txt)-2)/len(mark)/2
1353 1376 if nmark < 0: nmark =0
1354 1377 marks = mark*nmark
1355 1378 return '%s %s %s' % (marks,txt,marks)
1356 1379
1357 1380 #----------------------------------------------------------------------------
1358 1381 class EvalDict:
1359 1382 """
1360 1383 Emulate a dict which evaluates its contents in the caller's frame.
1361 1384
1362 1385 Usage:
1363 1386 >>> number = 19
1364 1387
1365 1388 >>> text = "python"
1366 1389
1367 1390 >>> print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
1368 1391 Python 2.1 rules!
1369 1392 """
1370 1393
1371 1394 # This version is due to sismex01@hebmex.com on c.l.py, and is basically a
1372 1395 # modified (shorter) version of:
1373 1396 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66018 by
1374 1397 # Skip Montanaro (skip@pobox.com).
1375 1398
1376 1399 def __getitem__(self, name):
1377 1400 frame = sys._getframe(1)
1378 1401 return eval(name, frame.f_globals, frame.f_locals)
1379 1402
1380 1403 EvalString = EvalDict # for backwards compatibility
1381 1404 #----------------------------------------------------------------------------
1382 1405 def qw(words,flat=0,sep=None,maxsplit=-1):
1383 1406 """Similar to Perl's qw() operator, but with some more options.
1384 1407
1385 1408 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
1386 1409
1387 1410 words can also be a list itself, and with flat=1, the output will be
1388 1411 recursively flattened.
1389 1412
1390 1413 Examples:
1391 1414
1392 1415 >>> qw('1 2')
1393 1416 ['1', '2']
1394 1417
1395 1418 >>> qw(['a b','1 2',['m n','p q']])
1396 1419 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
1397 1420
1398 1421 >>> qw(['a b','1 2',['m n','p q']],flat=1)
1399 1422 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
1400 1423 """
1401 1424
1402 1425 if type(words) in StringTypes:
1403 1426 return [word.strip() for word in words.split(sep,maxsplit)
1404 1427 if word and not word.isspace() ]
1405 1428 if flat:
1406 1429 return flatten(map(qw,words,[1]*len(words)))
1407 1430 return map(qw,words)
1408 1431
1409 1432 #----------------------------------------------------------------------------
1410 1433 def qwflat(words,sep=None,maxsplit=-1):
1411 1434 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
1412 1435 return qw(words,1,sep,maxsplit)
1413 1436
1414 1437 #----------------------------------------------------------------------------
1415 1438 def qw_lol(indata):
1416 1439 """qw_lol('a b') -> [['a','b']],
1417 1440 otherwise it's just a call to qw().
1418 1441
1419 1442 We need this to make sure the modules_some keys *always* end up as a
1420 1443 list of lists."""
1421 1444
1422 1445 if type(indata) in StringTypes:
1423 1446 return [qw(indata)]
1424 1447 else:
1425 1448 return qw(indata)
1426 1449
1427 1450 #----------------------------------------------------------------------------
1428 1451 def grep(pat,list,case=1):
1429 1452 """Simple minded grep-like function.
1430 1453 grep(pat,list) returns occurrences of pat in list, None on failure.
1431 1454
1432 1455 It only does simple string matching, with no support for regexps. Use the
1433 1456 option case=0 for case-insensitive matching."""
1434 1457
1435 1458 # This is pretty crude. At least it should implement copying only references
1436 1459 # to the original data in case it's big. Now it copies the data for output.
1437 1460 out=[]
1438 1461 if case:
1439 1462 for term in list:
1440 1463 if term.find(pat)>-1: out.append(term)
1441 1464 else:
1442 1465 lpat=pat.lower()
1443 1466 for term in list:
1444 1467 if term.lower().find(lpat)>-1: out.append(term)
1445 1468
1446 1469 if len(out): return out
1447 1470 else: return None
1448 1471
1449 1472 #----------------------------------------------------------------------------
1450 1473 def dgrep(pat,*opts):
1451 1474 """Return grep() on dir()+dir(__builtins__).
1452 1475
1453 1476 A very common use of grep() when working interactively."""
1454 1477
1455 1478 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
1456 1479
1457 1480 #----------------------------------------------------------------------------
1458 1481 def idgrep(pat):
1459 1482 """Case-insensitive dgrep()"""
1460 1483
1461 1484 return dgrep(pat,0)
1462 1485
1463 1486 #----------------------------------------------------------------------------
1464 1487 def igrep(pat,list):
1465 1488 """Synonym for case-insensitive grep."""
1466 1489
1467 1490 return grep(pat,list,case=0)
1468 1491
1469 1492 #----------------------------------------------------------------------------
1470 1493 def indent(str,nspaces=4,ntabs=0):
1471 1494 """Indent a string a given number of spaces or tabstops.
1472 1495
1473 1496 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
1474 1497 """
1475 1498 if str is None:
1476 1499 return
1477 1500 ind = '\t'*ntabs+' '*nspaces
1478 1501 outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
1479 1502 if outstr.endswith(os.linesep+ind):
1480 1503 return outstr[:-len(ind)]
1481 1504 else:
1482 1505 return outstr
1483 1506
1484 1507 #-----------------------------------------------------------------------------
1485 1508 def native_line_ends(filename,backup=1):
1486 1509 """Convert (in-place) a file to line-ends native to the current OS.
1487 1510
1488 1511 If the optional backup argument is given as false, no backup of the
1489 1512 original file is left. """
1490 1513
1491 1514 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
1492 1515
1493 1516 bak_filename = filename + backup_suffixes[os.name]
1494 1517
1495 1518 original = open(filename).read()
1496 1519 shutil.copy2(filename,bak_filename)
1497 1520 try:
1498 1521 new = open(filename,'wb')
1499 1522 new.write(os.linesep.join(original.splitlines()))
1500 1523 new.write(os.linesep) # ALWAYS put an eol at the end of the file
1501 1524 new.close()
1502 1525 except:
1503 1526 os.rename(bak_filename,filename)
1504 1527 if not backup:
1505 1528 try:
1506 1529 os.remove(bak_filename)
1507 1530 except:
1508 1531 pass
1509 1532
1510 1533 #----------------------------------------------------------------------------
1511 1534 def get_pager_cmd(pager_cmd = None):
1512 1535 """Return a pager command.
1513 1536
1514 1537 Makes some attempts at finding an OS-correct one."""
1515 1538
1516 1539 if os.name == 'posix':
1517 1540 default_pager_cmd = 'less -r' # -r for color control sequences
1518 1541 elif os.name in ['nt','dos']:
1519 1542 default_pager_cmd = 'type'
1520 1543
1521 1544 if pager_cmd is None:
1522 1545 try:
1523 1546 pager_cmd = os.environ['PAGER']
1524 1547 except:
1525 1548 pager_cmd = default_pager_cmd
1526 1549 return pager_cmd
1527 1550
1528 1551 #-----------------------------------------------------------------------------
1529 1552 def get_pager_start(pager,start):
1530 1553 """Return the string for paging files with an offset.
1531 1554
1532 1555 This is the '+N' argument which less and more (under Unix) accept.
1533 1556 """
1534 1557
1535 1558 if pager in ['less','more']:
1536 1559 if start:
1537 1560 start_string = '+' + str(start)
1538 1561 else:
1539 1562 start_string = ''
1540 1563 else:
1541 1564 start_string = ''
1542 1565 return start_string
1543 1566
1544 1567 #----------------------------------------------------------------------------
1545 1568 # (X)emacs on W32 doesn't like to be bypassed with msvcrt.getch()
1546 1569 if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
1547 1570 import msvcrt
1548 1571 def page_more():
1549 1572 """ Smart pausing between pages
1550 1573
1551 1574 @return: True if need print more lines, False if quit
1552 1575 """
1553 1576 Term.cout.write('---Return to continue, q to quit--- ')
1554 1577 ans = msvcrt.getch()
1555 1578 if ans in ("q", "Q"):
1556 1579 result = False
1557 1580 else:
1558 1581 result = True
1559 1582 Term.cout.write("\b"*37 + " "*37 + "\b"*37)
1560 1583 return result
1561 1584 else:
1562 1585 def page_more():
1563 1586 ans = raw_input('---Return to continue, q to quit--- ')
1564 1587 if ans.lower().startswith('q'):
1565 1588 return False
1566 1589 else:
1567 1590 return True
1568 1591
1569 1592 esc_re = re.compile(r"(\x1b[^m]+m)")
1570 1593
1571 1594 def page_dumb(strng,start=0,screen_lines=25):
1572 1595 """Very dumb 'pager' in Python, for when nothing else works.
1573 1596
1574 1597 Only moves forward, same interface as page(), except for pager_cmd and
1575 1598 mode."""
1576 1599
1577 1600 out_ln = strng.splitlines()[start:]
1578 1601 screens = chop(out_ln,screen_lines-1)
1579 1602 if len(screens) == 1:
1580 1603 print >>Term.cout, os.linesep.join(screens[0])
1581 1604 else:
1582 1605 last_escape = ""
1583 1606 for scr in screens[0:-1]:
1584 1607 hunk = os.linesep.join(scr)
1585 1608 print >>Term.cout, last_escape + hunk
1586 1609 if not page_more():
1587 1610 return
1588 1611 esc_list = esc_re.findall(hunk)
1589 1612 if len(esc_list) > 0:
1590 1613 last_escape = esc_list[-1]
1591 1614 print >>Term.cout, last_escape + os.linesep.join(screens[-1])
1592 1615
1593 1616 #----------------------------------------------------------------------------
1594 1617 def page(strng,start=0,screen_lines=0,pager_cmd = None):
1595 1618 """Print a string, piping through a pager after a certain length.
1596 1619
1597 1620 The screen_lines parameter specifies the number of *usable* lines of your
1598 1621 terminal screen (total lines minus lines you need to reserve to show other
1599 1622 information).
1600 1623
1601 1624 If you set screen_lines to a number <=0, page() will try to auto-determine
1602 1625 your screen size and will only use up to (screen_size+screen_lines) for
1603 1626 printing, paging after that. That is, if you want auto-detection but need
1604 1627 to reserve the bottom 3 lines of the screen, use screen_lines = -3, and for
1605 1628 auto-detection without any lines reserved simply use screen_lines = 0.
1606 1629
1607 1630 If a string won't fit in the allowed lines, it is sent through the
1608 1631 specified pager command. If none given, look for PAGER in the environment,
1609 1632 and ultimately default to less.
1610 1633
1611 1634 If no system pager works, the string is sent through a 'dumb pager'
1612 1635 written in python, very simplistic.
1613 1636 """
1614 1637
1615 1638 # Some routines may auto-compute start offsets incorrectly and pass a
1616 1639 # negative value. Offset to 0 for robustness.
1617 1640 start = max(0,start)
1618 1641
1619 1642 # first, try the hook
1620 1643 ip = ipapi.get()
1621 1644 if ip:
1622 1645 try:
1623 1646 ip.IP.hooks.show_in_pager(strng)
1624 1647 return
1625 1648 except ipapi.TryNext:
1626 1649 pass
1627 1650
1628 1651 # Ugly kludge, but calling curses.initscr() flat out crashes in emacs
1629 1652 TERM = os.environ.get('TERM','dumb')
1630 1653 if TERM in ['dumb','emacs'] and os.name != 'nt':
1631 1654 print strng
1632 1655 return
1633 1656 # chop off the topmost part of the string we don't want to see
1634 1657 str_lines = strng.split(os.linesep)[start:]
1635 1658 str_toprint = os.linesep.join(str_lines)
1636 1659 num_newlines = len(str_lines)
1637 1660 len_str = len(str_toprint)
1638 1661
1639 1662 # Dumb heuristics to guesstimate number of on-screen lines the string
1640 1663 # takes. Very basic, but good enough for docstrings in reasonable
1641 1664 # terminals. If someone later feels like refining it, it's not hard.
1642 1665 numlines = max(num_newlines,int(len_str/80)+1)
1643 1666
1644 1667 if os.name == "nt":
1645 1668 screen_lines_def = get_console_size(defaulty=25)[1]
1646 1669 else:
1647 1670 screen_lines_def = 25 # default value if we can't auto-determine
1648 1671
1649 1672 # auto-determine screen size
1650 1673 if screen_lines <= 0:
1651 1674 if TERM=='xterm':
1652 1675 use_curses = USE_CURSES
1653 1676 else:
1654 1677 # curses causes problems on many terminals other than xterm.
1655 1678 use_curses = False
1656 1679 if use_curses:
1657 1680 # There is a bug in curses, where *sometimes* it fails to properly
1658 1681 # initialize, and then after the endwin() call is made, the
1659 1682 # terminal is left in an unusable state. Rather than trying to
1660 1683 # check everytime for this (by requesting and comparing termios
1661 1684 # flags each time), we just save the initial terminal state and
1662 1685 # unconditionally reset it every time. It's cheaper than making
1663 1686 # the checks.
1664 1687 term_flags = termios.tcgetattr(sys.stdout)
1665 1688 scr = curses.initscr()
1666 1689 screen_lines_real,screen_cols = scr.getmaxyx()
1667 1690 curses.endwin()
1668 1691 # Restore terminal state in case endwin() didn't.
1669 1692 termios.tcsetattr(sys.stdout,termios.TCSANOW,term_flags)
1670 1693 # Now we have what we needed: the screen size in rows/columns
1671 1694 screen_lines += screen_lines_real
1672 1695 #print '***Screen size:',screen_lines_real,'lines x',\
1673 1696 #screen_cols,'columns.' # dbg
1674 1697 else:
1675 1698 screen_lines += screen_lines_def
1676 1699
1677 1700 #print 'numlines',numlines,'screenlines',screen_lines # dbg
1678 1701 if numlines <= screen_lines :
1679 1702 #print '*** normal print' # dbg
1680 1703 print >>Term.cout, str_toprint
1681 1704 else:
1682 1705 # Try to open pager and default to internal one if that fails.
1683 1706 # All failure modes are tagged as 'retval=1', to match the return
1684 1707 # value of a failed system command. If any intermediate attempt
1685 1708 # sets retval to 1, at the end we resort to our own page_dumb() pager.
1686 1709 pager_cmd = get_pager_cmd(pager_cmd)
1687 1710 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
1688 1711 if os.name == 'nt':
1689 1712 if pager_cmd.startswith('type'):
1690 1713 # The default WinXP 'type' command is failing on complex strings.
1691 1714 retval = 1
1692 1715 else:
1693 1716 tmpname = tempfile.mktemp('.txt')
1694 1717 tmpfile = file(tmpname,'wt')
1695 1718 tmpfile.write(strng)
1696 1719 tmpfile.close()
1697 1720 cmd = "%s < %s" % (pager_cmd,tmpname)
1698 1721 if os.system(cmd):
1699 1722 retval = 1
1700 1723 else:
1701 1724 retval = None
1702 1725 os.remove(tmpname)
1703 1726 else:
1704 1727 try:
1705 1728 retval = None
1706 1729 # if I use popen4, things hang. No idea why.
1707 1730 #pager,shell_out = os.popen4(pager_cmd)
1708 1731 pager = os.popen(pager_cmd,'w')
1709 1732 pager.write(strng)
1710 1733 pager.close()
1711 1734 retval = pager.close() # success returns None
1712 1735 except IOError,msg: # broken pipe when user quits
1713 1736 if msg.args == (32,'Broken pipe'):
1714 1737 retval = None
1715 1738 else:
1716 1739 retval = 1
1717 1740 except OSError:
1718 1741 # Other strange problems, sometimes seen in Win2k/cygwin
1719 1742 retval = 1
1720 1743 if retval is not None:
1721 1744 page_dumb(strng,screen_lines=screen_lines)
1722 1745
1723 1746 #----------------------------------------------------------------------------
1724 1747 def page_file(fname,start = 0, pager_cmd = None):
1725 1748 """Page a file, using an optional pager command and starting line.
1726 1749 """
1727 1750
1728 1751 pager_cmd = get_pager_cmd(pager_cmd)
1729 1752 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
1730 1753
1731 1754 try:
1732 1755 if os.environ['TERM'] in ['emacs','dumb']:
1733 1756 raise EnvironmentError
1734 1757 xsys(pager_cmd + ' ' + fname)
1735 1758 except:
1736 1759 try:
1737 1760 if start > 0:
1738 1761 start -= 1
1739 1762 page(open(fname).read(),start)
1740 1763 except:
1741 1764 print 'Unable to show file',`fname`
1742 1765
1743 1766
1744 1767 #----------------------------------------------------------------------------
1745 1768 def snip_print(str,width = 75,print_full = 0,header = ''):
1746 1769 """Print a string snipping the midsection to fit in width.
1747 1770
1748 1771 print_full: mode control:
1749 1772 - 0: only snip long strings
1750 1773 - 1: send to page() directly.
1751 1774 - 2: snip long strings and ask for full length viewing with page()
1752 1775 Return 1 if snipping was necessary, 0 otherwise."""
1753 1776
1754 1777 if print_full == 1:
1755 1778 page(header+str)
1756 1779 return 0
1757 1780
1758 1781 print header,
1759 1782 if len(str) < width:
1760 1783 print str
1761 1784 snip = 0
1762 1785 else:
1763 1786 whalf = int((width -5)/2)
1764 1787 print str[:whalf] + ' <...> ' + str[-whalf:]
1765 1788 snip = 1
1766 1789 if snip and print_full == 2:
1767 1790 if raw_input(header+' Snipped. View (y/n)? [N]').lower() == 'y':
1768 1791 page(str)
1769 1792 return snip
1770 1793
1771 1794 #****************************************************************************
1772 1795 # lists, dicts and structures
1773 1796
1774 1797 def belong(candidates,checklist):
1775 1798 """Check whether a list of items appear in a given list of options.
1776 1799
1777 1800 Returns a list of 1 and 0, one for each candidate given."""
1778 1801
1779 1802 return [x in checklist for x in candidates]
1780 1803
1781 1804 #----------------------------------------------------------------------------
1782 1805 def uniq_stable(elems):
1783 1806 """uniq_stable(elems) -> list
1784 1807
1785 1808 Return from an iterable, a list of all the unique elements in the input,
1786 1809 but maintaining the order in which they first appear.
1787 1810
1788 1811 A naive solution to this problem which just makes a dictionary with the
1789 1812 elements as keys fails to respect the stability condition, since
1790 1813 dictionaries are unsorted by nature.
1791 1814
1792 1815 Note: All elements in the input must be valid dictionary keys for this
1793 1816 routine to work, as it internally uses a dictionary for efficiency
1794 1817 reasons."""
1795 1818
1796 1819 unique = []
1797 1820 unique_dict = {}
1798 1821 for nn in elems:
1799 1822 if nn not in unique_dict:
1800 1823 unique.append(nn)
1801 1824 unique_dict[nn] = None
1802 1825 return unique
1803 1826
1804 1827 #----------------------------------------------------------------------------
1805 1828 class NLprinter:
1806 1829 """Print an arbitrarily nested list, indicating index numbers.
1807 1830
1808 1831 An instance of this class called nlprint is available and callable as a
1809 1832 function.
1810 1833
1811 1834 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
1812 1835 and using 'sep' to separate the index from the value. """
1813 1836
1814 1837 def __init__(self):
1815 1838 self.depth = 0
1816 1839
1817 1840 def __call__(self,lst,pos='',**kw):
1818 1841 """Prints the nested list numbering levels."""
1819 1842 kw.setdefault('indent',' ')
1820 1843 kw.setdefault('sep',': ')
1821 1844 kw.setdefault('start',0)
1822 1845 kw.setdefault('stop',len(lst))
1823 1846 # we need to remove start and stop from kw so they don't propagate
1824 1847 # into a recursive call for a nested list.
1825 1848 start = kw['start']; del kw['start']
1826 1849 stop = kw['stop']; del kw['stop']
1827 1850 if self.depth == 0 and 'header' in kw.keys():
1828 1851 print kw['header']
1829 1852
1830 1853 for idx in range(start,stop):
1831 1854 elem = lst[idx]
1832 1855 if type(elem)==type([]):
1833 1856 self.depth += 1
1834 1857 self.__call__(elem,itpl('$pos$idx,'),**kw)
1835 1858 self.depth -= 1
1836 1859 else:
1837 1860 printpl(kw['indent']*self.depth+'$pos$idx$kw["sep"]$elem')
1838 1861
1839 1862 nlprint = NLprinter()
1840 1863 #----------------------------------------------------------------------------
1841 1864 def all_belong(candidates,checklist):
1842 1865 """Check whether a list of items ALL appear in a given list of options.
1843 1866
1844 1867 Returns a single 1 or 0 value."""
1845 1868
1846 1869 return 1-(0 in [x in checklist for x in candidates])
1847 1870
1848 1871 #----------------------------------------------------------------------------
1849 1872 def sort_compare(lst1,lst2,inplace = 1):
1850 1873 """Sort and compare two lists.
1851 1874
1852 1875 By default it does it in place, thus modifying the lists. Use inplace = 0
1853 1876 to avoid that (at the cost of temporary copy creation)."""
1854 1877 if not inplace:
1855 1878 lst1 = lst1[:]
1856 1879 lst2 = lst2[:]
1857 1880 lst1.sort(); lst2.sort()
1858 1881 return lst1 == lst2
1859 1882
1860 1883 #----------------------------------------------------------------------------
1861 1884 def list2dict(lst):
1862 1885 """Takes a list of (key,value) pairs and turns it into a dict."""
1863 1886
1864 1887 dic = {}
1865 1888 for k,v in lst: dic[k] = v
1866 1889 return dic
1867 1890
1868 1891 #----------------------------------------------------------------------------
1869 1892 def list2dict2(lst,default=''):
1870 1893 """Takes a list and turns it into a dict.
1871 1894 Much slower than list2dict, but more versatile. This version can take
1872 1895 lists with sublists of arbitrary length (including sclars)."""
1873 1896
1874 1897 dic = {}
1875 1898 for elem in lst:
1876 1899 if type(elem) in (types.ListType,types.TupleType):
1877 1900 size = len(elem)
1878 1901 if size == 0:
1879 1902 pass
1880 1903 elif size == 1:
1881 1904 dic[elem] = default
1882 1905 else:
1883 1906 k,v = elem[0], elem[1:]
1884 1907 if len(v) == 1: v = v[0]
1885 1908 dic[k] = v
1886 1909 else:
1887 1910 dic[elem] = default
1888 1911 return dic
1889 1912
1890 1913 #----------------------------------------------------------------------------
1891 1914 def flatten(seq):
1892 1915 """Flatten a list of lists (NOT recursive, only works for 2d lists)."""
1893 1916
1894 1917 return [x for subseq in seq for x in subseq]
1895 1918
1896 1919 #----------------------------------------------------------------------------
1897 1920 def get_slice(seq,start=0,stop=None,step=1):
1898 1921 """Get a slice of a sequence with variable step. Specify start,stop,step."""
1899 1922 if stop == None:
1900 1923 stop = len(seq)
1901 1924 item = lambda i: seq[i]
1902 1925 return map(item,xrange(start,stop,step))
1903 1926
1904 1927 #----------------------------------------------------------------------------
1905 1928 def chop(seq,size):
1906 1929 """Chop a sequence into chunks of the given size."""
1907 1930 chunk = lambda i: seq[i:i+size]
1908 1931 return map(chunk,xrange(0,len(seq),size))
1909 1932
1910 1933 #----------------------------------------------------------------------------
1911 1934 # with is a keyword as of python 2.5, so this function is renamed to withobj
1912 1935 # from its old 'with' name.
1913 1936 def with_obj(object, **args):
1914 1937 """Set multiple attributes for an object, similar to Pascal's with.
1915 1938
1916 1939 Example:
1917 1940 with_obj(jim,
1918 1941 born = 1960,
1919 1942 haircolour = 'Brown',
1920 1943 eyecolour = 'Green')
1921 1944
1922 1945 Credit: Greg Ewing, in
1923 1946 http://mail.python.org/pipermail/python-list/2001-May/040703.html.
1924 1947
1925 1948 NOTE: up until IPython 0.7.2, this was called simply 'with', but 'with'
1926 1949 has become a keyword for Python 2.5, so we had to rename it."""
1927 1950
1928 1951 object.__dict__.update(args)
1929 1952
1930 1953 #----------------------------------------------------------------------------
1931 1954 def setattr_list(obj,alist,nspace = None):
1932 1955 """Set a list of attributes for an object taken from a namespace.
1933 1956
1934 1957 setattr_list(obj,alist,nspace) -> sets in obj all the attributes listed in
1935 1958 alist with their values taken from nspace, which must be a dict (something
1936 1959 like locals() will often do) If nspace isn't given, locals() of the
1937 1960 *caller* is used, so in most cases you can omit it.
1938 1961
1939 1962 Note that alist can be given as a string, which will be automatically
1940 1963 split into a list on whitespace. If given as a list, it must be a list of
1941 1964 *strings* (the variable names themselves), not of variables."""
1942 1965
1943 1966 # this grabs the local variables from the *previous* call frame -- that is
1944 1967 # the locals from the function that called setattr_list().
1945 1968 # - snipped from weave.inline()
1946 1969 if nspace is None:
1947 1970 call_frame = sys._getframe().f_back
1948 1971 nspace = call_frame.f_locals
1949 1972
1950 1973 if type(alist) in StringTypes:
1951 1974 alist = alist.split()
1952 1975 for attr in alist:
1953 1976 val = eval(attr,nspace)
1954 1977 setattr(obj,attr,val)
1955 1978
1956 1979 #----------------------------------------------------------------------------
1957 1980 def getattr_list(obj,alist,*args):
1958 1981 """getattr_list(obj,alist[, default]) -> attribute list.
1959 1982
1960 1983 Get a list of named attributes for an object. When a default argument is
1961 1984 given, it is returned when the attribute doesn't exist; without it, an
1962 1985 exception is raised in that case.
1963 1986
1964 1987 Note that alist can be given as a string, which will be automatically
1965 1988 split into a list on whitespace. If given as a list, it must be a list of
1966 1989 *strings* (the variable names themselves), not of variables."""
1967 1990
1968 1991 if type(alist) in StringTypes:
1969 1992 alist = alist.split()
1970 1993 if args:
1971 1994 if len(args)==1:
1972 1995 default = args[0]
1973 1996 return map(lambda attr: getattr(obj,attr,default),alist)
1974 1997 else:
1975 1998 raise ValueError,'getattr_list() takes only one optional argument'
1976 1999 else:
1977 2000 return map(lambda attr: getattr(obj,attr),alist)
1978 2001
1979 2002 #----------------------------------------------------------------------------
1980 2003 def map_method(method,object_list,*argseq,**kw):
1981 2004 """map_method(method,object_list,*args,**kw) -> list
1982 2005
1983 2006 Return a list of the results of applying the methods to the items of the
1984 2007 argument sequence(s). If more than one sequence is given, the method is
1985 2008 called with an argument list consisting of the corresponding item of each
1986 2009 sequence. All sequences must be of the same length.
1987 2010
1988 2011 Keyword arguments are passed verbatim to all objects called.
1989 2012
1990 2013 This is Python code, so it's not nearly as fast as the builtin map()."""
1991 2014
1992 2015 out_list = []
1993 2016 idx = 0
1994 2017 for object in object_list:
1995 2018 try:
1996 2019 handler = getattr(object, method)
1997 2020 except AttributeError:
1998 2021 out_list.append(None)
1999 2022 else:
2000 2023 if argseq:
2001 2024 args = map(lambda lst:lst[idx],argseq)
2002 2025 #print 'ob',object,'hand',handler,'ar',args # dbg
2003 2026 out_list.append(handler(args,**kw))
2004 2027 else:
2005 2028 out_list.append(handler(**kw))
2006 2029 idx += 1
2007 2030 return out_list
2008 2031
2009 2032 #----------------------------------------------------------------------------
2010 2033 def get_class_members(cls):
2011 2034 ret = dir(cls)
2012 2035 if hasattr(cls,'__bases__'):
2013 2036 for base in cls.__bases__:
2014 2037 ret.extend(get_class_members(base))
2015 2038 return ret
2016 2039
2017 2040 #----------------------------------------------------------------------------
2018 2041 def dir2(obj):
2019 2042 """dir2(obj) -> list of strings
2020 2043
2021 2044 Extended version of the Python builtin dir(), which does a few extra
2022 2045 checks, and supports common objects with unusual internals that confuse
2023 2046 dir(), such as Traits and PyCrust.
2024 2047
2025 2048 This version is guaranteed to return only a list of true strings, whereas
2026 2049 dir() returns anything that objects inject into themselves, even if they
2027 2050 are later not really valid for attribute access (many extension libraries
2028 2051 have such bugs).
2029 2052 """
2030 2053
2031 2054 # Start building the attribute list via dir(), and then complete it
2032 2055 # with a few extra special-purpose calls.
2033 2056 words = dir(obj)
2034 2057
2035 2058 if hasattr(obj,'__class__'):
2036 2059 words.append('__class__')
2037 2060 words.extend(get_class_members(obj.__class__))
2038 2061 #if '__base__' in words: 1/0
2039 2062
2040 2063 # Some libraries (such as traits) may introduce duplicates, we want to
2041 2064 # track and clean this up if it happens
2042 2065 may_have_dupes = False
2043 2066
2044 2067 # this is the 'dir' function for objects with Enthought's traits
2045 2068 if hasattr(obj, 'trait_names'):
2046 2069 try:
2047 2070 words.extend(obj.trait_names())
2048 2071 may_have_dupes = True
2049 2072 except TypeError:
2050 2073 # This will happen if `obj` is a class and not an instance.
2051 2074 pass
2052 2075
2053 2076 # Support for PyCrust-style _getAttributeNames magic method.
2054 2077 if hasattr(obj, '_getAttributeNames'):
2055 2078 try:
2056 2079 words.extend(obj._getAttributeNames())
2057 2080 may_have_dupes = True
2058 2081 except TypeError:
2059 2082 # `obj` is a class and not an instance. Ignore
2060 2083 # this error.
2061 2084 pass
2062 2085
2063 2086 if may_have_dupes:
2064 2087 # eliminate possible duplicates, as some traits may also
2065 2088 # appear as normal attributes in the dir() call.
2066 2089 words = list(set(words))
2067 2090 words.sort()
2068 2091
2069 2092 # filter out non-string attributes which may be stuffed by dir() calls
2070 2093 # and poor coding in third-party modules
2071 2094 return [w for w in words if isinstance(w, basestring)]
2072 2095
2073 2096 #----------------------------------------------------------------------------
2074 2097 def import_fail_info(mod_name,fns=None):
2075 2098 """Inform load failure for a module."""
2076 2099
2077 2100 if fns == None:
2078 2101 warn("Loading of %s failed.\n" % (mod_name,))
2079 2102 else:
2080 2103 warn("Loading of %s from %s failed.\n" % (fns,mod_name))
2081 2104
2082 2105 #----------------------------------------------------------------------------
2083 2106 # Proposed popitem() extension, written as a method
2084 2107
2085 2108
2086 2109 class NotGiven: pass
2087 2110
2088 2111 def popkey(dct,key,default=NotGiven):
2089 2112 """Return dct[key] and delete dct[key].
2090 2113
2091 2114 If default is given, return it if dct[key] doesn't exist, otherwise raise
2092 2115 KeyError. """
2093 2116
2094 2117 try:
2095 2118 val = dct[key]
2096 2119 except KeyError:
2097 2120 if default is NotGiven:
2098 2121 raise
2099 2122 else:
2100 2123 return default
2101 2124 else:
2102 2125 del dct[key]
2103 2126 return val
2104 2127
2105 2128 def wrap_deprecated(func, suggest = '<nothing>'):
2106 2129 def newFunc(*args, **kwargs):
2107 2130 warnings.warn("Call to deprecated function %s, use %s instead" %
2108 2131 ( func.__name__, suggest),
2109 2132 category=DeprecationWarning,
2110 2133 stacklevel = 2)
2111 2134 return func(*args, **kwargs)
2112 2135 return newFunc
2113 2136
2114 2137
2115 2138 def _num_cpus_unix():
2116 2139 """Return the number of active CPUs on a Unix system."""
2117 2140 return os.sysconf("SC_NPROCESSORS_ONLN")
2118 2141
2119 2142
2120 2143 def _num_cpus_darwin():
2121 2144 """Return the number of active CPUs on a Darwin system."""
2122 2145 p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE)
2123 2146 return p.stdout.read()
2124 2147
2125 2148
2126 2149 def _num_cpus_windows():
2127 2150 """Return the number of active CPUs on a Windows system."""
2128 2151 return os.environ.get("NUMBER_OF_PROCESSORS")
2129 2152
2130 2153
2131 2154 def num_cpus():
2132 2155 """Return the effective number of CPUs in the system as an integer.
2133 2156
2134 2157 This cross-platform function makes an attempt at finding the total number of
2135 2158 available CPUs in the system, as returned by various underlying system and
2136 2159 python calls.
2137 2160
2138 2161 If it can't find a sensible answer, it returns 1 (though an error *may* make
2139 2162 it return a large positive number that's actually incorrect).
2140 2163 """
2141 2164
2142 2165 # Many thanks to the Parallel Python project (http://www.parallelpython.com)
2143 2166 # for the names of the keys we needed to look up for this function. This
2144 2167 # code was inspired by their equivalent function.
2145 2168
2146 2169 ncpufuncs = {'Linux':_num_cpus_unix,
2147 2170 'Darwin':_num_cpus_darwin,
2148 2171 'Windows':_num_cpus_windows,
2149 2172 # On Vista, python < 2.5.2 has a bug and returns 'Microsoft'
2150 2173 # See http://bugs.python.org/issue1082 for details.
2151 2174 'Microsoft':_num_cpus_windows,
2152 2175 }
2153 2176
2154 2177 ncpufunc = ncpufuncs.get(platform.system(),
2155 2178 # default to unix version (Solaris, AIX, etc)
2156 2179 _num_cpus_unix)
2157 2180
2158 2181 try:
2159 2182 ncpus = max(1,int(ncpufunc()))
2160 2183 except:
2161 2184 ncpus = 1
2162 2185 return ncpus
2163 2186
2164 2187 def extract_vars(*names,**kw):
2165 2188 """Extract a set of variables by name from another frame.
2166 2189
2167 2190 :Parameters:
2168 2191 - `*names`: strings
2169 2192 One or more variable names which will be extracted from the caller's
2170 2193 frame.
2171 2194
2172 2195 :Keywords:
2173 2196 - `depth`: integer (0)
2174 2197 How many frames in the stack to walk when looking for your variables.
2175 2198
2176 2199
2177 2200 Examples:
2178 2201
2179 2202 In [2]: def func(x):
2180 2203 ...: y = 1
2181 2204 ...: print extract_vars('x','y')
2182 2205 ...:
2183 2206
2184 2207 In [3]: func('hello')
2185 2208 {'y': 1, 'x': 'hello'}
2186 2209 """
2187 2210
2188 2211 depth = kw.get('depth',0)
2189 2212
2190 2213 callerNS = sys._getframe(depth+1).f_locals
2191 2214 return dict((k,callerNS[k]) for k in names)
2192 2215
2193 2216
2194 2217 def extract_vars_above(*names):
2195 2218 """Extract a set of variables by name from another frame.
2196 2219
2197 2220 Similar to extractVars(), but with a specified depth of 1, so that names
2198 2221 are exctracted exactly from above the caller.
2199 2222
2200 2223 This is simply a convenience function so that the very common case (for us)
2201 2224 of skipping exactly 1 frame doesn't have to construct a special dict for
2202 2225 keyword passing."""
2203 2226
2204 2227 callerNS = sys._getframe(2).f_locals
2205 2228 return dict((k,callerNS[k]) for k in names)
2206 2229
2207 2230 def shexp(s):
2208 2231 """Expand $VARS and ~names in a string, like a shell
2209 2232
2210 2233 :Examples:
2211 2234
2212 2235 In [2]: os.environ['FOO']='test'
2213 2236
2214 2237 In [3]: shexp('variable FOO is $FOO')
2215 2238 Out[3]: 'variable FOO is test'
2216 2239 """
2217 2240 return os.path.expandvars(os.path.expanduser(s))
2218 2241
2219 2242
2220 2243 def list_strings(arg):
2221 2244 """Always return a list of strings, given a string or list of strings
2222 2245 as input.
2223 2246
2224 2247 :Examples:
2225 2248
2226 2249 In [7]: list_strings('A single string')
2227 2250 Out[7]: ['A single string']
2228 2251
2229 2252 In [8]: list_strings(['A single string in a list'])
2230 2253 Out[8]: ['A single string in a list']
2231 2254
2232 2255 In [9]: list_strings(['A','list','of','strings'])
2233 2256 Out[9]: ['A', 'list', 'of', 'strings']
2234 2257 """
2235 2258
2236 2259 if isinstance(arg,basestring): return [arg]
2237 2260 else: return arg
2238 2261
2239 2262 def marquee(txt='',width=78,mark='*'):
2240 2263 """Return the input string centered in a 'marquee'.
2241 2264
2242 2265 :Examples:
2243 2266
2244 2267 In [16]: marquee('A test',40)
2245 2268 Out[16]: '**************** A test ****************'
2246 2269
2247 2270 In [17]: marquee('A test',40,'-')
2248 2271 Out[17]: '---------------- A test ----------------'
2249 2272
2250 2273 In [18]: marquee('A test',40,' ')
2251 2274 Out[18]: ' A test '
2252 2275
2253 2276 """
2254 2277 if not txt:
2255 2278 return (mark*width)[:width]
2256 2279 nmark = (width-len(txt)-2)/len(mark)/2
2257 2280 if nmark < 0: nmark =0
2258 2281 marks = mark*nmark
2259 2282 return '%s %s %s' % (marks,txt,marks)
2260 2283
2261 2284 #*************************** end of file <genutils.py> **********************
@@ -1,667 +1,670 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 Tests for IPython.utils.traitlets.
5 5
6 6 Authors:
7 7
8 8 * Brian Granger
9 9 * Enthought, Inc. Some of the code in this file comes from enthought.traits
10 10 and is licensed under the BSD license. Also, many of the ideas also come
11 11 from enthought.traits even though our implementation is very different.
12 12 """
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Copyright (C) 2008-2009 The IPython Development Team
16 16 #
17 17 # Distributed under the terms of the BSD License. The full license is in
18 18 # the file COPYING, distributed as part of this software.
19 19 #-----------------------------------------------------------------------------
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Imports
23 23 #-----------------------------------------------------------------------------
24 24
25 25 import sys
26 26 import os
27 27
28 28
29 29 from unittest import TestCase
30 30
31 31 from IPython.utils.traitlets import (
32 32 HasTraitlets, MetaHasTraitlets, TraitletType, Any,
33 33 Int, Long, Float, Complex, Str, Unicode, Bool, TraitletError,
34 34 Undefined, Type, This, Instance
35 35 )
36 36
37 37
38 38 #-----------------------------------------------------------------------------
39 39 # Helper classes for testing
40 40 #-----------------------------------------------------------------------------
41 41
42 42
43 43 class HasTraitletsStub(HasTraitlets):
44 44
45 45 def _notify_traitlet(self, name, old, new):
46 46 self._notify_name = name
47 47 self._notify_old = old
48 48 self._notify_new = new
49 49
50 50
51 51 #-----------------------------------------------------------------------------
52 52 # Test classes
53 53 #-----------------------------------------------------------------------------
54 54
55 55
56 56 class TestTraitletType(TestCase):
57 57
58 58 def test_get_undefined(self):
59 59 class A(HasTraitlets):
60 60 a = TraitletType
61 61 a = A()
62 62 self.assertEquals(a.a, Undefined)
63 63
64 64 def test_set(self):
65 65 class A(HasTraitletsStub):
66 66 a = TraitletType
67 67
68 68 a = A()
69 69 a.a = 10
70 70 self.assertEquals(a.a, 10)
71 71 self.assertEquals(a._notify_name, 'a')
72 72 self.assertEquals(a._notify_old, Undefined)
73 73 self.assertEquals(a._notify_new, 10)
74 74
75 75 def test_validate(self):
76 76 class MyTT(TraitletType):
77 77 def validate(self, inst, value):
78 78 return -1
79 79 class A(HasTraitletsStub):
80 80 tt = MyTT
81 81
82 82 a = A()
83 83 a.tt = 10
84 84 self.assertEquals(a.tt, -1)
85 85
86 86 def test_default_validate(self):
87 87 class MyIntTT(TraitletType):
88 88 def validate(self, obj, value):
89 89 if isinstance(value, int):
90 90 return value
91 91 self.error(obj, value)
92 92 class A(HasTraitlets):
93 93 tt = MyIntTT(10)
94 94 a = A()
95 95 self.assertEquals(a.tt, 10)
96 96
97 97 # Defaults are validated when the HasTraitlets is instantiated
98 98 class B(HasTraitlets):
99 99 tt = MyIntTT('bad default')
100 100 self.assertRaises(TraitletError, B)
101 101
102 102 def test_is_valid_for(self):
103 103 class MyTT(TraitletType):
104 104 def is_valid_for(self, value):
105 105 return True
106 106 class A(HasTraitlets):
107 107 tt = MyTT
108 108
109 109 a = A()
110 110 a.tt = 10
111 111 self.assertEquals(a.tt, 10)
112 112
113 113 def test_value_for(self):
114 114 class MyTT(TraitletType):
115 115 def value_for(self, value):
116 116 return 20
117 117 class A(HasTraitlets):
118 118 tt = MyTT
119 119
120 120 a = A()
121 121 a.tt = 10
122 122 self.assertEquals(a.tt, 20)
123 123
124 124 def test_info(self):
125 125 class A(HasTraitlets):
126 126 tt = TraitletType
127 127 a = A()
128 128 self.assertEquals(A.tt.info(), 'any value')
129 129
130 130 def test_error(self):
131 131 class A(HasTraitlets):
132 132 tt = TraitletType
133 133 a = A()
134 134 self.assertRaises(TraitletError, A.tt.error, a, 10)
135 135
136 136
137 137 class TestHasTraitletsMeta(TestCase):
138 138
139 139 def test_metaclass(self):
140 140 self.assertEquals(type(HasTraitlets), MetaHasTraitlets)
141 141
142 142 class A(HasTraitlets):
143 143 a = Int
144 144
145 145 a = A()
146 146 self.assertEquals(type(a.__class__), MetaHasTraitlets)
147 147 self.assertEquals(a.a,0)
148 148 a.a = 10
149 149 self.assertEquals(a.a,10)
150 150
151 151 class B(HasTraitlets):
152 152 b = Int()
153 153
154 154 b = B()
155 155 self.assertEquals(b.b,0)
156 156 b.b = 10
157 157 self.assertEquals(b.b,10)
158 158
159 159 class C(HasTraitlets):
160 160 c = Int(30)
161 161
162 162 c = C()
163 163 self.assertEquals(c.c,30)
164 164 c.c = 10
165 165 self.assertEquals(c.c,10)
166 166
167 167 def test_this_class(self):
168 168 class A(HasTraitlets):
169 169 t = This()
170 170 tt = This()
171 171 class B(A):
172 172 tt = This()
173 173 ttt = This()
174 174 self.assertEquals(A.t.this_class, A)
175 175 self.assertEquals(B.t.this_class, A)
176 176 self.assertEquals(B.tt.this_class, B)
177 177 self.assertEquals(B.ttt.this_class, B)
178 178
179 179 class TestHasTraitletsNotify(TestCase):
180 180
181 181 def setUp(self):
182 182 self._notify1 = []
183 183 self._notify2 = []
184 184
185 185 def notify1(self, name, old, new):
186 186 self._notify1.append((name, old, new))
187 187
188 188 def notify2(self, name, old, new):
189 189 self._notify2.append((name, old, new))
190 190
191 191 def test_notify_all(self):
192 192
193 193 class A(HasTraitlets):
194 194 a = Int
195 195 b = Float
196 196
197 197 a = A()
198 198 a.on_traitlet_change(self.notify1)
199 199 a.a = 0
200 200 self.assertEquals(len(self._notify1),0)
201 201 a.b = 0.0
202 202 self.assertEquals(len(self._notify1),0)
203 203 a.a = 10
204 204 self.assert_(('a',0,10) in self._notify1)
205 205 a.b = 10.0
206 206 self.assert_(('b',0.0,10.0) in self._notify1)
207 207 self.assertRaises(TraitletError,setattr,a,'a','bad string')
208 208 self.assertRaises(TraitletError,setattr,a,'b','bad string')
209 209 self._notify1 = []
210 210 a.on_traitlet_change(self.notify1,remove=True)
211 211 a.a = 20
212 212 a.b = 20.0
213 213 self.assertEquals(len(self._notify1),0)
214 214
215 215 def test_notify_one(self):
216 216
217 217 class A(HasTraitlets):
218 218 a = Int
219 219 b = Float
220 220
221 221 a = A()
222 222 a.on_traitlet_change(self.notify1, 'a')
223 223 a.a = 0
224 224 self.assertEquals(len(self._notify1),0)
225 225 a.a = 10
226 226 self.assert_(('a',0,10) in self._notify1)
227 227 self.assertRaises(TraitletError,setattr,a,'a','bad string')
228 228
229 229 def test_subclass(self):
230 230
231 231 class A(HasTraitlets):
232 232 a = Int
233 233
234 234 class B(A):
235 235 b = Float
236 236
237 237 b = B()
238 238 self.assertEquals(b.a,0)
239 239 self.assertEquals(b.b,0.0)
240 240 b.a = 100
241 241 b.b = 100.0
242 242 self.assertEquals(b.a,100)
243 243 self.assertEquals(b.b,100.0)
244 244
245 245 def test_notify_subclass(self):
246 246
247 247 class A(HasTraitlets):
248 248 a = Int
249 249
250 250 class B(A):
251 251 b = Float
252 252
253 253 b = B()
254 254 b.on_traitlet_change(self.notify1, 'a')
255 255 b.on_traitlet_change(self.notify2, 'b')
256 256 b.a = 0
257 257 b.b = 0.0
258 258 self.assertEquals(len(self._notify1),0)
259 259 self.assertEquals(len(self._notify2),0)
260 260 b.a = 10
261 261 b.b = 10.0
262 262 self.assert_(('a',0,10) in self._notify1)
263 263 self.assert_(('b',0.0,10.0) in self._notify2)
264 264
265 265 def test_static_notify(self):
266 266
267 267 class A(HasTraitlets):
268 268 a = Int
269 269 _notify1 = []
270 270 def _a_changed(self, name, old, new):
271 271 self._notify1.append((name, old, new))
272 272
273 273 a = A()
274 274 a.a = 0
275 275 # This is broken!!!
276 276 self.assertEquals(len(a._notify1),0)
277 277 a.a = 10
278 278 self.assert_(('a',0,10) in a._notify1)
279 279
280 280 class B(A):
281 281 b = Float
282 282 _notify2 = []
283 283 def _b_changed(self, name, old, new):
284 284 self._notify2.append((name, old, new))
285 285
286 286 b = B()
287 287 b.a = 10
288 288 b.b = 10.0
289 289 self.assert_(('a',0,10) in b._notify1)
290 290 self.assert_(('b',0.0,10.0) in b._notify2)
291 291
292 292 def test_notify_args(self):
293 293
294 294 def callback0():
295 295 self.cb = ()
296 296 def callback1(name):
297 297 self.cb = (name,)
298 298 def callback2(name, new):
299 299 self.cb = (name, new)
300 300 def callback3(name, old, new):
301 301 self.cb = (name, old, new)
302 302
303 303 class A(HasTraitlets):
304 304 a = Int
305 305
306 306 a = A()
307 307 a.on_traitlet_change(callback0, 'a')
308 308 a.a = 10
309 309 self.assertEquals(self.cb,())
310 310 a.on_traitlet_change(callback0, 'a', remove=True)
311 311
312 312 a.on_traitlet_change(callback1, 'a')
313 313 a.a = 100
314 314 self.assertEquals(self.cb,('a',))
315 315 a.on_traitlet_change(callback1, 'a', remove=True)
316 316
317 317 a.on_traitlet_change(callback2, 'a')
318 318 a.a = 1000
319 319 self.assertEquals(self.cb,('a',1000))
320 320 a.on_traitlet_change(callback2, 'a', remove=True)
321 321
322 322 a.on_traitlet_change(callback3, 'a')
323 323 a.a = 10000
324 324 self.assertEquals(self.cb,('a',1000,10000))
325 325 a.on_traitlet_change(callback3, 'a', remove=True)
326 326
327 327 self.assertEquals(len(a._traitlet_notifiers['a']),0)
328 328
329 329
330 330 class TestHasTraitlets(TestCase):
331 331
332 332 def test_traitlet_names(self):
333 333 class A(HasTraitlets):
334 334 i = Int
335 335 f = Float
336 336 a = A()
337 337 self.assertEquals(a.traitlet_names(),['i','f'])
338 338
339 339 def test_traitlet_metadata(self):
340 340 class A(HasTraitlets):
341 341 i = Int(config_key='MY_VALUE')
342 342 a = A()
343 343 self.assertEquals(a.traitlet_metadata('i','config_key'), 'MY_VALUE')
344 344
345 345 def test_traitlets(self):
346 346 class A(HasTraitlets):
347 347 i = Int
348 348 f = Float
349 349 a = A()
350 350 self.assertEquals(a.traitlets(), dict(i=A.i, f=A.f))
351 351
352 352 def test_traitlets_metadata(self):
353 353 class A(HasTraitlets):
354 354 i = Int(config_key='VALUE1', other_thing='VALUE2')
355 355 f = Float(config_key='VALUE3', other_thing='VALUE2')
356 356 a = A()
357 # traitlets = a.traitlets(config_key=lambda v: True)
358 # self.assertEquals(traitlets, dict(i=A.i, f=A.f))
357 self.assertEquals(a.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 360 traitlets = a.traitlets(config_key='VALUE1', other_thing='VALUE2')
360 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 366 # Tests for specific traitlet types
364 367 #-----------------------------------------------------------------------------
365 368
366 369
367 370 class TestType(TestCase):
368 371
369 372 def test_default(self):
370 373
371 374 class B(object): pass
372 375 class A(HasTraitlets):
373 376 klass = Type
374 377
375 378 a = A()
376 379 self.assertEquals(a.klass, None)
377 380 a.klass = B
378 381 self.assertEquals(a.klass, B)
379 382 self.assertRaises(TraitletError, setattr, a, 'klass', 10)
380 383
381 384 def test_value(self):
382 385
383 386 class B(object): pass
384 387 class C(object): pass
385 388 class A(HasTraitlets):
386 389 klass = Type(B)
387 390
388 391 a = A()
389 392 self.assertEquals(a.klass, B)
390 393 self.assertRaises(TraitletError, setattr, a, 'klass', C)
391 394 self.assertRaises(TraitletError, setattr, a, 'klass', object)
392 395 a.klass = B
393 396
394 397 def test_allow_none(self):
395 398
396 399 class B(object): pass
397 400 class C(B): pass
398 401 class A(HasTraitlets):
399 402 klass = Type(B, allow_none=False)
400 403
401 404 a = A()
402 405 self.assertEquals(a.klass, B)
403 406 self.assertRaises(TraitletError, setattr, a, 'klass', None)
404 407 a.klass = C
405 408 self.assertEquals(a.klass, C)
406 409
407 410 def test_validate_klass(self):
408 411
409 412 def inner():
410 413 class A(HasTraitlets):
411 414 klass = Type('no strings allowed')
412 415
413 416 self.assertRaises(TraitletError, inner)
414 417
415 418 def test_validate_default(self):
416 419
417 420 class B(object): pass
418 421 class A(HasTraitlets):
419 422 klass = Type('bad default', B)
420 423
421 424 self.assertRaises(TraitletError, A)
422 425
423 426 class C(HasTraitlets):
424 427 klass = Type(None, B, allow_none=False)
425 428
426 429 self.assertRaises(TraitletError, C)
427 430
428 431 class TestInstance(TestCase):
429 432
430 433 def test_basic(self):
431 434 class Foo(object): pass
432 435 class Bar(Foo): pass
433 436 class Bah(object): pass
434 437
435 438 class A(HasTraitlets):
436 439 inst = Instance(Foo)
437 440
438 441 a = A()
439 442 self.assert_(a.inst is None)
440 443 a.inst = Foo()
441 444 self.assert_(isinstance(a.inst, Foo))
442 445 a.inst = Bar()
443 446 self.assert_(isinstance(a.inst, Foo))
444 447 self.assertRaises(TraitletError, setattr, a, 'inst', Foo)
445 448 self.assertRaises(TraitletError, setattr, a, 'inst', Bar)
446 449 self.assertRaises(TraitletError, setattr, a, 'inst', Bah())
447 450
448 451 def test_unique_default_value(self):
449 452 class Foo(object): pass
450 453 class A(HasTraitlets):
451 454 inst = Instance(Foo,(),{})
452 455
453 456 a = A()
454 457 b = A()
455 458 self.assert_(a.inst is not b.inst)
456 459
457 460 def test_args_kw(self):
458 461 class Foo(object):
459 462 def __init__(self, c): self.c = c
460 463 class Bar(object): pass
461 464 class Bah(object):
462 465 def __init__(self, c, d):
463 466 self.c = c; self.d = d
464 467
465 468 class A(HasTraitlets):
466 469 inst = Instance(Foo, (10,))
467 470 a = A()
468 471 self.assertEquals(a.inst.c, 10)
469 472
470 473 class B(HasTraitlets):
471 474 inst = Instance(Bah, args=(10,), kw=dict(d=20))
472 475 b = B()
473 476 self.assertEquals(b.inst.c, 10)
474 477 self.assertEquals(b.inst.d, 20)
475 478
476 479 class C(HasTraitlets):
477 480 inst = Instance(Foo)
478 481 c = C()
479 482 self.assert_(c.inst is None)
480 483
481 484 def test_bad_default(self):
482 485 class Foo(object): pass
483 486
484 487 class A(HasTraitlets):
485 488 inst = Instance(Foo, allow_none=False)
486 489
487 490 self.assertRaises(TraitletError, A)
488 491
489 492 def test_instance(self):
490 493 class Foo(object): pass
491 494
492 495 def inner():
493 496 class A(HasTraitlets):
494 497 inst = Instance(Foo())
495 498
496 499 self.assertRaises(TraitletError, inner)
497 500
498 501
499 502 class TestThis(TestCase):
500 503
501 504 def test_this_class(self):
502 505 class Foo(HasTraitlets):
503 506 this = This
504 507
505 508 f = Foo()
506 509 self.assertEquals(f.this, None)
507 510 g = Foo()
508 511 f.this = g
509 512 self.assertEquals(f.this, g)
510 513 self.assertRaises(TraitletError, setattr, f, 'this', 10)
511 514
512 515 def test_this_inst(self):
513 516 class Foo(HasTraitlets):
514 517 this = This()
515 518
516 519 f = Foo()
517 520 f.this = Foo()
518 521 self.assert_(isinstance(f.this, Foo))
519 522
520 523 def test_subclass(self):
521 524 class Foo(HasTraitlets):
522 525 t = This()
523 526 class Bar(Foo):
524 527 pass
525 528 f = Foo()
526 529 b = Bar()
527 530 f.t = b
528 531 b.t = f
529 532 self.assertEquals(f.t, b)
530 533 self.assertEquals(b.t, f)
531 534
532 535 def test_subclass_override(self):
533 536 class Foo(HasTraitlets):
534 537 t = This()
535 538 class Bar(Foo):
536 539 t = This()
537 540 f = Foo()
538 541 b = Bar()
539 542 f.t = b
540 543 self.assertEquals(f.t, b)
541 544 self.assertRaises(TraitletError, setattr, b, 't', f)
542 545
543 546 class TraitletTestBase(TestCase):
544 547 """A best testing class for basic traitlet types."""
545 548
546 549 def assign(self, value):
547 550 self.obj.value = value
548 551
549 552 def coerce(self, value):
550 553 return value
551 554
552 555 def test_good_values(self):
553 556 if hasattr(self, '_good_values'):
554 557 for value in self._good_values:
555 558 self.assign(value)
556 559 self.assertEquals(self.obj.value, self.coerce(value))
557 560
558 561 def test_bad_values(self):
559 562 if hasattr(self, '_bad_values'):
560 563 for value in self._bad_values:
561 564 self.assertRaises(TraitletError, self.assign, value)
562 565
563 566 def test_default_value(self):
564 567 if hasattr(self, '_default_value'):
565 568 self.assertEquals(self._default_value, self.obj.value)
566 569
567 570
568 571 class AnyTraitlet(HasTraitlets):
569 572
570 573 value = Any
571 574
572 575 class AnyTraitTest(TraitletTestBase):
573 576
574 577 obj = AnyTraitlet()
575 578
576 579 _default_value = None
577 580 _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j]
578 581 _bad_values = []
579 582
580 583
581 584 class IntTraitlet(HasTraitlets):
582 585
583 586 value = Int(99)
584 587
585 588 class TestInt(TraitletTestBase):
586 589
587 590 obj = IntTraitlet()
588 591 _default_value = 99
589 592 _good_values = [10, -10]
590 593 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None, 1j, 10L,
591 594 -10L, 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', u'10L',
592 595 u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', u'-10']
593 596
594 597
595 598 class LongTraitlet(HasTraitlets):
596 599
597 600 value = Long(99L)
598 601
599 602 class TestLong(TraitletTestBase):
600 603
601 604 obj = LongTraitlet()
602 605
603 606 _default_value = 99L
604 607 _good_values = [10, -10, 10L, -10L]
605 608 _bad_values = ['ten', u'ten', [10], [10l], {'ten': 10},(10,),(10L,),
606 609 None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1',
607 610 '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1',
608 611 u'-10.1']
609 612
610 613
611 614 class FloatTraitlet(HasTraitlets):
612 615
613 616 value = Float(99.0)
614 617
615 618 class TestFloat(TraitletTestBase):
616 619
617 620 obj = FloatTraitlet()
618 621
619 622 _default_value = 99.0
620 623 _good_values = [10, -10, 10.1, -10.1]
621 624 _bad_values = [10L, -10L, 'ten', u'ten', [10], {'ten': 10},(10,), None,
622 625 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10',
623 626 u'-10', u'10L', u'-10L', u'10.1', u'-10.1']
624 627
625 628
626 629 class ComplexTraitlet(HasTraitlets):
627 630
628 631 value = Complex(99.0-99.0j)
629 632
630 633 class TestComplex(TraitletTestBase):
631 634
632 635 obj = ComplexTraitlet()
633 636
634 637 _default_value = 99.0-99.0j
635 638 _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j,
636 639 10.1j, 10.1+10.1j, 10.1-10.1j]
637 640 _bad_values = [10L, -10L, u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None]
638 641
639 642
640 643 class StringTraitlet(HasTraitlets):
641 644
642 645 value = Str('string')
643 646
644 647 class TestString(TraitletTestBase):
645 648
646 649 obj = StringTraitlet()
647 650
648 651 _default_value = 'string'
649 652 _good_values = ['10', '-10', '10L',
650 653 '-10L', '10.1', '-10.1', 'string']
651 654 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j, [10],
652 655 ['ten'],{'ten': 10},(10,), None, u'string']
653 656
654 657
655 658 class UnicodeTraitlet(HasTraitlets):
656 659
657 660 value = Unicode(u'unicode')
658 661
659 662 class TestUnicode(TraitletTestBase):
660 663
661 664 obj = UnicodeTraitlet()
662 665
663 666 _default_value = u'unicode'
664 667 _good_values = ['10', '-10', '10L', '-10L', '10.1',
665 668 '-10.1', '', u'', 'string', u'string', ]
666 669 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j,
667 670 [10], ['ten'], [u'ten'], {'ten': 10},(10,), None]
@@ -1,858 +1,861 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 A lightweight Traits like module.
5 5
6 6 This is designed to provide a lightweight, simple, pure Python version of
7 7 many of the capabilities of enthought.traits. This includes:
8 8
9 9 * Validation
10 10 * Type specification with defaults
11 11 * Static and dynamic notification
12 12 * Basic predefined types
13 13 * An API that is similar to enthought.traits
14 14
15 15 We don't support:
16 16
17 17 * Delegation
18 18 * Automatic GUI generation
19 19 * A full set of trait types. Most importantly, we don't provide container
20 20 traitlets (list, dict, tuple) that can trigger notifications if their
21 21 contents change.
22 22 * API compatibility with enthought.traits
23 23
24 24 There are also some important difference in our design:
25 25
26 26 * enthought.traits does not validate default values. We do.
27 27
28 28 We choose to create this module because we need these capabilities, but
29 29 we need them to be pure Python so they work in all Python implementations,
30 30 including Jython and IronPython.
31 31
32 32 Authors:
33 33
34 34 * Brian Granger
35 35 * Enthought, Inc. Some of the code in this file comes from enthought.traits
36 36 and is licensed under the BSD license. Also, many of the ideas also come
37 37 from enthought.traits even though our implementation is very different.
38 38 """
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Copyright (C) 2008-2009 The IPython Development Team
42 42 #
43 43 # Distributed under the terms of the BSD License. The full license is in
44 44 # the file COPYING, distributed as part of this software.
45 45 #-----------------------------------------------------------------------------
46 46
47 47 #-----------------------------------------------------------------------------
48 48 # Imports
49 49 #-----------------------------------------------------------------------------
50 50
51 51
52 52 import inspect
53 53 import sys
54 54 import types
55 55 from types import InstanceType, ClassType, FunctionType
56 56
57 57 ClassTypes = (ClassType, type)
58 58
59 59 #-----------------------------------------------------------------------------
60 60 # Basic classes
61 61 #-----------------------------------------------------------------------------
62 62
63 63
64 64 class NoDefaultSpecified ( object ): pass
65 65 NoDefaultSpecified = NoDefaultSpecified()
66 66
67 67
68 68 class Undefined ( object ): pass
69 69 Undefined = Undefined()
70 70
71 71
72 72 class TraitletError(Exception):
73 73 pass
74 74
75 75
76 76 #-----------------------------------------------------------------------------
77 77 # Utilities
78 78 #-----------------------------------------------------------------------------
79 79
80 80
81 81 def class_of ( object ):
82 82 """ Returns a string containing the class name of an object with the
83 83 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
84 84 'a PlotValue').
85 85 """
86 86 if isinstance( object, basestring ):
87 87 return add_article( object )
88 88
89 89 return add_article( object.__class__.__name__ )
90 90
91 91
92 92 def add_article ( name ):
93 93 """ Returns a string containing the correct indefinite article ('a' or 'an')
94 94 prefixed to the specified string.
95 95 """
96 96 if name[:1].lower() in 'aeiou':
97 97 return 'an ' + name
98 98
99 99 return 'a ' + name
100 100
101 101
102 102 def repr_type(obj):
103 103 """ Return a string representation of a value and its type for readable
104 104 error messages.
105 105 """
106 106 the_type = type(obj)
107 107 if the_type is InstanceType:
108 108 # Old-style class.
109 109 the_type = obj.__class__
110 110 msg = '%r %r' % (obj, the_type)
111 111 return msg
112 112
113 113
114 114 def parse_notifier_name(name):
115 115 """Convert the name argument to a list of names.
116 116
117 117 Examples
118 118 --------
119 119
120 120 >>> parse_notifier_name('a')
121 121 ['a']
122 122 >>> parse_notifier_name(['a','b'])
123 123 ['a', 'b']
124 124 >>> parse_notifier_name(None)
125 125 ['anytraitlet']
126 126 """
127 127 if isinstance(name, str):
128 128 return [name]
129 129 elif name is None:
130 130 return ['anytraitlet']
131 131 elif isinstance(name, (list, tuple)):
132 132 for n in name:
133 133 assert isinstance(n, str), "names must be strings"
134 134 return name
135 135
136 136
137 137 class _SimpleTest:
138 138 def __init__ ( self, value ): self.value = value
139 139 def __call__ ( self, test ):
140 140 print test, self.value
141 141 return test == self.value
142 142 def __repr__(self):
143 143 return "<SimpleTest(%r)" % self.value
144 144 def __str__(self):
145 145 return self.__repr__()
146 146
147 147
148 148 #-----------------------------------------------------------------------------
149 149 # Base TraitletType for all traitlets
150 150 #-----------------------------------------------------------------------------
151 151
152 152
153 153 class TraitletType(object):
154 154 """A base class for all traitlet descriptors.
155 155
156 156 Notes
157 157 -----
158 158 Our implementation of traitlets is based on Python's descriptor
159 159 prototol. This class is the base class for all such descriptors. The
160 160 only magic we use is a custom metaclass for the main :class:`HasTraitlets`
161 161 class that does the following:
162 162
163 163 1. Sets the :attr:`name` attribute of every :class:`TraitletType`
164 164 instance in the class dict to the name of the attribute.
165 165 2. Sets the :attr:`this_class` attribute of every :class:`TraitletType`
166 166 instance in the class dict to the *class* that declared the traitlet.
167 167 This is used by the :class:`This` traitlet to allow subclasses to
168 168 accept superclasses for :class:`This` values.
169 169 """
170 170
171 171
172 172 metadata = {}
173 173 default_value = Undefined
174 174 info_text = 'any value'
175 175
176 176 def __init__(self, default_value=NoDefaultSpecified, **metadata):
177 177 """Create a TraitletType.
178 178 """
179 179 if default_value is not NoDefaultSpecified:
180 180 self.default_value = default_value
181 181
182 182 if len(metadata) > 0:
183 183 if len(self.metadata) > 0:
184 184 self._metadata = self.metadata.copy()
185 185 self._metadata.update(metadata)
186 186 else:
187 187 self._metadata = metadata
188 188 else:
189 189 self._metadata = self.metadata
190 190
191 191 self.init()
192 192
193 193 def init(self):
194 194 pass
195 195
196 196 def get_default_value(self):
197 197 """Create a new instance of the default value."""
198 198 dv = self.default_value
199 199 return dv
200 200
201 201 def set_default_value(self, obj):
202 202 dv = self.get_default_value()
203 203 newdv = self._validate(obj, dv)
204 204 obj._traitlet_values[self.name] = newdv
205 205
206 206
207 207 def __get__(self, obj, cls=None):
208 208 """Get the value of the traitlet by self.name for the instance.
209 209
210 210 Default values are instantiated when :meth:`HasTraitlets.__new__`
211 211 is called. Thus by the time this method gets called either the
212 212 default value or a user defined value (they called :meth:`__set__`)
213 213 is in the :class:`HasTraitlets` instance.
214 214 """
215 215 if obj is None:
216 216 return self
217 217 else:
218 218 try:
219 219 value = obj._traitlet_values[self.name]
220 220 except:
221 221 # HasTraitlets should call set_default_value to populate
222 222 # this. So this should never be reached.
223 223 raise TraitletError('Unexpected error in TraitletType: '
224 224 'default value not set properly')
225 225 else:
226 226 return value
227 227
228 228 def __set__(self, obj, value):
229 229 new_value = self._validate(obj, value)
230 230 old_value = self.__get__(obj)
231 231 if old_value != new_value:
232 232 obj._traitlet_values[self.name] = new_value
233 233 obj._notify_traitlet(self.name, old_value, new_value)
234 234
235 235 def _validate(self, obj, value):
236 236 if hasattr(self, 'validate'):
237 237 return self.validate(obj, value)
238 238 elif hasattr(self, 'is_valid_for'):
239 239 valid = self.is_valid_for(value)
240 240 if valid:
241 241 return value
242 242 else:
243 243 raise TraitletError('invalid value for type: %r' % value)
244 244 elif hasattr(self, 'value_for'):
245 245 return self.value_for(value)
246 246 else:
247 247 return value
248 248
249 249 def info(self):
250 250 return self.info_text
251 251
252 252 def error(self, obj, value):
253 253 if obj is not None:
254 254 e = "The '%s' traitlet of %s instance must be %s, but a value of %s was specified." \
255 255 % (self.name, class_of(obj),
256 256 self.info(), repr_type(value))
257 257 else:
258 258 e = "The '%s' traitlet must be %s, but a value of %r was specified." \
259 259 % (self.name, self.info(), repr_type(value))
260 260 raise TraitletError(e)
261 261
262 262 def get_metadata(self, key):
263 263 return getattr(self, '_metadata', {}).get(key, None)
264 264
265 265 def set_metadata(self, key, value):
266 266 getattr(self, '_metadata', {})[key] = value
267 267
268 268
269 269 #-----------------------------------------------------------------------------
270 270 # The HasTraitlets implementation
271 271 #-----------------------------------------------------------------------------
272 272
273 273
274 274 class MetaHasTraitlets(type):
275 275 """A metaclass for HasTraitlets.
276 276
277 277 This metaclass makes sure that any TraitletType class attributes are
278 278 instantiated and sets their name attribute.
279 279 """
280 280
281 281 def __new__(mcls, name, bases, classdict):
282 282 """Create the HasTraitlets class.
283 283
284 284 This instantiates all TraitletTypes in the class dict and sets their
285 285 :attr:`name` attribute.
286 286 """
287 287 # print "========================="
288 288 # print "MetaHasTraitlets.__new__"
289 289 # print "mcls, ", mcls
290 290 # print "name, ", name
291 291 # print "bases, ", bases
292 292 # print "classdict, ", classdict
293 293 for k,v in classdict.iteritems():
294 294 if isinstance(v, TraitletType):
295 295 v.name = k
296 296 elif inspect.isclass(v):
297 297 if issubclass(v, TraitletType):
298 298 vinst = v()
299 299 vinst.name = k
300 300 classdict[k] = vinst
301 301 return super(MetaHasTraitlets, mcls).__new__(mcls, name, bases, classdict)
302 302
303 303 def __init__(cls, name, bases, classdict):
304 304 """Finish initializing the HasTraitlets class.
305 305
306 306 This sets the :attr:`this_class` attribute of each TraitletType in the
307 307 class dict to the newly created class ``cls``.
308 308 """
309 309 # print "========================="
310 310 # print "MetaHasTraitlets.__init__"
311 311 # print "cls, ", cls
312 312 # print "name, ", name
313 313 # print "bases, ", bases
314 314 # print "classdict, ", classdict
315 315 for k, v in classdict.iteritems():
316 316 if isinstance(v, TraitletType):
317 317 v.this_class = cls
318 318 super(MetaHasTraitlets, cls).__init__(name, bases, classdict)
319 319
320 320 class HasTraitlets(object):
321 321
322 322 __metaclass__ = MetaHasTraitlets
323 323
324 324 def __new__(cls, *args, **kw):
325 325 inst = super(HasTraitlets, cls).__new__(cls, *args, **kw)
326 326 inst._traitlet_values = {}
327 327 inst._traitlet_notifiers = {}
328 328 # Here we tell all the TraitletType instances to set their default
329 329 # values on the instance.
330 330 for key in dir(cls):
331 331 value = getattr(cls, key)
332 332 if isinstance(value, TraitletType):
333 333 value.set_default_value(inst)
334 334 return inst
335 335
336 336 # def __init__(self):
337 337 # self._traitlet_values = {}
338 338 # self._traitlet_notifiers = {}
339 339
340 340 def _notify_traitlet(self, name, old_value, new_value):
341 341
342 342 # First dynamic ones
343 343 callables = self._traitlet_notifiers.get(name,[])
344 344 more_callables = self._traitlet_notifiers.get('anytraitlet',[])
345 345 callables.extend(more_callables)
346 346
347 347 # Now static ones
348 348 try:
349 349 cb = getattr(self, '_%s_changed' % name)
350 350 except:
351 351 pass
352 352 else:
353 353 callables.append(cb)
354 354
355 355 # Call them all now
356 356 for c in callables:
357 357 # Traits catches and logs errors here. I allow them to raise
358 358 if callable(c):
359 359 argspec = inspect.getargspec(c)
360 360 nargs = len(argspec[0])
361 361 # Bound methods have an additional 'self' argument
362 362 # I don't know how to treat unbound methods, but they
363 363 # can't really be used for callbacks.
364 364 if isinstance(c, types.MethodType):
365 365 offset = -1
366 366 else:
367 367 offset = 0
368 368 if nargs + offset == 0:
369 369 c()
370 370 elif nargs + offset == 1:
371 371 c(name)
372 372 elif nargs + offset == 2:
373 373 c(name, new_value)
374 374 elif nargs + offset == 3:
375 375 c(name, old_value, new_value)
376 376 else:
377 377 raise TraitletError('a traitlet changed callback '
378 378 'must have 0-3 arguments.')
379 379 else:
380 380 raise TraitletError('a traitlet changed callback '
381 381 'must be callable.')
382 382
383 383
384 384 def _add_notifiers(self, handler, name):
385 385 if not self._traitlet_notifiers.has_key(name):
386 386 nlist = []
387 387 self._traitlet_notifiers[name] = nlist
388 388 else:
389 389 nlist = self._traitlet_notifiers[name]
390 390 if handler not in nlist:
391 391 nlist.append(handler)
392 392
393 393 def _remove_notifiers(self, handler, name):
394 394 if self._traitlet_notifiers.has_key(name):
395 395 nlist = self._traitlet_notifiers[name]
396 396 try:
397 397 index = nlist.index(handler)
398 398 except ValueError:
399 399 pass
400 400 else:
401 401 del nlist[index]
402 402
403 403 def on_traitlet_change(self, handler, name=None, remove=False):
404 404 """Setup a handler to be called when a traitlet changes.
405 405
406 406 This is used to setup dynamic notifications of traitlet changes.
407 407
408 408 Static handlers can be created by creating methods on a HasTraitlets
409 409 subclass with the naming convention '_[traitletname]_changed'. Thus,
410 410 to create static handler for the traitlet 'a', create the method
411 411 _a_changed(self, name, old, new) (fewer arguments can be used, see
412 412 below).
413 413
414 414 Parameters
415 415 ----------
416 416 handler : callable
417 417 A callable that is called when a traitlet changes. Its
418 418 signature can be handler(), handler(name), handler(name, new)
419 419 or handler(name, old, new).
420 420 name : list, str, None
421 421 If None, the handler will apply to all traitlets. If a list
422 422 of str, handler will apply to all names in the list. If a
423 423 str, the handler will apply just to that name.
424 424 remove : bool
425 425 If False (the default), then install the handler. If True
426 426 then unintall it.
427 427 """
428 428 if remove:
429 429 names = parse_notifier_name(name)
430 430 for n in names:
431 431 self._remove_notifiers(handler, n)
432 432 else:
433 433 names = parse_notifier_name(name)
434 434 for n in names:
435 435 self._add_notifiers(handler, n)
436 436
437 437 def traitlet_names(self, **metadata):
438 438 """Get a list of all the names of this classes traitlets."""
439 439 return self.traitlets(**metadata).keys()
440 440
441 def traitlets(self, **metadata):
441 def traitlets(self, *args, **metadata):
442 442 """Get a list of all the traitlets of this class.
443 443
444 444 The TraitletTypes returned don't know anything about the values
445 445 that the various HasTraitlet's instances are holding.
446 446 """
447 447 traitlets = dict([memb for memb in inspect.getmembers(self.__class__) if \
448 448 isinstance(memb[1], TraitletType)])
449 if len(metadata) == 0:
449 if len(metadata) == 0 and len(args) == 0:
450 450 return traitlets
451 451
452 for meta_name in args:
453 metadata[meta_name] = lambda _: True
454
452 455 for meta_name, meta_eval in metadata.items():
453 456 if type(meta_eval) is not FunctionType:
454 457 metadata[meta_name] = _SimpleTest(meta_eval)
455 458
456 459 result = {}
457 460 for name, traitlet in traitlets.items():
458 461 for meta_name, meta_eval in metadata.items():
459 462 if not meta_eval(traitlet.get_metadata(meta_name)):
460 463 break
461 464 else:
462 465 result[name] = traitlet
463 466
464 467 return result
465 468
466 469 def traitlet_metadata(self, traitletname, key):
467 470 """Get metadata values for traitlet by key."""
468 471 try:
469 472 traitlet = getattr(self.__class__, traitletname)
470 473 except AttributeError:
471 474 raise TraitletError("Class %s does not have a traitlet named %s" %
472 475 (self.__class__.__name__, traitletname))
473 476 else:
474 477 return traitlet.get_metadata(key)
475 478
476 479 #-----------------------------------------------------------------------------
477 480 # Actual TraitletTypes implementations/subclasses
478 481 #-----------------------------------------------------------------------------
479 482
480 483 #-----------------------------------------------------------------------------
481 484 # TraitletTypes subclasses for handling classes and instances of classes
482 485 #-----------------------------------------------------------------------------
483 486
484 487
485 488 class ClassBasedTraitletType(TraitletType):
486 489 """A traitlet with error reporting for Type, Instance and This."""
487 490
488 491 def error(self, obj, value):
489 492 kind = type(value)
490 493 if kind is InstanceType:
491 494 msg = 'class %s' % value.__class__.__name__
492 495 else:
493 496 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
494 497
495 498 super(ClassBasedTraitletType, self).error(obj, msg)
496 499
497 500
498 501 class Type(ClassBasedTraitletType):
499 502 """A traitlet whose value must be a subclass of a specified class."""
500 503
501 504 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
502 505 """Construct a Type traitlet
503 506
504 507 A Type traitlet specifies that its values must be subclasses of
505 508 a particular class.
506 509
507 510 Parameters
508 511 ----------
509 512 default_value : class
510 513 The default value must be a subclass of klass.
511 514 klass : class, str, None
512 515 Values of this traitlet must be a subclass of klass. The klass
513 516 may be specified in a string like: 'foo.bar.MyClass'.
514 517 allow_none : boolean
515 518 Indicates whether None is allowed as an assignable value. Even if
516 519 ``False``, the default value may be ``None``.
517 520 """
518 521 if default_value is None:
519 522 if klass is None:
520 523 klass = object
521 524 elif klass is None:
522 525 klass = default_value
523 526
524 527 if not inspect.isclass(klass):
525 528 raise TraitletError("A Type traitlet must specify a class.")
526 529
527 530 self.klass = klass
528 531 self._allow_none = allow_none
529 532
530 533 super(Type, self).__init__(default_value, **metadata)
531 534
532 535 def validate(self, obj, value):
533 536 """Validates that the value is a valid object instance."""
534 537 try:
535 538 if issubclass(value, self.klass):
536 539 return value
537 540 except:
538 541 if (value is None) and (self._allow_none):
539 542 return value
540 543
541 544 self.error(obj, value)
542 545
543 546 def info(self):
544 547 """ Returns a description of the trait."""
545 548 klass = self.klass.__name__
546 549 result = 'a subclass of ' + klass
547 550 if self._allow_none:
548 551 return result + ' or None'
549 552 return result
550 553
551 554
552 555 class DefaultValueGenerator(object):
553 556 """A class for generating new default value instances."""
554 557
555 558 def __init__(self, klass, *args, **kw):
556 559 self.klass = klass
557 560 self.args = args
558 561 self.kw = kw
559 562
560 563 def generate(self):
561 564 return self.klass(*self.args, **self.kw)
562 565
563 566
564 567 class Instance(ClassBasedTraitletType):
565 568 """A trait whose value must be an instance of a specified class.
566 569
567 570 The value can also be an instance of a subclass of the specified class.
568 571 """
569 572
570 573 def __init__(self, klass=None, args=None, kw=None,
571 574 allow_none=True, **metadata ):
572 575 """Construct an Instance traitlet.
573 576
574 577 This traitlet allows values that are instances of a particular
575 578 class or its sublclasses. Our implementation is quite different
576 579 from that of enthough.traits as we don't allow instances to be used
577 580 for klass and we handle the ``args`` and ``kw`` arguments differently.
578 581
579 582 Parameters
580 583 ----------
581 584 klass : class
582 585 The class that forms the basis for the traitlet. Instances
583 586 and strings are not allowed.
584 587 args : tuple
585 588 Positional arguments for generating the default value.
586 589 kw : dict
587 590 Keyword arguments for generating the default value.
588 591 allow_none : bool
589 592 Indicates whether None is allowed as a value.
590 593
591 594 Default Value
592 595 -------------
593 596 If both ``args`` and ``kw`` are None, then the default value is None.
594 597 If ``args`` is a tuple and ``kw`` is a dict, then the default is
595 598 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
596 599 not (but not both), None is replace by ``()`` or ``{}``.
597 600 """
598 601
599 602 self._allow_none = allow_none
600 603
601 604 if (klass is None) or (not inspect.isclass(klass)):
602 605 raise TraitletError('The klass argument must be a class'
603 606 ' you gave: %r' % klass)
604 607 self.klass = klass
605 608
606 609 # self.klass is a class, so handle default_value
607 610 if args is None and kw is None:
608 611 default_value = None
609 612 else:
610 613 if args is None:
611 614 # kw is not None
612 615 args = ()
613 616 elif kw is None:
614 617 # args is not None
615 618 kw = {}
616 619
617 620 if not isinstance(kw, dict):
618 621 raise TraitletError("The 'kw' argument must be a dict or None.")
619 622 if not isinstance(args, tuple):
620 623 raise TraitletError("The 'args' argument must be a tuple or None.")
621 624
622 625 default_value = DefaultValueGenerator(self.klass, *args, **kw)
623 626
624 627 super(Instance, self).__init__(default_value, **metadata)
625 628
626 629 def validate(self, obj, value):
627 630 if value is None:
628 631 if self._allow_none:
629 632 return value
630 633 self.error(obj, value)
631 634
632 635 if isinstance(value, self.klass):
633 636 return value
634 637 else:
635 638 self.error(obj, value)
636 639
637 640 def info(self):
638 641 klass = self.klass.__name__
639 642 result = class_of(klass)
640 643 if self._allow_none:
641 644 return result + ' or None'
642 645
643 646 return result
644 647
645 648 def get_default_value(self):
646 649 """Instantiate a default value instance.
647 650
648 651 This is called when the containing HasTraitlets classes'
649 652 :meth:`__new__` method is called to ensure that a unique instance
650 653 is created for each HasTraitlets instance.
651 654 """
652 655 dv = self.default_value
653 656 if isinstance(dv, DefaultValueGenerator):
654 657 return dv.generate()
655 658 else:
656 659 return dv
657 660
658 661
659 662 class This(ClassBasedTraitletType):
660 663 """A traitlet for instances of the class containing this trait.
661 664
662 665 Because how how and when class bodies are executed, the ``This``
663 666 traitlet can only have a default value of None. This, and because we
664 667 always validate default values, ``allow_none`` is *always* true.
665 668 """
666 669
667 670 info_text = 'an instance of the same type as the receiver or None'
668 671
669 672 def __init__(self, **metadata):
670 673 super(This, self).__init__(None, **metadata)
671 674
672 675 def validate(self, obj, value):
673 676 # What if value is a superclass of obj.__class__? This is
674 677 # complicated if it was the superclass that defined the This
675 678 # traitlet.
676 679 if isinstance(value, self.this_class) or (value is None):
677 680 return value
678 681 else:
679 682 self.error(obj, value)
680 683
681 684
682 685 #-----------------------------------------------------------------------------
683 686 # Basic TraitletTypes implementations/subclasses
684 687 #-----------------------------------------------------------------------------
685 688
686 689
687 690 class Any(TraitletType):
688 691 default_value = None
689 692 info_text = 'any value'
690 693
691 694
692 695 class Int(TraitletType):
693 696 """A integer traitlet."""
694 697
695 698 evaluate = int
696 699 default_value = 0
697 700 info_text = 'an integer'
698 701
699 702 def validate(self, obj, value):
700 703 if isinstance(value, int):
701 704 return value
702 705 self.error(obj, value)
703 706
704 707 class CInt(Int):
705 708 """A casting version of the int traitlet."""
706 709
707 710 def validate(self, obj, value):
708 711 try:
709 712 return int(value)
710 713 except:
711 714 self.error(obj, value)
712 715
713 716
714 717 class Long(TraitletType):
715 718 """A long integer traitlet."""
716 719
717 720 evaluate = long
718 721 default_value = 0L
719 722 info_text = 'a long'
720 723
721 724 def validate(self, obj, value):
722 725 if isinstance(value, long):
723 726 return value
724 727 if isinstance(value, int):
725 728 return long(value)
726 729 self.error(obj, value)
727 730
728 731
729 732 class CLong(Long):
730 733 """A casting version of the long integer traitlet."""
731 734
732 735 def validate(self, obj, value):
733 736 try:
734 737 return long(value)
735 738 except:
736 739 self.error(obj, value)
737 740
738 741
739 742 class Float(TraitletType):
740 743 """A float traitlet."""
741 744
742 745 evaluate = float
743 746 default_value = 0.0
744 747 info_text = 'a float'
745 748
746 749 def validate(self, obj, value):
747 750 if isinstance(value, float):
748 751 return value
749 752 if isinstance(value, int):
750 753 return float(value)
751 754 self.error(obj, value)
752 755
753 756
754 757 class CFloat(Float):
755 758 """A casting version of the float traitlet."""
756 759
757 760 def validate(self, obj, value):
758 761 try:
759 762 return float(value)
760 763 except:
761 764 self.error(obj, value)
762 765
763 766 class Complex(TraitletType):
764 767 """A traitlet for complex numbers."""
765 768
766 769 evaluate = complex
767 770 default_value = 0.0 + 0.0j
768 771 info_text = 'a complex number'
769 772
770 773 def validate(self, obj, value):
771 774 if isinstance(value, complex):
772 775 return value
773 776 if isinstance(value, (float, int)):
774 777 return complex(value)
775 778 self.error(obj, value)
776 779
777 780
778 781 class CComplex(Complex):
779 782 """A casting version of the complex number traitlet."""
780 783
781 784 def validate (self, obj, value):
782 785 try:
783 786 return complex(value)
784 787 except:
785 788 self.error(obj, value)
786 789
787 790
788 791 class Str(TraitletType):
789 792 """A traitlet for strings."""
790 793
791 794 evaluate = lambda x: x
792 795 default_value = ''
793 796 info_text = 'a string'
794 797
795 798 def validate(self, obj, value):
796 799 if isinstance(value, str):
797 800 return value
798 801 self.error(obj, value)
799 802
800 803
801 804 class CStr(Str):
802 805 """A casting version of the string traitlet."""
803 806
804 807 def validate(self, obj, value):
805 808 try:
806 809 return str(value)
807 810 except:
808 811 try:
809 812 return unicode(value)
810 813 except:
811 814 self.error(obj, value)
812 815
813 816
814 817 class Unicode(TraitletType):
815 818 """A traitlet for unicode strings."""
816 819
817 820 evaluate = unicode
818 821 default_value = u''
819 822 info_text = 'a unicode string'
820 823
821 824 def validate(self, obj, value):
822 825 if isinstance(value, unicode):
823 826 return value
824 827 if isinstance(value, str):
825 828 return unicode(value)
826 829 self.error(obj, value)
827 830
828 831
829 832 class CUnicode(Unicode):
830 833 """A casting version of the unicode traitlet."""
831 834
832 835 def validate(self, obj, value):
833 836 try:
834 837 return unicode(value)
835 838 except:
836 839 self.error(obj, value)
837 840
838 841
839 842 class Bool(TraitletType):
840 843 """A boolean (True, False) traitlet."""
841 844 evaluate = bool
842 845 default_value = False
843 846 info_text = 'a boolean'
844 847
845 848 def validate(self, obj, value):
846 849 if isinstance(value, bool):
847 850 return value
848 851 self.error(obj, value)
849 852
850 853
851 854 class CBool(Bool):
852 855 """A casting version of the boolean traitlet."""
853 856
854 857 def validate(self, obj, value):
855 858 try:
856 859 return bool(value)
857 860 except:
858 861 self.error(obj, value) No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now