##// END OF EJS Templates
Replacing some .items() calls with .iteritems() for cleaner conversion with 2to3.
Thomas Kluyver -
Show More
@@ -1,54 +1,54 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 IPython.
5 5
6 6 IPython is a set of tools for interactive and exploratory computing in Python.
7 7 """
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2008-2009 The IPython Development Team
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 # Imports
17 17 #-----------------------------------------------------------------------------
18 18 from __future__ import absolute_import
19 19
20 20 import os
21 21 import sys
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Setup everything
25 25 #-----------------------------------------------------------------------------
26 26
27 27 if sys.version[0:3] < '2.6':
28 28 raise ImportError('Python Version 2.6 or above is required for IPython.')
29 29
30 30
31 31 # Make it easy to import extensions - they are always directly on pythonpath.
32 32 # Therefore, non-IPython modules can be added to extensions directory.
33 33 # This should probably be in ipapp.py.
34 34 sys.path.append(os.path.join(os.path.dirname(__file__), "extensions"))
35 35
36 36 #-----------------------------------------------------------------------------
37 37 # Setup the top level names
38 38 #-----------------------------------------------------------------------------
39 39
40 40 from .config.loader import Config
41 41 from .core import release
42 42 from .core.application import Application
43 43 from .frontend.terminal.embed import embed
44 44 from .core.error import TryNext
45 45 from .core.interactiveshell import InteractiveShell
46 46 from .testing import test
47 47
48 48 # Release data
49 49 __author__ = ''
50 for author, email in release.authors.values():
50 for author, email in release.authors.itervalues():
51 51 __author__ += author + ' <' + email + '>\n'
52 52 __license__ = release.license
53 53 __version__ = release.version
54 54 __revision__ = release.revision
@@ -1,139 +1,139 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 A base class for objects that are configurable.
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-2010 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 import datetime
25 25 from weakref import WeakValueDictionary
26 26
27 27 from IPython.utils.importstring import import_item
28 28 from loader import Config
29 29 from IPython.utils.traitlets import HasTraits, Instance
30 30
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Helper classes for Configurables
34 34 #-----------------------------------------------------------------------------
35 35
36 36
37 37 class ConfigurableError(Exception):
38 38 pass
39 39
40 40
41 41 #-----------------------------------------------------------------------------
42 42 # Configurable implementation
43 43 #-----------------------------------------------------------------------------
44 44
45 45
46 46 class Configurable(HasTraits):
47 47
48 48 config = Instance(Config,(),{})
49 49 created = None
50 50
51 51 def __init__(self, **kwargs):
52 52 """Create a conigurable given a config config.
53 53
54 54 Parameters
55 55 ----------
56 56 config : Config
57 57 If this is empty, default values are used. If config is a
58 58 :class:`Config` instance, it will be used to configure the
59 59 instance.
60 60
61 61 Notes
62 62 -----
63 63 Subclasses of Configurable must call the :meth:`__init__` method of
64 64 :class:`Configurable` *before* doing anything else and using
65 65 :func:`super`::
66 66
67 67 class MyConfigurable(Configurable):
68 68 def __init__(self, config=None):
69 69 super(MyConfigurable, self).__init__(config)
70 70 # Then any other code you need to finish initialization.
71 71
72 72 This ensures that instances will be configured properly.
73 73 """
74 74 config = kwargs.pop('config', None)
75 75 if config is not None:
76 76 # We used to deepcopy, but for now we are trying to just save
77 77 # by reference. This *could* have side effects as all components
78 78 # will share config. In fact, I did find such a side effect in
79 79 # _config_changed below. If a config attribute value was a mutable type
80 80 # all instances of a component were getting the same copy, effectively
81 81 # making that a class attribute.
82 82 # self.config = deepcopy(config)
83 83 self.config = config
84 84 # This should go second so individual keyword arguments override
85 85 # the values in config.
86 86 super(Configurable, self).__init__(**kwargs)
87 87 self.created = datetime.datetime.now()
88 88
89 89 #-------------------------------------------------------------------------
90 90 # Static trait notifiations
91 91 #-------------------------------------------------------------------------
92 92
93 93 def _config_changed(self, name, old, new):
94 94 """Update all the class traits having ``config=True`` as metadata.
95 95
96 96 For any class trait with a ``config`` metadata attribute that is
97 97 ``True``, we update the trait with the value of the corresponding
98 98 config entry.
99 99 """
100 100 # Get all traits with a config metadata entry that is True
101 101 traits = self.traits(config=True)
102 102
103 103 # We auto-load config section for this class as well as any parent
104 104 # classes that are Configurable subclasses. This starts with Configurable
105 105 # and works down the mro loading the config for each section.
106 106 section_names = [cls.__name__ for cls in \
107 107 reversed(self.__class__.__mro__) if
108 108 issubclass(cls, Configurable) and issubclass(self.__class__, cls)]
109 109
110 110 for sname in section_names:
111 111 # Don't do a blind getattr as that would cause the config to
112 112 # dynamically create the section with name self.__class__.__name__.
113 113 if new._has_section(sname):
114 114 my_config = new[sname]
115 for k, v in traits.items():
115 for k, v in traits.iteritems():
116 116 # Don't allow traitlets with config=True to start with
117 117 # uppercase. Otherwise, they are confused with Config
118 118 # subsections. But, developers shouldn't have uppercase
119 119 # attributes anyways! (PEP 6)
120 120 if k[0].upper()==k[0] and not k.startswith('_'):
121 121 raise ConfigurableError('Configurable traitlets with '
122 122 'config=True must start with a lowercase so they are '
123 123 'not confused with Config subsections: %s.%s' % \
124 124 (self.__class__.__name__, k))
125 125 try:
126 126 # Here we grab the value from the config
127 127 # If k has the naming convention of a config
128 128 # section, it will be auto created.
129 129 config_value = my_config[k]
130 130 except KeyError:
131 131 pass
132 132 else:
133 133 # print "Setting %s.%s from %s.%s=%r" % \
134 134 # (self.__class__.__name__,k,sname,k,config_value)
135 135 # We have to do a deepcopy here if we don't deepcopy the entire
136 136 # config object. If we don't, a mutable config_value will be
137 137 # shared by all instances, effectively making it a class attribute.
138 138 setattr(self, k, deepcopy(config_value))
139 139
@@ -1,372 +1,373 b''
1 # -*- coding: utf-8 -*-
1 2 # coding: utf-8
2 3 """A simple configuration system.
3 4
4 5 Authors
5 6 -------
6 7 * Brian Granger
7 8 * Fernando Perez
8 9 """
9 10
10 11 #-----------------------------------------------------------------------------
11 12 # Copyright (C) 2008-2009 The IPython Development Team
12 13 #
13 14 # Distributed under the terms of the BSD License. The full license is in
14 15 # the file COPYING, distributed as part of this software.
15 16 #-----------------------------------------------------------------------------
16 17
17 18 #-----------------------------------------------------------------------------
18 19 # Imports
19 20 #-----------------------------------------------------------------------------
20 21
21 22 import __builtin__
22 23 import os
23 24 import sys
24 25
25 26 from IPython.external import argparse
26 27 from IPython.utils.path import filefind
27 28
28 29 #-----------------------------------------------------------------------------
29 30 # Exceptions
30 31 #-----------------------------------------------------------------------------
31 32
32 33
33 34 class ConfigError(Exception):
34 35 pass
35 36
36 37
37 38 class ConfigLoaderError(ConfigError):
38 39 pass
39 40
40 41 #-----------------------------------------------------------------------------
41 42 # Argparse fix
42 43 #-----------------------------------------------------------------------------
43 44
44 45 # Unfortunately argparse by default prints help messages to stderr instead of
45 46 # stdout. This makes it annoying to capture long help screens at the command
46 47 # line, since one must know how to pipe stderr, which many users don't know how
47 48 # to do. So we override the print_help method with one that defaults to
48 49 # stdout and use our class instead.
49 50
50 51 class ArgumentParser(argparse.ArgumentParser):
51 52 """Simple argparse subclass that prints help to stdout by default."""
52 53
53 54 def print_help(self, file=None):
54 55 if file is None:
55 56 file = sys.stdout
56 57 return super(ArgumentParser, self).print_help(file)
57 58
58 59 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
59 60
60 61 #-----------------------------------------------------------------------------
61 62 # Config class for holding config information
62 63 #-----------------------------------------------------------------------------
63 64
64 65
65 66 class Config(dict):
66 67 """An attribute based dict that can do smart merges."""
67 68
68 69 def __init__(self, *args, **kwds):
69 70 dict.__init__(self, *args, **kwds)
70 71 # This sets self.__dict__ = self, but it has to be done this way
71 72 # because we are also overriding __setattr__.
72 73 dict.__setattr__(self, '__dict__', self)
73 74
74 75 def _merge(self, other):
75 76 to_update = {}
76 for k, v in other.items():
77 for k, v in other.iteritems():
77 78 if not self.has_key(k):
78 79 to_update[k] = v
79 80 else: # I have this key
80 81 if isinstance(v, Config):
81 82 # Recursively merge common sub Configs
82 83 self[k]._merge(v)
83 84 else:
84 85 # Plain updates for non-Configs
85 86 to_update[k] = v
86 87
87 88 self.update(to_update)
88 89
89 90 def _is_section_key(self, key):
90 91 if key[0].upper()==key[0] and not key.startswith('_'):
91 92 return True
92 93 else:
93 94 return False
94 95
95 96 def __contains__(self, key):
96 97 if self._is_section_key(key):
97 98 return True
98 99 else:
99 100 return super(Config, self).__contains__(key)
100 101 # .has_key is deprecated for dictionaries.
101 102 has_key = __contains__
102 103
103 104 def _has_section(self, key):
104 105 if self._is_section_key(key):
105 106 if super(Config, self).__contains__(key):
106 107 return True
107 108 return False
108 109
109 110 def copy(self):
110 111 return type(self)(dict.copy(self))
111 112
112 113 def __copy__(self):
113 114 return self.copy()
114 115
115 116 def __deepcopy__(self, memo):
116 117 import copy
117 118 return type(self)(copy.deepcopy(self.items()))
118 119
119 120 def __getitem__(self, key):
120 121 # Because we use this for an exec namespace, we need to delegate
121 122 # the lookup of names in __builtin__ to itself. This means
122 123 # that you can't have section or attribute names that are
123 124 # builtins.
124 125 try:
125 126 return getattr(__builtin__, key)
126 127 except AttributeError:
127 128 pass
128 129 if self._is_section_key(key):
129 130 try:
130 131 return dict.__getitem__(self, key)
131 132 except KeyError:
132 133 c = Config()
133 134 dict.__setitem__(self, key, c)
134 135 return c
135 136 else:
136 137 return dict.__getitem__(self, key)
137 138
138 139 def __setitem__(self, key, value):
139 140 # Don't allow names in __builtin__ to be modified.
140 141 if hasattr(__builtin__, key):
141 142 raise ConfigError('Config variable names cannot have the same name '
142 143 'as a Python builtin: %s' % key)
143 144 if self._is_section_key(key):
144 145 if not isinstance(value, Config):
145 146 raise ValueError('values whose keys begin with an uppercase '
146 147 'char must be Config instances: %r, %r' % (key, value))
147 148 else:
148 149 dict.__setitem__(self, key, value)
149 150
150 151 def __getattr__(self, key):
151 152 try:
152 153 return self.__getitem__(key)
153 154 except KeyError, e:
154 155 raise AttributeError(e)
155 156
156 157 def __setattr__(self, key, value):
157 158 try:
158 159 self.__setitem__(key, value)
159 160 except KeyError, e:
160 161 raise AttributeError(e)
161 162
162 163 def __delattr__(self, key):
163 164 try:
164 165 dict.__delitem__(self, key)
165 166 except KeyError, e:
166 167 raise AttributeError(e)
167 168
168 169
169 170 #-----------------------------------------------------------------------------
170 171 # Config loading classes
171 172 #-----------------------------------------------------------------------------
172 173
173 174
174 175 class ConfigLoader(object):
175 176 """A object for loading configurations from just about anywhere.
176 177
177 178 The resulting configuration is packaged as a :class:`Struct`.
178 179
179 180 Notes
180 181 -----
181 182 A :class:`ConfigLoader` does one thing: load a config from a source
182 183 (file, command line arguments) and returns the data as a :class:`Struct`.
183 184 There are lots of things that :class:`ConfigLoader` does not do. It does
184 185 not implement complex logic for finding config files. It does not handle
185 186 default values or merge multiple configs. These things need to be
186 187 handled elsewhere.
187 188 """
188 189
189 190 def __init__(self):
190 191 """A base class for config loaders.
191 192
192 193 Examples
193 194 --------
194 195
195 196 >>> cl = ConfigLoader()
196 197 >>> config = cl.load_config()
197 198 >>> config
198 199 {}
199 200 """
200 201 self.clear()
201 202
202 203 def clear(self):
203 204 self.config = Config()
204 205
205 206 def load_config(self):
206 207 """Load a config from somewhere, return a :class:`Config` instance.
207 208
208 209 Usually, this will cause self.config to be set and then returned.
209 210 However, in most cases, :meth:`ConfigLoader.clear` should be called
210 211 to erase any previous state.
211 212 """
212 213 self.clear()
213 214 return self.config
214 215
215 216
216 217 class FileConfigLoader(ConfigLoader):
217 218 """A base class for file based configurations.
218 219
219 220 As we add more file based config loaders, the common logic should go
220 221 here.
221 222 """
222 223 pass
223 224
224 225
225 226 class PyFileConfigLoader(FileConfigLoader):
226 227 """A config loader for pure python files.
227 228
228 229 This calls execfile on a plain python file and looks for attributes
229 230 that are all caps. These attribute are added to the config Struct.
230 231 """
231 232
232 233 def __init__(self, filename, path=None):
233 234 """Build a config loader for a filename and path.
234 235
235 236 Parameters
236 237 ----------
237 238 filename : str
238 239 The file name of the config file.
239 240 path : str, list, tuple
240 241 The path to search for the config file on, or a sequence of
241 242 paths to try in order.
242 243 """
243 244 super(PyFileConfigLoader, self).__init__()
244 245 self.filename = filename
245 246 self.path = path
246 247 self.full_filename = ''
247 248 self.data = None
248 249
249 250 def load_config(self):
250 251 """Load the config from a file and return it as a Struct."""
251 252 self.clear()
252 253 self._find_file()
253 254 self._read_file_as_dict()
254 255 self._convert_to_config()
255 256 return self.config
256 257
257 258 def _find_file(self):
258 259 """Try to find the file by searching the paths."""
259 260 self.full_filename = filefind(self.filename, self.path)
260 261
261 262 def _read_file_as_dict(self):
262 263 """Load the config file into self.config, with recursive loading."""
263 264 # This closure is made available in the namespace that is used
264 265 # to exec the config file. This allows users to call
265 266 # load_subconfig('myconfig.py') to load config files recursively.
266 267 # It needs to be a closure because it has references to self.path
267 268 # and self.config. The sub-config is loaded with the same path
268 269 # as the parent, but it uses an empty config which is then merged
269 270 # with the parents.
270 271 def load_subconfig(fname):
271 272 loader = PyFileConfigLoader(fname, self.path)
272 273 try:
273 274 sub_config = loader.load_config()
274 275 except IOError:
275 276 # Pass silently if the sub config is not there. This happens
276 277 # when a user us using a profile, but not the default config.
277 278 pass
278 279 else:
279 280 self.config._merge(sub_config)
280 281
281 282 # Again, this needs to be a closure and should be used in config
282 283 # files to get the config being loaded.
283 284 def get_config():
284 285 return self.config
285 286
286 287 namespace = dict(load_subconfig=load_subconfig, get_config=get_config)
287 288 execfile(self.full_filename, namespace)
288 289
289 290 def _convert_to_config(self):
290 291 if self.data is None:
291 292 ConfigLoaderError('self.data does not exist')
292 293
293 294
294 295 class CommandLineConfigLoader(ConfigLoader):
295 296 """A config loader for command line arguments.
296 297
297 298 As we add more command line based loaders, the common logic should go
298 299 here.
299 300 """
300 301
301 302
302 303 class ArgParseConfigLoader(CommandLineConfigLoader):
303 304
304 305 def __init__(self, argv=None, *parser_args, **parser_kw):
305 306 """Create a config loader for use with argparse.
306 307
307 308 Parameters
308 309 ----------
309 310
310 311 argv : optional, list
311 312 If given, used to read command-line arguments from, otherwise
312 313 sys.argv[1:] is used.
313 314
314 315 parser_args : tuple
315 316 A tuple of positional arguments that will be passed to the
316 317 constructor of :class:`argparse.ArgumentParser`.
317 318
318 319 parser_kw : dict
319 320 A tuple of keyword arguments that will be passed to the
320 321 constructor of :class:`argparse.ArgumentParser`.
321 322 """
322 323 super(CommandLineConfigLoader, self).__init__()
323 324 if argv == None:
324 325 argv = sys.argv[1:]
325 326 self.argv = argv
326 327 self.parser_args = parser_args
327 328 kwargs = dict(argument_default=argparse.SUPPRESS)
328 329 kwargs.update(parser_kw)
329 330 self.parser_kw = kwargs
330 331
331 332 def load_config(self, args=None):
332 333 """Parse command line arguments and return as a Struct.
333 334
334 335 Parameters
335 336 ----------
336 337
337 338 args : optional, list
338 339 If given, a list with the structure of sys.argv[1:] to parse
339 340 arguments from. If not given, the instance's self.argv attribute
340 341 (given at construction time) is used."""
341 342 self.clear()
342 343 if args is None:
343 344 args = self.argv
344 345 self._create_parser()
345 346 self._parse_args(args)
346 347 self._convert_to_config()
347 348 return self.config
348 349
349 350 def get_extra_args(self):
350 351 if hasattr(self, 'extra_args'):
351 352 return self.extra_args
352 353 else:
353 354 return []
354 355
355 356 def _create_parser(self):
356 357 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
357 358 self._add_arguments()
358 359
359 360 def _add_arguments(self):
360 361 raise NotImplementedError("subclasses must implement _add_arguments")
361 362
362 363 def _parse_args(self, args):
363 364 """self.parser->self.parsed_data"""
364 365 self.parsed_data, self.extra_args = self.parser.parse_known_args(args)
365 366
366 367 def _convert_to_config(self):
367 368 """self.parsed_data->self.config"""
368 for k, v in vars(self.parsed_data).items():
369 for k, v in vars(self.parsed_data).iteritems():
369 370 exec_str = 'self.config.' + k + '= v'
370 371 exec exec_str in locals(), globals()
371 372
372 373
@@ -1,346 +1,346 b''
1 1 """Implementations for various useful completers.
2 2
3 3 These are all loaded by default by IPython.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (C) 2010 The IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Stdlib imports
19 19 import glob
20 20 import inspect
21 21 import os
22 22 import re
23 23 import shlex
24 24 import sys
25 25
26 26 # Third-party imports
27 27 from time import time
28 28 from zipimport import zipimporter
29 29
30 30 # Our own imports
31 31 from IPython.core.completer import expand_user, compress_user
32 32 from IPython.core.error import TryNext
33 33
34 34 # FIXME: this should be pulled in with the right call via the component system
35 35 from IPython.core.ipapi import get as get_ipython
36 36
37 37 #-----------------------------------------------------------------------------
38 38 # Globals and constants
39 39 #-----------------------------------------------------------------------------
40 40
41 41 # Time in seconds after which the rootmodules will be stored permanently in the
42 42 # ipython ip.db database (kept in the user's .ipython dir).
43 43 TIMEOUT_STORAGE = 2
44 44
45 45 # Time in seconds after which we give up
46 46 TIMEOUT_GIVEUP = 20
47 47
48 48 # Regular expression for the python import statement
49 49 import_re = re.compile(r'.*(\.so|\.py[cod]?)$')
50 50
51 51 # RE for the ipython %run command (python + ipython scripts)
52 52 magic_run_re = re.compile(r'.*(\.ipy|\.py[w]?)$')
53 53
54 54 #-----------------------------------------------------------------------------
55 55 # Local utilities
56 56 #-----------------------------------------------------------------------------
57 57
58 58 def shlex_split(x):
59 59 """Helper function to split lines into segments.
60 60 """
61 61 # shlex.split raises an exception if there is a syntax error in sh syntax
62 62 # for example if no closing " is found. This function keeps dropping the
63 63 # last character of the line until shlex.split does not raise
64 64 # an exception. It adds end of the line to the result of shlex.split
65 65 #
66 66 # Example:
67 67 # %run "c:/python -> ['%run','"c:/python']
68 68
69 69 # shlex.split has unicode bugs, so encode first to str
70 70 if isinstance(x, unicode):
71 71 x = x.encode(sys.stdin.encoding)
72 72
73 73 endofline = []
74 74 while x != '':
75 75 try:
76 76 comps = shlex.split(x)
77 77 if len(endofline) >= 1:
78 78 comps.append(''.join(endofline))
79 79 return comps
80 80
81 81 except ValueError:
82 82 endofline = [x[-1:]]+endofline
83 83 x = x[:-1]
84 84
85 85 return [''.join(endofline)]
86 86
87 87 def module_list(path):
88 88 """
89 89 Return the list containing the names of the modules available in the given
90 90 folder.
91 91 """
92 92
93 93 if os.path.isdir(path):
94 94 folder_list = os.listdir(path)
95 95 elif path.endswith('.egg'):
96 96 try:
97 97 folder_list = [f for f in zipimporter(path)._files]
98 98 except:
99 99 folder_list = []
100 100 else:
101 101 folder_list = []
102 102
103 103 if not folder_list:
104 104 return []
105 105
106 106 # A few local constants to be used in loops below
107 107 isfile = os.path.isfile
108 108 pjoin = os.path.join
109 109 basename = os.path.basename
110 110
111 111 # Now find actual path matches for packages or modules
112 112 folder_list = [p for p in folder_list
113 113 if isfile(pjoin(path, p,'__init__.py'))
114 114 or import_re.match(p) ]
115 115
116 116 return [basename(p).split('.')[0] for p in folder_list]
117 117
118 118 def get_root_modules():
119 119 """
120 120 Returns a list containing the names of all the modules available in the
121 121 folders of the pythonpath.
122 122 """
123 123 ip = get_ipython()
124 124
125 125 if 'rootmodules' in ip.db:
126 126 return ip.db['rootmodules']
127 127
128 128 t = time()
129 129 store = False
130 130 modules = list(sys.builtin_module_names)
131 131 for path in sys.path:
132 132 modules += module_list(path)
133 133 if time() - t >= TIMEOUT_STORAGE and not store:
134 134 store = True
135 135 print("\nCaching the list of root modules, please wait!")
136 136 print("(This will only be done once - type '%rehashx' to "
137 137 "reset cache!)\n")
138 138 sys.stdout.flush()
139 139 if time() - t > TIMEOUT_GIVEUP:
140 140 print("This is taking too long, we give up.\n")
141 141 ip.db['rootmodules'] = []
142 142 return []
143 143
144 144 modules = set(modules)
145 145 if '__init__' in modules:
146 146 modules.remove('__init__')
147 147 modules = list(modules)
148 148 if store:
149 149 ip.db['rootmodules'] = modules
150 150 return modules
151 151
152 152
153 153 def is_importable(module, attr, only_modules):
154 154 if only_modules:
155 155 return inspect.ismodule(getattr(module, attr))
156 156 else:
157 157 return not(attr[:2] == '__' and attr[-2:] == '__')
158 158
159 159
160 160 def try_import(mod, only_modules=False):
161 161 try:
162 162 m = __import__(mod)
163 163 except:
164 164 return []
165 165 mods = mod.split('.')
166 166 for module in mods[1:]:
167 167 m = getattr(m, module)
168 168
169 169 m_is_init = hasattr(m, '__file__') and '__init__' in m.__file__
170 170
171 171 completions = []
172 172 if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init:
173 173 completions.extend( [attr for attr in dir(m) if
174 174 is_importable(m, attr, only_modules)])
175 175
176 176 completions.extend(getattr(m, '__all__', []))
177 177 if m_is_init:
178 178 completions.extend(module_list(os.path.dirname(m.__file__)))
179 179 completions = set(completions)
180 180 if '__init__' in completions:
181 181 completions.remove('__init__')
182 182 return list(completions)
183 183
184 184
185 185 #-----------------------------------------------------------------------------
186 186 # Completion-related functions.
187 187 #-----------------------------------------------------------------------------
188 188
189 189 def quick_completer(cmd, completions):
190 190 """ Easily create a trivial completer for a command.
191 191
192 192 Takes either a list of completions, or all completions in string (that will
193 193 be split on whitespace).
194 194
195 195 Example::
196 196
197 197 [d:\ipython]|1> import ipy_completers
198 198 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
199 199 [d:\ipython]|3> foo b<TAB>
200 200 bar baz
201 201 [d:\ipython]|3> foo ba
202 202 """
203 203
204 204 if isinstance(completions, basestring):
205 205 completions = completions.split()
206 206
207 207 def do_complete(self, event):
208 208 return completions
209 209
210 210 get_ipython().set_hook('complete_command',do_complete, str_key = cmd)
211 211
212 212
213 213 def module_completion(line):
214 214 """
215 215 Returns a list containing the completion possibilities for an import line.
216 216
217 217 The line looks like this :
218 218 'import xml.d'
219 219 'from xml.dom import'
220 220 """
221 221
222 222 words = line.split(' ')
223 223 nwords = len(words)
224 224
225 225 # from whatever <tab> -> 'import '
226 226 if nwords == 3 and words[0] == 'from':
227 227 return ['import ']
228 228
229 229 # 'from xy<tab>' or 'import xy<tab>'
230 230 if nwords < 3 and (words[0] in ['import','from']) :
231 231 if nwords == 1:
232 232 return get_root_modules()
233 233 mod = words[1].split('.')
234 234 if len(mod) < 2:
235 235 return get_root_modules()
236 236 completion_list = try_import('.'.join(mod[:-1]), True)
237 237 return ['.'.join(mod[:-1] + [el]) for el in completion_list]
238 238
239 239 # 'from xyz import abc<tab>'
240 240 if nwords >= 3 and words[0] == 'from':
241 241 mod = words[1]
242 242 return try_import(mod)
243 243
244 244 #-----------------------------------------------------------------------------
245 245 # Completers
246 246 #-----------------------------------------------------------------------------
247 247 # These all have the func(self, event) signature to be used as custom
248 248 # completers
249 249
250 250 def module_completer(self,event):
251 251 """Give completions after user has typed 'import ...' or 'from ...'"""
252 252
253 253 # This works in all versions of python. While 2.5 has
254 254 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
255 255 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
256 256 # of possibly problematic side effects.
257 257 # This search the folders in the sys.path for available modules.
258 258
259 259 return module_completion(event.line)
260 260
261 261 # FIXME: there's a lot of logic common to the run, cd and builtin file
262 262 # completers, that is currently reimplemented in each.
263 263
264 264 def magic_run_completer(self, event):
265 265 """Complete files that end in .py or .ipy for the %run command.
266 266 """
267 267 comps = shlex_split(event.line)
268 268 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
269 269
270 270 #print("\nev=", event) # dbg
271 271 #print("rp=", relpath) # dbg
272 272 #print('comps=', comps) # dbg
273 273
274 274 lglob = glob.glob
275 275 isdir = os.path.isdir
276 276 relpath, tilde_expand, tilde_val = expand_user(relpath)
277 277
278 278 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*') if isdir(f)]
279 279
280 280 # Find if the user has already typed the first filename, after which we
281 281 # should complete on all files, since after the first one other files may
282 282 # be arguments to the input script.
283 283
284 284 if filter(magic_run_re.match, comps):
285 285 pys = [f.replace('\\','/') for f in lglob('*')]
286 286 else:
287 287 pys = [f.replace('\\','/')
288 288 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
289 289 lglob(relpath + '*.pyw')]
290 290 #print('run comp:', dirs+pys) # dbg
291 291 return [compress_user(p, tilde_expand, tilde_val) for p in dirs+pys]
292 292
293 293
294 294 def cd_completer(self, event):
295 295 """Completer function for cd, which only returns directories."""
296 296 ip = get_ipython()
297 297 relpath = event.symbol
298 298
299 299 #print(event) # dbg
300 300 if event.line.endswith('-b') or ' -b ' in event.line:
301 301 # return only bookmark completions
302 302 bkms = self.db.get('bookmarks', None)
303 303 if bkms:
304 304 return bkms.keys()
305 305 else:
306 306 return []
307 307
308 308 if event.symbol == '-':
309 309 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
310 310 # jump in directory history by number
311 311 fmt = '-%0' + width_dh +'d [%s]'
312 312 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
313 313 if len(ents) > 1:
314 314 return ents
315 315 return []
316 316
317 317 if event.symbol.startswith('--'):
318 318 return ["--" + os.path.basename(d) for d in ip.user_ns['_dh']]
319 319
320 320 # Expand ~ in path and normalize directory separators.
321 321 relpath, tilde_expand, tilde_val = expand_user(relpath)
322 322 relpath = relpath.replace('\\','/')
323 323
324 324 found = []
325 325 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
326 326 if os.path.isdir(f)]:
327 327 if ' ' in d:
328 328 # we don't want to deal with any of that, complex code
329 329 # for this is elsewhere
330 330 raise TryNext
331 331
332 332 found.append(d)
333 333
334 334 if not found:
335 335 if os.path.isdir(relpath):
336 336 return [compress_user(relpath, tilde_expand, tilde_val)]
337 337
338 338 # if no completions so far, try bookmarks
339 bks = self.db.get('bookmarks',{}).keys()
339 bks = self.db.get('bookmarks',{}).iterkeys()
340 340 bkmatches = [s for s in bks if s.startswith(event.symbol)]
341 341 if bkmatches:
342 342 return bkmatches
343 343
344 344 raise TryNext
345 345
346 346 return [compress_user(p, tilde_expand, tilde_val) for p in found]
@@ -1,283 +1,283 b''
1 1 # -*- coding: utf-8 -*-
2 2 """ History related magics and functionality """
3 3
4 4 # Stdlib imports
5 5 import fnmatch
6 6 import os
7 7
8 8 import IPython.utils.io
9 9 from IPython.utils.io import ask_yes_no
10 10 from IPython.utils.warn import warn
11 11 from IPython.core import ipapi
12 12
13 13 def magic_history(self, parameter_s = ''):
14 14 """Print input history (_i<n> variables), with most recent last.
15 15
16 16 %history -> print at most 40 inputs (some may be multi-line)\\
17 17 %history n -> print at most n inputs\\
18 18 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
19 19
20 20 By default, input history is printed without line numbers so it can be
21 21 directly pasted into an editor.
22 22
23 23 With -n, each input's number <n> is shown, and is accessible as the
24 24 automatically generated variable _i<n> as well as In[<n>]. Multi-line
25 25 statements are printed starting at a new line for easy copy/paste.
26 26
27 27 Options:
28 28
29 29 -n: print line numbers for each input.
30 30 This feature is only available if numbered prompts are in use.
31 31
32 32 -o: also print outputs for each input.
33 33
34 34 -p: print classic '>>>' python prompts before each input. This is useful
35 35 for making documentation, and in conjunction with -o, for producing
36 36 doctest-ready output.
37 37
38 38 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
39 39
40 40 -t: print the 'translated' history, as IPython understands it. IPython
41 41 filters your input and converts it all into valid Python source before
42 42 executing it (things like magics or aliases are turned into function
43 43 calls, for example). With this option, you'll see the native history
44 44 instead of the user-entered version: '%cd /' will be seen as
45 45 'get_ipython().magic("%cd /")' instead of '%cd /'.
46 46
47 47 -g: treat the arg as a pattern to grep for in (full) history.
48 48 This includes the "shadow history" (almost all commands ever written).
49 49 Use '%hist -g' to show full shadow history (may be very long).
50 50 In shadow history, every index nuwber starts with 0.
51 51
52 52 -f FILENAME: instead of printing the output to the screen, redirect it to
53 53 the given file. The file is always overwritten, though IPython asks for
54 54 confirmation first if it already exists.
55 55 """
56 56
57 57 if not self.shell.displayhook.do_full_cache:
58 58 print 'This feature is only available if numbered prompts are in use.'
59 59 return
60 60 opts,args = self.parse_options(parameter_s,'gnoptsrf:',mode='list')
61 61
62 62 # Check if output to specific file was requested.
63 63 try:
64 64 outfname = opts['f']
65 65 except KeyError:
66 66 outfile = IPython.utils.io.Term.cout # default
67 67 # We don't want to close stdout at the end!
68 68 close_at_end = False
69 69 else:
70 70 if os.path.exists(outfname):
71 71 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
72 72 print 'Aborting.'
73 73 return
74 74
75 75 outfile = open(outfname,'w')
76 76 close_at_end = True
77 77
78 78 if 't' in opts:
79 79 input_hist = self.shell.input_hist
80 80 elif 'r' in opts:
81 81 input_hist = self.shell.input_hist_raw
82 82 else:
83 83 # Raw history is the default
84 84 input_hist = self.shell.input_hist_raw
85 85
86 86 default_length = 40
87 87 pattern = None
88 88 if 'g' in opts:
89 89 init = 1
90 90 final = len(input_hist)
91 91 parts = parameter_s.split(None, 1)
92 92 if len(parts) == 1:
93 93 parts += '*'
94 94 head, pattern = parts
95 95 pattern = "*" + pattern + "*"
96 96 elif len(args) == 0:
97 97 final = len(input_hist)-1
98 98 init = max(1,final-default_length)
99 99 elif len(args) == 1:
100 100 final = len(input_hist)
101 101 init = max(1, final-int(args[0]))
102 102 elif len(args) == 2:
103 103 init, final = map(int, args)
104 104 else:
105 105 warn('%hist takes 0, 1 or 2 arguments separated by spaces.')
106 106 print >> IPython.utils.io.Term.cout, self.magic_hist.__doc__
107 107 return
108 108
109 109 width = len(str(final))
110 110 line_sep = ['','\n']
111 111 print_nums = 'n' in opts
112 112 print_outputs = 'o' in opts
113 113 pyprompts = 'p' in opts
114 114
115 115 found = False
116 116 if pattern is not None:
117 117 sh = self.shell.shadowhist.all()
118 118 for idx, s in sh:
119 119 if fnmatch.fnmatch(s, pattern):
120 120 print >> outfile, "0%d: %s" %(idx, s.expandtabs(4))
121 121 found = True
122 122
123 123 if found:
124 124 print >> outfile, "==="
125 125 print >> outfile, \
126 126 "shadow history ends, fetch by %rep <number> (must start with 0)"
127 127 print >> outfile, "=== start of normal history ==="
128 128
129 129 for in_num in range(init, final):
130 130 # Print user history with tabs expanded to 4 spaces. The GUI clients
131 131 # use hard tabs for easier usability in auto-indented code, but we want
132 132 # to produce PEP-8 compliant history for safe pasting into an editor.
133 133 inline = input_hist[in_num].expandtabs(4)
134 134
135 135 if pattern is not None and not fnmatch.fnmatch(inline, pattern):
136 136 continue
137 137
138 138 multiline = int(inline.count('\n') > 1)
139 139 if print_nums:
140 140 print >> outfile, \
141 141 '%s:%s' % (str(in_num).ljust(width), line_sep[multiline]),
142 142 if pyprompts:
143 143 print >> outfile, '>>>',
144 144 if multiline:
145 145 lines = inline.splitlines()
146 146 print >> outfile, '\n... '.join(lines)
147 147 print >> outfile, '... '
148 148 else:
149 149 print >> outfile, inline,
150 150 else:
151 151 print >> outfile, inline,
152 152 if print_outputs:
153 153 output = self.shell.output_hist.get(in_num)
154 154 if output is not None:
155 155 print >> outfile, repr(output)
156 156
157 157 if close_at_end:
158 158 outfile.close()
159 159
160 160
161 161 def magic_hist(self, parameter_s=''):
162 162 """Alternate name for %history."""
163 163 return self.magic_history(parameter_s)
164 164
165 165
166 166 def rep_f(self, arg):
167 167 r""" Repeat a command, or get command to input line for editing
168 168
169 169 - %rep (no arguments):
170 170
171 171 Place a string version of last computation result (stored in the special '_'
172 172 variable) to the next input prompt. Allows you to create elaborate command
173 173 lines without using copy-paste::
174 174
175 175 $ l = ["hei", "vaan"]
176 176 $ "".join(l)
177 177 ==> heivaan
178 178 $ %rep
179 179 $ heivaan_ <== cursor blinking
180 180
181 181 %rep 45
182 182
183 183 Place history line 45 to next input prompt. Use %hist to find out the
184 184 number.
185 185
186 186 %rep 1-4 6-7 3
187 187
188 188 Repeat the specified lines immediately. Input slice syntax is the same as
189 189 in %macro and %save.
190 190
191 191 %rep foo
192 192
193 193 Place the most recent line that has the substring "foo" to next input.
194 194 (e.g. 'svn ci -m foobar').
195 195 """
196 196
197 197 opts,args = self.parse_options(arg,'',mode='list')
198 198 if not args:
199 199 self.set_next_input(str(self.shell.user_ns["_"]))
200 200 return
201 201
202 202 if len(args) == 1 and not '-' in args[0]:
203 203 arg = args[0]
204 204 if len(arg) > 1 and arg.startswith('0'):
205 205 # get from shadow hist
206 206 num = int(arg[1:])
207 207 line = self.shell.shadowhist.get(num)
208 208 self.set_next_input(str(line))
209 209 return
210 210 try:
211 211 num = int(args[0])
212 212 self.set_next_input(str(self.shell.input_hist_raw[num]).rstrip())
213 213 return
214 214 except ValueError:
215 215 pass
216 216
217 217 for h in reversed(self.shell.input_hist_raw):
218 218 if 'rep' in h:
219 219 continue
220 220 if fnmatch.fnmatch(h,'*' + arg + '*'):
221 221 self.set_next_input(str(h).rstrip())
222 222 return
223 223
224 224 try:
225 225 lines = self.extract_input_slices(args, True)
226 226 print "lines",lines
227 227 self.runlines(lines)
228 228 except ValueError:
229 229 print "Not found in recent history:", args
230 230
231 231
232 232 _sentinel = object()
233 233
234 234 class ShadowHist(object):
235 235 def __init__(self, db):
236 236 # cmd => idx mapping
237 237 self.curidx = 0
238 238 self.db = db
239 239 self.disabled = False
240 240
241 241 def inc_idx(self):
242 242 idx = self.db.get('shadowhist_idx', 1)
243 243 self.db['shadowhist_idx'] = idx + 1
244 244 return idx
245 245
246 246 def add(self, ent):
247 247 if self.disabled:
248 248 return
249 249 try:
250 250 old = self.db.hget('shadowhist', ent, _sentinel)
251 251 if old is not _sentinel:
252 252 return
253 253 newidx = self.inc_idx()
254 254 #print "new",newidx # dbg
255 255 self.db.hset('shadowhist',ent, newidx)
256 256 except:
257 257 ipapi.get().showtraceback()
258 258 print "WARNING: disabling shadow history"
259 259 self.disabled = True
260 260
261 261 def all(self):
262 262 d = self.db.hdict('shadowhist')
263 items = [(i,s) for (s,i) in d.items()]
263 items = [(i,s) for (s,i) in d.iteritems()]
264 264 items.sort()
265 265 return items
266 266
267 267 def get(self, idx):
268 268 all = self.all()
269 269
270 270 for k, v in all:
271 271 #print k,v
272 272 if k == idx:
273 273 return v
274 274
275 275
276 276 def init_ipython(ip):
277 277 ip.define_magic("rep",rep_f)
278 278 ip.define_magic("hist",magic_hist)
279 279 ip.define_magic("history",magic_history)
280 280
281 281 # XXX - ipy_completers are in quarantine, need to be updated to new apis
282 282 #import ipy_completers
283 283 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
@@ -1,2584 +1,2584 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Main IPython class."""
3 3
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de>
6 6 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
7 7 # Copyright (C) 2008-2010 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 from __future__ import with_statement
18 18 from __future__ import absolute_import
19 19
20 20 import __builtin__
21 21 import __future__
22 22 import abc
23 23 import atexit
24 24 import codeop
25 25 import os
26 26 import re
27 27 import string
28 28 import sys
29 29 import tempfile
30 30 import types
31 31 from contextlib import nested
32 32
33 33 from IPython.config.configurable import Configurable
34 34 from IPython.core import debugger, oinspect
35 35 from IPython.core import history as ipcorehist
36 36 from IPython.core import page
37 37 from IPython.core import prefilter
38 38 from IPython.core import shadowns
39 39 from IPython.core import ultratb
40 40 from IPython.core.alias import AliasManager
41 41 from IPython.core.builtin_trap import BuiltinTrap
42 42 from IPython.core.display_trap import DisplayTrap
43 43 from IPython.core.displayhook import DisplayHook
44 44 from IPython.core.error import TryNext, UsageError
45 45 from IPython.core.extensions import ExtensionManager
46 46 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
47 47 from IPython.core.inputlist import InputList
48 48 from IPython.core.inputsplitter import IPythonInputSplitter
49 49 from IPython.core.logger import Logger
50 50 from IPython.core.magic import Magic
51 51 from IPython.core.payload import PayloadManager
52 52 from IPython.core.plugin import PluginManager
53 53 from IPython.core.prefilter import PrefilterManager, ESC_MAGIC
54 54 from IPython.external.Itpl import ItplNS
55 55 from IPython.utils import PyColorize
56 56 from IPython.utils import io
57 57 from IPython.utils import pickleshare
58 58 from IPython.utils.doctestreload import doctest_reload
59 59 from IPython.utils.io import ask_yes_no, rprint
60 60 from IPython.utils.ipstruct import Struct
61 61 from IPython.utils.path import get_home_dir, get_ipython_dir, HomeDirError
62 62 from IPython.utils.process import system, getoutput
63 63 from IPython.utils.strdispatch import StrDispatch
64 64 from IPython.utils.syspathcontext import prepended_to_syspath
65 65 from IPython.utils.text import num_ini_spaces, format_screen, LSString, SList
66 66 from IPython.utils.traitlets import (Int, Str, CBool, CaselessStrEnum, Enum,
67 67 List, Unicode, Instance, Type)
68 68 from IPython.utils.warn import warn, error, fatal
69 69 import IPython.core.hooks
70 70
71 71 #-----------------------------------------------------------------------------
72 72 # Globals
73 73 #-----------------------------------------------------------------------------
74 74
75 75 # compiled regexps for autoindent management
76 76 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
77 77
78 78 #-----------------------------------------------------------------------------
79 79 # Utilities
80 80 #-----------------------------------------------------------------------------
81 81
82 82 # store the builtin raw_input globally, and use this always, in case user code
83 83 # overwrites it (like wx.py.PyShell does)
84 84 raw_input_original = raw_input
85 85
86 86 def softspace(file, newvalue):
87 87 """Copied from code.py, to remove the dependency"""
88 88
89 89 oldvalue = 0
90 90 try:
91 91 oldvalue = file.softspace
92 92 except AttributeError:
93 93 pass
94 94 try:
95 95 file.softspace = newvalue
96 96 except (AttributeError, TypeError):
97 97 # "attribute-less object" or "read-only attributes"
98 98 pass
99 99 return oldvalue
100 100
101 101
102 102 def no_op(*a, **kw): pass
103 103
104 104 class SpaceInInput(Exception): pass
105 105
106 106 class Bunch: pass
107 107
108 108
109 109 def get_default_colors():
110 110 if sys.platform=='darwin':
111 111 return "LightBG"
112 112 elif os.name=='nt':
113 113 return 'Linux'
114 114 else:
115 115 return 'Linux'
116 116
117 117
118 118 class SeparateStr(Str):
119 119 """A Str subclass to validate separate_in, separate_out, etc.
120 120
121 121 This is a Str based trait that converts '0'->'' and '\\n'->'\n'.
122 122 """
123 123
124 124 def validate(self, obj, value):
125 125 if value == '0': value = ''
126 126 value = value.replace('\\n','\n')
127 127 return super(SeparateStr, self).validate(obj, value)
128 128
129 129 class MultipleInstanceError(Exception):
130 130 pass
131 131
132 132
133 133 #-----------------------------------------------------------------------------
134 134 # Main IPython class
135 135 #-----------------------------------------------------------------------------
136 136
137 137
138 138 class InteractiveShell(Configurable, Magic):
139 139 """An enhanced, interactive shell for Python."""
140 140
141 141 _instance = None
142 142 autocall = Enum((0,1,2), default_value=1, config=True)
143 143 # TODO: remove all autoindent logic and put into frontends.
144 144 # We can't do this yet because even runlines uses the autoindent.
145 145 autoindent = CBool(True, config=True)
146 146 automagic = CBool(True, config=True)
147 147 cache_size = Int(1000, config=True)
148 148 color_info = CBool(True, config=True)
149 149 colors = CaselessStrEnum(('NoColor','LightBG','Linux'),
150 150 default_value=get_default_colors(), config=True)
151 151 debug = CBool(False, config=True)
152 152 deep_reload = CBool(False, config=True)
153 153 displayhook_class = Type(DisplayHook)
154 154 exit_now = CBool(False)
155 155 filename = Str("<ipython console>")
156 156 ipython_dir= Unicode('', config=True) # Set to get_ipython_dir() in __init__
157 157
158 158 # Input splitter, to split entire cells of input into either individual
159 159 # interactive statements or whole blocks.
160 160 input_splitter = Instance('IPython.core.inputsplitter.IPythonInputSplitter',
161 161 (), {})
162 162 logstart = CBool(False, config=True)
163 163 logfile = Str('', config=True)
164 164 logappend = Str('', config=True)
165 165 object_info_string_level = Enum((0,1,2), default_value=0,
166 166 config=True)
167 167 pdb = CBool(False, config=True)
168 168
169 169 pprint = CBool(True, config=True)
170 170 profile = Str('', config=True)
171 171 prompt_in1 = Str('In [\\#]: ', config=True)
172 172 prompt_in2 = Str(' .\\D.: ', config=True)
173 173 prompt_out = Str('Out[\\#]: ', config=True)
174 174 prompts_pad_left = CBool(True, config=True)
175 175 quiet = CBool(False, config=True)
176 176
177 177 # The readline stuff will eventually be moved to the terminal subclass
178 178 # but for now, we can't do that as readline is welded in everywhere.
179 179 readline_use = CBool(True, config=True)
180 180 readline_merge_completions = CBool(True, config=True)
181 181 readline_omit__names = Enum((0,1,2), default_value=0, config=True)
182 182 readline_remove_delims = Str('-/~', config=True)
183 183 readline_parse_and_bind = List([
184 184 'tab: complete',
185 185 '"\C-l": clear-screen',
186 186 'set show-all-if-ambiguous on',
187 187 '"\C-o": tab-insert',
188 188 '"\M-i": " "',
189 189 '"\M-o": "\d\d\d\d"',
190 190 '"\M-I": "\d\d\d\d"',
191 191 '"\C-r": reverse-search-history',
192 192 '"\C-s": forward-search-history',
193 193 '"\C-p": history-search-backward',
194 194 '"\C-n": history-search-forward',
195 195 '"\e[A": history-search-backward',
196 196 '"\e[B": history-search-forward',
197 197 '"\C-k": kill-line',
198 198 '"\C-u": unix-line-discard',
199 199 ], allow_none=False, config=True)
200 200
201 201 # TODO: this part of prompt management should be moved to the frontends.
202 202 # Use custom TraitTypes that convert '0'->'' and '\\n'->'\n'
203 203 separate_in = SeparateStr('\n', config=True)
204 204 separate_out = SeparateStr('', config=True)
205 205 separate_out2 = SeparateStr('', config=True)
206 206 wildcards_case_sensitive = CBool(True, config=True)
207 207 xmode = CaselessStrEnum(('Context','Plain', 'Verbose'),
208 208 default_value='Context', config=True)
209 209
210 210 # Subcomponents of InteractiveShell
211 211 alias_manager = Instance('IPython.core.alias.AliasManager')
212 212 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
213 213 builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap')
214 214 display_trap = Instance('IPython.core.display_trap.DisplayTrap')
215 215 extension_manager = Instance('IPython.core.extensions.ExtensionManager')
216 216 plugin_manager = Instance('IPython.core.plugin.PluginManager')
217 217 payload_manager = Instance('IPython.core.payload.PayloadManager')
218 218
219 219 # Private interface
220 220 _post_execute = set()
221 221
222 222 def __init__(self, config=None, ipython_dir=None,
223 223 user_ns=None, user_global_ns=None,
224 224 custom_exceptions=((), None)):
225 225
226 226 # This is where traits with a config_key argument are updated
227 227 # from the values on config.
228 228 super(InteractiveShell, self).__init__(config=config)
229 229
230 230 # These are relatively independent and stateless
231 231 self.init_ipython_dir(ipython_dir)
232 232 self.init_instance_attrs()
233 233 self.init_environment()
234 234
235 235 # Create namespaces (user_ns, user_global_ns, etc.)
236 236 self.init_create_namespaces(user_ns, user_global_ns)
237 237 # This has to be done after init_create_namespaces because it uses
238 238 # something in self.user_ns, but before init_sys_modules, which
239 239 # is the first thing to modify sys.
240 240 # TODO: When we override sys.stdout and sys.stderr before this class
241 241 # is created, we are saving the overridden ones here. Not sure if this
242 242 # is what we want to do.
243 243 self.save_sys_module_state()
244 244 self.init_sys_modules()
245 245
246 246 self.init_history()
247 247 self.init_encoding()
248 248 self.init_prefilter()
249 249
250 250 Magic.__init__(self, self)
251 251
252 252 self.init_syntax_highlighting()
253 253 self.init_hooks()
254 254 self.init_pushd_popd_magic()
255 255 # self.init_traceback_handlers use to be here, but we moved it below
256 256 # because it and init_io have to come after init_readline.
257 257 self.init_user_ns()
258 258 self.init_logger()
259 259 self.init_alias()
260 260 self.init_builtins()
261 261
262 262 # pre_config_initialization
263 263 self.init_shadow_hist()
264 264
265 265 # The next section should contain everything that was in ipmaker.
266 266 self.init_logstart()
267 267
268 268 # The following was in post_config_initialization
269 269 self.init_inspector()
270 270 # init_readline() must come before init_io(), because init_io uses
271 271 # readline related things.
272 272 self.init_readline()
273 273 # init_completer must come after init_readline, because it needs to
274 274 # know whether readline is present or not system-wide to configure the
275 275 # completers, since the completion machinery can now operate
276 276 # independently of readline (e.g. over the network)
277 277 self.init_completer()
278 278 # TODO: init_io() needs to happen before init_traceback handlers
279 279 # because the traceback handlers hardcode the stdout/stderr streams.
280 280 # This logic in in debugger.Pdb and should eventually be changed.
281 281 self.init_io()
282 282 self.init_traceback_handlers(custom_exceptions)
283 283 self.init_prompts()
284 284 self.init_displayhook()
285 285 self.init_reload_doctest()
286 286 self.init_magics()
287 287 self.init_pdb()
288 288 self.init_extension_manager()
289 289 self.init_plugin_manager()
290 290 self.init_payload()
291 291 self.hooks.late_startup_hook()
292 292 atexit.register(self.atexit_operations)
293 293
294 294 @classmethod
295 295 def instance(cls, *args, **kwargs):
296 296 """Returns a global InteractiveShell instance."""
297 297 if cls._instance is None:
298 298 inst = cls(*args, **kwargs)
299 299 # Now make sure that the instance will also be returned by
300 300 # the subclasses instance attribute.
301 301 for subclass in cls.mro():
302 302 if issubclass(cls, subclass) and \
303 303 issubclass(subclass, InteractiveShell):
304 304 subclass._instance = inst
305 305 else:
306 306 break
307 307 if isinstance(cls._instance, cls):
308 308 return cls._instance
309 309 else:
310 310 raise MultipleInstanceError(
311 311 'Multiple incompatible subclass instances of '
312 312 'InteractiveShell are being created.'
313 313 )
314 314
315 315 @classmethod
316 316 def initialized(cls):
317 317 return hasattr(cls, "_instance")
318 318
319 319 def get_ipython(self):
320 320 """Return the currently running IPython instance."""
321 321 return self
322 322
323 323 #-------------------------------------------------------------------------
324 324 # Trait changed handlers
325 325 #-------------------------------------------------------------------------
326 326
327 327 def _ipython_dir_changed(self, name, new):
328 328 if not os.path.isdir(new):
329 329 os.makedirs(new, mode = 0777)
330 330
331 331 def set_autoindent(self,value=None):
332 332 """Set the autoindent flag, checking for readline support.
333 333
334 334 If called with no arguments, it acts as a toggle."""
335 335
336 336 if not self.has_readline:
337 337 if os.name == 'posix':
338 338 warn("The auto-indent feature requires the readline library")
339 339 self.autoindent = 0
340 340 return
341 341 if value is None:
342 342 self.autoindent = not self.autoindent
343 343 else:
344 344 self.autoindent = value
345 345
346 346 #-------------------------------------------------------------------------
347 347 # init_* methods called by __init__
348 348 #-------------------------------------------------------------------------
349 349
350 350 def init_ipython_dir(self, ipython_dir):
351 351 if ipython_dir is not None:
352 352 self.ipython_dir = ipython_dir
353 353 self.config.Global.ipython_dir = self.ipython_dir
354 354 return
355 355
356 356 if hasattr(self.config.Global, 'ipython_dir'):
357 357 self.ipython_dir = self.config.Global.ipython_dir
358 358 else:
359 359 self.ipython_dir = get_ipython_dir()
360 360
361 361 # All children can just read this
362 362 self.config.Global.ipython_dir = self.ipython_dir
363 363
364 364 def init_instance_attrs(self):
365 365 self.more = False
366 366
367 367 # command compiler
368 368 self.compile = codeop.CommandCompiler()
369 369
370 370 # User input buffer
371 371 self.buffer = []
372 372
373 373 # Make an empty namespace, which extension writers can rely on both
374 374 # existing and NEVER being used by ipython itself. This gives them a
375 375 # convenient location for storing additional information and state
376 376 # their extensions may require, without fear of collisions with other
377 377 # ipython names that may develop later.
378 378 self.meta = Struct()
379 379
380 380 # Object variable to store code object waiting execution. This is
381 381 # used mainly by the multithreaded shells, but it can come in handy in
382 382 # other situations. No need to use a Queue here, since it's a single
383 383 # item which gets cleared once run.
384 384 self.code_to_run = None
385 385
386 386 # Temporary files used for various purposes. Deleted at exit.
387 387 self.tempfiles = []
388 388
389 389 # Keep track of readline usage (later set by init_readline)
390 390 self.has_readline = False
391 391
392 392 # keep track of where we started running (mainly for crash post-mortem)
393 393 # This is not being used anywhere currently.
394 394 self.starting_dir = os.getcwd()
395 395
396 396 # Indentation management
397 397 self.indent_current_nsp = 0
398 398
399 399 def init_environment(self):
400 400 """Any changes we need to make to the user's environment."""
401 401 pass
402 402
403 403 def init_encoding(self):
404 404 # Get system encoding at startup time. Certain terminals (like Emacs
405 405 # under Win32 have it set to None, and we need to have a known valid
406 406 # encoding to use in the raw_input() method
407 407 try:
408 408 self.stdin_encoding = sys.stdin.encoding or 'ascii'
409 409 except AttributeError:
410 410 self.stdin_encoding = 'ascii'
411 411
412 412 def init_syntax_highlighting(self):
413 413 # Python source parser/formatter for syntax highlighting
414 414 pyformat = PyColorize.Parser().format
415 415 self.pycolorize = lambda src: pyformat(src,'str',self.colors)
416 416
417 417 def init_pushd_popd_magic(self):
418 418 # for pushd/popd management
419 419 try:
420 420 self.home_dir = get_home_dir()
421 421 except HomeDirError, msg:
422 422 fatal(msg)
423 423
424 424 self.dir_stack = []
425 425
426 426 def init_logger(self):
427 427 self.logger = Logger(self, logfname='ipython_log.py', logmode='rotate')
428 428 # local shortcut, this is used a LOT
429 429 self.log = self.logger.log
430 430
431 431 def init_logstart(self):
432 432 if self.logappend:
433 433 self.magic_logstart(self.logappend + ' append')
434 434 elif self.logfile:
435 435 self.magic_logstart(self.logfile)
436 436 elif self.logstart:
437 437 self.magic_logstart()
438 438
439 439 def init_builtins(self):
440 440 self.builtin_trap = BuiltinTrap(shell=self)
441 441
442 442 def init_inspector(self):
443 443 # Object inspector
444 444 self.inspector = oinspect.Inspector(oinspect.InspectColors,
445 445 PyColorize.ANSICodeColors,
446 446 'NoColor',
447 447 self.object_info_string_level)
448 448
449 449 def init_io(self):
450 450 # This will just use sys.stdout and sys.stderr. If you want to
451 451 # override sys.stdout and sys.stderr themselves, you need to do that
452 452 # *before* instantiating this class, because Term holds onto
453 453 # references to the underlying streams.
454 454 if sys.platform == 'win32' and self.has_readline:
455 455 Term = io.IOTerm(cout=self.readline._outputfile,
456 456 cerr=self.readline._outputfile)
457 457 else:
458 458 Term = io.IOTerm()
459 459 io.Term = Term
460 460
461 461 def init_prompts(self):
462 462 # TODO: This is a pass for now because the prompts are managed inside
463 463 # the DisplayHook. Once there is a separate prompt manager, this
464 464 # will initialize that object and all prompt related information.
465 465 pass
466 466
467 467 def init_displayhook(self):
468 468 # Initialize displayhook, set in/out prompts and printing system
469 469 self.displayhook = self.displayhook_class(
470 470 shell=self,
471 471 cache_size=self.cache_size,
472 472 input_sep = self.separate_in,
473 473 output_sep = self.separate_out,
474 474 output_sep2 = self.separate_out2,
475 475 ps1 = self.prompt_in1,
476 476 ps2 = self.prompt_in2,
477 477 ps_out = self.prompt_out,
478 478 pad_left = self.prompts_pad_left
479 479 )
480 480 # This is a context manager that installs/revmoes the displayhook at
481 481 # the appropriate time.
482 482 self.display_trap = DisplayTrap(hook=self.displayhook)
483 483
484 484 def init_reload_doctest(self):
485 485 # Do a proper resetting of doctest, including the necessary displayhook
486 486 # monkeypatching
487 487 try:
488 488 doctest_reload()
489 489 except ImportError:
490 490 warn("doctest module does not exist.")
491 491
492 492 #-------------------------------------------------------------------------
493 493 # Things related to injections into the sys module
494 494 #-------------------------------------------------------------------------
495 495
496 496 def save_sys_module_state(self):
497 497 """Save the state of hooks in the sys module.
498 498
499 499 This has to be called after self.user_ns is created.
500 500 """
501 501 self._orig_sys_module_state = {}
502 502 self._orig_sys_module_state['stdin'] = sys.stdin
503 503 self._orig_sys_module_state['stdout'] = sys.stdout
504 504 self._orig_sys_module_state['stderr'] = sys.stderr
505 505 self._orig_sys_module_state['excepthook'] = sys.excepthook
506 506 try:
507 507 self._orig_sys_modules_main_name = self.user_ns['__name__']
508 508 except KeyError:
509 509 pass
510 510
511 511 def restore_sys_module_state(self):
512 512 """Restore the state of the sys module."""
513 513 try:
514 for k, v in self._orig_sys_module_state.items():
514 for k, v in self._orig_sys_module_state.iteritems():
515 515 setattr(sys, k, v)
516 516 except AttributeError:
517 517 pass
518 518 # Reset what what done in self.init_sys_modules
519 519 try:
520 520 sys.modules[self.user_ns['__name__']] = self._orig_sys_modules_main_name
521 521 except (AttributeError, KeyError):
522 522 pass
523 523
524 524 #-------------------------------------------------------------------------
525 525 # Things related to hooks
526 526 #-------------------------------------------------------------------------
527 527
528 528 def init_hooks(self):
529 529 # hooks holds pointers used for user-side customizations
530 530 self.hooks = Struct()
531 531
532 532 self.strdispatchers = {}
533 533
534 534 # Set all default hooks, defined in the IPython.hooks module.
535 535 hooks = IPython.core.hooks
536 536 for hook_name in hooks.__all__:
537 537 # default hooks have priority 100, i.e. low; user hooks should have
538 538 # 0-100 priority
539 539 self.set_hook(hook_name,getattr(hooks,hook_name), 100)
540 540
541 541 def set_hook(self,name,hook, priority = 50, str_key = None, re_key = None):
542 542 """set_hook(name,hook) -> sets an internal IPython hook.
543 543
544 544 IPython exposes some of its internal API as user-modifiable hooks. By
545 545 adding your function to one of these hooks, you can modify IPython's
546 546 behavior to call at runtime your own routines."""
547 547
548 548 # At some point in the future, this should validate the hook before it
549 549 # accepts it. Probably at least check that the hook takes the number
550 550 # of args it's supposed to.
551 551
552 552 f = types.MethodType(hook, self)
553 553
554 554 # check if the hook is for strdispatcher first
555 555 if str_key is not None:
556 556 sdp = self.strdispatchers.get(name, StrDispatch())
557 557 sdp.add_s(str_key, f, priority )
558 558 self.strdispatchers[name] = sdp
559 559 return
560 560 if re_key is not None:
561 561 sdp = self.strdispatchers.get(name, StrDispatch())
562 562 sdp.add_re(re.compile(re_key), f, priority )
563 563 self.strdispatchers[name] = sdp
564 564 return
565 565
566 566 dp = getattr(self.hooks, name, None)
567 567 if name not in IPython.core.hooks.__all__:
568 568 print "Warning! Hook '%s' is not one of %s" % \
569 569 (name, IPython.core.hooks.__all__ )
570 570 if not dp:
571 571 dp = IPython.core.hooks.CommandChainDispatcher()
572 572
573 573 try:
574 574 dp.add(f,priority)
575 575 except AttributeError:
576 576 # it was not commandchain, plain old func - replace
577 577 dp = f
578 578
579 579 setattr(self.hooks,name, dp)
580 580
581 581 def register_post_execute(self, func):
582 582 """Register a function for calling after code execution.
583 583 """
584 584 if not callable(func):
585 585 raise ValueError('argument %s must be callable' % func)
586 586 self._post_execute.add(func)
587 587
588 588 #-------------------------------------------------------------------------
589 589 # Things related to the "main" module
590 590 #-------------------------------------------------------------------------
591 591
592 592 def new_main_mod(self,ns=None):
593 593 """Return a new 'main' module object for user code execution.
594 594 """
595 595 main_mod = self._user_main_module
596 596 init_fakemod_dict(main_mod,ns)
597 597 return main_mod
598 598
599 599 def cache_main_mod(self,ns,fname):
600 600 """Cache a main module's namespace.
601 601
602 602 When scripts are executed via %run, we must keep a reference to the
603 603 namespace of their __main__ module (a FakeModule instance) around so
604 604 that Python doesn't clear it, rendering objects defined therein
605 605 useless.
606 606
607 607 This method keeps said reference in a private dict, keyed by the
608 608 absolute path of the module object (which corresponds to the script
609 609 path). This way, for multiple executions of the same script we only
610 610 keep one copy of the namespace (the last one), thus preventing memory
611 611 leaks from old references while allowing the objects from the last
612 612 execution to be accessible.
613 613
614 614 Note: we can not allow the actual FakeModule instances to be deleted,
615 615 because of how Python tears down modules (it hard-sets all their
616 616 references to None without regard for reference counts). This method
617 617 must therefore make a *copy* of the given namespace, to allow the
618 618 original module's __dict__ to be cleared and reused.
619 619
620 620
621 621 Parameters
622 622 ----------
623 623 ns : a namespace (a dict, typically)
624 624
625 625 fname : str
626 626 Filename associated with the namespace.
627 627
628 628 Examples
629 629 --------
630 630
631 631 In [10]: import IPython
632 632
633 633 In [11]: _ip.cache_main_mod(IPython.__dict__,IPython.__file__)
634 634
635 635 In [12]: IPython.__file__ in _ip._main_ns_cache
636 636 Out[12]: True
637 637 """
638 638 self._main_ns_cache[os.path.abspath(fname)] = ns.copy()
639 639
640 640 def clear_main_mod_cache(self):
641 641 """Clear the cache of main modules.
642 642
643 643 Mainly for use by utilities like %reset.
644 644
645 645 Examples
646 646 --------
647 647
648 648 In [15]: import IPython
649 649
650 650 In [16]: _ip.cache_main_mod(IPython.__dict__,IPython.__file__)
651 651
652 652 In [17]: len(_ip._main_ns_cache) > 0
653 653 Out[17]: True
654 654
655 655 In [18]: _ip.clear_main_mod_cache()
656 656
657 657 In [19]: len(_ip._main_ns_cache) == 0
658 658 Out[19]: True
659 659 """
660 660 self._main_ns_cache.clear()
661 661
662 662 #-------------------------------------------------------------------------
663 663 # Things related to debugging
664 664 #-------------------------------------------------------------------------
665 665
666 666 def init_pdb(self):
667 667 # Set calling of pdb on exceptions
668 668 # self.call_pdb is a property
669 669 self.call_pdb = self.pdb
670 670
671 671 def _get_call_pdb(self):
672 672 return self._call_pdb
673 673
674 674 def _set_call_pdb(self,val):
675 675
676 676 if val not in (0,1,False,True):
677 677 raise ValueError,'new call_pdb value must be boolean'
678 678
679 679 # store value in instance
680 680 self._call_pdb = val
681 681
682 682 # notify the actual exception handlers
683 683 self.InteractiveTB.call_pdb = val
684 684
685 685 call_pdb = property(_get_call_pdb,_set_call_pdb,None,
686 686 'Control auto-activation of pdb at exceptions')
687 687
688 688 def debugger(self,force=False):
689 689 """Call the pydb/pdb debugger.
690 690
691 691 Keywords:
692 692
693 693 - force(False): by default, this routine checks the instance call_pdb
694 694 flag and does not actually invoke the debugger if the flag is false.
695 695 The 'force' option forces the debugger to activate even if the flag
696 696 is false.
697 697 """
698 698
699 699 if not (force or self.call_pdb):
700 700 return
701 701
702 702 if not hasattr(sys,'last_traceback'):
703 703 error('No traceback has been produced, nothing to debug.')
704 704 return
705 705
706 706 # use pydb if available
707 707 if debugger.has_pydb:
708 708 from pydb import pm
709 709 else:
710 710 # fallback to our internal debugger
711 711 pm = lambda : self.InteractiveTB.debugger(force=True)
712 712 self.history_saving_wrapper(pm)()
713 713
714 714 #-------------------------------------------------------------------------
715 715 # Things related to IPython's various namespaces
716 716 #-------------------------------------------------------------------------
717 717
718 718 def init_create_namespaces(self, user_ns=None, user_global_ns=None):
719 719 # Create the namespace where the user will operate. user_ns is
720 720 # normally the only one used, and it is passed to the exec calls as
721 721 # the locals argument. But we do carry a user_global_ns namespace
722 722 # given as the exec 'globals' argument, This is useful in embedding
723 723 # situations where the ipython shell opens in a context where the
724 724 # distinction between locals and globals is meaningful. For
725 725 # non-embedded contexts, it is just the same object as the user_ns dict.
726 726
727 727 # FIXME. For some strange reason, __builtins__ is showing up at user
728 728 # level as a dict instead of a module. This is a manual fix, but I
729 729 # should really track down where the problem is coming from. Alex
730 730 # Schmolck reported this problem first.
731 731
732 732 # A useful post by Alex Martelli on this topic:
733 733 # Re: inconsistent value from __builtins__
734 734 # Von: Alex Martelli <aleaxit@yahoo.com>
735 735 # Datum: Freitag 01 Oktober 2004 04:45:34 nachmittags/abends
736 736 # Gruppen: comp.lang.python
737 737
738 738 # Michael Hohn <hohn@hooknose.lbl.gov> wrote:
739 739 # > >>> print type(builtin_check.get_global_binding('__builtins__'))
740 740 # > <type 'dict'>
741 741 # > >>> print type(__builtins__)
742 742 # > <type 'module'>
743 743 # > Is this difference in return value intentional?
744 744
745 745 # Well, it's documented that '__builtins__' can be either a dictionary
746 746 # or a module, and it's been that way for a long time. Whether it's
747 747 # intentional (or sensible), I don't know. In any case, the idea is
748 748 # that if you need to access the built-in namespace directly, you
749 749 # should start with "import __builtin__" (note, no 's') which will
750 750 # definitely give you a module. Yeah, it's somewhat confusing:-(.
751 751
752 752 # These routines return properly built dicts as needed by the rest of
753 753 # the code, and can also be used by extension writers to generate
754 754 # properly initialized namespaces.
755 755 user_ns, user_global_ns = self.make_user_namespaces(user_ns,
756 756 user_global_ns)
757 757
758 758 # Assign namespaces
759 759 # This is the namespace where all normal user variables live
760 760 self.user_ns = user_ns
761 761 self.user_global_ns = user_global_ns
762 762
763 763 # An auxiliary namespace that checks what parts of the user_ns were
764 764 # loaded at startup, so we can list later only variables defined in
765 765 # actual interactive use. Since it is always a subset of user_ns, it
766 766 # doesn't need to be separately tracked in the ns_table.
767 767 self.user_ns_hidden = {}
768 768
769 769 # A namespace to keep track of internal data structures to prevent
770 770 # them from cluttering user-visible stuff. Will be updated later
771 771 self.internal_ns = {}
772 772
773 773 # Now that FakeModule produces a real module, we've run into a nasty
774 774 # problem: after script execution (via %run), the module where the user
775 775 # code ran is deleted. Now that this object is a true module (needed
776 776 # so docetst and other tools work correctly), the Python module
777 777 # teardown mechanism runs over it, and sets to None every variable
778 778 # present in that module. Top-level references to objects from the
779 779 # script survive, because the user_ns is updated with them. However,
780 780 # calling functions defined in the script that use other things from
781 781 # the script will fail, because the function's closure had references
782 782 # to the original objects, which are now all None. So we must protect
783 783 # these modules from deletion by keeping a cache.
784 784 #
785 785 # To avoid keeping stale modules around (we only need the one from the
786 786 # last run), we use a dict keyed with the full path to the script, so
787 787 # only the last version of the module is held in the cache. Note,
788 788 # however, that we must cache the module *namespace contents* (their
789 789 # __dict__). Because if we try to cache the actual modules, old ones
790 790 # (uncached) could be destroyed while still holding references (such as
791 791 # those held by GUI objects that tend to be long-lived)>
792 792 #
793 793 # The %reset command will flush this cache. See the cache_main_mod()
794 794 # and clear_main_mod_cache() methods for details on use.
795 795
796 796 # This is the cache used for 'main' namespaces
797 797 self._main_ns_cache = {}
798 798 # And this is the single instance of FakeModule whose __dict__ we keep
799 799 # copying and clearing for reuse on each %run
800 800 self._user_main_module = FakeModule()
801 801
802 802 # A table holding all the namespaces IPython deals with, so that
803 803 # introspection facilities can search easily.
804 804 self.ns_table = {'user':user_ns,
805 805 'user_global':user_global_ns,
806 806 'internal':self.internal_ns,
807 807 'builtin':__builtin__.__dict__
808 808 }
809 809
810 810 # Similarly, track all namespaces where references can be held and that
811 811 # we can safely clear (so it can NOT include builtin). This one can be
812 812 # a simple list. Note that the main execution namespaces, user_ns and
813 813 # user_global_ns, can NOT be listed here, as clearing them blindly
814 814 # causes errors in object __del__ methods. Instead, the reset() method
815 815 # clears them manually and carefully.
816 816 self.ns_refs_table = [ self.user_ns_hidden,
817 817 self.internal_ns, self._main_ns_cache ]
818 818
819 819 def make_user_namespaces(self, user_ns=None, user_global_ns=None):
820 820 """Return a valid local and global user interactive namespaces.
821 821
822 822 This builds a dict with the minimal information needed to operate as a
823 823 valid IPython user namespace, which you can pass to the various
824 824 embedding classes in ipython. The default implementation returns the
825 825 same dict for both the locals and the globals to allow functions to
826 826 refer to variables in the namespace. Customized implementations can
827 827 return different dicts. The locals dictionary can actually be anything
828 828 following the basic mapping protocol of a dict, but the globals dict
829 829 must be a true dict, not even a subclass. It is recommended that any
830 830 custom object for the locals namespace synchronize with the globals
831 831 dict somehow.
832 832
833 833 Raises TypeError if the provided globals namespace is not a true dict.
834 834
835 835 Parameters
836 836 ----------
837 837 user_ns : dict-like, optional
838 838 The current user namespace. The items in this namespace should
839 839 be included in the output. If None, an appropriate blank
840 840 namespace should be created.
841 841 user_global_ns : dict, optional
842 842 The current user global namespace. The items in this namespace
843 843 should be included in the output. If None, an appropriate
844 844 blank namespace should be created.
845 845
846 846 Returns
847 847 -------
848 848 A pair of dictionary-like object to be used as the local namespace
849 849 of the interpreter and a dict to be used as the global namespace.
850 850 """
851 851
852 852
853 853 # We must ensure that __builtin__ (without the final 's') is always
854 854 # available and pointing to the __builtin__ *module*. For more details:
855 855 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
856 856
857 857 if user_ns is None:
858 858 # Set __name__ to __main__ to better match the behavior of the
859 859 # normal interpreter.
860 860 user_ns = {'__name__' :'__main__',
861 861 '__builtin__' : __builtin__,
862 862 '__builtins__' : __builtin__,
863 863 }
864 864 else:
865 865 user_ns.setdefault('__name__','__main__')
866 866 user_ns.setdefault('__builtin__',__builtin__)
867 867 user_ns.setdefault('__builtins__',__builtin__)
868 868
869 869 if user_global_ns is None:
870 870 user_global_ns = user_ns
871 871 if type(user_global_ns) is not dict:
872 872 raise TypeError("user_global_ns must be a true dict; got %r"
873 873 % type(user_global_ns))
874 874
875 875 return user_ns, user_global_ns
876 876
877 877 def init_sys_modules(self):
878 878 # We need to insert into sys.modules something that looks like a
879 879 # module but which accesses the IPython namespace, for shelve and
880 880 # pickle to work interactively. Normally they rely on getting
881 881 # everything out of __main__, but for embedding purposes each IPython
882 882 # instance has its own private namespace, so we can't go shoving
883 883 # everything into __main__.
884 884
885 885 # note, however, that we should only do this for non-embedded
886 886 # ipythons, which really mimic the __main__.__dict__ with their own
887 887 # namespace. Embedded instances, on the other hand, should not do
888 888 # this because they need to manage the user local/global namespaces
889 889 # only, but they live within a 'normal' __main__ (meaning, they
890 890 # shouldn't overtake the execution environment of the script they're
891 891 # embedded in).
892 892
893 893 # This is overridden in the InteractiveShellEmbed subclass to a no-op.
894 894
895 895 try:
896 896 main_name = self.user_ns['__name__']
897 897 except KeyError:
898 898 raise KeyError('user_ns dictionary MUST have a "__name__" key')
899 899 else:
900 900 sys.modules[main_name] = FakeModule(self.user_ns)
901 901
902 902 def init_user_ns(self):
903 903 """Initialize all user-visible namespaces to their minimum defaults.
904 904
905 905 Certain history lists are also initialized here, as they effectively
906 906 act as user namespaces.
907 907
908 908 Notes
909 909 -----
910 910 All data structures here are only filled in, they are NOT reset by this
911 911 method. If they were not empty before, data will simply be added to
912 912 therm.
913 913 """
914 914 # This function works in two parts: first we put a few things in
915 915 # user_ns, and we sync that contents into user_ns_hidden so that these
916 916 # initial variables aren't shown by %who. After the sync, we add the
917 917 # rest of what we *do* want the user to see with %who even on a new
918 918 # session (probably nothing, so theye really only see their own stuff)
919 919
920 920 # The user dict must *always* have a __builtin__ reference to the
921 921 # Python standard __builtin__ namespace, which must be imported.
922 922 # This is so that certain operations in prompt evaluation can be
923 923 # reliably executed with builtins. Note that we can NOT use
924 924 # __builtins__ (note the 's'), because that can either be a dict or a
925 925 # module, and can even mutate at runtime, depending on the context
926 926 # (Python makes no guarantees on it). In contrast, __builtin__ is
927 927 # always a module object, though it must be explicitly imported.
928 928
929 929 # For more details:
930 930 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
931 931 ns = dict(__builtin__ = __builtin__)
932 932
933 933 # Put 'help' in the user namespace
934 934 try:
935 935 from site import _Helper
936 936 ns['help'] = _Helper()
937 937 except ImportError:
938 938 warn('help() not available - check site.py')
939 939
940 940 # make global variables for user access to the histories
941 941 ns['_ih'] = self.input_hist
942 942 ns['_oh'] = self.output_hist
943 943 ns['_dh'] = self.dir_hist
944 944
945 945 ns['_sh'] = shadowns
946 946
947 947 # user aliases to input and output histories. These shouldn't show up
948 948 # in %who, as they can have very large reprs.
949 949 ns['In'] = self.input_hist
950 950 ns['Out'] = self.output_hist
951 951
952 952 # Store myself as the public api!!!
953 953 ns['get_ipython'] = self.get_ipython
954 954
955 955 # Sync what we've added so far to user_ns_hidden so these aren't seen
956 956 # by %who
957 957 self.user_ns_hidden.update(ns)
958 958
959 959 # Anything put into ns now would show up in %who. Think twice before
960 960 # putting anything here, as we really want %who to show the user their
961 961 # stuff, not our variables.
962 962
963 963 # Finally, update the real user's namespace
964 964 self.user_ns.update(ns)
965 965
966 966
967 967 def reset(self):
968 968 """Clear all internal namespaces.
969 969
970 970 Note that this is much more aggressive than %reset, since it clears
971 971 fully all namespaces, as well as all input/output lists.
972 972 """
973 973 self.alias_manager.clear_aliases()
974 974
975 975 # Clear input and output histories
976 976 self.input_hist[:] = []
977 977 self.input_hist_raw[:] = []
978 978 self.output_hist.clear()
979 979
980 980 # Clear namespaces holding user references
981 981 for ns in self.ns_refs_table:
982 982 ns.clear()
983 983
984 984 # The main execution namespaces must be cleared very carefully,
985 985 # skipping the deletion of the builtin-related keys, because doing so
986 986 # would cause errors in many object's __del__ methods.
987 987 for ns in [self.user_ns, self.user_global_ns]:
988 988 drop_keys = set(ns.keys())
989 989 drop_keys.discard('__builtin__')
990 990 drop_keys.discard('__builtins__')
991 991 for k in drop_keys:
992 992 del ns[k]
993 993
994 994 # Restore the user namespaces to minimal usability
995 995 self.init_user_ns()
996 996
997 997 # Restore the default and user aliases
998 998 self.alias_manager.init_aliases()
999 999
1000 1000 def reset_selective(self, regex=None):
1001 1001 """Clear selective variables from internal namespaces based on a
1002 1002 specified regular expression.
1003 1003
1004 1004 Parameters
1005 1005 ----------
1006 1006 regex : string or compiled pattern, optional
1007 1007 A regular expression pattern that will be used in searching
1008 1008 variable names in the users namespaces.
1009 1009 """
1010 1010 if regex is not None:
1011 1011 try:
1012 1012 m = re.compile(regex)
1013 1013 except TypeError:
1014 1014 raise TypeError('regex must be a string or compiled pattern')
1015 1015 # Search for keys in each namespace that match the given regex
1016 1016 # If a match is found, delete the key/value pair.
1017 1017 for ns in self.ns_refs_table:
1018 1018 for var in ns:
1019 1019 if m.search(var):
1020 1020 del ns[var]
1021 1021
1022 1022 def push(self, variables, interactive=True):
1023 1023 """Inject a group of variables into the IPython user namespace.
1024 1024
1025 1025 Parameters
1026 1026 ----------
1027 1027 variables : dict, str or list/tuple of str
1028 1028 The variables to inject into the user's namespace. If a dict, a
1029 1029 simple update is done. If a str, the string is assumed to have
1030 1030 variable names separated by spaces. A list/tuple of str can also
1031 1031 be used to give the variable names. If just the variable names are
1032 1032 give (list/tuple/str) then the variable values looked up in the
1033 1033 callers frame.
1034 1034 interactive : bool
1035 1035 If True (default), the variables will be listed with the ``who``
1036 1036 magic.
1037 1037 """
1038 1038 vdict = None
1039 1039
1040 1040 # We need a dict of name/value pairs to do namespace updates.
1041 1041 if isinstance(variables, dict):
1042 1042 vdict = variables
1043 1043 elif isinstance(variables, (basestring, list, tuple)):
1044 1044 if isinstance(variables, basestring):
1045 1045 vlist = variables.split()
1046 1046 else:
1047 1047 vlist = variables
1048 1048 vdict = {}
1049 1049 cf = sys._getframe(1)
1050 1050 for name in vlist:
1051 1051 try:
1052 1052 vdict[name] = eval(name, cf.f_globals, cf.f_locals)
1053 1053 except:
1054 1054 print ('Could not get variable %s from %s' %
1055 1055 (name,cf.f_code.co_name))
1056 1056 else:
1057 1057 raise ValueError('variables must be a dict/str/list/tuple')
1058 1058
1059 1059 # Propagate variables to user namespace
1060 1060 self.user_ns.update(vdict)
1061 1061
1062 1062 # And configure interactive visibility
1063 1063 config_ns = self.user_ns_hidden
1064 1064 if interactive:
1065 1065 for name, val in vdict.iteritems():
1066 1066 config_ns.pop(name, None)
1067 1067 else:
1068 1068 for name,val in vdict.iteritems():
1069 1069 config_ns[name] = val
1070 1070
1071 1071 #-------------------------------------------------------------------------
1072 1072 # Things related to object introspection
1073 1073 #-------------------------------------------------------------------------
1074 1074
1075 1075 def _ofind(self, oname, namespaces=None):
1076 1076 """Find an object in the available namespaces.
1077 1077
1078 1078 self._ofind(oname) -> dict with keys: found,obj,ospace,ismagic
1079 1079
1080 1080 Has special code to detect magic functions.
1081 1081 """
1082 1082 #oname = oname.strip()
1083 1083 #print '1- oname: <%r>' % oname # dbg
1084 1084 try:
1085 1085 oname = oname.strip().encode('ascii')
1086 1086 #print '2- oname: <%r>' % oname # dbg
1087 1087 except UnicodeEncodeError:
1088 1088 print 'Python identifiers can only contain ascii characters.'
1089 1089 return dict(found=False)
1090 1090
1091 1091 alias_ns = None
1092 1092 if namespaces is None:
1093 1093 # Namespaces to search in:
1094 1094 # Put them in a list. The order is important so that we
1095 1095 # find things in the same order that Python finds them.
1096 1096 namespaces = [ ('Interactive', self.user_ns),
1097 1097 ('IPython internal', self.internal_ns),
1098 1098 ('Python builtin', __builtin__.__dict__),
1099 1099 ('Alias', self.alias_manager.alias_table),
1100 1100 ]
1101 1101 alias_ns = self.alias_manager.alias_table
1102 1102
1103 1103 # initialize results to 'null'
1104 1104 found = False; obj = None; ospace = None; ds = None;
1105 1105 ismagic = False; isalias = False; parent = None
1106 1106
1107 1107 # We need to special-case 'print', which as of python2.6 registers as a
1108 1108 # function but should only be treated as one if print_function was
1109 1109 # loaded with a future import. In this case, just bail.
1110 1110 if (oname == 'print' and not (self.compile.compiler.flags &
1111 1111 __future__.CO_FUTURE_PRINT_FUNCTION)):
1112 1112 return {'found':found, 'obj':obj, 'namespace':ospace,
1113 1113 'ismagic':ismagic, 'isalias':isalias, 'parent':parent}
1114 1114
1115 1115 # Look for the given name by splitting it in parts. If the head is
1116 1116 # found, then we look for all the remaining parts as members, and only
1117 1117 # declare success if we can find them all.
1118 1118 oname_parts = oname.split('.')
1119 1119 oname_head, oname_rest = oname_parts[0],oname_parts[1:]
1120 1120 for nsname,ns in namespaces:
1121 1121 try:
1122 1122 obj = ns[oname_head]
1123 1123 except KeyError:
1124 1124 continue
1125 1125 else:
1126 1126 #print 'oname_rest:', oname_rest # dbg
1127 1127 for part in oname_rest:
1128 1128 try:
1129 1129 parent = obj
1130 1130 obj = getattr(obj,part)
1131 1131 except:
1132 1132 # Blanket except b/c some badly implemented objects
1133 1133 # allow __getattr__ to raise exceptions other than
1134 1134 # AttributeError, which then crashes IPython.
1135 1135 break
1136 1136 else:
1137 1137 # If we finish the for loop (no break), we got all members
1138 1138 found = True
1139 1139 ospace = nsname
1140 1140 if ns == alias_ns:
1141 1141 isalias = True
1142 1142 break # namespace loop
1143 1143
1144 1144 # Try to see if it's magic
1145 1145 if not found:
1146 1146 if oname.startswith(ESC_MAGIC):
1147 1147 oname = oname[1:]
1148 1148 obj = getattr(self,'magic_'+oname,None)
1149 1149 if obj is not None:
1150 1150 found = True
1151 1151 ospace = 'IPython internal'
1152 1152 ismagic = True
1153 1153
1154 1154 # Last try: special-case some literals like '', [], {}, etc:
1155 1155 if not found and oname_head in ["''",'""','[]','{}','()']:
1156 1156 obj = eval(oname_head)
1157 1157 found = True
1158 1158 ospace = 'Interactive'
1159 1159
1160 1160 return {'found':found, 'obj':obj, 'namespace':ospace,
1161 1161 'ismagic':ismagic, 'isalias':isalias, 'parent':parent}
1162 1162
1163 1163 def _ofind_property(self, oname, info):
1164 1164 """Second part of object finding, to look for property details."""
1165 1165 if info.found:
1166 1166 # Get the docstring of the class property if it exists.
1167 1167 path = oname.split('.')
1168 1168 root = '.'.join(path[:-1])
1169 1169 if info.parent is not None:
1170 1170 try:
1171 1171 target = getattr(info.parent, '__class__')
1172 1172 # The object belongs to a class instance.
1173 1173 try:
1174 1174 target = getattr(target, path[-1])
1175 1175 # The class defines the object.
1176 1176 if isinstance(target, property):
1177 1177 oname = root + '.__class__.' + path[-1]
1178 1178 info = Struct(self._ofind(oname))
1179 1179 except AttributeError: pass
1180 1180 except AttributeError: pass
1181 1181
1182 1182 # We return either the new info or the unmodified input if the object
1183 1183 # hadn't been found
1184 1184 return info
1185 1185
1186 1186 def _object_find(self, oname, namespaces=None):
1187 1187 """Find an object and return a struct with info about it."""
1188 1188 inf = Struct(self._ofind(oname, namespaces))
1189 1189 return Struct(self._ofind_property(oname, inf))
1190 1190
1191 1191 def _inspect(self, meth, oname, namespaces=None, **kw):
1192 1192 """Generic interface to the inspector system.
1193 1193
1194 1194 This function is meant to be called by pdef, pdoc & friends."""
1195 1195 info = self._object_find(oname)
1196 1196 if info.found:
1197 1197 pmethod = getattr(self.inspector, meth)
1198 1198 formatter = format_screen if info.ismagic else None
1199 1199 if meth == 'pdoc':
1200 1200 pmethod(info.obj, oname, formatter)
1201 1201 elif meth == 'pinfo':
1202 1202 pmethod(info.obj, oname, formatter, info, **kw)
1203 1203 else:
1204 1204 pmethod(info.obj, oname)
1205 1205 else:
1206 1206 print 'Object `%s` not found.' % oname
1207 1207 return 'not found' # so callers can take other action
1208 1208
1209 1209 def object_inspect(self, oname):
1210 1210 info = self._object_find(oname)
1211 1211 if info.found:
1212 1212 return self.inspector.info(info.obj, oname, info=info)
1213 1213 else:
1214 1214 return oinspect.object_info(name=oname, found=False)
1215 1215
1216 1216 #-------------------------------------------------------------------------
1217 1217 # Things related to history management
1218 1218 #-------------------------------------------------------------------------
1219 1219
1220 1220 def init_history(self):
1221 1221 # List of input with multi-line handling.
1222 1222 self.input_hist = InputList()
1223 1223 # This one will hold the 'raw' input history, without any
1224 1224 # pre-processing. This will allow users to retrieve the input just as
1225 1225 # it was exactly typed in by the user, with %hist -r.
1226 1226 self.input_hist_raw = InputList()
1227 1227
1228 1228 # list of visited directories
1229 1229 try:
1230 1230 self.dir_hist = [os.getcwd()]
1231 1231 except OSError:
1232 1232 self.dir_hist = []
1233 1233
1234 1234 # dict of output history
1235 1235 self.output_hist = {}
1236 1236
1237 1237 # Now the history file
1238 1238 if self.profile:
1239 1239 histfname = 'history-%s' % self.profile
1240 1240 else:
1241 1241 histfname = 'history'
1242 1242 self.histfile = os.path.join(self.ipython_dir, histfname)
1243 1243
1244 1244 # Fill the history zero entry, user counter starts at 1
1245 1245 self.input_hist.append('\n')
1246 1246 self.input_hist_raw.append('\n')
1247 1247
1248 1248 def init_shadow_hist(self):
1249 1249 try:
1250 1250 self.db = pickleshare.PickleShareDB(self.ipython_dir + "/db")
1251 1251 except UnicodeDecodeError:
1252 1252 print "Your ipython_dir can't be decoded to unicode!"
1253 1253 print "Please set HOME environment variable to something that"
1254 1254 print r"only has ASCII characters, e.g. c:\home"
1255 1255 print "Now it is", self.ipython_dir
1256 1256 sys.exit()
1257 1257 self.shadowhist = ipcorehist.ShadowHist(self.db)
1258 1258
1259 1259 def savehist(self):
1260 1260 """Save input history to a file (via readline library)."""
1261 1261
1262 1262 try:
1263 1263 self.readline.write_history_file(self.histfile)
1264 1264 except:
1265 1265 print 'Unable to save IPython command history to file: ' + \
1266 1266 `self.histfile`
1267 1267
1268 1268 def reloadhist(self):
1269 1269 """Reload the input history from disk file."""
1270 1270
1271 1271 try:
1272 1272 self.readline.clear_history()
1273 1273 self.readline.read_history_file(self.shell.histfile)
1274 1274 except AttributeError:
1275 1275 pass
1276 1276
1277 1277 def history_saving_wrapper(self, func):
1278 1278 """ Wrap func for readline history saving
1279 1279
1280 1280 Convert func into callable that saves & restores
1281 1281 history around the call """
1282 1282
1283 1283 if self.has_readline:
1284 1284 from IPython.utils import rlineimpl as readline
1285 1285 else:
1286 1286 return func
1287 1287
1288 1288 def wrapper():
1289 1289 self.savehist()
1290 1290 try:
1291 1291 func()
1292 1292 finally:
1293 1293 readline.read_history_file(self.histfile)
1294 1294 return wrapper
1295 1295
1296 1296 def get_history(self, index=None, raw=False, output=True):
1297 1297 """Get the history list.
1298 1298
1299 1299 Get the input and output history.
1300 1300
1301 1301 Parameters
1302 1302 ----------
1303 1303 index : n or (n1, n2) or None
1304 1304 If n, then the last entries. If a tuple, then all in
1305 1305 range(n1, n2). If None, then all entries. Raises IndexError if
1306 1306 the format of index is incorrect.
1307 1307 raw : bool
1308 1308 If True, return the raw input.
1309 1309 output : bool
1310 1310 If True, then return the output as well.
1311 1311
1312 1312 Returns
1313 1313 -------
1314 1314 If output is True, then return a dict of tuples, keyed by the prompt
1315 1315 numbers and with values of (input, output). If output is False, then
1316 1316 a dict, keyed by the prompt number with the values of input. Raises
1317 1317 IndexError if no history is found.
1318 1318 """
1319 1319 if raw:
1320 1320 input_hist = self.input_hist_raw
1321 1321 else:
1322 1322 input_hist = self.input_hist
1323 1323 if output:
1324 1324 output_hist = self.user_ns['Out']
1325 1325 n = len(input_hist)
1326 1326 if index is None:
1327 1327 start=0; stop=n
1328 1328 elif isinstance(index, int):
1329 1329 start=n-index; stop=n
1330 1330 elif isinstance(index, tuple) and len(index) == 2:
1331 1331 start=index[0]; stop=index[1]
1332 1332 else:
1333 1333 raise IndexError('Not a valid index for the input history: %r'
1334 1334 % index)
1335 1335 hist = {}
1336 1336 for i in range(start, stop):
1337 1337 if output:
1338 1338 hist[i] = (input_hist[i], output_hist.get(i))
1339 1339 else:
1340 1340 hist[i] = input_hist[i]
1341 1341 if len(hist)==0:
1342 1342 raise IndexError('No history for range of indices: %r' % index)
1343 1343 return hist
1344 1344
1345 1345 #-------------------------------------------------------------------------
1346 1346 # Things related to exception handling and tracebacks (not debugging)
1347 1347 #-------------------------------------------------------------------------
1348 1348
1349 1349 def init_traceback_handlers(self, custom_exceptions):
1350 1350 # Syntax error handler.
1351 1351 self.SyntaxTB = ultratb.SyntaxTB(color_scheme='NoColor')
1352 1352
1353 1353 # The interactive one is initialized with an offset, meaning we always
1354 1354 # want to remove the topmost item in the traceback, which is our own
1355 1355 # internal code. Valid modes: ['Plain','Context','Verbose']
1356 1356 self.InteractiveTB = ultratb.AutoFormattedTB(mode = 'Plain',
1357 1357 color_scheme='NoColor',
1358 1358 tb_offset = 1)
1359 1359
1360 1360 # The instance will store a pointer to the system-wide exception hook,
1361 1361 # so that runtime code (such as magics) can access it. This is because
1362 1362 # during the read-eval loop, it may get temporarily overwritten.
1363 1363 self.sys_excepthook = sys.excepthook
1364 1364
1365 1365 # and add any custom exception handlers the user may have specified
1366 1366 self.set_custom_exc(*custom_exceptions)
1367 1367
1368 1368 # Set the exception mode
1369 1369 self.InteractiveTB.set_mode(mode=self.xmode)
1370 1370
1371 1371 def set_custom_exc(self, exc_tuple, handler):
1372 1372 """set_custom_exc(exc_tuple,handler)
1373 1373
1374 1374 Set a custom exception handler, which will be called if any of the
1375 1375 exceptions in exc_tuple occur in the mainloop (specifically, in the
1376 1376 runcode() method.
1377 1377
1378 1378 Inputs:
1379 1379
1380 1380 - exc_tuple: a *tuple* of valid exceptions to call the defined
1381 1381 handler for. It is very important that you use a tuple, and NOT A
1382 1382 LIST here, because of the way Python's except statement works. If
1383 1383 you only want to trap a single exception, use a singleton tuple:
1384 1384
1385 1385 exc_tuple == (MyCustomException,)
1386 1386
1387 1387 - handler: this must be defined as a function with the following
1388 1388 basic interface::
1389 1389
1390 1390 def my_handler(self, etype, value, tb, tb_offset=None)
1391 1391 ...
1392 1392 # The return value must be
1393 1393 return structured_traceback
1394 1394
1395 1395 This will be made into an instance method (via new.instancemethod)
1396 1396 of IPython itself, and it will be called if any of the exceptions
1397 1397 listed in the exc_tuple are caught. If the handler is None, an
1398 1398 internal basic one is used, which just prints basic info.
1399 1399
1400 1400 WARNING: by putting in your own exception handler into IPython's main
1401 1401 execution loop, you run a very good chance of nasty crashes. This
1402 1402 facility should only be used if you really know what you are doing."""
1403 1403
1404 1404 assert type(exc_tuple)==type(()) , \
1405 1405 "The custom exceptions must be given AS A TUPLE."
1406 1406
1407 1407 def dummy_handler(self,etype,value,tb):
1408 1408 print '*** Simple custom exception handler ***'
1409 1409 print 'Exception type :',etype
1410 1410 print 'Exception value:',value
1411 1411 print 'Traceback :',tb
1412 1412 print 'Source code :','\n'.join(self.buffer)
1413 1413
1414 1414 if handler is None: handler = dummy_handler
1415 1415
1416 1416 self.CustomTB = types.MethodType(handler, self)
1417 1417 self.custom_exceptions = exc_tuple
1418 1418
1419 1419 def excepthook(self, etype, value, tb):
1420 1420 """One more defense for GUI apps that call sys.excepthook.
1421 1421
1422 1422 GUI frameworks like wxPython trap exceptions and call
1423 1423 sys.excepthook themselves. I guess this is a feature that
1424 1424 enables them to keep running after exceptions that would
1425 1425 otherwise kill their mainloop. This is a bother for IPython
1426 1426 which excepts to catch all of the program exceptions with a try:
1427 1427 except: statement.
1428 1428
1429 1429 Normally, IPython sets sys.excepthook to a CrashHandler instance, so if
1430 1430 any app directly invokes sys.excepthook, it will look to the user like
1431 1431 IPython crashed. In order to work around this, we can disable the
1432 1432 CrashHandler and replace it with this excepthook instead, which prints a
1433 1433 regular traceback using our InteractiveTB. In this fashion, apps which
1434 1434 call sys.excepthook will generate a regular-looking exception from
1435 1435 IPython, and the CrashHandler will only be triggered by real IPython
1436 1436 crashes.
1437 1437
1438 1438 This hook should be used sparingly, only in places which are not likely
1439 1439 to be true IPython errors.
1440 1440 """
1441 1441 self.showtraceback((etype,value,tb),tb_offset=0)
1442 1442
1443 1443 def showtraceback(self,exc_tuple = None,filename=None,tb_offset=None,
1444 1444 exception_only=False):
1445 1445 """Display the exception that just occurred.
1446 1446
1447 1447 If nothing is known about the exception, this is the method which
1448 1448 should be used throughout the code for presenting user tracebacks,
1449 1449 rather than directly invoking the InteractiveTB object.
1450 1450
1451 1451 A specific showsyntaxerror() also exists, but this method can take
1452 1452 care of calling it if needed, so unless you are explicitly catching a
1453 1453 SyntaxError exception, don't try to analyze the stack manually and
1454 1454 simply call this method."""
1455 1455
1456 1456 try:
1457 1457 if exc_tuple is None:
1458 1458 etype, value, tb = sys.exc_info()
1459 1459 else:
1460 1460 etype, value, tb = exc_tuple
1461 1461
1462 1462 if etype is None:
1463 1463 if hasattr(sys, 'last_type'):
1464 1464 etype, value, tb = sys.last_type, sys.last_value, \
1465 1465 sys.last_traceback
1466 1466 else:
1467 1467 self.write_err('No traceback available to show.\n')
1468 1468 return
1469 1469
1470 1470 if etype is SyntaxError:
1471 1471 # Though this won't be called by syntax errors in the input
1472 1472 # line, there may be SyntaxError cases whith imported code.
1473 1473 self.showsyntaxerror(filename)
1474 1474 elif etype is UsageError:
1475 1475 print "UsageError:", value
1476 1476 else:
1477 1477 # WARNING: these variables are somewhat deprecated and not
1478 1478 # necessarily safe to use in a threaded environment, but tools
1479 1479 # like pdb depend on their existence, so let's set them. If we
1480 1480 # find problems in the field, we'll need to revisit their use.
1481 1481 sys.last_type = etype
1482 1482 sys.last_value = value
1483 1483 sys.last_traceback = tb
1484 1484
1485 1485 if etype in self.custom_exceptions:
1486 1486 # FIXME: Old custom traceback objects may just return a
1487 1487 # string, in that case we just put it into a list
1488 1488 stb = self.CustomTB(etype, value, tb, tb_offset)
1489 1489 if isinstance(ctb, basestring):
1490 1490 stb = [stb]
1491 1491 else:
1492 1492 if exception_only:
1493 1493 stb = ['An exception has occurred, use %tb to see '
1494 1494 'the full traceback.\n']
1495 1495 stb.extend(self.InteractiveTB.get_exception_only(etype,
1496 1496 value))
1497 1497 else:
1498 1498 stb = self.InteractiveTB.structured_traceback(etype,
1499 1499 value, tb, tb_offset=tb_offset)
1500 1500 # FIXME: the pdb calling should be done by us, not by
1501 1501 # the code computing the traceback.
1502 1502 if self.InteractiveTB.call_pdb:
1503 1503 # pdb mucks up readline, fix it back
1504 1504 self.set_readline_completer()
1505 1505
1506 1506 # Actually show the traceback
1507 1507 self._showtraceback(etype, value, stb)
1508 1508
1509 1509 except KeyboardInterrupt:
1510 1510 self.write_err("\nKeyboardInterrupt\n")
1511 1511
1512 1512 def _showtraceback(self, etype, evalue, stb):
1513 1513 """Actually show a traceback.
1514 1514
1515 1515 Subclasses may override this method to put the traceback on a different
1516 1516 place, like a side channel.
1517 1517 """
1518 1518 print >> io.Term.cout, self.InteractiveTB.stb2text(stb)
1519 1519
1520 1520 def showsyntaxerror(self, filename=None):
1521 1521 """Display the syntax error that just occurred.
1522 1522
1523 1523 This doesn't display a stack trace because there isn't one.
1524 1524
1525 1525 If a filename is given, it is stuffed in the exception instead
1526 1526 of what was there before (because Python's parser always uses
1527 1527 "<string>" when reading from a string).
1528 1528 """
1529 1529 etype, value, last_traceback = sys.exc_info()
1530 1530
1531 1531 # See note about these variables in showtraceback() above
1532 1532 sys.last_type = etype
1533 1533 sys.last_value = value
1534 1534 sys.last_traceback = last_traceback
1535 1535
1536 1536 if filename and etype is SyntaxError:
1537 1537 # Work hard to stuff the correct filename in the exception
1538 1538 try:
1539 1539 msg, (dummy_filename, lineno, offset, line) = value
1540 1540 except:
1541 1541 # Not the format we expect; leave it alone
1542 1542 pass
1543 1543 else:
1544 1544 # Stuff in the right filename
1545 1545 try:
1546 1546 # Assume SyntaxError is a class exception
1547 1547 value = SyntaxError(msg, (filename, lineno, offset, line))
1548 1548 except:
1549 1549 # If that failed, assume SyntaxError is a string
1550 1550 value = msg, (filename, lineno, offset, line)
1551 1551 stb = self.SyntaxTB.structured_traceback(etype, value, [])
1552 1552 self._showtraceback(etype, value, stb)
1553 1553
1554 1554 #-------------------------------------------------------------------------
1555 1555 # Things related to readline
1556 1556 #-------------------------------------------------------------------------
1557 1557
1558 1558 def init_readline(self):
1559 1559 """Command history completion/saving/reloading."""
1560 1560
1561 1561 if self.readline_use:
1562 1562 import IPython.utils.rlineimpl as readline
1563 1563
1564 1564 self.rl_next_input = None
1565 1565 self.rl_do_indent = False
1566 1566
1567 1567 if not self.readline_use or not readline.have_readline:
1568 1568 self.has_readline = False
1569 1569 self.readline = None
1570 1570 # Set a number of methods that depend on readline to be no-op
1571 1571 self.savehist = no_op
1572 1572 self.reloadhist = no_op
1573 1573 self.set_readline_completer = no_op
1574 1574 self.set_custom_completer = no_op
1575 1575 self.set_completer_frame = no_op
1576 1576 warn('Readline services not available or not loaded.')
1577 1577 else:
1578 1578 self.has_readline = True
1579 1579 self.readline = readline
1580 1580 sys.modules['readline'] = readline
1581 1581
1582 1582 # Platform-specific configuration
1583 1583 if os.name == 'nt':
1584 1584 # FIXME - check with Frederick to see if we can harmonize
1585 1585 # naming conventions with pyreadline to avoid this
1586 1586 # platform-dependent check
1587 1587 self.readline_startup_hook = readline.set_pre_input_hook
1588 1588 else:
1589 1589 self.readline_startup_hook = readline.set_startup_hook
1590 1590
1591 1591 # Load user's initrc file (readline config)
1592 1592 # Or if libedit is used, load editrc.
1593 1593 inputrc_name = os.environ.get('INPUTRC')
1594 1594 if inputrc_name is None:
1595 1595 home_dir = get_home_dir()
1596 1596 if home_dir is not None:
1597 1597 inputrc_name = '.inputrc'
1598 1598 if readline.uses_libedit:
1599 1599 inputrc_name = '.editrc'
1600 1600 inputrc_name = os.path.join(home_dir, inputrc_name)
1601 1601 if os.path.isfile(inputrc_name):
1602 1602 try:
1603 1603 readline.read_init_file(inputrc_name)
1604 1604 except:
1605 1605 warn('Problems reading readline initialization file <%s>'
1606 1606 % inputrc_name)
1607 1607
1608 1608 # Configure readline according to user's prefs
1609 1609 # This is only done if GNU readline is being used. If libedit
1610 1610 # is being used (as on Leopard) the readline config is
1611 1611 # not run as the syntax for libedit is different.
1612 1612 if not readline.uses_libedit:
1613 1613 for rlcommand in self.readline_parse_and_bind:
1614 1614 #print "loading rl:",rlcommand # dbg
1615 1615 readline.parse_and_bind(rlcommand)
1616 1616
1617 1617 # Remove some chars from the delimiters list. If we encounter
1618 1618 # unicode chars, discard them.
1619 1619 delims = readline.get_completer_delims().encode("ascii", "ignore")
1620 1620 delims = delims.translate(string._idmap,
1621 1621 self.readline_remove_delims)
1622 1622 delims = delims.replace(ESC_MAGIC, '')
1623 1623 readline.set_completer_delims(delims)
1624 1624 # otherwise we end up with a monster history after a while:
1625 1625 readline.set_history_length(1000)
1626 1626 try:
1627 1627 #print '*** Reading readline history' # dbg
1628 1628 readline.read_history_file(self.histfile)
1629 1629 except IOError:
1630 1630 pass # It doesn't exist yet.
1631 1631
1632 1632 # If we have readline, we want our history saved upon ipython
1633 1633 # exiting.
1634 1634 atexit.register(self.savehist)
1635 1635
1636 1636 # Configure auto-indent for all platforms
1637 1637 self.set_autoindent(self.autoindent)
1638 1638
1639 1639 def set_next_input(self, s):
1640 1640 """ Sets the 'default' input string for the next command line.
1641 1641
1642 1642 Requires readline.
1643 1643
1644 1644 Example:
1645 1645
1646 1646 [D:\ipython]|1> _ip.set_next_input("Hello Word")
1647 1647 [D:\ipython]|2> Hello Word_ # cursor is here
1648 1648 """
1649 1649
1650 1650 self.rl_next_input = s
1651 1651
1652 1652 # Maybe move this to the terminal subclass?
1653 1653 def pre_readline(self):
1654 1654 """readline hook to be used at the start of each line.
1655 1655
1656 1656 Currently it handles auto-indent only."""
1657 1657
1658 1658 if self.rl_do_indent:
1659 1659 self.readline.insert_text(self._indent_current_str())
1660 1660 if self.rl_next_input is not None:
1661 1661 self.readline.insert_text(self.rl_next_input)
1662 1662 self.rl_next_input = None
1663 1663
1664 1664 def _indent_current_str(self):
1665 1665 """return the current level of indentation as a string"""
1666 1666 return self.indent_current_nsp * ' '
1667 1667
1668 1668 #-------------------------------------------------------------------------
1669 1669 # Things related to text completion
1670 1670 #-------------------------------------------------------------------------
1671 1671
1672 1672 def init_completer(self):
1673 1673 """Initialize the completion machinery.
1674 1674
1675 1675 This creates completion machinery that can be used by client code,
1676 1676 either interactively in-process (typically triggered by the readline
1677 1677 library), programatically (such as in test suites) or out-of-prcess
1678 1678 (typically over the network by remote frontends).
1679 1679 """
1680 1680 from IPython.core.completer import IPCompleter
1681 1681 from IPython.core.completerlib import (module_completer,
1682 1682 magic_run_completer, cd_completer)
1683 1683
1684 1684 self.Completer = IPCompleter(self,
1685 1685 self.user_ns,
1686 1686 self.user_global_ns,
1687 1687 self.readline_omit__names,
1688 1688 self.alias_manager.alias_table,
1689 1689 self.has_readline)
1690 1690
1691 1691 # Add custom completers to the basic ones built into IPCompleter
1692 1692 sdisp = self.strdispatchers.get('complete_command', StrDispatch())
1693 1693 self.strdispatchers['complete_command'] = sdisp
1694 1694 self.Completer.custom_completers = sdisp
1695 1695
1696 1696 self.set_hook('complete_command', module_completer, str_key = 'import')
1697 1697 self.set_hook('complete_command', module_completer, str_key = 'from')
1698 1698 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
1699 1699 self.set_hook('complete_command', cd_completer, str_key = '%cd')
1700 1700
1701 1701 # Only configure readline if we truly are using readline. IPython can
1702 1702 # do tab-completion over the network, in GUIs, etc, where readline
1703 1703 # itself may be absent
1704 1704 if self.has_readline:
1705 1705 self.set_readline_completer()
1706 1706
1707 1707 def complete(self, text, line=None, cursor_pos=None):
1708 1708 """Return the completed text and a list of completions.
1709 1709
1710 1710 Parameters
1711 1711 ----------
1712 1712
1713 1713 text : string
1714 1714 A string of text to be completed on. It can be given as empty and
1715 1715 instead a line/position pair are given. In this case, the
1716 1716 completer itself will split the line like readline does.
1717 1717
1718 1718 line : string, optional
1719 1719 The complete line that text is part of.
1720 1720
1721 1721 cursor_pos : int, optional
1722 1722 The position of the cursor on the input line.
1723 1723
1724 1724 Returns
1725 1725 -------
1726 1726 text : string
1727 1727 The actual text that was completed.
1728 1728
1729 1729 matches : list
1730 1730 A sorted list with all possible completions.
1731 1731
1732 1732 The optional arguments allow the completion to take more context into
1733 1733 account, and are part of the low-level completion API.
1734 1734
1735 1735 This is a wrapper around the completion mechanism, similar to what
1736 1736 readline does at the command line when the TAB key is hit. By
1737 1737 exposing it as a method, it can be used by other non-readline
1738 1738 environments (such as GUIs) for text completion.
1739 1739
1740 1740 Simple usage example:
1741 1741
1742 1742 In [1]: x = 'hello'
1743 1743
1744 1744 In [2]: _ip.complete('x.l')
1745 1745 Out[2]: ('x.l', ['x.ljust', 'x.lower', 'x.lstrip'])
1746 1746 """
1747 1747
1748 1748 # Inject names into __builtin__ so we can complete on the added names.
1749 1749 with self.builtin_trap:
1750 1750 return self.Completer.complete(text, line, cursor_pos)
1751 1751
1752 1752 def set_custom_completer(self, completer, pos=0):
1753 1753 """Adds a new custom completer function.
1754 1754
1755 1755 The position argument (defaults to 0) is the index in the completers
1756 1756 list where you want the completer to be inserted."""
1757 1757
1758 1758 newcomp = types.MethodType(completer, self.Completer)
1759 1759 self.Completer.matchers.insert(pos,newcomp)
1760 1760
1761 1761 def set_readline_completer(self):
1762 1762 """Reset readline's completer to be our own."""
1763 1763 self.readline.set_completer(self.Completer.rlcomplete)
1764 1764
1765 1765 def set_completer_frame(self, frame=None):
1766 1766 """Set the frame of the completer."""
1767 1767 if frame:
1768 1768 self.Completer.namespace = frame.f_locals
1769 1769 self.Completer.global_namespace = frame.f_globals
1770 1770 else:
1771 1771 self.Completer.namespace = self.user_ns
1772 1772 self.Completer.global_namespace = self.user_global_ns
1773 1773
1774 1774 #-------------------------------------------------------------------------
1775 1775 # Things related to magics
1776 1776 #-------------------------------------------------------------------------
1777 1777
1778 1778 def init_magics(self):
1779 1779 # FIXME: Move the color initialization to the DisplayHook, which
1780 1780 # should be split into a prompt manager and displayhook. We probably
1781 1781 # even need a centralize colors management object.
1782 1782 self.magic_colors(self.colors)
1783 1783 # History was moved to a separate module
1784 1784 from . import history
1785 1785 history.init_ipython(self)
1786 1786
1787 1787 def magic(self,arg_s):
1788 1788 """Call a magic function by name.
1789 1789
1790 1790 Input: a string containing the name of the magic function to call and
1791 1791 any additional arguments to be passed to the magic.
1792 1792
1793 1793 magic('name -opt foo bar') is equivalent to typing at the ipython
1794 1794 prompt:
1795 1795
1796 1796 In[1]: %name -opt foo bar
1797 1797
1798 1798 To call a magic without arguments, simply use magic('name').
1799 1799
1800 1800 This provides a proper Python function to call IPython's magics in any
1801 1801 valid Python code you can type at the interpreter, including loops and
1802 1802 compound statements.
1803 1803 """
1804 1804 args = arg_s.split(' ',1)
1805 1805 magic_name = args[0]
1806 1806 magic_name = magic_name.lstrip(prefilter.ESC_MAGIC)
1807 1807
1808 1808 try:
1809 1809 magic_args = args[1]
1810 1810 except IndexError:
1811 1811 magic_args = ''
1812 1812 fn = getattr(self,'magic_'+magic_name,None)
1813 1813 if fn is None:
1814 1814 error("Magic function `%s` not found." % magic_name)
1815 1815 else:
1816 1816 magic_args = self.var_expand(magic_args,1)
1817 1817 with nested(self.builtin_trap,):
1818 1818 result = fn(magic_args)
1819 1819 return result
1820 1820
1821 1821 def define_magic(self, magicname, func):
1822 1822 """Expose own function as magic function for ipython
1823 1823
1824 1824 def foo_impl(self,parameter_s=''):
1825 1825 'My very own magic!. (Use docstrings, IPython reads them).'
1826 1826 print 'Magic function. Passed parameter is between < >:'
1827 1827 print '<%s>' % parameter_s
1828 1828 print 'The self object is:',self
1829 1829 newcomp = types.MethodType(completer, self.Completer)
1830 1830 self.define_magic('foo',foo_impl)
1831 1831 """
1832 1832
1833 1833 im = types.MethodType(func, self)
1834 1834 old = getattr(self, "magic_" + magicname, None)
1835 1835 setattr(self, "magic_" + magicname, im)
1836 1836 return old
1837 1837
1838 1838 #-------------------------------------------------------------------------
1839 1839 # Things related to macros
1840 1840 #-------------------------------------------------------------------------
1841 1841
1842 1842 def define_macro(self, name, themacro):
1843 1843 """Define a new macro
1844 1844
1845 1845 Parameters
1846 1846 ----------
1847 1847 name : str
1848 1848 The name of the macro.
1849 1849 themacro : str or Macro
1850 1850 The action to do upon invoking the macro. If a string, a new
1851 1851 Macro object is created by passing the string to it.
1852 1852 """
1853 1853
1854 1854 from IPython.core import macro
1855 1855
1856 1856 if isinstance(themacro, basestring):
1857 1857 themacro = macro.Macro(themacro)
1858 1858 if not isinstance(themacro, macro.Macro):
1859 1859 raise ValueError('A macro must be a string or a Macro instance.')
1860 1860 self.user_ns[name] = themacro
1861 1861
1862 1862 #-------------------------------------------------------------------------
1863 1863 # Things related to the running of system commands
1864 1864 #-------------------------------------------------------------------------
1865 1865
1866 1866 def system(self, cmd):
1867 1867 """Call the given cmd in a subprocess.
1868 1868
1869 1869 Parameters
1870 1870 ----------
1871 1871 cmd : str
1872 1872 Command to execute (can not end in '&', as bacground processes are
1873 1873 not supported.
1874 1874 """
1875 1875 # We do not support backgrounding processes because we either use
1876 1876 # pexpect or pipes to read from. Users can always just call
1877 1877 # os.system() if they really want a background process.
1878 1878 if cmd.endswith('&'):
1879 1879 raise OSError("Background processes not supported.")
1880 1880
1881 1881 return system(self.var_expand(cmd, depth=2))
1882 1882
1883 1883 def getoutput(self, cmd, split=True):
1884 1884 """Get output (possibly including stderr) from a subprocess.
1885 1885
1886 1886 Parameters
1887 1887 ----------
1888 1888 cmd : str
1889 1889 Command to execute (can not end in '&', as background processes are
1890 1890 not supported.
1891 1891 split : bool, optional
1892 1892
1893 1893 If True, split the output into an IPython SList. Otherwise, an
1894 1894 IPython LSString is returned. These are objects similar to normal
1895 1895 lists and strings, with a few convenience attributes for easier
1896 1896 manipulation of line-based output. You can use '?' on them for
1897 1897 details.
1898 1898 """
1899 1899 if cmd.endswith('&'):
1900 1900 raise OSError("Background processes not supported.")
1901 1901 out = getoutput(self.var_expand(cmd, depth=2))
1902 1902 if split:
1903 1903 out = SList(out.splitlines())
1904 1904 else:
1905 1905 out = LSString(out)
1906 1906 return out
1907 1907
1908 1908 #-------------------------------------------------------------------------
1909 1909 # Things related to aliases
1910 1910 #-------------------------------------------------------------------------
1911 1911
1912 1912 def init_alias(self):
1913 1913 self.alias_manager = AliasManager(shell=self, config=self.config)
1914 1914 self.ns_table['alias'] = self.alias_manager.alias_table,
1915 1915
1916 1916 #-------------------------------------------------------------------------
1917 1917 # Things related to extensions and plugins
1918 1918 #-------------------------------------------------------------------------
1919 1919
1920 1920 def init_extension_manager(self):
1921 1921 self.extension_manager = ExtensionManager(shell=self, config=self.config)
1922 1922
1923 1923 def init_plugin_manager(self):
1924 1924 self.plugin_manager = PluginManager(config=self.config)
1925 1925
1926 1926 #-------------------------------------------------------------------------
1927 1927 # Things related to payloads
1928 1928 #-------------------------------------------------------------------------
1929 1929
1930 1930 def init_payload(self):
1931 1931 self.payload_manager = PayloadManager(config=self.config)
1932 1932
1933 1933 #-------------------------------------------------------------------------
1934 1934 # Things related to the prefilter
1935 1935 #-------------------------------------------------------------------------
1936 1936
1937 1937 def init_prefilter(self):
1938 1938 self.prefilter_manager = PrefilterManager(shell=self, config=self.config)
1939 1939 # Ultimately this will be refactored in the new interpreter code, but
1940 1940 # for now, we should expose the main prefilter method (there's legacy
1941 1941 # code out there that may rely on this).
1942 1942 self.prefilter = self.prefilter_manager.prefilter_lines
1943 1943
1944 1944
1945 1945 def auto_rewrite_input(self, cmd):
1946 1946 """Print to the screen the rewritten form of the user's command.
1947 1947
1948 1948 This shows visual feedback by rewriting input lines that cause
1949 1949 automatic calling to kick in, like::
1950 1950
1951 1951 /f x
1952 1952
1953 1953 into::
1954 1954
1955 1955 ------> f(x)
1956 1956
1957 1957 after the user's input prompt. This helps the user understand that the
1958 1958 input line was transformed automatically by IPython.
1959 1959 """
1960 1960 rw = self.displayhook.prompt1.auto_rewrite() + cmd
1961 1961
1962 1962 try:
1963 1963 # plain ascii works better w/ pyreadline, on some machines, so
1964 1964 # we use it and only print uncolored rewrite if we have unicode
1965 1965 rw = str(rw)
1966 1966 print >> IPython.utils.io.Term.cout, rw
1967 1967 except UnicodeEncodeError:
1968 1968 print "------> " + cmd
1969 1969
1970 1970 #-------------------------------------------------------------------------
1971 1971 # Things related to extracting values/expressions from kernel and user_ns
1972 1972 #-------------------------------------------------------------------------
1973 1973
1974 1974 def _simple_error(self):
1975 1975 etype, value = sys.exc_info()[:2]
1976 1976 return u'[ERROR] {e.__name__}: {v}'.format(e=etype, v=value)
1977 1977
1978 1978 def user_variables(self, names):
1979 1979 """Get a list of variable names from the user's namespace.
1980 1980
1981 1981 Parameters
1982 1982 ----------
1983 1983 names : list of strings
1984 1984 A list of names of variables to be read from the user namespace.
1985 1985
1986 1986 Returns
1987 1987 -------
1988 1988 A dict, keyed by the input names and with the repr() of each value.
1989 1989 """
1990 1990 out = {}
1991 1991 user_ns = self.user_ns
1992 1992 for varname in names:
1993 1993 try:
1994 1994 value = repr(user_ns[varname])
1995 1995 except:
1996 1996 value = self._simple_error()
1997 1997 out[varname] = value
1998 1998 return out
1999 1999
2000 2000 def user_expressions(self, expressions):
2001 2001 """Evaluate a dict of expressions in the user's namespace.
2002 2002
2003 2003 Parameters
2004 2004 ----------
2005 2005 expressions : dict
2006 2006 A dict with string keys and string values. The expression values
2007 2007 should be valid Python expressions, each of which will be evaluated
2008 2008 in the user namespace.
2009 2009
2010 2010 Returns
2011 2011 -------
2012 2012 A dict, keyed like the input expressions dict, with the repr() of each
2013 2013 value.
2014 2014 """
2015 2015 out = {}
2016 2016 user_ns = self.user_ns
2017 2017 global_ns = self.user_global_ns
2018 2018 for key, expr in expressions.iteritems():
2019 2019 try:
2020 2020 value = repr(eval(expr, global_ns, user_ns))
2021 2021 except:
2022 2022 value = self._simple_error()
2023 2023 out[key] = value
2024 2024 return out
2025 2025
2026 2026 #-------------------------------------------------------------------------
2027 2027 # Things related to the running of code
2028 2028 #-------------------------------------------------------------------------
2029 2029
2030 2030 def ex(self, cmd):
2031 2031 """Execute a normal python statement in user namespace."""
2032 2032 with nested(self.builtin_trap,):
2033 2033 exec cmd in self.user_global_ns, self.user_ns
2034 2034
2035 2035 def ev(self, expr):
2036 2036 """Evaluate python expression expr in user namespace.
2037 2037
2038 2038 Returns the result of evaluation
2039 2039 """
2040 2040 with nested(self.builtin_trap,):
2041 2041 return eval(expr, self.user_global_ns, self.user_ns)
2042 2042
2043 2043 def safe_execfile(self, fname, *where, **kw):
2044 2044 """A safe version of the builtin execfile().
2045 2045
2046 2046 This version will never throw an exception, but instead print
2047 2047 helpful error messages to the screen. This only works on pure
2048 2048 Python files with the .py extension.
2049 2049
2050 2050 Parameters
2051 2051 ----------
2052 2052 fname : string
2053 2053 The name of the file to be executed.
2054 2054 where : tuple
2055 2055 One or two namespaces, passed to execfile() as (globals,locals).
2056 2056 If only one is given, it is passed as both.
2057 2057 exit_ignore : bool (False)
2058 2058 If True, then silence SystemExit for non-zero status (it is always
2059 2059 silenced for zero status, as it is so common).
2060 2060 """
2061 2061 kw.setdefault('exit_ignore', False)
2062 2062
2063 2063 fname = os.path.abspath(os.path.expanduser(fname))
2064 2064
2065 2065 # Make sure we have a .py file
2066 2066 if not fname.endswith('.py'):
2067 2067 warn('File must end with .py to be run using execfile: <%s>' % fname)
2068 2068
2069 2069 # Make sure we can open the file
2070 2070 try:
2071 2071 with open(fname) as thefile:
2072 2072 pass
2073 2073 except:
2074 2074 warn('Could not open file <%s> for safe execution.' % fname)
2075 2075 return
2076 2076
2077 2077 # Find things also in current directory. This is needed to mimic the
2078 2078 # behavior of running a script from the system command line, where
2079 2079 # Python inserts the script's directory into sys.path
2080 2080 dname = os.path.dirname(fname)
2081 2081
2082 2082 with prepended_to_syspath(dname):
2083 2083 try:
2084 2084 execfile(fname,*where)
2085 2085 except SystemExit, status:
2086 2086 # If the call was made with 0 or None exit status (sys.exit(0)
2087 2087 # or sys.exit() ), don't bother showing a traceback, as both of
2088 2088 # these are considered normal by the OS:
2089 2089 # > python -c'import sys;sys.exit(0)'; echo $?
2090 2090 # 0
2091 2091 # > python -c'import sys;sys.exit()'; echo $?
2092 2092 # 0
2093 2093 # For other exit status, we show the exception unless
2094 2094 # explicitly silenced, but only in short form.
2095 2095 if status.code not in (0, None) and not kw['exit_ignore']:
2096 2096 self.showtraceback(exception_only=True)
2097 2097 except:
2098 2098 self.showtraceback()
2099 2099
2100 2100 def safe_execfile_ipy(self, fname):
2101 2101 """Like safe_execfile, but for .ipy files with IPython syntax.
2102 2102
2103 2103 Parameters
2104 2104 ----------
2105 2105 fname : str
2106 2106 The name of the file to execute. The filename must have a
2107 2107 .ipy extension.
2108 2108 """
2109 2109 fname = os.path.abspath(os.path.expanduser(fname))
2110 2110
2111 2111 # Make sure we have a .py file
2112 2112 if not fname.endswith('.ipy'):
2113 2113 warn('File must end with .py to be run using execfile: <%s>' % fname)
2114 2114
2115 2115 # Make sure we can open the file
2116 2116 try:
2117 2117 with open(fname) as thefile:
2118 2118 pass
2119 2119 except:
2120 2120 warn('Could not open file <%s> for safe execution.' % fname)
2121 2121 return
2122 2122
2123 2123 # Find things also in current directory. This is needed to mimic the
2124 2124 # behavior of running a script from the system command line, where
2125 2125 # Python inserts the script's directory into sys.path
2126 2126 dname = os.path.dirname(fname)
2127 2127
2128 2128 with prepended_to_syspath(dname):
2129 2129 try:
2130 2130 with open(fname) as thefile:
2131 2131 script = thefile.read()
2132 2132 # self.runlines currently captures all exceptions
2133 2133 # raise in user code. It would be nice if there were
2134 2134 # versions of runlines, execfile that did raise, so
2135 2135 # we could catch the errors.
2136 2136 self.runlines(script, clean=True)
2137 2137 except:
2138 2138 self.showtraceback()
2139 2139 warn('Unknown failure executing file: <%s>' % fname)
2140 2140
2141 2141 def run_cell(self, cell):
2142 2142 """Run the contents of an entire multiline 'cell' of code.
2143 2143
2144 2144 The cell is split into separate blocks which can be executed
2145 2145 individually. Then, based on how many blocks there are, they are
2146 2146 executed as follows:
2147 2147
2148 2148 - A single block: 'single' mode.
2149 2149
2150 2150 If there's more than one block, it depends:
2151 2151
2152 2152 - if the last one is no more than two lines long, run all but the last
2153 2153 in 'exec' mode and the very last one in 'single' mode. This makes it
2154 2154 easy to type simple expressions at the end to see computed values. -
2155 2155 otherwise (last one is also multiline), run all in 'exec' mode
2156 2156
2157 2157 When code is executed in 'single' mode, :func:`sys.displayhook` fires,
2158 2158 results are displayed and output prompts are computed. In 'exec' mode,
2159 2159 no results are displayed unless :func:`print` is called explicitly;
2160 2160 this mode is more akin to running a script.
2161 2161
2162 2162 Parameters
2163 2163 ----------
2164 2164 cell : str
2165 2165 A single or multiline string.
2166 2166 """
2167 2167 #################################################################
2168 2168 # FIXME
2169 2169 # =====
2170 2170 # This execution logic should stop calling runlines altogether, and
2171 2171 # instead we should do what runlines does, in a controlled manner, here
2172 2172 # (runlines mutates lots of state as it goes calling sub-methods that
2173 2173 # also mutate state). Basically we should:
2174 2174 # - apply dynamic transforms for single-line input (the ones that
2175 2175 # split_blocks won't apply since they need context).
2176 2176 # - increment the global execution counter (we need to pull that out
2177 2177 # from outputcache's control; outputcache should instead read it from
2178 2178 # the main object).
2179 2179 # - do any logging of input
2180 2180 # - update histories (raw/translated)
2181 2181 # - then, call plain runsource (for single blocks, so displayhook is
2182 2182 # triggered) or runcode (for multiline blocks in exec mode).
2183 2183 #
2184 2184 # Once this is done, we'll be able to stop using runlines and we'll
2185 2185 # also have a much cleaner separation of logging, input history and
2186 2186 # output cache management.
2187 2187 #################################################################
2188 2188
2189 2189 # We need to break up the input into executable blocks that can be run
2190 2190 # in 'single' mode, to provide comfortable user behavior.
2191 2191 blocks = self.input_splitter.split_blocks(cell)
2192 2192
2193 2193 if not blocks:
2194 2194 return
2195 2195
2196 2196 # Single-block input should behave like an interactive prompt
2197 2197 if len(blocks) == 1:
2198 2198 self.runlines(blocks[0])
2199 2199 return
2200 2200
2201 2201 # In multi-block input, if the last block is a simple (one-two lines)
2202 2202 # expression, run it in single mode so it produces output. Otherwise
2203 2203 # just feed the whole thing to runcode.
2204 2204 # This seems like a reasonable usability design.
2205 2205 last = blocks[-1]
2206 2206
2207 2207 # Note: below, whenever we call runcode, we must sync history
2208 2208 # ourselves, because runcode is NOT meant to manage history at all.
2209 2209 if len(last.splitlines()) < 2:
2210 2210 # Get the main body to run as a cell
2211 2211 body = ''.join(blocks[:-1])
2212 2212 self.input_hist.append(body)
2213 2213 self.input_hist_raw.append(body)
2214 2214 retcode = self.runcode(body, post_execute=False)
2215 2215 if retcode==0:
2216 2216 # And the last expression via runlines so it produces output
2217 2217 self.runlines(last)
2218 2218 else:
2219 2219 # Run the whole cell as one entity
2220 2220 self.input_hist.append(cell)
2221 2221 self.input_hist_raw.append(cell)
2222 2222 self.runcode(cell)
2223 2223
2224 2224 def runlines(self, lines, clean=False):
2225 2225 """Run a string of one or more lines of source.
2226 2226
2227 2227 This method is capable of running a string containing multiple source
2228 2228 lines, as if they had been entered at the IPython prompt. Since it
2229 2229 exposes IPython's processing machinery, the given strings can contain
2230 2230 magic calls (%magic), special shell access (!cmd), etc.
2231 2231 """
2232 2232
2233 2233 if isinstance(lines, (list, tuple)):
2234 2234 lines = '\n'.join(lines)
2235 2235
2236 2236 if clean:
2237 2237 lines = self._cleanup_ipy_script(lines)
2238 2238
2239 2239 # We must start with a clean buffer, in case this is run from an
2240 2240 # interactive IPython session (via a magic, for example).
2241 2241 self.resetbuffer()
2242 2242 lines = lines.splitlines()
2243 2243 more = 0
2244 2244 with nested(self.builtin_trap, self.display_trap):
2245 2245 for line in lines:
2246 2246 # skip blank lines so we don't mess up the prompt counter, but
2247 2247 # do NOT skip even a blank line if we are in a code block (more
2248 2248 # is true)
2249 2249
2250 2250 if line or more:
2251 2251 # push to raw history, so hist line numbers stay in sync
2252 2252 self.input_hist_raw.append(line + '\n')
2253 2253 prefiltered = self.prefilter_manager.prefilter_lines(line,
2254 2254 more)
2255 2255 more = self.push_line(prefiltered)
2256 2256 # IPython's runsource returns None if there was an error
2257 2257 # compiling the code. This allows us to stop processing
2258 2258 # right away, so the user gets the error message at the
2259 2259 # right place.
2260 2260 if more is None:
2261 2261 break
2262 2262 else:
2263 2263 self.input_hist_raw.append("\n")
2264 2264 # final newline in case the input didn't have it, so that the code
2265 2265 # actually does get executed
2266 2266 if more:
2267 2267 self.push_line('\n')
2268 2268
2269 2269 def runsource(self, source, filename='<input>', symbol='single'):
2270 2270 """Compile and run some source in the interpreter.
2271 2271
2272 2272 Arguments are as for compile_command().
2273 2273
2274 2274 One several things can happen:
2275 2275
2276 2276 1) The input is incorrect; compile_command() raised an
2277 2277 exception (SyntaxError or OverflowError). A syntax traceback
2278 2278 will be printed by calling the showsyntaxerror() method.
2279 2279
2280 2280 2) The input is incomplete, and more input is required;
2281 2281 compile_command() returned None. Nothing happens.
2282 2282
2283 2283 3) The input is complete; compile_command() returned a code
2284 2284 object. The code is executed by calling self.runcode() (which
2285 2285 also handles run-time exceptions, except for SystemExit).
2286 2286
2287 2287 The return value is:
2288 2288
2289 2289 - True in case 2
2290 2290
2291 2291 - False in the other cases, unless an exception is raised, where
2292 2292 None is returned instead. This can be used by external callers to
2293 2293 know whether to continue feeding input or not.
2294 2294
2295 2295 The return value can be used to decide whether to use sys.ps1 or
2296 2296 sys.ps2 to prompt the next line."""
2297 2297
2298 2298 # We need to ensure that the source is unicode from here on.
2299 2299 if type(source)==str:
2300 2300 source = source.decode(self.stdin_encoding)
2301 2301
2302 2302 # if the source code has leading blanks, add 'if 1:\n' to it
2303 2303 # this allows execution of indented pasted code. It is tempting
2304 2304 # to add '\n' at the end of source to run commands like ' a=1'
2305 2305 # directly, but this fails for more complicated scenarios
2306 2306
2307 2307 if source[:1] in [' ', '\t']:
2308 2308 source = u'if 1:\n%s' % source
2309 2309
2310 2310 try:
2311 2311 code = self.compile(source,filename,symbol)
2312 2312 except (OverflowError, SyntaxError, ValueError, TypeError, MemoryError):
2313 2313 # Case 1
2314 2314 self.showsyntaxerror(filename)
2315 2315 return None
2316 2316
2317 2317 if code is None:
2318 2318 # Case 2
2319 2319 return True
2320 2320
2321 2321 # Case 3
2322 2322 # We store the code object so that threaded shells and
2323 2323 # custom exception handlers can access all this info if needed.
2324 2324 # The source corresponding to this can be obtained from the
2325 2325 # buffer attribute as '\n'.join(self.buffer).
2326 2326 self.code_to_run = code
2327 2327 # now actually execute the code object
2328 2328 if self.runcode(code) == 0:
2329 2329 return False
2330 2330 else:
2331 2331 return None
2332 2332
2333 2333 def runcode(self, code_obj, post_execute=True):
2334 2334 """Execute a code object.
2335 2335
2336 2336 When an exception occurs, self.showtraceback() is called to display a
2337 2337 traceback.
2338 2338
2339 2339 Return value: a flag indicating whether the code to be run completed
2340 2340 successfully:
2341 2341
2342 2342 - 0: successful execution.
2343 2343 - 1: an error occurred.
2344 2344 """
2345 2345
2346 2346 # Set our own excepthook in case the user code tries to call it
2347 2347 # directly, so that the IPython crash handler doesn't get triggered
2348 2348 old_excepthook,sys.excepthook = sys.excepthook, self.excepthook
2349 2349
2350 2350 # we save the original sys.excepthook in the instance, in case config
2351 2351 # code (such as magics) needs access to it.
2352 2352 self.sys_excepthook = old_excepthook
2353 2353 outflag = 1 # happens in more places, so it's easier as default
2354 2354 try:
2355 2355 try:
2356 2356 self.hooks.pre_runcode_hook()
2357 2357 #rprint('Running code') # dbg
2358 2358 exec code_obj in self.user_global_ns, self.user_ns
2359 2359 finally:
2360 2360 # Reset our crash handler in place
2361 2361 sys.excepthook = old_excepthook
2362 2362 except SystemExit:
2363 2363 self.resetbuffer()
2364 2364 self.showtraceback(exception_only=True)
2365 2365 warn("To exit: use any of 'exit', 'quit', %Exit or Ctrl-D.", level=1)
2366 2366 except self.custom_exceptions:
2367 2367 etype,value,tb = sys.exc_info()
2368 2368 self.CustomTB(etype,value,tb)
2369 2369 except:
2370 2370 self.showtraceback()
2371 2371 else:
2372 2372 outflag = 0
2373 2373 if softspace(sys.stdout, 0):
2374 2374 print
2375 2375
2376 2376 # Execute any registered post-execution functions. Here, any errors
2377 2377 # are reported only minimally and just on the terminal, because the
2378 2378 # main exception channel may be occupied with a user traceback.
2379 2379 # FIXME: we need to think this mechanism a little more carefully.
2380 2380 if post_execute:
2381 2381 for func in self._post_execute:
2382 2382 try:
2383 2383 func()
2384 2384 except:
2385 2385 head = '[ ERROR ] Evaluating post_execute function: %s' % \
2386 2386 func
2387 2387 print >> io.Term.cout, head
2388 2388 print >> io.Term.cout, self._simple_error()
2389 2389 print >> io.Term.cout, 'Removing from post_execute'
2390 2390 self._post_execute.remove(func)
2391 2391
2392 2392 # Flush out code object which has been run (and source)
2393 2393 self.code_to_run = None
2394 2394 return outflag
2395 2395
2396 2396 def push_line(self, line):
2397 2397 """Push a line to the interpreter.
2398 2398
2399 2399 The line should not have a trailing newline; it may have
2400 2400 internal newlines. The line is appended to a buffer and the
2401 2401 interpreter's runsource() method is called with the
2402 2402 concatenated contents of the buffer as source. If this
2403 2403 indicates that the command was executed or invalid, the buffer
2404 2404 is reset; otherwise, the command is incomplete, and the buffer
2405 2405 is left as it was after the line was appended. The return
2406 2406 value is 1 if more input is required, 0 if the line was dealt
2407 2407 with in some way (this is the same as runsource()).
2408 2408 """
2409 2409
2410 2410 # autoindent management should be done here, and not in the
2411 2411 # interactive loop, since that one is only seen by keyboard input. We
2412 2412 # need this done correctly even for code run via runlines (which uses
2413 2413 # push).
2414 2414
2415 2415 #print 'push line: <%s>' % line # dbg
2416 2416 for subline in line.splitlines():
2417 2417 self._autoindent_update(subline)
2418 2418 self.buffer.append(line)
2419 2419 more = self.runsource('\n'.join(self.buffer), self.filename)
2420 2420 if not more:
2421 2421 self.resetbuffer()
2422 2422 return more
2423 2423
2424 2424 def resetbuffer(self):
2425 2425 """Reset the input buffer."""
2426 2426 self.buffer[:] = []
2427 2427
2428 2428 def _is_secondary_block_start(self, s):
2429 2429 if not s.endswith(':'):
2430 2430 return False
2431 2431 if (s.startswith('elif') or
2432 2432 s.startswith('else') or
2433 2433 s.startswith('except') or
2434 2434 s.startswith('finally')):
2435 2435 return True
2436 2436
2437 2437 def _cleanup_ipy_script(self, script):
2438 2438 """Make a script safe for self.runlines()
2439 2439
2440 2440 Currently, IPython is lines based, with blocks being detected by
2441 2441 empty lines. This is a problem for block based scripts that may
2442 2442 not have empty lines after blocks. This script adds those empty
2443 2443 lines to make scripts safe for running in the current line based
2444 2444 IPython.
2445 2445 """
2446 2446 res = []
2447 2447 lines = script.splitlines()
2448 2448 level = 0
2449 2449
2450 2450 for l in lines:
2451 2451 lstripped = l.lstrip()
2452 2452 stripped = l.strip()
2453 2453 if not stripped:
2454 2454 continue
2455 2455 newlevel = len(l) - len(lstripped)
2456 2456 if level > 0 and newlevel == 0 and \
2457 2457 not self._is_secondary_block_start(stripped):
2458 2458 # add empty line
2459 2459 res.append('')
2460 2460 res.append(l)
2461 2461 level = newlevel
2462 2462
2463 2463 return '\n'.join(res) + '\n'
2464 2464
2465 2465 def _autoindent_update(self,line):
2466 2466 """Keep track of the indent level."""
2467 2467
2468 2468 #debugx('line')
2469 2469 #debugx('self.indent_current_nsp')
2470 2470 if self.autoindent:
2471 2471 if line:
2472 2472 inisp = num_ini_spaces(line)
2473 2473 if inisp < self.indent_current_nsp:
2474 2474 self.indent_current_nsp = inisp
2475 2475
2476 2476 if line[-1] == ':':
2477 2477 self.indent_current_nsp += 4
2478 2478 elif dedent_re.match(line):
2479 2479 self.indent_current_nsp -= 4
2480 2480 else:
2481 2481 self.indent_current_nsp = 0
2482 2482
2483 2483 #-------------------------------------------------------------------------
2484 2484 # Things related to GUI support and pylab
2485 2485 #-------------------------------------------------------------------------
2486 2486
2487 2487 def enable_pylab(self, gui=None):
2488 2488 raise NotImplementedError('Implement enable_pylab in a subclass')
2489 2489
2490 2490 #-------------------------------------------------------------------------
2491 2491 # Utilities
2492 2492 #-------------------------------------------------------------------------
2493 2493
2494 2494 def var_expand(self,cmd,depth=0):
2495 2495 """Expand python variables in a string.
2496 2496
2497 2497 The depth argument indicates how many frames above the caller should
2498 2498 be walked to look for the local namespace where to expand variables.
2499 2499
2500 2500 The global namespace for expansion is always the user's interactive
2501 2501 namespace.
2502 2502 """
2503 2503
2504 2504 return str(ItplNS(cmd,
2505 2505 self.user_ns, # globals
2506 2506 # Skip our own frame in searching for locals:
2507 2507 sys._getframe(depth+1).f_locals # locals
2508 2508 ))
2509 2509
2510 2510 def mktempfile(self,data=None):
2511 2511 """Make a new tempfile and return its filename.
2512 2512
2513 2513 This makes a call to tempfile.mktemp, but it registers the created
2514 2514 filename internally so ipython cleans it up at exit time.
2515 2515
2516 2516 Optional inputs:
2517 2517
2518 2518 - data(None): if data is given, it gets written out to the temp file
2519 2519 immediately, and the file is closed again."""
2520 2520
2521 2521 filename = tempfile.mktemp('.py','ipython_edit_')
2522 2522 self.tempfiles.append(filename)
2523 2523
2524 2524 if data:
2525 2525 tmp_file = open(filename,'w')
2526 2526 tmp_file.write(data)
2527 2527 tmp_file.close()
2528 2528 return filename
2529 2529
2530 2530 # TODO: This should be removed when Term is refactored.
2531 2531 def write(self,data):
2532 2532 """Write a string to the default output"""
2533 2533 io.Term.cout.write(data)
2534 2534
2535 2535 # TODO: This should be removed when Term is refactored.
2536 2536 def write_err(self,data):
2537 2537 """Write a string to the default error output"""
2538 2538 io.Term.cerr.write(data)
2539 2539
2540 2540 def ask_yes_no(self,prompt,default=True):
2541 2541 if self.quiet:
2542 2542 return True
2543 2543 return ask_yes_no(prompt,default)
2544 2544
2545 2545 def show_usage(self):
2546 2546 """Show a usage message"""
2547 2547 page.page(IPython.core.usage.interactive_usage)
2548 2548
2549 2549 #-------------------------------------------------------------------------
2550 2550 # Things related to IPython exiting
2551 2551 #-------------------------------------------------------------------------
2552 2552 def atexit_operations(self):
2553 2553 """This will be executed at the time of exit.
2554 2554
2555 2555 Cleanup operations and saving of persistent data that is done
2556 2556 unconditionally by IPython should be performed here.
2557 2557
2558 2558 For things that may depend on startup flags or platform specifics (such
2559 2559 as having readline or not), register a separate atexit function in the
2560 2560 code that has the appropriate information, rather than trying to
2561 2561 clutter
2562 2562 """
2563 2563 # Cleanup all tempfiles left around
2564 2564 for tfile in self.tempfiles:
2565 2565 try:
2566 2566 os.unlink(tfile)
2567 2567 except OSError:
2568 2568 pass
2569 2569
2570 2570 # Clear all user namespaces to release all references cleanly.
2571 2571 self.reset()
2572 2572
2573 2573 # Run user hooks
2574 2574 self.hooks.shutdown_hook()
2575 2575
2576 2576 def cleanup(self):
2577 2577 self.restore_sys_module_state()
2578 2578
2579 2579
2580 2580 class InteractiveShellABC(object):
2581 2581 """An abstract base class for InteractiveShell."""
2582 2582 __metaclass__ = abc.ABCMeta
2583 2583
2584 2584 InteractiveShellABC.register(InteractiveShell)
@@ -1,658 +1,658 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the inputsplitter module.
3 3 """
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (C) 2010 The IPython Development Team
6 6 #
7 7 # Distributed under the terms of the BSD License. The full license is in
8 8 # the file COPYING, distributed as part of this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14 # stdlib
15 15 import unittest
16 16 import sys
17 17
18 18 # Third party
19 19 import nose.tools as nt
20 20
21 21 # Our own
22 22 from IPython.core import inputsplitter as isp
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Semi-complete examples (also used as tests)
26 26 #-----------------------------------------------------------------------------
27 27
28 28 # Note: at the bottom, there's a slightly more complete version of this that
29 29 # can be useful during development of code here.
30 30
31 31 def mini_interactive_loop(raw_input):
32 32 """Minimal example of the logic of an interactive interpreter loop.
33 33
34 34 This serves as an example, and it is used by the test system with a fake
35 35 raw_input that simulates interactive input."""
36 36
37 37 from IPython.core.inputsplitter import InputSplitter
38 38
39 39 isp = InputSplitter()
40 40 # In practice, this input loop would be wrapped in an outside loop to read
41 41 # input indefinitely, until some exit/quit command was issued. Here we
42 42 # only illustrate the basic inner loop.
43 43 while isp.push_accepts_more():
44 44 indent = ' '*isp.indent_spaces
45 45 prompt = '>>> ' + indent
46 46 line = indent + raw_input(prompt)
47 47 isp.push(line)
48 48
49 49 # Here we just return input so we can use it in a test suite, but a real
50 50 # interpreter would instead send it for execution somewhere.
51 51 src = isp.source_reset()
52 52 #print 'Input source was:\n', src # dbg
53 53 return src
54 54
55 55 #-----------------------------------------------------------------------------
56 56 # Test utilities, just for local use
57 57 #-----------------------------------------------------------------------------
58 58
59 59 def assemble(block):
60 60 """Assemble a block into multi-line sub-blocks."""
61 61 return ['\n'.join(sub_block)+'\n' for sub_block in block]
62 62
63 63
64 64 def pseudo_input(lines):
65 65 """Return a function that acts like raw_input but feeds the input list."""
66 66 ilines = iter(lines)
67 67 def raw_in(prompt):
68 68 try:
69 69 return next(ilines)
70 70 except StopIteration:
71 71 return ''
72 72 return raw_in
73 73
74 74 #-----------------------------------------------------------------------------
75 75 # Tests
76 76 #-----------------------------------------------------------------------------
77 77 def test_spaces():
78 78 tests = [('', 0),
79 79 (' ', 1),
80 80 ('\n', 0),
81 81 (' \n', 1),
82 82 ('x', 0),
83 83 (' x', 1),
84 84 (' x',2),
85 85 (' x',4),
86 86 # Note: tabs are counted as a single whitespace!
87 87 ('\tx', 1),
88 88 ('\t x', 2),
89 89 ]
90 90
91 91 for s, nsp in tests:
92 92 nt.assert_equal(isp.num_ini_spaces(s), nsp)
93 93
94 94
95 95 def test_remove_comments():
96 96 tests = [('text', 'text'),
97 97 ('text # comment', 'text '),
98 98 ('text # comment\n', 'text \n'),
99 99 ('text # comment \n', 'text \n'),
100 100 ('line # c \nline\n','line \nline\n'),
101 101 ('line # c \nline#c2 \nline\nline #c\n\n',
102 102 'line \nline\nline\nline \n\n'),
103 103 ]
104 104
105 105 for inp, out in tests:
106 106 nt.assert_equal(isp.remove_comments(inp), out)
107 107
108 108
109 109 def test_get_input_encoding():
110 110 encoding = isp.get_input_encoding()
111 111 nt.assert_true(isinstance(encoding, basestring))
112 112 # simple-minded check that at least encoding a simple string works with the
113 113 # encoding we got.
114 114 nt.assert_equal('test'.encode(encoding), 'test')
115 115
116 116
117 117 class NoInputEncodingTestCase(unittest.TestCase):
118 118 def setUp(self):
119 119 self.old_stdin = sys.stdin
120 120 class X: pass
121 121 fake_stdin = X()
122 122 sys.stdin = fake_stdin
123 123
124 124 def test(self):
125 125 # Verify that if sys.stdin has no 'encoding' attribute we do the right
126 126 # thing
127 127 enc = isp.get_input_encoding()
128 128 self.assertEqual(enc, 'ascii')
129 129
130 130 def tearDown(self):
131 131 sys.stdin = self.old_stdin
132 132
133 133
134 134 class InputSplitterTestCase(unittest.TestCase):
135 135 def setUp(self):
136 136 self.isp = isp.InputSplitter()
137 137
138 138 def test_reset(self):
139 139 isp = self.isp
140 140 isp.push('x=1')
141 141 isp.reset()
142 142 self.assertEqual(isp._buffer, [])
143 143 self.assertEqual(isp.indent_spaces, 0)
144 144 self.assertEqual(isp.source, '')
145 145 self.assertEqual(isp.code, None)
146 146 self.assertEqual(isp._is_complete, False)
147 147
148 148 def test_source(self):
149 149 self.isp._store('1')
150 150 self.isp._store('2')
151 151 self.assertEqual(self.isp.source, '1\n2\n')
152 152 self.assertTrue(len(self.isp._buffer)>0)
153 153 self.assertEqual(self.isp.source_reset(), '1\n2\n')
154 154 self.assertEqual(self.isp._buffer, [])
155 155 self.assertEqual(self.isp.source, '')
156 156
157 157 def test_indent(self):
158 158 isp = self.isp # shorthand
159 159 isp.push('x=1')
160 160 self.assertEqual(isp.indent_spaces, 0)
161 161 isp.push('if 1:\n x=1')
162 162 self.assertEqual(isp.indent_spaces, 4)
163 163 isp.push('y=2\n')
164 164 self.assertEqual(isp.indent_spaces, 0)
165 165 isp.push('if 1:')
166 166 self.assertEqual(isp.indent_spaces, 4)
167 167 isp.push(' x=1')
168 168 self.assertEqual(isp.indent_spaces, 4)
169 169 # Blank lines shouldn't change the indent level
170 170 isp.push(' '*2)
171 171 self.assertEqual(isp.indent_spaces, 4)
172 172
173 173 def test_indent2(self):
174 174 isp = self.isp
175 175 # When a multiline statement contains parens or multiline strings, we
176 176 # shouldn't get confused.
177 177 isp.push("if 1:")
178 178 isp.push(" x = (1+\n 2)")
179 179 self.assertEqual(isp.indent_spaces, 4)
180 180
181 181 def test_dedent(self):
182 182 isp = self.isp # shorthand
183 183 isp.push('if 1:')
184 184 self.assertEqual(isp.indent_spaces, 4)
185 185 isp.push(' pass')
186 186 self.assertEqual(isp.indent_spaces, 0)
187 187
188 188 def test_push(self):
189 189 isp = self.isp
190 190 self.assertTrue(isp.push('x=1'))
191 191
192 192 def test_push2(self):
193 193 isp = self.isp
194 194 self.assertFalse(isp.push('if 1:'))
195 195 for line in [' x=1', '# a comment', ' y=2']:
196 196 self.assertTrue(isp.push(line))
197 197
198 198 def test_push3(self):
199 199 """Test input with leading whitespace"""
200 200 isp = self.isp
201 201 isp.push(' x=1')
202 202 isp.push(' y=2')
203 203 self.assertEqual(isp.source, 'if 1:\n x=1\n y=2\n')
204 204
205 205 def test_replace_mode(self):
206 206 isp = self.isp
207 207 isp.input_mode = 'cell'
208 208 isp.push('x=1')
209 209 self.assertEqual(isp.source, 'x=1\n')
210 210 isp.push('x=2')
211 211 self.assertEqual(isp.source, 'x=2\n')
212 212
213 213 def test_push_accepts_more(self):
214 214 isp = self.isp
215 215 isp.push('x=1')
216 216 self.assertFalse(isp.push_accepts_more())
217 217
218 218 def test_push_accepts_more2(self):
219 219 isp = self.isp
220 220 isp.push('if 1:')
221 221 self.assertTrue(isp.push_accepts_more())
222 222 isp.push(' x=1')
223 223 self.assertTrue(isp.push_accepts_more())
224 224 isp.push('')
225 225 self.assertFalse(isp.push_accepts_more())
226 226
227 227 def test_push_accepts_more3(self):
228 228 isp = self.isp
229 229 isp.push("x = (2+\n3)")
230 230 self.assertFalse(isp.push_accepts_more())
231 231
232 232 def test_push_accepts_more4(self):
233 233 isp = self.isp
234 234 # When a multiline statement contains parens or multiline strings, we
235 235 # shouldn't get confused.
236 236 # FIXME: we should be able to better handle de-dents in statements like
237 237 # multiline strings and multiline expressions (continued with \ or
238 238 # parens). Right now we aren't handling the indentation tracking quite
239 239 # correctly with this, though in practice it may not be too much of a
240 240 # problem. We'll need to see.
241 241 isp.push("if 1:")
242 242 isp.push(" x = (2+")
243 243 isp.push(" 3)")
244 244 self.assertTrue(isp.push_accepts_more())
245 245 isp.push(" y = 3")
246 246 self.assertTrue(isp.push_accepts_more())
247 247 isp.push('')
248 248 self.assertFalse(isp.push_accepts_more())
249 249
250 250 def test_continuation(self):
251 251 isp = self.isp
252 252 isp.push("import os, \\")
253 253 self.assertTrue(isp.push_accepts_more())
254 254 isp.push("sys")
255 255 self.assertFalse(isp.push_accepts_more())
256 256
257 257 def test_syntax_error(self):
258 258 isp = self.isp
259 259 # Syntax errors immediately produce a 'ready' block, so the invalid
260 260 # Python can be sent to the kernel for evaluation with possible ipython
261 261 # special-syntax conversion.
262 262 isp.push('run foo')
263 263 self.assertFalse(isp.push_accepts_more())
264 264
265 265 def check_split(self, block_lines, compile=True):
266 266 blocks = assemble(block_lines)
267 267 lines = ''.join(blocks)
268 268 oblock = self.isp.split_blocks(lines)
269 269 self.assertEqual(oblock, blocks)
270 270 if compile:
271 271 for block in blocks:
272 272 self.isp._compile(block)
273 273
274 274 def test_split(self):
275 275 # All blocks of input we want to test in a list. The format for each
276 276 # block is a list of lists, with each inner lists consisting of all the
277 277 # lines (as single-lines) that should make up a sub-block.
278 278
279 279 # Note: do NOT put here sub-blocks that don't compile, as the
280 280 # check_split() routine makes a final verification pass to check that
281 281 # each sub_block, as returned by split_blocks(), does compile
282 282 # correctly.
283 283 all_blocks = [ [['x=1']],
284 284
285 285 [['x=1'],
286 286 ['y=2']],
287 287
288 288 [['x=1',
289 289 '# a comment'],
290 290 ['y=11']],
291 291
292 292 [['if 1:',
293 293 ' x=1'],
294 294 ['y=3']],
295 295
296 296 [['def f(x):',
297 297 ' return x'],
298 298 ['x=1']],
299 299
300 300 [['def f(x):',
301 301 ' x+=1',
302 302 ' ',
303 303 ' return x'],
304 304 ['x=1']],
305 305
306 306 [['def f(x):',
307 307 ' if x>0:',
308 308 ' y=1',
309 309 ' # a comment',
310 310 ' else:',
311 311 ' y=4',
312 312 ' ',
313 313 ' return y'],
314 314 ['x=1'],
315 315 ['if 1:',
316 316 ' y=11'] ],
317 317
318 318 [['for i in range(10):'
319 319 ' x=i**2']],
320 320
321 321 [['for i in range(10):'
322 322 ' x=i**2'],
323 323 ['z = 1']],
324 324 ]
325 325 for block_lines in all_blocks:
326 326 self.check_split(block_lines)
327 327
328 328 def test_split_syntax_errors(self):
329 329 # Block splitting with invalid syntax
330 330 all_blocks = [ [['a syntax error']],
331 331
332 332 [['x=1',
333 333 'another syntax error']],
334 334
335 335 [['for i in range(10):'
336 336 ' yet another error']],
337 337
338 338 ]
339 339 for block_lines in all_blocks:
340 340 self.check_split(block_lines, compile=False)
341 341
342 342
343 343 class InteractiveLoopTestCase(unittest.TestCase):
344 344 """Tests for an interactive loop like a python shell.
345 345 """
346 346 def check_ns(self, lines, ns):
347 347 """Validate that the given input lines produce the resulting namespace.
348 348
349 349 Note: the input lines are given exactly as they would be typed in an
350 350 auto-indenting environment, as mini_interactive_loop above already does
351 351 auto-indenting and prepends spaces to the input.
352 352 """
353 353 src = mini_interactive_loop(pseudo_input(lines))
354 354 test_ns = {}
355 355 exec src in test_ns
356 356 # We can't check that the provided ns is identical to the test_ns,
357 357 # because Python fills test_ns with extra keys (copyright, etc). But
358 358 # we can check that the given dict is *contained* in test_ns
359 for k,v in ns.items():
359 for k,v in ns.iteritems():
360 360 self.assertEqual(test_ns[k], v)
361 361
362 362 def test_simple(self):
363 363 self.check_ns(['x=1'], dict(x=1))
364 364
365 365 def test_simple2(self):
366 366 self.check_ns(['if 1:', 'x=2'], dict(x=2))
367 367
368 368 def test_xy(self):
369 369 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
370 370
371 371 def test_abc(self):
372 372 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
373 373
374 374 def test_multi(self):
375 375 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
376 376
377 377
378 378 def test_LineInfo():
379 379 """Simple test for LineInfo construction and str()"""
380 380 linfo = isp.LineInfo(' %cd /home')
381 381 nt.assert_equals(str(linfo), 'LineInfo [ |%|cd|/home]')
382 382
383 383
384 384 def test_split_user_input():
385 385 """Unicode test - split_user_input already has good doctests"""
386 386 line = u"PΓ©rez Fernando"
387 387 parts = isp.split_user_input(line)
388 388 parts_expected = (u'', u'', u'', line)
389 389 nt.assert_equal(parts, parts_expected)
390 390
391 391
392 392 # Transformer tests
393 393 def transform_checker(tests, func):
394 394 """Utility to loop over test inputs"""
395 395 for inp, tr in tests:
396 396 nt.assert_equals(func(inp), tr)
397 397
398 398 # Data for all the syntax tests in the form of lists of pairs of
399 399 # raw/transformed input. We store it here as a global dict so that we can use
400 400 # it both within single-function tests and also to validate the behavior of the
401 401 # larger objects
402 402
403 403 syntax = \
404 404 dict(assign_system =
405 405 [('a =! ls', 'a = get_ipython().getoutput("ls")'),
406 406 ('b = !ls', 'b = get_ipython().getoutput("ls")'),
407 407 ('x=1', 'x=1'), # normal input is unmodified
408 408 (' ',' '), # blank lines are kept intact
409 409 ],
410 410
411 411 assign_magic =
412 412 [('a =% who', 'a = get_ipython().magic("who")'),
413 413 ('b = %who', 'b = get_ipython().magic("who")'),
414 414 ('x=1', 'x=1'), # normal input is unmodified
415 415 (' ',' '), # blank lines are kept intact
416 416 ],
417 417
418 418 classic_prompt =
419 419 [('>>> x=1', 'x=1'),
420 420 ('x=1', 'x=1'), # normal input is unmodified
421 421 (' ', ' '), # blank lines are kept intact
422 422 ('... ', ''), # continuation prompts
423 423 ],
424 424
425 425 ipy_prompt =
426 426 [('In [1]: x=1', 'x=1'),
427 427 ('x=1', 'x=1'), # normal input is unmodified
428 428 (' ',' '), # blank lines are kept intact
429 429 (' ....: ', ''), # continuation prompts
430 430 ],
431 431
432 432 # Tests for the escape transformer to leave normal code alone
433 433 escaped_noesc =
434 434 [ (' ', ' '),
435 435 ('x=1', 'x=1'),
436 436 ],
437 437
438 438 # System calls
439 439 escaped_shell =
440 440 [ ('!ls', 'get_ipython().system("ls")'),
441 441 # Double-escape shell, this means to capture the output of the
442 442 # subprocess and return it
443 443 ('!!ls', 'get_ipython().getoutput("ls")'),
444 444 ],
445 445
446 446 # Help/object info
447 447 escaped_help =
448 448 [ ('?', 'get_ipython().show_usage()'),
449 449 ('?x1', 'get_ipython().magic("pinfo x1")'),
450 450 ('??x2', 'get_ipython().magic("pinfo2 x2")'),
451 451 ('x3?', 'get_ipython().magic("pinfo x3")'),
452 452 ('x4??', 'get_ipython().magic("pinfo2 x4")'),
453 453 ('%hist?', 'get_ipython().magic("pinfo %hist")'),
454 454 ('f*?', 'get_ipython().magic("psearch f*")'),
455 455 ('ax.*aspe*?', 'get_ipython().magic("psearch ax.*aspe*")'),
456 456 ],
457 457
458 458 # Explicit magic calls
459 459 escaped_magic =
460 460 [ ('%cd', 'get_ipython().magic("cd")'),
461 461 ('%cd /home', 'get_ipython().magic("cd /home")'),
462 462 (' %magic', ' get_ipython().magic("magic")'),
463 463 ],
464 464
465 465 # Quoting with separate arguments
466 466 escaped_quote =
467 467 [ (',f', 'f("")'),
468 468 (',f x', 'f("x")'),
469 469 (' ,f y', ' f("y")'),
470 470 (',f a b', 'f("a", "b")'),
471 471 ],
472 472
473 473 # Quoting with single argument
474 474 escaped_quote2 =
475 475 [ (';f', 'f("")'),
476 476 (';f x', 'f("x")'),
477 477 (' ;f y', ' f("y")'),
478 478 (';f a b', 'f("a b")'),
479 479 ],
480 480
481 481 # Simply apply parens
482 482 escaped_paren =
483 483 [ ('/f', 'f()'),
484 484 ('/f x', 'f(x)'),
485 485 (' /f y', ' f(y)'),
486 486 ('/f a b', 'f(a, b)'),
487 487 ],
488 488
489 489 )
490 490
491 491 # multiline syntax examples. Each of these should be a list of lists, with
492 492 # each entry itself having pairs of raw/transformed input. The union (with
493 493 # '\n'.join() of the transformed inputs is what the splitter should produce
494 494 # when fed the raw lines one at a time via push.
495 495 syntax_ml = \
496 496 dict(classic_prompt =
497 497 [ [('>>> for i in range(10):','for i in range(10):'),
498 498 ('... print i',' print i'),
499 499 ('... ', ''),
500 500 ],
501 501 ],
502 502
503 503 ipy_prompt =
504 504 [ [('In [24]: for i in range(10):','for i in range(10):'),
505 505 (' ....: print i',' print i'),
506 506 (' ....: ', ''),
507 507 ],
508 508 ],
509 509 )
510 510
511 511
512 512 def test_assign_system():
513 513 transform_checker(syntax['assign_system'], isp.transform_assign_system)
514 514
515 515
516 516 def test_assign_magic():
517 517 transform_checker(syntax['assign_magic'], isp.transform_assign_magic)
518 518
519 519
520 520 def test_classic_prompt():
521 521 transform_checker(syntax['classic_prompt'], isp.transform_classic_prompt)
522 522 for example in syntax_ml['classic_prompt']:
523 523 transform_checker(example, isp.transform_classic_prompt)
524 524
525 525
526 526 def test_ipy_prompt():
527 527 transform_checker(syntax['ipy_prompt'], isp.transform_ipy_prompt)
528 528 for example in syntax_ml['ipy_prompt']:
529 529 transform_checker(example, isp.transform_ipy_prompt)
530 530
531 531
532 532 def test_escaped_noesc():
533 533 transform_checker(syntax['escaped_noesc'], isp.transform_escaped)
534 534
535 535
536 536 def test_escaped_shell():
537 537 transform_checker(syntax['escaped_shell'], isp.transform_escaped)
538 538
539 539
540 540 def test_escaped_help():
541 541 transform_checker(syntax['escaped_help'], isp.transform_escaped)
542 542
543 543
544 544 def test_escaped_magic():
545 545 transform_checker(syntax['escaped_magic'], isp.transform_escaped)
546 546
547 547
548 548 def test_escaped_quote():
549 549 transform_checker(syntax['escaped_quote'], isp.transform_escaped)
550 550
551 551
552 552 def test_escaped_quote2():
553 553 transform_checker(syntax['escaped_quote2'], isp.transform_escaped)
554 554
555 555
556 556 def test_escaped_paren():
557 557 transform_checker(syntax['escaped_paren'], isp.transform_escaped)
558 558
559 559
560 560 class IPythonInputTestCase(InputSplitterTestCase):
561 561 """By just creating a new class whose .isp is a different instance, we
562 562 re-run the same test battery on the new input splitter.
563 563
564 564 In addition, this runs the tests over the syntax and syntax_ml dicts that
565 565 were tested by individual functions, as part of the OO interface.
566 566 """
567 567
568 568 def setUp(self):
569 569 self.isp = isp.IPythonInputSplitter(input_mode='line')
570 570
571 571 def test_syntax(self):
572 572 """Call all single-line syntax tests from the main object"""
573 573 isp = self.isp
574 574 for example in syntax.itervalues():
575 575 for raw, out_t in example:
576 576 if raw.startswith(' '):
577 577 continue
578 578
579 579 isp.push(raw)
580 580 out = isp.source_reset().rstrip()
581 581 self.assertEqual(out, out_t)
582 582
583 583 def test_syntax_multiline(self):
584 584 isp = self.isp
585 585 for example in syntax_ml.itervalues():
586 586 out_t_parts = []
587 587 for line_pairs in example:
588 588 for raw, out_t_part in line_pairs:
589 589 isp.push(raw)
590 590 out_t_parts.append(out_t_part)
591 591
592 592 out = isp.source_reset().rstrip()
593 593 out_t = '\n'.join(out_t_parts).rstrip()
594 594 self.assertEqual(out, out_t)
595 595
596 596
597 597 class BlockIPythonInputTestCase(IPythonInputTestCase):
598 598
599 599 # Deactivate tests that don't make sense for the block mode
600 600 test_push3 = test_split = lambda s: None
601 601
602 602 def setUp(self):
603 603 self.isp = isp.IPythonInputSplitter(input_mode='cell')
604 604
605 605 def test_syntax_multiline(self):
606 606 isp = self.isp
607 607 for example in syntax_ml.itervalues():
608 608 raw_parts = []
609 609 out_t_parts = []
610 610 for line_pairs in example:
611 611 for raw, out_t_part in line_pairs:
612 612 raw_parts.append(raw)
613 613 out_t_parts.append(out_t_part)
614 614
615 615 raw = '\n'.join(raw_parts)
616 616 out_t = '\n'.join(out_t_parts)
617 617
618 618 isp.push(raw)
619 619 out = isp.source_reset()
620 620 # Match ignoring trailing whitespace
621 621 self.assertEqual(out.rstrip(), out_t.rstrip())
622 622
623 623
624 624 #-----------------------------------------------------------------------------
625 625 # Main - use as a script, mostly for developer experiments
626 626 #-----------------------------------------------------------------------------
627 627
628 628 if __name__ == '__main__':
629 629 # A simple demo for interactive experimentation. This code will not get
630 630 # picked up by any test suite.
631 631 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
632 632
633 633 # configure here the syntax to use, prompt and whether to autoindent
634 634 #isp, start_prompt = InputSplitter(), '>>> '
635 635 isp, start_prompt = IPythonInputSplitter(), 'In> '
636 636
637 637 autoindent = True
638 638 #autoindent = False
639 639
640 640 try:
641 641 while True:
642 642 prompt = start_prompt
643 643 while isp.push_accepts_more():
644 644 indent = ' '*isp.indent_spaces
645 645 if autoindent:
646 646 line = indent + raw_input(prompt+indent)
647 647 else:
648 648 line = raw_input(prompt)
649 649 isp.push(line)
650 650 prompt = '... '
651 651
652 652 # Here we just return input so we can use it in a test suite, but a
653 653 # real interpreter would instead send it for execution somewhere.
654 654 #src = isp.source; raise EOFError # dbg
655 655 src = isp.source_reset()
656 656 print 'Input source was:\n', src
657 657 except EOFError:
658 658 print 'Bye'
@@ -1,357 +1,357 b''
1 1 """Tests for various magic functions.
2 2
3 3 Needs to be run by nose (to make ipython session available).
4 4 """
5 5 from __future__ import absolute_import
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Imports
9 9 #-----------------------------------------------------------------------------
10 10
11 11 import os
12 12 import sys
13 13 import tempfile
14 14 import types
15 15 from cStringIO import StringIO
16 16
17 17 import nose.tools as nt
18 18
19 19 from IPython.utils.path import get_long_path_name
20 20 from IPython.testing import decorators as dec
21 21 from IPython.testing import tools as tt
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Test functions begin
25 25 #-----------------------------------------------------------------------------
26 26 def test_rehashx():
27 27 # clear up everything
28 28 _ip = get_ipython()
29 29 _ip.alias_manager.alias_table.clear()
30 30 del _ip.db['syscmdlist']
31 31
32 32 _ip.magic('rehashx')
33 33 # Practically ALL ipython development systems will have more than 10 aliases
34 34
35 35 yield (nt.assert_true, len(_ip.alias_manager.alias_table) > 10)
36 for key, val in _ip.alias_manager.alias_table.items():
36 for key, val in _ip.alias_manager.alias_table.iteritems():
37 37 # we must strip dots from alias names
38 38 nt.assert_true('.' not in key)
39 39
40 40 # rehashx must fill up syscmdlist
41 41 scoms = _ip.db['syscmdlist']
42 42 yield (nt.assert_true, len(scoms) > 10)
43 43
44 44
45 45 def test_magic_parse_options():
46 46 """Test that we don't mangle paths when parsing magic options."""
47 47 ip = get_ipython()
48 48 path = 'c:\\x'
49 49 opts = ip.parse_options('-f %s' % path,'f:')[0]
50 50 # argv splitting is os-dependent
51 51 if os.name == 'posix':
52 52 expected = 'c:x'
53 53 else:
54 54 expected = path
55 55 nt.assert_equals(opts['f'], expected)
56 56
57 57
58 58 def doctest_hist_f():
59 59 """Test %hist -f with temporary filename.
60 60
61 61 In [9]: import tempfile
62 62
63 63 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
64 64
65 65 In [11]: %hist -n -f $tfile 3
66 66
67 67 In [13]: import os; os.unlink(tfile)
68 68 """
69 69
70 70
71 71 def doctest_hist_r():
72 72 """Test %hist -r
73 73
74 74 XXX - This test is not recording the output correctly. For some reason, in
75 75 testing mode the raw history isn't getting populated. No idea why.
76 76 Disabling the output checking for now, though at least we do run it.
77 77
78 78 In [1]: 'hist' in _ip.lsmagic()
79 79 Out[1]: True
80 80
81 81 In [2]: x=1
82 82
83 83 In [3]: %hist -r 2
84 84 x=1 # random
85 85 %hist -r 2
86 86 """
87 87
88 88 def doctest_hist_op():
89 89 """Test %hist -op
90 90
91 91 In [1]: class b:
92 92 ...: pass
93 93 ...:
94 94
95 95 In [2]: class s(b):
96 96 ...: def __str__(self):
97 97 ...: return 's'
98 98 ...:
99 99
100 100 In [3]:
101 101
102 102 In [4]: class r(b):
103 103 ...: def __repr__(self):
104 104 ...: return 'r'
105 105 ...:
106 106
107 107 In [5]: class sr(s,r): pass
108 108 ...:
109 109
110 110 In [6]:
111 111
112 112 In [7]: bb=b()
113 113
114 114 In [8]: ss=s()
115 115
116 116 In [9]: rr=r()
117 117
118 118 In [10]: ssrr=sr()
119 119
120 120 In [11]: bb
121 121 Out[11]: <...b instance at ...>
122 122
123 123 In [12]: ss
124 124 Out[12]: <...s instance at ...>
125 125
126 126 In [13]:
127 127
128 128 In [14]: %hist -op
129 129 >>> class b:
130 130 ... pass
131 131 ...
132 132 >>> class s(b):
133 133 ... def __str__(self):
134 134 ... return 's'
135 135 ...
136 136 >>>
137 137 >>> class r(b):
138 138 ... def __repr__(self):
139 139 ... return 'r'
140 140 ...
141 141 >>> class sr(s,r): pass
142 142 >>>
143 143 >>> bb=b()
144 144 >>> ss=s()
145 145 >>> rr=r()
146 146 >>> ssrr=sr()
147 147 >>> bb
148 148 <...b instance at ...>
149 149 >>> ss
150 150 <...s instance at ...>
151 151 >>>
152 152 """
153 153
154 154 def test_shist():
155 155 # Simple tests of ShadowHist class - test generator.
156 156 import os, shutil, tempfile
157 157
158 158 from IPython.utils import pickleshare
159 159 from IPython.core.history import ShadowHist
160 160
161 161 tfile = tempfile.mktemp('','tmp-ipython-')
162 162
163 163 db = pickleshare.PickleShareDB(tfile)
164 164 s = ShadowHist(db)
165 165 s.add('hello')
166 166 s.add('world')
167 167 s.add('hello')
168 168 s.add('hello')
169 169 s.add('karhu')
170 170
171 171 yield nt.assert_equals,s.all(),[(1, 'hello'), (2, 'world'), (3, 'karhu')]
172 172
173 173 yield nt.assert_equal,s.get(2),'world'
174 174
175 175 shutil.rmtree(tfile)
176 176
177 177
178 178 # XXX failing for now, until we get clearcmd out of quarantine. But we should
179 179 # fix this and revert the skip to happen only if numpy is not around.
180 180 #@dec.skipif_not_numpy
181 181 @dec.skip_known_failure
182 182 def test_numpy_clear_array_undec():
183 183 from IPython.extensions import clearcmd
184 184
185 185 _ip.ex('import numpy as np')
186 186 _ip.ex('a = np.empty(2)')
187 187 yield (nt.assert_true, 'a' in _ip.user_ns)
188 188 _ip.magic('clear array')
189 189 yield (nt.assert_false, 'a' in _ip.user_ns)
190 190
191 191
192 192 # Multiple tests for clipboard pasting
193 193 @dec.parametric
194 194 def test_paste():
195 195 _ip = get_ipython()
196 196 def paste(txt, flags='-q'):
197 197 """Paste input text, by default in quiet mode"""
198 198 hooks.clipboard_get = lambda : txt
199 199 _ip.magic('paste '+flags)
200 200
201 201 # Inject fake clipboard hook but save original so we can restore it later
202 202 hooks = _ip.hooks
203 203 user_ns = _ip.user_ns
204 204 original_clip = hooks.clipboard_get
205 205
206 206 try:
207 207 # This try/except with an emtpy except clause is here only because
208 208 # try/yield/finally is invalid syntax in Python 2.4. This will be
209 209 # removed when we drop 2.4-compatibility, and the emtpy except below
210 210 # will be changed to a finally.
211 211
212 212 # Run tests with fake clipboard function
213 213 user_ns.pop('x', None)
214 214 paste('x=1')
215 215 yield nt.assert_equal(user_ns['x'], 1)
216 216
217 217 user_ns.pop('x', None)
218 218 paste('>>> x=2')
219 219 yield nt.assert_equal(user_ns['x'], 2)
220 220
221 221 paste("""
222 222 >>> x = [1,2,3]
223 223 >>> y = []
224 224 >>> for i in x:
225 225 ... y.append(i**2)
226 226 ...
227 227 """)
228 228 yield nt.assert_equal(user_ns['x'], [1,2,3])
229 229 yield nt.assert_equal(user_ns['y'], [1,4,9])
230 230
231 231 # Now, test that paste -r works
232 232 user_ns.pop('x', None)
233 233 yield nt.assert_false('x' in user_ns)
234 234 _ip.magic('paste -r')
235 235 yield nt.assert_equal(user_ns['x'], [1,2,3])
236 236
237 237 # Also test paste echoing, by temporarily faking the writer
238 238 w = StringIO()
239 239 writer = _ip.write
240 240 _ip.write = w.write
241 241 code = """
242 242 a = 100
243 243 b = 200"""
244 244 try:
245 245 paste(code,'')
246 246 out = w.getvalue()
247 247 finally:
248 248 _ip.write = writer
249 249 yield nt.assert_equal(user_ns['a'], 100)
250 250 yield nt.assert_equal(user_ns['b'], 200)
251 251 yield nt.assert_equal(out, code+"\n## -- End pasted text --\n")
252 252
253 253 finally:
254 254 # This should be in a finally clause, instead of the bare except above.
255 255 # Restore original hook
256 256 hooks.clipboard_get = original_clip
257 257
258 258
259 259 def test_time():
260 260 _ip.magic('time None')
261 261
262 262
263 263 def doctest_time():
264 264 """
265 265 In [10]: %time None
266 266 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
267 267 Wall time: 0.00 s
268 268 """
269 269
270 270
271 271 def test_doctest_mode():
272 272 "Toggle doctest_mode twice, it should be a no-op and run without error"
273 273 _ip.magic('doctest_mode')
274 274 _ip.magic('doctest_mode')
275 275
276 276
277 277 def test_parse_options():
278 278 """Tests for basic options parsing in magics."""
279 279 # These are only the most minimal of tests, more should be added later. At
280 280 # the very least we check that basic text/unicode calls work OK.
281 281 nt.assert_equal(_ip.parse_options('foo', '')[1], 'foo')
282 282 nt.assert_equal(_ip.parse_options(u'foo', '')[1], u'foo')
283 283
284 284
285 285 def test_dirops():
286 286 """Test various directory handling operations."""
287 287 curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
288 288
289 289 startdir = os.getcwd()
290 290 ipdir = _ip.ipython_dir
291 291 try:
292 292 _ip.magic('cd "%s"' % ipdir)
293 293 nt.assert_equal(curpath(), ipdir)
294 294 _ip.magic('cd -')
295 295 nt.assert_equal(curpath(), startdir)
296 296 _ip.magic('pushd "%s"' % ipdir)
297 297 nt.assert_equal(curpath(), ipdir)
298 298 _ip.magic('popd')
299 299 nt.assert_equal(curpath(), startdir)
300 300 finally:
301 301 os.chdir(startdir)
302 302
303 303
304 304 def check_cpaste(code, should_fail=False):
305 305 """Execute code via 'cpaste' and ensure it was executed, unless
306 306 should_fail is set.
307 307 """
308 308 _ip.user_ns['code_ran'] = False
309 309
310 310 src = StringIO()
311 311 src.write('\n')
312 312 src.write(code)
313 313 src.write('\n--\n')
314 314 src.seek(0)
315 315
316 316 stdin_save = sys.stdin
317 317 sys.stdin = src
318 318
319 319 try:
320 320 _ip.magic('cpaste')
321 321 except:
322 322 if not should_fail:
323 323 raise AssertionError("Failure not expected : '%s'" %
324 324 code)
325 325 else:
326 326 assert _ip.user_ns['code_ran']
327 327 if should_fail:
328 328 raise AssertionError("Failure expected : '%s'" % code)
329 329 finally:
330 330 sys.stdin = stdin_save
331 331
332 332
333 333 def test_cpaste():
334 334 """Test cpaste magic"""
335 335
336 336 def run():
337 337 """Marker function: sets a flag when executed.
338 338 """
339 339 _ip.user_ns['code_ran'] = True
340 340 return 'run' # return string so '+ run()' doesn't result in success
341 341
342 342 tests = {'pass': ["> > > run()",
343 343 ">>> > run()",
344 344 "+++ run()",
345 345 "++ run()",
346 346 " >>> run()"],
347 347
348 348 'fail': ["+ + run()",
349 349 " ++ run()"]}
350 350
351 351 _ip.user_ns['run'] = run
352 352
353 353 for code in tests['pass']:
354 354 check_cpaste(code)
355 355
356 356 for code in tests['fail']:
357 357 check_cpaste(code, should_fail=True)
@@ -1,2501 +1,2472 b''
1 # -*- coding: utf-8 -*-
1 2 # configobj.py
2 3 # A config file reader/writer that supports nested sections in config files.
3 4 # Copyright (C) 2005-2008 Michael Foord, Nicola Larosa
4 5 # E-mail: fuzzyman AT voidspace DOT org DOT uk
5 6 # nico AT tekNico DOT net
6 7
7 8 # ConfigObj 4
8 9 # http://www.voidspace.org.uk/python/configobj.html
9 10
10 11 # Released subject to the BSD License
11 12 # Please see http://www.voidspace.org.uk/python/license.shtml
12 13
13 14 # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
14 15 # For information about bugfixes, updates and support, please join the
15 16 # ConfigObj mailing list:
16 17 # http://lists.sourceforge.net/lists/listinfo/configobj-develop
17 18 # Comments, suggestions and bug reports welcome.
18 19
19 20 from __future__ import generators
20 21
21 22 import sys
22 23 INTP_VER = sys.version_info[:2]
23 24 if INTP_VER < (2, 2):
24 25 raise RuntimeError("Python v.2.2 or later needed")
25 26
26 27 import os, re
27 28 compiler = None
28 29 try:
29 30 import compiler
30 31 except ImportError:
31 32 # for IronPython
32 33 pass
33 34 from types import StringTypes
34 35 from warnings import warn
35 try:
36 from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
37 except ImportError:
38 # Python 2.2 does not have these
39 # UTF-8
40 BOM_UTF8 = '\xef\xbb\xbf'
41 # UTF-16, little endian
42 BOM_UTF16_LE = '\xff\xfe'
43 # UTF-16, big endian
44 BOM_UTF16_BE = '\xfe\xff'
45 if sys.byteorder == 'little':
46 # UTF-16, native endianness
47 BOM_UTF16 = BOM_UTF16_LE
48 else:
49 # UTF-16, native endianness
50 BOM_UTF16 = BOM_UTF16_BE
36 from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
51 37
52 38 # A dictionary mapping BOM to
53 39 # the encoding to decode with, and what to set the
54 40 # encoding attribute to.
55 41 BOMS = {
56 42 BOM_UTF8: ('utf_8', None),
57 43 BOM_UTF16_BE: ('utf16_be', 'utf_16'),
58 44 BOM_UTF16_LE: ('utf16_le', 'utf_16'),
59 45 BOM_UTF16: ('utf_16', 'utf_16'),
60 46 }
61 47 # All legal variants of the BOM codecs.
62 48 # TODO: the list of aliases is not meant to be exhaustive, is there a
63 49 # better way ?
64 50 BOM_LIST = {
65 51 'utf_16': 'utf_16',
66 52 'u16': 'utf_16',
67 53 'utf16': 'utf_16',
68 54 'utf-16': 'utf_16',
69 55 'utf16_be': 'utf16_be',
70 56 'utf_16_be': 'utf16_be',
71 57 'utf-16be': 'utf16_be',
72 58 'utf16_le': 'utf16_le',
73 59 'utf_16_le': 'utf16_le',
74 60 'utf-16le': 'utf16_le',
75 61 'utf_8': 'utf_8',
76 62 'u8': 'utf_8',
77 63 'utf': 'utf_8',
78 64 'utf8': 'utf_8',
79 65 'utf-8': 'utf_8',
80 66 }
81 67
82 68 # Map of encodings to the BOM to write.
83 69 BOM_SET = {
84 70 'utf_8': BOM_UTF8,
85 71 'utf_16': BOM_UTF16,
86 72 'utf16_be': BOM_UTF16_BE,
87 73 'utf16_le': BOM_UTF16_LE,
88 74 None: BOM_UTF8
89 75 }
90 76
91 77
92 78 def match_utf8(encoding):
93 79 return BOM_LIST.get(encoding.lower()) == 'utf_8'
94 80
95 81
96 82 # Quote strings used for writing values
97 83 squot = "'%s'"
98 84 dquot = '"%s"'
99 85 noquot = "%s"
100 86 wspace_plus = ' \r\t\n\v\t\'"'
101 87 tsquot = '"""%s"""'
102 88 tdquot = "'''%s'''"
103 89
104 try:
105 enumerate
106 except NameError:
107 def enumerate(obj):
108 """enumerate for Python 2.2."""
109 i = -1
110 for item in obj:
111 i += 1
112 yield i, item
113
114 try:
115 True, False
116 except NameError:
117 True, False = 1, 0
118
119 90
120 91 __version__ = '4.5.2'
121 92
122 93 __revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
123 94
124 95 __docformat__ = "restructuredtext en"
125 96
126 97 __all__ = (
127 98 '__version__',
128 99 'DEFAULT_INDENT_TYPE',
129 100 'DEFAULT_INTERPOLATION',
130 101 'ConfigObjError',
131 102 'NestingError',
132 103 'ParseError',
133 104 'DuplicateError',
134 105 'ConfigspecError',
135 106 'ConfigObj',
136 107 'SimpleVal',
137 108 'InterpolationError',
138 109 'InterpolationLoopError',
139 110 'MissingInterpolationOption',
140 111 'RepeatSectionError',
141 112 'ReloadError',
142 113 'UnreprError',
143 114 'UnknownType',
144 115 '__docformat__',
145 116 'flatten_errors',
146 117 )
147 118
148 119 DEFAULT_INTERPOLATION = 'configparser'
149 120 DEFAULT_INDENT_TYPE = ' '
150 121 MAX_INTERPOL_DEPTH = 10
151 122
152 123 OPTION_DEFAULTS = {
153 124 'interpolation': True,
154 125 'raise_errors': False,
155 126 'list_values': True,
156 127 'create_empty': False,
157 128 'file_error': False,
158 129 'configspec': None,
159 130 'stringify': True,
160 131 # option may be set to one of ('', ' ', '\t')
161 132 'indent_type': None,
162 133 'encoding': None,
163 134 'default_encoding': None,
164 135 'unrepr': False,
165 136 'write_empty_values': False,
166 137 }
167 138
168 139
169 140
170 141 def getObj(s):
171 142 s = "a=" + s
172 143 if compiler is None:
173 144 raise ImportError('compiler module not available')
174 145 p = compiler.parse(s)
175 146 return p.getChildren()[1].getChildren()[0].getChildren()[1]
176 147
177 148
178 149 class UnknownType(Exception):
179 150 pass
180 151
181 152
182 153 class Builder(object):
183 154
184 155 def build(self, o):
185 156 m = getattr(self, 'build_' + o.__class__.__name__, None)
186 157 if m is None:
187 158 raise UnknownType(o.__class__.__name__)
188 159 return m(o)
189 160
190 161 def build_List(self, o):
191 162 return map(self.build, o.getChildren())
192 163
193 164 def build_Const(self, o):
194 165 return o.value
195 166
196 167 def build_Dict(self, o):
197 168 d = {}
198 169 i = iter(map(self.build, o.getChildren()))
199 170 for el in i:
200 171 d[el] = i.next()
201 172 return d
202 173
203 174 def build_Tuple(self, o):
204 175 return tuple(self.build_List(o))
205 176
206 177 def build_Name(self, o):
207 178 if o.name == 'None':
208 179 return None
209 180 if o.name == 'True':
210 181 return True
211 182 if o.name == 'False':
212 183 return False
213 184
214 185 # An undefined Name
215 186 raise UnknownType('Undefined Name')
216 187
217 188 def build_Add(self, o):
218 189 real, imag = map(self.build_Const, o.getChildren())
219 190 try:
220 191 real = float(real)
221 192 except TypeError:
222 193 raise UnknownType('Add')
223 194 if not isinstance(imag, complex) or imag.real != 0.0:
224 195 raise UnknownType('Add')
225 196 return real+imag
226 197
227 198 def build_Getattr(self, o):
228 199 parent = self.build(o.expr)
229 200 return getattr(parent, o.attrname)
230 201
231 202 def build_UnarySub(self, o):
232 203 return -self.build_Const(o.getChildren()[0])
233 204
234 205 def build_UnaryAdd(self, o):
235 206 return self.build_Const(o.getChildren()[0])
236 207
237 208
238 209 _builder = Builder()
239 210
240 211
241 212 def unrepr(s):
242 213 if not s:
243 214 return s
244 215 return _builder.build(getObj(s))
245 216
246 217
247 218
248 219 class ConfigObjError(SyntaxError):
249 220 """
250 221 This is the base class for all errors that ConfigObj raises.
251 222 It is a subclass of SyntaxError.
252 223 """
253 224 def __init__(self, message='', line_number=None, line=''):
254 225 self.line = line
255 226 self.line_number = line_number
256 227 self.message = message
257 228 SyntaxError.__init__(self, message)
258 229
259 230
260 231 class NestingError(ConfigObjError):
261 232 """
262 233 This error indicates a level of nesting that doesn't match.
263 234 """
264 235
265 236
266 237 class ParseError(ConfigObjError):
267 238 """
268 239 This error indicates that a line is badly written.
269 240 It is neither a valid ``key = value`` line,
270 241 nor a valid section marker line.
271 242 """
272 243
273 244
274 245 class ReloadError(IOError):
275 246 """
276 247 A 'reload' operation failed.
277 248 This exception is a subclass of ``IOError``.
278 249 """
279 250 def __init__(self):
280 251 IOError.__init__(self, 'reload failed, filename is not set.')
281 252
282 253
283 254 class DuplicateError(ConfigObjError):
284 255 """
285 256 The keyword or section specified already exists.
286 257 """
287 258
288 259
289 260 class ConfigspecError(ConfigObjError):
290 261 """
291 262 An error occured whilst parsing a configspec.
292 263 """
293 264
294 265
295 266 class InterpolationError(ConfigObjError):
296 267 """Base class for the two interpolation errors."""
297 268
298 269
299 270 class InterpolationLoopError(InterpolationError):
300 271 """Maximum interpolation depth exceeded in string interpolation."""
301 272
302 273 def __init__(self, option):
303 274 InterpolationError.__init__(
304 275 self,
305 276 'interpolation loop detected in value "%s".' % option)
306 277
307 278
308 279 class RepeatSectionError(ConfigObjError):
309 280 """
310 281 This error indicates additional sections in a section with a
311 282 ``__many__`` (repeated) section.
312 283 """
313 284
314 285
315 286 class MissingInterpolationOption(InterpolationError):
316 287 """A value specified for interpolation was missing."""
317 288
318 289 def __init__(self, option):
319 290 InterpolationError.__init__(
320 291 self,
321 292 'missing option "%s" in interpolation.' % option)
322 293
323 294
324 295 class UnreprError(ConfigObjError):
325 296 """An error parsing in unrepr mode."""
326 297
327 298
328 299
329 300 class InterpolationEngine(object):
330 301 """
331 302 A helper class to help perform string interpolation.
332 303
333 304 This class is an abstract base class; its descendants perform
334 305 the actual work.
335 306 """
336 307
337 308 # compiled regexp to use in self.interpolate()
338 309 _KEYCRE = re.compile(r"%\(([^)]*)\)s")
339 310
340 311 def __init__(self, section):
341 312 # the Section instance that "owns" this engine
342 313 self.section = section
343 314
344 315
345 316 def interpolate(self, key, value):
346 317 def recursive_interpolate(key, value, section, backtrail):
347 318 """The function that does the actual work.
348 319
349 320 ``value``: the string we're trying to interpolate.
350 321 ``section``: the section in which that string was found
351 322 ``backtrail``: a dict to keep track of where we've been,
352 323 to detect and prevent infinite recursion loops
353 324
354 325 This is similar to a depth-first-search algorithm.
355 326 """
356 327 # Have we been here already?
357 328 if backtrail.has_key((key, section.name)):
358 329 # Yes - infinite loop detected
359 330 raise InterpolationLoopError(key)
360 331 # Place a marker on our backtrail so we won't come back here again
361 332 backtrail[(key, section.name)] = 1
362 333
363 334 # Now start the actual work
364 335 match = self._KEYCRE.search(value)
365 336 while match:
366 337 # The actual parsing of the match is implementation-dependent,
367 338 # so delegate to our helper function
368 339 k, v, s = self._parse_match(match)
369 340 if k is None:
370 341 # That's the signal that no further interpolation is needed
371 342 replacement = v
372 343 else:
373 344 # Further interpolation may be needed to obtain final value
374 345 replacement = recursive_interpolate(k, v, s, backtrail)
375 346 # Replace the matched string with its final value
376 347 start, end = match.span()
377 348 value = ''.join((value[:start], replacement, value[end:]))
378 349 new_search_start = start + len(replacement)
379 350 # Pick up the next interpolation key, if any, for next time
380 351 # through the while loop
381 352 match = self._KEYCRE.search(value, new_search_start)
382 353
383 354 # Now safe to come back here again; remove marker from backtrail
384 355 del backtrail[(key, section.name)]
385 356
386 357 return value
387 358
388 359 # Back in interpolate(), all we have to do is kick off the recursive
389 360 # function with appropriate starting values
390 361 value = recursive_interpolate(key, value, self.section, {})
391 362 return value
392 363
393 364
394 365 def _fetch(self, key):
395 366 """Helper function to fetch values from owning section.
396 367
397 368 Returns a 2-tuple: the value, and the section where it was found.
398 369 """
399 370 # switch off interpolation before we try and fetch anything !
400 371 save_interp = self.section.main.interpolation
401 372 self.section.main.interpolation = False
402 373
403 374 # Start at section that "owns" this InterpolationEngine
404 375 current_section = self.section
405 376 while True:
406 377 # try the current section first
407 378 val = current_section.get(key)
408 379 if val is not None:
409 380 break
410 381 # try "DEFAULT" next
411 382 val = current_section.get('DEFAULT', {}).get(key)
412 383 if val is not None:
413 384 break
414 385 # move up to parent and try again
415 386 # top-level's parent is itself
416 387 if current_section.parent is current_section:
417 388 # reached top level, time to give up
418 389 break
419 390 current_section = current_section.parent
420 391
421 392 # restore interpolation to previous value before returning
422 393 self.section.main.interpolation = save_interp
423 394 if val is None:
424 395 raise MissingInterpolationOption(key)
425 396 return val, current_section
426 397
427 398
428 399 def _parse_match(self, match):
429 400 """Implementation-dependent helper function.
430 401
431 402 Will be passed a match object corresponding to the interpolation
432 403 key we just found (e.g., "%(foo)s" or "$foo"). Should look up that
433 404 key in the appropriate config file section (using the ``_fetch()``
434 405 helper function) and return a 3-tuple: (key, value, section)
435 406
436 407 ``key`` is the name of the key we're looking for
437 408 ``value`` is the value found for that key
438 409 ``section`` is a reference to the section where it was found
439 410
440 411 ``key`` and ``section`` should be None if no further
441 412 interpolation should be performed on the resulting value
442 413 (e.g., if we interpolated "$$" and returned "$").
443 414 """
444 415 raise NotImplementedError()
445 416
446 417
447 418
448 419 class ConfigParserInterpolation(InterpolationEngine):
449 420 """Behaves like ConfigParser."""
450 421 _KEYCRE = re.compile(r"%\(([^)]*)\)s")
451 422
452 423 def _parse_match(self, match):
453 424 key = match.group(1)
454 425 value, section = self._fetch(key)
455 426 return key, value, section
456 427
457 428
458 429
459 430 class TemplateInterpolation(InterpolationEngine):
460 431 """Behaves like string.Template."""
461 432 _delimiter = '$'
462 433 _KEYCRE = re.compile(r"""
463 434 \$(?:
464 435 (?P<escaped>\$) | # Two $ signs
465 436 (?P<named>[_a-z][_a-z0-9]*) | # $name format
466 437 {(?P<braced>[^}]*)} # ${name} format
467 438 )
468 439 """, re.IGNORECASE | re.VERBOSE)
469 440
470 441 def _parse_match(self, match):
471 442 # Valid name (in or out of braces): fetch value from section
472 443 key = match.group('named') or match.group('braced')
473 444 if key is not None:
474 445 value, section = self._fetch(key)
475 446 return key, value, section
476 447 # Escaped delimiter (e.g., $$): return single delimiter
477 448 if match.group('escaped') is not None:
478 449 # Return None for key and section to indicate it's time to stop
479 450 return None, self._delimiter, None
480 451 # Anything else: ignore completely, just return it unchanged
481 452 return None, match.group(), None
482 453
483 454
484 455 interpolation_engines = {
485 456 'configparser': ConfigParserInterpolation,
486 457 'template': TemplateInterpolation,
487 458 }
488 459
489 460
490 461
491 462 class Section(dict):
492 463 """
493 464 A dictionary-like object that represents a section in a config file.
494 465
495 466 It does string interpolation if the 'interpolation' attribute
496 467 of the 'main' object is set to True.
497 468
498 469 Interpolation is tried first from this object, then from the 'DEFAULT'
499 470 section of this object, next from the parent and its 'DEFAULT' section,
500 471 and so on until the main object is reached.
501 472
502 473 A Section will behave like an ordered dictionary - following the
503 474 order of the ``scalars`` and ``sections`` attributes.
504 475 You can use this to change the order of members.
505 476
506 477 Iteration follows the order: scalars, then sections.
507 478 """
508 479
509 480 def __init__(self, parent, depth, main, indict=None, name=None):
510 481 """
511 482 * parent is the section above
512 483 * depth is the depth level of this section
513 484 * main is the main ConfigObj
514 485 * indict is a dictionary to initialise the section with
515 486 """
516 487 if indict is None:
517 488 indict = {}
518 489 dict.__init__(self)
519 490 # used for nesting level *and* interpolation
520 491 self.parent = parent
521 492 # used for the interpolation attribute
522 493 self.main = main
523 494 # level of nesting depth of this Section
524 495 self.depth = depth
525 496 # purely for information
526 497 self.name = name
527 498 #
528 499 self._initialise()
529 500 # we do this explicitly so that __setitem__ is used properly
530 501 # (rather than just passing to ``dict.__init__``)
531 502 for entry, value in indict.iteritems():
532 503 self[entry] = value
533 504
534 505
535 506 def _initialise(self):
536 507 # the sequence of scalar values in this Section
537 508 self.scalars = []
538 509 # the sequence of sections in this Section
539 510 self.sections = []
540 511 # for comments :-)
541 512 self.comments = {}
542 513 self.inline_comments = {}
543 514 # for the configspec
544 515 self.configspec = {}
545 516 self._order = []
546 517 self._configspec_comments = {}
547 518 self._configspec_inline_comments = {}
548 519 self._cs_section_comments = {}
549 520 self._cs_section_inline_comments = {}
550 521 # for defaults
551 522 self.defaults = []
552 523 self.default_values = {}
553 524
554 525
555 526 def _interpolate(self, key, value):
556 527 try:
557 528 # do we already have an interpolation engine?
558 529 engine = self._interpolation_engine
559 530 except AttributeError:
560 531 # not yet: first time running _interpolate(), so pick the engine
561 532 name = self.main.interpolation
562 533 if name == True: # note that "if name:" would be incorrect here
563 534 # backwards-compatibility: interpolation=True means use default
564 535 name = DEFAULT_INTERPOLATION
565 536 name = name.lower() # so that "Template", "template", etc. all work
566 537 class_ = interpolation_engines.get(name, None)
567 538 if class_ is None:
568 539 # invalid value for self.main.interpolation
569 540 self.main.interpolation = False
570 541 return value
571 542 else:
572 543 # save reference to engine so we don't have to do this again
573 544 engine = self._interpolation_engine = class_(self)
574 545 # let the engine do the actual work
575 546 return engine.interpolate(key, value)
576 547
577 548
578 549 def __getitem__(self, key):
579 550 """Fetch the item and do string interpolation."""
580 551 val = dict.__getitem__(self, key)
581 552 if self.main.interpolation and isinstance(val, StringTypes):
582 553 return self._interpolate(key, val)
583 554 return val
584 555
585 556
586 557 def __setitem__(self, key, value, unrepr=False):
587 558 """
588 559 Correctly set a value.
589 560
590 561 Making dictionary values Section instances.
591 562 (We have to special case 'Section' instances - which are also dicts)
592 563
593 564 Keys must be strings.
594 565 Values need only be strings (or lists of strings) if
595 566 ``main.stringify`` is set.
596 567
597 568 `unrepr`` must be set when setting a value to a dictionary, without
598 569 creating a new sub-section.
599 570 """
600 571 if not isinstance(key, StringTypes):
601 572 raise ValueError('The key "%s" is not a string.' % key)
602 573
603 574 # add the comment
604 575 if not self.comments.has_key(key):
605 576 self.comments[key] = []
606 577 self.inline_comments[key] = ''
607 578 # remove the entry from defaults
608 579 if key in self.defaults:
609 580 self.defaults.remove(key)
610 581 #
611 582 if isinstance(value, Section):
612 583 if not self.has_key(key):
613 584 self.sections.append(key)
614 585 dict.__setitem__(self, key, value)
615 586 elif isinstance(value, dict) and not unrepr:
616 587 # First create the new depth level,
617 588 # then create the section
618 589 if not self.has_key(key):
619 590 self.sections.append(key)
620 591 new_depth = self.depth + 1
621 592 dict.__setitem__(
622 593 self,
623 594 key,
624 595 Section(
625 596 self,
626 597 new_depth,
627 598 self.main,
628 599 indict=value,
629 600 name=key))
630 601 else:
631 602 if not self.has_key(key):
632 603 self.scalars.append(key)
633 604 if not self.main.stringify:
634 605 if isinstance(value, StringTypes):
635 606 pass
636 607 elif isinstance(value, (list, tuple)):
637 608 for entry in value:
638 609 if not isinstance(entry, StringTypes):
639 610 raise TypeError('Value is not a string "%s".' % entry)
640 611 else:
641 612 raise TypeError('Value is not a string "%s".' % value)
642 613 dict.__setitem__(self, key, value)
643 614
644 615
645 616 def __delitem__(self, key):
646 617 """Remove items from the sequence when deleting."""
647 618 dict. __delitem__(self, key)
648 619 if key in self.scalars:
649 620 self.scalars.remove(key)
650 621 else:
651 622 self.sections.remove(key)
652 623 del self.comments[key]
653 624 del self.inline_comments[key]
654 625
655 626
656 627 def get(self, key, default=None):
657 628 """A version of ``get`` that doesn't bypass string interpolation."""
658 629 try:
659 630 return self[key]
660 631 except KeyError:
661 632 return default
662 633
663 634
664 635 def update(self, indict):
665 636 """
666 637 A version of update that uses our ``__setitem__``.
667 638 """
668 639 for entry in indict:
669 640 self[entry] = indict[entry]
670 641
671 642
672 643 def pop(self, key, *args):
673 644 """
674 645 'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
675 646 If key is not found, d is returned if given, otherwise KeyError is raised'
676 647 """
677 648 val = dict.pop(self, key, *args)
678 649 if key in self.scalars:
679 650 del self.comments[key]
680 651 del self.inline_comments[key]
681 652 self.scalars.remove(key)
682 653 elif key in self.sections:
683 654 del self.comments[key]
684 655 del self.inline_comments[key]
685 656 self.sections.remove(key)
686 657 if self.main.interpolation and isinstance(val, StringTypes):
687 658 return self._interpolate(key, val)
688 659 return val
689 660
690 661
691 662 def popitem(self):
692 663 """Pops the first (key,val)"""
693 664 sequence = (self.scalars + self.sections)
694 665 if not sequence:
695 666 raise KeyError(": 'popitem(): dictionary is empty'")
696 667 key = sequence[0]
697 668 val = self[key]
698 669 del self[key]
699 670 return key, val
700 671
701 672
702 673 def clear(self):
703 674 """
704 675 A version of clear that also affects scalars/sections
705 676 Also clears comments and configspec.
706 677
707 678 Leaves other attributes alone :
708 679 depth/main/parent are not affected
709 680 """
710 681 dict.clear(self)
711 682 self.scalars = []
712 683 self.sections = []
713 684 self.comments = {}
714 685 self.inline_comments = {}
715 686 self.configspec = {}
716 687
717 688
718 689 def setdefault(self, key, default=None):
719 690 """A version of setdefault that sets sequence if appropriate."""
720 691 try:
721 692 return self[key]
722 693 except KeyError:
723 694 self[key] = default
724 695 return self[key]
725 696
726 697
727 698 def items(self):
728 699 """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
729 700 return zip((self.scalars + self.sections), self.values())
730 701
731 702
732 703 def keys(self):
733 704 """D.keys() -> list of D's keys"""
734 705 return (self.scalars + self.sections)
735 706
736 707
737 708 def values(self):
738 709 """D.values() -> list of D's values"""
739 710 return [self[key] for key in (self.scalars + self.sections)]
740 711
741 712
742 713 def iteritems(self):
743 714 """D.iteritems() -> an iterator over the (key, value) items of D"""
744 715 return iter(self.items())
745 716
746 717
747 718 def iterkeys(self):
748 719 """D.iterkeys() -> an iterator over the keys of D"""
749 720 return iter((self.scalars + self.sections))
750 721
751 722 __iter__ = iterkeys
752 723
753 724
754 725 def itervalues(self):
755 726 """D.itervalues() -> an iterator over the values of D"""
756 727 return iter(self.values())
757 728
758 729
759 730 def __repr__(self):
760 731 """x.__repr__() <==> repr(x)"""
761 732 return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
762 733 for key in (self.scalars + self.sections)])
763 734
764 735 __str__ = __repr__
765 736 __str__.__doc__ = "x.__str__() <==> str(x)"
766 737
767 738
768 739 # Extra methods - not in a normal dictionary
769 740
770 741 def dict(self):
771 742 """
772 743 Return a deepcopy of self as a dictionary.
773 744
774 745 All members that are ``Section`` instances are recursively turned to
775 746 ordinary dictionaries - by calling their ``dict`` method.
776 747
777 748 >>> n = a.dict()
778 749 >>> n == a
779 750 1
780 751 >>> n is a
781 752 0
782 753 """
783 754 newdict = {}
784 755 for entry in self:
785 756 this_entry = self[entry]
786 757 if isinstance(this_entry, Section):
787 758 this_entry = this_entry.dict()
788 759 elif isinstance(this_entry, list):
789 760 # create a copy rather than a reference
790 761 this_entry = list(this_entry)
791 762 elif isinstance(this_entry, tuple):
792 763 # create a copy rather than a reference
793 764 this_entry = tuple(this_entry)
794 765 newdict[entry] = this_entry
795 766 return newdict
796 767
797 768
798 769 def merge(self, indict):
799 770 """
800 771 A recursive update - useful for merging config files.
801 772
802 773 >>> a = '''[section1]
803 774 ... option1 = True
804 775 ... [[subsection]]
805 776 ... more_options = False
806 777 ... # end of file'''.splitlines()
807 778 >>> b = '''# File is user.ini
808 779 ... [section1]
809 780 ... option1 = False
810 781 ... # end of file'''.splitlines()
811 782 >>> c1 = ConfigObj(b)
812 783 >>> c2 = ConfigObj(a)
813 784 >>> c2.merge(c1)
814 785 >>> c2
815 786 {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}
816 787 """
817 for key, val in indict.items():
788 for key, val in indict.iteritems():
818 789 if (key in self and isinstance(self[key], dict) and
819 790 isinstance(val, dict)):
820 791 self[key].merge(val)
821 792 else:
822 793 self[key] = val
823 794
824 795
825 796 def rename(self, oldkey, newkey):
826 797 """
827 798 Change a keyname to another, without changing position in sequence.
828 799
829 800 Implemented so that transformations can be made on keys,
830 801 as well as on values. (used by encode and decode)
831 802
832 803 Also renames comments.
833 804 """
834 805 if oldkey in self.scalars:
835 806 the_list = self.scalars
836 807 elif oldkey in self.sections:
837 808 the_list = self.sections
838 809 else:
839 810 raise KeyError('Key "%s" not found.' % oldkey)
840 811 pos = the_list.index(oldkey)
841 812 #
842 813 val = self[oldkey]
843 814 dict.__delitem__(self, oldkey)
844 815 dict.__setitem__(self, newkey, val)
845 816 the_list.remove(oldkey)
846 817 the_list.insert(pos, newkey)
847 818 comm = self.comments[oldkey]
848 819 inline_comment = self.inline_comments[oldkey]
849 820 del self.comments[oldkey]
850 821 del self.inline_comments[oldkey]
851 822 self.comments[newkey] = comm
852 823 self.inline_comments[newkey] = inline_comment
853 824
854 825
855 826 def walk(self, function, raise_errors=True,
856 827 call_on_sections=False, **keywargs):
857 828 """
858 829 Walk every member and call a function on the keyword and value.
859 830
860 831 Return a dictionary of the return values
861 832
862 833 If the function raises an exception, raise the errror
863 834 unless ``raise_errors=False``, in which case set the return value to
864 835 ``False``.
865 836
866 837 Any unrecognised keyword arguments you pass to walk, will be pased on
867 838 to the function you pass in.
868 839
869 840 Note: if ``call_on_sections`` is ``True`` then - on encountering a
870 841 subsection, *first* the function is called for the *whole* subsection,
871 842 and then recurses into it's members. This means your function must be
872 843 able to handle strings, dictionaries and lists. This allows you
873 844 to change the key of subsections as well as for ordinary members. The
874 845 return value when called on the whole subsection has to be discarded.
875 846
876 847 See the encode and decode methods for examples, including functions.
877 848
878 849 .. caution::
879 850
880 851 You can use ``walk`` to transform the names of members of a section
881 852 but you mustn't add or delete members.
882 853
883 854 >>> config = '''[XXXXsection]
884 855 ... XXXXkey = XXXXvalue'''.splitlines()
885 856 >>> cfg = ConfigObj(config)
886 857 >>> cfg
887 858 {'XXXXsection': {'XXXXkey': 'XXXXvalue'}}
888 859 >>> def transform(section, key):
889 860 ... val = section[key]
890 861 ... newkey = key.replace('XXXX', 'CLIENT1')
891 862 ... section.rename(key, newkey)
892 863 ... if isinstance(val, (tuple, list, dict)):
893 864 ... pass
894 865 ... else:
895 866 ... val = val.replace('XXXX', 'CLIENT1')
896 867 ... section[newkey] = val
897 868 >>> cfg.walk(transform, call_on_sections=True)
898 869 {'CLIENT1section': {'CLIENT1key': None}}
899 870 >>> cfg
900 871 {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}
901 872 """
902 873 out = {}
903 874 # scalars first
904 875 for i in range(len(self.scalars)):
905 876 entry = self.scalars[i]
906 877 try:
907 878 val = function(self, entry, **keywargs)
908 879 # bound again in case name has changed
909 880 entry = self.scalars[i]
910 881 out[entry] = val
911 882 except Exception:
912 883 if raise_errors:
913 884 raise
914 885 else:
915 886 entry = self.scalars[i]
916 887 out[entry] = False
917 888 # then sections
918 889 for i in range(len(self.sections)):
919 890 entry = self.sections[i]
920 891 if call_on_sections:
921 892 try:
922 893 function(self, entry, **keywargs)
923 894 except Exception:
924 895 if raise_errors:
925 896 raise
926 897 else:
927 898 entry = self.sections[i]
928 899 out[entry] = False
929 900 # bound again in case name has changed
930 901 entry = self.sections[i]
931 902 # previous result is discarded
932 903 out[entry] = self[entry].walk(
933 904 function,
934 905 raise_errors=raise_errors,
935 906 call_on_sections=call_on_sections,
936 907 **keywargs)
937 908 return out
938 909
939 910
940 911 def decode(self, encoding):
941 912 """
942 913 Decode all strings and values to unicode, using the specified encoding.
943 914
944 915 Works with subsections and list values.
945 916
946 917 Uses the ``walk`` method.
947 918
948 919 Testing ``encode`` and ``decode``.
949 920 >>> m = ConfigObj(a)
950 921 >>> m.decode('ascii')
951 922 >>> def testuni(val):
952 923 ... for entry in val:
953 924 ... if not isinstance(entry, unicode):
954 925 ... print >> sys.stderr, type(entry)
955 926 ... raise AssertionError, 'decode failed.'
956 927 ... if isinstance(val[entry], dict):
957 928 ... testuni(val[entry])
958 929 ... elif not isinstance(val[entry], unicode):
959 930 ... raise AssertionError, 'decode failed.'
960 931 >>> testuni(m)
961 932 >>> m.encode('ascii')
962 933 >>> a == m
963 934 1
964 935 """
965 936 warn('use of ``decode`` is deprecated.', DeprecationWarning)
966 937 def decode(section, key, encoding=encoding, warn=True):
967 938 """ """
968 939 val = section[key]
969 940 if isinstance(val, (list, tuple)):
970 941 newval = []
971 942 for entry in val:
972 943 newval.append(entry.decode(encoding))
973 944 elif isinstance(val, dict):
974 945 newval = val
975 946 else:
976 947 newval = val.decode(encoding)
977 948 newkey = key.decode(encoding)
978 949 section.rename(key, newkey)
979 950 section[newkey] = newval
980 951 # using ``call_on_sections`` allows us to modify section names
981 952 self.walk(decode, call_on_sections=True)
982 953
983 954
984 955 def encode(self, encoding):
985 956 """
986 957 Encode all strings and values from unicode,
987 958 using the specified encoding.
988 959
989 960 Works with subsections and list values.
990 961 Uses the ``walk`` method.
991 962 """
992 963 warn('use of ``encode`` is deprecated.', DeprecationWarning)
993 964 def encode(section, key, encoding=encoding):
994 965 """ """
995 966 val = section[key]
996 967 if isinstance(val, (list, tuple)):
997 968 newval = []
998 969 for entry in val:
999 970 newval.append(entry.encode(encoding))
1000 971 elif isinstance(val, dict):
1001 972 newval = val
1002 973 else:
1003 974 newval = val.encode(encoding)
1004 975 newkey = key.encode(encoding)
1005 976 section.rename(key, newkey)
1006 977 section[newkey] = newval
1007 978 self.walk(encode, call_on_sections=True)
1008 979
1009 980
1010 981 def istrue(self, key):
1011 982 """A deprecated version of ``as_bool``."""
1012 983 warn('use of ``istrue`` is deprecated. Use ``as_bool`` method '
1013 984 'instead.', DeprecationWarning)
1014 985 return self.as_bool(key)
1015 986
1016 987
1017 988 def as_bool(self, key):
1018 989 """
1019 990 Accepts a key as input. The corresponding value must be a string or
1020 991 the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
1021 992 retain compatibility with Python 2.2.
1022 993
1023 994 If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns
1024 995 ``True``.
1025 996
1026 997 If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns
1027 998 ``False``.
1028 999
1029 1000 ``as_bool`` is not case sensitive.
1030 1001
1031 1002 Any other input will raise a ``ValueError``.
1032 1003
1033 1004 >>> a = ConfigObj()
1034 1005 >>> a['a'] = 'fish'
1035 1006 >>> a.as_bool('a')
1036 1007 Traceback (most recent call last):
1037 1008 ValueError: Value "fish" is neither True nor False
1038 1009 >>> a['b'] = 'True'
1039 1010 >>> a.as_bool('b')
1040 1011 1
1041 1012 >>> a['b'] = 'off'
1042 1013 >>> a.as_bool('b')
1043 1014 0
1044 1015 """
1045 1016 val = self[key]
1046 1017 if val == True:
1047 1018 return True
1048 1019 elif val == False:
1049 1020 return False
1050 1021 else:
1051 1022 try:
1052 1023 if not isinstance(val, StringTypes):
1053 1024 # TODO: Why do we raise a KeyError here?
1054 1025 raise KeyError()
1055 1026 else:
1056 1027 return self.main._bools[val.lower()]
1057 1028 except KeyError:
1058 1029 raise ValueError('Value "%s" is neither True nor False' % val)
1059 1030
1060 1031
1061 1032 def as_int(self, key):
1062 1033 """
1063 1034 A convenience method which coerces the specified value to an integer.
1064 1035
1065 1036 If the value is an invalid literal for ``int``, a ``ValueError`` will
1066 1037 be raised.
1067 1038
1068 1039 >>> a = ConfigObj()
1069 1040 >>> a['a'] = 'fish'
1070 1041 >>> a.as_int('a')
1071 1042 Traceback (most recent call last):
1072 1043 ValueError: invalid literal for int(): fish
1073 1044 >>> a['b'] = '1'
1074 1045 >>> a.as_int('b')
1075 1046 1
1076 1047 >>> a['b'] = '3.2'
1077 1048 >>> a.as_int('b')
1078 1049 Traceback (most recent call last):
1079 1050 ValueError: invalid literal for int(): 3.2
1080 1051 """
1081 1052 return int(self[key])
1082 1053
1083 1054
1084 1055 def as_float(self, key):
1085 1056 """
1086 1057 A convenience method which coerces the specified value to a float.
1087 1058
1088 1059 If the value is an invalid literal for ``float``, a ``ValueError`` will
1089 1060 be raised.
1090 1061
1091 1062 >>> a = ConfigObj()
1092 1063 >>> a['a'] = 'fish'
1093 1064 >>> a.as_float('a')
1094 1065 Traceback (most recent call last):
1095 1066 ValueError: invalid literal for float(): fish
1096 1067 >>> a['b'] = '1'
1097 1068 >>> a.as_float('b')
1098 1069 1.0
1099 1070 >>> a['b'] = '3.2'
1100 1071 >>> a.as_float('b')
1101 1072 3.2000000000000002
1102 1073 """
1103 1074 return float(self[key])
1104 1075
1105 1076
1106 1077 def restore_default(self, key):
1107 1078 """
1108 1079 Restore (and return) default value for the specified key.
1109 1080
1110 1081 This method will only work for a ConfigObj that was created
1111 1082 with a configspec and has been validated.
1112 1083
1113 1084 If there is no default value for this key, ``KeyError`` is raised.
1114 1085 """
1115 1086 default = self.default_values[key]
1116 1087 dict.__setitem__(self, key, default)
1117 1088 if key not in self.defaults:
1118 1089 self.defaults.append(key)
1119 1090 return default
1120 1091
1121 1092
1122 1093 def restore_defaults(self):
1123 1094 """
1124 1095 Recursively restore default values to all members
1125 1096 that have them.
1126 1097
1127 1098 This method will only work for a ConfigObj that was created
1128 1099 with a configspec and has been validated.
1129 1100
1130 1101 It doesn't delete or modify entries without default values.
1131 1102 """
1132 1103 for key in self.default_values:
1133 1104 self.restore_default(key)
1134 1105
1135 1106 for section in self.sections:
1136 1107 self[section].restore_defaults()
1137 1108
1138 1109
1139 1110 class ConfigObj(Section):
1140 1111 """An object to read, create, and write config files."""
1141 1112
1142 1113 _keyword = re.compile(r'''^ # line start
1143 1114 (\s*) # indentation
1144 1115 ( # keyword
1145 1116 (?:".*?")| # double quotes
1146 1117 (?:'.*?')| # single quotes
1147 1118 (?:[^'"=].*?) # no quotes
1148 1119 )
1149 1120 \s*=\s* # divider
1150 1121 (.*) # value (including list values and comments)
1151 1122 $ # line end
1152 1123 ''',
1153 1124 re.VERBOSE)
1154 1125
1155 1126 _sectionmarker = re.compile(r'''^
1156 1127 (\s*) # 1: indentation
1157 1128 ((?:\[\s*)+) # 2: section marker open
1158 1129 ( # 3: section name open
1159 1130 (?:"\s*\S.*?\s*")| # at least one non-space with double quotes
1160 1131 (?:'\s*\S.*?\s*')| # at least one non-space with single quotes
1161 1132 (?:[^'"\s].*?) # at least one non-space unquoted
1162 1133 ) # section name close
1163 1134 ((?:\s*\])+) # 4: section marker close
1164 1135 \s*(\#.*)? # 5: optional comment
1165 1136 $''',
1166 1137 re.VERBOSE)
1167 1138
1168 1139 # this regexp pulls list values out as a single string
1169 1140 # or single values and comments
1170 1141 # FIXME: this regex adds a '' to the end of comma terminated lists
1171 1142 # workaround in ``_handle_value``
1172 1143 _valueexp = re.compile(r'''^
1173 1144 (?:
1174 1145 (?:
1175 1146 (
1176 1147 (?:
1177 1148 (?:
1178 1149 (?:".*?")| # double quotes
1179 1150 (?:'.*?')| # single quotes
1180 1151 (?:[^'",\#][^,\#]*?) # unquoted
1181 1152 )
1182 1153 \s*,\s* # comma
1183 1154 )* # match all list items ending in a comma (if any)
1184 1155 )
1185 1156 (
1186 1157 (?:".*?")| # double quotes
1187 1158 (?:'.*?')| # single quotes
1188 1159 (?:[^'",\#\s][^,]*?)| # unquoted
1189 1160 (?:(?<!,)) # Empty value
1190 1161 )? # last item in a list - or string value
1191 1162 )|
1192 1163 (,) # alternatively a single comma - empty list
1193 1164 )
1194 1165 \s*(\#.*)? # optional comment
1195 1166 $''',
1196 1167 re.VERBOSE)
1197 1168
1198 1169 # use findall to get the members of a list value
1199 1170 _listvalueexp = re.compile(r'''
1200 1171 (
1201 1172 (?:".*?")| # double quotes
1202 1173 (?:'.*?')| # single quotes
1203 1174 (?:[^'",\#].*?) # unquoted
1204 1175 )
1205 1176 \s*,\s* # comma
1206 1177 ''',
1207 1178 re.VERBOSE)
1208 1179
1209 1180 # this regexp is used for the value
1210 1181 # when lists are switched off
1211 1182 _nolistvalue = re.compile(r'''^
1212 1183 (
1213 1184 (?:".*?")| # double quotes
1214 1185 (?:'.*?')| # single quotes
1215 1186 (?:[^'"\#].*?)| # unquoted
1216 1187 (?:) # Empty value
1217 1188 )
1218 1189 \s*(\#.*)? # optional comment
1219 1190 $''',
1220 1191 re.VERBOSE)
1221 1192
1222 1193 # regexes for finding triple quoted values on one line
1223 1194 _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
1224 1195 _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
1225 1196 _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
1226 1197 _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
1227 1198
1228 1199 _triple_quote = {
1229 1200 "'''": (_single_line_single, _multi_line_single),
1230 1201 '"""': (_single_line_double, _multi_line_double),
1231 1202 }
1232 1203
1233 1204 # Used by the ``istrue`` Section method
1234 1205 _bools = {
1235 1206 'yes': True, 'no': False,
1236 1207 'on': True, 'off': False,
1237 1208 '1': True, '0': False,
1238 1209 'true': True, 'false': False,
1239 1210 }
1240 1211
1241 1212
1242 1213 def __init__(self, infile=None, options=None, **kwargs):
1243 1214 """
1244 1215 Parse a config file or create a config file object.
1245 1216
1246 1217 ``ConfigObj(infile=None, options=None, **kwargs)``
1247 1218 """
1248 1219 # init the superclass
1249 1220 Section.__init__(self, self, 0, self)
1250 1221
1251 1222 if infile is None:
1252 1223 infile = []
1253 1224 if options is None:
1254 1225 options = {}
1255 1226 else:
1256 1227 options = dict(options)
1257 1228
1258 1229 # keyword arguments take precedence over an options dictionary
1259 1230 options.update(kwargs)
1260 1231
1261 1232 defaults = OPTION_DEFAULTS.copy()
1262 1233 # TODO: check the values too.
1263 1234 for entry in options:
1264 1235 if entry not in defaults:
1265 1236 raise TypeError('Unrecognised option "%s".' % entry)
1266 1237
1267 1238 # Add any explicit options to the defaults
1268 1239 defaults.update(options)
1269 1240 self._initialise(defaults)
1270 1241 configspec = defaults['configspec']
1271 1242 self._original_configspec = configspec
1272 1243 self._load(infile, configspec)
1273 1244
1274 1245
1275 1246 def _load(self, infile, configspec):
1276 1247 if isinstance(infile, StringTypes):
1277 1248 self.filename = infile
1278 1249 if os.path.isfile(infile):
1279 1250 h = open(infile, 'rb')
1280 1251 infile = h.read() or []
1281 1252 h.close()
1282 1253 elif self.file_error:
1283 1254 # raise an error if the file doesn't exist
1284 1255 raise IOError('Config file not found: "%s".' % self.filename)
1285 1256 else:
1286 1257 # file doesn't already exist
1287 1258 if self.create_empty:
1288 1259 # this is a good test that the filename specified
1289 1260 # isn't impossible - like on a non-existent device
1290 1261 h = open(infile, 'w')
1291 1262 h.write('')
1292 1263 h.close()
1293 1264 infile = []
1294 1265
1295 1266 elif isinstance(infile, (list, tuple)):
1296 1267 infile = list(infile)
1297 1268
1298 1269 elif isinstance(infile, dict):
1299 1270 # initialise self
1300 1271 # the Section class handles creating subsections
1301 1272 if isinstance(infile, ConfigObj):
1302 1273 # get a copy of our ConfigObj
1303 1274 infile = infile.dict()
1304 1275
1305 1276 for entry in infile:
1306 1277 self[entry] = infile[entry]
1307 1278 del self._errors
1308 1279
1309 1280 if configspec is not None:
1310 1281 self._handle_configspec(configspec)
1311 1282 else:
1312 1283 self.configspec = None
1313 1284 return
1314 1285
1315 1286 elif hasattr(infile, 'read'):
1316 1287 # This supports file like objects
1317 1288 infile = infile.read() or []
1318 1289 # needs splitting into lines - but needs doing *after* decoding
1319 1290 # in case it's not an 8 bit encoding
1320 1291 else:
1321 1292 raise TypeError('infile must be a filename, file like object, or list of lines.')
1322 1293
1323 1294 if infile:
1324 1295 # don't do it for the empty ConfigObj
1325 1296 infile = self._handle_bom(infile)
1326 1297 # infile is now *always* a list
1327 1298 #
1328 1299 # Set the newlines attribute (first line ending it finds)
1329 1300 # and strip trailing '\n' or '\r' from lines
1330 1301 for line in infile:
1331 1302 if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
1332 1303 continue
1333 1304 for end in ('\r\n', '\n', '\r'):
1334 1305 if line.endswith(end):
1335 1306 self.newlines = end
1336 1307 break
1337 1308 break
1338 1309
1339 1310 infile = [line.rstrip('\r\n') for line in infile]
1340 1311
1341 1312 self._parse(infile)
1342 1313 # if we had any errors, now is the time to raise them
1343 1314 if self._errors:
1344 1315 info = "at line %s." % self._errors[0].line_number
1345 1316 if len(self._errors) > 1:
1346 1317 msg = "Parsing failed with several errors.\nFirst error %s" % info
1347 1318 error = ConfigObjError(msg)
1348 1319 else:
1349 1320 error = self._errors[0]
1350 1321 # set the errors attribute; it's a list of tuples:
1351 1322 # (error_type, message, line_number)
1352 1323 error.errors = self._errors
1353 1324 # set the config attribute
1354 1325 error.config = self
1355 1326 raise error
1356 1327 # delete private attributes
1357 1328 del self._errors
1358 1329
1359 1330 if configspec is None:
1360 1331 self.configspec = None
1361 1332 else:
1362 1333 self._handle_configspec(configspec)
1363 1334
1364 1335
1365 1336 def _initialise(self, options=None):
1366 1337 if options is None:
1367 1338 options = OPTION_DEFAULTS
1368 1339
1369 1340 # initialise a few variables
1370 1341 self.filename = None
1371 1342 self._errors = []
1372 1343 self.raise_errors = options['raise_errors']
1373 1344 self.interpolation = options['interpolation']
1374 1345 self.list_values = options['list_values']
1375 1346 self.create_empty = options['create_empty']
1376 1347 self.file_error = options['file_error']
1377 1348 self.stringify = options['stringify']
1378 1349 self.indent_type = options['indent_type']
1379 1350 self.encoding = options['encoding']
1380 1351 self.default_encoding = options['default_encoding']
1381 1352 self.BOM = False
1382 1353 self.newlines = None
1383 1354 self.write_empty_values = options['write_empty_values']
1384 1355 self.unrepr = options['unrepr']
1385 1356
1386 1357 self.initial_comment = []
1387 1358 self.final_comment = []
1388 1359 self.configspec = {}
1389 1360
1390 1361 # Clear section attributes as well
1391 1362 Section._initialise(self)
1392 1363
1393 1364
1394 1365 def __repr__(self):
1395 1366 return ('ConfigObj({%s})' %
1396 1367 ', '.join([('%s: %s' % (repr(key), repr(self[key])))
1397 1368 for key in (self.scalars + self.sections)]))
1398 1369
1399 1370
1400 1371 def _handle_bom(self, infile):
1401 1372 """
1402 1373 Handle any BOM, and decode if necessary.
1403 1374
1404 1375 If an encoding is specified, that *must* be used - but the BOM should
1405 1376 still be removed (and the BOM attribute set).
1406 1377
1407 1378 (If the encoding is wrongly specified, then a BOM for an alternative
1408 1379 encoding won't be discovered or removed.)
1409 1380
1410 1381 If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1411 1382 removed. The BOM attribute will be set. UTF16 will be decoded to
1412 1383 unicode.
1413 1384
1414 1385 NOTE: This method must not be called with an empty ``infile``.
1415 1386
1416 1387 Specifying the *wrong* encoding is likely to cause a
1417 1388 ``UnicodeDecodeError``.
1418 1389
1419 1390 ``infile`` must always be returned as a list of lines, but may be
1420 1391 passed in as a single string.
1421 1392 """
1422 1393 if ((self.encoding is not None) and
1423 1394 (self.encoding.lower() not in BOM_LIST)):
1424 1395 # No need to check for a BOM
1425 1396 # the encoding specified doesn't have one
1426 1397 # just decode
1427 1398 return self._decode(infile, self.encoding)
1428 1399
1429 1400 if isinstance(infile, (list, tuple)):
1430 1401 line = infile[0]
1431 1402 else:
1432 1403 line = infile
1433 1404 if self.encoding is not None:
1434 1405 # encoding explicitly supplied
1435 1406 # And it could have an associated BOM
1436 1407 # TODO: if encoding is just UTF16 - we ought to check for both
1437 1408 # TODO: big endian and little endian versions.
1438 1409 enc = BOM_LIST[self.encoding.lower()]
1439 1410 if enc == 'utf_16':
1440 1411 # For UTF16 we try big endian and little endian
1441 for BOM, (encoding, final_encoding) in BOMS.items():
1412 for BOM, (encoding, final_encoding) in BOMS.iteritems():
1442 1413 if not final_encoding:
1443 1414 # skip UTF8
1444 1415 continue
1445 1416 if infile.startswith(BOM):
1446 1417 ### BOM discovered
1447 1418 ##self.BOM = True
1448 1419 # Don't need to remove BOM
1449 1420 return self._decode(infile, encoding)
1450 1421
1451 1422 # If we get this far, will *probably* raise a DecodeError
1452 1423 # As it doesn't appear to start with a BOM
1453 1424 return self._decode(infile, self.encoding)
1454 1425
1455 1426 # Must be UTF8
1456 1427 BOM = BOM_SET[enc]
1457 1428 if not line.startswith(BOM):
1458 1429 return self._decode(infile, self.encoding)
1459 1430
1460 1431 newline = line[len(BOM):]
1461 1432
1462 1433 # BOM removed
1463 1434 if isinstance(infile, (list, tuple)):
1464 1435 infile[0] = newline
1465 1436 else:
1466 1437 infile = newline
1467 1438 self.BOM = True
1468 1439 return self._decode(infile, self.encoding)
1469 1440
1470 1441 # No encoding specified - so we need to check for UTF8/UTF16
1471 for BOM, (encoding, final_encoding) in BOMS.items():
1442 for BOM, (encoding, final_encoding) in BOMS.iteritems():
1472 1443 if not line.startswith(BOM):
1473 1444 continue
1474 1445 else:
1475 1446 # BOM discovered
1476 1447 self.encoding = final_encoding
1477 1448 if not final_encoding:
1478 1449 self.BOM = True
1479 1450 # UTF8
1480 1451 # remove BOM
1481 1452 newline = line[len(BOM):]
1482 1453 if isinstance(infile, (list, tuple)):
1483 1454 infile[0] = newline
1484 1455 else:
1485 1456 infile = newline
1486 1457 # UTF8 - don't decode
1487 1458 if isinstance(infile, StringTypes):
1488 1459 return infile.splitlines(True)
1489 1460 else:
1490 1461 return infile
1491 1462 # UTF16 - have to decode
1492 1463 return self._decode(infile, encoding)
1493 1464
1494 1465 # No BOM discovered and no encoding specified, just return
1495 1466 if isinstance(infile, StringTypes):
1496 1467 # infile read from a file will be a single string
1497 1468 return infile.splitlines(True)
1498 1469 return infile
1499 1470
1500 1471
1501 1472 def _a_to_u(self, aString):
1502 1473 """Decode ASCII strings to unicode if a self.encoding is specified."""
1503 1474 if self.encoding:
1504 1475 return aString.decode('ascii')
1505 1476 else:
1506 1477 return aString
1507 1478
1508 1479
1509 1480 def _decode(self, infile, encoding):
1510 1481 """
1511 1482 Decode infile to unicode. Using the specified encoding.
1512 1483
1513 1484 if is a string, it also needs converting to a list.
1514 1485 """
1515 1486 if isinstance(infile, StringTypes):
1516 1487 # can't be unicode
1517 1488 # NOTE: Could raise a ``UnicodeDecodeError``
1518 1489 return infile.decode(encoding).splitlines(True)
1519 1490 for i, line in enumerate(infile):
1520 1491 if not isinstance(line, unicode):
1521 1492 # NOTE: The isinstance test here handles mixed lists of unicode/string
1522 1493 # NOTE: But the decode will break on any non-string values
1523 1494 # NOTE: Or could raise a ``UnicodeDecodeError``
1524 1495 infile[i] = line.decode(encoding)
1525 1496 return infile
1526 1497
1527 1498
1528 1499 def _decode_element(self, line):
1529 1500 """Decode element to unicode if necessary."""
1530 1501 if not self.encoding:
1531 1502 return line
1532 1503 if isinstance(line, str) and self.default_encoding:
1533 1504 return line.decode(self.default_encoding)
1534 1505 return line
1535 1506
1536 1507
1537 1508 def _str(self, value):
1538 1509 """
1539 1510 Used by ``stringify`` within validate, to turn non-string values
1540 1511 into strings.
1541 1512 """
1542 1513 if not isinstance(value, StringTypes):
1543 1514 return str(value)
1544 1515 else:
1545 1516 return value
1546 1517
1547 1518
1548 1519 def _parse(self, infile):
1549 1520 """Actually parse the config file."""
1550 1521 temp_list_values = self.list_values
1551 1522 if self.unrepr:
1552 1523 self.list_values = False
1553 1524
1554 1525 comment_list = []
1555 1526 done_start = False
1556 1527 this_section = self
1557 1528 maxline = len(infile) - 1
1558 1529 cur_index = -1
1559 1530 reset_comment = False
1560 1531
1561 1532 while cur_index < maxline:
1562 1533 if reset_comment:
1563 1534 comment_list = []
1564 1535 cur_index += 1
1565 1536 line = infile[cur_index]
1566 1537 sline = line.strip()
1567 1538 # do we have anything on the line ?
1568 1539 if not sline or sline.startswith('#'):
1569 1540 reset_comment = False
1570 1541 comment_list.append(line)
1571 1542 continue
1572 1543
1573 1544 if not done_start:
1574 1545 # preserve initial comment
1575 1546 self.initial_comment = comment_list
1576 1547 comment_list = []
1577 1548 done_start = True
1578 1549
1579 1550 reset_comment = True
1580 1551 # first we check if it's a section marker
1581 1552 mat = self._sectionmarker.match(line)
1582 1553 if mat is not None:
1583 1554 # is a section line
1584 1555 (indent, sect_open, sect_name, sect_close, comment) = mat.groups()
1585 1556 if indent and (self.indent_type is None):
1586 1557 self.indent_type = indent
1587 1558 cur_depth = sect_open.count('[')
1588 1559 if cur_depth != sect_close.count(']'):
1589 1560 self._handle_error("Cannot compute the section depth at line %s.",
1590 1561 NestingError, infile, cur_index)
1591 1562 continue
1592 1563
1593 1564 if cur_depth < this_section.depth:
1594 1565 # the new section is dropping back to a previous level
1595 1566 try:
1596 1567 parent = self._match_depth(this_section,
1597 1568 cur_depth).parent
1598 1569 except SyntaxError:
1599 1570 self._handle_error("Cannot compute nesting level at line %s.",
1600 1571 NestingError, infile, cur_index)
1601 1572 continue
1602 1573 elif cur_depth == this_section.depth:
1603 1574 # the new section is a sibling of the current section
1604 1575 parent = this_section.parent
1605 1576 elif cur_depth == this_section.depth + 1:
1606 1577 # the new section is a child the current section
1607 1578 parent = this_section
1608 1579 else:
1609 1580 self._handle_error("Section too nested at line %s.",
1610 1581 NestingError, infile, cur_index)
1611 1582
1612 1583 sect_name = self._unquote(sect_name)
1613 1584 if parent.has_key(sect_name):
1614 1585 self._handle_error('Duplicate section name at line %s.',
1615 1586 DuplicateError, infile, cur_index)
1616 1587 continue
1617 1588
1618 1589 # create the new section
1619 1590 this_section = Section(
1620 1591 parent,
1621 1592 cur_depth,
1622 1593 self,
1623 1594 name=sect_name)
1624 1595 parent[sect_name] = this_section
1625 1596 parent.inline_comments[sect_name] = comment
1626 1597 parent.comments[sect_name] = comment_list
1627 1598 continue
1628 1599 #
1629 1600 # it's not a section marker,
1630 1601 # so it should be a valid ``key = value`` line
1631 1602 mat = self._keyword.match(line)
1632 1603 if mat is None:
1633 1604 # it neither matched as a keyword
1634 1605 # or a section marker
1635 1606 self._handle_error(
1636 1607 'Invalid line at line "%s".',
1637 1608 ParseError, infile, cur_index)
1638 1609 else:
1639 1610 # is a keyword value
1640 1611 # value will include any inline comment
1641 1612 (indent, key, value) = mat.groups()
1642 1613 if indent and (self.indent_type is None):
1643 1614 self.indent_type = indent
1644 1615 # check for a multiline value
1645 1616 if value[:3] in ['"""', "'''"]:
1646 1617 try:
1647 1618 (value, comment, cur_index) = self._multiline(
1648 1619 value, infile, cur_index, maxline)
1649 1620 except SyntaxError:
1650 1621 self._handle_error(
1651 1622 'Parse error in value at line %s.',
1652 1623 ParseError, infile, cur_index)
1653 1624 continue
1654 1625 else:
1655 1626 if self.unrepr:
1656 1627 comment = ''
1657 1628 try:
1658 1629 value = unrepr(value)
1659 1630 except Exception, e:
1660 1631 if type(e) == UnknownType:
1661 1632 msg = 'Unknown name or type in value at line %s.'
1662 1633 else:
1663 1634 msg = 'Parse error in value at line %s.'
1664 1635 self._handle_error(msg, UnreprError, infile,
1665 1636 cur_index)
1666 1637 continue
1667 1638 else:
1668 1639 if self.unrepr:
1669 1640 comment = ''
1670 1641 try:
1671 1642 value = unrepr(value)
1672 1643 except Exception, e:
1673 1644 if isinstance(e, UnknownType):
1674 1645 msg = 'Unknown name or type in value at line %s.'
1675 1646 else:
1676 1647 msg = 'Parse error in value at line %s.'
1677 1648 self._handle_error(msg, UnreprError, infile,
1678 1649 cur_index)
1679 1650 continue
1680 1651 else:
1681 1652 # extract comment and lists
1682 1653 try:
1683 1654 (value, comment) = self._handle_value(value)
1684 1655 except SyntaxError:
1685 1656 self._handle_error(
1686 1657 'Parse error in value at line %s.',
1687 1658 ParseError, infile, cur_index)
1688 1659 continue
1689 1660 #
1690 1661 key = self._unquote(key)
1691 1662 if this_section.has_key(key):
1692 1663 self._handle_error(
1693 1664 'Duplicate keyword name at line %s.',
1694 1665 DuplicateError, infile, cur_index)
1695 1666 continue
1696 1667 # add the key.
1697 1668 # we set unrepr because if we have got this far we will never
1698 1669 # be creating a new section
1699 1670 this_section.__setitem__(key, value, unrepr=True)
1700 1671 this_section.inline_comments[key] = comment
1701 1672 this_section.comments[key] = comment_list
1702 1673 continue
1703 1674 #
1704 1675 if self.indent_type is None:
1705 1676 # no indentation used, set the type accordingly
1706 1677 self.indent_type = ''
1707 1678
1708 1679 # preserve the final comment
1709 1680 if not self and not self.initial_comment:
1710 1681 self.initial_comment = comment_list
1711 1682 elif not reset_comment:
1712 1683 self.final_comment = comment_list
1713 1684 self.list_values = temp_list_values
1714 1685
1715 1686
1716 1687 def _match_depth(self, sect, depth):
1717 1688 """
1718 1689 Given a section and a depth level, walk back through the sections
1719 1690 parents to see if the depth level matches a previous section.
1720 1691
1721 1692 Return a reference to the right section,
1722 1693 or raise a SyntaxError.
1723 1694 """
1724 1695 while depth < sect.depth:
1725 1696 if sect is sect.parent:
1726 1697 # we've reached the top level already
1727 1698 raise SyntaxError()
1728 1699 sect = sect.parent
1729 1700 if sect.depth == depth:
1730 1701 return sect
1731 1702 # shouldn't get here
1732 1703 raise SyntaxError()
1733 1704
1734 1705
1735 1706 def _handle_error(self, text, ErrorClass, infile, cur_index):
1736 1707 """
1737 1708 Handle an error according to the error settings.
1738 1709
1739 1710 Either raise the error or store it.
1740 1711 The error will have occured at ``cur_index``
1741 1712 """
1742 1713 line = infile[cur_index]
1743 1714 cur_index += 1
1744 1715 message = text % cur_index
1745 1716 error = ErrorClass(message, cur_index, line)
1746 1717 if self.raise_errors:
1747 1718 # raise the error - parsing stops here
1748 1719 raise error
1749 1720 # store the error
1750 1721 # reraise when parsing has finished
1751 1722 self._errors.append(error)
1752 1723
1753 1724
1754 1725 def _unquote(self, value):
1755 1726 """Return an unquoted version of a value"""
1756 1727 if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1757 1728 value = value[1:-1]
1758 1729 return value
1759 1730
1760 1731
1761 1732 def _quote(self, value, multiline=True):
1762 1733 """
1763 1734 Return a safely quoted version of a value.
1764 1735
1765 1736 Raise a ConfigObjError if the value cannot be safely quoted.
1766 1737 If multiline is ``True`` (default) then use triple quotes
1767 1738 if necessary.
1768 1739
1769 1740 Don't quote values that don't need it.
1770 1741 Recursively quote members of a list and return a comma joined list.
1771 1742 Multiline is ``False`` for lists.
1772 1743 Obey list syntax for empty and single member lists.
1773 1744
1774 1745 If ``list_values=False`` then the value is only quoted if it contains
1775 1746 a ``\n`` (is multiline) or '#'.
1776 1747
1777 1748 If ``write_empty_values`` is set, and the value is an empty string, it
1778 1749 won't be quoted.
1779 1750 """
1780 1751 if multiline and self.write_empty_values and value == '':
1781 1752 # Only if multiline is set, so that it is used for values not
1782 1753 # keys, and not values that are part of a list
1783 1754 return ''
1784 1755
1785 1756 if multiline and isinstance(value, (list, tuple)):
1786 1757 if not value:
1787 1758 return ','
1788 1759 elif len(value) == 1:
1789 1760 return self._quote(value[0], multiline=False) + ','
1790 1761 return ', '.join([self._quote(val, multiline=False)
1791 1762 for val in value])
1792 1763 if not isinstance(value, StringTypes):
1793 1764 if self.stringify:
1794 1765 value = str(value)
1795 1766 else:
1796 1767 raise TypeError('Value "%s" is not a string.' % value)
1797 1768
1798 1769 if not value:
1799 1770 return '""'
1800 1771
1801 1772 no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1802 1773 need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1803 1774 hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1804 1775 check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1805 1776
1806 1777 if check_for_single:
1807 1778 if not self.list_values:
1808 1779 # we don't quote if ``list_values=False``
1809 1780 quot = noquot
1810 1781 # for normal values either single or double quotes will do
1811 1782 elif '\n' in value:
1812 1783 # will only happen if multiline is off - e.g. '\n' in key
1813 1784 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1814 1785 elif ((value[0] not in wspace_plus) and
1815 1786 (value[-1] not in wspace_plus) and
1816 1787 (',' not in value)):
1817 1788 quot = noquot
1818 1789 else:
1819 1790 quot = self._get_single_quote(value)
1820 1791 else:
1821 1792 # if value has '\n' or "'" *and* '"', it will need triple quotes
1822 1793 quot = self._get_triple_quote(value)
1823 1794
1824 1795 if quot == noquot and '#' in value and self.list_values:
1825 1796 quot = self._get_single_quote(value)
1826 1797
1827 1798 return quot % value
1828 1799
1829 1800
1830 1801 def _get_single_quote(self, value):
1831 1802 if ("'" in value) and ('"' in value):
1832 1803 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1833 1804 elif '"' in value:
1834 1805 quot = squot
1835 1806 else:
1836 1807 quot = dquot
1837 1808 return quot
1838 1809
1839 1810
1840 1811 def _get_triple_quote(self, value):
1841 1812 if (value.find('"""') != -1) and (value.find("'''") != -1):
1842 1813 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1843 1814 if value.find('"""') == -1:
1844 1815 quot = tdquot
1845 1816 else:
1846 1817 quot = tsquot
1847 1818 return quot
1848 1819
1849 1820
1850 1821 def _handle_value(self, value):
1851 1822 """
1852 1823 Given a value string, unquote, remove comment,
1853 1824 handle lists. (including empty and single member lists)
1854 1825 """
1855 1826 # do we look for lists in values ?
1856 1827 if not self.list_values:
1857 1828 mat = self._nolistvalue.match(value)
1858 1829 if mat is None:
1859 1830 raise SyntaxError()
1860 1831 # NOTE: we don't unquote here
1861 1832 return mat.groups()
1862 1833 #
1863 1834 mat = self._valueexp.match(value)
1864 1835 if mat is None:
1865 1836 # the value is badly constructed, probably badly quoted,
1866 1837 # or an invalid list
1867 1838 raise SyntaxError()
1868 1839 (list_values, single, empty_list, comment) = mat.groups()
1869 1840 if (list_values == '') and (single is None):
1870 1841 # change this if you want to accept empty values
1871 1842 raise SyntaxError()
1872 1843 # NOTE: note there is no error handling from here if the regex
1873 1844 # is wrong: then incorrect values will slip through
1874 1845 if empty_list is not None:
1875 1846 # the single comma - meaning an empty list
1876 1847 return ([], comment)
1877 1848 if single is not None:
1878 1849 # handle empty values
1879 1850 if list_values and not single:
1880 1851 # FIXME: the '' is a workaround because our regex now matches
1881 1852 # '' at the end of a list if it has a trailing comma
1882 1853 single = None
1883 1854 else:
1884 1855 single = single or '""'
1885 1856 single = self._unquote(single)
1886 1857 if list_values == '':
1887 1858 # not a list value
1888 1859 return (single, comment)
1889 1860 the_list = self._listvalueexp.findall(list_values)
1890 1861 the_list = [self._unquote(val) for val in the_list]
1891 1862 if single is not None:
1892 1863 the_list += [single]
1893 1864 return (the_list, comment)
1894 1865
1895 1866
1896 1867 def _multiline(self, value, infile, cur_index, maxline):
1897 1868 """Extract the value, where we are in a multiline situation."""
1898 1869 quot = value[:3]
1899 1870 newvalue = value[3:]
1900 1871 single_line = self._triple_quote[quot][0]
1901 1872 multi_line = self._triple_quote[quot][1]
1902 1873 mat = single_line.match(value)
1903 1874 if mat is not None:
1904 1875 retval = list(mat.groups())
1905 1876 retval.append(cur_index)
1906 1877 return retval
1907 1878 elif newvalue.find(quot) != -1:
1908 1879 # somehow the triple quote is missing
1909 1880 raise SyntaxError()
1910 1881 #
1911 1882 while cur_index < maxline:
1912 1883 cur_index += 1
1913 1884 newvalue += '\n'
1914 1885 line = infile[cur_index]
1915 1886 if line.find(quot) == -1:
1916 1887 newvalue += line
1917 1888 else:
1918 1889 # end of multiline, process it
1919 1890 break
1920 1891 else:
1921 1892 # we've got to the end of the config, oops...
1922 1893 raise SyntaxError()
1923 1894 mat = multi_line.match(line)
1924 1895 if mat is None:
1925 1896 # a badly formed line
1926 1897 raise SyntaxError()
1927 1898 (value, comment) = mat.groups()
1928 1899 return (newvalue + value, comment, cur_index)
1929 1900
1930 1901
1931 1902 def _handle_configspec(self, configspec):
1932 1903 """Parse the configspec."""
1933 1904 # FIXME: Should we check that the configspec was created with the
1934 1905 # correct settings ? (i.e. ``list_values=False``)
1935 1906 if not isinstance(configspec, ConfigObj):
1936 1907 try:
1937 1908 configspec = ConfigObj(configspec,
1938 1909 raise_errors=True,
1939 1910 file_error=True,
1940 1911 list_values=False)
1941 1912 except ConfigObjError, e:
1942 1913 # FIXME: Should these errors have a reference
1943 1914 # to the already parsed ConfigObj ?
1944 1915 raise ConfigspecError('Parsing configspec failed: %s' % e)
1945 1916 except IOError, e:
1946 1917 raise IOError('Reading configspec failed: %s' % e)
1947 1918
1948 1919 self._set_configspec_value(configspec, self)
1949 1920
1950 1921
1951 1922 def _set_configspec_value(self, configspec, section):
1952 1923 """Used to recursively set configspec values."""
1953 1924 if '__many__' in configspec.sections:
1954 1925 section.configspec['__many__'] = configspec['__many__']
1955 1926 if len(configspec.sections) > 1:
1956 1927 # FIXME: can we supply any useful information here ?
1957 1928 raise RepeatSectionError()
1958 1929
1959 1930 if hasattr(configspec, 'initial_comment'):
1960 1931 section._configspec_initial_comment = configspec.initial_comment
1961 1932 section._configspec_final_comment = configspec.final_comment
1962 1933 section._configspec_encoding = configspec.encoding
1963 1934 section._configspec_BOM = configspec.BOM
1964 1935 section._configspec_newlines = configspec.newlines
1965 1936 section._configspec_indent_type = configspec.indent_type
1966 1937
1967 1938 for entry in configspec.scalars:
1968 1939 section._configspec_comments[entry] = configspec.comments[entry]
1969 1940 section._configspec_inline_comments[entry] = configspec.inline_comments[entry]
1970 1941 section.configspec[entry] = configspec[entry]
1971 1942 section._order.append(entry)
1972 1943
1973 1944 for entry in configspec.sections:
1974 1945 if entry == '__many__':
1975 1946 continue
1976 1947
1977 1948 section._cs_section_comments[entry] = configspec.comments[entry]
1978 1949 section._cs_section_inline_comments[entry] = configspec.inline_comments[entry]
1979 1950 if not section.has_key(entry):
1980 1951 section[entry] = {}
1981 1952 self._set_configspec_value(configspec[entry], section[entry])
1982 1953
1983 1954
1984 1955 def _handle_repeat(self, section, configspec):
1985 1956 """Dynamically assign configspec for repeated section."""
1986 1957 try:
1987 1958 section_keys = configspec.sections
1988 1959 scalar_keys = configspec.scalars
1989 1960 except AttributeError:
1990 1961 section_keys = [entry for entry in configspec
1991 1962 if isinstance(configspec[entry], dict)]
1992 1963 scalar_keys = [entry for entry in configspec
1993 1964 if not isinstance(configspec[entry], dict)]
1994 1965
1995 1966 if '__many__' in section_keys and len(section_keys) > 1:
1996 1967 # FIXME: can we supply any useful information here ?
1997 1968 raise RepeatSectionError()
1998 1969
1999 1970 scalars = {}
2000 1971 sections = {}
2001 1972 for entry in scalar_keys:
2002 1973 val = configspec[entry]
2003 1974 scalars[entry] = val
2004 1975 for entry in section_keys:
2005 1976 val = configspec[entry]
2006 1977 if entry == '__many__':
2007 1978 scalars[entry] = val
2008 1979 continue
2009 1980 sections[entry] = val
2010 1981
2011 1982 section.configspec = scalars
2012 1983 for entry in sections:
2013 1984 if not section.has_key(entry):
2014 1985 section[entry] = {}
2015 1986 self._handle_repeat(section[entry], sections[entry])
2016 1987
2017 1988
2018 1989 def _write_line(self, indent_string, entry, this_entry, comment):
2019 1990 """Write an individual line, for the write method"""
2020 1991 # NOTE: the calls to self._quote here handles non-StringType values.
2021 1992 if not self.unrepr:
2022 1993 val = self._decode_element(self._quote(this_entry))
2023 1994 else:
2024 1995 val = repr(this_entry)
2025 1996 return '%s%s%s%s%s' % (indent_string,
2026 1997 self._decode_element(self._quote(entry, multiline=False)),
2027 1998 self._a_to_u(' = '),
2028 1999 val,
2029 2000 self._decode_element(comment))
2030 2001
2031 2002
2032 2003 def _write_marker(self, indent_string, depth, entry, comment):
2033 2004 """Write a section marker line"""
2034 2005 return '%s%s%s%s%s' % (indent_string,
2035 2006 self._a_to_u('[' * depth),
2036 2007 self._quote(self._decode_element(entry), multiline=False),
2037 2008 self._a_to_u(']' * depth),
2038 2009 self._decode_element(comment))
2039 2010
2040 2011
2041 2012 def _handle_comment(self, comment):
2042 2013 """Deal with a comment."""
2043 2014 if not comment:
2044 2015 return ''
2045 2016 start = self.indent_type
2046 2017 if not comment.startswith('#'):
2047 2018 start += self._a_to_u(' # ')
2048 2019 return (start + comment)
2049 2020
2050 2021
2051 2022 # Public methods
2052 2023
2053 2024 def write(self, outfile=None, section=None):
2054 2025 """
2055 2026 Write the current ConfigObj as a file
2056 2027
2057 2028 tekNico: FIXME: use StringIO instead of real files
2058 2029
2059 2030 >>> filename = a.filename
2060 2031 >>> a.filename = 'test.ini'
2061 2032 >>> a.write()
2062 2033 >>> a.filename = filename
2063 2034 >>> a == ConfigObj('test.ini', raise_errors=True)
2064 2035 1
2065 2036 """
2066 2037 if self.indent_type is None:
2067 2038 # this can be true if initialised from a dictionary
2068 2039 self.indent_type = DEFAULT_INDENT_TYPE
2069 2040
2070 2041 out = []
2071 2042 cs = self._a_to_u('#')
2072 2043 csp = self._a_to_u('# ')
2073 2044 if section is None:
2074 2045 int_val = self.interpolation
2075 2046 self.interpolation = False
2076 2047 section = self
2077 2048 for line in self.initial_comment:
2078 2049 line = self._decode_element(line)
2079 2050 stripped_line = line.strip()
2080 2051 if stripped_line and not stripped_line.startswith(cs):
2081 2052 line = csp + line
2082 2053 out.append(line)
2083 2054
2084 2055 indent_string = self.indent_type * section.depth
2085 2056 for entry in (section.scalars + section.sections):
2086 2057 if entry in section.defaults:
2087 2058 # don't write out default values
2088 2059 continue
2089 2060 for comment_line in section.comments[entry]:
2090 2061 comment_line = self._decode_element(comment_line.lstrip())
2091 2062 if comment_line and not comment_line.startswith(cs):
2092 2063 comment_line = csp + comment_line
2093 2064 out.append(indent_string + comment_line)
2094 2065 this_entry = section[entry]
2095 2066 comment = self._handle_comment(section.inline_comments[entry])
2096 2067
2097 2068 if isinstance(this_entry, dict):
2098 2069 # a section
2099 2070 out.append(self._write_marker(
2100 2071 indent_string,
2101 2072 this_entry.depth,
2102 2073 entry,
2103 2074 comment))
2104 2075 out.extend(self.write(section=this_entry))
2105 2076 else:
2106 2077 out.append(self._write_line(
2107 2078 indent_string,
2108 2079 entry,
2109 2080 this_entry,
2110 2081 comment))
2111 2082
2112 2083 if section is self:
2113 2084 for line in self.final_comment:
2114 2085 line = self._decode_element(line)
2115 2086 stripped_line = line.strip()
2116 2087 if stripped_line and not stripped_line.startswith(cs):
2117 2088 line = csp + line
2118 2089 out.append(line)
2119 2090 self.interpolation = int_val
2120 2091
2121 2092 if section is not self:
2122 2093 return out
2123 2094
2124 2095 if (self.filename is None) and (outfile is None):
2125 2096 # output a list of lines
2126 2097 # might need to encode
2127 2098 # NOTE: This will *screw* UTF16, each line will start with the BOM
2128 2099 if self.encoding:
2129 2100 out = [l.encode(self.encoding) for l in out]
2130 2101 if (self.BOM and ((self.encoding is None) or
2131 2102 (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2132 2103 # Add the UTF8 BOM
2133 2104 if not out:
2134 2105 out.append('')
2135 2106 out[0] = BOM_UTF8 + out[0]
2136 2107 return out
2137 2108
2138 2109 # Turn the list to a string, joined with correct newlines
2139 2110 newline = self.newlines or os.linesep
2140 2111 output = self._a_to_u(newline).join(out)
2141 2112 if self.encoding:
2142 2113 output = output.encode(self.encoding)
2143 2114 if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2144 2115 # Add the UTF8 BOM
2145 2116 output = BOM_UTF8 + output
2146 2117
2147 2118 if not output.endswith(newline):
2148 2119 output += newline
2149 2120 if outfile is not None:
2150 2121 outfile.write(output)
2151 2122 else:
2152 2123 h = open(self.filename, 'wb')
2153 2124 h.write(output)
2154 2125 h.close()
2155 2126
2156 2127
2157 2128 def validate(self, validator, preserve_errors=False, copy=False,
2158 2129 section=None):
2159 2130 """
2160 2131 Test the ConfigObj against a configspec.
2161 2132
2162 2133 It uses the ``validator`` object from *validate.py*.
2163 2134
2164 2135 To run ``validate`` on the current ConfigObj, call: ::
2165 2136
2166 2137 test = config.validate(validator)
2167 2138
2168 2139 (Normally having previously passed in the configspec when the ConfigObj
2169 2140 was created - you can dynamically assign a dictionary of checks to the
2170 2141 ``configspec`` attribute of a section though).
2171 2142
2172 2143 It returns ``True`` if everything passes, or a dictionary of
2173 2144 pass/fails (True/False). If every member of a subsection passes, it
2174 2145 will just have the value ``True``. (It also returns ``False`` if all
2175 2146 members fail).
2176 2147
2177 2148 In addition, it converts the values from strings to their native
2178 2149 types if their checks pass (and ``stringify`` is set).
2179 2150
2180 2151 If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2181 2152 of a marking a fail with a ``False``, it will preserve the actual
2182 2153 exception object. This can contain info about the reason for failure.
2183 2154 For example the ``VdtValueTooSmallError`` indicates that the value
2184 2155 supplied was too small. If a value (or section) is missing it will
2185 2156 still be marked as ``False``.
2186 2157
2187 2158 You must have the validate module to use ``preserve_errors=True``.
2188 2159
2189 2160 You can then use the ``flatten_errors`` function to turn your nested
2190 2161 results dictionary into a flattened list of failures - useful for
2191 2162 displaying meaningful error messages.
2192 2163 """
2193 2164 if section is None:
2194 2165 if self.configspec is None:
2195 2166 raise ValueError('No configspec supplied.')
2196 2167 if preserve_errors:
2197 2168 # We do this once to remove a top level dependency on the validate module
2198 2169 # Which makes importing configobj faster
2199 2170 from validate import VdtMissingValue
2200 2171 self._vdtMissingValue = VdtMissingValue
2201 2172 section = self
2202 2173 #
2203 2174 spec_section = section.configspec
2204 2175 if copy and hasattr(section, '_configspec_initial_comment'):
2205 2176 section.initial_comment = section._configspec_initial_comment
2206 2177 section.final_comment = section._configspec_final_comment
2207 2178 section.encoding = section._configspec_encoding
2208 2179 section.BOM = section._configspec_BOM
2209 2180 section.newlines = section._configspec_newlines
2210 2181 section.indent_type = section._configspec_indent_type
2211 2182
2212 2183 if '__many__' in section.configspec:
2213 2184 many = spec_section['__many__']
2214 2185 # dynamically assign the configspecs
2215 2186 # for the sections below
2216 2187 for entry in section.sections:
2217 2188 self._handle_repeat(section[entry], many)
2218 2189 #
2219 2190 out = {}
2220 2191 ret_true = True
2221 2192 ret_false = True
2222 2193 order = [k for k in section._order if k in spec_section]
2223 2194 order += [k for k in spec_section if k not in order]
2224 2195 for entry in order:
2225 2196 if entry == '__many__':
2226 2197 continue
2227 2198 if (not entry in section.scalars) or (entry in section.defaults):
2228 2199 # missing entries
2229 2200 # or entries from defaults
2230 2201 missing = True
2231 2202 val = None
2232 2203 if copy and not entry in section.scalars:
2233 2204 # copy comments
2234 2205 section.comments[entry] = (
2235 2206 section._configspec_comments.get(entry, []))
2236 2207 section.inline_comments[entry] = (
2237 2208 section._configspec_inline_comments.get(entry, ''))
2238 2209 #
2239 2210 else:
2240 2211 missing = False
2241 2212 val = section[entry]
2242 2213 try:
2243 2214 check = validator.check(spec_section[entry],
2244 2215 val,
2245 2216 missing=missing
2246 2217 )
2247 2218 except validator.baseErrorClass, e:
2248 2219 if not preserve_errors or isinstance(e, self._vdtMissingValue):
2249 2220 out[entry] = False
2250 2221 else:
2251 2222 # preserve the error
2252 2223 out[entry] = e
2253 2224 ret_false = False
2254 2225 ret_true = False
2255 2226 else:
2256 2227 try:
2257 2228 section.default_values.pop(entry, None)
2258 2229 except AttributeError:
2259 2230 # For Python 2.2 compatibility
2260 2231 try:
2261 2232 del section.default_values[entry]
2262 2233 except KeyError:
2263 2234 pass
2264 2235
2265 2236 if hasattr(validator, 'get_default_value'):
2266 2237 try:
2267 2238 section.default_values[entry] = validator.get_default_value(spec_section[entry])
2268 2239 except KeyError:
2269 2240 # No default
2270 2241 pass
2271 2242
2272 2243 ret_false = False
2273 2244 out[entry] = True
2274 2245 if self.stringify or missing:
2275 2246 # if we are doing type conversion
2276 2247 # or the value is a supplied default
2277 2248 if not self.stringify:
2278 2249 if isinstance(check, (list, tuple)):
2279 2250 # preserve lists
2280 2251 check = [self._str(item) for item in check]
2281 2252 elif missing and check is None:
2282 2253 # convert the None from a default to a ''
2283 2254 check = ''
2284 2255 else:
2285 2256 check = self._str(check)
2286 2257 if (check != val) or missing:
2287 2258 section[entry] = check
2288 2259 if not copy and missing and entry not in section.defaults:
2289 2260 section.defaults.append(entry)
2290 2261 # Missing sections will have been created as empty ones when the
2291 2262 # configspec was read.
2292 2263 for entry in section.sections:
2293 2264 # FIXME: this means DEFAULT is not copied in copy mode
2294 2265 if section is self and entry == 'DEFAULT':
2295 2266 continue
2296 2267 if copy:
2297 2268 section.comments[entry] = section._cs_section_comments[entry]
2298 2269 section.inline_comments[entry] = (
2299 2270 section._cs_section_inline_comments[entry])
2300 2271 check = self.validate(validator, preserve_errors=preserve_errors,
2301 2272 copy=copy, section=section[entry])
2302 2273 out[entry] = check
2303 2274 if check == False:
2304 2275 ret_true = False
2305 2276 elif check == True:
2306 2277 ret_false = False
2307 2278 else:
2308 2279 ret_true = False
2309 2280 ret_false = False
2310 2281 #
2311 2282 if ret_true:
2312 2283 return True
2313 2284 elif ret_false:
2314 2285 return False
2315 2286 return out
2316 2287
2317 2288
2318 2289 def reset(self):
2319 2290 """Clear ConfigObj instance and restore to 'freshly created' state."""
2320 2291 self.clear()
2321 2292 self._initialise()
2322 2293 # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
2323 2294 # requires an empty dictionary
2324 2295 self.configspec = None
2325 2296 # Just to be sure ;-)
2326 2297 self._original_configspec = None
2327 2298
2328 2299
2329 2300 def reload(self):
2330 2301 """
2331 2302 Reload a ConfigObj from file.
2332 2303
2333 2304 This method raises a ``ReloadError`` if the ConfigObj doesn't have
2334 2305 a filename attribute pointing to a file.
2335 2306 """
2336 2307 if not isinstance(self.filename, StringTypes):
2337 2308 raise ReloadError()
2338 2309
2339 2310 filename = self.filename
2340 2311 current_options = {}
2341 2312 for entry in OPTION_DEFAULTS:
2342 2313 if entry == 'configspec':
2343 2314 continue
2344 2315 current_options[entry] = getattr(self, entry)
2345 2316
2346 2317 configspec = self._original_configspec
2347 2318 current_options['configspec'] = configspec
2348 2319
2349 2320 self.clear()
2350 2321 self._initialise(current_options)
2351 2322 self._load(filename, configspec)
2352 2323
2353 2324
2354 2325
2355 2326 class SimpleVal(object):
2356 2327 """
2357 2328 A simple validator.
2358 2329 Can be used to check that all members expected are present.
2359 2330
2360 2331 To use it, provide a configspec with all your members in (the value given
2361 2332 will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2362 2333 method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2363 2334 members are present, or a dictionary with True/False meaning
2364 2335 present/missing. (Whole missing sections will be replaced with ``False``)
2365 2336 """
2366 2337
2367 2338 def __init__(self):
2368 2339 self.baseErrorClass = ConfigObjError
2369 2340
2370 2341 def check(self, check, member, missing=False):
2371 2342 """A dummy check method, always returns the value unchanged."""
2372 2343 if missing:
2373 2344 raise self.baseErrorClass()
2374 2345 return member
2375 2346
2376 2347
2377 2348 # Check / processing functions for options
2378 2349 def flatten_errors(cfg, res, levels=None, results=None):
2379 2350 """
2380 2351 An example function that will turn a nested dictionary of results
2381 2352 (as returned by ``ConfigObj.validate``) into a flat list.
2382 2353
2383 2354 ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2384 2355 dictionary returned by ``validate``.
2385 2356
2386 2357 (This is a recursive function, so you shouldn't use the ``levels`` or
2387 2358 ``results`` arguments - they are used by the function.
2388 2359
2389 2360 Returns a list of keys that failed. Each member of the list is a tuple :
2390 2361 ::
2391 2362
2392 2363 ([list of sections...], key, result)
2393 2364
2394 2365 If ``validate`` was called with ``preserve_errors=False`` (the default)
2395 2366 then ``result`` will always be ``False``.
2396 2367
2397 2368 *list of sections* is a flattened list of sections that the key was found
2398 2369 in.
2399 2370
2400 2371 If the section was missing then key will be ``None``.
2401 2372
2402 2373 If the value (or section) was missing then ``result`` will be ``False``.
2403 2374
2404 2375 If ``validate`` was called with ``preserve_errors=True`` and a value
2405 2376 was present, but failed the check, then ``result`` will be the exception
2406 2377 object returned. You can use this as a string that describes the failure.
2407 2378
2408 2379 For example *The value "3" is of the wrong type*.
2409 2380
2410 2381 >>> import validate
2411 2382 >>> vtor = validate.Validator()
2412 2383 >>> my_ini = '''
2413 2384 ... option1 = True
2414 2385 ... [section1]
2415 2386 ... option1 = True
2416 2387 ... [section2]
2417 2388 ... another_option = Probably
2418 2389 ... [section3]
2419 2390 ... another_option = True
2420 2391 ... [[section3b]]
2421 2392 ... value = 3
2422 2393 ... value2 = a
2423 2394 ... value3 = 11
2424 2395 ... '''
2425 2396 >>> my_cfg = '''
2426 2397 ... option1 = boolean()
2427 2398 ... option2 = boolean()
2428 2399 ... option3 = boolean(default=Bad_value)
2429 2400 ... [section1]
2430 2401 ... option1 = boolean()
2431 2402 ... option2 = boolean()
2432 2403 ... option3 = boolean(default=Bad_value)
2433 2404 ... [section2]
2434 2405 ... another_option = boolean()
2435 2406 ... [section3]
2436 2407 ... another_option = boolean()
2437 2408 ... [[section3b]]
2438 2409 ... value = integer
2439 2410 ... value2 = integer
2440 2411 ... value3 = integer(0, 10)
2441 2412 ... [[[section3b-sub]]]
2442 2413 ... value = string
2443 2414 ... [section4]
2444 2415 ... another_option = boolean()
2445 2416 ... '''
2446 2417 >>> cs = my_cfg.split('\\n')
2447 2418 >>> ini = my_ini.split('\\n')
2448 2419 >>> cfg = ConfigObj(ini, configspec=cs)
2449 2420 >>> res = cfg.validate(vtor, preserve_errors=True)
2450 2421 >>> errors = []
2451 2422 >>> for entry in flatten_errors(cfg, res):
2452 2423 ... section_list, key, error = entry
2453 2424 ... section_list.insert(0, '[root]')
2454 2425 ... if key is not None:
2455 2426 ... section_list.append(key)
2456 2427 ... else:
2457 2428 ... section_list.append('[missing]')
2458 2429 ... section_string = ', '.join(section_list)
2459 2430 ... errors.append((section_string, ' = ', error))
2460 2431 >>> errors.sort()
2461 2432 >>> for entry in errors:
2462 2433 ... print entry[0], entry[1], (entry[2] or 0)
2463 2434 [root], option2 = 0
2464 2435 [root], option3 = the value "Bad_value" is of the wrong type.
2465 2436 [root], section1, option2 = 0
2466 2437 [root], section1, option3 = the value "Bad_value" is of the wrong type.
2467 2438 [root], section2, another_option = the value "Probably" is of the wrong type.
2468 2439 [root], section3, section3b, section3b-sub, [missing] = 0
2469 2440 [root], section3, section3b, value2 = the value "a" is of the wrong type.
2470 2441 [root], section3, section3b, value3 = the value "11" is too big.
2471 2442 [root], section4, [missing] = 0
2472 2443 """
2473 2444 if levels is None:
2474 2445 # first time called
2475 2446 levels = []
2476 2447 results = []
2477 2448 if res is True:
2478 2449 return results
2479 2450 if res is False:
2480 2451 results.append((levels[:], None, False))
2481 2452 if levels:
2482 2453 levels.pop()
2483 2454 return results
2484 for (key, val) in res.items():
2455 for (key, val) in res.iteritems():
2485 2456 if val == True:
2486 2457 continue
2487 2458 if isinstance(cfg.get(key), dict):
2488 2459 # Go down one level
2489 2460 levels.append(key)
2490 2461 flatten_errors(cfg[key], val, levels, results)
2491 2462 continue
2492 2463 results.append((levels[:], key, val))
2493 2464 #
2494 2465 # Go up one level
2495 2466 if levels:
2496 2467 levels.pop()
2497 2468 #
2498 2469 return results
2499 2470
2500 2471
2501 2472 """*A programming language is a medium of expression.* - Paul Graham"""
@@ -1,705 +1,692 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 pretty
4 4 ~~
5 5
6 6 Python advanced pretty printer. This pretty printer is intended to
7 7 replace the old `pprint` python module which does not allow developers
8 8 to provide their own pretty print callbacks.
9 9
10 10 This module is based on ruby's `prettyprint.rb` library by `Tanaka Akira`.
11 11
12 12
13 13 Example Usage
14 14 =============
15 15
16 16 To directly print the representation of an object use `pprint`::
17 17
18 18 from pretty import pprint
19 19 pprint(complex_object)
20 20
21 21 To get a string of the output use `pretty`::
22 22
23 23 from pretty import pretty
24 24 string = pretty(complex_object)
25 25
26 26
27 27 Extending
28 28 =========
29 29
30 30 The pretty library allows developers to add pretty printing rules for their
31 31 own objects. This process is straightforward. All you have to do is to
32 32 add a `__pretty__` method to your object and call the methods on the
33 33 pretty printer passed::
34 34
35 35 class MyObject(object):
36 36
37 37 def __pretty__(self, p, cycle):
38 38 ...
39 39
40 40 Depending on the python version you want to support you have two
41 41 possibilities. The following list shows the python 2.5 version and the
42 42 compatibility one.
43 43
44 44
45 45 Here the example implementation of a `__pretty__` method for a list
46 46 subclass for python 2.5 and higher (python 2.5 requires the with statement
47 47 __future__ import)::
48 48
49 49 class MyList(list):
50 50
51 51 def __pretty__(self, p, cycle):
52 52 if cycle:
53 53 p.text('MyList(...)')
54 54 else:
55 55 with p.group(8, 'MyList([', '])'):
56 56 for idx, item in enumerate(self):
57 57 if idx:
58 58 p.text(',')
59 59 p.breakable()
60 60 p.pretty(item)
61 61
62 62 The `cycle` parameter is `True` if pretty detected a cycle. You *have* to
63 63 react to that or the result is an infinite loop. `p.text()` just adds
64 64 non breaking text to the output, `p.breakable()` either adds a whitespace
65 65 or breaks here. If you pass it an argument it's used instead of the
66 66 default space. `p.pretty` prettyprints another object using the pretty print
67 67 method.
68 68
69 69 The first parameter to the `group` function specifies the extra indentation
70 70 of the next line. In this example the next item will either be not
71 71 breaked (if the items are short enough) or aligned with the right edge of
72 72 the opening bracked of `MyList`.
73 73
74 74 If you want to support python 2.4 and lower you can use this code::
75 75
76 76 class MyList(list):
77 77
78 78 def __pretty__(self, p, cycle):
79 79 if cycle:
80 80 p.text('MyList(...)')
81 81 else:
82 82 p.begin_group(8, 'MyList([')
83 83 for idx, item in enumerate(self):
84 84 if idx:
85 85 p.text(',')
86 86 p.breakable()
87 87 p.pretty(item)
88 88 p.end_group(8, '])')
89 89
90 90 If you just want to indent something you can use the group function
91 91 without open / close parameters. Under python 2.5 you can also use this
92 92 code::
93 93
94 94 with p.indent(2):
95 95 ...
96 96
97 97 Or under python2.4 you might want to modify ``p.indentation`` by hand but
98 98 this is rather ugly.
99 99
100 100 :copyright: 2007 by Armin Ronacher.
101 101 Portions (c) 2009 by Robert Kern.
102 102 :license: BSD License.
103 103 """
104 104 import __future__
105 105 import sys
106 106 import types
107 107 import re
108 108 import datetime
109 109 from StringIO import StringIO
110 110 from collections import deque
111 111
112 112
113 113 __all__ = ['pretty', 'pprint', 'PrettyPrinter', 'RepresentationPrinter',
114 114 'for_type', 'for_type_by_name']
115 115
116 116
117 117 _re_pattern_type = type(re.compile(''))
118 118
119 119
120 120 def pretty(obj, verbose=False, max_width=79, newline='\n'):
121 121 """
122 122 Pretty print the object's representation.
123 123 """
124 124 stream = StringIO()
125 125 printer = RepresentationPrinter(stream, verbose, max_width, newline)
126 126 printer.pretty(obj)
127 127 printer.flush()
128 128 return stream.getvalue()
129 129
130 130
131 131 def pprint(obj, verbose=False, max_width=79, newline='\n'):
132 132 """
133 133 Like `pretty` but print to stdout.
134 134 """
135 135 printer = RepresentationPrinter(sys.stdout, verbose, max_width, newline)
136 136 printer.pretty(obj)
137 137 printer.flush()
138 138 sys.stdout.write(newline)
139 139 sys.stdout.flush()
140 140
141
142 # add python2.5 context managers if we have the with statement feature
143 if hasattr(__future__, 'with_statement'): exec '''
144 141 from __future__ import with_statement
145 142 from contextlib import contextmanager
146 143
147 144 class _PrettyPrinterBase(object):
148 145
149 146 @contextmanager
150 147 def indent(self, indent):
151 148 """with statement support for indenting/dedenting."""
152 149 self.indentation += indent
153 150 try:
154 151 yield
155 152 finally:
156 153 self.indentation -= indent
157 154
158 155 @contextmanager
159 156 def group(self, indent=0, open='', close=''):
160 157 """like begin_group / end_group but for the with statement."""
161 158 self.begin_group(indent, open)
162 159 try:
163 160 with self.indent(indent):
164 161 yield
165 162 finally:
166 163 self.end_group(indent, close)
167 '''
168 else:
169 class _PrettyPrinterBase(object):
170
171 def _unsupported(self, *a, **kw):
172 """unsupported operation"""
173 raise RuntimeError('not available in this python version')
174 group = indent = _unsupported
175 del _unsupported
176
177 164
178 165 class PrettyPrinter(_PrettyPrinterBase):
179 166 """
180 167 Baseclass for the `RepresentationPrinter` prettyprinter that is used to
181 168 generate pretty reprs of objects. Contrary to the `RepresentationPrinter`
182 169 this printer knows nothing about the default pprinters or the `__pretty__`
183 170 callback method.
184 171 """
185 172
186 173 def __init__(self, output, max_width=79, newline='\n'):
187 174 self.output = output
188 175 self.max_width = max_width
189 176 self.newline = newline
190 177 self.output_width = 0
191 178 self.buffer_width = 0
192 179 self.buffer = deque()
193 180
194 181 root_group = Group(0)
195 182 self.group_stack = [root_group]
196 183 self.group_queue = GroupQueue(root_group)
197 184 self.indentation = 0
198 185
199 186 def _break_outer_groups(self):
200 187 while self.max_width < self.output_width + self.buffer_width:
201 188 group = self.group_queue.deq()
202 189 if not group:
203 190 return
204 191 while group.breakables:
205 192 x = self.buffer.popleft()
206 193 self.output_width = x.output(self.output, self.output_width)
207 194 self.buffer_width -= x.width
208 195 while self.buffer and isinstance(self.buffer[0], Text):
209 196 x = self.buffer.popleft()
210 197 self.output_width = x.output(self.output, self.output_width)
211 198 self.buffer_width -= x.width
212 199
213 200 def text(self, obj):
214 201 """Add literal text to the output."""
215 202 width = len(obj)
216 203 if self.buffer:
217 204 text = self.buffer[-1]
218 205 if not isinstance(text, Text):
219 206 text = Text()
220 207 self.buffer.append(text)
221 208 text.add(obj, width)
222 209 self.buffer_width += width
223 210 self._break_outer_groups()
224 211 else:
225 212 self.output.write(obj)
226 213 self.output_width += width
227 214
228 215 def breakable(self, sep=' '):
229 216 """
230 217 Add a breakable separator to the output. This does not mean that it
231 218 will automatically break here. If no breaking on this position takes
232 219 place the `sep` is inserted which default to one space.
233 220 """
234 221 width = len(sep)
235 222 group = self.group_stack[-1]
236 223 if group.want_break:
237 224 self.flush()
238 225 self.output.write(self.newline)
239 226 self.output.write(' ' * self.indentation)
240 227 self.output_width = self.indentation
241 228 self.buffer_width = 0
242 229 else:
243 230 self.buffer.append(Breakable(sep, width, self))
244 231 self.buffer_width += width
245 232 self._break_outer_groups()
246 233
247 234
248 235 def begin_group(self, indent=0, open=''):
249 236 """
250 237 Begin a group. If you want support for python < 2.5 which doesn't has
251 238 the with statement this is the preferred way:
252 239
253 240 p.begin_group(1, '{')
254 241 ...
255 242 p.end_group(1, '}')
256 243
257 244 The python 2.5 expression would be this:
258 245
259 246 with p.group(1, '{', '}'):
260 247 ...
261 248
262 249 The first parameter specifies the indentation for the next line (usually
263 250 the width of the opening text), the second the opening text. All
264 251 parameters are optional.
265 252 """
266 253 if open:
267 254 self.text(open)
268 255 group = Group(self.group_stack[-1].depth + 1)
269 256 self.group_stack.append(group)
270 257 self.group_queue.enq(group)
271 258 self.indentation += indent
272 259
273 260 def end_group(self, dedent=0, close=''):
274 261 """End a group. See `begin_group` for more details."""
275 262 self.indentation -= dedent
276 263 group = self.group_stack.pop()
277 264 if not group.breakables:
278 265 self.group_queue.remove(group)
279 266 if close:
280 267 self.text(close)
281 268
282 269 def flush(self):
283 270 """Flush data that is left in the buffer."""
284 271 for data in self.buffer:
285 272 self.output_width += data.output(self.output, self.output_width)
286 273 self.buffer.clear()
287 274 self.buffer_width = 0
288 275
289 276
290 277 def _get_mro(obj_class):
291 278 """ Get a reasonable method resolution order of a class and its superclasses
292 279 for both old-style and new-style classes.
293 280 """
294 281 if not hasattr(obj_class, '__mro__'):
295 282 # Old-style class. Mix in object to make a fake new-style class.
296 283 try:
297 284 obj_class = type(obj_class.__name__, (obj_class, object), {})
298 285 except TypeError:
299 286 # Old-style extension type that does not descend from object.
300 287 # FIXME: try to construct a more thorough MRO.
301 288 mro = [obj_class]
302 289 else:
303 290 mro = obj_class.__mro__[1:-1]
304 291 else:
305 292 mro = obj_class.__mro__
306 293 return mro
307 294
308 295
309 296 class RepresentationPrinter(PrettyPrinter):
310 297 """
311 298 Special pretty printer that has a `pretty` method that calls the pretty
312 299 printer for a python object.
313 300
314 301 This class stores processing data on `self` so you must *never* use
315 302 this class in a threaded environment. Always lock it or reinstanciate
316 303 it.
317 304
318 305 Instances also have a verbose flag callbacks can access to control their
319 306 output. For example the default instance repr prints all attributes and
320 307 methods that are not prefixed by an underscore if the printer is in
321 308 verbose mode.
322 309 """
323 310
324 311 def __init__(self, output, verbose=False, max_width=79, newline='\n'):
325 312 PrettyPrinter.__init__(self, output, max_width, newline)
326 313 self.verbose = verbose
327 314 self.stack = []
328 315
329 316 def pretty(self, obj):
330 317 """Pretty print the given object."""
331 318 obj_id = id(obj)
332 319 cycle = obj_id in self.stack
333 320 self.stack.append(obj_id)
334 321 self.begin_group()
335 322 try:
336 323 obj_class = getattr(obj, '__class__', None) or type(obj)
337 324 if hasattr(obj_class, '__pretty__'):
338 325 return obj_class.__pretty__(obj, self, cycle)
339 326 try:
340 327 printer = _singleton_pprinters[obj_id]
341 328 except (TypeError, KeyError):
342 329 pass
343 330 else:
344 331 return printer(obj, self, cycle)
345 332 for cls in _get_mro(obj_class):
346 333 if cls in _type_pprinters:
347 334 return _type_pprinters[cls](obj, self, cycle)
348 335 else:
349 336 printer = self._in_deferred_types(cls)
350 337 if printer is not None:
351 338 return printer(obj, self, cycle)
352 339 return _default_pprint(obj, self, cycle)
353 340 finally:
354 341 self.end_group()
355 342 self.stack.pop()
356 343
357 344 def _in_deferred_types(self, cls):
358 345 """
359 346 Check if the given class is specified in the deferred type registry.
360 347
361 348 Returns the printer from the registry if it exists, and None if the
362 349 class is not in the registry. Successful matches will be moved to the
363 350 regular type registry for future use.
364 351 """
365 352 mod = getattr(cls, '__module__', None)
366 353 name = getattr(cls, '__name__', None)
367 354 key = (mod, name)
368 355 printer = None
369 356 if key in _deferred_type_pprinters:
370 357 # Move the printer over to the regular registry.
371 358 printer = _deferred_type_pprinters.pop(key)
372 359 _type_pprinters[cls] = printer
373 360 return printer
374 361
375 362
376 363
377 364 class Printable(object):
378 365
379 366 def output(self, stream, output_width):
380 367 return output_width
381 368
382 369
383 370 class Text(Printable):
384 371
385 372 def __init__(self):
386 373 self.objs = []
387 374 self.width = 0
388 375
389 376 def output(self, stream, output_width):
390 377 for obj in self.objs:
391 378 stream.write(obj)
392 379 return output_width + self.width
393 380
394 381 def add(self, obj, width):
395 382 self.objs.append(obj)
396 383 self.width += width
397 384
398 385
399 386 class Breakable(Printable):
400 387
401 388 def __init__(self, seq, width, pretty):
402 389 self.obj = seq
403 390 self.width = width
404 391 self.pretty = pretty
405 392 self.indentation = pretty.indentation
406 393 self.group = pretty.group_stack[-1]
407 394 self.group.breakables.append(self)
408 395
409 396 def output(self, stream, output_width):
410 397 self.group.breakables.popleft()
411 398 if self.group.want_break:
412 399 stream.write(self.pretty.newline)
413 400 stream.write(' ' * self.indentation)
414 401 return self.indentation
415 402 if not self.group.breakables:
416 403 self.pretty.group_queue.remove(self.group)
417 404 stream.write(self.obj)
418 405 return output_width + self.width
419 406
420 407
421 408 class Group(Printable):
422 409
423 410 def __init__(self, depth):
424 411 self.depth = depth
425 412 self.breakables = deque()
426 413 self.want_break = False
427 414
428 415
429 416 class GroupQueue(object):
430 417
431 418 def __init__(self, *groups):
432 419 self.queue = []
433 420 for group in groups:
434 421 self.enq(group)
435 422
436 423 def enq(self, group):
437 424 depth = group.depth
438 425 while depth > len(self.queue) - 1:
439 426 self.queue.append([])
440 427 self.queue[depth].append(group)
441 428
442 429 def deq(self):
443 430 for stack in self.queue:
444 431 for idx, group in enumerate(reversed(stack)):
445 432 if group.breakables:
446 433 del stack[idx]
447 434 group.want_break = True
448 435 return group
449 436 for group in stack:
450 437 group.want_break = True
451 438 del stack[:]
452 439
453 440 def remove(self, group):
454 441 try:
455 442 self.queue[group.depth].remove(group)
456 443 except ValueError:
457 444 pass
458 445
459 446
460 447 _baseclass_reprs = (object.__repr__, types.InstanceType.__repr__)
461 448
462 449
463 450 def _default_pprint(obj, p, cycle):
464 451 """
465 452 The default print function. Used if an object does not provide one and
466 453 it's none of the builtin objects.
467 454 """
468 455 klass = getattr(obj, '__class__', None) or type(obj)
469 456 if getattr(klass, '__repr__', None) not in _baseclass_reprs:
470 457 # A user-provided repr.
471 458 p.text(repr(obj))
472 459 return
473 460 p.begin_group(1, '<')
474 461 p.pretty(klass)
475 462 p.text(' at 0x%x' % id(obj))
476 463 if cycle:
477 464 p.text(' ...')
478 465 elif p.verbose:
479 466 first = True
480 467 for key in dir(obj):
481 468 if not key.startswith('_'):
482 469 try:
483 470 value = getattr(obj, key)
484 471 except AttributeError:
485 472 continue
486 473 if isinstance(value, types.MethodType):
487 474 continue
488 475 if not first:
489 476 p.text(',')
490 477 p.breakable()
491 478 p.text(key)
492 479 p.text('=')
493 480 step = len(key) + 1
494 481 p.indentation += step
495 482 p.pretty(value)
496 483 p.indentation -= step
497 484 first = False
498 485 p.end_group(1, '>')
499 486
500 487
501 488 def _seq_pprinter_factory(start, end):
502 489 """
503 490 Factory that returns a pprint function useful for sequences. Used by
504 491 the default pprint for tuples, dicts, lists, sets and frozensets.
505 492 """
506 493 def inner(obj, p, cycle):
507 494 if cycle:
508 495 return p.text(start + '...' + end)
509 496 step = len(start)
510 497 p.begin_group(step, start)
511 498 for idx, x in enumerate(obj):
512 499 if idx:
513 500 p.text(',')
514 501 p.breakable()
515 502 p.pretty(x)
516 503 if len(obj) == 1 and type(obj) is tuple:
517 504 # Special case for 1-item tuples.
518 505 p.text(',')
519 506 p.end_group(step, end)
520 507 return inner
521 508
522 509
523 510 def _dict_pprinter_factory(start, end):
524 511 """
525 512 Factory that returns a pprint function used by the default pprint of
526 513 dicts and dict proxies.
527 514 """
528 515 def inner(obj, p, cycle):
529 516 if cycle:
530 517 return p.text('{...}')
531 518 p.begin_group(1, start)
532 519 keys = obj.keys()
533 520 try:
534 521 keys.sort()
535 522 except Exception, e:
536 523 # Sometimes the keys don't sort.
537 524 pass
538 525 for idx, key in enumerate(keys):
539 526 if idx:
540 527 p.text(',')
541 528 p.breakable()
542 529 p.pretty(key)
543 530 p.text(': ')
544 531 p.pretty(obj[key])
545 532 p.end_group(1, end)
546 533 return inner
547 534
548 535
549 536 def _super_pprint(obj, p, cycle):
550 537 """The pprint for the super type."""
551 538 p.begin_group(8, '<super: ')
552 539 p.pretty(obj.__self_class__)
553 540 p.text(',')
554 541 p.breakable()
555 542 p.pretty(obj.__self__)
556 543 p.end_group(8, '>')
557 544
558 545
559 546 def _re_pattern_pprint(obj, p, cycle):
560 547 """The pprint function for regular expression patterns."""
561 548 p.text('re.compile(')
562 549 pattern = repr(obj.pattern)
563 550 if pattern[:1] in 'uU':
564 551 pattern = pattern[1:]
565 552 prefix = 'ur'
566 553 else:
567 554 prefix = 'r'
568 555 pattern = prefix + pattern.replace('\\\\', '\\')
569 556 p.text(pattern)
570 557 if obj.flags:
571 558 p.text(',')
572 559 p.breakable()
573 560 done_one = False
574 561 for flag in ('TEMPLATE', 'IGNORECASE', 'LOCALE', 'MULTILINE', 'DOTALL',
575 562 'UNICODE', 'VERBOSE', 'DEBUG'):
576 563 if obj.flags & getattr(re, flag):
577 564 if done_one:
578 565 p.text('|')
579 566 p.text('re.' + flag)
580 567 done_one = True
581 568 p.text(')')
582 569
583 570
584 571 def _type_pprint(obj, p, cycle):
585 572 """The pprint for classes and types."""
586 573 if obj.__module__ in ('__builtin__', 'exceptions'):
587 574 name = obj.__name__
588 575 else:
589 576 name = obj.__module__ + '.' + obj.__name__
590 577 p.text(name)
591 578
592 579
593 580 def _repr_pprint(obj, p, cycle):
594 581 """A pprint that just redirects to the normal repr function."""
595 582 p.text(repr(obj))
596 583
597 584
598 585 def _function_pprint(obj, p, cycle):
599 586 """Base pprint for all functions and builtin functions."""
600 587 if obj.__module__ in ('__builtin__', 'exceptions') or not obj.__module__:
601 588 name = obj.__name__
602 589 else:
603 590 name = obj.__module__ + '.' + obj.__name__
604 591 p.text('<function %s>' % name)
605 592
606 593
607 594 def _exception_pprint(obj, p, cycle):
608 595 """Base pprint for all exceptions."""
609 596 if obj.__class__.__module__ == 'exceptions':
610 597 name = obj.__class__.__name__
611 598 else:
612 599 name = '%s.%s' % (
613 600 obj.__class__.__module__,
614 601 obj.__class__.__name__
615 602 )
616 603 step = len(name) + 1
617 604 p.begin_group(step, '(')
618 605 for idx, arg in enumerate(getattr(obj, 'args', ())):
619 606 if idx:
620 607 p.text(',')
621 608 p.breakable()
622 609 p.pretty(arg)
623 610 p.end_group(step, ')')
624 611
625 612
626 613 #: the exception base
627 614 try:
628 615 _exception_base = BaseException
629 616 except NameError:
630 617 _exception_base = Exception
631 618
632 619
633 620 #: printers for builtin types
634 621 _type_pprinters = {
635 622 int: _repr_pprint,
636 623 long: _repr_pprint,
637 624 float: _repr_pprint,
638 625 str: _repr_pprint,
639 626 unicode: _repr_pprint,
640 627 tuple: _seq_pprinter_factory('(', ')'),
641 628 list: _seq_pprinter_factory('[', ']'),
642 629 dict: _dict_pprinter_factory('{', '}'),
643 630 types.DictProxyType: _dict_pprinter_factory('<dictproxy {', '}>'),
644 631 set: _seq_pprinter_factory('set([', '])'),
645 632 frozenset: _seq_pprinter_factory('frozenset([', '])'),
646 633 super: _super_pprint,
647 634 _re_pattern_type: _re_pattern_pprint,
648 635 type: _type_pprint,
649 636 types.ClassType: _type_pprint,
650 637 types.FunctionType: _function_pprint,
651 638 types.BuiltinFunctionType: _function_pprint,
652 639 types.SliceType: _repr_pprint,
653 640 types.MethodType: _repr_pprint,
654 641 xrange: _repr_pprint,
655 642 datetime.datetime: _repr_pprint,
656 643 datetime.timedelta: _repr_pprint,
657 644 _exception_base: _exception_pprint
658 645 }
659 646
660 647 #: printers for types specified by name
661 648 _deferred_type_pprinters = {
662 649 }
663 650
664 651 def for_type(typ, func):
665 652 """
666 653 Add a pretty printer for a given type.
667 654 """
668 655 oldfunc = _type_pprinters.get(typ, None)
669 656 if func is not None:
670 657 # To support easy restoration of old pprinters, we need to ignore Nones.
671 658 _type_pprinters[typ] = func
672 659 return oldfunc
673 660
674 661 def for_type_by_name(type_module, type_name, func):
675 662 """
676 663 Add a pretty printer for a type specified by the module and name of a type
677 664 rather than the type object itself.
678 665 """
679 666 key = (type_module, type_name)
680 667 oldfunc = _deferred_type_pprinters.get(key, None)
681 668 if func is not None:
682 669 # To support easy restoration of old pprinters, we need to ignore Nones.
683 670 _deferred_type_pprinters[key] = func
684 671 return oldfunc
685 672
686 673
687 674 #: printers for the default singletons
688 675 _singleton_pprinters = dict.fromkeys(map(id, [None, True, False, Ellipsis,
689 676 NotImplemented]), _repr_pprint)
690 677
691 678
692 679 if __name__ == '__main__':
693 680 from random import randrange
694 681 class Foo(object):
695 682 def __init__(self):
696 683 self.foo = 1
697 684 self.bar = re.compile(r'\s+')
698 685 self.blub = dict.fromkeys(range(30), randrange(1, 40))
699 686 self.hehe = 23424.234234
700 687 self.list = ["blub", "blah", self]
701 688
702 689 def get_foo(self):
703 690 print "foo"
704 691
705 692 pprint(Foo(), verbose=True)
@@ -1,3707 +1,3708 b''
1 # -*- coding: utf-8 -*-
1 2 # module pyparsing.py
2 3 #
3 4 # Copyright (c) 2003-2009 Paul T. McGuire
4 5 #
5 6 # Permission is hereby granted, free of charge, to any person obtaining
6 7 # a copy of this software and associated documentation files (the
7 8 # "Software"), to deal in the Software without restriction, including
8 9 # without limitation the rights to use, copy, modify, merge, publish,
9 10 # distribute, sublicense, and/or sell copies of the Software, and to
10 11 # permit persons to whom the Software is furnished to do so, subject to
11 12 # the following conditions:
12 13 #
13 14 # The above copyright notice and this permission notice shall be
14 15 # included in all copies or substantial portions of the Software.
15 16 #
16 17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 20 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 21 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 22 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 23 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 24 #
24 25 #from __future__ import generators
25 26
26 27 __doc__ = \
27 28 """
28 29 pyparsing module - Classes and methods to define and execute parsing grammars
29 30
30 31 The pyparsing module is an alternative approach to creating and executing simple grammars,
31 32 vs. the traditional lex/yacc approach, or the use of regular expressions. With pyparsing, you
32 33 don't need to learn a new syntax for defining grammars or matching expressions - the parsing module
33 34 provides a library of classes that you use to construct the grammar directly in Python.
34 35
35 36 Here is a program to parse "Hello, World!" (or any greeting of the form "<salutation>, <addressee>!")::
36 37
37 38 from pyparsing import Word, alphas
38 39
39 40 # define grammar of a greeting
40 41 greet = Word( alphas ) + "," + Word( alphas ) + "!"
41 42
42 43 hello = "Hello, World!"
43 44 print hello, "->", greet.parseString( hello )
44 45
45 46 The program outputs the following::
46 47
47 48 Hello, World! -> ['Hello', ',', 'World', '!']
48 49
49 50 The Python representation of the grammar is quite readable, owing to the self-explanatory
50 51 class names, and the use of '+', '|' and '^' operators.
51 52
52 53 The parsed results returned from parseString() can be accessed as a nested list, a dictionary, or an
53 54 object with named attributes.
54 55
55 56 The pyparsing module handles some of the problems that are typically vexing when writing text parsers:
56 57 - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello , World !", etc.)
57 58 - quoted strings
58 59 - embedded comments
59 60 """
60 61
61 62 __version__ = "1.5.2"
62 63 __versionTime__ = "17 February 2009 19:45"
63 64 __author__ = "Paul McGuire <ptmcg@users.sourceforge.net>"
64 65
65 66 import string
66 67 from weakref import ref as wkref
67 68 import copy
68 69 import sys
69 70 import warnings
70 71 import re
71 72 import sre_constants
72 73 #~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) )
73 74
74 75 __all__ = [
75 76 'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty',
76 77 'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal',
77 78 'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or',
78 79 'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException',
79 80 'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException',
80 81 'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 'Upcase',
81 82 'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore',
82 83 'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col',
83 84 'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString',
84 85 'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'getTokensEndLoc', 'hexnums',
85 86 'htmlComment', 'javaStyleComment', 'keepOriginalText', 'line', 'lineEnd', 'lineStart', 'lineno',
86 87 'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral',
87 88 'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables',
88 89 'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity',
89 90 'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd',
90 91 'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute',
91 92 'indentedBlock', 'originalTextFor',
92 93 ]
93 94
94 95
95 96 """
96 97 Detect if we are running version 3.X and make appropriate changes
97 98 Robert A. Clark
98 99 """
99 100 if sys.version_info[0] > 2:
100 101 _PY3K = True
101 102 _MAX_INT = sys.maxsize
102 103 basestring = str
103 104 else:
104 105 _PY3K = False
105 106 _MAX_INT = sys.maxint
106 107
107 108 if not _PY3K:
108 109 def _ustr(obj):
109 110 """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries
110 111 str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It
111 112 then < returns the unicode object | encodes it with the default encoding | ... >.
112 113 """
113 114 if isinstance(obj,unicode):
114 115 return obj
115 116
116 117 try:
117 118 # If this works, then _ustr(obj) has the same behaviour as str(obj), so
118 119 # it won't break any existing code.
119 120 return str(obj)
120 121
121 122 except UnicodeEncodeError:
122 123 # The Python docs (http://docs.python.org/ref/customization.html#l2h-182)
123 124 # state that "The return value must be a string object". However, does a
124 125 # unicode object (being a subclass of basestring) count as a "string
125 126 # object"?
126 127 # If so, then return a unicode object:
127 128 return unicode(obj)
128 129 # Else encode it... but how? There are many choices... :)
129 130 # Replace unprintables with escape codes?
130 131 #return unicode(obj).encode(sys.getdefaultencoding(), 'backslashreplace_errors')
131 132 # Replace unprintables with question marks?
132 133 #return unicode(obj).encode(sys.getdefaultencoding(), 'replace')
133 134 # ...
134 135 else:
135 136 _ustr = str
136 137 unichr = chr
137 138
138 139 if not _PY3K:
139 140 def _str2dict(strg):
140 141 return dict( [(c,0) for c in strg] )
141 142 else:
142 143 _str2dict = set
143 144
144 145 def _xml_escape(data):
145 146 """Escape &, <, >, ", ', etc. in a string of data."""
146 147
147 148 # ampersand must be replaced first
148 149 from_symbols = '&><"\''
149 150 to_symbols = ['&'+s+';' for s in "amp gt lt quot apos".split()]
150 151 for from_,to_ in zip(from_symbols, to_symbols):
151 152 data = data.replace(from_, to_)
152 153 return data
153 154
154 155 class _Constants(object):
155 156 pass
156 157
157 158 if not _PY3K:
158 159 alphas = string.lowercase + string.uppercase
159 160 else:
160 161 alphas = string.ascii_lowercase + string.ascii_uppercase
161 162 nums = string.digits
162 163 hexnums = nums + "ABCDEFabcdef"
163 164 alphanums = alphas + nums
164 165 _bslash = chr(92)
165 166 printables = "".join( [ c for c in string.printable if c not in string.whitespace ] )
166 167
167 168 class ParseBaseException(Exception):
168 169 """base exception class for all parsing runtime exceptions"""
169 170 # Performance tuning: we construct a *lot* of these, so keep this
170 171 # constructor as small and fast as possible
171 172 def __init__( self, pstr, loc=0, msg=None, elem=None ):
172 173 self.loc = loc
173 174 if msg is None:
174 175 self.msg = pstr
175 176 self.pstr = ""
176 177 else:
177 178 self.msg = msg
178 179 self.pstr = pstr
179 180 self.parserElement = elem
180 181
181 182 def __getattr__( self, aname ):
182 183 """supported attributes by name are:
183 184 - lineno - returns the line number of the exception text
184 185 - col - returns the column number of the exception text
185 186 - line - returns the line containing the exception text
186 187 """
187 188 if( aname == "lineno" ):
188 189 return lineno( self.loc, self.pstr )
189 190 elif( aname in ("col", "column") ):
190 191 return col( self.loc, self.pstr )
191 192 elif( aname == "line" ):
192 193 return line( self.loc, self.pstr )
193 194 else:
194 195 raise AttributeError(aname)
195 196
196 197 def __str__( self ):
197 198 return "%s (at char %d), (line:%d, col:%d)" % \
198 199 ( self.msg, self.loc, self.lineno, self.column )
199 200 def __repr__( self ):
200 201 return _ustr(self)
201 202 def markInputline( self, markerString = ">!<" ):
202 203 """Extracts the exception line from the input string, and marks
203 204 the location of the exception with a special symbol.
204 205 """
205 206 line_str = self.line
206 207 line_column = self.column - 1
207 208 if markerString:
208 209 line_str = "".join( [line_str[:line_column],
209 210 markerString, line_str[line_column:]])
210 211 return line_str.strip()
211 212 def __dir__(self):
212 213 return "loc msg pstr parserElement lineno col line " \
213 214 "markInputLine __str__ __repr__".split()
214 215
215 216 class ParseException(ParseBaseException):
216 217 """exception thrown when parse expressions don't match class;
217 218 supported attributes by name are:
218 219 - lineno - returns the line number of the exception text
219 220 - col - returns the column number of the exception text
220 221 - line - returns the line containing the exception text
221 222 """
222 223 pass
223 224
224 225 class ParseFatalException(ParseBaseException):
225 226 """user-throwable exception thrown when inconsistent parse content
226 227 is found; stops all parsing immediately"""
227 228 pass
228 229
229 230 class ParseSyntaxException(ParseFatalException):
230 231 """just like ParseFatalException, but thrown internally when an
231 232 ErrorStop indicates that parsing is to stop immediately because
232 233 an unbacktrackable syntax error has been found"""
233 234 def __init__(self, pe):
234 235 super(ParseSyntaxException, self).__init__(
235 236 pe.pstr, pe.loc, pe.msg, pe.parserElement)
236 237
237 238 #~ class ReparseException(ParseBaseException):
238 239 #~ """Experimental class - parse actions can raise this exception to cause
239 240 #~ pyparsing to reparse the input string:
240 241 #~ - with a modified input string, and/or
241 242 #~ - with a modified start location
242 243 #~ Set the values of the ReparseException in the constructor, and raise the
243 244 #~ exception in a parse action to cause pyparsing to use the new string/location.
244 245 #~ Setting the values as None causes no change to be made.
245 246 #~ """
246 247 #~ def __init_( self, newstring, restartLoc ):
247 248 #~ self.newParseText = newstring
248 249 #~ self.reparseLoc = restartLoc
249 250
250 251 class RecursiveGrammarException(Exception):
251 252 """exception thrown by validate() if the grammar could be improperly recursive"""
252 253 def __init__( self, parseElementList ):
253 254 self.parseElementTrace = parseElementList
254 255
255 256 def __str__( self ):
256 257 return "RecursiveGrammarException: %s" % self.parseElementTrace
257 258
258 259 class _ParseResultsWithOffset(object):
259 260 def __init__(self,p1,p2):
260 261 self.tup = (p1,p2)
261 262 def __getitem__(self,i):
262 263 return self.tup[i]
263 264 def __repr__(self):
264 265 return repr(self.tup)
265 266 def setOffset(self,i):
266 267 self.tup = (self.tup[0],i)
267 268
268 269 class ParseResults(object):
269 270 """Structured parse results, to provide multiple means of access to the parsed data:
270 271 - as a list (len(results))
271 272 - by list index (results[0], results[1], etc.)
272 273 - by attribute (results.<resultsName>)
273 274 """
274 275 __slots__ = ( "__toklist", "__tokdict", "__doinit", "__name", "__parent", "__accumNames", "__weakref__" )
275 276 def __new__(cls, toklist, name=None, asList=True, modal=True ):
276 277 if isinstance(toklist, cls):
277 278 return toklist
278 279 retobj = object.__new__(cls)
279 280 retobj.__doinit = True
280 281 return retobj
281 282
282 283 # Performance tuning: we construct a *lot* of these, so keep this
283 284 # constructor as small and fast as possible
284 285 def __init__( self, toklist, name=None, asList=True, modal=True ):
285 286 if self.__doinit:
286 287 self.__doinit = False
287 288 self.__name = None
288 289 self.__parent = None
289 290 self.__accumNames = {}
290 291 if isinstance(toklist, list):
291 292 self.__toklist = toklist[:]
292 293 else:
293 294 self.__toklist = [toklist]
294 295 self.__tokdict = dict()
295 296
296 297 if name:
297 298 if not modal:
298 299 self.__accumNames[name] = 0
299 300 if isinstance(name,int):
300 301 name = _ustr(name) # will always return a str, but use _ustr for consistency
301 302 self.__name = name
302 303 if not toklist in (None,'',[]):
303 304 if isinstance(toklist,basestring):
304 305 toklist = [ toklist ]
305 306 if asList:
306 307 if isinstance(toklist,ParseResults):
307 308 self[name] = _ParseResultsWithOffset(toklist.copy(),0)
308 309 else:
309 310 self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0)
310 311 self[name].__name = name
311 312 else:
312 313 try:
313 314 self[name] = toklist[0]
314 315 except (KeyError,TypeError,IndexError):
315 316 self[name] = toklist
316 317
317 318 def __getitem__( self, i ):
318 319 if isinstance( i, (int,slice) ):
319 320 return self.__toklist[i]
320 321 else:
321 322 if i not in self.__accumNames:
322 323 return self.__tokdict[i][-1][0]
323 324 else:
324 325 return ParseResults([ v[0] for v in self.__tokdict[i] ])
325 326
326 327 def __setitem__( self, k, v ):
327 328 if isinstance(v,_ParseResultsWithOffset):
328 329 self.__tokdict[k] = self.__tokdict.get(k,list()) + [v]
329 330 sub = v[0]
330 331 elif isinstance(k,int):
331 332 self.__toklist[k] = v
332 333 sub = v
333 334 else:
334 335 self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)]
335 336 sub = v
336 337 if isinstance(sub,ParseResults):
337 338 sub.__parent = wkref(self)
338 339
339 340 def __delitem__( self, i ):
340 341 if isinstance(i,(int,slice)):
341 342 mylen = len( self.__toklist )
342 343 del self.__toklist[i]
343 344
344 345 # convert int to slice
345 346 if isinstance(i, int):
346 347 if i < 0:
347 348 i += mylen
348 349 i = slice(i, i+1)
349 350 # get removed indices
350 351 removed = list(range(*i.indices(mylen)))
351 352 removed.reverse()
352 353 # fixup indices in token dictionary
353 354 for name in self.__tokdict:
354 355 occurrences = self.__tokdict[name]
355 356 for j in removed:
356 357 for k, (value, position) in enumerate(occurrences):
357 358 occurrences[k] = _ParseResultsWithOffset(value, position - (position > j))
358 359 else:
359 360 del self.__tokdict[i]
360 361
361 362 def __contains__( self, k ):
362 363 return k in self.__tokdict
363 364
364 365 def __len__( self ): return len( self.__toklist )
365 366 def __bool__(self): return len( self.__toklist ) > 0
366 367 __nonzero__ = __bool__
367 368 def __iter__( self ): return iter( self.__toklist )
368 369 def __reversed__( self ): return iter( reversed(self.__toklist) )
369 370 def keys( self ):
370 371 """Returns all named result keys."""
371 372 return self.__tokdict.keys()
372 373
373 374 def pop( self, index=-1 ):
374 375 """Removes and returns item at specified index (default=last).
375 376 Will work with either numeric indices or dict-key indicies."""
376 377 ret = self[index]
377 378 del self[index]
378 379 return ret
379 380
380 381 def get(self, key, defaultValue=None):
381 382 """Returns named result matching the given key, or if there is no
382 383 such name, then returns the given defaultValue or None if no
383 384 defaultValue is specified."""
384 385 if key in self:
385 386 return self[key]
386 387 else:
387 388 return defaultValue
388 389
389 390 def insert( self, index, insStr ):
390 391 self.__toklist.insert(index, insStr)
391 392 # fixup indices in token dictionary
392 393 for name in self.__tokdict:
393 394 occurrences = self.__tokdict[name]
394 395 for k, (value, position) in enumerate(occurrences):
395 396 occurrences[k] = _ParseResultsWithOffset(value, position + (position > index))
396 397
397 398 def items( self ):
398 399 """Returns all named result keys and values as a list of tuples."""
399 400 return [(k,self[k]) for k in self.__tokdict]
400 401
401 402 def values( self ):
402 403 """Returns all named result values."""
403 return [ v[-1][0] for v in self.__tokdict.values() ]
404 return [ v[-1][0] for v in self.__tokdict.itervalues() ]
404 405
405 406 def __getattr__( self, name ):
406 407 if name not in self.__slots__:
407 408 if name in self.__tokdict:
408 409 if name not in self.__accumNames:
409 410 return self.__tokdict[name][-1][0]
410 411 else:
411 412 return ParseResults([ v[0] for v in self.__tokdict[name] ])
412 413 else:
413 414 return ""
414 415 return None
415 416
416 417 def __add__( self, other ):
417 418 ret = self.copy()
418 419 ret += other
419 420 return ret
420 421
421 422 def __iadd__( self, other ):
422 423 if other.__tokdict:
423 424 offset = len(self.__toklist)
424 425 addoffset = ( lambda a: (a<0 and offset) or (a+offset) )
425 otheritems = other.__tokdict.items()
426 otheritems = other.__tokdict.iteritems()
426 427 otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) )
427 428 for (k,vlist) in otheritems for v in vlist]
428 429 for k,v in otherdictitems:
429 430 self[k] = v
430 431 if isinstance(v[0],ParseResults):
431 432 v[0].__parent = wkref(self)
432 433
433 434 self.__toklist += other.__toklist
434 435 self.__accumNames.update( other.__accumNames )
435 436 del other
436 437 return self
437 438
438 439 def __repr__( self ):
439 440 return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) )
440 441
441 442 def __str__( self ):
442 443 out = "["
443 444 sep = ""
444 445 for i in self.__toklist:
445 446 if isinstance(i, ParseResults):
446 447 out += sep + _ustr(i)
447 448 else:
448 449 out += sep + repr(i)
449 450 sep = ", "
450 451 out += "]"
451 452 return out
452 453
453 454 def _asStringList( self, sep='' ):
454 455 out = []
455 456 for item in self.__toklist:
456 457 if out and sep:
457 458 out.append(sep)
458 459 if isinstance( item, ParseResults ):
459 460 out += item._asStringList()
460 461 else:
461 462 out.append( _ustr(item) )
462 463 return out
463 464
464 465 def asList( self ):
465 466 """Returns the parse results as a nested list of matching tokens, all converted to strings."""
466 467 out = []
467 468 for res in self.__toklist:
468 469 if isinstance(res,ParseResults):
469 470 out.append( res.asList() )
470 471 else:
471 472 out.append( res )
472 473 return out
473 474
474 475 def asDict( self ):
475 476 """Returns the named parse results as dictionary."""
476 477 return dict( self.items() )
477 478
478 479 def copy( self ):
479 480 """Returns a new copy of a ParseResults object."""
480 481 ret = ParseResults( self.__toklist )
481 482 ret.__tokdict = self.__tokdict.copy()
482 483 ret.__parent = self.__parent
483 484 ret.__accumNames.update( self.__accumNames )
484 485 ret.__name = self.__name
485 486 return ret
486 487
487 488 def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ):
488 489 """Returns the parse results as XML. Tags are created for tokens and lists that have defined results names."""
489 490 nl = "\n"
490 491 out = []
491 namedItems = dict( [ (v[1],k) for (k,vlist) in self.__tokdict.items()
492 namedItems = dict([(v[1],k) for (k,vlist) in self.__tokdict.iteritems()
492 493 for v in vlist ] )
493 494 nextLevelIndent = indent + " "
494 495
495 496 # collapse out indents if formatting is not desired
496 497 if not formatted:
497 498 indent = ""
498 499 nextLevelIndent = ""
499 500 nl = ""
500 501
501 502 selfTag = None
502 503 if doctag is not None:
503 504 selfTag = doctag
504 505 else:
505 506 if self.__name:
506 507 selfTag = self.__name
507 508
508 509 if not selfTag:
509 510 if namedItemsOnly:
510 511 return ""
511 512 else:
512 513 selfTag = "ITEM"
513 514
514 515 out += [ nl, indent, "<", selfTag, ">" ]
515 516
516 517 worklist = self.__toklist
517 518 for i,res in enumerate(worklist):
518 519 if isinstance(res,ParseResults):
519 520 if i in namedItems:
520 521 out += [ res.asXML(namedItems[i],
521 522 namedItemsOnly and doctag is None,
522 523 nextLevelIndent,
523 524 formatted)]
524 525 else:
525 526 out += [ res.asXML(None,
526 527 namedItemsOnly and doctag is None,
527 528 nextLevelIndent,
528 529 formatted)]
529 530 else:
530 531 # individual token, see if there is a name for it
531 532 resTag = None
532 533 if i in namedItems:
533 534 resTag = namedItems[i]
534 535 if not resTag:
535 536 if namedItemsOnly:
536 537 continue
537 538 else:
538 539 resTag = "ITEM"
539 540 xmlBodyText = _xml_escape(_ustr(res))
540 541 out += [ nl, nextLevelIndent, "<", resTag, ">",
541 542 xmlBodyText,
542 543 "</", resTag, ">" ]
543 544
544 545 out += [ nl, indent, "</", selfTag, ">" ]
545 546 return "".join(out)
546 547
547 548 def __lookup(self,sub):
548 for k,vlist in self.__tokdict.items():
549 for k,vlist in self.__tokdict.iteritems():
549 550 for v,loc in vlist:
550 551 if sub is v:
551 552 return k
552 553 return None
553 554
554 555 def getName(self):
555 556 """Returns the results name for this token expression."""
556 557 if self.__name:
557 558 return self.__name
558 559 elif self.__parent:
559 560 par = self.__parent()
560 561 if par:
561 562 return par.__lookup(self)
562 563 else:
563 564 return None
564 565 elif (len(self) == 1 and
565 566 len(self.__tokdict) == 1 and
566 567 self.__tokdict.values()[0][0][1] in (0,-1)):
567 568 return self.__tokdict.keys()[0]
568 569 else:
569 570 return None
570 571
571 572 def dump(self,indent='',depth=0):
572 573 """Diagnostic method for listing out the contents of a ParseResults.
573 574 Accepts an optional indent argument so that this string can be embedded
574 575 in a nested display of other data."""
575 576 out = []
576 577 out.append( indent+_ustr(self.asList()) )
577 578 keys = self.items()
578 579 keys.sort()
579 580 for k,v in keys:
580 581 if out:
581 582 out.append('\n')
582 583 out.append( "%s%s- %s: " % (indent,(' '*depth), k) )
583 584 if isinstance(v,ParseResults):
584 585 if v.keys():
585 586 #~ out.append('\n')
586 587 out.append( v.dump(indent,depth+1) )
587 588 #~ out.append('\n')
588 589 else:
589 590 out.append(_ustr(v))
590 591 else:
591 592 out.append(_ustr(v))
592 593 #~ out.append('\n')
593 594 return "".join(out)
594 595
595 596 # add support for pickle protocol
596 597 def __getstate__(self):
597 598 return ( self.__toklist,
598 599 ( self.__tokdict.copy(),
599 600 self.__parent is not None and self.__parent() or None,
600 601 self.__accumNames,
601 602 self.__name ) )
602 603
603 604 def __setstate__(self,state):
604 605 self.__toklist = state[0]
605 606 self.__tokdict, \
606 607 par, \
607 608 inAccumNames, \
608 609 self.__name = state[1]
609 610 self.__accumNames = {}
610 611 self.__accumNames.update(inAccumNames)
611 612 if par is not None:
612 613 self.__parent = wkref(par)
613 614 else:
614 615 self.__parent = None
615 616
616 617 def __dir__(self):
617 618 return dir(super(ParseResults,self)) + self.keys()
618 619
619 620 def col (loc,strg):
620 621 """Returns current column within a string, counting newlines as line separators.
621 622 The first column is number 1.
622 623
623 624 Note: the default parsing behavior is to expand tabs in the input string
624 625 before starting the parsing process. See L{I{ParserElement.parseString}<ParserElement.parseString>} for more information
625 626 on parsing strings containing <TAB>s, and suggested methods to maintain a
626 627 consistent view of the parsed string, the parse location, and line and column
627 628 positions within the parsed string.
628 629 """
629 630 return (loc<len(strg) and strg[loc] == '\n') and 1 or loc - strg.rfind("\n", 0, loc)
630 631
631 632 def lineno(loc,strg):
632 633 """Returns current line number within a string, counting newlines as line separators.
633 634 The first line is number 1.
634 635
635 636 Note: the default parsing behavior is to expand tabs in the input string
636 637 before starting the parsing process. See L{I{ParserElement.parseString}<ParserElement.parseString>} for more information
637 638 on parsing strings containing <TAB>s, and suggested methods to maintain a
638 639 consistent view of the parsed string, the parse location, and line and column
639 640 positions within the parsed string.
640 641 """
641 642 return strg.count("\n",0,loc) + 1
642 643
643 644 def line( loc, strg ):
644 645 """Returns the line of text containing loc within a string, counting newlines as line separators.
645 646 """
646 647 lastCR = strg.rfind("\n", 0, loc)
647 648 nextCR = strg.find("\n", loc)
648 649 if nextCR > 0:
649 650 return strg[lastCR+1:nextCR]
650 651 else:
651 652 return strg[lastCR+1:]
652 653
653 654 def _defaultStartDebugAction( instring, loc, expr ):
654 655 print ("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))
655 656
656 657 def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ):
657 658 print ("Matched " + _ustr(expr) + " -> " + str(toks.asList()))
658 659
659 660 def _defaultExceptionDebugAction( instring, loc, expr, exc ):
660 661 print ("Exception raised:" + _ustr(exc))
661 662
662 663 def nullDebugAction(*args):
663 664 """'Do-nothing' debug action, to suppress debugging output during parsing."""
664 665 pass
665 666
666 667 class ParserElement(object):
667 668 """Abstract base level parser element class."""
668 669 DEFAULT_WHITE_CHARS = " \n\t\r"
669 670
670 671 def setDefaultWhitespaceChars( chars ):
671 672 """Overrides the default whitespace chars
672 673 """
673 674 ParserElement.DEFAULT_WHITE_CHARS = chars
674 675 setDefaultWhitespaceChars = staticmethod(setDefaultWhitespaceChars)
675 676
676 677 def __init__( self, savelist=False ):
677 678 self.parseAction = list()
678 679 self.failAction = None
679 680 #~ self.name = "<unknown>" # don't define self.name, let subclasses try/except upcall
680 681 self.strRepr = None
681 682 self.resultsName = None
682 683 self.saveAsList = savelist
683 684 self.skipWhitespace = True
684 685 self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
685 686 self.copyDefaultWhiteChars = True
686 687 self.mayReturnEmpty = False # used when checking for left-recursion
687 688 self.keepTabs = False
688 689 self.ignoreExprs = list()
689 690 self.debug = False
690 691 self.streamlined = False
691 692 self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index
692 693 self.errmsg = ""
693 694 self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all)
694 695 self.debugActions = ( None, None, None ) #custom debug actions
695 696 self.re = None
696 697 self.callPreparse = True # used to avoid redundant calls to preParse
697 698 self.callDuringTry = False
698 699
699 700 def copy( self ):
700 701 """Make a copy of this ParserElement. Useful for defining different parse actions
701 702 for the same parsing pattern, using copies of the original parse element."""
702 703 cpy = copy.copy( self )
703 704 cpy.parseAction = self.parseAction[:]
704 705 cpy.ignoreExprs = self.ignoreExprs[:]
705 706 if self.copyDefaultWhiteChars:
706 707 cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
707 708 return cpy
708 709
709 710 def setName( self, name ):
710 711 """Define name for this expression, for use in debugging."""
711 712 self.name = name
712 713 self.errmsg = "Expected " + self.name
713 714 if hasattr(self,"exception"):
714 715 self.exception.msg = self.errmsg
715 716 return self
716 717
717 718 def setResultsName( self, name, listAllMatches=False ):
718 719 """Define name for referencing matching tokens as a nested attribute
719 720 of the returned parse results.
720 721 NOTE: this returns a *copy* of the original ParserElement object;
721 722 this is so that the client can define a basic element, such as an
722 723 integer, and reference it in multiple places with different names.
723 724 """
724 725 newself = self.copy()
725 726 newself.resultsName = name
726 727 newself.modalResults = not listAllMatches
727 728 return newself
728 729
729 730 def setBreak(self,breakFlag = True):
730 731 """Method to invoke the Python pdb debugger when this element is
731 732 about to be parsed. Set breakFlag to True to enable, False to
732 733 disable.
733 734 """
734 735 if breakFlag:
735 736 _parseMethod = self._parse
736 737 def breaker(instring, loc, doActions=True, callPreParse=True):
737 738 import pdb
738 739 pdb.set_trace()
739 740 return _parseMethod( instring, loc, doActions, callPreParse )
740 741 breaker._originalParseMethod = _parseMethod
741 742 self._parse = breaker
742 743 else:
743 744 if hasattr(self._parse,"_originalParseMethod"):
744 745 self._parse = self._parse._originalParseMethod
745 746 return self
746 747
747 748 def _normalizeParseActionArgs( f ):
748 749 """Internal method used to decorate parse actions that take fewer than 3 arguments,
749 750 so that all parse actions can be called as f(s,l,t)."""
750 751 STAR_ARGS = 4
751 752
752 753 try:
753 754 restore = None
754 755 if isinstance(f,type):
755 756 restore = f
756 757 f = f.__init__
757 758 if not _PY3K:
758 759 codeObj = f.func_code
759 760 else:
760 761 codeObj = f.code
761 762 if codeObj.co_flags & STAR_ARGS:
762 763 return f
763 764 numargs = codeObj.co_argcount
764 765 if not _PY3K:
765 766 if hasattr(f,"im_self"):
766 767 numargs -= 1
767 768 else:
768 769 if hasattr(f,"__self__"):
769 770 numargs -= 1
770 771 if restore:
771 772 f = restore
772 773 except AttributeError:
773 774 try:
774 775 if not _PY3K:
775 776 call_im_func_code = f.__call__.im_func.func_code
776 777 else:
777 778 call_im_func_code = f.__code__
778 779
779 780 # not a function, must be a callable object, get info from the
780 781 # im_func binding of its bound __call__ method
781 782 if call_im_func_code.co_flags & STAR_ARGS:
782 783 return f
783 784 numargs = call_im_func_code.co_argcount
784 785 if not _PY3K:
785 786 if hasattr(f.__call__,"im_self"):
786 787 numargs -= 1
787 788 else:
788 789 if hasattr(f.__call__,"__self__"):
789 790 numargs -= 0
790 791 except AttributeError:
791 792 if not _PY3K:
792 793 call_func_code = f.__call__.func_code
793 794 else:
794 795 call_func_code = f.__call__.__code__
795 796 # not a bound method, get info directly from __call__ method
796 797 if call_func_code.co_flags & STAR_ARGS:
797 798 return f
798 799 numargs = call_func_code.co_argcount
799 800 if not _PY3K:
800 801 if hasattr(f.__call__,"im_self"):
801 802 numargs -= 1
802 803 else:
803 804 if hasattr(f.__call__,"__self__"):
804 805 numargs -= 1
805 806
806 807
807 808 #~ print ("adding function %s with %d args" % (f.func_name,numargs))
808 809 if numargs == 3:
809 810 return f
810 811 else:
811 812 if numargs > 3:
812 813 def tmp(s,l,t):
813 814 return f(f.__call__.__self__, s,l,t)
814 815 if numargs == 2:
815 816 def tmp(s,l,t):
816 817 return f(l,t)
817 818 elif numargs == 1:
818 819 def tmp(s,l,t):
819 820 return f(t)
820 821 else: #~ numargs == 0:
821 822 def tmp(s,l,t):
822 823 return f()
823 824 try:
824 825 tmp.__name__ = f.__name__
825 826 except (AttributeError,TypeError):
826 827 # no need for special handling if attribute doesnt exist
827 828 pass
828 829 try:
829 830 tmp.__doc__ = f.__doc__
830 831 except (AttributeError,TypeError):
831 832 # no need for special handling if attribute doesnt exist
832 833 pass
833 834 try:
834 835 tmp.__dict__.update(f.__dict__)
835 836 except (AttributeError,TypeError):
836 837 # no need for special handling if attribute doesnt exist
837 838 pass
838 839 return tmp
839 840 _normalizeParseActionArgs = staticmethod(_normalizeParseActionArgs)
840 841
841 842 def setParseAction( self, *fns, **kwargs ):
842 843 """Define action to perform when successfully matching parse element definition.
843 844 Parse action fn is a callable method with 0-3 arguments, called as fn(s,loc,toks),
844 845 fn(loc,toks), fn(toks), or just fn(), where:
845 846 - s = the original string being parsed (see note below)
846 847 - loc = the location of the matching substring
847 848 - toks = a list of the matched tokens, packaged as a ParseResults object
848 849 If the functions in fns modify the tokens, they can return them as the return
849 850 value from fn, and the modified list of tokens will replace the original.
850 851 Otherwise, fn does not need to return any value.
851 852
852 853 Note: the default parsing behavior is to expand tabs in the input string
853 854 before starting the parsing process. See L{I{parseString}<parseString>} for more information
854 855 on parsing strings containing <TAB>s, and suggested methods to maintain a
855 856 consistent view of the parsed string, the parse location, and line and column
856 857 positions within the parsed string.
857 858 """
858 859 self.parseAction = list(map(self._normalizeParseActionArgs, list(fns)))
859 860 self.callDuringTry = ("callDuringTry" in kwargs and kwargs["callDuringTry"])
860 861 return self
861 862
862 863 def addParseAction( self, *fns, **kwargs ):
863 864 """Add parse action to expression's list of parse actions. See L{I{setParseAction}<setParseAction>}."""
864 865 self.parseAction += list(map(self._normalizeParseActionArgs, list(fns)))
865 866 self.callDuringTry = self.callDuringTry or ("callDuringTry" in kwargs and kwargs["callDuringTry"])
866 867 return self
867 868
868 869 def setFailAction( self, fn ):
869 870 """Define action to perform if parsing fails at this expression.
870 871 Fail acton fn is a callable function that takes the arguments
871 872 fn(s,loc,expr,err) where:
872 873 - s = string being parsed
873 874 - loc = location where expression match was attempted and failed
874 875 - expr = the parse expression that failed
875 876 - err = the exception thrown
876 877 The function returns no value. It may throw ParseFatalException
877 878 if it is desired to stop parsing immediately."""
878 879 self.failAction = fn
879 880 return self
880 881
881 882 def _skipIgnorables( self, instring, loc ):
882 883 exprsFound = True
883 884 while exprsFound:
884 885 exprsFound = False
885 886 for e in self.ignoreExprs:
886 887 try:
887 888 while 1:
888 889 loc,dummy = e._parse( instring, loc )
889 890 exprsFound = True
890 891 except ParseException:
891 892 pass
892 893 return loc
893 894
894 895 def preParse( self, instring, loc ):
895 896 if self.ignoreExprs:
896 897 loc = self._skipIgnorables( instring, loc )
897 898
898 899 if self.skipWhitespace:
899 900 wt = self.whiteChars
900 901 instrlen = len(instring)
901 902 while loc < instrlen and instring[loc] in wt:
902 903 loc += 1
903 904
904 905 return loc
905 906
906 907 def parseImpl( self, instring, loc, doActions=True ):
907 908 return loc, []
908 909
909 910 def postParse( self, instring, loc, tokenlist ):
910 911 return tokenlist
911 912
912 913 #~ @profile
913 914 def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ):
914 915 debugging = ( self.debug ) #and doActions )
915 916
916 917 if debugging or self.failAction:
917 918 #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))
918 919 if (self.debugActions[0] ):
919 920 self.debugActions[0]( instring, loc, self )
920 921 if callPreParse and self.callPreparse:
921 922 preloc = self.preParse( instring, loc )
922 923 else:
923 924 preloc = loc
924 925 tokensStart = loc
925 926 try:
926 927 try:
927 928 loc,tokens = self.parseImpl( instring, preloc, doActions )
928 929 except IndexError:
929 930 raise ParseException( instring, len(instring), self.errmsg, self )
930 931 except ParseBaseException, err:
931 932 #~ print ("Exception raised:", err)
932 933 if self.debugActions[2]:
933 934 self.debugActions[2]( instring, tokensStart, self, err )
934 935 if self.failAction:
935 936 self.failAction( instring, tokensStart, self, err )
936 937 raise
937 938 else:
938 939 if callPreParse and self.callPreparse:
939 940 preloc = self.preParse( instring, loc )
940 941 else:
941 942 preloc = loc
942 943 tokensStart = loc
943 944 if self.mayIndexError or loc >= len(instring):
944 945 try:
945 946 loc,tokens = self.parseImpl( instring, preloc, doActions )
946 947 except IndexError:
947 948 raise ParseException( instring, len(instring), self.errmsg, self )
948 949 else:
949 950 loc,tokens = self.parseImpl( instring, preloc, doActions )
950 951
951 952 tokens = self.postParse( instring, loc, tokens )
952 953
953 954 retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults )
954 955 if self.parseAction and (doActions or self.callDuringTry):
955 956 if debugging:
956 957 try:
957 958 for fn in self.parseAction:
958 959 tokens = fn( instring, tokensStart, retTokens )
959 960 if tokens is not None:
960 961 retTokens = ParseResults( tokens,
961 962 self.resultsName,
962 963 asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
963 964 modal=self.modalResults )
964 965 except ParseBaseException, err:
965 966 #~ print "Exception raised in user parse action:", err
966 967 if (self.debugActions[2] ):
967 968 self.debugActions[2]( instring, tokensStart, self, err )
968 969 raise
969 970 else:
970 971 for fn in self.parseAction:
971 972 tokens = fn( instring, tokensStart, retTokens )
972 973 if tokens is not None:
973 974 retTokens = ParseResults( tokens,
974 975 self.resultsName,
975 976 asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
976 977 modal=self.modalResults )
977 978
978 979 if debugging:
979 980 #~ print ("Matched",self,"->",retTokens.asList())
980 981 if (self.debugActions[1] ):
981 982 self.debugActions[1]( instring, tokensStart, loc, self, retTokens )
982 983
983 984 return loc, retTokens
984 985
985 986 def tryParse( self, instring, loc ):
986 987 try:
987 988 return self._parse( instring, loc, doActions=False )[0]
988 989 except ParseFatalException:
989 990 raise ParseException( instring, loc, self.errmsg, self)
990 991
991 992 # this method gets repeatedly called during backtracking with the same arguments -
992 993 # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression
993 994 def _parseCache( self, instring, loc, doActions=True, callPreParse=True ):
994 995 lookup = (self,instring,loc,callPreParse,doActions)
995 996 if lookup in ParserElement._exprArgCache:
996 997 value = ParserElement._exprArgCache[ lookup ]
997 998 if isinstance(value,Exception):
998 999 raise value
999 1000 return value
1000 1001 else:
1001 1002 try:
1002 1003 value = self._parseNoCache( instring, loc, doActions, callPreParse )
1003 1004 ParserElement._exprArgCache[ lookup ] = (value[0],value[1].copy())
1004 1005 return value
1005 1006 except ParseBaseException, pe:
1006 1007 ParserElement._exprArgCache[ lookup ] = pe
1007 1008 raise
1008 1009
1009 1010 _parse = _parseNoCache
1010 1011
1011 1012 # argument cache for optimizing repeated calls when backtracking through recursive expressions
1012 1013 _exprArgCache = {}
1013 1014 def resetCache():
1014 1015 ParserElement._exprArgCache.clear()
1015 1016 resetCache = staticmethod(resetCache)
1016 1017
1017 1018 _packratEnabled = False
1018 1019 def enablePackrat():
1019 1020 """Enables "packrat" parsing, which adds memoizing to the parsing logic.
1020 1021 Repeated parse attempts at the same string location (which happens
1021 1022 often in many complex grammars) can immediately return a cached value,
1022 1023 instead of re-executing parsing/validating code. Memoizing is done of
1023 1024 both valid results and parsing exceptions.
1024 1025
1025 1026 This speedup may break existing programs that use parse actions that
1026 1027 have side-effects. For this reason, packrat parsing is disabled when
1027 1028 you first import pyparsing. To activate the packrat feature, your
1028 1029 program must call the class method ParserElement.enablePackrat(). If
1029 1030 your program uses psyco to "compile as you go", you must call
1030 1031 enablePackrat before calling psyco.full(). If you do not do this,
1031 1032 Python will crash. For best results, call enablePackrat() immediately
1032 1033 after importing pyparsing.
1033 1034 """
1034 1035 if not ParserElement._packratEnabled:
1035 1036 ParserElement._packratEnabled = True
1036 1037 ParserElement._parse = ParserElement._parseCache
1037 1038 enablePackrat = staticmethod(enablePackrat)
1038 1039
1039 1040 def parseString( self, instring, parseAll=False ):
1040 1041 """Execute the parse expression with the given string.
1041 1042 This is the main interface to the client code, once the complete
1042 1043 expression has been built.
1043 1044
1044 1045 If you want the grammar to require that the entire input string be
1045 1046 successfully parsed, then set parseAll to True (equivalent to ending
1046 1047 the grammar with StringEnd()).
1047 1048
1048 1049 Note: parseString implicitly calls expandtabs() on the input string,
1049 1050 in order to report proper column numbers in parse actions.
1050 1051 If the input string contains tabs and
1051 1052 the grammar uses parse actions that use the loc argument to index into the
1052 1053 string being parsed, you can ensure you have a consistent view of the input
1053 1054 string by:
1054 1055 - calling parseWithTabs on your grammar before calling parseString
1055 1056 (see L{I{parseWithTabs}<parseWithTabs>})
1056 1057 - define your parse action using the full (s,loc,toks) signature, and
1057 1058 reference the input string using the parse action's s argument
1058 1059 - explictly expand the tabs in your input string before calling
1059 1060 parseString
1060 1061 """
1061 1062 ParserElement.resetCache()
1062 1063 if not self.streamlined:
1063 1064 self.streamline()
1064 1065 #~ self.saveAsList = True
1065 1066 for e in self.ignoreExprs:
1066 1067 e.streamline()
1067 1068 if not self.keepTabs:
1068 1069 instring = instring.expandtabs()
1069 1070 try:
1070 1071 loc, tokens = self._parse( instring, 0 )
1071 1072 if parseAll:
1072 1073 loc = self.preParse( instring, loc )
1073 1074 StringEnd()._parse( instring, loc )
1074 1075 except ParseBaseException, exc:
1075 1076 # catch and re-raise exception from here, clears out pyparsing internal stack trace
1076 1077 raise exc
1077 1078 else:
1078 1079 return tokens
1079 1080
1080 1081 def scanString( self, instring, maxMatches=_MAX_INT ):
1081 1082 """Scan the input string for expression matches. Each match will return the
1082 1083 matching tokens, start location, and end location. May be called with optional
1083 1084 maxMatches argument, to clip scanning after 'n' matches are found.
1084 1085
1085 1086 Note that the start and end locations are reported relative to the string
1086 1087 being parsed. See L{I{parseString}<parseString>} for more information on parsing
1087 1088 strings with embedded tabs."""
1088 1089 if not self.streamlined:
1089 1090 self.streamline()
1090 1091 for e in self.ignoreExprs:
1091 1092 e.streamline()
1092 1093
1093 1094 if not self.keepTabs:
1094 1095 instring = _ustr(instring).expandtabs()
1095 1096 instrlen = len(instring)
1096 1097 loc = 0
1097 1098 preparseFn = self.preParse
1098 1099 parseFn = self._parse
1099 1100 ParserElement.resetCache()
1100 1101 matches = 0
1101 1102 try:
1102 1103 while loc <= instrlen and matches < maxMatches:
1103 1104 try:
1104 1105 preloc = preparseFn( instring, loc )
1105 1106 nextLoc,tokens = parseFn( instring, preloc, callPreParse=False )
1106 1107 except ParseException:
1107 1108 loc = preloc+1
1108 1109 else:
1109 1110 matches += 1
1110 1111 yield tokens, preloc, nextLoc
1111 1112 loc = nextLoc
1112 1113 except ParseBaseException, pe:
1113 1114 raise pe
1114 1115
1115 1116 def transformString( self, instring ):
1116 1117 """Extension to scanString, to modify matching text with modified tokens that may
1117 1118 be returned from a parse action. To use transformString, define a grammar and
1118 1119 attach a parse action to it that modifies the returned token list.
1119 1120 Invoking transformString() on a target string will then scan for matches,
1120 1121 and replace the matched text patterns according to the logic in the parse
1121 1122 action. transformString() returns the resulting transformed string."""
1122 1123 out = []
1123 1124 lastE = 0
1124 1125 # force preservation of <TAB>s, to minimize unwanted transformation of string, and to
1125 1126 # keep string locs straight between transformString and scanString
1126 1127 self.keepTabs = True
1127 1128 try:
1128 1129 for t,s,e in self.scanString( instring ):
1129 1130 out.append( instring[lastE:s] )
1130 1131 if t:
1131 1132 if isinstance(t,ParseResults):
1132 1133 out += t.asList()
1133 1134 elif isinstance(t,list):
1134 1135 out += t
1135 1136 else:
1136 1137 out.append(t)
1137 1138 lastE = e
1138 1139 out.append(instring[lastE:])
1139 1140 return "".join(map(_ustr,out))
1140 1141 except ParseBaseException, pe:
1141 1142 raise pe
1142 1143
1143 1144 def searchString( self, instring, maxMatches=_MAX_INT ):
1144 1145 """Another extension to scanString, simplifying the access to the tokens found
1145 1146 to match the given parse expression. May be called with optional
1146 1147 maxMatches argument, to clip searching after 'n' matches are found.
1147 1148 """
1148 1149 try:
1149 1150 return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ])
1150 1151 except ParseBaseException, pe:
1151 1152 raise pe
1152 1153
1153 1154 def __add__(self, other ):
1154 1155 """Implementation of + operator - returns And"""
1155 1156 if isinstance( other, basestring ):
1156 1157 other = Literal( other )
1157 1158 if not isinstance( other, ParserElement ):
1158 1159 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
1159 1160 SyntaxWarning, stacklevel=2)
1160 1161 return None
1161 1162 return And( [ self, other ] )
1162 1163
1163 1164 def __radd__(self, other ):
1164 1165 """Implementation of + operator when left operand is not a ParserElement"""
1165 1166 if isinstance( other, basestring ):
1166 1167 other = Literal( other )
1167 1168 if not isinstance( other, ParserElement ):
1168 1169 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
1169 1170 SyntaxWarning, stacklevel=2)
1170 1171 return None
1171 1172 return other + self
1172 1173
1173 1174 def __sub__(self, other):
1174 1175 """Implementation of - operator, returns And with error stop"""
1175 1176 if isinstance( other, basestring ):
1176 1177 other = Literal( other )
1177 1178 if not isinstance( other, ParserElement ):
1178 1179 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
1179 1180 SyntaxWarning, stacklevel=2)
1180 1181 return None
1181 1182 return And( [ self, And._ErrorStop(), other ] )
1182 1183
1183 1184 def __rsub__(self, other ):
1184 1185 """Implementation of - operator when left operand is not a ParserElement"""
1185 1186 if isinstance( other, basestring ):
1186 1187 other = Literal( other )
1187 1188 if not isinstance( other, ParserElement ):
1188 1189 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
1189 1190 SyntaxWarning, stacklevel=2)
1190 1191 return None
1191 1192 return other - self
1192 1193
1193 1194 def __mul__(self,other):
1194 1195 if isinstance(other,int):
1195 1196 minElements, optElements = other,0
1196 1197 elif isinstance(other,tuple):
1197 1198 other = (other + (None, None))[:2]
1198 1199 if other[0] is None:
1199 1200 other = (0, other[1])
1200 1201 if isinstance(other[0],int) and other[1] is None:
1201 1202 if other[0] == 0:
1202 1203 return ZeroOrMore(self)
1203 1204 if other[0] == 1:
1204 1205 return OneOrMore(self)
1205 1206 else:
1206 1207 return self*other[0] + ZeroOrMore(self)
1207 1208 elif isinstance(other[0],int) and isinstance(other[1],int):
1208 1209 minElements, optElements = other
1209 1210 optElements -= minElements
1210 1211 else:
1211 1212 raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1]))
1212 1213 else:
1213 1214 raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other))
1214 1215
1215 1216 if minElements < 0:
1216 1217 raise ValueError("cannot multiply ParserElement by negative value")
1217 1218 if optElements < 0:
1218 1219 raise ValueError("second tuple value must be greater or equal to first tuple value")
1219 1220 if minElements == optElements == 0:
1220 1221 raise ValueError("cannot multiply ParserElement by 0 or (0,0)")
1221 1222
1222 1223 if (optElements):
1223 1224 def makeOptionalList(n):
1224 1225 if n>1:
1225 1226 return Optional(self + makeOptionalList(n-1))
1226 1227 else:
1227 1228 return Optional(self)
1228 1229 if minElements:
1229 1230 if minElements == 1:
1230 1231 ret = self + makeOptionalList(optElements)
1231 1232 else:
1232 1233 ret = And([self]*minElements) + makeOptionalList(optElements)
1233 1234 else:
1234 1235 ret = makeOptionalList(optElements)
1235 1236 else:
1236 1237 if minElements == 1:
1237 1238 ret = self
1238 1239 else:
1239 1240 ret = And([self]*minElements)
1240 1241 return ret
1241 1242
1242 1243 def __rmul__(self, other):
1243 1244 return self.__mul__(other)
1244 1245
1245 1246 def __or__(self, other ):
1246 1247 """Implementation of | operator - returns MatchFirst"""
1247 1248 if isinstance( other, basestring ):
1248 1249 other = Literal( other )
1249 1250 if not isinstance( other, ParserElement ):
1250 1251 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
1251 1252 SyntaxWarning, stacklevel=2)
1252 1253 return None
1253 1254 return MatchFirst( [ self, other ] )
1254 1255
1255 1256 def __ror__(self, other ):
1256 1257 """Implementation of | operator when left operand is not a ParserElement"""
1257 1258 if isinstance( other, basestring ):
1258 1259 other = Literal( other )
1259 1260 if not isinstance( other, ParserElement ):
1260 1261 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
1261 1262 SyntaxWarning, stacklevel=2)
1262 1263 return None
1263 1264 return other | self
1264 1265
1265 1266 def __xor__(self, other ):
1266 1267 """Implementation of ^ operator - returns Or"""
1267 1268 if isinstance( other, basestring ):
1268 1269 other = Literal( other )
1269 1270 if not isinstance( other, ParserElement ):
1270 1271 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
1271 1272 SyntaxWarning, stacklevel=2)
1272 1273 return None
1273 1274 return Or( [ self, other ] )
1274 1275
1275 1276 def __rxor__(self, other ):
1276 1277 """Implementation of ^ operator when left operand is not a ParserElement"""
1277 1278 if isinstance( other, basestring ):
1278 1279 other = Literal( other )
1279 1280 if not isinstance( other, ParserElement ):
1280 1281 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
1281 1282 SyntaxWarning, stacklevel=2)
1282 1283 return None
1283 1284 return other ^ self
1284 1285
1285 1286 def __and__(self, other ):
1286 1287 """Implementation of & operator - returns Each"""
1287 1288 if isinstance( other, basestring ):
1288 1289 other = Literal( other )
1289 1290 if not isinstance( other, ParserElement ):
1290 1291 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
1291 1292 SyntaxWarning, stacklevel=2)
1292 1293 return None
1293 1294 return Each( [ self, other ] )
1294 1295
1295 1296 def __rand__(self, other ):
1296 1297 """Implementation of & operator when left operand is not a ParserElement"""
1297 1298 if isinstance( other, basestring ):
1298 1299 other = Literal( other )
1299 1300 if not isinstance( other, ParserElement ):
1300 1301 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
1301 1302 SyntaxWarning, stacklevel=2)
1302 1303 return None
1303 1304 return other & self
1304 1305
1305 1306 def __invert__( self ):
1306 1307 """Implementation of ~ operator - returns NotAny"""
1307 1308 return NotAny( self )
1308 1309
1309 1310 def __call__(self, name):
1310 1311 """Shortcut for setResultsName, with listAllMatches=default::
1311 1312 userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno")
1312 1313 could be written as::
1313 1314 userdata = Word(alphas)("name") + Word(nums+"-")("socsecno")
1314 1315 """
1315 1316 return self.setResultsName(name)
1316 1317
1317 1318 def suppress( self ):
1318 1319 """Suppresses the output of this ParserElement; useful to keep punctuation from
1319 1320 cluttering up returned output.
1320 1321 """
1321 1322 return Suppress( self )
1322 1323
1323 1324 def leaveWhitespace( self ):
1324 1325 """Disables the skipping of whitespace before matching the characters in the
1325 1326 ParserElement's defined pattern. This is normally only used internally by
1326 1327 the pyparsing module, but may be needed in some whitespace-sensitive grammars.
1327 1328 """
1328 1329 self.skipWhitespace = False
1329 1330 return self
1330 1331
1331 1332 def setWhitespaceChars( self, chars ):
1332 1333 """Overrides the default whitespace chars
1333 1334 """
1334 1335 self.skipWhitespace = True
1335 1336 self.whiteChars = chars
1336 1337 self.copyDefaultWhiteChars = False
1337 1338 return self
1338 1339
1339 1340 def parseWithTabs( self ):
1340 1341 """Overrides default behavior to expand <TAB>s to spaces before parsing the input string.
1341 1342 Must be called before parseString when the input grammar contains elements that
1342 1343 match <TAB> characters."""
1343 1344 self.keepTabs = True
1344 1345 return self
1345 1346
1346 1347 def ignore( self, other ):
1347 1348 """Define expression to be ignored (e.g., comments) while doing pattern
1348 1349 matching; may be called repeatedly, to define multiple comment or other
1349 1350 ignorable patterns.
1350 1351 """
1351 1352 if isinstance( other, Suppress ):
1352 1353 if other not in self.ignoreExprs:
1353 1354 self.ignoreExprs.append( other )
1354 1355 else:
1355 1356 self.ignoreExprs.append( Suppress( other ) )
1356 1357 return self
1357 1358
1358 1359 def setDebugActions( self, startAction, successAction, exceptionAction ):
1359 1360 """Enable display of debugging messages while doing pattern matching."""
1360 1361 self.debugActions = (startAction or _defaultStartDebugAction,
1361 1362 successAction or _defaultSuccessDebugAction,
1362 1363 exceptionAction or _defaultExceptionDebugAction)
1363 1364 self.debug = True
1364 1365 return self
1365 1366
1366 1367 def setDebug( self, flag=True ):
1367 1368 """Enable display of debugging messages while doing pattern matching.
1368 1369 Set flag to True to enable, False to disable."""
1369 1370 if flag:
1370 1371 self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction )
1371 1372 else:
1372 1373 self.debug = False
1373 1374 return self
1374 1375
1375 1376 def __str__( self ):
1376 1377 return self.name
1377 1378
1378 1379 def __repr__( self ):
1379 1380 return _ustr(self)
1380 1381
1381 1382 def streamline( self ):
1382 1383 self.streamlined = True
1383 1384 self.strRepr = None
1384 1385 return self
1385 1386
1386 1387 def checkRecursion( self, parseElementList ):
1387 1388 pass
1388 1389
1389 1390 def validate( self, validateTrace=[] ):
1390 1391 """Check defined expressions for valid structure, check for infinite recursive definitions."""
1391 1392 self.checkRecursion( [] )
1392 1393
1393 1394 def parseFile( self, file_or_filename, parseAll=False ):
1394 1395 """Execute the parse expression on the given file or filename.
1395 1396 If a filename is specified (instead of a file object),
1396 1397 the entire file is opened, read, and closed before parsing.
1397 1398 """
1398 1399 try:
1399 1400 file_contents = file_or_filename.read()
1400 1401 except AttributeError:
1401 1402 f = open(file_or_filename, "rb")
1402 1403 file_contents = f.read()
1403 1404 f.close()
1404 1405 try:
1405 1406 return self.parseString(file_contents, parseAll)
1406 1407 except ParseBaseException, exc:
1407 1408 # catch and re-raise exception from here, clears out pyparsing internal stack trace
1408 1409 raise exc
1409 1410
1410 1411 def getException(self):
1411 1412 return ParseException("",0,self.errmsg,self)
1412 1413
1413 1414 def __getattr__(self,aname):
1414 1415 if aname == "myException":
1415 1416 self.myException = ret = self.getException();
1416 1417 return ret;
1417 1418 else:
1418 1419 raise AttributeError("no such attribute " + aname)
1419 1420
1420 1421 def __eq__(self,other):
1421 1422 if isinstance(other, ParserElement):
1422 1423 return self is other or self.__dict__ == other.__dict__
1423 1424 elif isinstance(other, basestring):
1424 1425 try:
1425 1426 self.parseString(_ustr(other), parseAll=True)
1426 1427 return True
1427 1428 except ParseBaseException:
1428 1429 return False
1429 1430 else:
1430 1431 return super(ParserElement,self)==other
1431 1432
1432 1433 def __ne__(self,other):
1433 1434 return not (self == other)
1434 1435
1435 1436 def __hash__(self):
1436 1437 return hash(id(self))
1437 1438
1438 1439 def __req__(self,other):
1439 1440 return self == other
1440 1441
1441 1442 def __rne__(self,other):
1442 1443 return not (self == other)
1443 1444
1444 1445
1445 1446 class Token(ParserElement):
1446 1447 """Abstract ParserElement subclass, for defining atomic matching patterns."""
1447 1448 def __init__( self ):
1448 1449 super(Token,self).__init__( savelist=False )
1449 1450 #self.myException = ParseException("",0,"",self)
1450 1451
1451 1452 def setName(self, name):
1452 1453 s = super(Token,self).setName(name)
1453 1454 self.errmsg = "Expected " + self.name
1454 1455 #s.myException.msg = self.errmsg
1455 1456 return s
1456 1457
1457 1458
1458 1459 class Empty(Token):
1459 1460 """An empty token, will always match."""
1460 1461 def __init__( self ):
1461 1462 super(Empty,self).__init__()
1462 1463 self.name = "Empty"
1463 1464 self.mayReturnEmpty = True
1464 1465 self.mayIndexError = False
1465 1466
1466 1467
1467 1468 class NoMatch(Token):
1468 1469 """A token that will never match."""
1469 1470 def __init__( self ):
1470 1471 super(NoMatch,self).__init__()
1471 1472 self.name = "NoMatch"
1472 1473 self.mayReturnEmpty = True
1473 1474 self.mayIndexError = False
1474 1475 self.errmsg = "Unmatchable token"
1475 1476 #self.myException.msg = self.errmsg
1476 1477
1477 1478 def parseImpl( self, instring, loc, doActions=True ):
1478 1479 exc = self.myException
1479 1480 exc.loc = loc
1480 1481 exc.pstr = instring
1481 1482 raise exc
1482 1483
1483 1484
1484 1485 class Literal(Token):
1485 1486 """Token to exactly match a specified string."""
1486 1487 def __init__( self, matchString ):
1487 1488 super(Literal,self).__init__()
1488 1489 self.match = matchString
1489 1490 self.matchLen = len(matchString)
1490 1491 try:
1491 1492 self.firstMatchChar = matchString[0]
1492 1493 except IndexError:
1493 1494 warnings.warn("null string passed to Literal; use Empty() instead",
1494 1495 SyntaxWarning, stacklevel=2)
1495 1496 self.__class__ = Empty
1496 1497 self.name = '"%s"' % _ustr(self.match)
1497 1498 self.errmsg = "Expected " + self.name
1498 1499 self.mayReturnEmpty = False
1499 1500 #self.myException.msg = self.errmsg
1500 1501 self.mayIndexError = False
1501 1502
1502 1503 # Performance tuning: this routine gets called a *lot*
1503 1504 # if this is a single character match string and the first character matches,
1504 1505 # short-circuit as quickly as possible, and avoid calling startswith
1505 1506 #~ @profile
1506 1507 def parseImpl( self, instring, loc, doActions=True ):
1507 1508 if (instring[loc] == self.firstMatchChar and
1508 1509 (self.matchLen==1 or instring.startswith(self.match,loc)) ):
1509 1510 return loc+self.matchLen, self.match
1510 1511 #~ raise ParseException( instring, loc, self.errmsg )
1511 1512 exc = self.myException
1512 1513 exc.loc = loc
1513 1514 exc.pstr = instring
1514 1515 raise exc
1515 1516 _L = Literal
1516 1517
1517 1518 class Keyword(Token):
1518 1519 """Token to exactly match a specified string as a keyword, that is, it must be
1519 1520 immediately followed by a non-keyword character. Compare with Literal::
1520 1521 Literal("if") will match the leading 'if' in 'ifAndOnlyIf'.
1521 1522 Keyword("if") will not; it will only match the leading 'if in 'if x=1', or 'if(y==2)'
1522 1523 Accepts two optional constructor arguments in addition to the keyword string:
1523 1524 identChars is a string of characters that would be valid identifier characters,
1524 1525 defaulting to all alphanumerics + "_" and "$"; caseless allows case-insensitive
1525 1526 matching, default is False.
1526 1527 """
1527 1528 DEFAULT_KEYWORD_CHARS = alphanums+"_$"
1528 1529
1529 1530 def __init__( self, matchString, identChars=DEFAULT_KEYWORD_CHARS, caseless=False ):
1530 1531 super(Keyword,self).__init__()
1531 1532 self.match = matchString
1532 1533 self.matchLen = len(matchString)
1533 1534 try:
1534 1535 self.firstMatchChar = matchString[0]
1535 1536 except IndexError:
1536 1537 warnings.warn("null string passed to Keyword; use Empty() instead",
1537 1538 SyntaxWarning, stacklevel=2)
1538 1539 self.name = '"%s"' % self.match
1539 1540 self.errmsg = "Expected " + self.name
1540 1541 self.mayReturnEmpty = False
1541 1542 #self.myException.msg = self.errmsg
1542 1543 self.mayIndexError = False
1543 1544 self.caseless = caseless
1544 1545 if caseless:
1545 1546 self.caselessmatch = matchString.upper()
1546 1547 identChars = identChars.upper()
1547 1548 self.identChars = _str2dict(identChars)
1548 1549
1549 1550 def parseImpl( self, instring, loc, doActions=True ):
1550 1551 if self.caseless:
1551 1552 if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
1552 1553 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and
1553 1554 (loc == 0 or instring[loc-1].upper() not in self.identChars) ):
1554 1555 return loc+self.matchLen, self.match
1555 1556 else:
1556 1557 if (instring[loc] == self.firstMatchChar and
1557 1558 (self.matchLen==1 or instring.startswith(self.match,loc)) and
1558 1559 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and
1559 1560 (loc == 0 or instring[loc-1] not in self.identChars) ):
1560 1561 return loc+self.matchLen, self.match
1561 1562 #~ raise ParseException( instring, loc, self.errmsg )
1562 1563 exc = self.myException
1563 1564 exc.loc = loc
1564 1565 exc.pstr = instring
1565 1566 raise exc
1566 1567
1567 1568 def copy(self):
1568 1569 c = super(Keyword,self).copy()
1569 1570 c.identChars = Keyword.DEFAULT_KEYWORD_CHARS
1570 1571 return c
1571 1572
1572 1573 def setDefaultKeywordChars( chars ):
1573 1574 """Overrides the default Keyword chars
1574 1575 """
1575 1576 Keyword.DEFAULT_KEYWORD_CHARS = chars
1576 1577 setDefaultKeywordChars = staticmethod(setDefaultKeywordChars)
1577 1578
1578 1579 class CaselessLiteral(Literal):
1579 1580 """Token to match a specified string, ignoring case of letters.
1580 1581 Note: the matched results will always be in the case of the given
1581 1582 match string, NOT the case of the input text.
1582 1583 """
1583 1584 def __init__( self, matchString ):
1584 1585 super(CaselessLiteral,self).__init__( matchString.upper() )
1585 1586 # Preserve the defining literal.
1586 1587 self.returnString = matchString
1587 1588 self.name = "'%s'" % self.returnString
1588 1589 self.errmsg = "Expected " + self.name
1589 1590 #self.myException.msg = self.errmsg
1590 1591
1591 1592 def parseImpl( self, instring, loc, doActions=True ):
1592 1593 if instring[ loc:loc+self.matchLen ].upper() == self.match:
1593 1594 return loc+self.matchLen, self.returnString
1594 1595 #~ raise ParseException( instring, loc, self.errmsg )
1595 1596 exc = self.myException
1596 1597 exc.loc = loc
1597 1598 exc.pstr = instring
1598 1599 raise exc
1599 1600
1600 1601 class CaselessKeyword(Keyword):
1601 1602 def __init__( self, matchString, identChars=Keyword.DEFAULT_KEYWORD_CHARS ):
1602 1603 super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True )
1603 1604
1604 1605 def parseImpl( self, instring, loc, doActions=True ):
1605 1606 if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
1606 1607 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ):
1607 1608 return loc+self.matchLen, self.match
1608 1609 #~ raise ParseException( instring, loc, self.errmsg )
1609 1610 exc = self.myException
1610 1611 exc.loc = loc
1611 1612 exc.pstr = instring
1612 1613 raise exc
1613 1614
1614 1615 class Word(Token):
1615 1616 """Token for matching words composed of allowed character sets.
1616 1617 Defined with string containing all allowed initial characters,
1617 1618 an optional string containing allowed body characters (if omitted,
1618 1619 defaults to the initial character set), and an optional minimum,
1619 1620 maximum, and/or exact length. The default value for min is 1 (a
1620 1621 minimum value < 1 is not valid); the default values for max and exact
1621 1622 are 0, meaning no maximum or exact length restriction.
1622 1623 """
1623 1624 def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False ):
1624 1625 super(Word,self).__init__()
1625 1626 self.initCharsOrig = initChars
1626 1627 self.initChars = _str2dict(initChars)
1627 1628 if bodyChars :
1628 1629 self.bodyCharsOrig = bodyChars
1629 1630 self.bodyChars = _str2dict(bodyChars)
1630 1631 else:
1631 1632 self.bodyCharsOrig = initChars
1632 1633 self.bodyChars = _str2dict(initChars)
1633 1634
1634 1635 self.maxSpecified = max > 0
1635 1636
1636 1637 if min < 1:
1637 1638 raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted")
1638 1639
1639 1640 self.minLen = min
1640 1641
1641 1642 if max > 0:
1642 1643 self.maxLen = max
1643 1644 else:
1644 1645 self.maxLen = _MAX_INT
1645 1646
1646 1647 if exact > 0:
1647 1648 self.maxLen = exact
1648 1649 self.minLen = exact
1649 1650
1650 1651 self.name = _ustr(self)
1651 1652 self.errmsg = "Expected " + self.name
1652 1653 #self.myException.msg = self.errmsg
1653 1654 self.mayIndexError = False
1654 1655 self.asKeyword = asKeyword
1655 1656
1656 1657 if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0):
1657 1658 if self.bodyCharsOrig == self.initCharsOrig:
1658 1659 self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig)
1659 1660 elif len(self.bodyCharsOrig) == 1:
1660 1661 self.reString = "%s[%s]*" % \
1661 1662 (re.escape(self.initCharsOrig),
1662 1663 _escapeRegexRangeChars(self.bodyCharsOrig),)
1663 1664 else:
1664 1665 self.reString = "[%s][%s]*" % \
1665 1666 (_escapeRegexRangeChars(self.initCharsOrig),
1666 1667 _escapeRegexRangeChars(self.bodyCharsOrig),)
1667 1668 if self.asKeyword:
1668 1669 self.reString = r"\b"+self.reString+r"\b"
1669 1670 try:
1670 1671 self.re = re.compile( self.reString )
1671 1672 except:
1672 1673 self.re = None
1673 1674
1674 1675 def parseImpl( self, instring, loc, doActions=True ):
1675 1676 if self.re:
1676 1677 result = self.re.match(instring,loc)
1677 1678 if not result:
1678 1679 exc = self.myException
1679 1680 exc.loc = loc
1680 1681 exc.pstr = instring
1681 1682 raise exc
1682 1683
1683 1684 loc = result.end()
1684 1685 return loc,result.group()
1685 1686
1686 1687 if not(instring[ loc ] in self.initChars):
1687 1688 #~ raise ParseException( instring, loc, self.errmsg )
1688 1689 exc = self.myException
1689 1690 exc.loc = loc
1690 1691 exc.pstr = instring
1691 1692 raise exc
1692 1693 start = loc
1693 1694 loc += 1
1694 1695 instrlen = len(instring)
1695 1696 bodychars = self.bodyChars
1696 1697 maxloc = start + self.maxLen
1697 1698 maxloc = min( maxloc, instrlen )
1698 1699 while loc < maxloc and instring[loc] in bodychars:
1699 1700 loc += 1
1700 1701
1701 1702 throwException = False
1702 1703 if loc - start < self.minLen:
1703 1704 throwException = True
1704 1705 if self.maxSpecified and loc < instrlen and instring[loc] in bodychars:
1705 1706 throwException = True
1706 1707 if self.asKeyword:
1707 1708 if (start>0 and instring[start-1] in bodychars) or (loc<instrlen and instring[loc] in bodychars):
1708 1709 throwException = True
1709 1710
1710 1711 if throwException:
1711 1712 #~ raise ParseException( instring, loc, self.errmsg )
1712 1713 exc = self.myException
1713 1714 exc.loc = loc
1714 1715 exc.pstr = instring
1715 1716 raise exc
1716 1717
1717 1718 return loc, instring[start:loc]
1718 1719
1719 1720 def __str__( self ):
1720 1721 try:
1721 1722 return super(Word,self).__str__()
1722 1723 except:
1723 1724 pass
1724 1725
1725 1726
1726 1727 if self.strRepr is None:
1727 1728
1728 1729 def charsAsStr(s):
1729 1730 if len(s)>4:
1730 1731 return s[:4]+"..."
1731 1732 else:
1732 1733 return s
1733 1734
1734 1735 if ( self.initCharsOrig != self.bodyCharsOrig ):
1735 1736 self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) )
1736 1737 else:
1737 1738 self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig)
1738 1739
1739 1740 return self.strRepr
1740 1741
1741 1742
1742 1743 class Regex(Token):
1743 1744 """Token for matching strings that match a given regular expression.
1744 1745 Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module.
1745 1746 """
1746 1747 def __init__( self, pattern, flags=0):
1747 1748 """The parameters pattern and flags are passed to the re.compile() function as-is. See the Python re module for an explanation of the acceptable patterns and flags."""
1748 1749 super(Regex,self).__init__()
1749 1750
1750 1751 if len(pattern) == 0:
1751 1752 warnings.warn("null string passed to Regex; use Empty() instead",
1752 1753 SyntaxWarning, stacklevel=2)
1753 1754
1754 1755 self.pattern = pattern
1755 1756 self.flags = flags
1756 1757
1757 1758 try:
1758 1759 self.re = re.compile(self.pattern, self.flags)
1759 1760 self.reString = self.pattern
1760 1761 except sre_constants.error:
1761 1762 warnings.warn("invalid pattern (%s) passed to Regex" % pattern,
1762 1763 SyntaxWarning, stacklevel=2)
1763 1764 raise
1764 1765
1765 1766 self.name = _ustr(self)
1766 1767 self.errmsg = "Expected " + self.name
1767 1768 #self.myException.msg = self.errmsg
1768 1769 self.mayIndexError = False
1769 1770 self.mayReturnEmpty = True
1770 1771
1771 1772 def parseImpl( self, instring, loc, doActions=True ):
1772 1773 result = self.re.match(instring,loc)
1773 1774 if not result:
1774 1775 exc = self.myException
1775 1776 exc.loc = loc
1776 1777 exc.pstr = instring
1777 1778 raise exc
1778 1779
1779 1780 loc = result.end()
1780 1781 d = result.groupdict()
1781 1782 ret = ParseResults(result.group())
1782 1783 if d:
1783 1784 for k in d:
1784 1785 ret[k] = d[k]
1785 1786 return loc,ret
1786 1787
1787 1788 def __str__( self ):
1788 1789 try:
1789 1790 return super(Regex,self).__str__()
1790 1791 except:
1791 1792 pass
1792 1793
1793 1794 if self.strRepr is None:
1794 1795 self.strRepr = "Re:(%s)" % repr(self.pattern)
1795 1796
1796 1797 return self.strRepr
1797 1798
1798 1799
1799 1800 class QuotedString(Token):
1800 1801 """Token for matching strings that are delimited by quoting characters.
1801 1802 """
1802 1803 def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None):
1803 1804 """
1804 1805 Defined with the following parameters:
1805 1806 - quoteChar - string of one or more characters defining the quote delimiting string
1806 1807 - escChar - character to escape quotes, typically backslash (default=None)
1807 1808 - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=None)
1808 1809 - multiline - boolean indicating whether quotes can span multiple lines (default=False)
1809 1810 - unquoteResults - boolean indicating whether the matched text should be unquoted (default=True)
1810 1811 - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=None => same as quoteChar)
1811 1812 """
1812 1813 super(QuotedString,self).__init__()
1813 1814
1814 1815 # remove white space from quote chars - wont work anyway
1815 1816 quoteChar = quoteChar.strip()
1816 1817 if len(quoteChar) == 0:
1817 1818 warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
1818 1819 raise SyntaxError()
1819 1820
1820 1821 if endQuoteChar is None:
1821 1822 endQuoteChar = quoteChar
1822 1823 else:
1823 1824 endQuoteChar = endQuoteChar.strip()
1824 1825 if len(endQuoteChar) == 0:
1825 1826 warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
1826 1827 raise SyntaxError()
1827 1828
1828 1829 self.quoteChar = quoteChar
1829 1830 self.quoteCharLen = len(quoteChar)
1830 1831 self.firstQuoteChar = quoteChar[0]
1831 1832 self.endQuoteChar = endQuoteChar
1832 1833 self.endQuoteCharLen = len(endQuoteChar)
1833 1834 self.escChar = escChar
1834 1835 self.escQuote = escQuote
1835 1836 self.unquoteResults = unquoteResults
1836 1837
1837 1838 if multiline:
1838 1839 self.flags = re.MULTILINE | re.DOTALL
1839 1840 self.pattern = r'%s(?:[^%s%s]' % \
1840 1841 ( re.escape(self.quoteChar),
1841 1842 _escapeRegexRangeChars(self.endQuoteChar[0]),
1842 1843 (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
1843 1844 else:
1844 1845 self.flags = 0
1845 1846 self.pattern = r'%s(?:[^%s\n\r%s]' % \
1846 1847 ( re.escape(self.quoteChar),
1847 1848 _escapeRegexRangeChars(self.endQuoteChar[0]),
1848 1849 (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
1849 1850 if len(self.endQuoteChar) > 1:
1850 1851 self.pattern += (
1851 1852 '|(?:' + ')|(?:'.join(["%s[^%s]" % (re.escape(self.endQuoteChar[:i]),
1852 1853 _escapeRegexRangeChars(self.endQuoteChar[i]))
1853 1854 for i in range(len(self.endQuoteChar)-1,0,-1)]) + ')'
1854 1855 )
1855 1856 if escQuote:
1856 1857 self.pattern += (r'|(?:%s)' % re.escape(escQuote))
1857 1858 if escChar:
1858 1859 self.pattern += (r'|(?:%s.)' % re.escape(escChar))
1859 1860 self.escCharReplacePattern = re.escape(self.escChar)+"(.)"
1860 1861 self.pattern += (r')*%s' % re.escape(self.endQuoteChar))
1861 1862
1862 1863 try:
1863 1864 self.re = re.compile(self.pattern, self.flags)
1864 1865 self.reString = self.pattern
1865 1866 except sre_constants.error:
1866 1867 warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern,
1867 1868 SyntaxWarning, stacklevel=2)
1868 1869 raise
1869 1870
1870 1871 self.name = _ustr(self)
1871 1872 self.errmsg = "Expected " + self.name
1872 1873 #self.myException.msg = self.errmsg
1873 1874 self.mayIndexError = False
1874 1875 self.mayReturnEmpty = True
1875 1876
1876 1877 def parseImpl( self, instring, loc, doActions=True ):
1877 1878 result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None
1878 1879 if not result:
1879 1880 exc = self.myException
1880 1881 exc.loc = loc
1881 1882 exc.pstr = instring
1882 1883 raise exc
1883 1884
1884 1885 loc = result.end()
1885 1886 ret = result.group()
1886 1887
1887 1888 if self.unquoteResults:
1888 1889
1889 1890 # strip off quotes
1890 1891 ret = ret[self.quoteCharLen:-self.endQuoteCharLen]
1891 1892
1892 1893 if isinstance(ret,basestring):
1893 1894 # replace escaped characters
1894 1895 if self.escChar:
1895 1896 ret = re.sub(self.escCharReplacePattern,"\g<1>",ret)
1896 1897
1897 1898 # replace escaped quotes
1898 1899 if self.escQuote:
1899 1900 ret = ret.replace(self.escQuote, self.endQuoteChar)
1900 1901
1901 1902 return loc, ret
1902 1903
1903 1904 def __str__( self ):
1904 1905 try:
1905 1906 return super(QuotedString,self).__str__()
1906 1907 except:
1907 1908 pass
1908 1909
1909 1910 if self.strRepr is None:
1910 1911 self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar)
1911 1912
1912 1913 return self.strRepr
1913 1914
1914 1915
1915 1916 class CharsNotIn(Token):
1916 1917 """Token for matching words composed of characters *not* in a given set.
1917 1918 Defined with string containing all disallowed characters, and an optional
1918 1919 minimum, maximum, and/or exact length. The default value for min is 1 (a
1919 1920 minimum value < 1 is not valid); the default values for max and exact
1920 1921 are 0, meaning no maximum or exact length restriction.
1921 1922 """
1922 1923 def __init__( self, notChars, min=1, max=0, exact=0 ):
1923 1924 super(CharsNotIn,self).__init__()
1924 1925 self.skipWhitespace = False
1925 1926 self.notChars = notChars
1926 1927
1927 1928 if min < 1:
1928 1929 raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted")
1929 1930
1930 1931 self.minLen = min
1931 1932
1932 1933 if max > 0:
1933 1934 self.maxLen = max
1934 1935 else:
1935 1936 self.maxLen = _MAX_INT
1936 1937
1937 1938 if exact > 0:
1938 1939 self.maxLen = exact
1939 1940 self.minLen = exact
1940 1941
1941 1942 self.name = _ustr(self)
1942 1943 self.errmsg = "Expected " + self.name
1943 1944 self.mayReturnEmpty = ( self.minLen == 0 )
1944 1945 #self.myException.msg = self.errmsg
1945 1946 self.mayIndexError = False
1946 1947
1947 1948 def parseImpl( self, instring, loc, doActions=True ):
1948 1949 if instring[loc] in self.notChars:
1949 1950 #~ raise ParseException( instring, loc, self.errmsg )
1950 1951 exc = self.myException
1951 1952 exc.loc = loc
1952 1953 exc.pstr = instring
1953 1954 raise exc
1954 1955
1955 1956 start = loc
1956 1957 loc += 1
1957 1958 notchars = self.notChars
1958 1959 maxlen = min( start+self.maxLen, len(instring) )
1959 1960 while loc < maxlen and \
1960 1961 (instring[loc] not in notchars):
1961 1962 loc += 1
1962 1963
1963 1964 if loc - start < self.minLen:
1964 1965 #~ raise ParseException( instring, loc, self.errmsg )
1965 1966 exc = self.myException
1966 1967 exc.loc = loc
1967 1968 exc.pstr = instring
1968 1969 raise exc
1969 1970
1970 1971 return loc, instring[start:loc]
1971 1972
1972 1973 def __str__( self ):
1973 1974 try:
1974 1975 return super(CharsNotIn, self).__str__()
1975 1976 except:
1976 1977 pass
1977 1978
1978 1979 if self.strRepr is None:
1979 1980 if len(self.notChars) > 4:
1980 1981 self.strRepr = "!W:(%s...)" % self.notChars[:4]
1981 1982 else:
1982 1983 self.strRepr = "!W:(%s)" % self.notChars
1983 1984
1984 1985 return self.strRepr
1985 1986
1986 1987 class White(Token):
1987 1988 """Special matching class for matching whitespace. Normally, whitespace is ignored
1988 1989 by pyparsing grammars. This class is included when some whitespace structures
1989 1990 are significant. Define with a string containing the whitespace characters to be
1990 1991 matched; default is " \\t\\r\\n". Also takes optional min, max, and exact arguments,
1991 1992 as defined for the Word class."""
1992 1993 whiteStrs = {
1993 1994 " " : "<SPC>",
1994 1995 "\t": "<TAB>",
1995 1996 "\n": "<LF>",
1996 1997 "\r": "<CR>",
1997 1998 "\f": "<FF>",
1998 1999 }
1999 2000 def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0):
2000 2001 super(White,self).__init__()
2001 2002 self.matchWhite = ws
2002 2003 self.setWhitespaceChars( "".join([c for c in self.whiteChars if c not in self.matchWhite]) )
2003 2004 #~ self.leaveWhitespace()
2004 2005 self.name = ("".join([White.whiteStrs[c] for c in self.matchWhite]))
2005 2006 self.mayReturnEmpty = True
2006 2007 self.errmsg = "Expected " + self.name
2007 2008 #self.myException.msg = self.errmsg
2008 2009
2009 2010 self.minLen = min
2010 2011
2011 2012 if max > 0:
2012 2013 self.maxLen = max
2013 2014 else:
2014 2015 self.maxLen = _MAX_INT
2015 2016
2016 2017 if exact > 0:
2017 2018 self.maxLen = exact
2018 2019 self.minLen = exact
2019 2020
2020 2021 def parseImpl( self, instring, loc, doActions=True ):
2021 2022 if not(instring[ loc ] in self.matchWhite):
2022 2023 #~ raise ParseException( instring, loc, self.errmsg )
2023 2024 exc = self.myException
2024 2025 exc.loc = loc
2025 2026 exc.pstr = instring
2026 2027 raise exc
2027 2028 start = loc
2028 2029 loc += 1
2029 2030 maxloc = start + self.maxLen
2030 2031 maxloc = min( maxloc, len(instring) )
2031 2032 while loc < maxloc and instring[loc] in self.matchWhite:
2032 2033 loc += 1
2033 2034
2034 2035 if loc - start < self.minLen:
2035 2036 #~ raise ParseException( instring, loc, self.errmsg )
2036 2037 exc = self.myException
2037 2038 exc.loc = loc
2038 2039 exc.pstr = instring
2039 2040 raise exc
2040 2041
2041 2042 return loc, instring[start:loc]
2042 2043
2043 2044
2044 2045 class _PositionToken(Token):
2045 2046 def __init__( self ):
2046 2047 super(_PositionToken,self).__init__()
2047 2048 self.name=self.__class__.__name__
2048 2049 self.mayReturnEmpty = True
2049 2050 self.mayIndexError = False
2050 2051
2051 2052 class GoToColumn(_PositionToken):
2052 2053 """Token to advance to a specific column of input text; useful for tabular report scraping."""
2053 2054 def __init__( self, colno ):
2054 2055 super(GoToColumn,self).__init__()
2055 2056 self.col = colno
2056 2057
2057 2058 def preParse( self, instring, loc ):
2058 2059 if col(loc,instring) != self.col:
2059 2060 instrlen = len(instring)
2060 2061 if self.ignoreExprs:
2061 2062 loc = self._skipIgnorables( instring, loc )
2062 2063 while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col :
2063 2064 loc += 1
2064 2065 return loc
2065 2066
2066 2067 def parseImpl( self, instring, loc, doActions=True ):
2067 2068 thiscol = col( loc, instring )
2068 2069 if thiscol > self.col:
2069 2070 raise ParseException( instring, loc, "Text not in expected column", self )
2070 2071 newloc = loc + self.col - thiscol
2071 2072 ret = instring[ loc: newloc ]
2072 2073 return newloc, ret
2073 2074
2074 2075 class LineStart(_PositionToken):
2075 2076 """Matches if current position is at the beginning of a line within the parse string"""
2076 2077 def __init__( self ):
2077 2078 super(LineStart,self).__init__()
2078 2079 self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") )
2079 2080 self.errmsg = "Expected start of line"
2080 2081 #self.myException.msg = self.errmsg
2081 2082
2082 2083 def preParse( self, instring, loc ):
2083 2084 preloc = super(LineStart,self).preParse(instring,loc)
2084 2085 if instring[preloc] == "\n":
2085 2086 loc += 1
2086 2087 return loc
2087 2088
2088 2089 def parseImpl( self, instring, loc, doActions=True ):
2089 2090 if not( loc==0 or
2090 2091 (loc == self.preParse( instring, 0 )) or
2091 2092 (instring[loc-1] == "\n") ): #col(loc, instring) != 1:
2092 2093 #~ raise ParseException( instring, loc, "Expected start of line" )
2093 2094 exc = self.myException
2094 2095 exc.loc = loc
2095 2096 exc.pstr = instring
2096 2097 raise exc
2097 2098 return loc, []
2098 2099
2099 2100 class LineEnd(_PositionToken):
2100 2101 """Matches if current position is at the end of a line within the parse string"""
2101 2102 def __init__( self ):
2102 2103 super(LineEnd,self).__init__()
2103 2104 self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") )
2104 2105 self.errmsg = "Expected end of line"
2105 2106 #self.myException.msg = self.errmsg
2106 2107
2107 2108 def parseImpl( self, instring, loc, doActions=True ):
2108 2109 if loc<len(instring):
2109 2110 if instring[loc] == "\n":
2110 2111 return loc+1, "\n"
2111 2112 else:
2112 2113 #~ raise ParseException( instring, loc, "Expected end of line" )
2113 2114 exc = self.myException
2114 2115 exc.loc = loc
2115 2116 exc.pstr = instring
2116 2117 raise exc
2117 2118 elif loc == len(instring):
2118 2119 return loc+1, []
2119 2120 else:
2120 2121 exc = self.myException
2121 2122 exc.loc = loc
2122 2123 exc.pstr = instring
2123 2124 raise exc
2124 2125
2125 2126 class StringStart(_PositionToken):
2126 2127 """Matches if current position is at the beginning of the parse string"""
2127 2128 def __init__( self ):
2128 2129 super(StringStart,self).__init__()
2129 2130 self.errmsg = "Expected start of text"
2130 2131 #self.myException.msg = self.errmsg
2131 2132
2132 2133 def parseImpl( self, instring, loc, doActions=True ):
2133 2134 if loc != 0:
2134 2135 # see if entire string up to here is just whitespace and ignoreables
2135 2136 if loc != self.preParse( instring, 0 ):
2136 2137 #~ raise ParseException( instring, loc, "Expected start of text" )
2137 2138 exc = self.myException
2138 2139 exc.loc = loc
2139 2140 exc.pstr = instring
2140 2141 raise exc
2141 2142 return loc, []
2142 2143
2143 2144 class StringEnd(_PositionToken):
2144 2145 """Matches if current position is at the end of the parse string"""
2145 2146 def __init__( self ):
2146 2147 super(StringEnd,self).__init__()
2147 2148 self.errmsg = "Expected end of text"
2148 2149 #self.myException.msg = self.errmsg
2149 2150
2150 2151 def parseImpl( self, instring, loc, doActions=True ):
2151 2152 if loc < len(instring):
2152 2153 #~ raise ParseException( instring, loc, "Expected end of text" )
2153 2154 exc = self.myException
2154 2155 exc.loc = loc
2155 2156 exc.pstr = instring
2156 2157 raise exc
2157 2158 elif loc == len(instring):
2158 2159 return loc+1, []
2159 2160 elif loc > len(instring):
2160 2161 return loc, []
2161 2162 else:
2162 2163 exc = self.myException
2163 2164 exc.loc = loc
2164 2165 exc.pstr = instring
2165 2166 raise exc
2166 2167
2167 2168 class WordStart(_PositionToken):
2168 2169 """Matches if the current position is at the beginning of a Word, and
2169 2170 is not preceded by any character in a given set of wordChars
2170 2171 (default=printables). To emulate the \b behavior of regular expressions,
2171 2172 use WordStart(alphanums). WordStart will also match at the beginning of
2172 2173 the string being parsed, or at the beginning of a line.
2173 2174 """
2174 2175 def __init__(self, wordChars = printables):
2175 2176 super(WordStart,self).__init__()
2176 2177 self.wordChars = _str2dict(wordChars)
2177 2178 self.errmsg = "Not at the start of a word"
2178 2179
2179 2180 def parseImpl(self, instring, loc, doActions=True ):
2180 2181 if loc != 0:
2181 2182 if (instring[loc-1] in self.wordChars or
2182 2183 instring[loc] not in self.wordChars):
2183 2184 exc = self.myException
2184 2185 exc.loc = loc
2185 2186 exc.pstr = instring
2186 2187 raise exc
2187 2188 return loc, []
2188 2189
2189 2190 class WordEnd(_PositionToken):
2190 2191 """Matches if the current position is at the end of a Word, and
2191 2192 is not followed by any character in a given set of wordChars
2192 2193 (default=printables). To emulate the \b behavior of regular expressions,
2193 2194 use WordEnd(alphanums). WordEnd will also match at the end of
2194 2195 the string being parsed, or at the end of a line.
2195 2196 """
2196 2197 def __init__(self, wordChars = printables):
2197 2198 super(WordEnd,self).__init__()
2198 2199 self.wordChars = _str2dict(wordChars)
2199 2200 self.skipWhitespace = False
2200 2201 self.errmsg = "Not at the end of a word"
2201 2202
2202 2203 def parseImpl(self, instring, loc, doActions=True ):
2203 2204 instrlen = len(instring)
2204 2205 if instrlen>0 and loc<instrlen:
2205 2206 if (instring[loc] in self.wordChars or
2206 2207 instring[loc-1] not in self.wordChars):
2207 2208 #~ raise ParseException( instring, loc, "Expected end of word" )
2208 2209 exc = self.myException
2209 2210 exc.loc = loc
2210 2211 exc.pstr = instring
2211 2212 raise exc
2212 2213 return loc, []
2213 2214
2214 2215
2215 2216 class ParseExpression(ParserElement):
2216 2217 """Abstract subclass of ParserElement, for combining and post-processing parsed tokens."""
2217 2218 def __init__( self, exprs, savelist = False ):
2218 2219 super(ParseExpression,self).__init__(savelist)
2219 2220 if isinstance( exprs, list ):
2220 2221 self.exprs = exprs
2221 2222 elif isinstance( exprs, basestring ):
2222 2223 self.exprs = [ Literal( exprs ) ]
2223 2224 else:
2224 2225 try:
2225 2226 self.exprs = list( exprs )
2226 2227 except TypeError:
2227 2228 self.exprs = [ exprs ]
2228 2229 self.callPreparse = False
2229 2230
2230 2231 def __getitem__( self, i ):
2231 2232 return self.exprs[i]
2232 2233
2233 2234 def append( self, other ):
2234 2235 self.exprs.append( other )
2235 2236 self.strRepr = None
2236 2237 return self
2237 2238
2238 2239 def leaveWhitespace( self ):
2239 2240 """Extends leaveWhitespace defined in base class, and also invokes leaveWhitespace on
2240 2241 all contained expressions."""
2241 2242 self.skipWhitespace = False
2242 2243 self.exprs = [ e.copy() for e in self.exprs ]
2243 2244 for e in self.exprs:
2244 2245 e.leaveWhitespace()
2245 2246 return self
2246 2247
2247 2248 def ignore( self, other ):
2248 2249 if isinstance( other, Suppress ):
2249 2250 if other not in self.ignoreExprs:
2250 2251 super( ParseExpression, self).ignore( other )
2251 2252 for e in self.exprs:
2252 2253 e.ignore( self.ignoreExprs[-1] )
2253 2254 else:
2254 2255 super( ParseExpression, self).ignore( other )
2255 2256 for e in self.exprs:
2256 2257 e.ignore( self.ignoreExprs[-1] )
2257 2258 return self
2258 2259
2259 2260 def __str__( self ):
2260 2261 try:
2261 2262 return super(ParseExpression,self).__str__()
2262 2263 except:
2263 2264 pass
2264 2265
2265 2266 if self.strRepr is None:
2266 2267 self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.exprs) )
2267 2268 return self.strRepr
2268 2269
2269 2270 def streamline( self ):
2270 2271 super(ParseExpression,self).streamline()
2271 2272
2272 2273 for e in self.exprs:
2273 2274 e.streamline()
2274 2275
2275 2276 # collapse nested And's of the form And( And( And( a,b), c), d) to And( a,b,c,d )
2276 2277 # but only if there are no parse actions or resultsNames on the nested And's
2277 2278 # (likewise for Or's and MatchFirst's)
2278 2279 if ( len(self.exprs) == 2 ):
2279 2280 other = self.exprs[0]
2280 2281 if ( isinstance( other, self.__class__ ) and
2281 2282 not(other.parseAction) and
2282 2283 other.resultsName is None and
2283 2284 not other.debug ):
2284 2285 self.exprs = other.exprs[:] + [ self.exprs[1] ]
2285 2286 self.strRepr = None
2286 2287 self.mayReturnEmpty |= other.mayReturnEmpty
2287 2288 self.mayIndexError |= other.mayIndexError
2288 2289
2289 2290 other = self.exprs[-1]
2290 2291 if ( isinstance( other, self.__class__ ) and
2291 2292 not(other.parseAction) and
2292 2293 other.resultsName is None and
2293 2294 not other.debug ):
2294 2295 self.exprs = self.exprs[:-1] + other.exprs[:]
2295 2296 self.strRepr = None
2296 2297 self.mayReturnEmpty |= other.mayReturnEmpty
2297 2298 self.mayIndexError |= other.mayIndexError
2298 2299
2299 2300 return self
2300 2301
2301 2302 def setResultsName( self, name, listAllMatches=False ):
2302 2303 ret = super(ParseExpression,self).setResultsName(name,listAllMatches)
2303 2304 return ret
2304 2305
2305 2306 def validate( self, validateTrace=[] ):
2306 2307 tmp = validateTrace[:]+[self]
2307 2308 for e in self.exprs:
2308 2309 e.validate(tmp)
2309 2310 self.checkRecursion( [] )
2310 2311
2311 2312 class And(ParseExpression):
2312 2313 """Requires all given ParseExpressions to be found in the given order.
2313 2314 Expressions may be separated by whitespace.
2314 2315 May be constructed using the '+' operator.
2315 2316 """
2316 2317
2317 2318 class _ErrorStop(Empty):
2318 2319 def __init__(self, *args, **kwargs):
2319 2320 super(Empty,self).__init__(*args, **kwargs)
2320 2321 self.leaveWhitespace()
2321 2322
2322 2323 def __init__( self, exprs, savelist = True ):
2323 2324 super(And,self).__init__(exprs, savelist)
2324 2325 self.mayReturnEmpty = True
2325 2326 for e in self.exprs:
2326 2327 if not e.mayReturnEmpty:
2327 2328 self.mayReturnEmpty = False
2328 2329 break
2329 2330 self.setWhitespaceChars( exprs[0].whiteChars )
2330 2331 self.skipWhitespace = exprs[0].skipWhitespace
2331 2332 self.callPreparse = True
2332 2333
2333 2334 def parseImpl( self, instring, loc, doActions=True ):
2334 2335 # pass False as last arg to _parse for first element, since we already
2335 2336 # pre-parsed the string as part of our And pre-parsing
2336 2337 loc, resultlist = self.exprs[0]._parse( instring, loc, doActions, callPreParse=False )
2337 2338 errorStop = False
2338 2339 for e in self.exprs[1:]:
2339 2340 if isinstance(e, And._ErrorStop):
2340 2341 errorStop = True
2341 2342 continue
2342 2343 if errorStop:
2343 2344 try:
2344 2345 loc, exprtokens = e._parse( instring, loc, doActions )
2345 2346 except ParseSyntaxException:
2346 2347 raise
2347 2348 except ParseBaseException, pe:
2348 2349 raise ParseSyntaxException(pe)
2349 2350 except IndexError, ie:
2350 2351 raise ParseSyntaxException( ParseException(instring, len(instring), self.errmsg, self) )
2351 2352 else:
2352 2353 loc, exprtokens = e._parse( instring, loc, doActions )
2353 2354 if exprtokens or exprtokens.keys():
2354 2355 resultlist += exprtokens
2355 2356 return loc, resultlist
2356 2357
2357 2358 def __iadd__(self, other ):
2358 2359 if isinstance( other, basestring ):
2359 2360 other = Literal( other )
2360 2361 return self.append( other ) #And( [ self, other ] )
2361 2362
2362 2363 def checkRecursion( self, parseElementList ):
2363 2364 subRecCheckList = parseElementList[:] + [ self ]
2364 2365 for e in self.exprs:
2365 2366 e.checkRecursion( subRecCheckList )
2366 2367 if not e.mayReturnEmpty:
2367 2368 break
2368 2369
2369 2370 def __str__( self ):
2370 2371 if hasattr(self,"name"):
2371 2372 return self.name
2372 2373
2373 2374 if self.strRepr is None:
2374 2375 self.strRepr = "{" + " ".join( [ _ustr(e) for e in self.exprs ] ) + "}"
2375 2376
2376 2377 return self.strRepr
2377 2378
2378 2379
2379 2380 class Or(ParseExpression):
2380 2381 """Requires that at least one ParseExpression is found.
2381 2382 If two expressions match, the expression that matches the longest string will be used.
2382 2383 May be constructed using the '^' operator.
2383 2384 """
2384 2385 def __init__( self, exprs, savelist = False ):
2385 2386 super(Or,self).__init__(exprs, savelist)
2386 2387 self.mayReturnEmpty = False
2387 2388 for e in self.exprs:
2388 2389 if e.mayReturnEmpty:
2389 2390 self.mayReturnEmpty = True
2390 2391 break
2391 2392
2392 2393 def parseImpl( self, instring, loc, doActions=True ):
2393 2394 maxExcLoc = -1
2394 2395 maxMatchLoc = -1
2395 2396 maxException = None
2396 2397 for e in self.exprs:
2397 2398 try:
2398 2399 loc2 = e.tryParse( instring, loc )
2399 2400 except ParseException, err:
2400 2401 if err.loc > maxExcLoc:
2401 2402 maxException = err
2402 2403 maxExcLoc = err.loc
2403 2404 except IndexError:
2404 2405 if len(instring) > maxExcLoc:
2405 2406 maxException = ParseException(instring,len(instring),e.errmsg,self)
2406 2407 maxExcLoc = len(instring)
2407 2408 else:
2408 2409 if loc2 > maxMatchLoc:
2409 2410 maxMatchLoc = loc2
2410 2411 maxMatchExp = e
2411 2412
2412 2413 if maxMatchLoc < 0:
2413 2414 if maxException is not None:
2414 2415 raise maxException
2415 2416 else:
2416 2417 raise ParseException(instring, loc, "no defined alternatives to match", self)
2417 2418
2418 2419 return maxMatchExp._parse( instring, loc, doActions )
2419 2420
2420 2421 def __ixor__(self, other ):
2421 2422 if isinstance( other, basestring ):
2422 2423 other = Literal( other )
2423 2424 return self.append( other ) #Or( [ self, other ] )
2424 2425
2425 2426 def __str__( self ):
2426 2427 if hasattr(self,"name"):
2427 2428 return self.name
2428 2429
2429 2430 if self.strRepr is None:
2430 2431 self.strRepr = "{" + " ^ ".join( [ _ustr(e) for e in self.exprs ] ) + "}"
2431 2432
2432 2433 return self.strRepr
2433 2434
2434 2435 def checkRecursion( self, parseElementList ):
2435 2436 subRecCheckList = parseElementList[:] + [ self ]
2436 2437 for e in self.exprs:
2437 2438 e.checkRecursion( subRecCheckList )
2438 2439
2439 2440
2440 2441 class MatchFirst(ParseExpression):
2441 2442 """Requires that at least one ParseExpression is found.
2442 2443 If two expressions match, the first one listed is the one that will match.
2443 2444 May be constructed using the '|' operator.
2444 2445 """
2445 2446 def __init__( self, exprs, savelist = False ):
2446 2447 super(MatchFirst,self).__init__(exprs, savelist)
2447 2448 if exprs:
2448 2449 self.mayReturnEmpty = False
2449 2450 for e in self.exprs:
2450 2451 if e.mayReturnEmpty:
2451 2452 self.mayReturnEmpty = True
2452 2453 break
2453 2454 else:
2454 2455 self.mayReturnEmpty = True
2455 2456
2456 2457 def parseImpl( self, instring, loc, doActions=True ):
2457 2458 maxExcLoc = -1
2458 2459 maxException = None
2459 2460 for e in self.exprs:
2460 2461 try:
2461 2462 ret = e._parse( instring, loc, doActions )
2462 2463 return ret
2463 2464 except ParseException, err:
2464 2465 if err.loc > maxExcLoc:
2465 2466 maxException = err
2466 2467 maxExcLoc = err.loc
2467 2468 except IndexError:
2468 2469 if len(instring) > maxExcLoc:
2469 2470 maxException = ParseException(instring,len(instring),e.errmsg,self)
2470 2471 maxExcLoc = len(instring)
2471 2472
2472 2473 # only got here if no expression matched, raise exception for match that made it the furthest
2473 2474 else:
2474 2475 if maxException is not None:
2475 2476 raise maxException
2476 2477 else:
2477 2478 raise ParseException(instring, loc, "no defined alternatives to match", self)
2478 2479
2479 2480 def __ior__(self, other ):
2480 2481 if isinstance( other, basestring ):
2481 2482 other = Literal( other )
2482 2483 return self.append( other ) #MatchFirst( [ self, other ] )
2483 2484
2484 2485 def __str__( self ):
2485 2486 if hasattr(self,"name"):
2486 2487 return self.name
2487 2488
2488 2489 if self.strRepr is None:
2489 2490 self.strRepr = "{" + " | ".join( [ _ustr(e) for e in self.exprs ] ) + "}"
2490 2491
2491 2492 return self.strRepr
2492 2493
2493 2494 def checkRecursion( self, parseElementList ):
2494 2495 subRecCheckList = parseElementList[:] + [ self ]
2495 2496 for e in self.exprs:
2496 2497 e.checkRecursion( subRecCheckList )
2497 2498
2498 2499
2499 2500 class Each(ParseExpression):
2500 2501 """Requires all given ParseExpressions to be found, but in any order.
2501 2502 Expressions may be separated by whitespace.
2502 2503 May be constructed using the '&' operator.
2503 2504 """
2504 2505 def __init__( self, exprs, savelist = True ):
2505 2506 super(Each,self).__init__(exprs, savelist)
2506 2507 self.mayReturnEmpty = True
2507 2508 for e in self.exprs:
2508 2509 if not e.mayReturnEmpty:
2509 2510 self.mayReturnEmpty = False
2510 2511 break
2511 2512 self.skipWhitespace = True
2512 2513 self.initExprGroups = True
2513 2514
2514 2515 def parseImpl( self, instring, loc, doActions=True ):
2515 2516 if self.initExprGroups:
2516 2517 self.optionals = [ e.expr for e in self.exprs if isinstance(e,Optional) ]
2517 2518 self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ]
2518 2519 self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ]
2519 2520 self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ]
2520 2521 self.required += self.multirequired
2521 2522 self.initExprGroups = False
2522 2523 tmpLoc = loc
2523 2524 tmpReqd = self.required[:]
2524 2525 tmpOpt = self.optionals[:]
2525 2526 matchOrder = []
2526 2527
2527 2528 keepMatching = True
2528 2529 while keepMatching:
2529 2530 tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired
2530 2531 failed = []
2531 2532 for e in tmpExprs:
2532 2533 try:
2533 2534 tmpLoc = e.tryParse( instring, tmpLoc )
2534 2535 except ParseException:
2535 2536 failed.append(e)
2536 2537 else:
2537 2538 matchOrder.append(e)
2538 2539 if e in tmpReqd:
2539 2540 tmpReqd.remove(e)
2540 2541 elif e in tmpOpt:
2541 2542 tmpOpt.remove(e)
2542 2543 if len(failed) == len(tmpExprs):
2543 2544 keepMatching = False
2544 2545
2545 2546 if tmpReqd:
2546 2547 missing = ", ".join( [ _ustr(e) for e in tmpReqd ] )
2547 2548 raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing )
2548 2549
2549 2550 # add any unmatched Optionals, in case they have default values defined
2550 2551 matchOrder += list(e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt)
2551 2552
2552 2553 resultlist = []
2553 2554 for e in matchOrder:
2554 2555 loc,results = e._parse(instring,loc,doActions)
2555 2556 resultlist.append(results)
2556 2557
2557 2558 finalResults = ParseResults([])
2558 2559 for r in resultlist:
2559 2560 dups = {}
2560 2561 for k in r.keys():
2561 2562 if k in finalResults.keys():
2562 2563 tmp = ParseResults(finalResults[k])
2563 2564 tmp += ParseResults(r[k])
2564 2565 dups[k] = tmp
2565 2566 finalResults += ParseResults(r)
2566 for k,v in dups.items():
2567 for k,v in dups.iteritems():
2567 2568 finalResults[k] = v
2568 2569 return loc, finalResults
2569 2570
2570 2571 def __str__( self ):
2571 2572 if hasattr(self,"name"):
2572 2573 return self.name
2573 2574
2574 2575 if self.strRepr is None:
2575 2576 self.strRepr = "{" + " & ".join( [ _ustr(e) for e in self.exprs ] ) + "}"
2576 2577
2577 2578 return self.strRepr
2578 2579
2579 2580 def checkRecursion( self, parseElementList ):
2580 2581 subRecCheckList = parseElementList[:] + [ self ]
2581 2582 for e in self.exprs:
2582 2583 e.checkRecursion( subRecCheckList )
2583 2584
2584 2585
2585 2586 class ParseElementEnhance(ParserElement):
2586 2587 """Abstract subclass of ParserElement, for combining and post-processing parsed tokens."""
2587 2588 def __init__( self, expr, savelist=False ):
2588 2589 super(ParseElementEnhance,self).__init__(savelist)
2589 2590 if isinstance( expr, basestring ):
2590 2591 expr = Literal(expr)
2591 2592 self.expr = expr
2592 2593 self.strRepr = None
2593 2594 if expr is not None:
2594 2595 self.mayIndexError = expr.mayIndexError
2595 2596 self.mayReturnEmpty = expr.mayReturnEmpty
2596 2597 self.setWhitespaceChars( expr.whiteChars )
2597 2598 self.skipWhitespace = expr.skipWhitespace
2598 2599 self.saveAsList = expr.saveAsList
2599 2600 self.callPreparse = expr.callPreparse
2600 2601 self.ignoreExprs.extend(expr.ignoreExprs)
2601 2602
2602 2603 def parseImpl( self, instring, loc, doActions=True ):
2603 2604 if self.expr is not None:
2604 2605 return self.expr._parse( instring, loc, doActions, callPreParse=False )
2605 2606 else:
2606 2607 raise ParseException("",loc,self.errmsg,self)
2607 2608
2608 2609 def leaveWhitespace( self ):
2609 2610 self.skipWhitespace = False
2610 2611 self.expr = self.expr.copy()
2611 2612 if self.expr is not None:
2612 2613 self.expr.leaveWhitespace()
2613 2614 return self
2614 2615
2615 2616 def ignore( self, other ):
2616 2617 if isinstance( other, Suppress ):
2617 2618 if other not in self.ignoreExprs:
2618 2619 super( ParseElementEnhance, self).ignore( other )
2619 2620 if self.expr is not None:
2620 2621 self.expr.ignore( self.ignoreExprs[-1] )
2621 2622 else:
2622 2623 super( ParseElementEnhance, self).ignore( other )
2623 2624 if self.expr is not None:
2624 2625 self.expr.ignore( self.ignoreExprs[-1] )
2625 2626 return self
2626 2627
2627 2628 def streamline( self ):
2628 2629 super(ParseElementEnhance,self).streamline()
2629 2630 if self.expr is not None:
2630 2631 self.expr.streamline()
2631 2632 return self
2632 2633
2633 2634 def checkRecursion( self, parseElementList ):
2634 2635 if self in parseElementList:
2635 2636 raise RecursiveGrammarException( parseElementList+[self] )
2636 2637 subRecCheckList = parseElementList[:] + [ self ]
2637 2638 if self.expr is not None:
2638 2639 self.expr.checkRecursion( subRecCheckList )
2639 2640
2640 2641 def validate( self, validateTrace=[] ):
2641 2642 tmp = validateTrace[:]+[self]
2642 2643 if self.expr is not None:
2643 2644 self.expr.validate(tmp)
2644 2645 self.checkRecursion( [] )
2645 2646
2646 2647 def __str__( self ):
2647 2648 try:
2648 2649 return super(ParseElementEnhance,self).__str__()
2649 2650 except:
2650 2651 pass
2651 2652
2652 2653 if self.strRepr is None and self.expr is not None:
2653 2654 self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) )
2654 2655 return self.strRepr
2655 2656
2656 2657
2657 2658 class FollowedBy(ParseElementEnhance):
2658 2659 """Lookahead matching of the given parse expression. FollowedBy
2659 2660 does *not* advance the parsing position within the input string, it only
2660 2661 verifies that the specified parse expression matches at the current
2661 2662 position. FollowedBy always returns a null token list."""
2662 2663 def __init__( self, expr ):
2663 2664 super(FollowedBy,self).__init__(expr)
2664 2665 self.mayReturnEmpty = True
2665 2666
2666 2667 def parseImpl( self, instring, loc, doActions=True ):
2667 2668 self.expr.tryParse( instring, loc )
2668 2669 return loc, []
2669 2670
2670 2671
2671 2672 class NotAny(ParseElementEnhance):
2672 2673 """Lookahead to disallow matching with the given parse expression. NotAny
2673 2674 does *not* advance the parsing position within the input string, it only
2674 2675 verifies that the specified parse expression does *not* match at the current
2675 2676 position. Also, NotAny does *not* skip over leading whitespace. NotAny
2676 2677 always returns a null token list. May be constructed using the '~' operator."""
2677 2678 def __init__( self, expr ):
2678 2679 super(NotAny,self).__init__(expr)
2679 2680 #~ self.leaveWhitespace()
2680 2681 self.skipWhitespace = False # do NOT use self.leaveWhitespace(), don't want to propagate to exprs
2681 2682 self.mayReturnEmpty = True
2682 2683 self.errmsg = "Found unwanted token, "+_ustr(self.expr)
2683 2684 #self.myException = ParseException("",0,self.errmsg,self)
2684 2685
2685 2686 def parseImpl( self, instring, loc, doActions=True ):
2686 2687 try:
2687 2688 self.expr.tryParse( instring, loc )
2688 2689 except (ParseException,IndexError):
2689 2690 pass
2690 2691 else:
2691 2692 #~ raise ParseException(instring, loc, self.errmsg )
2692 2693 exc = self.myException
2693 2694 exc.loc = loc
2694 2695 exc.pstr = instring
2695 2696 raise exc
2696 2697 return loc, []
2697 2698
2698 2699 def __str__( self ):
2699 2700 if hasattr(self,"name"):
2700 2701 return self.name
2701 2702
2702 2703 if self.strRepr is None:
2703 2704 self.strRepr = "~{" + _ustr(self.expr) + "}"
2704 2705
2705 2706 return self.strRepr
2706 2707
2707 2708
2708 2709 class ZeroOrMore(ParseElementEnhance):
2709 2710 """Optional repetition of zero or more of the given expression."""
2710 2711 def __init__( self, expr ):
2711 2712 super(ZeroOrMore,self).__init__(expr)
2712 2713 self.mayReturnEmpty = True
2713 2714
2714 2715 def parseImpl( self, instring, loc, doActions=True ):
2715 2716 tokens = []
2716 2717 try:
2717 2718 loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False )
2718 2719 hasIgnoreExprs = ( len(self.ignoreExprs) > 0 )
2719 2720 while 1:
2720 2721 if hasIgnoreExprs:
2721 2722 preloc = self._skipIgnorables( instring, loc )
2722 2723 else:
2723 2724 preloc = loc
2724 2725 loc, tmptokens = self.expr._parse( instring, preloc, doActions )
2725 2726 if tmptokens or tmptokens.keys():
2726 2727 tokens += tmptokens
2727 2728 except (ParseException,IndexError):
2728 2729 pass
2729 2730
2730 2731 return loc, tokens
2731 2732
2732 2733 def __str__( self ):
2733 2734 if hasattr(self,"name"):
2734 2735 return self.name
2735 2736
2736 2737 if self.strRepr is None:
2737 2738 self.strRepr = "[" + _ustr(self.expr) + "]..."
2738 2739
2739 2740 return self.strRepr
2740 2741
2741 2742 def setResultsName( self, name, listAllMatches=False ):
2742 2743 ret = super(ZeroOrMore,self).setResultsName(name,listAllMatches)
2743 2744 ret.saveAsList = True
2744 2745 return ret
2745 2746
2746 2747
2747 2748 class OneOrMore(ParseElementEnhance):
2748 2749 """Repetition of one or more of the given expression."""
2749 2750 def parseImpl( self, instring, loc, doActions=True ):
2750 2751 # must be at least one
2751 2752 loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False )
2752 2753 try:
2753 2754 hasIgnoreExprs = ( len(self.ignoreExprs) > 0 )
2754 2755 while 1:
2755 2756 if hasIgnoreExprs:
2756 2757 preloc = self._skipIgnorables( instring, loc )
2757 2758 else:
2758 2759 preloc = loc
2759 2760 loc, tmptokens = self.expr._parse( instring, preloc, doActions )
2760 2761 if tmptokens or tmptokens.keys():
2761 2762 tokens += tmptokens
2762 2763 except (ParseException,IndexError):
2763 2764 pass
2764 2765
2765 2766 return loc, tokens
2766 2767
2767 2768 def __str__( self ):
2768 2769 if hasattr(self,"name"):
2769 2770 return self.name
2770 2771
2771 2772 if self.strRepr is None:
2772 2773 self.strRepr = "{" + _ustr(self.expr) + "}..."
2773 2774
2774 2775 return self.strRepr
2775 2776
2776 2777 def setResultsName( self, name, listAllMatches=False ):
2777 2778 ret = super(OneOrMore,self).setResultsName(name,listAllMatches)
2778 2779 ret.saveAsList = True
2779 2780 return ret
2780 2781
2781 2782 class _NullToken(object):
2782 2783 def __bool__(self):
2783 2784 return False
2784 2785 __nonzero__ = __bool__
2785 2786 def __str__(self):
2786 2787 return ""
2787 2788
2788 2789 _optionalNotMatched = _NullToken()
2789 2790 class Optional(ParseElementEnhance):
2790 2791 """Optional matching of the given expression.
2791 2792 A default return string can also be specified, if the optional expression
2792 2793 is not found.
2793 2794 """
2794 2795 def __init__( self, exprs, default=_optionalNotMatched ):
2795 2796 super(Optional,self).__init__( exprs, savelist=False )
2796 2797 self.defaultValue = default
2797 2798 self.mayReturnEmpty = True
2798 2799
2799 2800 def parseImpl( self, instring, loc, doActions=True ):
2800 2801 try:
2801 2802 loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False )
2802 2803 except (ParseException,IndexError):
2803 2804 if self.defaultValue is not _optionalNotMatched:
2804 2805 if self.expr.resultsName:
2805 2806 tokens = ParseResults([ self.defaultValue ])
2806 2807 tokens[self.expr.resultsName] = self.defaultValue
2807 2808 else:
2808 2809 tokens = [ self.defaultValue ]
2809 2810 else:
2810 2811 tokens = []
2811 2812 return loc, tokens
2812 2813
2813 2814 def __str__( self ):
2814 2815 if hasattr(self,"name"):
2815 2816 return self.name
2816 2817
2817 2818 if self.strRepr is None:
2818 2819 self.strRepr = "[" + _ustr(self.expr) + "]"
2819 2820
2820 2821 return self.strRepr
2821 2822
2822 2823
2823 2824 class SkipTo(ParseElementEnhance):
2824 2825 """Token for skipping over all undefined text until the matched expression is found.
2825 2826 If include is set to true, the matched expression is also parsed (the skipped text
2826 2827 and matched expression are returned as a 2-element list). The ignore
2827 2828 argument is used to define grammars (typically quoted strings and comments) that
2828 2829 might contain false matches.
2829 2830 """
2830 2831 def __init__( self, other, include=False, ignore=None, failOn=None ):
2831 2832 super( SkipTo, self ).__init__( other )
2832 2833 self.ignoreExpr = ignore
2833 2834 self.mayReturnEmpty = True
2834 2835 self.mayIndexError = False
2835 2836 self.includeMatch = include
2836 2837 self.asList = False
2837 2838 if failOn is not None and isinstance(failOn, basestring):
2838 2839 self.failOn = Literal(failOn)
2839 2840 else:
2840 2841 self.failOn = failOn
2841 2842 self.errmsg = "No match found for "+_ustr(self.expr)
2842 2843 #self.myException = ParseException("",0,self.errmsg,self)
2843 2844
2844 2845 def parseImpl( self, instring, loc, doActions=True ):
2845 2846 startLoc = loc
2846 2847 instrlen = len(instring)
2847 2848 expr = self.expr
2848 2849 failParse = False
2849 2850 while loc <= instrlen:
2850 2851 try:
2851 2852 if self.failOn:
2852 2853 try:
2853 2854 self.failOn.tryParse(instring, loc)
2854 2855 except ParseBaseException:
2855 2856 pass
2856 2857 else:
2857 2858 failParse = True
2858 2859 raise ParseException(instring, loc, "Found expression " + str(self.failOn))
2859 2860 failParse = False
2860 2861 if self.ignoreExpr is not None:
2861 2862 while 1:
2862 2863 try:
2863 2864 loc = self.ignoreExpr.tryParse(instring,loc)
2864 2865 print "found ignoreExpr, advance to", loc
2865 2866 except ParseBaseException:
2866 2867 break
2867 2868 expr._parse( instring, loc, doActions=False, callPreParse=False )
2868 2869 skipText = instring[startLoc:loc]
2869 2870 if self.includeMatch:
2870 2871 loc,mat = expr._parse(instring,loc,doActions,callPreParse=False)
2871 2872 if mat:
2872 2873 skipRes = ParseResults( skipText )
2873 2874 skipRes += mat
2874 2875 return loc, [ skipRes ]
2875 2876 else:
2876 2877 return loc, [ skipText ]
2877 2878 else:
2878 2879 return loc, [ skipText ]
2879 2880 except (ParseException,IndexError):
2880 2881 if failParse:
2881 2882 raise
2882 2883 else:
2883 2884 loc += 1
2884 2885 exc = self.myException
2885 2886 exc.loc = loc
2886 2887 exc.pstr = instring
2887 2888 raise exc
2888 2889
2889 2890 class Forward(ParseElementEnhance):
2890 2891 """Forward declaration of an expression to be defined later -
2891 2892 used for recursive grammars, such as algebraic infix notation.
2892 2893 When the expression is known, it is assigned to the Forward variable using the '<<' operator.
2893 2894
2894 2895 Note: take care when assigning to Forward not to overlook precedence of operators.
2895 2896 Specifically, '|' has a lower precedence than '<<', so that::
2896 2897 fwdExpr << a | b | c
2897 2898 will actually be evaluated as::
2898 2899 (fwdExpr << a) | b | c
2899 2900 thereby leaving b and c out as parseable alternatives. It is recommended that you
2900 2901 explicitly group the values inserted into the Forward::
2901 2902 fwdExpr << (a | b | c)
2902 2903 """
2903 2904 def __init__( self, other=None ):
2904 2905 super(Forward,self).__init__( other, savelist=False )
2905 2906
2906 2907 def __lshift__( self, other ):
2907 2908 if isinstance( other, basestring ):
2908 2909 other = Literal(other)
2909 2910 self.expr = other
2910 2911 self.mayReturnEmpty = other.mayReturnEmpty
2911 2912 self.strRepr = None
2912 2913 self.mayIndexError = self.expr.mayIndexError
2913 2914 self.mayReturnEmpty = self.expr.mayReturnEmpty
2914 2915 self.setWhitespaceChars( self.expr.whiteChars )
2915 2916 self.skipWhitespace = self.expr.skipWhitespace
2916 2917 self.saveAsList = self.expr.saveAsList
2917 2918 self.ignoreExprs.extend(self.expr.ignoreExprs)
2918 2919 return None
2919 2920
2920 2921 def leaveWhitespace( self ):
2921 2922 self.skipWhitespace = False
2922 2923 return self
2923 2924
2924 2925 def streamline( self ):
2925 2926 if not self.streamlined:
2926 2927 self.streamlined = True
2927 2928 if self.expr is not None:
2928 2929 self.expr.streamline()
2929 2930 return self
2930 2931
2931 2932 def validate( self, validateTrace=[] ):
2932 2933 if self not in validateTrace:
2933 2934 tmp = validateTrace[:]+[self]
2934 2935 if self.expr is not None:
2935 2936 self.expr.validate(tmp)
2936 2937 self.checkRecursion([])
2937 2938
2938 2939 def __str__( self ):
2939 2940 if hasattr(self,"name"):
2940 2941 return self.name
2941 2942
2942 2943 self._revertClass = self.__class__
2943 2944 self.__class__ = _ForwardNoRecurse
2944 2945 try:
2945 2946 if self.expr is not None:
2946 2947 retString = _ustr(self.expr)
2947 2948 else:
2948 2949 retString = "None"
2949 2950 finally:
2950 2951 self.__class__ = self._revertClass
2951 2952 return self.__class__.__name__ + ": " + retString
2952 2953
2953 2954 def copy(self):
2954 2955 if self.expr is not None:
2955 2956 return super(Forward,self).copy()
2956 2957 else:
2957 2958 ret = Forward()
2958 2959 ret << self
2959 2960 return ret
2960 2961
2961 2962 class _ForwardNoRecurse(Forward):
2962 2963 def __str__( self ):
2963 2964 return "..."
2964 2965
2965 2966 class TokenConverter(ParseElementEnhance):
2966 2967 """Abstract subclass of ParseExpression, for converting parsed results."""
2967 2968 def __init__( self, expr, savelist=False ):
2968 2969 super(TokenConverter,self).__init__( expr )#, savelist )
2969 2970 self.saveAsList = False
2970 2971
2971 2972 class Upcase(TokenConverter):
2972 2973 """Converter to upper case all matching tokens."""
2973 2974 def __init__(self, *args):
2974 2975 super(Upcase,self).__init__(*args)
2975 2976 warnings.warn("Upcase class is deprecated, use upcaseTokens parse action instead",
2976 2977 DeprecationWarning,stacklevel=2)
2977 2978
2978 2979 def postParse( self, instring, loc, tokenlist ):
2979 2980 return list(map( string.upper, tokenlist ))
2980 2981
2981 2982
2982 2983 class Combine(TokenConverter):
2983 2984 """Converter to concatenate all matching tokens to a single string.
2984 2985 By default, the matching patterns must also be contiguous in the input string;
2985 2986 this can be disabled by specifying 'adjacent=False' in the constructor.
2986 2987 """
2987 2988 def __init__( self, expr, joinString="", adjacent=True ):
2988 2989 super(Combine,self).__init__( expr )
2989 2990 # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself
2990 2991 if adjacent:
2991 2992 self.leaveWhitespace()
2992 2993 self.adjacent = adjacent
2993 2994 self.skipWhitespace = True
2994 2995 self.joinString = joinString
2995 2996
2996 2997 def ignore( self, other ):
2997 2998 if self.adjacent:
2998 2999 ParserElement.ignore(self, other)
2999 3000 else:
3000 3001 super( Combine, self).ignore( other )
3001 3002 return self
3002 3003
3003 3004 def postParse( self, instring, loc, tokenlist ):
3004 3005 retToks = tokenlist.copy()
3005 3006 del retToks[:]
3006 3007 retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults)
3007 3008
3008 3009 if self.resultsName and len(retToks.keys())>0:
3009 3010 return [ retToks ]
3010 3011 else:
3011 3012 return retToks
3012 3013
3013 3014 class Group(TokenConverter):
3014 3015 """Converter to return the matched tokens as a list - useful for returning tokens of ZeroOrMore and OneOrMore expressions."""
3015 3016 def __init__( self, expr ):
3016 3017 super(Group,self).__init__( expr )
3017 3018 self.saveAsList = True
3018 3019
3019 3020 def postParse( self, instring, loc, tokenlist ):
3020 3021 return [ tokenlist ]
3021 3022
3022 3023 class Dict(TokenConverter):
3023 3024 """Converter to return a repetitive expression as a list, but also as a dictionary.
3024 3025 Each element can also be referenced using the first token in the expression as its key.
3025 3026 Useful for tabular report scraping when the first column can be used as a item key.
3026 3027 """
3027 3028 def __init__( self, exprs ):
3028 3029 super(Dict,self).__init__( exprs )
3029 3030 self.saveAsList = True
3030 3031
3031 3032 def postParse( self, instring, loc, tokenlist ):
3032 3033 for i,tok in enumerate(tokenlist):
3033 3034 if len(tok) == 0:
3034 3035 continue
3035 3036 ikey = tok[0]
3036 3037 if isinstance(ikey,int):
3037 3038 ikey = _ustr(tok[0]).strip()
3038 3039 if len(tok)==1:
3039 3040 tokenlist[ikey] = _ParseResultsWithOffset("",i)
3040 3041 elif len(tok)==2 and not isinstance(tok[1],ParseResults):
3041 3042 tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i)
3042 3043 else:
3043 3044 dictvalue = tok.copy() #ParseResults(i)
3044 3045 del dictvalue[0]
3045 3046 if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.keys()):
3046 3047 tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i)
3047 3048 else:
3048 3049 tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i)
3049 3050
3050 3051 if self.resultsName:
3051 3052 return [ tokenlist ]
3052 3053 else:
3053 3054 return tokenlist
3054 3055
3055 3056
3056 3057 class Suppress(TokenConverter):
3057 3058 """Converter for ignoring the results of a parsed expression."""
3058 3059 def postParse( self, instring, loc, tokenlist ):
3059 3060 return []
3060 3061
3061 3062 def suppress( self ):
3062 3063 return self
3063 3064
3064 3065
3065 3066 class OnlyOnce(object):
3066 3067 """Wrapper for parse actions, to ensure they are only called once."""
3067 3068 def __init__(self, methodCall):
3068 3069 self.callable = ParserElement._normalizeParseActionArgs(methodCall)
3069 3070 self.called = False
3070 3071 def __call__(self,s,l,t):
3071 3072 if not self.called:
3072 3073 results = self.callable(s,l,t)
3073 3074 self.called = True
3074 3075 return results
3075 3076 raise ParseException(s,l,"")
3076 3077 def reset(self):
3077 3078 self.called = False
3078 3079
3079 3080 def traceParseAction(f):
3080 3081 """Decorator for debugging parse actions."""
3081 3082 f = ParserElement._normalizeParseActionArgs(f)
3082 3083 def z(*paArgs):
3083 3084 thisFunc = f.func_name
3084 3085 s,l,t = paArgs[-3:]
3085 3086 if len(paArgs)>3:
3086 3087 thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc
3087 3088 sys.stderr.write( ">>entering %s(line: '%s', %d, %s)\n" % (thisFunc,line(l,s),l,t) )
3088 3089 try:
3089 3090 ret = f(*paArgs)
3090 3091 except Exception, exc:
3091 3092 sys.stderr.write( "<<leaving %s (exception: %s)\n" % (thisFunc,exc) )
3092 3093 raise
3093 3094 sys.stderr.write( "<<leaving %s (ret: %s)\n" % (thisFunc,ret) )
3094 3095 return ret
3095 3096 try:
3096 3097 z.__name__ = f.__name__
3097 3098 except AttributeError:
3098 3099 pass
3099 3100 return z
3100 3101
3101 3102 #
3102 3103 # global helpers
3103 3104 #
3104 3105 def delimitedList( expr, delim=",", combine=False ):
3105 3106 """Helper to define a delimited list of expressions - the delimiter defaults to ','.
3106 3107 By default, the list elements and delimiters can have intervening whitespace, and
3107 3108 comments, but this can be overridden by passing 'combine=True' in the constructor.
3108 3109 If combine is set to True, the matching tokens are returned as a single token
3109 3110 string, with the delimiters included; otherwise, the matching tokens are returned
3110 3111 as a list of tokens, with the delimiters suppressed.
3111 3112 """
3112 3113 dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..."
3113 3114 if combine:
3114 3115 return Combine( expr + ZeroOrMore( delim + expr ) ).setName(dlName)
3115 3116 else:
3116 3117 return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName)
3117 3118
3118 3119 def countedArray( expr ):
3119 3120 """Helper to define a counted list of expressions.
3120 3121 This helper defines a pattern of the form::
3121 3122 integer expr expr expr...
3122 3123 where the leading integer tells how many expr expressions follow.
3123 3124 The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed.
3124 3125 """
3125 3126 arrayExpr = Forward()
3126 3127 def countFieldParseAction(s,l,t):
3127 3128 n = int(t[0])
3128 3129 arrayExpr << (n and Group(And([expr]*n)) or Group(empty))
3129 3130 return []
3130 3131 return ( Word(nums).setName("arrayLen").setParseAction(countFieldParseAction, callDuringTry=True) + arrayExpr )
3131 3132
3132 3133 def _flatten(L):
3133 3134 if type(L) is not list: return [L]
3134 3135 if L == []: return L
3135 3136 return _flatten(L[0]) + _flatten(L[1:])
3136 3137
3137 3138 def matchPreviousLiteral(expr):
3138 3139 """Helper to define an expression that is indirectly defined from
3139 3140 the tokens matched in a previous expression, that is, it looks
3140 3141 for a 'repeat' of a previous expression. For example::
3141 3142 first = Word(nums)
3142 3143 second = matchPreviousLiteral(first)
3143 3144 matchExpr = first + ":" + second
3144 3145 will match "1:1", but not "1:2". Because this matches a
3145 3146 previous literal, will also match the leading "1:1" in "1:10".
3146 3147 If this is not desired, use matchPreviousExpr.
3147 3148 Do *not* use with packrat parsing enabled.
3148 3149 """
3149 3150 rep = Forward()
3150 3151 def copyTokenToRepeater(s,l,t):
3151 3152 if t:
3152 3153 if len(t) == 1:
3153 3154 rep << t[0]
3154 3155 else:
3155 3156 # flatten t tokens
3156 3157 tflat = _flatten(t.asList())
3157 3158 rep << And( [ Literal(tt) for tt in tflat ] )
3158 3159 else:
3159 3160 rep << Empty()
3160 3161 expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
3161 3162 return rep
3162 3163
3163 3164 def matchPreviousExpr(expr):
3164 3165 """Helper to define an expression that is indirectly defined from
3165 3166 the tokens matched in a previous expression, that is, it looks
3166 3167 for a 'repeat' of a previous expression. For example::
3167 3168 first = Word(nums)
3168 3169 second = matchPreviousExpr(first)
3169 3170 matchExpr = first + ":" + second
3170 3171 will match "1:1", but not "1:2". Because this matches by
3171 3172 expressions, will *not* match the leading "1:1" in "1:10";
3172 3173 the expressions are evaluated first, and then compared, so
3173 3174 "1" is compared with "10".
3174 3175 Do *not* use with packrat parsing enabled.
3175 3176 """
3176 3177 rep = Forward()
3177 3178 e2 = expr.copy()
3178 3179 rep << e2
3179 3180 def copyTokenToRepeater(s,l,t):
3180 3181 matchTokens = _flatten(t.asList())
3181 3182 def mustMatchTheseTokens(s,l,t):
3182 3183 theseTokens = _flatten(t.asList())
3183 3184 if theseTokens != matchTokens:
3184 3185 raise ParseException("",0,"")
3185 3186 rep.setParseAction( mustMatchTheseTokens, callDuringTry=True )
3186 3187 expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
3187 3188 return rep
3188 3189
3189 3190 def _escapeRegexRangeChars(s):
3190 3191 #~ escape these chars: ^-]
3191 3192 for c in r"\^-]":
3192 3193 s = s.replace(c,_bslash+c)
3193 3194 s = s.replace("\n",r"\n")
3194 3195 s = s.replace("\t",r"\t")
3195 3196 return _ustr(s)
3196 3197
3197 3198 def oneOf( strs, caseless=False, useRegex=True ):
3198 3199 """Helper to quickly define a set of alternative Literals, and makes sure to do
3199 3200 longest-first testing when there is a conflict, regardless of the input order,
3200 3201 but returns a MatchFirst for best performance.
3201 3202
3202 3203 Parameters:
3203 3204 - strs - a string of space-delimited literals, or a list of string literals
3204 3205 - caseless - (default=False) - treat all literals as caseless
3205 3206 - useRegex - (default=True) - as an optimization, will generate a Regex
3206 3207 object; otherwise, will generate a MatchFirst object (if caseless=True, or
3207 3208 if creating a Regex raises an exception)
3208 3209 """
3209 3210 if caseless:
3210 3211 isequal = ( lambda a,b: a.upper() == b.upper() )
3211 3212 masks = ( lambda a,b: b.upper().startswith(a.upper()) )
3212 3213 parseElementClass = CaselessLiteral
3213 3214 else:
3214 3215 isequal = ( lambda a,b: a == b )
3215 3216 masks = ( lambda a,b: b.startswith(a) )
3216 3217 parseElementClass = Literal
3217 3218
3218 3219 if isinstance(strs,(list,tuple)):
3219 3220 symbols = list(strs[:])
3220 3221 elif isinstance(strs,basestring):
3221 3222 symbols = strs.split()
3222 3223 else:
3223 3224 warnings.warn("Invalid argument to oneOf, expected string or list",
3224 3225 SyntaxWarning, stacklevel=2)
3225 3226
3226 3227 i = 0
3227 3228 while i < len(symbols)-1:
3228 3229 cur = symbols[i]
3229 3230 for j,other in enumerate(symbols[i+1:]):
3230 3231 if ( isequal(other, cur) ):
3231 3232 del symbols[i+j+1]
3232 3233 break
3233 3234 elif ( masks(cur, other) ):
3234 3235 del symbols[i+j+1]
3235 3236 symbols.insert(i,other)
3236 3237 cur = other
3237 3238 break
3238 3239 else:
3239 3240 i += 1
3240 3241
3241 3242 if not caseless and useRegex:
3242 3243 #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] ))
3243 3244 try:
3244 3245 if len(symbols)==len("".join(symbols)):
3245 3246 return Regex( "[%s]" % "".join( [ _escapeRegexRangeChars(sym) for sym in symbols] ) )
3246 3247 else:
3247 3248 return Regex( "|".join( [ re.escape(sym) for sym in symbols] ) )
3248 3249 except:
3249 3250 warnings.warn("Exception creating Regex for oneOf, building MatchFirst",
3250 3251 SyntaxWarning, stacklevel=2)
3251 3252
3252 3253
3253 3254 # last resort, just use MatchFirst
3254 3255 return MatchFirst( [ parseElementClass(sym) for sym in symbols ] )
3255 3256
3256 3257 def dictOf( key, value ):
3257 3258 """Helper to easily and clearly define a dictionary by specifying the respective patterns
3258 3259 for the key and value. Takes care of defining the Dict, ZeroOrMore, and Group tokens
3259 3260 in the proper order. The key pattern can include delimiting markers or punctuation,
3260 3261 as long as they are suppressed, thereby leaving the significant key text. The value
3261 3262 pattern can include named results, so that the Dict results can include named token
3262 3263 fields.
3263 3264 """
3264 3265 return Dict( ZeroOrMore( Group ( key + value ) ) )
3265 3266
3266 3267 def originalTextFor(expr, asString=True):
3267 3268 """Helper to return the original, untokenized text for a given expression. Useful to
3268 3269 restore the parsed fields of an HTML start tag into the raw tag text itself, or to
3269 3270 revert separate tokens with intervening whitespace back to the original matching
3270 3271 input text. Simpler to use than the parse action keepOriginalText, and does not
3271 3272 require the inspect module to chase up the call stack. By default, returns a
3272 3273 string containing the original parsed text.
3273 3274
3274 3275 If the optional asString argument is passed as False, then the return value is a
3275 3276 ParseResults containing any results names that were originally matched, and a
3276 3277 single token containing the original matched text from the input string. So if
3277 3278 the expression passed to originalTextFor contains expressions with defined
3278 3279 results names, you must set asString to False if you want to preserve those
3279 3280 results name values."""
3280 3281 locMarker = Empty().setParseAction(lambda s,loc,t: loc)
3281 3282 matchExpr = locMarker("_original_start") + expr + locMarker("_original_end")
3282 3283 if asString:
3283 3284 extractText = lambda s,l,t: s[t._original_start:t._original_end]
3284 3285 else:
3285 3286 def extractText(s,l,t):
3286 3287 del t[:]
3287 3288 t.insert(0, s[t._original_start:t._original_end])
3288 3289 del t["_original_start"]
3289 3290 del t["_original_end"]
3290 3291 matchExpr.setParseAction(extractText)
3291 3292 return matchExpr
3292 3293
3293 3294 # convenience constants for positional expressions
3294 3295 empty = Empty().setName("empty")
3295 3296 lineStart = LineStart().setName("lineStart")
3296 3297 lineEnd = LineEnd().setName("lineEnd")
3297 3298 stringStart = StringStart().setName("stringStart")
3298 3299 stringEnd = StringEnd().setName("stringEnd")
3299 3300
3300 3301 _escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1])
3301 3302 _printables_less_backslash = "".join([ c for c in printables if c not in r"\]" ])
3302 3303 _escapedHexChar = Combine( Suppress(_bslash + "0x") + Word(hexnums) ).setParseAction(lambda s,l,t:unichr(int(t[0],16)))
3303 3304 _escapedOctChar = Combine( Suppress(_bslash) + Word("0","01234567") ).setParseAction(lambda s,l,t:unichr(int(t[0],8)))
3304 3305 _singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | Word(_printables_less_backslash,exact=1)
3305 3306 _charRange = Group(_singleChar + Suppress("-") + _singleChar)
3306 3307 _reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]"
3307 3308
3308 3309 _expanded = lambda p: (isinstance(p,ParseResults) and ''.join([ unichr(c) for c in range(ord(p[0]),ord(p[1])+1) ]) or p)
3309 3310
3310 3311 def srange(s):
3311 3312 r"""Helper to easily define string ranges for use in Word construction. Borrows
3312 3313 syntax from regexp '[]' string range definitions::
3313 3314 srange("[0-9]") -> "0123456789"
3314 3315 srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz"
3315 3316 srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_"
3316 3317 The input string must be enclosed in []'s, and the returned string is the expanded
3317 3318 character set joined into a single string.
3318 3319 The values enclosed in the []'s may be::
3319 3320 a single character
3320 3321 an escaped character with a leading backslash (such as \- or \])
3321 3322 an escaped hex character with a leading '\0x' (\0x21, which is a '!' character)
3322 3323 an escaped octal character with a leading '\0' (\041, which is a '!' character)
3323 3324 a range of any of the above, separated by a dash ('a-z', etc.)
3324 3325 any combination of the above ('aeiouy', 'a-zA-Z0-9_$', etc.)
3325 3326 """
3326 3327 try:
3327 3328 return "".join([_expanded(part) for part in _reBracketExpr.parseString(s).body])
3328 3329 except:
3329 3330 return ""
3330 3331
3331 3332 def matchOnlyAtCol(n):
3332 3333 """Helper method for defining parse actions that require matching at a specific
3333 3334 column in the input text.
3334 3335 """
3335 3336 def verifyCol(strg,locn,toks):
3336 3337 if col(locn,strg) != n:
3337 3338 raise ParseException(strg,locn,"matched token not at column %d" % n)
3338 3339 return verifyCol
3339 3340
3340 3341 def replaceWith(replStr):
3341 3342 """Helper method for common parse actions that simply return a literal value. Especially
3342 3343 useful when used with transformString().
3343 3344 """
3344 3345 def _replFunc(*args):
3345 3346 return [replStr]
3346 3347 return _replFunc
3347 3348
3348 3349 def removeQuotes(s,l,t):
3349 3350 """Helper parse action for removing quotation marks from parsed quoted strings.
3350 3351 To use, add this parse action to quoted string using::
3351 3352 quotedString.setParseAction( removeQuotes )
3352 3353 """
3353 3354 return t[0][1:-1]
3354 3355
3355 3356 def upcaseTokens(s,l,t):
3356 3357 """Helper parse action to convert tokens to upper case."""
3357 3358 return [ tt.upper() for tt in map(_ustr,t) ]
3358 3359
3359 3360 def downcaseTokens(s,l,t):
3360 3361 """Helper parse action to convert tokens to lower case."""
3361 3362 return [ tt.lower() for tt in map(_ustr,t) ]
3362 3363
3363 3364 def keepOriginalText(s,startLoc,t):
3364 3365 """Helper parse action to preserve original parsed text,
3365 3366 overriding any nested parse actions."""
3366 3367 try:
3367 3368 endloc = getTokensEndLoc()
3368 3369 except ParseException:
3369 3370 raise ParseFatalException("incorrect usage of keepOriginalText - may only be called as a parse action")
3370 3371 del t[:]
3371 3372 t += ParseResults(s[startLoc:endloc])
3372 3373 return t
3373 3374
3374 3375 def getTokensEndLoc():
3375 3376 """Method to be called from within a parse action to determine the end
3376 3377 location of the parsed tokens."""
3377 3378 import inspect
3378 3379 fstack = inspect.stack()
3379 3380 try:
3380 3381 # search up the stack (through intervening argument normalizers) for correct calling routine
3381 3382 for f in fstack[2:]:
3382 3383 if f[3] == "_parseNoCache":
3383 3384 endloc = f[0].f_locals["loc"]
3384 3385 return endloc
3385 3386 else:
3386 3387 raise ParseFatalException("incorrect usage of getTokensEndLoc - may only be called from within a parse action")
3387 3388 finally:
3388 3389 del fstack
3389 3390
3390 3391 def _makeTags(tagStr, xml):
3391 3392 """Internal helper to construct opening and closing tag expressions, given a tag name"""
3392 3393 if isinstance(tagStr,basestring):
3393 3394 resname = tagStr
3394 3395 tagStr = Keyword(tagStr, caseless=not xml)
3395 3396 else:
3396 3397 resname = tagStr.name
3397 3398
3398 3399 tagAttrName = Word(alphas,alphanums+"_-:")
3399 3400 if (xml):
3400 3401 tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes )
3401 3402 openTag = Suppress("<") + tagStr + \
3402 3403 Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \
3403 3404 Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
3404 3405 else:
3405 3406 printablesLessRAbrack = "".join( [ c for c in printables if c not in ">" ] )
3406 3407 tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack)
3407 3408 openTag = Suppress("<") + tagStr + \
3408 3409 Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \
3409 3410 Optional( Suppress("=") + tagAttrValue ) ))) + \
3410 3411 Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
3411 3412 closeTag = Combine(_L("</") + tagStr + ">")
3412 3413
3413 3414 openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % tagStr)
3414 3415 closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("</%s>" % tagStr)
3415 3416
3416 3417 return openTag, closeTag
3417 3418
3418 3419 def makeHTMLTags(tagStr):
3419 3420 """Helper to construct opening and closing tag expressions for HTML, given a tag name"""
3420 3421 return _makeTags( tagStr, False )
3421 3422
3422 3423 def makeXMLTags(tagStr):
3423 3424 """Helper to construct opening and closing tag expressions for XML, given a tag name"""
3424 3425 return _makeTags( tagStr, True )
3425 3426
3426 3427 def withAttribute(*args,**attrDict):
3427 3428 """Helper to create a validating parse action to be used with start tags created
3428 3429 with makeXMLTags or makeHTMLTags. Use withAttribute to qualify a starting tag
3429 3430 with a required attribute value, to avoid false matches on common tags such as
3430 3431 <TD> or <DIV>.
3431 3432
3432 3433 Call withAttribute with a series of attribute names and values. Specify the list
3433 3434 of filter attributes names and values as:
3434 3435 - keyword arguments, as in (class="Customer",align="right"), or
3435 3436 - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") )
3436 3437 For attribute names with a namespace prefix, you must use the second form. Attribute
3437 3438 names are matched insensitive to upper/lower case.
3438 3439
3439 3440 To verify that the attribute exists, but without specifying a value, pass
3440 3441 withAttribute.ANY_VALUE as the value.
3441 3442 """
3442 3443 if args:
3443 3444 attrs = args[:]
3444 3445 else:
3445 attrs = attrDict.items()
3446 attrs = attrDict.iteritems()
3446 3447 attrs = [(k,v) for k,v in attrs]
3447 3448 def pa(s,l,tokens):
3448 3449 for attrName,attrValue in attrs:
3449 3450 if attrName not in tokens:
3450 3451 raise ParseException(s,l,"no matching attribute " + attrName)
3451 3452 if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue:
3452 3453 raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" %
3453 3454 (attrName, tokens[attrName], attrValue))
3454 3455 return pa
3455 3456 withAttribute.ANY_VALUE = object()
3456 3457
3457 3458 opAssoc = _Constants()
3458 3459 opAssoc.LEFT = object()
3459 3460 opAssoc.RIGHT = object()
3460 3461
3461 3462 def operatorPrecedence( baseExpr, opList ):
3462 3463 """Helper method for constructing grammars of expressions made up of
3463 3464 operators working in a precedence hierarchy. Operators may be unary or
3464 3465 binary, left- or right-associative. Parse actions can also be attached
3465 3466 to operator expressions.
3466 3467
3467 3468 Parameters:
3468 3469 - baseExpr - expression representing the most basic element for the nested
3469 3470 - opList - list of tuples, one for each operator precedence level in the
3470 3471 expression grammar; each tuple is of the form
3471 3472 (opExpr, numTerms, rightLeftAssoc, parseAction), where:
3472 3473 - opExpr is the pyparsing expression for the operator;
3473 3474 may also be a string, which will be converted to a Literal;
3474 3475 if numTerms is 3, opExpr is a tuple of two expressions, for the
3475 3476 two operators separating the 3 terms
3476 3477 - numTerms is the number of terms for this operator (must
3477 3478 be 1, 2, or 3)
3478 3479 - rightLeftAssoc is the indicator whether the operator is
3479 3480 right or left associative, using the pyparsing-defined
3480 3481 constants opAssoc.RIGHT and opAssoc.LEFT.
3481 3482 - parseAction is the parse action to be associated with
3482 3483 expressions matching this operator expression (the
3483 3484 parse action tuple member may be omitted)
3484 3485 """
3485 3486 ret = Forward()
3486 3487 lastExpr = baseExpr | ( Suppress('(') + ret + Suppress(')') )
3487 3488 for i,operDef in enumerate(opList):
3488 3489 opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4]
3489 3490 if arity == 3:
3490 3491 if opExpr is None or len(opExpr) != 2:
3491 3492 raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions")
3492 3493 opExpr1, opExpr2 = opExpr
3493 3494 thisExpr = Forward()#.setName("expr%d" % i)
3494 3495 if rightLeftAssoc == opAssoc.LEFT:
3495 3496 if arity == 1:
3496 3497 matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) )
3497 3498 elif arity == 2:
3498 3499 if opExpr is not None:
3499 3500 matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) )
3500 3501 else:
3501 3502 matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) )
3502 3503 elif arity == 3:
3503 3504 matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \
3504 3505 Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr )
3505 3506 else:
3506 3507 raise ValueError("operator must be unary (1), binary (2), or ternary (3)")
3507 3508 elif rightLeftAssoc == opAssoc.RIGHT:
3508 3509 if arity == 1:
3509 3510 # try to avoid LR with this extra test
3510 3511 if not isinstance(opExpr, Optional):
3511 3512 opExpr = Optional(opExpr)
3512 3513 matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr )
3513 3514 elif arity == 2:
3514 3515 if opExpr is not None:
3515 3516 matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) )
3516 3517 else:
3517 3518 matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) )
3518 3519 elif arity == 3:
3519 3520 matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \
3520 3521 Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr )
3521 3522 else:
3522 3523 raise ValueError("operator must be unary (1), binary (2), or ternary (3)")
3523 3524 else:
3524 3525 raise ValueError("operator must indicate right or left associativity")
3525 3526 if pa:
3526 3527 matchExpr.setParseAction( pa )
3527 3528 thisExpr << ( matchExpr | lastExpr )
3528 3529 lastExpr = thisExpr
3529 3530 ret << lastExpr
3530 3531 return ret
3531 3532
3532 3533 dblQuotedString = Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*"').setName("string enclosed in double quotes")
3533 3534 sglQuotedString = Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*'").setName("string enclosed in single quotes")
3534 3535 quotedString = Regex(r'''(?:"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*")|(?:'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*')''').setName("quotedString using single or double quotes")
3535 3536 unicodeString = Combine(_L('u') + quotedString.copy())
3536 3537
3537 3538 def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString):
3538 3539 """Helper method for defining nested lists enclosed in opening and closing
3539 3540 delimiters ("(" and ")" are the default).
3540 3541
3541 3542 Parameters:
3542 3543 - opener - opening character for a nested list (default="("); can also be a pyparsing expression
3543 3544 - closer - closing character for a nested list (default=")"); can also be a pyparsing expression
3544 3545 - content - expression for items within the nested lists (default=None)
3545 3546 - ignoreExpr - expression for ignoring opening and closing delimiters (default=quotedString)
3546 3547
3547 3548 If an expression is not provided for the content argument, the nested
3548 3549 expression will capture all whitespace-delimited content between delimiters
3549 3550 as a list of separate values.
3550 3551
3551 3552 Use the ignoreExpr argument to define expressions that may contain
3552 3553 opening or closing characters that should not be treated as opening
3553 3554 or closing characters for nesting, such as quotedString or a comment
3554 3555 expression. Specify multiple expressions using an Or or MatchFirst.
3555 3556 The default is quotedString, but if no expressions are to be ignored,
3556 3557 then pass None for this argument.
3557 3558 """
3558 3559 if opener == closer:
3559 3560 raise ValueError("opening and closing strings cannot be the same")
3560 3561 if content is None:
3561 3562 if isinstance(opener,basestring) and isinstance(closer,basestring):
3562 3563 if len(opener) == 1 and len(closer)==1:
3563 3564 if ignoreExpr is not None:
3564 3565 content = (Combine(OneOrMore(~ignoreExpr +
3565 3566 CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1))
3566 3567 ).setParseAction(lambda t:t[0].strip()))
3567 3568 else:
3568 3569 content = (empty+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS
3569 3570 ).setParseAction(lambda t:t[0].strip()))
3570 3571 else:
3571 3572 if ignoreExpr is not None:
3572 3573 content = (Combine(OneOrMore(~ignoreExpr +
3573 3574 ~Literal(opener) + ~Literal(closer) +
3574 3575 CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1))
3575 3576 ).setParseAction(lambda t:t[0].strip()))
3576 3577 else:
3577 3578 content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) +
3578 3579 CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1))
3579 3580 ).setParseAction(lambda t:t[0].strip()))
3580 3581 else:
3581 3582 raise ValueError("opening and closing arguments must be strings if no content expression is given")
3582 3583 ret = Forward()
3583 3584 if ignoreExpr is not None:
3584 3585 ret << Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) )
3585 3586 else:
3586 3587 ret << Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) )
3587 3588 return ret
3588 3589
3589 3590 def indentedBlock(blockStatementExpr, indentStack, indent=True):
3590 3591 """Helper method for defining space-delimited indentation blocks, such as
3591 3592 those used to define block statements in Python source code.
3592 3593
3593 3594 Parameters:
3594 3595 - blockStatementExpr - expression defining syntax of statement that
3595 3596 is repeated within the indented block
3596 3597 - indentStack - list created by caller to manage indentation stack
3597 3598 (multiple statementWithIndentedBlock expressions within a single grammar
3598 3599 should share a common indentStack)
3599 3600 - indent - boolean indicating whether block must be indented beyond the
3600 3601 the current level; set to False for block of left-most statements
3601 3602 (default=True)
3602 3603
3603 3604 A valid block must contain at least one blockStatement.
3604 3605 """
3605 3606 def checkPeerIndent(s,l,t):
3606 3607 if l >= len(s): return
3607 3608 curCol = col(l,s)
3608 3609 if curCol != indentStack[-1]:
3609 3610 if curCol > indentStack[-1]:
3610 3611 raise ParseFatalException(s,l,"illegal nesting")
3611 3612 raise ParseException(s,l,"not a peer entry")
3612 3613
3613 3614 def checkSubIndent(s,l,t):
3614 3615 curCol = col(l,s)
3615 3616 if curCol > indentStack[-1]:
3616 3617 indentStack.append( curCol )
3617 3618 else:
3618 3619 raise ParseException(s,l,"not a subentry")
3619 3620
3620 3621 def checkUnindent(s,l,t):
3621 3622 if l >= len(s): return
3622 3623 curCol = col(l,s)
3623 3624 if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]):
3624 3625 raise ParseException(s,l,"not an unindent")
3625 3626 indentStack.pop()
3626 3627
3627 3628 NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress())
3628 3629 INDENT = Empty() + Empty().setParseAction(checkSubIndent)
3629 3630 PEER = Empty().setParseAction(checkPeerIndent)
3630 3631 UNDENT = Empty().setParseAction(checkUnindent)
3631 3632 if indent:
3632 3633 smExpr = Group( Optional(NL) +
3633 3634 FollowedBy(blockStatementExpr) +
3634 3635 INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT)
3635 3636 else:
3636 3637 smExpr = Group( Optional(NL) +
3637 3638 (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) )
3638 3639 blockStatementExpr.ignore(_bslash + LineEnd())
3639 3640 return smExpr
3640 3641
3641 3642 alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]")
3642 3643 punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]")
3643 3644
3644 3645 anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:"))
3645 3646 commonHTMLEntity = Combine(_L("&") + oneOf("gt lt amp nbsp quot").setResultsName("entity") +";").streamline()
3646 3647 _htmlEntityMap = dict(zip("gt lt amp nbsp quot".split(),'><& "'))
3647 3648 replaceHTMLEntity = lambda t : t.entity in _htmlEntityMap and _htmlEntityMap[t.entity] or None
3648 3649
3649 3650 # it's easy to get these comment structures wrong - they're very common, so may as well make them available
3650 3651 cStyleComment = Regex(r"/\*(?:[^*]*\*+)+?/").setName("C style comment")
3651 3652
3652 3653 htmlComment = Regex(r"<!--[\s\S]*?-->")
3653 3654 restOfLine = Regex(r".*").leaveWhitespace()
3654 3655 dblSlashComment = Regex(r"\/\/(\\\n|.)*").setName("// comment")
3655 3656 cppStyleComment = Regex(r"/(?:\*(?:[^*]*\*+)+?/|/[^\n]*(?:\n[^\n]*)*?(?:(?<!\\)|\Z))").setName("C++ style comment")
3656 3657
3657 3658 javaStyleComment = cppStyleComment
3658 3659 pythonStyleComment = Regex(r"#.*").setName("Python style comment")
3659 3660 _noncomma = "".join( [ c for c in printables if c != "," ] )
3660 3661 _commasepitem = Combine(OneOrMore(Word(_noncomma) +
3661 3662 Optional( Word(" \t") +
3662 3663 ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem")
3663 3664 commaSeparatedList = delimitedList( Optional( quotedString | _commasepitem, default="") ).setName("commaSeparatedList")
3664 3665
3665 3666
3666 3667 if __name__ == "__main__":
3667 3668
3668 3669 def test( teststring ):
3669 3670 try:
3670 3671 tokens = simpleSQL.parseString( teststring )
3671 3672 tokenlist = tokens.asList()
3672 3673 print (teststring + "->" + str(tokenlist))
3673 3674 print ("tokens = " + str(tokens))
3674 3675 print ("tokens.columns = " + str(tokens.columns))
3675 3676 print ("tokens.tables = " + str(tokens.tables))
3676 3677 print (tokens.asXML("SQL",True))
3677 3678 except ParseBaseException,err:
3678 3679 print (teststring + "->")
3679 3680 print (err.line)
3680 3681 print (" "*(err.column-1) + "^")
3681 3682 print (err)
3682 3683 print()
3683 3684
3684 3685 selectToken = CaselessLiteral( "select" )
3685 3686 fromToken = CaselessLiteral( "from" )
3686 3687
3687 3688 ident = Word( alphas, alphanums + "_$" )
3688 3689 columnName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens )
3689 3690 columnNameList = Group( delimitedList( columnName ) )#.setName("columns")
3690 3691 tableName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens )
3691 3692 tableNameList = Group( delimitedList( tableName ) )#.setName("tables")
3692 3693 simpleSQL = ( selectToken + \
3693 3694 ( '*' | columnNameList ).setResultsName( "columns" ) + \
3694 3695 fromToken + \
3695 3696 tableNameList.setResultsName( "tables" ) )
3696 3697
3697 3698 test( "SELECT * from XYZZY, ABC" )
3698 3699 test( "select * from SYS.XYZZY" )
3699 3700 test( "Select A from Sys.dual" )
3700 3701 test( "Select AA,BB,CC from Sys.dual" )
3701 3702 test( "Select A, B, C from Sys.dual" )
3702 3703 test( "Select A, B, C from Sys.dual" )
3703 3704 test( "Xelect A, B, C from Sys.dual" )
3704 3705 test( "Select A, B, C frox Sys.dual" )
3705 3706 test( "Select" )
3706 3707 test( "Select ^^^ frox Sys.dual" )
3707 3708 test( "Select A, B, C from Sys.dual, Table2 " )
@@ -1,834 +1,834 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 Facilities for launching IPython processes asynchronously.
5 5 """
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008-2009 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17
18 18 import os
19 19 import re
20 20 import sys
21 21
22 22 from IPython.config.configurable import Configurable
23 23 from IPython.external import Itpl
24 24 from IPython.utils.traitlets import Str, Int, List, Unicode
25 25 from IPython.utils.path import get_ipython_module_path
26 26 from IPython.utils.process import find_cmd, pycmd2argv, FindCmdError
27 27 from IPython.kernel.twistedutil import (
28 28 gatherBoth,
29 29 make_deferred,
30 30 sleep_deferred
31 31 )
32 32 from IPython.kernel.winhpcjob import (
33 33 IPControllerTask, IPEngineTask,
34 34 IPControllerJob, IPEngineSetJob
35 35 )
36 36
37 37 from twisted.internet import reactor, defer
38 38 from twisted.internet.defer import inlineCallbacks
39 39 from twisted.internet.protocol import ProcessProtocol
40 40 from twisted.internet.utils import getProcessOutput
41 41 from twisted.internet.error import ProcessDone, ProcessTerminated
42 42 from twisted.python import log
43 43 from twisted.python.failure import Failure
44 44
45 45
46 46 #-----------------------------------------------------------------------------
47 47 # Paths to the kernel apps
48 48 #-----------------------------------------------------------------------------
49 49
50 50
51 51 ipcluster_cmd_argv = pycmd2argv(get_ipython_module_path(
52 52 'IPython.kernel.ipclusterapp'
53 53 ))
54 54
55 55 ipengine_cmd_argv = pycmd2argv(get_ipython_module_path(
56 56 'IPython.kernel.ipengineapp'
57 57 ))
58 58
59 59 ipcontroller_cmd_argv = pycmd2argv(get_ipython_module_path(
60 60 'IPython.kernel.ipcontrollerapp'
61 61 ))
62 62
63 63 #-----------------------------------------------------------------------------
64 64 # Base launchers and errors
65 65 #-----------------------------------------------------------------------------
66 66
67 67
68 68 class LauncherError(Exception):
69 69 pass
70 70
71 71
72 72 class ProcessStateError(LauncherError):
73 73 pass
74 74
75 75
76 76 class UnknownStatus(LauncherError):
77 77 pass
78 78
79 79
80 80 class BaseLauncher(Configurable):
81 81 """An asbtraction for starting, stopping and signaling a process."""
82 82
83 83 # In all of the launchers, the work_dir is where child processes will be
84 84 # run. This will usually be the cluster_dir, but may not be. any work_dir
85 85 # passed into the __init__ method will override the config value.
86 86 # This should not be used to set the work_dir for the actual engine
87 87 # and controller. Instead, use their own config files or the
88 88 # controller_args, engine_args attributes of the launchers to add
89 89 # the --work-dir option.
90 90 work_dir = Unicode(u'')
91 91
92 92 def __init__(self, work_dir=u'', config=None):
93 93 super(BaseLauncher, self).__init__(work_dir=work_dir, config=config)
94 94 self.state = 'before' # can be before, running, after
95 95 self.stop_deferreds = []
96 96 self.start_data = None
97 97 self.stop_data = None
98 98
99 99 @property
100 100 def args(self):
101 101 """A list of cmd and args that will be used to start the process.
102 102
103 103 This is what is passed to :func:`spawnProcess` and the first element
104 104 will be the process name.
105 105 """
106 106 return self.find_args()
107 107
108 108 def find_args(self):
109 109 """The ``.args`` property calls this to find the args list.
110 110
111 111 Subcommand should implement this to construct the cmd and args.
112 112 """
113 113 raise NotImplementedError('find_args must be implemented in a subclass')
114 114
115 115 @property
116 116 def arg_str(self):
117 117 """The string form of the program arguments."""
118 118 return ' '.join(self.args)
119 119
120 120 @property
121 121 def running(self):
122 122 """Am I running."""
123 123 if self.state == 'running':
124 124 return True
125 125 else:
126 126 return False
127 127
128 128 def start(self):
129 129 """Start the process.
130 130
131 131 This must return a deferred that fires with information about the
132 132 process starting (like a pid, job id, etc.).
133 133 """
134 134 return defer.fail(
135 135 Failure(NotImplementedError(
136 136 'start must be implemented in a subclass')
137 137 )
138 138 )
139 139
140 140 def stop(self):
141 141 """Stop the process and notify observers of stopping.
142 142
143 143 This must return a deferred that fires with information about the
144 144 processing stopping, like errors that occur while the process is
145 145 attempting to be shut down. This deferred won't fire when the process
146 146 actually stops. To observe the actual process stopping, see
147 147 :func:`observe_stop`.
148 148 """
149 149 return defer.fail(
150 150 Failure(NotImplementedError(
151 151 'stop must be implemented in a subclass')
152 152 )
153 153 )
154 154
155 155 def observe_stop(self):
156 156 """Get a deferred that will fire when the process stops.
157 157
158 158 The deferred will fire with data that contains information about
159 159 the exit status of the process.
160 160 """
161 161 if self.state=='after':
162 162 return defer.succeed(self.stop_data)
163 163 else:
164 164 d = defer.Deferred()
165 165 self.stop_deferreds.append(d)
166 166 return d
167 167
168 168 def notify_start(self, data):
169 169 """Call this to trigger startup actions.
170 170
171 171 This logs the process startup and sets the state to 'running'. It is
172 172 a pass-through so it can be used as a callback.
173 173 """
174 174
175 175 log.msg('Process %r started: %r' % (self.args[0], data))
176 176 self.start_data = data
177 177 self.state = 'running'
178 178 return data
179 179
180 180 def notify_stop(self, data):
181 181 """Call this to trigger process stop actions.
182 182
183 183 This logs the process stopping and sets the state to 'after'. Call
184 184 this to trigger all the deferreds from :func:`observe_stop`."""
185 185
186 186 log.msg('Process %r stopped: %r' % (self.args[0], data))
187 187 self.stop_data = data
188 188 self.state = 'after'
189 189 for i in range(len(self.stop_deferreds)):
190 190 d = self.stop_deferreds.pop()
191 191 d.callback(data)
192 192 return data
193 193
194 194 def signal(self, sig):
195 195 """Signal the process.
196 196
197 197 Return a semi-meaningless deferred after signaling the process.
198 198
199 199 Parameters
200 200 ----------
201 201 sig : str or int
202 202 'KILL', 'INT', etc., or any signal number
203 203 """
204 204 return defer.fail(
205 205 Failure(NotImplementedError(
206 206 'signal must be implemented in a subclass')
207 207 )
208 208 )
209 209
210 210
211 211 #-----------------------------------------------------------------------------
212 212 # Local process launchers
213 213 #-----------------------------------------------------------------------------
214 214
215 215
216 216 class LocalProcessLauncherProtocol(ProcessProtocol):
217 217 """A ProcessProtocol to go with the LocalProcessLauncher."""
218 218
219 219 def __init__(self, process_launcher):
220 220 self.process_launcher = process_launcher
221 221 self.pid = None
222 222
223 223 def connectionMade(self):
224 224 self.pid = self.transport.pid
225 225 self.process_launcher.notify_start(self.transport.pid)
226 226
227 227 def processEnded(self, status):
228 228 value = status.value
229 229 if isinstance(value, ProcessDone):
230 230 self.process_launcher.notify_stop(
231 231 {'exit_code':0,
232 232 'signal':None,
233 233 'status':None,
234 234 'pid':self.pid
235 235 }
236 236 )
237 237 elif isinstance(value, ProcessTerminated):
238 238 self.process_launcher.notify_stop(
239 239 {'exit_code':value.exitCode,
240 240 'signal':value.signal,
241 241 'status':value.status,
242 242 'pid':self.pid
243 243 }
244 244 )
245 245 else:
246 246 raise UnknownStatus("Unknown exit status, this is probably a "
247 247 "bug in Twisted")
248 248
249 249 def outReceived(self, data):
250 250 log.msg(data)
251 251
252 252 def errReceived(self, data):
253 253 log.err(data)
254 254
255 255
256 256 class LocalProcessLauncher(BaseLauncher):
257 257 """Start and stop an external process in an asynchronous manner.
258 258
259 259 This will launch the external process with a working directory of
260 260 ``self.work_dir``.
261 261 """
262 262
263 263 # This is used to to construct self.args, which is passed to
264 264 # spawnProcess.
265 265 cmd_and_args = List([])
266 266
267 267 def __init__(self, work_dir=u'', config=None):
268 268 super(LocalProcessLauncher, self).__init__(
269 269 work_dir=work_dir, config=config
270 270 )
271 271 self.process_protocol = None
272 272 self.start_deferred = None
273 273
274 274 def find_args(self):
275 275 return self.cmd_and_args
276 276
277 277 def start(self):
278 278 if self.state == 'before':
279 279 self.process_protocol = LocalProcessLauncherProtocol(self)
280 280 self.start_deferred = defer.Deferred()
281 281 self.process_transport = reactor.spawnProcess(
282 282 self.process_protocol,
283 283 str(self.args[0]), # twisted expects these to be str, not unicode
284 284 [str(a) for a in self.args], # str expected, not unicode
285 285 env=os.environ,
286 286 path=self.work_dir # start in the work_dir
287 287 )
288 288 return self.start_deferred
289 289 else:
290 290 s = 'The process was already started and has state: %r' % self.state
291 291 return defer.fail(ProcessStateError(s))
292 292
293 293 def notify_start(self, data):
294 294 super(LocalProcessLauncher, self).notify_start(data)
295 295 self.start_deferred.callback(data)
296 296
297 297 def stop(self):
298 298 return self.interrupt_then_kill()
299 299
300 300 @make_deferred
301 301 def signal(self, sig):
302 302 if self.state == 'running':
303 303 self.process_transport.signalProcess(sig)
304 304
305 305 @inlineCallbacks
306 306 def interrupt_then_kill(self, delay=2.0):
307 307 """Send INT, wait a delay and then send KILL."""
308 308 yield self.signal('INT')
309 309 yield sleep_deferred(delay)
310 310 yield self.signal('KILL')
311 311
312 312
313 313 class LocalControllerLauncher(LocalProcessLauncher):
314 314 """Launch a controller as a regular external process."""
315 315
316 316 controller_cmd = List(ipcontroller_cmd_argv, config=True)
317 317 # Command line arguments to ipcontroller.
318 318 controller_args = List(['--log-to-file','--log-level', '40'], config=True)
319 319
320 320 def find_args(self):
321 321 return self.controller_cmd + self.controller_args
322 322
323 323 def start(self, cluster_dir):
324 324 """Start the controller by cluster_dir."""
325 325 self.controller_args.extend(['--cluster-dir', cluster_dir])
326 326 self.cluster_dir = unicode(cluster_dir)
327 327 log.msg("Starting LocalControllerLauncher: %r" % self.args)
328 328 return super(LocalControllerLauncher, self).start()
329 329
330 330
331 331 class LocalEngineLauncher(LocalProcessLauncher):
332 332 """Launch a single engine as a regular externall process."""
333 333
334 334 engine_cmd = List(ipengine_cmd_argv, config=True)
335 335 # Command line arguments for ipengine.
336 336 engine_args = List(
337 337 ['--log-to-file','--log-level', '40'], config=True
338 338 )
339 339
340 340 def find_args(self):
341 341 return self.engine_cmd + self.engine_args
342 342
343 343 def start(self, cluster_dir):
344 344 """Start the engine by cluster_dir."""
345 345 self.engine_args.extend(['--cluster-dir', cluster_dir])
346 346 self.cluster_dir = unicode(cluster_dir)
347 347 return super(LocalEngineLauncher, self).start()
348 348
349 349
350 350 class LocalEngineSetLauncher(BaseLauncher):
351 351 """Launch a set of engines as regular external processes."""
352 352
353 353 # Command line arguments for ipengine.
354 354 engine_args = List(
355 355 ['--log-to-file','--log-level', '40'], config=True
356 356 )
357 357
358 358 def __init__(self, work_dir=u'', config=None):
359 359 super(LocalEngineSetLauncher, self).__init__(
360 360 work_dir=work_dir, config=config
361 361 )
362 362 self.launchers = []
363 363
364 364 def start(self, n, cluster_dir):
365 365 """Start n engines by profile or cluster_dir."""
366 366 self.cluster_dir = unicode(cluster_dir)
367 367 dlist = []
368 368 for i in range(n):
369 369 el = LocalEngineLauncher(work_dir=self.work_dir, config=self.config)
370 370 # Copy the engine args over to each engine launcher.
371 371 import copy
372 372 el.engine_args = copy.deepcopy(self.engine_args)
373 373 d = el.start(cluster_dir)
374 374 if i==0:
375 375 log.msg("Starting LocalEngineSetLauncher: %r" % el.args)
376 376 self.launchers.append(el)
377 377 dlist.append(d)
378 378 # The consumeErrors here could be dangerous
379 379 dfinal = gatherBoth(dlist, consumeErrors=True)
380 380 dfinal.addCallback(self.notify_start)
381 381 return dfinal
382 382
383 383 def find_args(self):
384 384 return ['engine set']
385 385
386 386 def signal(self, sig):
387 387 dlist = []
388 388 for el in self.launchers:
389 389 d = el.signal(sig)
390 390 dlist.append(d)
391 391 dfinal = gatherBoth(dlist, consumeErrors=True)
392 392 return dfinal
393 393
394 394 def interrupt_then_kill(self, delay=1.0):
395 395 dlist = []
396 396 for el in self.launchers:
397 397 d = el.interrupt_then_kill(delay)
398 398 dlist.append(d)
399 399 dfinal = gatherBoth(dlist, consumeErrors=True)
400 400 return dfinal
401 401
402 402 def stop(self):
403 403 return self.interrupt_then_kill()
404 404
405 405 def observe_stop(self):
406 406 dlist = [el.observe_stop() for el in self.launchers]
407 407 dfinal = gatherBoth(dlist, consumeErrors=False)
408 408 dfinal.addCallback(self.notify_stop)
409 409 return dfinal
410 410
411 411
412 412 #-----------------------------------------------------------------------------
413 413 # MPIExec launchers
414 414 #-----------------------------------------------------------------------------
415 415
416 416
417 417 class MPIExecLauncher(LocalProcessLauncher):
418 418 """Launch an external process using mpiexec."""
419 419
420 420 # The mpiexec command to use in starting the process.
421 421 mpi_cmd = List(['mpiexec'], config=True)
422 422 # The command line arguments to pass to mpiexec.
423 423 mpi_args = List([], config=True)
424 424 # The program to start using mpiexec.
425 425 program = List(['date'], config=True)
426 426 # The command line argument to the program.
427 427 program_args = List([], config=True)
428 428 # The number of instances of the program to start.
429 429 n = Int(1, config=True)
430 430
431 431 def find_args(self):
432 432 """Build self.args using all the fields."""
433 433 return self.mpi_cmd + ['-n', self.n] + self.mpi_args + \
434 434 self.program + self.program_args
435 435
436 436 def start(self, n):
437 437 """Start n instances of the program using mpiexec."""
438 438 self.n = n
439 439 return super(MPIExecLauncher, self).start()
440 440
441 441
442 442 class MPIExecControllerLauncher(MPIExecLauncher):
443 443 """Launch a controller using mpiexec."""
444 444
445 445 controller_cmd = List(ipcontroller_cmd_argv, config=True)
446 446 # Command line arguments to ipcontroller.
447 447 controller_args = List(['--log-to-file','--log-level', '40'], config=True)
448 448 n = Int(1, config=False)
449 449
450 450 def start(self, cluster_dir):
451 451 """Start the controller by cluster_dir."""
452 452 self.controller_args.extend(['--cluster-dir', cluster_dir])
453 453 self.cluster_dir = unicode(cluster_dir)
454 454 log.msg("Starting MPIExecControllerLauncher: %r" % self.args)
455 455 return super(MPIExecControllerLauncher, self).start(1)
456 456
457 457 def find_args(self):
458 458 return self.mpi_cmd + ['-n', self.n] + self.mpi_args + \
459 459 self.controller_cmd + self.controller_args
460 460
461 461
462 462 class MPIExecEngineSetLauncher(MPIExecLauncher):
463 463
464 464 engine_cmd = List(ipengine_cmd_argv, config=True)
465 465 # Command line arguments for ipengine.
466 466 engine_args = List(
467 467 ['--log-to-file','--log-level', '40'], config=True
468 468 )
469 469 n = Int(1, config=True)
470 470
471 471 def start(self, n, cluster_dir):
472 472 """Start n engines by profile or cluster_dir."""
473 473 self.engine_args.extend(['--cluster-dir', cluster_dir])
474 474 self.cluster_dir = unicode(cluster_dir)
475 475 self.n = n
476 476 log.msg('Starting MPIExecEngineSetLauncher: %r' % self.args)
477 477 return super(MPIExecEngineSetLauncher, self).start(n)
478 478
479 479 def find_args(self):
480 480 return self.mpi_cmd + ['-n', self.n] + self.mpi_args + \
481 481 self.engine_cmd + self.engine_args
482 482
483 483
484 484 #-----------------------------------------------------------------------------
485 485 # SSH launchers
486 486 #-----------------------------------------------------------------------------
487 487
488 488 # TODO: Get SSH Launcher working again.
489 489
490 490 class SSHLauncher(BaseLauncher):
491 491 """A minimal launcher for ssh.
492 492
493 493 To be useful this will probably have to be extended to use the ``sshx``
494 494 idea for environment variables. There could be other things this needs
495 495 as well.
496 496 """
497 497
498 498 ssh_cmd = List(['ssh'], config=True)
499 499 ssh_args = List([], config=True)
500 500 program = List(['date'], config=True)
501 501 program_args = List([], config=True)
502 502 hostname = Str('', config=True)
503 503 user = Str('', config=True)
504 504 location = Str('')
505 505
506 506 def _hostname_changed(self, name, old, new):
507 507 self.location = '%s@%s' % (self.user, new)
508 508
509 509 def _user_changed(self, name, old, new):
510 510 self.location = '%s@%s' % (new, self.hostname)
511 511
512 512 def find_args(self):
513 513 return self.ssh_cmd + self.ssh_args + [self.location] + \
514 514 self.program + self.program_args
515 515
516 516 def start(self, n, hostname=None, user=None):
517 517 if hostname is not None:
518 518 self.hostname = hostname
519 519 if user is not None:
520 520 self.user = user
521 521 return super(SSHLauncher, self).start()
522 522
523 523
524 524 class SSHControllerLauncher(SSHLauncher):
525 525 pass
526 526
527 527
528 528 class SSHEngineSetLauncher(BaseLauncher):
529 529 pass
530 530
531 531
532 532 #-----------------------------------------------------------------------------
533 533 # Windows HPC Server 2008 scheduler launchers
534 534 #-----------------------------------------------------------------------------
535 535
536 536
537 537 # This is only used on Windows.
538 538 def find_job_cmd():
539 539 if os.name=='nt':
540 540 try:
541 541 return find_cmd('job')
542 542 except FindCmdError:
543 543 return 'job'
544 544 else:
545 545 return 'job'
546 546
547 547
548 548 class WindowsHPCLauncher(BaseLauncher):
549 549
550 550 # A regular expression used to get the job id from the output of the
551 551 # submit_command.
552 552 job_id_regexp = Str(r'\d+', config=True)
553 553 # The filename of the instantiated job script.
554 554 job_file_name = Unicode(u'ipython_job.xml', config=True)
555 555 # The full path to the instantiated job script. This gets made dynamically
556 556 # by combining the work_dir with the job_file_name.
557 557 job_file = Unicode(u'')
558 558 # The hostname of the scheduler to submit the job to
559 559 scheduler = Str('', config=True)
560 560 job_cmd = Str(find_job_cmd(), config=True)
561 561
562 562 def __init__(self, work_dir=u'', config=None):
563 563 super(WindowsHPCLauncher, self).__init__(
564 564 work_dir=work_dir, config=config
565 565 )
566 566
567 567 @property
568 568 def job_file(self):
569 569 return os.path.join(self.work_dir, self.job_file_name)
570 570
571 571 def write_job_file(self, n):
572 572 raise NotImplementedError("Implement write_job_file in a subclass.")
573 573
574 574 def find_args(self):
575 575 return ['job.exe']
576 576
577 577 def parse_job_id(self, output):
578 578 """Take the output of the submit command and return the job id."""
579 579 m = re.search(self.job_id_regexp, output)
580 580 if m is not None:
581 581 job_id = m.group()
582 582 else:
583 583 raise LauncherError("Job id couldn't be determined: %s" % output)
584 584 self.job_id = job_id
585 585 log.msg('Job started with job id: %r' % job_id)
586 586 return job_id
587 587
588 588 @inlineCallbacks
589 589 def start(self, n):
590 590 """Start n copies of the process using the Win HPC job scheduler."""
591 591 self.write_job_file(n)
592 592 args = [
593 593 'submit',
594 594 '/jobfile:%s' % self.job_file,
595 595 '/scheduler:%s' % self.scheduler
596 596 ]
597 597 log.msg("Starting Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),))
598 598 # Twisted will raise DeprecationWarnings if we try to pass unicode to this
599 599 output = yield getProcessOutput(str(self.job_cmd),
600 600 [str(a) for a in args],
601 601 env=dict((str(k),str(v)) for k,v in os.environ.items()),
602 602 path=self.work_dir
603 603 )
604 604 job_id = self.parse_job_id(output)
605 605 self.notify_start(job_id)
606 606 defer.returnValue(job_id)
607 607
608 608 @inlineCallbacks
609 609 def stop(self):
610 610 args = [
611 611 'cancel',
612 612 self.job_id,
613 613 '/scheduler:%s' % self.scheduler
614 614 ]
615 615 log.msg("Stopping Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),))
616 616 try:
617 617 # Twisted will raise DeprecationWarnings if we try to pass unicode to this
618 618 output = yield getProcessOutput(str(self.job_cmd),
619 619 [str(a) for a in args],
620 env=dict((str(k),str(v)) for k,v in os.environ.items()),
620 env=dict((str(k),str(v)) for k,v in os.environ.iteritems()),
621 621 path=self.work_dir
622 622 )
623 623 except:
624 624 output = 'The job already appears to be stoppped: %r' % self.job_id
625 625 self.notify_stop(output) # Pass the output of the kill cmd
626 626 defer.returnValue(output)
627 627
628 628
629 629 class WindowsHPCControllerLauncher(WindowsHPCLauncher):
630 630
631 631 job_file_name = Unicode(u'ipcontroller_job.xml', config=True)
632 632 extra_args = List([], config=False)
633 633
634 634 def write_job_file(self, n):
635 635 job = IPControllerJob(config=self.config)
636 636
637 637 t = IPControllerTask(config=self.config)
638 638 # The tasks work directory is *not* the actual work directory of
639 639 # the controller. It is used as the base path for the stdout/stderr
640 640 # files that the scheduler redirects to.
641 641 t.work_directory = self.cluster_dir
642 642 # Add the --cluster-dir and from self.start().
643 643 t.controller_args.extend(self.extra_args)
644 644 job.add_task(t)
645 645
646 646 log.msg("Writing job description file: %s" % self.job_file)
647 647 job.write(self.job_file)
648 648
649 649 @property
650 650 def job_file(self):
651 651 return os.path.join(self.cluster_dir, self.job_file_name)
652 652
653 653 def start(self, cluster_dir):
654 654 """Start the controller by cluster_dir."""
655 655 self.extra_args = ['--cluster-dir', cluster_dir]
656 656 self.cluster_dir = unicode(cluster_dir)
657 657 return super(WindowsHPCControllerLauncher, self).start(1)
658 658
659 659
660 660 class WindowsHPCEngineSetLauncher(WindowsHPCLauncher):
661 661
662 662 job_file_name = Unicode(u'ipengineset_job.xml', config=True)
663 663 extra_args = List([], config=False)
664 664
665 665 def write_job_file(self, n):
666 666 job = IPEngineSetJob(config=self.config)
667 667
668 668 for i in range(n):
669 669 t = IPEngineTask(config=self.config)
670 670 # The tasks work directory is *not* the actual work directory of
671 671 # the engine. It is used as the base path for the stdout/stderr
672 672 # files that the scheduler redirects to.
673 673 t.work_directory = self.cluster_dir
674 674 # Add the --cluster-dir and from self.start().
675 675 t.engine_args.extend(self.extra_args)
676 676 job.add_task(t)
677 677
678 678 log.msg("Writing job description file: %s" % self.job_file)
679 679 job.write(self.job_file)
680 680
681 681 @property
682 682 def job_file(self):
683 683 return os.path.join(self.cluster_dir, self.job_file_name)
684 684
685 685 def start(self, n, cluster_dir):
686 686 """Start the controller by cluster_dir."""
687 687 self.extra_args = ['--cluster-dir', cluster_dir]
688 688 self.cluster_dir = unicode(cluster_dir)
689 689 return super(WindowsHPCEngineSetLauncher, self).start(n)
690 690
691 691
692 692 #-----------------------------------------------------------------------------
693 693 # Batch (PBS) system launchers
694 694 #-----------------------------------------------------------------------------
695 695
696 696 # TODO: Get PBS launcher working again.
697 697
698 698 class BatchSystemLauncher(BaseLauncher):
699 699 """Launch an external process using a batch system.
700 700
701 701 This class is designed to work with UNIX batch systems like PBS, LSF,
702 702 GridEngine, etc. The overall model is that there are different commands
703 703 like qsub, qdel, etc. that handle the starting and stopping of the process.
704 704
705 705 This class also has the notion of a batch script. The ``batch_template``
706 706 attribute can be set to a string that is a template for the batch script.
707 707 This template is instantiated using Itpl. Thus the template can use
708 708 ${n} fot the number of instances. Subclasses can add additional variables
709 709 to the template dict.
710 710 """
711 711
712 712 # Subclasses must fill these in. See PBSEngineSet
713 713 # The name of the command line program used to submit jobs.
714 714 submit_command = Str('', config=True)
715 715 # The name of the command line program used to delete jobs.
716 716 delete_command = Str('', config=True)
717 717 # A regular expression used to get the job id from the output of the
718 718 # submit_command.
719 719 job_id_regexp = Str('', config=True)
720 720 # The string that is the batch script template itself.
721 721 batch_template = Str('', config=True)
722 722 # The filename of the instantiated batch script.
723 723 batch_file_name = Unicode(u'batch_script', config=True)
724 724 # The full path to the instantiated batch script.
725 725 batch_file = Unicode(u'')
726 726
727 727 def __init__(self, work_dir=u'', config=None):
728 728 super(BatchSystemLauncher, self).__init__(
729 729 work_dir=work_dir, config=config
730 730 )
731 731 self.batch_file = os.path.join(self.work_dir, self.batch_file_name)
732 732 self.context = {}
733 733
734 734 def parse_job_id(self, output):
735 735 """Take the output of the submit command and return the job id."""
736 736 m = re.match(self.job_id_regexp, output)
737 737 if m is not None:
738 738 job_id = m.group()
739 739 else:
740 740 raise LauncherError("Job id couldn't be determined: %s" % output)
741 741 self.job_id = job_id
742 742 log.msg('Job started with job id: %r' % job_id)
743 743 return job_id
744 744
745 745 def write_batch_script(self, n):
746 746 """Instantiate and write the batch script to the work_dir."""
747 747 self.context['n'] = n
748 748 script_as_string = Itpl.itplns(self.batch_template, self.context)
749 749 log.msg('Writing instantiated batch script: %s' % self.batch_file)
750 750 f = open(self.batch_file, 'w')
751 751 f.write(script_as_string)
752 752 f.close()
753 753
754 754 @inlineCallbacks
755 755 def start(self, n):
756 756 """Start n copies of the process using a batch system."""
757 757 self.write_batch_script(n)
758 758 output = yield getProcessOutput(self.submit_command,
759 759 [self.batch_file], env=os.environ)
760 760 job_id = self.parse_job_id(output)
761 761 self.notify_start(job_id)
762 762 defer.returnValue(job_id)
763 763
764 764 @inlineCallbacks
765 765 def stop(self):
766 766 output = yield getProcessOutput(self.delete_command,
767 767 [self.job_id], env=os.environ
768 768 )
769 769 self.notify_stop(output) # Pass the output of the kill cmd
770 770 defer.returnValue(output)
771 771
772 772
773 773 class PBSLauncher(BatchSystemLauncher):
774 774 """A BatchSystemLauncher subclass for PBS."""
775 775
776 776 submit_command = Str('qsub', config=True)
777 777 delete_command = Str('qdel', config=True)
778 778 job_id_regexp = Str(r'\d+', config=True)
779 779 batch_template = Str('', config=True)
780 780 batch_file_name = Unicode(u'pbs_batch_script', config=True)
781 781 batch_file = Unicode(u'')
782 782
783 783
784 784 class PBSControllerLauncher(PBSLauncher):
785 785 """Launch a controller using PBS."""
786 786
787 787 batch_file_name = Unicode(u'pbs_batch_script_controller', config=True)
788 788
789 789 def start(self, cluster_dir):
790 790 """Start the controller by profile or cluster_dir."""
791 791 # Here we save profile and cluster_dir in the context so they
792 792 # can be used in the batch script template as ${profile} and
793 793 # ${cluster_dir}
794 794 self.context['cluster_dir'] = cluster_dir
795 795 self.cluster_dir = unicode(cluster_dir)
796 796 log.msg("Starting PBSControllerLauncher: %r" % self.args)
797 797 return super(PBSControllerLauncher, self).start(1)
798 798
799 799
800 800 class PBSEngineSetLauncher(PBSLauncher):
801 801
802 802 batch_file_name = Unicode(u'pbs_batch_script_engines', config=True)
803 803
804 804 def start(self, n, cluster_dir):
805 805 """Start n engines by profile or cluster_dir."""
806 806 self.program_args.extend(['--cluster-dir', cluster_dir])
807 807 self.cluster_dir = unicode(cluster_dir)
808 808 log.msg('Starting PBSEngineSetLauncher: %r' % self.args)
809 809 return super(PBSEngineSetLauncher, self).start(n)
810 810
811 811
812 812 #-----------------------------------------------------------------------------
813 813 # A launcher for ipcluster itself!
814 814 #-----------------------------------------------------------------------------
815 815
816 816
817 817 class IPClusterLauncher(LocalProcessLauncher):
818 818 """Launch the ipcluster program in an external process."""
819 819
820 820 ipcluster_cmd = List(ipcluster_cmd_argv, config=True)
821 821 # Command line arguments to pass to ipcluster.
822 822 ipcluster_args = List(
823 823 ['--clean-logs', '--log-to-file', '--log-level', '40'], config=True)
824 824 ipcluster_subcommand = Str('start')
825 825 ipcluster_n = Int(2)
826 826
827 827 def find_args(self):
828 828 return self.ipcluster_cmd + [self.ipcluster_subcommand] + \
829 829 ['-n', repr(self.ipcluster_n)] + self.ipcluster_args
830 830
831 831 def start(self):
832 832 log.msg("Starting ipcluster: %r" % self.args)
833 833 return super(IPClusterLauncher, self).start()
834 834
@@ -1,186 +1,186 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3
4 4 """Tests for pendingdeferred.py"""
5 5
6 6 __docformat__ = "restructuredtext en"
7 7
8 8 #-------------------------------------------------------------------------------
9 9 # Copyright (C) 2008 The IPython Development Team
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 # Imports
17 17 #-------------------------------------------------------------------------------
18 18
19 19 # Tell nose to skip this module
20 20 __test__ = {}
21 21
22 22 from twisted.internet import defer
23 23 from twisted.python import failure
24 24
25 25 from IPython.testing.util import DeferredTestCase
26 26 import IPython.kernel.pendingdeferred as pd
27 27 from IPython.kernel import error
28 28 from IPython.kernel.util import printer
29 29
30 30
31 31 class Foo(object):
32 32
33 33 def bar(self, bahz):
34 34 return defer.succeed('blahblah: %s' % bahz)
35 35
36 36 class TwoPhaseFoo(pd.PendingDeferredManager):
37 37
38 38 def __init__(self, foo):
39 39 self.foo = foo
40 40 pd.PendingDeferredManager.__init__(self)
41 41
42 42 @pd.two_phase
43 43 def bar(self, bahz):
44 44 return self.foo.bar(bahz)
45 45
46 46 class PendingDeferredManagerTest(DeferredTestCase):
47 47
48 48 def setUp(self):
49 49 self.pdm = pd.PendingDeferredManager()
50 50
51 51 def tearDown(self):
52 52 pass
53 53
54 54 def testBasic(self):
55 55 dDict = {}
56 56 # Create 10 deferreds and save them
57 57 for i in range(10):
58 58 d = defer.Deferred()
59 59 did = self.pdm.save_pending_deferred(d)
60 60 dDict[did] = d
61 61 # Make sure they are begin saved
62 for k in dDict.keys():
62 for k in dDict.iterkeys():
63 63 self.assert_(self.pdm.quick_has_id(k))
64 64 # Get the pending deferred (block=True), then callback with 'foo' and compare
65 65 for did in dDict.keys()[0:5]:
66 66 d = self.pdm.get_pending_deferred(did,block=True)
67 67 dDict[did].callback('foo')
68 68 d.addCallback(lambda r: self.assert_(r=='foo'))
69 69 # Get the pending deferreds with (block=False) and make sure ResultNotCompleted is raised
70 70 for did in dDict.keys()[5:10]:
71 71 d = self.pdm.get_pending_deferred(did,block=False)
72 72 d.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
73 73 # Now callback the last 5, get them and compare.
74 74 for did in dDict.keys()[5:10]:
75 75 dDict[did].callback('foo')
76 76 d = self.pdm.get_pending_deferred(did,block=False)
77 77 d.addCallback(lambda r: self.assert_(r=='foo'))
78 78
79 79 def test_save_then_delete(self):
80 80 d = defer.Deferred()
81 81 did = self.pdm.save_pending_deferred(d)
82 82 self.assert_(self.pdm.quick_has_id(did))
83 83 self.pdm.delete_pending_deferred(did)
84 84 self.assert_(not self.pdm.quick_has_id(did))
85 85
86 86 def test_save_get_delete(self):
87 87 d = defer.Deferred()
88 88 did = self.pdm.save_pending_deferred(d)
89 89 d2 = self.pdm.get_pending_deferred(did,True)
90 90 d2.addErrback(lambda f: self.assertRaises(error.AbortedPendingDeferredError, f.raiseException))
91 91 self.pdm.delete_pending_deferred(did)
92 92 return d2
93 93
94 94 def test_double_get(self):
95 95 d = defer.Deferred()
96 96 did = self.pdm.save_pending_deferred(d)
97 97 d2 = self.pdm.get_pending_deferred(did,True)
98 98 d3 = self.pdm.get_pending_deferred(did,True)
99 99 d3.addErrback(lambda f: self.assertRaises(error.InvalidDeferredID, f.raiseException))
100 100
101 101 def test_get_after_callback(self):
102 102 d = defer.Deferred()
103 103 did = self.pdm.save_pending_deferred(d)
104 104 d.callback('foo')
105 105 d2 = self.pdm.get_pending_deferred(did,True)
106 106 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
107 107 self.assert_(not self.pdm.quick_has_id(did))
108 108
109 109 def test_get_before_callback(self):
110 110 d = defer.Deferred()
111 111 did = self.pdm.save_pending_deferred(d)
112 112 d2 = self.pdm.get_pending_deferred(did,True)
113 113 d.callback('foo')
114 114 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
115 115 self.assert_(not self.pdm.quick_has_id(did))
116 116 d = defer.Deferred()
117 117 did = self.pdm.save_pending_deferred(d)
118 118 d2 = self.pdm.get_pending_deferred(did,True)
119 119 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
120 120 d.callback('foo')
121 121 self.assert_(not self.pdm.quick_has_id(did))
122 122
123 123 def test_get_after_errback(self):
124 124 class MyError(Exception):
125 125 pass
126 126 d = defer.Deferred()
127 127 did = self.pdm.save_pending_deferred(d)
128 128 d.errback(failure.Failure(MyError('foo')))
129 129 d2 = self.pdm.get_pending_deferred(did,True)
130 130 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
131 131 self.assert_(not self.pdm.quick_has_id(did))
132 132
133 133 def test_get_before_errback(self):
134 134 class MyError(Exception):
135 135 pass
136 136 d = defer.Deferred()
137 137 did = self.pdm.save_pending_deferred(d)
138 138 d2 = self.pdm.get_pending_deferred(did,True)
139 139 d.errback(failure.Failure(MyError('foo')))
140 140 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
141 141 self.assert_(not self.pdm.quick_has_id(did))
142 142 d = defer.Deferred()
143 143 did = self.pdm.save_pending_deferred(d)
144 144 d2 = self.pdm.get_pending_deferred(did,True)
145 145 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
146 146 d.errback(failure.Failure(MyError('foo')))
147 147 self.assert_(not self.pdm.quick_has_id(did))
148 148
149 149 def test_noresult_noblock(self):
150 150 d = defer.Deferred()
151 151 did = self.pdm.save_pending_deferred(d)
152 152 d2 = self.pdm.get_pending_deferred(did,False)
153 153 d2.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
154 154
155 155 def test_with_callbacks(self):
156 156 d = defer.Deferred()
157 157 d.addCallback(lambda r: r+' foo')
158 158 d.addCallback(lambda r: r+' bar')
159 159 did = self.pdm.save_pending_deferred(d)
160 160 d2 = self.pdm.get_pending_deferred(did,True)
161 161 d.callback('bam')
162 162 d2.addCallback(lambda r: self.assertEquals(r,'bam foo bar'))
163 163
164 164 def test_with_errbacks(self):
165 165 class MyError(Exception):
166 166 pass
167 167 d = defer.Deferred()
168 168 d.addCallback(lambda r: 'foo')
169 169 d.addErrback(lambda f: 'caught error')
170 170 did = self.pdm.save_pending_deferred(d)
171 171 d2 = self.pdm.get_pending_deferred(did,True)
172 172 d.errback(failure.Failure(MyError('bam')))
173 173 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
174 174
175 175 def test_nested_deferreds(self):
176 176 d = defer.Deferred()
177 177 d2 = defer.Deferred()
178 178 d.addCallback(lambda r: d2)
179 179 did = self.pdm.save_pending_deferred(d)
180 180 d.callback('foo')
181 181 d3 = self.pdm.get_pending_deferred(did,False)
182 182 d3.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
183 183 d2.callback('bar')
184 184 d3 = self.pdm.get_pending_deferred(did,False)
185 185 d3.addCallback(lambda r: self.assertEquals(r,'bar'))
186 186
@@ -1,316 +1,316 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 Job and task components for writing .xml files that the Windows HPC Server
5 5 2008 can use to start jobs.
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2008-2009 The IPython Development Team
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 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 from __future__ import with_statement
20 20
21 21 import os
22 22 import re
23 23 import uuid
24 24
25 25 from xml.etree import ElementTree as ET
26 26
27 27 from IPython.config.configurable import Configurable
28 28 from IPython.utils.traitlets import (
29 29 Str, Int, List, Instance,
30 30 Enum, Bool, CStr
31 31 )
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Job and Task classes
35 35 #-----------------------------------------------------------------------------
36 36
37 37
38 38 def as_str(value):
39 39 if isinstance(value, str):
40 40 return value
41 41 elif isinstance(value, bool):
42 42 if value:
43 43 return 'true'
44 44 else:
45 45 return 'false'
46 46 elif isinstance(value, (int, float)):
47 47 return repr(value)
48 48 else:
49 49 return value
50 50
51 51
52 52 def indent(elem, level=0):
53 53 i = "\n" + level*" "
54 54 if len(elem):
55 55 if not elem.text or not elem.text.strip():
56 56 elem.text = i + " "
57 57 if not elem.tail or not elem.tail.strip():
58 58 elem.tail = i
59 59 for elem in elem:
60 60 indent(elem, level+1)
61 61 if not elem.tail or not elem.tail.strip():
62 62 elem.tail = i
63 63 else:
64 64 if level and (not elem.tail or not elem.tail.strip()):
65 65 elem.tail = i
66 66
67 67
68 68 def find_username():
69 69 domain = os.environ.get('USERDOMAIN')
70 70 username = os.environ.get('USERNAME','')
71 71 if domain is None:
72 72 return username
73 73 else:
74 74 return '%s\\%s' % (domain, username)
75 75
76 76
77 77 class WinHPCJob(Configurable):
78 78
79 79 job_id = Str('')
80 80 job_name = Str('MyJob', config=True)
81 81 min_cores = Int(1, config=True)
82 82 max_cores = Int(1, config=True)
83 83 min_sockets = Int(1, config=True)
84 84 max_sockets = Int(1, config=True)
85 85 min_nodes = Int(1, config=True)
86 86 max_nodes = Int(1, config=True)
87 87 unit_type = Str("Core", config=True)
88 88 auto_calculate_min = Bool(True, config=True)
89 89 auto_calculate_max = Bool(True, config=True)
90 90 run_until_canceled = Bool(False, config=True)
91 91 is_exclusive = Bool(False, config=True)
92 92 username = Str(find_username(), config=True)
93 93 job_type = Str('Batch', config=True)
94 94 priority = Enum(('Lowest','BelowNormal','Normal','AboveNormal','Highest'),
95 95 default_value='Highest', config=True)
96 96 requested_nodes = Str('', config=True)
97 97 project = Str('IPython', config=True)
98 98 xmlns = Str('http://schemas.microsoft.com/HPCS2008/scheduler/')
99 99 version = Str("2.000")
100 100 tasks = List([])
101 101
102 102 @property
103 103 def owner(self):
104 104 return self.username
105 105
106 106 def _write_attr(self, root, attr, key):
107 107 s = as_str(getattr(self, attr, ''))
108 108 if s:
109 109 root.set(key, s)
110 110
111 111 def as_element(self):
112 112 # We have to add _A_ type things to get the right order than
113 113 # the MSFT XML parser expects.
114 114 root = ET.Element('Job')
115 115 self._write_attr(root, 'version', '_A_Version')
116 116 self._write_attr(root, 'job_name', '_B_Name')
117 117 self._write_attr(root, 'unit_type', '_C_UnitType')
118 118 self._write_attr(root, 'min_cores', '_D_MinCores')
119 119 self._write_attr(root, 'max_cores', '_E_MaxCores')
120 120 self._write_attr(root, 'min_sockets', '_F_MinSockets')
121 121 self._write_attr(root, 'max_sockets', '_G_MaxSockets')
122 122 self._write_attr(root, 'min_nodes', '_H_MinNodes')
123 123 self._write_attr(root, 'max_nodes', '_I_MaxNodes')
124 124 self._write_attr(root, 'run_until_canceled', '_J_RunUntilCanceled')
125 125 self._write_attr(root, 'is_exclusive', '_K_IsExclusive')
126 126 self._write_attr(root, 'username', '_L_UserName')
127 127 self._write_attr(root, 'job_type', '_M_JobType')
128 128 self._write_attr(root, 'priority', '_N_Priority')
129 129 self._write_attr(root, 'requested_nodes', '_O_RequestedNodes')
130 130 self._write_attr(root, 'auto_calculate_max', '_P_AutoCalculateMax')
131 131 self._write_attr(root, 'auto_calculate_min', '_Q_AutoCalculateMin')
132 132 self._write_attr(root, 'project', '_R_Project')
133 133 self._write_attr(root, 'owner', '_S_Owner')
134 134 self._write_attr(root, 'xmlns', '_T_xmlns')
135 135 dependencies = ET.SubElement(root, "Dependencies")
136 136 etasks = ET.SubElement(root, "Tasks")
137 137 for t in self.tasks:
138 138 etasks.append(t.as_element())
139 139 return root
140 140
141 141 def tostring(self):
142 142 """Return the string representation of the job description XML."""
143 143 root = self.as_element()
144 144 indent(root)
145 145 txt = ET.tostring(root, encoding="utf-8")
146 146 # Now remove the tokens used to order the attributes.
147 147 txt = re.sub(r'_[A-Z]_','',txt)
148 148 txt = '<?xml version="1.0" encoding="utf-8"?>\n' + txt
149 149 return txt
150 150
151 151 def write(self, filename):
152 152 """Write the XML job description to a file."""
153 153 txt = self.tostring()
154 154 with open(filename, 'w') as f:
155 155 f.write(txt)
156 156
157 157 def add_task(self, task):
158 158 """Add a task to the job.
159 159
160 160 Parameters
161 161 ----------
162 162 task : :class:`WinHPCTask`
163 163 The task object to add.
164 164 """
165 165 self.tasks.append(task)
166 166
167 167
168 168 class WinHPCTask(Configurable):
169 169
170 170 task_id = Str('')
171 171 task_name = Str('')
172 172 version = Str("2.000")
173 173 min_cores = Int(1, config=True)
174 174 max_cores = Int(1, config=True)
175 175 min_sockets = Int(1, config=True)
176 176 max_sockets = Int(1, config=True)
177 177 min_nodes = Int(1, config=True)
178 178 max_nodes = Int(1, config=True)
179 179 unit_type = Str("Core", config=True)
180 180 command_line = CStr('', config=True)
181 181 work_directory = CStr('', config=True)
182 182 is_rerunnaable = Bool(True, config=True)
183 183 std_out_file_path = CStr('', config=True)
184 184 std_err_file_path = CStr('', config=True)
185 185 is_parametric = Bool(False, config=True)
186 186 environment_variables = Instance(dict, args=(), config=True)
187 187
188 188 def _write_attr(self, root, attr, key):
189 189 s = as_str(getattr(self, attr, ''))
190 190 if s:
191 191 root.set(key, s)
192 192
193 193 def as_element(self):
194 194 root = ET.Element('Task')
195 195 self._write_attr(root, 'version', '_A_Version')
196 196 self._write_attr(root, 'task_name', '_B_Name')
197 197 self._write_attr(root, 'min_cores', '_C_MinCores')
198 198 self._write_attr(root, 'max_cores', '_D_MaxCores')
199 199 self._write_attr(root, 'min_sockets', '_E_MinSockets')
200 200 self._write_attr(root, 'max_sockets', '_F_MaxSockets')
201 201 self._write_attr(root, 'min_nodes', '_G_MinNodes')
202 202 self._write_attr(root, 'max_nodes', '_H_MaxNodes')
203 203 self._write_attr(root, 'command_line', '_I_CommandLine')
204 204 self._write_attr(root, 'work_directory', '_J_WorkDirectory')
205 205 self._write_attr(root, 'is_rerunnaable', '_K_IsRerunnable')
206 206 self._write_attr(root, 'std_out_file_path', '_L_StdOutFilePath')
207 207 self._write_attr(root, 'std_err_file_path', '_M_StdErrFilePath')
208 208 self._write_attr(root, 'is_parametric', '_N_IsParametric')
209 209 self._write_attr(root, 'unit_type', '_O_UnitType')
210 210 root.append(self.get_env_vars())
211 211 return root
212 212
213 213 def get_env_vars(self):
214 214 env_vars = ET.Element('EnvironmentVariables')
215 for k, v in self.environment_variables.items():
215 for k, v in self.environment_variables.iteritems():
216 216 variable = ET.SubElement(env_vars, "Variable")
217 217 name = ET.SubElement(variable, "Name")
218 218 name.text = k
219 219 value = ET.SubElement(variable, "Value")
220 220 value.text = v
221 221 return env_vars
222 222
223 223
224 224
225 225 # By declaring these, we can configure the controller and engine separately!
226 226
227 227 class IPControllerJob(WinHPCJob):
228 228 job_name = Str('IPController', config=False)
229 229 is_exclusive = Bool(False, config=True)
230 230 username = Str(find_username(), config=True)
231 231 priority = Enum(('Lowest','BelowNormal','Normal','AboveNormal','Highest'),
232 232 default_value='Highest', config=True)
233 233 requested_nodes = Str('', config=True)
234 234 project = Str('IPython', config=True)
235 235
236 236
237 237 class IPEngineSetJob(WinHPCJob):
238 238 job_name = Str('IPEngineSet', config=False)
239 239 is_exclusive = Bool(False, config=True)
240 240 username = Str(find_username(), config=True)
241 241 priority = Enum(('Lowest','BelowNormal','Normal','AboveNormal','Highest'),
242 242 default_value='Highest', config=True)
243 243 requested_nodes = Str('', config=True)
244 244 project = Str('IPython', config=True)
245 245
246 246
247 247 class IPControllerTask(WinHPCTask):
248 248
249 249 task_name = Str('IPController', config=True)
250 250 controller_cmd = List(['ipcontroller.exe'], config=True)
251 251 controller_args = List(['--log-to-file', '--log-level', '40'], config=True)
252 252 # I don't want these to be configurable
253 253 std_out_file_path = CStr('', config=False)
254 254 std_err_file_path = CStr('', config=False)
255 255 min_cores = Int(1, config=False)
256 256 max_cores = Int(1, config=False)
257 257 min_sockets = Int(1, config=False)
258 258 max_sockets = Int(1, config=False)
259 259 min_nodes = Int(1, config=False)
260 260 max_nodes = Int(1, config=False)
261 261 unit_type = Str("Core", config=False)
262 262 work_directory = CStr('', config=False)
263 263
264 264 def __init__(self, config=None):
265 265 super(IPControllerTask, self).__init__(config=config)
266 266 the_uuid = uuid.uuid1()
267 267 self.std_out_file_path = os.path.join('log','ipcontroller-%s.out' % the_uuid)
268 268 self.std_err_file_path = os.path.join('log','ipcontroller-%s.err' % the_uuid)
269 269
270 270 @property
271 271 def command_line(self):
272 272 return ' '.join(self.controller_cmd + self.controller_args)
273 273
274 274
275 275 class IPEngineTask(WinHPCTask):
276 276
277 277 task_name = Str('IPEngine', config=True)
278 278 engine_cmd = List(['ipengine.exe'], config=True)
279 279 engine_args = List(['--log-to-file', '--log-level', '40'], config=True)
280 280 # I don't want these to be configurable
281 281 std_out_file_path = CStr('', config=False)
282 282 std_err_file_path = CStr('', config=False)
283 283 min_cores = Int(1, config=False)
284 284 max_cores = Int(1, config=False)
285 285 min_sockets = Int(1, config=False)
286 286 max_sockets = Int(1, config=False)
287 287 min_nodes = Int(1, config=False)
288 288 max_nodes = Int(1, config=False)
289 289 unit_type = Str("Core", config=False)
290 290 work_directory = CStr('', config=False)
291 291
292 292 def __init__(self, config=None):
293 293 super(IPEngineTask,self).__init__(config=config)
294 294 the_uuid = uuid.uuid1()
295 295 self.std_out_file_path = os.path.join('log','ipengine-%s.out' % the_uuid)
296 296 self.std_err_file_path = os.path.join('log','ipengine-%s.err' % the_uuid)
297 297
298 298 @property
299 299 def command_line(self):
300 300 return ' '.join(self.engine_cmd + self.engine_args)
301 301
302 302
303 303 # j = WinHPCJob(None)
304 304 # j.job_name = 'IPCluster'
305 305 # j.username = 'GNET\\bgranger'
306 306 # j.requested_nodes = 'GREEN'
307 307 #
308 308 # t = WinHPCTask(None)
309 309 # t.task_name = 'Controller'
310 310 # t.command_line = r"\\blue\domainusers$\bgranger\Python\Python25\Scripts\ipcontroller.exe --log-to-file -p default --log-level 10"
311 311 # t.work_directory = r"\\blue\domainusers$\bgranger\.ipython\cluster_default"
312 312 # t.std_out_file_path = 'controller-out.txt'
313 313 # t.std_err_file_path = 'controller-err.txt'
314 314 # t.environment_variables['PYTHONPATH'] = r"\\blue\domainusers$\bgranger\Python\Python25\Lib\site-packages"
315 315 # j.add_task(t)
316 316
General Comments 0
You need to be logged in to leave comments. Login now