##// END OF EJS Templates
allow utils.text.indent to [optionally] flatten existing indentation.
MinRK -
Show More
@@ -1,241 +1,241 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A base class for a configurable application.
3 A base class for a configurable application.
4
4
5 Authors:
5 Authors:
6
6
7 * Brian Granger
7 * Brian Granger
8 """
8 """
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2008-2011 The IPython Development Team
11 # Copyright (C) 2008-2011 The IPython Development Team
12 #
12 #
13 # Distributed under the terms of the BSD License. The full license is in
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Imports
18 # Imports
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 from copy import deepcopy
21 from copy import deepcopy
22 import logging
22 import logging
23 import sys
23 import sys
24
24
25 from IPython.config.configurable import SingletonConfigurable
25 from IPython.config.configurable import SingletonConfigurable
26 from IPython.config.loader import (
26 from IPython.config.loader import (
27 KeyValueConfigLoader, PyFileConfigLoader, Config
27 KeyValueConfigLoader, PyFileConfigLoader, Config
28 )
28 )
29
29
30 from IPython.utils.traitlets import (
30 from IPython.utils.traitlets import (
31 Unicode, List, Int, Enum, Dict
31 Unicode, List, Int, Enum, Dict
32 )
32 )
33 from IPython.utils.text import indent
33 from IPython.utils.text import indent
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Descriptions for
36 # Descriptions for
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38
38
39 flag_description = """
39 flag_description = """
40 Flags are command-line arguments passed as '--<flag>'.
40 Flags are command-line arguments passed as '--<flag>'.
41 These take no parameters, unlike regular key-value arguments.
41 These take no parameters, unlike regular key-value arguments.
42 They are typically used for setting boolean flags, or enabling
42 They are typically used for setting boolean flags, or enabling
43 modes that involve setting multiple options together.
43 modes that involve setting multiple options together.
44 """.strip() # trim newlines of front and back
44 """.strip() # trim newlines of front and back
45
45
46 alias_description = """
46 alias_description = """
47 These are commonly set parameters, given abbreviated aliases for convenience.
47 These are commonly set parameters, given abbreviated aliases for convenience.
48 They are set in the same `name=value` way as class parameters, where
48 They are set in the same `name=value` way as class parameters, where
49 <name> is replaced by the real parameter for which it is an alias.
49 <name> is replaced by the real parameter for which it is an alias.
50 """.strip() # trim newlines of front and back
50 """.strip() # trim newlines of front and back
51
51
52 keyvalue_description = """
52 keyvalue_description = """
53 Parameters are set from command-line arguments of the form:
53 Parameters are set from command-line arguments of the form:
54 `Class.trait=value`. Parameters will *never* be prefixed with '-'.
54 `Class.trait=value`. Parameters will *never* be prefixed with '-'.
55 This line is evaluated in Python, so simple expressions are allowed, e.g.
55 This line is evaluated in Python, so simple expressions are allowed, e.g.
56 `C.a='range(3)'` For setting C.a=[0,1,2]
56 `C.a='range(3)'` For setting C.a=[0,1,2]
57 """.strip() # trim newlines of front and back
57 """.strip() # trim newlines of front and back
58
58
59 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
60 # Application class
60 # Application class
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62
62
63
63
64 class ApplicationError(Exception):
64 class ApplicationError(Exception):
65 pass
65 pass
66
66
67
67
68 class Application(SingletonConfigurable):
68 class Application(SingletonConfigurable):
69 """A singleton application with full configuration support."""
69 """A singleton application with full configuration support."""
70
70
71 # The name of the application, will usually match the name of the command
71 # The name of the application, will usually match the name of the command
72 # line application
72 # line application
73 name = Unicode(u'application')
73 name = Unicode(u'application')
74
74
75 # The description of the application that is printed at the beginning
75 # The description of the application that is printed at the beginning
76 # of the help.
76 # of the help.
77 description = Unicode(u'This is an application.')
77 description = Unicode(u'This is an application.')
78 # default section descriptions
78 # default section descriptions
79 flag_description = Unicode(flag_description)
79 flag_description = Unicode(flag_description)
80 alias_description = Unicode(alias_description)
80 alias_description = Unicode(alias_description)
81 keyvalue_description = Unicode(keyvalue_description)
81 keyvalue_description = Unicode(keyvalue_description)
82
82
83
83
84 # A sequence of Configurable subclasses whose config=True attributes will
84 # A sequence of Configurable subclasses whose config=True attributes will
85 # be exposed at the command line.
85 # be exposed at the command line.
86 classes = List([])
86 classes = List([])
87
87
88 # The version string of this application.
88 # The version string of this application.
89 version = Unicode(u'0.0')
89 version = Unicode(u'0.0')
90
90
91 # The log level for the application
91 # The log level for the application
92 log_level = Enum((0,10,20,30,40,50), default_value=logging.WARN,
92 log_level = Enum((0,10,20,30,40,50), default_value=logging.WARN,
93 config=True,
93 config=True,
94 help="Set the log level (0,10,20,30,40,50).")
94 help="Set the log level (0,10,20,30,40,50).")
95
95
96 # the alias map for configurables
96 # the alias map for configurables
97 aliases = Dict(dict(log_level='Application.log_level'))
97 aliases = Dict(dict(log_level='Application.log_level'))
98
98
99 # flags for loading Configurables or store_const style flags
99 # flags for loading Configurables or store_const style flags
100 # flags are loaded from this dict by '--key' flags
100 # flags are loaded from this dict by '--key' flags
101 # this must be a dict of two-tuples, the first element being the Config/dict
101 # this must be a dict of two-tuples, the first element being the Config/dict
102 # and the second being the help string for the flag
102 # and the second being the help string for the flag
103 flags = Dict()
103 flags = Dict()
104
104
105
105
106 def __init__(self, **kwargs):
106 def __init__(self, **kwargs):
107 SingletonConfigurable.__init__(self, **kwargs)
107 SingletonConfigurable.__init__(self, **kwargs)
108 # Add my class to self.classes so my attributes appear in command line
108 # Add my class to self.classes so my attributes appear in command line
109 # options.
109 # options.
110 self.classes.insert(0, self.__class__)
110 self.classes.insert(0, self.__class__)
111
111
112 # ensure self.flags dict is valid
112 # ensure self.flags dict is valid
113 for key,value in self.flags.iteritems():
113 for key,value in self.flags.iteritems():
114 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
114 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
115 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
115 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
116 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
116 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
117 self.init_logging()
117 self.init_logging()
118
118
119 def _config_changed(self, name, old, new):
119 def _config_changed(self, name, old, new):
120 SingletonConfigurable._config_changed(self, name, old, new)
120 SingletonConfigurable._config_changed(self, name, old, new)
121 self.log.debug('Config changed:')
121 self.log.debug('Config changed:')
122 self.log.debug(repr(new))
122 self.log.debug(repr(new))
123
123
124 def init_logging(self):
124 def init_logging(self):
125 """Start logging for this application.
125 """Start logging for this application.
126
126
127 The default is to log to stdout using a StreaHandler. The log level
127 The default is to log to stdout using a StreaHandler. The log level
128 starts at loggin.WARN, but this can be adjusted by setting the
128 starts at loggin.WARN, but this can be adjusted by setting the
129 ``log_level`` attribute.
129 ``log_level`` attribute.
130 """
130 """
131 self.log = logging.getLogger(self.__class__.__name__)
131 self.log = logging.getLogger(self.__class__.__name__)
132 self.log.setLevel(self.log_level)
132 self.log.setLevel(self.log_level)
133 self._log_handler = logging.StreamHandler()
133 self._log_handler = logging.StreamHandler()
134 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
134 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
135 self._log_handler.setFormatter(self._log_formatter)
135 self._log_handler.setFormatter(self._log_formatter)
136 self.log.addHandler(self._log_handler)
136 self.log.addHandler(self._log_handler)
137
137
138 def _log_level_changed(self, name, old, new):
138 def _log_level_changed(self, name, old, new):
139 """Adjust the log level when log_level is set."""
139 """Adjust the log level when log_level is set."""
140 self.log.setLevel(new)
140 self.log.setLevel(new)
141
141
142 def print_alias_help(self):
142 def print_alias_help(self):
143 """print the alias part of the help"""
143 """print the alias part of the help"""
144 if not self.aliases:
144 if not self.aliases:
145 return
145 return
146
146
147 print "Aliases"
147 print "Aliases"
148 print "-------"
148 print "-------"
149 print self.alias_description
149 print self.alias_description
150 print
150 print
151
151
152 classdict = {}
152 classdict = {}
153 for c in self.classes:
153 for c in self.classes:
154 classdict[c.__name__] = c
154 classdict[c.__name__] = c
155
155
156 for alias, longname in self.aliases.iteritems():
156 for alias, longname in self.aliases.iteritems():
157 classname, traitname = longname.split('.',1)
157 classname, traitname = longname.split('.',1)
158 cls = classdict[classname]
158 cls = classdict[classname]
159
159
160 trait = cls.class_traits(config=True)[traitname]
160 trait = cls.class_traits(config=True)[traitname]
161 help = trait.get_metadata('help')
161 help = trait.get_metadata('help')
162 print alias, "(%s)"%longname, ':', trait.__class__.__name__
162 print alias, "(%s)"%longname, ':', trait.__class__.__name__
163 if help:
163 if help:
164 print indent(help)
164 print indent(help, flatten=True)
165 print
165 print
166
166
167 def print_flag_help(self):
167 def print_flag_help(self):
168 """print the flag part of the help"""
168 """print the flag part of the help"""
169 if not self.flags:
169 if not self.flags:
170 return
170 return
171
171
172 print "Flags"
172 print "Flags"
173 print "-----"
173 print "-----"
174 print self.flag_description
174 print self.flag_description
175 print
175 print
176
176
177 for m, (cfg,help) in self.flags.iteritems():
177 for m, (cfg,help) in self.flags.iteritems():
178 print '--'+m
178 print '--'+m
179 print indent(help)
179 print indent(help, flatten=True)
180 print
180 print
181
181
182 def print_help(self):
182 def print_help(self):
183 """Print the help for each Configurable class in self.classes."""
183 """Print the help for each Configurable class in self.classes."""
184 self.print_flag_help()
184 self.print_flag_help()
185 self.print_alias_help()
185 self.print_alias_help()
186 if self.classes:
186 if self.classes:
187 print "Class parameters"
187 print "Class parameters"
188 print "----------------"
188 print "----------------"
189 print self.keyvalue_description
189 print self.keyvalue_description
190 print
190 print
191
191
192 for cls in self.classes:
192 for cls in self.classes:
193 cls.class_print_help()
193 cls.class_print_help()
194 print
194 print
195
195
196 def print_description(self):
196 def print_description(self):
197 """Print the application description."""
197 """Print the application description."""
198 print self.description
198 print self.description
199 print
199 print
200
200
201 def print_version(self):
201 def print_version(self):
202 """Print the version string."""
202 """Print the version string."""
203 print self.version
203 print self.version
204
204
205 def update_config(self, config):
205 def update_config(self, config):
206 """Fire the traits events when the config is updated."""
206 """Fire the traits events when the config is updated."""
207 # Save a copy of the current config.
207 # Save a copy of the current config.
208 newconfig = deepcopy(self.config)
208 newconfig = deepcopy(self.config)
209 # Merge the new config into the current one.
209 # Merge the new config into the current one.
210 newconfig._merge(config)
210 newconfig._merge(config)
211 # Save the combined config as self.config, which triggers the traits
211 # Save the combined config as self.config, which triggers the traits
212 # events.
212 # events.
213 self.config = newconfig
213 self.config = newconfig
214
214
215 def parse_command_line(self, argv=None):
215 def parse_command_line(self, argv=None):
216 """Parse the command line arguments."""
216 """Parse the command line arguments."""
217 argv = sys.argv[1:] if argv is None else argv
217 argv = sys.argv[1:] if argv is None else argv
218
218
219 if '-h' in argv or '--help' in argv:
219 if '-h' in argv or '--help' in argv:
220 self.print_description()
220 self.print_description()
221 self.print_help()
221 self.print_help()
222 self.exit(1)
222 self.exit(1)
223
223
224 if '--version' in argv:
224 if '--version' in argv:
225 self.print_version()
225 self.print_version()
226 self.exit(1)
226 self.exit(1)
227
227
228 loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
228 loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
229 flags=self.flags)
229 flags=self.flags)
230 config = loader.load_config()
230 config = loader.load_config()
231 self.update_config(config)
231 self.update_config(config)
232
232
233 def load_config_file(self, filename, path=None):
233 def load_config_file(self, filename, path=None):
234 """Load a .py based config file by filename and path."""
234 """Load a .py based config file by filename and path."""
235 loader = PyFileConfigLoader(filename, path=path)
235 loader = PyFileConfigLoader(filename, path=path)
236 config = loader.load_config()
236 config = loader.load_config()
237 self.update_config(config)
237 self.update_config(config)
238
238
239 def exit(self, exit_status=0):
239 def exit(self, exit_status=0):
240 self.log.debug("Exiting application: %s" % self.name)
240 self.log.debug("Exiting application: %s" % self.name)
241 sys.exit(exit_status)
241 sys.exit(exit_status)
@@ -1,225 +1,224 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 A base class for objects that are configurable.
4 A base class for objects that are configurable.
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * Fernando Perez
9 * Fernando Perez
10 """
10 """
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2008-2010 The IPython Development Team
13 # Copyright (C) 2008-2010 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 from copy import deepcopy
23 from copy import deepcopy
24 import datetime
24 import datetime
25
25
26 from loader import Config
26 from loader import Config
27 from IPython.utils.traitlets import HasTraits, Instance
27 from IPython.utils.traitlets import HasTraits, Instance
28 from IPython.utils.text import indent
28 from IPython.utils.text import indent
29
29
30
30
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32 # Helper classes for Configurables
32 # Helper classes for Configurables
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34
34
35
35
36 class ConfigurableError(Exception):
36 class ConfigurableError(Exception):
37 pass
37 pass
38
38
39
39
40 class MultipleInstanceError(ConfigurableError):
40 class MultipleInstanceError(ConfigurableError):
41 pass
41 pass
42
42
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44 # Configurable implementation
44 # Configurable implementation
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47
48 class Configurable(HasTraits):
47 class Configurable(HasTraits):
49
48
50 config = Instance(Config,(),{})
49 config = Instance(Config,(),{})
51 created = None
50 created = None
52
51
53 def __init__(self, **kwargs):
52 def __init__(self, **kwargs):
54 """Create a conigurable given a config config.
53 """Create a conigurable given a config config.
55
54
56 Parameters
55 Parameters
57 ----------
56 ----------
58 config : Config
57 config : Config
59 If this is empty, default values are used. If config is a
58 If this is empty, default values are used. If config is a
60 :class:`Config` instance, it will be used to configure the
59 :class:`Config` instance, it will be used to configure the
61 instance.
60 instance.
62
61
63 Notes
62 Notes
64 -----
63 -----
65 Subclasses of Configurable must call the :meth:`__init__` method of
64 Subclasses of Configurable must call the :meth:`__init__` method of
66 :class:`Configurable` *before* doing anything else and using
65 :class:`Configurable` *before* doing anything else and using
67 :func:`super`::
66 :func:`super`::
68
67
69 class MyConfigurable(Configurable):
68 class MyConfigurable(Configurable):
70 def __init__(self, config=None):
69 def __init__(self, config=None):
71 super(MyConfigurable, self).__init__(config)
70 super(MyConfigurable, self).__init__(config)
72 # Then any other code you need to finish initialization.
71 # Then any other code you need to finish initialization.
73
72
74 This ensures that instances will be configured properly.
73 This ensures that instances will be configured properly.
75 """
74 """
76 config = kwargs.pop('config', None)
75 config = kwargs.pop('config', None)
77 if config is not None:
76 if config is not None:
78 # We used to deepcopy, but for now we are trying to just save
77 # We used to deepcopy, but for now we are trying to just save
79 # by reference. This *could* have side effects as all components
78 # by reference. This *could* have side effects as all components
80 # will share config. In fact, I did find such a side effect in
79 # will share config. In fact, I did find such a side effect in
81 # _config_changed below. If a config attribute value was a mutable type
80 # _config_changed below. If a config attribute value was a mutable type
82 # all instances of a component were getting the same copy, effectively
81 # all instances of a component were getting the same copy, effectively
83 # making that a class attribute.
82 # making that a class attribute.
84 # self.config = deepcopy(config)
83 # self.config = deepcopy(config)
85 self.config = config
84 self.config = config
86 # This should go second so individual keyword arguments override
85 # This should go second so individual keyword arguments override
87 # the values in config.
86 # the values in config.
88 super(Configurable, self).__init__(**kwargs)
87 super(Configurable, self).__init__(**kwargs)
89 self.created = datetime.datetime.now()
88 self.created = datetime.datetime.now()
90
89
91 #-------------------------------------------------------------------------
90 #-------------------------------------------------------------------------
92 # Static trait notifiations
91 # Static trait notifiations
93 #-------------------------------------------------------------------------
92 #-------------------------------------------------------------------------
94
93
95 def _config_changed(self, name, old, new):
94 def _config_changed(self, name, old, new):
96 """Update all the class traits having ``config=True`` as metadata.
95 """Update all the class traits having ``config=True`` as metadata.
97
96
98 For any class trait with a ``config`` metadata attribute that is
97 For any class trait with a ``config`` metadata attribute that is
99 ``True``, we update the trait with the value of the corresponding
98 ``True``, we update the trait with the value of the corresponding
100 config entry.
99 config entry.
101 """
100 """
102 # Get all traits with a config metadata entry that is True
101 # Get all traits with a config metadata entry that is True
103 traits = self.traits(config=True)
102 traits = self.traits(config=True)
104
103
105 # We auto-load config section for this class as well as any parent
104 # We auto-load config section for this class as well as any parent
106 # classes that are Configurable subclasses. This starts with Configurable
105 # classes that are Configurable subclasses. This starts with Configurable
107 # and works down the mro loading the config for each section.
106 # and works down the mro loading the config for each section.
108 section_names = [cls.__name__ for cls in \
107 section_names = [cls.__name__ for cls in \
109 reversed(self.__class__.__mro__) if
108 reversed(self.__class__.__mro__) if
110 issubclass(cls, Configurable) and issubclass(self.__class__, cls)]
109 issubclass(cls, Configurable) and issubclass(self.__class__, cls)]
111
110
112 for sname in section_names:
111 for sname in section_names:
113 # Don't do a blind getattr as that would cause the config to
112 # Don't do a blind getattr as that would cause the config to
114 # dynamically create the section with name self.__class__.__name__.
113 # dynamically create the section with name self.__class__.__name__.
115 if new._has_section(sname):
114 if new._has_section(sname):
116 my_config = new[sname]
115 my_config = new[sname]
117 for k, v in traits.iteritems():
116 for k, v in traits.iteritems():
118 # Don't allow traitlets with config=True to start with
117 # Don't allow traitlets with config=True to start with
119 # uppercase. Otherwise, they are confused with Config
118 # uppercase. Otherwise, they are confused with Config
120 # subsections. But, developers shouldn't have uppercase
119 # subsections. But, developers shouldn't have uppercase
121 # attributes anyways! (PEP 6)
120 # attributes anyways! (PEP 6)
122 if k[0].upper()==k[0] and not k.startswith('_'):
121 if k[0].upper()==k[0] and not k.startswith('_'):
123 raise ConfigurableError('Configurable traitlets with '
122 raise ConfigurableError('Configurable traitlets with '
124 'config=True must start with a lowercase so they are '
123 'config=True must start with a lowercase so they are '
125 'not confused with Config subsections: %s.%s' % \
124 'not confused with Config subsections: %s.%s' % \
126 (self.__class__.__name__, k))
125 (self.__class__.__name__, k))
127 try:
126 try:
128 # Here we grab the value from the config
127 # Here we grab the value from the config
129 # If k has the naming convention of a config
128 # If k has the naming convention of a config
130 # section, it will be auto created.
129 # section, it will be auto created.
131 config_value = my_config[k]
130 config_value = my_config[k]
132 except KeyError:
131 except KeyError:
133 pass
132 pass
134 else:
133 else:
135 # print "Setting %s.%s from %s.%s=%r" % \
134 # print "Setting %s.%s from %s.%s=%r" % \
136 # (self.__class__.__name__,k,sname,k,config_value)
135 # (self.__class__.__name__,k,sname,k,config_value)
137 # We have to do a deepcopy here if we don't deepcopy the entire
136 # We have to do a deepcopy here if we don't deepcopy the entire
138 # config object. If we don't, a mutable config_value will be
137 # config object. If we don't, a mutable config_value will be
139 # shared by all instances, effectively making it a class attribute.
138 # shared by all instances, effectively making it a class attribute.
140 setattr(self, k, deepcopy(config_value))
139 setattr(self, k, deepcopy(config_value))
141
140
142 @classmethod
141 @classmethod
143 def class_get_help(cls):
142 def class_get_help(cls):
144 """Get the help string for this class in ReST format."""
143 """Get the help string for this class in ReST format."""
145 cls_traits = cls.class_traits(config=True)
144 cls_traits = cls.class_traits(config=True)
146 final_help = []
145 final_help = []
147 final_help.append(u'%s options' % cls.__name__)
146 final_help.append(u'%s options' % cls.__name__)
148 final_help.append(len(final_help[0])*u'-')
147 final_help.append(len(final_help[0])*u'-')
149 for k, v in cls_traits.items():
148 for k, v in cls_traits.items():
150 help = v.get_metadata('help')
149 help = v.get_metadata('help')
151 header = "%s.%s : %s" % (cls.__name__, k, v.__class__.__name__)
150 header = "%s.%s : %s" % (cls.__name__, k, v.__class__.__name__)
152 final_help.append(header)
151 final_help.append(header)
153 if help is not None:
152 if help is not None:
154 final_help.append(indent(help))
153 final_help.append(indent(help, flatten=True))
155 return '\n'.join(final_help)
154 return '\n'.join(final_help)
156
155
157 @classmethod
156 @classmethod
158 def class_print_help(cls):
157 def class_print_help(cls):
159 print cls.class_get_help()
158 print cls.class_get_help()
160
159
161
160
162 class SingletonConfigurable(Configurable):
161 class SingletonConfigurable(Configurable):
163 """A configurable that only allows one instance.
162 """A configurable that only allows one instance.
164
163
165 This class is for classes that should only have one instance of itself
164 This class is for classes that should only have one instance of itself
166 or *any* subclass. To create and retrieve such a class use the
165 or *any* subclass. To create and retrieve such a class use the
167 :meth:`SingletonConfigurable.instance` method.
166 :meth:`SingletonConfigurable.instance` method.
168 """
167 """
169
168
170 _instance = None
169 _instance = None
171
170
172 @classmethod
171 @classmethod
173 def instance(cls, *args, **kwargs):
172 def instance(cls, *args, **kwargs):
174 """Returns a global instance of this class.
173 """Returns a global instance of this class.
175
174
176 This method create a new instance if none have previously been created
175 This method create a new instance if none have previously been created
177 and returns a previously created instance is one already exists.
176 and returns a previously created instance is one already exists.
178
177
179 The arguments and keyword arguments passed to this method are passed
178 The arguments and keyword arguments passed to this method are passed
180 on to the :meth:`__init__` method of the class upon instantiation.
179 on to the :meth:`__init__` method of the class upon instantiation.
181
180
182 Examples
181 Examples
183 --------
182 --------
184
183
185 Create a singleton class using instance, and retrieve it::
184 Create a singleton class using instance, and retrieve it::
186
185
187 >>> from IPython.config.configurable import SingletonConfigurable
186 >>> from IPython.config.configurable import SingletonConfigurable
188 >>> class Foo(SingletonConfigurable): pass
187 >>> class Foo(SingletonConfigurable): pass
189 >>> foo = Foo.instance()
188 >>> foo = Foo.instance()
190 >>> foo == Foo.instance()
189 >>> foo == Foo.instance()
191 True
190 True
192
191
193 Create a subclass that is retrived using the base class instance::
192 Create a subclass that is retrived using the base class instance::
194
193
195 >>> class Bar(SingletonConfigurable): pass
194 >>> class Bar(SingletonConfigurable): pass
196 >>> class Bam(Bar): pass
195 >>> class Bam(Bar): pass
197 >>> bam = Bam.instance()
196 >>> bam = Bam.instance()
198 >>> bam == Bar.instance()
197 >>> bam == Bar.instance()
199 True
198 True
200 """
199 """
201 # Create and save the instance
200 # Create and save the instance
202 if cls._instance is None:
201 if cls._instance is None:
203 inst = cls(*args, **kwargs)
202 inst = cls(*args, **kwargs)
204 # Now make sure that the instance will also be returned by
203 # Now make sure that the instance will also be returned by
205 # the subclasses instance attribute.
204 # the subclasses instance attribute.
206 for subclass in cls.mro():
205 for subclass in cls.mro():
207 if issubclass(cls, subclass) and \
206 if issubclass(cls, subclass) and \
208 issubclass(subclass, SingletonConfigurable) and \
207 issubclass(subclass, SingletonConfigurable) and \
209 subclass != SingletonConfigurable:
208 subclass != SingletonConfigurable:
210 subclass._instance = inst
209 subclass._instance = inst
211 else:
210 else:
212 break
211 break
213 if isinstance(cls._instance, cls):
212 if isinstance(cls._instance, cls):
214 return cls._instance
213 return cls._instance
215 else:
214 else:
216 raise MultipleInstanceError(
215 raise MultipleInstanceError(
217 'Multiple incompatible subclass instances of '
216 'Multiple incompatible subclass instances of '
218 '%s are being created.' % cls.__name__
217 '%s are being created.' % cls.__name__
219 )
218 )
220
219
221 @classmethod
220 @classmethod
222 def initialized(cls):
221 def initialized(cls):
223 """Has an instance been created?"""
222 """Has an instance been created?"""
224 return hasattr(cls, "_instance") and cls._instance is not None
223 return hasattr(cls, "_instance") and cls._instance is not None
225
224
@@ -1,497 +1,521 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Utilities for working with strings and text.
3 Utilities for working with strings and text.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import __main__
17 import __main__
18
18
19 import os
19 import os
20 import re
20 import re
21 import shutil
21 import shutil
22
22
23 from IPython.external.path import path
23 from IPython.external.path import path
24
24
25 from IPython.utils.io import nlprint
25 from IPython.utils.io import nlprint
26 from IPython.utils.data import flatten
26 from IPython.utils.data import flatten
27
27
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 # Code
29 # Code
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31
31
32
32
33 def unquote_ends(istr):
33 def unquote_ends(istr):
34 """Remove a single pair of quotes from the endpoints of a string."""
34 """Remove a single pair of quotes from the endpoints of a string."""
35
35
36 if not istr:
36 if not istr:
37 return istr
37 return istr
38 if (istr[0]=="'" and istr[-1]=="'") or \
38 if (istr[0]=="'" and istr[-1]=="'") or \
39 (istr[0]=='"' and istr[-1]=='"'):
39 (istr[0]=='"' and istr[-1]=='"'):
40 return istr[1:-1]
40 return istr[1:-1]
41 else:
41 else:
42 return istr
42 return istr
43
43
44
44
45 class LSString(str):
45 class LSString(str):
46 """String derivative with a special access attributes.
46 """String derivative with a special access attributes.
47
47
48 These are normal strings, but with the special attributes:
48 These are normal strings, but with the special attributes:
49
49
50 .l (or .list) : value as list (split on newlines).
50 .l (or .list) : value as list (split on newlines).
51 .n (or .nlstr): original value (the string itself).
51 .n (or .nlstr): original value (the string itself).
52 .s (or .spstr): value as whitespace-separated string.
52 .s (or .spstr): value as whitespace-separated string.
53 .p (or .paths): list of path objects
53 .p (or .paths): list of path objects
54
54
55 Any values which require transformations are computed only once and
55 Any values which require transformations are computed only once and
56 cached.
56 cached.
57
57
58 Such strings are very useful to efficiently interact with the shell, which
58 Such strings are very useful to efficiently interact with the shell, which
59 typically only understands whitespace-separated options for commands."""
59 typically only understands whitespace-separated options for commands."""
60
60
61 def get_list(self):
61 def get_list(self):
62 try:
62 try:
63 return self.__list
63 return self.__list
64 except AttributeError:
64 except AttributeError:
65 self.__list = self.split('\n')
65 self.__list = self.split('\n')
66 return self.__list
66 return self.__list
67
67
68 l = list = property(get_list)
68 l = list = property(get_list)
69
69
70 def get_spstr(self):
70 def get_spstr(self):
71 try:
71 try:
72 return self.__spstr
72 return self.__spstr
73 except AttributeError:
73 except AttributeError:
74 self.__spstr = self.replace('\n',' ')
74 self.__spstr = self.replace('\n',' ')
75 return self.__spstr
75 return self.__spstr
76
76
77 s = spstr = property(get_spstr)
77 s = spstr = property(get_spstr)
78
78
79 def get_nlstr(self):
79 def get_nlstr(self):
80 return self
80 return self
81
81
82 n = nlstr = property(get_nlstr)
82 n = nlstr = property(get_nlstr)
83
83
84 def get_paths(self):
84 def get_paths(self):
85 try:
85 try:
86 return self.__paths
86 return self.__paths
87 except AttributeError:
87 except AttributeError:
88 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
88 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
89 return self.__paths
89 return self.__paths
90
90
91 p = paths = property(get_paths)
91 p = paths = property(get_paths)
92
92
93 # FIXME: We need to reimplement type specific displayhook and then add this
93 # FIXME: We need to reimplement type specific displayhook and then add this
94 # back as a custom printer. This should also be moved outside utils into the
94 # back as a custom printer. This should also be moved outside utils into the
95 # core.
95 # core.
96
96
97 # def print_lsstring(arg):
97 # def print_lsstring(arg):
98 # """ Prettier (non-repr-like) and more informative printer for LSString """
98 # """ Prettier (non-repr-like) and more informative printer for LSString """
99 # print "LSString (.p, .n, .l, .s available). Value:"
99 # print "LSString (.p, .n, .l, .s available). Value:"
100 # print arg
100 # print arg
101 #
101 #
102 #
102 #
103 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
103 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
104
104
105
105
106 class SList(list):
106 class SList(list):
107 """List derivative with a special access attributes.
107 """List derivative with a special access attributes.
108
108
109 These are normal lists, but with the special attributes:
109 These are normal lists, but with the special attributes:
110
110
111 .l (or .list) : value as list (the list itself).
111 .l (or .list) : value as list (the list itself).
112 .n (or .nlstr): value as a string, joined on newlines.
112 .n (or .nlstr): value as a string, joined on newlines.
113 .s (or .spstr): value as a string, joined on spaces.
113 .s (or .spstr): value as a string, joined on spaces.
114 .p (or .paths): list of path objects
114 .p (or .paths): list of path objects
115
115
116 Any values which require transformations are computed only once and
116 Any values which require transformations are computed only once and
117 cached."""
117 cached."""
118
118
119 def get_list(self):
119 def get_list(self):
120 return self
120 return self
121
121
122 l = list = property(get_list)
122 l = list = property(get_list)
123
123
124 def get_spstr(self):
124 def get_spstr(self):
125 try:
125 try:
126 return self.__spstr
126 return self.__spstr
127 except AttributeError:
127 except AttributeError:
128 self.__spstr = ' '.join(self)
128 self.__spstr = ' '.join(self)
129 return self.__spstr
129 return self.__spstr
130
130
131 s = spstr = property(get_spstr)
131 s = spstr = property(get_spstr)
132
132
133 def get_nlstr(self):
133 def get_nlstr(self):
134 try:
134 try:
135 return self.__nlstr
135 return self.__nlstr
136 except AttributeError:
136 except AttributeError:
137 self.__nlstr = '\n'.join(self)
137 self.__nlstr = '\n'.join(self)
138 return self.__nlstr
138 return self.__nlstr
139
139
140 n = nlstr = property(get_nlstr)
140 n = nlstr = property(get_nlstr)
141
141
142 def get_paths(self):
142 def get_paths(self):
143 try:
143 try:
144 return self.__paths
144 return self.__paths
145 except AttributeError:
145 except AttributeError:
146 self.__paths = [path(p) for p in self if os.path.exists(p)]
146 self.__paths = [path(p) for p in self if os.path.exists(p)]
147 return self.__paths
147 return self.__paths
148
148
149 p = paths = property(get_paths)
149 p = paths = property(get_paths)
150
150
151 def grep(self, pattern, prune = False, field = None):
151 def grep(self, pattern, prune = False, field = None):
152 """ Return all strings matching 'pattern' (a regex or callable)
152 """ Return all strings matching 'pattern' (a regex or callable)
153
153
154 This is case-insensitive. If prune is true, return all items
154 This is case-insensitive. If prune is true, return all items
155 NOT matching the pattern.
155 NOT matching the pattern.
156
156
157 If field is specified, the match must occur in the specified
157 If field is specified, the match must occur in the specified
158 whitespace-separated field.
158 whitespace-separated field.
159
159
160 Examples::
160 Examples::
161
161
162 a.grep( lambda x: x.startswith('C') )
162 a.grep( lambda x: x.startswith('C') )
163 a.grep('Cha.*log', prune=1)
163 a.grep('Cha.*log', prune=1)
164 a.grep('chm', field=-1)
164 a.grep('chm', field=-1)
165 """
165 """
166
166
167 def match_target(s):
167 def match_target(s):
168 if field is None:
168 if field is None:
169 return s
169 return s
170 parts = s.split()
170 parts = s.split()
171 try:
171 try:
172 tgt = parts[field]
172 tgt = parts[field]
173 return tgt
173 return tgt
174 except IndexError:
174 except IndexError:
175 return ""
175 return ""
176
176
177 if isinstance(pattern, basestring):
177 if isinstance(pattern, basestring):
178 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
178 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
179 else:
179 else:
180 pred = pattern
180 pred = pattern
181 if not prune:
181 if not prune:
182 return SList([el for el in self if pred(match_target(el))])
182 return SList([el for el in self if pred(match_target(el))])
183 else:
183 else:
184 return SList([el for el in self if not pred(match_target(el))])
184 return SList([el for el in self if not pred(match_target(el))])
185
185
186 def fields(self, *fields):
186 def fields(self, *fields):
187 """ Collect whitespace-separated fields from string list
187 """ Collect whitespace-separated fields from string list
188
188
189 Allows quick awk-like usage of string lists.
189 Allows quick awk-like usage of string lists.
190
190
191 Example data (in var a, created by 'a = !ls -l')::
191 Example data (in var a, created by 'a = !ls -l')::
192 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
192 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
193 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
193 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
194
194
195 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
195 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
196 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
196 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
197 (note the joining by space).
197 (note the joining by space).
198 a.fields(-1) is ['ChangeLog', 'IPython']
198 a.fields(-1) is ['ChangeLog', 'IPython']
199
199
200 IndexErrors are ignored.
200 IndexErrors are ignored.
201
201
202 Without args, fields() just split()'s the strings.
202 Without args, fields() just split()'s the strings.
203 """
203 """
204 if len(fields) == 0:
204 if len(fields) == 0:
205 return [el.split() for el in self]
205 return [el.split() for el in self]
206
206
207 res = SList()
207 res = SList()
208 for el in [f.split() for f in self]:
208 for el in [f.split() for f in self]:
209 lineparts = []
209 lineparts = []
210
210
211 for fd in fields:
211 for fd in fields:
212 try:
212 try:
213 lineparts.append(el[fd])
213 lineparts.append(el[fd])
214 except IndexError:
214 except IndexError:
215 pass
215 pass
216 if lineparts:
216 if lineparts:
217 res.append(" ".join(lineparts))
217 res.append(" ".join(lineparts))
218
218
219 return res
219 return res
220
220
221 def sort(self,field= None, nums = False):
221 def sort(self,field= None, nums = False):
222 """ sort by specified fields (see fields())
222 """ sort by specified fields (see fields())
223
223
224 Example::
224 Example::
225 a.sort(1, nums = True)
225 a.sort(1, nums = True)
226
226
227 Sorts a by second field, in numerical order (so that 21 > 3)
227 Sorts a by second field, in numerical order (so that 21 > 3)
228
228
229 """
229 """
230
230
231 #decorate, sort, undecorate
231 #decorate, sort, undecorate
232 if field is not None:
232 if field is not None:
233 dsu = [[SList([line]).fields(field), line] for line in self]
233 dsu = [[SList([line]).fields(field), line] for line in self]
234 else:
234 else:
235 dsu = [[line, line] for line in self]
235 dsu = [[line, line] for line in self]
236 if nums:
236 if nums:
237 for i in range(len(dsu)):
237 for i in range(len(dsu)):
238 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
238 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
239 try:
239 try:
240 n = int(numstr)
240 n = int(numstr)
241 except ValueError:
241 except ValueError:
242 n = 0;
242 n = 0;
243 dsu[i][0] = n
243 dsu[i][0] = n
244
244
245
245
246 dsu.sort()
246 dsu.sort()
247 return SList([t[1] for t in dsu])
247 return SList([t[1] for t in dsu])
248
248
249
249
250 # FIXME: We need to reimplement type specific displayhook and then add this
250 # FIXME: We need to reimplement type specific displayhook and then add this
251 # back as a custom printer. This should also be moved outside utils into the
251 # back as a custom printer. This should also be moved outside utils into the
252 # core.
252 # core.
253
253
254 # def print_slist(arg):
254 # def print_slist(arg):
255 # """ Prettier (non-repr-like) and more informative printer for SList """
255 # """ Prettier (non-repr-like) and more informative printer for SList """
256 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
256 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
257 # if hasattr(arg, 'hideonce') and arg.hideonce:
257 # if hasattr(arg, 'hideonce') and arg.hideonce:
258 # arg.hideonce = False
258 # arg.hideonce = False
259 # return
259 # return
260 #
260 #
261 # nlprint(arg)
261 # nlprint(arg)
262 #
262 #
263 # print_slist = result_display.when_type(SList)(print_slist)
263 # print_slist = result_display.when_type(SList)(print_slist)
264
264
265
265
266 def esc_quotes(strng):
266 def esc_quotes(strng):
267 """Return the input string with single and double quotes escaped out"""
267 """Return the input string with single and double quotes escaped out"""
268
268
269 return strng.replace('"','\\"').replace("'","\\'")
269 return strng.replace('"','\\"').replace("'","\\'")
270
270
271
271
272 def make_quoted_expr(s):
272 def make_quoted_expr(s):
273 """Return string s in appropriate quotes, using raw string if possible.
273 """Return string s in appropriate quotes, using raw string if possible.
274
274
275 XXX - example removed because it caused encoding errors in documentation
275 XXX - example removed because it caused encoding errors in documentation
276 generation. We need a new example that doesn't contain invalid chars.
276 generation. We need a new example that doesn't contain invalid chars.
277
277
278 Note the use of raw string and padding at the end to allow trailing
278 Note the use of raw string and padding at the end to allow trailing
279 backslash.
279 backslash.
280 """
280 """
281
281
282 tail = ''
282 tail = ''
283 tailpadding = ''
283 tailpadding = ''
284 raw = ''
284 raw = ''
285 ucode = 'u'
285 ucode = 'u'
286 if "\\" in s:
286 if "\\" in s:
287 raw = 'r'
287 raw = 'r'
288 if s.endswith('\\'):
288 if s.endswith('\\'):
289 tail = '[:-1]'
289 tail = '[:-1]'
290 tailpadding = '_'
290 tailpadding = '_'
291 if '"' not in s:
291 if '"' not in s:
292 quote = '"'
292 quote = '"'
293 elif "'" not in s:
293 elif "'" not in s:
294 quote = "'"
294 quote = "'"
295 elif '"""' not in s and not s.endswith('"'):
295 elif '"""' not in s and not s.endswith('"'):
296 quote = '"""'
296 quote = '"""'
297 elif "'''" not in s and not s.endswith("'"):
297 elif "'''" not in s and not s.endswith("'"):
298 quote = "'''"
298 quote = "'''"
299 else:
299 else:
300 # give up, backslash-escaped string will do
300 # give up, backslash-escaped string will do
301 return '"%s"' % esc_quotes(s)
301 return '"%s"' % esc_quotes(s)
302 res = ucode + raw + quote + s + tailpadding + quote + tail
302 res = ucode + raw + quote + s + tailpadding + quote + tail
303 return res
303 return res
304
304
305
305
306 def qw(words,flat=0,sep=None,maxsplit=-1):
306 def qw(words,flat=0,sep=None,maxsplit=-1):
307 """Similar to Perl's qw() operator, but with some more options.
307 """Similar to Perl's qw() operator, but with some more options.
308
308
309 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
309 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
310
310
311 words can also be a list itself, and with flat=1, the output will be
311 words can also be a list itself, and with flat=1, the output will be
312 recursively flattened.
312 recursively flattened.
313
313
314 Examples:
314 Examples:
315
315
316 >>> qw('1 2')
316 >>> qw('1 2')
317 ['1', '2']
317 ['1', '2']
318
318
319 >>> qw(['a b','1 2',['m n','p q']])
319 >>> qw(['a b','1 2',['m n','p q']])
320 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
320 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
321
321
322 >>> qw(['a b','1 2',['m n','p q']],flat=1)
322 >>> qw(['a b','1 2',['m n','p q']],flat=1)
323 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
323 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
324 """
324 """
325
325
326 if isinstance(words, basestring):
326 if isinstance(words, basestring):
327 return [word.strip() for word in words.split(sep,maxsplit)
327 return [word.strip() for word in words.split(sep,maxsplit)
328 if word and not word.isspace() ]
328 if word and not word.isspace() ]
329 if flat:
329 if flat:
330 return flatten(map(qw,words,[1]*len(words)))
330 return flatten(map(qw,words,[1]*len(words)))
331 return map(qw,words)
331 return map(qw,words)
332
332
333
333
334 def qwflat(words,sep=None,maxsplit=-1):
334 def qwflat(words,sep=None,maxsplit=-1):
335 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
335 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
336 return qw(words,1,sep,maxsplit)
336 return qw(words,1,sep,maxsplit)
337
337
338
338
339 def qw_lol(indata):
339 def qw_lol(indata):
340 """qw_lol('a b') -> [['a','b']],
340 """qw_lol('a b') -> [['a','b']],
341 otherwise it's just a call to qw().
341 otherwise it's just a call to qw().
342
342
343 We need this to make sure the modules_some keys *always* end up as a
343 We need this to make sure the modules_some keys *always* end up as a
344 list of lists."""
344 list of lists."""
345
345
346 if isinstance(indata, basestring):
346 if isinstance(indata, basestring):
347 return [qw(indata)]
347 return [qw(indata)]
348 else:
348 else:
349 return qw(indata)
349 return qw(indata)
350
350
351
351
352 def grep(pat,list,case=1):
352 def grep(pat,list,case=1):
353 """Simple minded grep-like function.
353 """Simple minded grep-like function.
354 grep(pat,list) returns occurrences of pat in list, None on failure.
354 grep(pat,list) returns occurrences of pat in list, None on failure.
355
355
356 It only does simple string matching, with no support for regexps. Use the
356 It only does simple string matching, with no support for regexps. Use the
357 option case=0 for case-insensitive matching."""
357 option case=0 for case-insensitive matching."""
358
358
359 # This is pretty crude. At least it should implement copying only references
359 # This is pretty crude. At least it should implement copying only references
360 # to the original data in case it's big. Now it copies the data for output.
360 # to the original data in case it's big. Now it copies the data for output.
361 out=[]
361 out=[]
362 if case:
362 if case:
363 for term in list:
363 for term in list:
364 if term.find(pat)>-1: out.append(term)
364 if term.find(pat)>-1: out.append(term)
365 else:
365 else:
366 lpat=pat.lower()
366 lpat=pat.lower()
367 for term in list:
367 for term in list:
368 if term.lower().find(lpat)>-1: out.append(term)
368 if term.lower().find(lpat)>-1: out.append(term)
369
369
370 if len(out): return out
370 if len(out): return out
371 else: return None
371 else: return None
372
372
373
373
374 def dgrep(pat,*opts):
374 def dgrep(pat,*opts):
375 """Return grep() on dir()+dir(__builtins__).
375 """Return grep() on dir()+dir(__builtins__).
376
376
377 A very common use of grep() when working interactively."""
377 A very common use of grep() when working interactively."""
378
378
379 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
379 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
380
380
381
381
382 def idgrep(pat):
382 def idgrep(pat):
383 """Case-insensitive dgrep()"""
383 """Case-insensitive dgrep()"""
384
384
385 return dgrep(pat,0)
385 return dgrep(pat,0)
386
386
387
387
388 def igrep(pat,list):
388 def igrep(pat,list):
389 """Synonym for case-insensitive grep."""
389 """Synonym for case-insensitive grep."""
390
390
391 return grep(pat,list,case=0)
391 return grep(pat,list,case=0)
392
392
393
393
394 def indent(str,nspaces=4,ntabs=0):
394 def indent(instr,nspaces=4, ntabs=0, flatten=False):
395 """Indent a string a given number of spaces or tabstops.
395 """Indent a string a given number of spaces or tabstops.
396
396
397 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
397 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
398
399 Parameters
400 ----------
401
402 instr : basestring
403 The string to be indented.
404 nspaces : int (default: 4)
405 The number of spaces to be indented.
406 ntabs : int (default: 0)
407 The number of tabs to be indented.
408 flatten : bool (default: False)
409 Whether to scrub existing indentation. If True, all lines will be
410 aligned to the same indentation. If False, existing indentation will
411 be strictly increased.
412
413 Returns
414 -------
415
416 str|unicode : string indented by ntabs and nspaces.
417
398 """
418 """
399 if str is None:
419 if instr is None:
400 return
420 return
401 ind = '\t'*ntabs+' '*nspaces
421 ind = '\t'*ntabs+' '*nspaces
402 outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
422 if flatten:
423 pat = re.compile(r'^\s*', re.MULTILINE)
424 else:
425 pat = re.compile(r'^', re.MULTILINE)
426 outstr = re.sub(pat, ind, instr)
403 if outstr.endswith(os.linesep+ind):
427 if outstr.endswith(os.linesep+ind):
404 return outstr[:-len(ind)]
428 return outstr[:-len(ind)]
405 else:
429 else:
406 return outstr
430 return outstr
407
431
408 def native_line_ends(filename,backup=1):
432 def native_line_ends(filename,backup=1):
409 """Convert (in-place) a file to line-ends native to the current OS.
433 """Convert (in-place) a file to line-ends native to the current OS.
410
434
411 If the optional backup argument is given as false, no backup of the
435 If the optional backup argument is given as false, no backup of the
412 original file is left. """
436 original file is left. """
413
437
414 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
438 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
415
439
416 bak_filename = filename + backup_suffixes[os.name]
440 bak_filename = filename + backup_suffixes[os.name]
417
441
418 original = open(filename).read()
442 original = open(filename).read()
419 shutil.copy2(filename,bak_filename)
443 shutil.copy2(filename,bak_filename)
420 try:
444 try:
421 new = open(filename,'wb')
445 new = open(filename,'wb')
422 new.write(os.linesep.join(original.splitlines()))
446 new.write(os.linesep.join(original.splitlines()))
423 new.write(os.linesep) # ALWAYS put an eol at the end of the file
447 new.write(os.linesep) # ALWAYS put an eol at the end of the file
424 new.close()
448 new.close()
425 except:
449 except:
426 os.rename(bak_filename,filename)
450 os.rename(bak_filename,filename)
427 if not backup:
451 if not backup:
428 try:
452 try:
429 os.remove(bak_filename)
453 os.remove(bak_filename)
430 except:
454 except:
431 pass
455 pass
432
456
433
457
434 def list_strings(arg):
458 def list_strings(arg):
435 """Always return a list of strings, given a string or list of strings
459 """Always return a list of strings, given a string or list of strings
436 as input.
460 as input.
437
461
438 :Examples:
462 :Examples:
439
463
440 In [7]: list_strings('A single string')
464 In [7]: list_strings('A single string')
441 Out[7]: ['A single string']
465 Out[7]: ['A single string']
442
466
443 In [8]: list_strings(['A single string in a list'])
467 In [8]: list_strings(['A single string in a list'])
444 Out[8]: ['A single string in a list']
468 Out[8]: ['A single string in a list']
445
469
446 In [9]: list_strings(['A','list','of','strings'])
470 In [9]: list_strings(['A','list','of','strings'])
447 Out[9]: ['A', 'list', 'of', 'strings']
471 Out[9]: ['A', 'list', 'of', 'strings']
448 """
472 """
449
473
450 if isinstance(arg,basestring): return [arg]
474 if isinstance(arg,basestring): return [arg]
451 else: return arg
475 else: return arg
452
476
453
477
454 def marquee(txt='',width=78,mark='*'):
478 def marquee(txt='',width=78,mark='*'):
455 """Return the input string centered in a 'marquee'.
479 """Return the input string centered in a 'marquee'.
456
480
457 :Examples:
481 :Examples:
458
482
459 In [16]: marquee('A test',40)
483 In [16]: marquee('A test',40)
460 Out[16]: '**************** A test ****************'
484 Out[16]: '**************** A test ****************'
461
485
462 In [17]: marquee('A test',40,'-')
486 In [17]: marquee('A test',40,'-')
463 Out[17]: '---------------- A test ----------------'
487 Out[17]: '---------------- A test ----------------'
464
488
465 In [18]: marquee('A test',40,' ')
489 In [18]: marquee('A test',40,' ')
466 Out[18]: ' A test '
490 Out[18]: ' A test '
467
491
468 """
492 """
469 if not txt:
493 if not txt:
470 return (mark*width)[:width]
494 return (mark*width)[:width]
471 nmark = (width-len(txt)-2)/len(mark)/2
495 nmark = (width-len(txt)-2)/len(mark)/2
472 if nmark < 0: nmark =0
496 if nmark < 0: nmark =0
473 marks = mark*nmark
497 marks = mark*nmark
474 return '%s %s %s' % (marks,txt,marks)
498 return '%s %s %s' % (marks,txt,marks)
475
499
476
500
477 ini_spaces_re = re.compile(r'^(\s+)')
501 ini_spaces_re = re.compile(r'^(\s+)')
478
502
479 def num_ini_spaces(strng):
503 def num_ini_spaces(strng):
480 """Return the number of initial spaces in a string"""
504 """Return the number of initial spaces in a string"""
481
505
482 ini_spaces = ini_spaces_re.match(strng)
506 ini_spaces = ini_spaces_re.match(strng)
483 if ini_spaces:
507 if ini_spaces:
484 return ini_spaces.end()
508 return ini_spaces.end()
485 else:
509 else:
486 return 0
510 return 0
487
511
488
512
489 def format_screen(strng):
513 def format_screen(strng):
490 """Format a string for screen printing.
514 """Format a string for screen printing.
491
515
492 This removes some latex-type format codes."""
516 This removes some latex-type format codes."""
493 # Paragraph continue
517 # Paragraph continue
494 par_re = re.compile(r'\\$',re.MULTILINE)
518 par_re = re.compile(r'\\$',re.MULTILINE)
495 strng = par_re.sub('',strng)
519 strng = par_re.sub('',strng)
496 return strng
520 return strng
497
521
General Comments 0
You need to be logged in to leave comments. Login now