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