##// END OF EJS Templates
allow_new_attr to Struct. _ip.options now disallows attr creation
vivainio -
Show More
@@ -1,330 +1,336 b''
1 ''' IPython customization API
1 ''' IPython customization API
2
2
3 Your one-stop module for configuring & extending ipython
3 Your one-stop module for configuring & extending ipython
4
4
5 The API will probably break when ipython 1.0 is released, but so
5 The API will probably break when ipython 1.0 is released, but so
6 will the other configuration method (rc files).
6 will the other configuration method (rc files).
7
7
8 All names prefixed by underscores are for internal use, not part
8 All names prefixed by underscores are for internal use, not part
9 of the public api.
9 of the public api.
10
10
11 Below is an example that you can just put to a module and import from ipython.
11 Below is an example that you can just put to a module and import from ipython.
12
12
13 A good practice is to install the config script below as e.g.
13 A good practice is to install the config script below as e.g.
14
14
15 ~/.ipython/my_private_conf.py
15 ~/.ipython/my_private_conf.py
16
16
17 And do
17 And do
18
18
19 import_mod my_private_conf
19 import_mod my_private_conf
20
20
21 in ~/.ipython/ipythonrc
21 in ~/.ipython/ipythonrc
22
22
23 That way the module is imported at startup and you can have all your
23 That way the module is imported at startup and you can have all your
24 personal configuration (as opposed to boilerplate ipythonrc-PROFILENAME
24 personal configuration (as opposed to boilerplate ipythonrc-PROFILENAME
25 stuff) in there.
25 stuff) in there.
26
26
27 -----------------------------------------------
27 -----------------------------------------------
28 import IPython.ipapi
28 import IPython.ipapi
29 ip = IPython.ipapi.get()
29 ip = IPython.ipapi.get()
30
30
31 def ankka_f(self, arg):
31 def ankka_f(self, arg):
32 print "Ankka",self,"says uppercase:",arg.upper()
32 print "Ankka",self,"says uppercase:",arg.upper()
33
33
34 ip.expose_magic("ankka",ankka_f)
34 ip.expose_magic("ankka",ankka_f)
35
35
36 ip.magic('alias sayhi echo "Testing, hi ok"')
36 ip.magic('alias sayhi echo "Testing, hi ok"')
37 ip.magic('alias helloworld echo "Hello world"')
37 ip.magic('alias helloworld echo "Hello world"')
38 ip.system('pwd')
38 ip.system('pwd')
39
39
40 ip.ex('import re')
40 ip.ex('import re')
41 ip.ex("""
41 ip.ex("""
42 def funcci(a,b):
42 def funcci(a,b):
43 print a+b
43 print a+b
44 print funcci(3,4)
44 print funcci(3,4)
45 """)
45 """)
46 ip.ex("funcci(348,9)")
46 ip.ex("funcci(348,9)")
47
47
48 def jed_editor(self,filename, linenum=None):
48 def jed_editor(self,filename, linenum=None):
49 print "Calling my own editor, jed ... via hook!"
49 print "Calling my own editor, jed ... via hook!"
50 import os
50 import os
51 if linenum is None: linenum = 0
51 if linenum is None: linenum = 0
52 os.system('jed +%d %s' % (linenum, filename))
52 os.system('jed +%d %s' % (linenum, filename))
53 print "exiting jed"
53 print "exiting jed"
54
54
55 ip.set_hook('editor',jed_editor)
55 ip.set_hook('editor',jed_editor)
56
56
57 o = ip.options
57 o = ip.options
58 o.autocall = 2 # FULL autocall mode
58 o.autocall = 2 # FULL autocall mode
59
59
60 print "done!"
60 print "done!"
61 '''
61 '''
62
62
63 # stdlib imports
63 # stdlib imports
64 import __builtin__
64 import __builtin__
65 import sys
65 import sys
66
66
67 # our own
67 # our own
68 from IPython.genutils import warn,error
68 from IPython.genutils import warn,error
69
69
70 class TryNext(Exception):
70 class TryNext(Exception):
71 """Try next hook exception.
71 """Try next hook exception.
72
72
73 Raise this in your hook function to indicate that the next hook handler
73 Raise this in your hook function to indicate that the next hook handler
74 should be used to handle the operation. If you pass arguments to the
74 should be used to handle the operation. If you pass arguments to the
75 constructor those arguments will be used by the next hook instead of the
75 constructor those arguments will be used by the next hook instead of the
76 original ones.
76 original ones.
77 """
77 """
78
78
79 def __init__(self, *args, **kwargs):
79 def __init__(self, *args, **kwargs):
80 self.args = args
80 self.args = args
81 self.kwargs = kwargs
81 self.kwargs = kwargs
82
82
83 # contains the most recently instantiated IPApi
83 # contains the most recently instantiated IPApi
84
84
85 class IPythonNotRunning:
85 class IPythonNotRunning:
86 """Dummy do-nothing class.
86 """Dummy do-nothing class.
87
87
88 Instances of this class return a dummy attribute on all accesses, which
88 Instances of this class return a dummy attribute on all accesses, which
89 can be called and warns. This makes it easier to write scripts which use
89 can be called and warns. This makes it easier to write scripts which use
90 the ipapi.get() object for informational purposes to operate both with and
90 the ipapi.get() object for informational purposes to operate both with and
91 without ipython. Obviously code which uses the ipython object for
91 without ipython. Obviously code which uses the ipython object for
92 computations will not work, but this allows a wider range of code to
92 computations will not work, but this allows a wider range of code to
93 transparently work whether ipython is being used or not."""
93 transparently work whether ipython is being used or not."""
94
94
95 def __str__(self):
95 def __str__(self):
96 return "<IPythonNotRunning>"
96 return "<IPythonNotRunning>"
97
97
98 __repr__ = __str__
98 __repr__ = __str__
99
99
100 def __getattr__(self,name):
100 def __getattr__(self,name):
101 return self.dummy
101 return self.dummy
102
102
103 def dummy(self,*args,**kw):
103 def dummy(self,*args,**kw):
104 """Dummy function, which doesn't do anything but warn."""
104 """Dummy function, which doesn't do anything but warn."""
105 warn("IPython is not running, this is a dummy no-op function")
105 warn("IPython is not running, this is a dummy no-op function")
106
106
107 _recent = None
107 _recent = None
108
108
109
109
110 def get(allow_dummy=False):
110 def get(allow_dummy=False):
111 """Get an IPApi object.
111 """Get an IPApi object.
112
112
113 If allow_dummy is true, returns an instance of IPythonNotRunning
113 If allow_dummy is true, returns an instance of IPythonNotRunning
114 instead of None if not running under IPython.
114 instead of None if not running under IPython.
115
115
116 Running this should be the first thing you do when writing extensions that
116 Running this should be the first thing you do when writing extensions that
117 can be imported as normal modules. You can then direct all the
117 can be imported as normal modules. You can then direct all the
118 configuration operations against the returned object.
118 configuration operations against the returned object.
119 """
119 """
120 global _recent
120 global _recent
121 if allow_dummy and not _recent:
121 if allow_dummy and not _recent:
122 _recent = IPythonNotRunning()
122 _recent = IPythonNotRunning()
123 return _recent
123 return _recent
124
124
125 class IPApi:
125 class IPApi:
126 """ The actual API class for configuring IPython
126 """ The actual API class for configuring IPython
127
127
128 You should do all of the IPython configuration by getting an IPApi object
128 You should do all of the IPython configuration by getting an IPApi object
129 with IPython.ipapi.get() and using the attributes and methods of the
129 with IPython.ipapi.get() and using the attributes and methods of the
130 returned object."""
130 returned object."""
131
131
132 def __init__(self,ip):
132 def __init__(self,ip):
133
133
134 # All attributes exposed here are considered to be the public API of
134 # All attributes exposed here are considered to be the public API of
135 # IPython. As needs dictate, some of these may be wrapped as
135 # IPython. As needs dictate, some of these may be wrapped as
136 # properties.
136 # properties.
137
137
138 self.magic = ip.ipmagic
138 self.magic = ip.ipmagic
139
139
140 self.system = ip.ipsystem
140 self.system = ip.ipsystem
141
141
142 self.set_hook = ip.set_hook
142 self.set_hook = ip.set_hook
143
143
144 self.set_custom_exc = ip.set_custom_exc
144 self.set_custom_exc = ip.set_custom_exc
145
145
146 self.user_ns = ip.user_ns
146 self.user_ns = ip.user_ns
147
147
148 self.set_crash_handler = ip.set_crash_handler
148 self.set_crash_handler = ip.set_crash_handler
149
149
150 # Session-specific data store, which can be used to store
150 # Session-specific data store, which can be used to store
151 # data that should persist through the ipython session.
151 # data that should persist through the ipython session.
152 self.meta = ip.meta
152 self.meta = ip.meta
153
153
154 # The ipython instance provided
154 # The ipython instance provided
155 self.IP = ip
155 self.IP = ip
156
156
157 global _recent
157 global _recent
158 _recent = self
158 _recent = self
159
159
160 # Use a property for some things which are added to the instance very
160 # Use a property for some things which are added to the instance very
161 # late. I don't have time right now to disentangle the initialization
161 # late. I don't have time right now to disentangle the initialization
162 # order issues, so a property lets us delay item extraction while
162 # order issues, so a property lets us delay item extraction while
163 # providing a normal attribute API.
163 # providing a normal attribute API.
164 def get_db(self):
164 def get_db(self):
165 """A handle to persistent dict-like database (a PickleShareDB object)"""
165 """A handle to persistent dict-like database (a PickleShareDB object)"""
166 return self.IP.db
166 return self.IP.db
167
167
168 db = property(get_db,None,None,get_db.__doc__)
168 db = property(get_db,None,None,get_db.__doc__)
169
169
170 def get_options(self):
170 def get_options(self):
171 """All configurable variables."""
171 """All configurable variables."""
172
173 # catch typos by disabling new attribute creation. If new attr creation
174 # is in fact wanted (e.g. when exposing new options), do allow_new_attr(True)
175 # for the received rc struct.
176
177 self.IP.rc.allow_new_attr(False)
172 return self.IP.rc
178 return self.IP.rc
173
179
174 options = property(get_options,None,None,get_options.__doc__)
180 options = property(get_options,None,None,get_options.__doc__)
175
181
176 def expose_magic(self,magicname, func):
182 def expose_magic(self,magicname, func):
177 ''' Expose own function as magic function for ipython
183 ''' Expose own function as magic function for ipython
178
184
179 def foo_impl(self,parameter_s=''):
185 def foo_impl(self,parameter_s=''):
180 """My very own magic!. (Use docstrings, IPython reads them)."""
186 """My very own magic!. (Use docstrings, IPython reads them)."""
181 print 'Magic function. Passed parameter is between < >: <'+parameter_s+'>'
187 print 'Magic function. Passed parameter is between < >: <'+parameter_s+'>'
182 print 'The self object is:',self
188 print 'The self object is:',self
183
189
184 ipapi.expose_magic("foo",foo_impl)
190 ipapi.expose_magic("foo",foo_impl)
185 '''
191 '''
186
192
187 import new
193 import new
188 im = new.instancemethod(func,self.IP, self.IP.__class__)
194 im = new.instancemethod(func,self.IP, self.IP.__class__)
189 setattr(self.IP, "magic_" + magicname, im)
195 setattr(self.IP, "magic_" + magicname, im)
190
196
191 def ex(self,cmd):
197 def ex(self,cmd):
192 """ Execute a normal python statement in user namespace """
198 """ Execute a normal python statement in user namespace """
193 exec cmd in self.user_ns
199 exec cmd in self.user_ns
194
200
195 def ev(self,expr):
201 def ev(self,expr):
196 """ Evaluate python expression expr in user namespace
202 """ Evaluate python expression expr in user namespace
197
203
198 Returns the result of evaluation"""
204 Returns the result of evaluation"""
199 return eval(expr,self.user_ns)
205 return eval(expr,self.user_ns)
200
206
201 def runlines(self,lines):
207 def runlines(self,lines):
202 """ Run the specified lines in interpreter, honoring ipython directives.
208 """ Run the specified lines in interpreter, honoring ipython directives.
203
209
204 This allows %magic and !shell escape notations.
210 This allows %magic and !shell escape notations.
205
211
206 Takes either all lines in one string or list of lines.
212 Takes either all lines in one string or list of lines.
207 """
213 """
208 if isinstance(lines,basestring):
214 if isinstance(lines,basestring):
209 self.IP.runlines(lines)
215 self.IP.runlines(lines)
210 else:
216 else:
211 self.IP.runlines('\n'.join(lines))
217 self.IP.runlines('\n'.join(lines))
212
218
213 def to_user_ns(self,vars):
219 def to_user_ns(self,vars):
214 """Inject a group of variables into the IPython user namespace.
220 """Inject a group of variables into the IPython user namespace.
215
221
216 Inputs:
222 Inputs:
217
223
218 - vars: string with variable names separated by whitespace
224 - vars: string with variable names separated by whitespace
219
225
220 This utility routine is meant to ease interactive debugging work,
226 This utility routine is meant to ease interactive debugging work,
221 where you want to easily propagate some internal variable in your code
227 where you want to easily propagate some internal variable in your code
222 up to the interactive namespace for further exploration.
228 up to the interactive namespace for further exploration.
223
229
224 When you run code via %run, globals in your script become visible at
230 When you run code via %run, globals in your script become visible at
225 the interactive prompt, but this doesn't happen for locals inside your
231 the interactive prompt, but this doesn't happen for locals inside your
226 own functions and methods. Yet when debugging, it is common to want
232 own functions and methods. Yet when debugging, it is common to want
227 to explore some internal variables further at the interactive propmt.
233 to explore some internal variables further at the interactive propmt.
228
234
229 Examples:
235 Examples:
230
236
231 To use this, you first must obtain a handle on the ipython object as
237 To use this, you first must obtain a handle on the ipython object as
232 indicated above, via:
238 indicated above, via:
233
239
234 import IPython.ipapi
240 import IPython.ipapi
235 ip = IPython.ipapi.get()
241 ip = IPython.ipapi.get()
236
242
237 Once this is done, inside a routine foo() where you want to expose
243 Once this is done, inside a routine foo() where you want to expose
238 variables x and y, you do the following:
244 variables x and y, you do the following:
239
245
240 def foo():
246 def foo():
241 ...
247 ...
242 x = your_computation()
248 x = your_computation()
243 y = something_else()
249 y = something_else()
244
250
245 # This pushes x and y to the interactive prompt immediately, even
251 # This pushes x and y to the interactive prompt immediately, even
246 # if this routine crashes on the next line after:
252 # if this routine crashes on the next line after:
247 ip.to_user_ns('x y')
253 ip.to_user_ns('x y')
248 ...
254 ...
249 # return
255 # return
250
256
251 If you need to rename variables, just use ip.user_ns with dict
257 If you need to rename variables, just use ip.user_ns with dict
252 and update:
258 and update:
253
259
254 # exposes variables 'foo' as 'x' and 'bar' as 'y' in IPython
260 # exposes variables 'foo' as 'x' and 'bar' as 'y' in IPython
255 # user namespace
261 # user namespace
256 ip.user_ns.update(dict(x=foo,y=bar))
262 ip.user_ns.update(dict(x=foo,y=bar))
257 """
263 """
258
264
259 # print 'vars given:',vars # dbg
265 # print 'vars given:',vars # dbg
260 # Get the caller's frame to evaluate the given names in
266 # Get the caller's frame to evaluate the given names in
261 cf = sys._getframe(1)
267 cf = sys._getframe(1)
262
268
263 user_ns = self.user_ns
269 user_ns = self.user_ns
264
270
265 for name in vars.split():
271 for name in vars.split():
266 try:
272 try:
267 user_ns[name] = eval(name,cf.f_globals,cf.f_locals)
273 user_ns[name] = eval(name,cf.f_globals,cf.f_locals)
268 except:
274 except:
269 error('could not get var. %s from %s' %
275 error('could not get var. %s from %s' %
270 (name,cf.f_code.co_name))
276 (name,cf.f_code.co_name))
271
277
272 def launch_new_instance(user_ns = None):
278 def launch_new_instance(user_ns = None):
273 """ Make and start a new ipython instance.
279 """ Make and start a new ipython instance.
274
280
275 This can be called even without having an already initialized
281 This can be called even without having an already initialized
276 ipython session running.
282 ipython session running.
277
283
278 This is also used as the egg entry point for the 'ipython' script.
284 This is also used as the egg entry point for the 'ipython' script.
279
285
280 """
286 """
281 ses = make_session(user_ns)
287 ses = make_session(user_ns)
282 ses.mainloop()
288 ses.mainloop()
283
289
284
290
285 def make_user_ns(user_ns = None):
291 def make_user_ns(user_ns = None):
286 """Return a valid user interactive namespace.
292 """Return a valid user interactive namespace.
287
293
288 This builds a dict with the minimal information needed to operate as a
294 This builds a dict with the minimal information needed to operate as a
289 valid IPython user namespace, which you can pass to the various embedding
295 valid IPython user namespace, which you can pass to the various embedding
290 classes in ipython.
296 classes in ipython.
291 """
297 """
292
298
293 if user_ns is None:
299 if user_ns is None:
294 # Set __name__ to __main__ to better match the behavior of the
300 # Set __name__ to __main__ to better match the behavior of the
295 # normal interpreter.
301 # normal interpreter.
296 user_ns = {'__name__' :'__main__',
302 user_ns = {'__name__' :'__main__',
297 '__builtins__' : __builtin__,
303 '__builtins__' : __builtin__,
298 }
304 }
299 else:
305 else:
300 user_ns.setdefault('__name__','__main__')
306 user_ns.setdefault('__name__','__main__')
301 user_ns.setdefault('__builtins__',__builtin__)
307 user_ns.setdefault('__builtins__',__builtin__)
302
308
303 return user_ns
309 return user_ns
304
310
305
311
306 def make_user_global_ns(ns = None):
312 def make_user_global_ns(ns = None):
307 """Return a valid user global namespace.
313 """Return a valid user global namespace.
308
314
309 Similar to make_user_ns(), but global namespaces are really only needed in
315 Similar to make_user_ns(), but global namespaces are really only needed in
310 embedded applications, where there is a distinction between the user's
316 embedded applications, where there is a distinction between the user's
311 interactive namespace and the global one where ipython is running."""
317 interactive namespace and the global one where ipython is running."""
312
318
313 if ns is None: ns = {}
319 if ns is None: ns = {}
314 return ns
320 return ns
315
321
316
322
317 def make_session(user_ns = None):
323 def make_session(user_ns = None):
318 """Makes, but does not launch an IPython session.
324 """Makes, but does not launch an IPython session.
319
325
320 Later on you can call obj.mainloop() on the returned object.
326 Later on you can call obj.mainloop() on the returned object.
321
327
322 Inputs:
328 Inputs:
323
329
324 - user_ns(None): a dict to be used as the user's namespace with initial
330 - user_ns(None): a dict to be used as the user's namespace with initial
325 data.
331 data.
326
332
327 WARNING: This should *not* be run when a session exists already."""
333 WARNING: This should *not* be run when a session exists already."""
328
334
329 import IPython
335 import IPython
330 return IPython.Shell.start(user_ns)
336 return IPython.Shell.start(user_ns)
@@ -1,376 +1,393 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Mimic C structs with lots of extra functionality.
2 """Mimic C structs with lots of extra functionality.
3
3
4 $Id: ipstruct.py 1005 2006-01-12 08:39:26Z fperez $"""
4 $Id: ipstruct.py 1949 2006-11-28 19:12:15Z vivainio $"""
5
5
6 #*****************************************************************************
6 #*****************************************************************************
7 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
7 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
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 from IPython import Release
13 from IPython import Release
14 __author__ = '%s <%s>' % Release.authors['Fernando']
14 __author__ = '%s <%s>' % Release.authors['Fernando']
15 __license__ = Release.license
15 __license__ = Release.license
16
16
17 __all__ = ['Struct']
17 __all__ = ['Struct']
18
18
19 import types
19 import types
20
20
21 from IPython.genutils import list2dict2
21 from IPython.genutils import list2dict2
22
22
23 class Struct:
23 class Struct:
24 """Class to mimic C structs but also provide convenient dictionary-like
24 """Class to mimic C structs but also provide convenient dictionary-like
25 functionality.
25 functionality.
26
26
27 Instances can be initialized with a dictionary, a list of key=value pairs
27 Instances can be initialized with a dictionary, a list of key=value pairs
28 or both. If both are present, the dictionary must come first.
28 or both. If both are present, the dictionary must come first.
29
29
30 Because Python classes provide direct assignment to their members, it's
30 Because Python classes provide direct assignment to their members, it's
31 easy to overwrite normal methods (S.copy = 1 would destroy access to
31 easy to overwrite normal methods (S.copy = 1 would destroy access to
32 S.copy()). For this reason, all builtin method names are protected and
32 S.copy()). For this reason, all builtin method names are protected and
33 can't be assigned to. An attempt to do s.copy=1 or s['copy']=1 will raise
33 can't be assigned to. An attempt to do s.copy=1 or s['copy']=1 will raise
34 a KeyError exception. If you really want to, you can bypass this
34 a KeyError exception. If you really want to, you can bypass this
35 protection by directly assigning to __dict__: s.__dict__['copy']=1 will
35 protection by directly assigning to __dict__: s.__dict__['copy']=1 will
36 still work. Doing this will break functionality, though. As in most of
36 still work. Doing this will break functionality, though. As in most of
37 Python, namespace protection is weakly enforced, so feel free to shoot
37 Python, namespace protection is weakly enforced, so feel free to shoot
38 yourself if you really want to.
38 yourself if you really want to.
39
39
40 Note that this class uses more memory and is *much* slower than a regular
40 Note that this class uses more memory and is *much* slower than a regular
41 dictionary, so be careful in situations where memory or performance are
41 dictionary, so be careful in situations where memory or performance are
42 critical. But for day to day use it should behave fine. It is particularly
42 critical. But for day to day use it should behave fine. It is particularly
43 convenient for storing configuration data in programs.
43 convenient for storing configuration data in programs.
44
44
45 +,+=,- and -= are implemented. +/+= do merges (non-destructive updates),
45 +,+=,- and -= are implemented. +/+= do merges (non-destructive updates),
46 -/-= remove keys from the original. See the method descripitions.
46 -/-= remove keys from the original. See the method descripitions.
47
47
48 This class allows a quick access syntax: both s.key and s['key'] are
48 This class allows a quick access syntax: both s.key and s['key'] are
49 valid. This syntax has a limitation: each 'key' has to be explicitly
49 valid. This syntax has a limitation: each 'key' has to be explicitly
50 accessed by its original name. The normal s.key syntax doesn't provide
50 accessed by its original name. The normal s.key syntax doesn't provide
51 access to the keys via variables whose values evaluate to the desired
51 access to the keys via variables whose values evaluate to the desired
52 keys. An example should clarify this:
52 keys. An example should clarify this:
53
53
54 Define a dictionary and initialize both with dict and k=v pairs:
54 Define a dictionary and initialize both with dict and k=v pairs:
55 >>> d={'a':1,'b':2}
55 >>> d={'a':1,'b':2}
56 >>> s=Struct(d,hi=10,ho=20)
56 >>> s=Struct(d,hi=10,ho=20)
57 The return of __repr__ can be used to create a new instance:
57 The return of __repr__ can be used to create a new instance:
58 >>> s
58 >>> s
59 Struct({'ho': 20, 'b': 2, 'hi': 10, 'a': 1})
59 Struct({'ho': 20, 'b': 2, 'hi': 10, 'a': 1})
60 __str__ (called by print) shows it's not quite a regular dictionary:
60 __str__ (called by print) shows it's not quite a regular dictionary:
61 >>> print s
61 >>> print s
62 Struct {a: 1, b: 2, hi: 10, ho: 20}
62 Struct {a: 1, b: 2, hi: 10, ho: 20}
63 Access by explicitly named key with dot notation:
63 Access by explicitly named key with dot notation:
64 >>> s.a
64 >>> s.a
65 1
65 1
66 Or like a dictionary:
66 Or like a dictionary:
67 >>> s['a']
67 >>> s['a']
68 1
68 1
69 If you want a variable to hold the key value, only dictionary access works:
69 If you want a variable to hold the key value, only dictionary access works:
70 >>> key='hi'
70 >>> key='hi'
71 >>> s.key
71 >>> s.key
72 Traceback (most recent call last):
72 Traceback (most recent call last):
73 File "<stdin>", line 1, in ?
73 File "<stdin>", line 1, in ?
74 AttributeError: Struct instance has no attribute 'key'
74 AttributeError: Struct instance has no attribute 'key'
75 >>> s[key]
75 >>> s[key]
76 10
76 10
77
77
78 Another limitation of the s.key syntax (and Struct(key=val)
78 Another limitation of the s.key syntax (and Struct(key=val)
79 initialization): keys can't be numbers. But numeric keys can be used and
79 initialization): keys can't be numbers. But numeric keys can be used and
80 accessed using the dictionary syntax. Again, an example:
80 accessed using the dictionary syntax. Again, an example:
81
81
82 This doesn't work:
82 This doesn't work:
83 >>> s=Struct(4='hi')
83 >>> s=Struct(4='hi')
84 SyntaxError: keyword can't be an expression
84 SyntaxError: keyword can't be an expression
85 But this does:
85 But this does:
86 >>> s=Struct()
86 >>> s=Struct()
87 >>> s[4]='hi'
87 >>> s[4]='hi'
88 >>> s
88 >>> s
89 Struct({4: 'hi'})
89 Struct({4: 'hi'})
90 >>> s[4]
90 >>> s[4]
91 'hi'
91 'hi'
92 """
92 """
93
93
94 # Attributes to which __setitem__ and __setattr__ will block access.
94 # Attributes to which __setitem__ and __setattr__ will block access.
95 # Note: much of this will be moot in Python 2.2 and will be done in a much
95 # Note: much of this will be moot in Python 2.2 and will be done in a much
96 # cleaner way.
96 # cleaner way.
97 __protected = ('copy dict dictcopy get has_attr has_key items keys '
97 __protected = ('copy dict dictcopy get has_attr has_key items keys '
98 'merge popitem setdefault update values '
98 'merge popitem setdefault update values '
99 '__make_dict __dict_invert ').split()
99 '__make_dict __dict_invert ').split()
100
100
101 def __init__(self,dict=None,**kw):
101 def __init__(self,dict=None,**kw):
102 """Initialize with a dictionary, another Struct, or by giving
102 """Initialize with a dictionary, another Struct, or by giving
103 explicitly the list of attributes.
103 explicitly the list of attributes.
104
104
105 Both can be used, but the dictionary must come first:
105 Both can be used, but the dictionary must come first:
106 Struct(dict), Struct(k1=v1,k2=v2) or Struct(dict,k1=v1,k2=v2).
106 Struct(dict), Struct(k1=v1,k2=v2) or Struct(dict,k1=v1,k2=v2).
107 """
107 """
108 self.__dict__['__allownew'] = True
108 if dict is None:
109 if dict is None:
109 dict = {}
110 dict = {}
110 if isinstance(dict,Struct):
111 if isinstance(dict,Struct):
111 dict = dict.dict()
112 dict = dict.dict()
112 elif dict and type(dict) is not types.DictType:
113 elif dict and type(dict) is not types.DictType:
113 raise TypeError,\
114 raise TypeError,\
114 'Initialize with a dictionary or key=val pairs.'
115 'Initialize with a dictionary or key=val pairs.'
115 dict.update(kw)
116 dict.update(kw)
116 # do the updating by hand to guarantee that we go through the
117 # do the updating by hand to guarantee that we go through the
117 # safety-checked __setitem__
118 # safety-checked __setitem__
118 for k,v in dict.items():
119 for k,v in dict.items():
119 self[k] = v
120 self[k] = v
121
120
122
121 def __setitem__(self,key,value):
123 def __setitem__(self,key,value):
122 """Used when struct[key] = val calls are made."""
124 """Used when struct[key] = val calls are made."""
123 if key in Struct.__protected:
125 if key in Struct.__protected:
124 raise KeyError,'Key '+`key`+' is a protected key of class Struct.'
126 raise KeyError,'Key '+`key`+' is a protected key of class Struct.'
127 if not self['__allownew'] and key not in self.__dict__:
128 raise KeyError(
129 "Can't create unknown attribute %s - Check for typos, or use allow_new_attr to create new attributes!" %
130 key)
131
125 self.__dict__[key] = value
132 self.__dict__[key] = value
126
133
127 def __setattr__(self, key, value):
134 def __setattr__(self, key, value):
128 """Used when struct.key = val calls are made."""
135 """Used when struct.key = val calls are made."""
129 self.__setitem__(key,value)
136 self.__setitem__(key,value)
130
137
131 def __str__(self):
138 def __str__(self):
132 """Gets called by print."""
139 """Gets called by print."""
133
140
134 return 'Struct('+str(self.__dict__)+')'
141 return 'Struct('+str(self.__dict__)+')'
135
142
136 def __repr__(self):
143 def __repr__(self):
137 """Gets called by repr.
144 """Gets called by repr.
138
145
139 A Struct can be recreated with S_new=eval(repr(S_old))."""
146 A Struct can be recreated with S_new=eval(repr(S_old))."""
140 return 'Struct('+str(self.__dict__)+')'
147 return 'Struct('+str(self.__dict__)+')'
141
148
142 def __getitem__(self,key):
149 def __getitem__(self,key):
143 """Allows struct[key] access."""
150 """Allows struct[key] access."""
144 return self.__dict__[key]
151 return self.__dict__[key]
145
152
146 def __contains__(self,key):
153 def __contains__(self,key):
147 """Allows use of the 'in' operator."""
154 """Allows use of the 'in' operator."""
148 return self.__dict__.has_key(key)
155 return self.__dict__.has_key(key)
149
156
150 def __iadd__(self,other):
157 def __iadd__(self,other):
151 """S += S2 is a shorthand for S.merge(S2)."""
158 """S += S2 is a shorthand for S.merge(S2)."""
152 self.merge(other)
159 self.merge(other)
153 return self
160 return self
154
161
155 def __add__(self,other):
162 def __add__(self,other):
156 """S + S2 -> New Struct made form S and S.merge(S2)"""
163 """S + S2 -> New Struct made form S and S.merge(S2)"""
157 Sout = self.copy()
164 Sout = self.copy()
158 Sout.merge(other)
165 Sout.merge(other)
159 return Sout
166 return Sout
160
167
161 def __sub__(self,other):
168 def __sub__(self,other):
162 """Return S1-S2, where all keys in S2 have been deleted (if present)
169 """Return S1-S2, where all keys in S2 have been deleted (if present)
163 from S1."""
170 from S1."""
164 Sout = self.copy()
171 Sout = self.copy()
165 Sout -= other
172 Sout -= other
166 return Sout
173 return Sout
167
174
168 def __isub__(self,other):
175 def __isub__(self,other):
169 """Do in place S = S - S2, meaning all keys in S2 have been deleted
176 """Do in place S = S - S2, meaning all keys in S2 have been deleted
170 (if present) from S1."""
177 (if present) from S1."""
171
178
172 for k in other.keys():
179 for k in other.keys():
173 if self.has_key(k):
180 if self.has_key(k):
174 del self.__dict__[k]
181 del self.__dict__[k]
175
182
176 def __make_dict(self,__loc_data__,**kw):
183 def __make_dict(self,__loc_data__,**kw):
177 "Helper function for update and merge. Return a dict from data."
184 "Helper function for update and merge. Return a dict from data."
178
185
179 if __loc_data__ == None:
186 if __loc_data__ == None:
180 dict = {}
187 dict = {}
181 elif type(__loc_data__) is types.DictType:
188 elif type(__loc_data__) is types.DictType:
182 dict = __loc_data__
189 dict = __loc_data__
183 elif isinstance(__loc_data__,Struct):
190 elif isinstance(__loc_data__,Struct):
184 dict = __loc_data__.__dict__
191 dict = __loc_data__.__dict__
185 else:
192 else:
186 raise TypeError, 'Update with a dict, a Struct or key=val pairs.'
193 raise TypeError, 'Update with a dict, a Struct or key=val pairs.'
187 if kw:
194 if kw:
188 dict.update(kw)
195 dict.update(kw)
189 return dict
196 return dict
190
197
191 def __dict_invert(self,dict):
198 def __dict_invert(self,dict):
192 """Helper function for merge. Takes a dictionary whose values are
199 """Helper function for merge. Takes a dictionary whose values are
193 lists and returns a dict. with the elements of each list as keys and
200 lists and returns a dict. with the elements of each list as keys and
194 the original keys as values."""
201 the original keys as values."""
195
202
196 outdict = {}
203 outdict = {}
197 for k,lst in dict.items():
204 for k,lst in dict.items():
198 if type(lst) is types.StringType:
205 if type(lst) is types.StringType:
199 lst = lst.split()
206 lst = lst.split()
200 for entry in lst:
207 for entry in lst:
201 outdict[entry] = k
208 outdict[entry] = k
202 return outdict
209 return outdict
203
210
204 def clear(self):
211 def clear(self):
205 """Clear all attributes."""
212 """Clear all attributes."""
206 self.__dict__.clear()
213 self.__dict__.clear()
207
214
208 def copy(self):
215 def copy(self):
209 """Return a (shallow) copy of a Struct."""
216 """Return a (shallow) copy of a Struct."""
210 return Struct(self.__dict__.copy())
217 return Struct(self.__dict__.copy())
211
218
212 def dict(self):
219 def dict(self):
213 """Return the Struct's dictionary."""
220 """Return the Struct's dictionary."""
214 return self.__dict__
221 return self.__dict__
215
222
216 def dictcopy(self):
223 def dictcopy(self):
217 """Return a (shallow) copy of the Struct's dictionary."""
224 """Return a (shallow) copy of the Struct's dictionary."""
218 return self.__dict__.copy()
225 return self.__dict__.copy()
219
226
220 def popitem(self):
227 def popitem(self):
221 """S.popitem() -> (k, v), remove and return some (key, value) pair as
228 """S.popitem() -> (k, v), remove and return some (key, value) pair as
222 a 2-tuple; but raise KeyError if S is empty."""
229 a 2-tuple; but raise KeyError if S is empty."""
223 return self.__dict__.popitem()
230 return self.__dict__.popitem()
224
231
225 def update(self,__loc_data__=None,**kw):
232 def update(self,__loc_data__=None,**kw):
226 """Update (merge) with data from another Struct or from a dictionary.
233 """Update (merge) with data from another Struct or from a dictionary.
227 Optionally, one or more key=value pairs can be given at the end for
234 Optionally, one or more key=value pairs can be given at the end for
228 direct update."""
235 direct update."""
229
236
230 # The funny name __loc_data__ is to prevent a common variable name which
237 # The funny name __loc_data__ is to prevent a common variable name which
231 # could be a fieled of a Struct to collide with this parameter. The problem
238 # could be a fieled of a Struct to collide with this parameter. The problem
232 # would arise if the function is called with a keyword with this same name
239 # would arise if the function is called with a keyword with this same name
233 # that a user means to add as a Struct field.
240 # that a user means to add as a Struct field.
234 newdict = Struct.__make_dict(self,__loc_data__,**kw)
241 newdict = Struct.__make_dict(self,__loc_data__,**kw)
235 for k,v in newdict.items():
242 for k,v in newdict.items():
236 self[k] = v
243 self[k] = v
237
244
238 def merge(self,__loc_data__=None,__conflict_solve=None,**kw):
245 def merge(self,__loc_data__=None,__conflict_solve=None,**kw):
239 """S.merge(data,conflict,k=v1,k=v2,...) -> merge data and k=v into S.
246 """S.merge(data,conflict,k=v1,k=v2,...) -> merge data and k=v into S.
240
247
241 This is similar to update(), but much more flexible. First, a dict is
248 This is similar to update(), but much more flexible. First, a dict is
242 made from data+key=value pairs. When merging this dict with the Struct
249 made from data+key=value pairs. When merging this dict with the Struct
243 S, the optional dictionary 'conflict' is used to decide what to do.
250 S, the optional dictionary 'conflict' is used to decide what to do.
244
251
245 If conflict is not given, the default behavior is to preserve any keys
252 If conflict is not given, the default behavior is to preserve any keys
246 with their current value (the opposite of the update method's
253 with their current value (the opposite of the update method's
247 behavior).
254 behavior).
248
255
249 conflict is a dictionary of binary functions which will be used to
256 conflict is a dictionary of binary functions which will be used to
250 solve key conflicts. It must have the following structure:
257 solve key conflicts. It must have the following structure:
251
258
252 conflict == { fn1 : [Skey1,Skey2,...], fn2 : [Skey3], etc }
259 conflict == { fn1 : [Skey1,Skey2,...], fn2 : [Skey3], etc }
253
260
254 Values must be lists or whitespace separated strings which are
261 Values must be lists or whitespace separated strings which are
255 automatically converted to lists of strings by calling string.split().
262 automatically converted to lists of strings by calling string.split().
256
263
257 Each key of conflict is a function which defines a policy for
264 Each key of conflict is a function which defines a policy for
258 resolving conflicts when merging with the input data. Each fn must be
265 resolving conflicts when merging with the input data. Each fn must be
259 a binary function which returns the desired outcome for a key
266 a binary function which returns the desired outcome for a key
260 conflict. These functions will be called as fn(old,new).
267 conflict. These functions will be called as fn(old,new).
261
268
262 An example is probably in order. Suppose you are merging the struct S
269 An example is probably in order. Suppose you are merging the struct S
263 with a dict D and the following conflict policy dict:
270 with a dict D and the following conflict policy dict:
264
271
265 S.merge(D,{fn1:['a','b',4], fn2:'key_c key_d'})
272 S.merge(D,{fn1:['a','b',4], fn2:'key_c key_d'})
266
273
267 If the key 'a' is found in both S and D, the merge method will call:
274 If the key 'a' is found in both S and D, the merge method will call:
268
275
269 S['a'] = fn1(S['a'],D['a'])
276 S['a'] = fn1(S['a'],D['a'])
270
277
271 As a convenience, merge() provides five (the most commonly needed)
278 As a convenience, merge() provides five (the most commonly needed)
272 pre-defined policies: preserve, update, add, add_flip and add_s. The
279 pre-defined policies: preserve, update, add, add_flip and add_s. The
273 easiest explanation is their implementation:
280 easiest explanation is their implementation:
274
281
275 preserve = lambda old,new: old
282 preserve = lambda old,new: old
276 update = lambda old,new: new
283 update = lambda old,new: new
277 add = lambda old,new: old + new
284 add = lambda old,new: old + new
278 add_flip = lambda old,new: new + old # note change of order!
285 add_flip = lambda old,new: new + old # note change of order!
279 add_s = lambda old,new: old + ' ' + new # only works for strings!
286 add_s = lambda old,new: old + ' ' + new # only works for strings!
280
287
281 You can use those four words (as strings) as keys in conflict instead
288 You can use those four words (as strings) as keys in conflict instead
282 of defining them as functions, and the merge method will substitute
289 of defining them as functions, and the merge method will substitute
283 the appropriate functions for you. That is, the call
290 the appropriate functions for you. That is, the call
284
291
285 S.merge(D,{'preserve':'a b c','add':[4,5,'d'],my_function:[6]})
292 S.merge(D,{'preserve':'a b c','add':[4,5,'d'],my_function:[6]})
286
293
287 will automatically substitute the functions preserve and add for the
294 will automatically substitute the functions preserve and add for the
288 names 'preserve' and 'add' before making any function calls.
295 names 'preserve' and 'add' before making any function calls.
289
296
290 For more complicated conflict resolution policies, you still need to
297 For more complicated conflict resolution policies, you still need to
291 construct your own functions. """
298 construct your own functions. """
292
299
293 data_dict = Struct.__make_dict(self,__loc_data__,**kw)
300 data_dict = Struct.__make_dict(self,__loc_data__,**kw)
294
301
295 # policies for conflict resolution: two argument functions which return
302 # policies for conflict resolution: two argument functions which return
296 # the value that will go in the new struct
303 # the value that will go in the new struct
297 preserve = lambda old,new: old
304 preserve = lambda old,new: old
298 update = lambda old,new: new
305 update = lambda old,new: new
299 add = lambda old,new: old + new
306 add = lambda old,new: old + new
300 add_flip = lambda old,new: new + old # note change of order!
307 add_flip = lambda old,new: new + old # note change of order!
301 add_s = lambda old,new: old + ' ' + new
308 add_s = lambda old,new: old + ' ' + new
302
309
303 # default policy is to keep current keys when there's a conflict
310 # default policy is to keep current keys when there's a conflict
304 conflict_solve = list2dict2(self.keys(),default = preserve)
311 conflict_solve = list2dict2(self.keys(),default = preserve)
305
312
306 # the conflict_solve dictionary is given by the user 'inverted': we
313 # the conflict_solve dictionary is given by the user 'inverted': we
307 # need a name-function mapping, it comes as a function -> names
314 # need a name-function mapping, it comes as a function -> names
308 # dict. Make a local copy (b/c we'll make changes), replace user
315 # dict. Make a local copy (b/c we'll make changes), replace user
309 # strings for the three builtin policies and invert it.
316 # strings for the three builtin policies and invert it.
310 if __conflict_solve:
317 if __conflict_solve:
311 inv_conflict_solve_user = __conflict_solve.copy()
318 inv_conflict_solve_user = __conflict_solve.copy()
312 for name, func in [('preserve',preserve), ('update',update),
319 for name, func in [('preserve',preserve), ('update',update),
313 ('add',add), ('add_flip',add_flip), ('add_s',add_s)]:
320 ('add',add), ('add_flip',add_flip), ('add_s',add_s)]:
314 if name in inv_conflict_solve_user.keys():
321 if name in inv_conflict_solve_user.keys():
315 inv_conflict_solve_user[func] = inv_conflict_solve_user[name]
322 inv_conflict_solve_user[func] = inv_conflict_solve_user[name]
316 del inv_conflict_solve_user[name]
323 del inv_conflict_solve_user[name]
317 conflict_solve.update(Struct.__dict_invert(self,inv_conflict_solve_user))
324 conflict_solve.update(Struct.__dict_invert(self,inv_conflict_solve_user))
318 #print 'merge. conflict_solve: '; pprint(conflict_solve) # dbg
325 #print 'merge. conflict_solve: '; pprint(conflict_solve) # dbg
319 #print '*'*50,'in merger. conflict_solver:'; pprint(conflict_solve)
326 #print '*'*50,'in merger. conflict_solver:'; pprint(conflict_solve)
320 for key in data_dict:
327 for key in data_dict:
321 if key not in self:
328 if key not in self:
322 self[key] = data_dict[key]
329 self[key] = data_dict[key]
323 else:
330 else:
324 self[key] = conflict_solve[key](self[key],data_dict[key])
331 self[key] = conflict_solve[key](self[key],data_dict[key])
325
332
326 def has_key(self,key):
333 def has_key(self,key):
327 """Like has_key() dictionary method."""
334 """Like has_key() dictionary method."""
328 return self.__dict__.has_key(key)
335 return self.__dict__.has_key(key)
329
336
330 def hasattr(self,key):
337 def hasattr(self,key):
331 """hasattr function available as a method.
338 """hasattr function available as a method.
332
339
333 Implemented like has_key, to make sure that all available keys in the
340 Implemented like has_key, to make sure that all available keys in the
334 internal dictionary of the Struct appear also as attributes (even
341 internal dictionary of the Struct appear also as attributes (even
335 numeric keys)."""
342 numeric keys)."""
336 return self.__dict__.has_key(key)
343 return self.__dict__.has_key(key)
337
344
338 def items(self):
345 def items(self):
339 """Return the items in the Struct's dictionary, in the same format
346 """Return the items in the Struct's dictionary, in the same format
340 as a call to {}.items()."""
347 as a call to {}.items()."""
341 return self.__dict__.items()
348 return self.__dict__.items()
342
349
343 def keys(self):
350 def keys(self):
344 """Return the keys in the Struct's dictionary, in the same format
351 """Return the keys in the Struct's dictionary, in the same format
345 as a call to {}.keys()."""
352 as a call to {}.keys()."""
346 return self.__dict__.keys()
353 return self.__dict__.keys()
347
354
348 def values(self,keys=None):
355 def values(self,keys=None):
349 """Return the values in the Struct's dictionary, in the same format
356 """Return the values in the Struct's dictionary, in the same format
350 as a call to {}.values().
357 as a call to {}.values().
351
358
352 Can be called with an optional argument keys, which must be a list or
359 Can be called with an optional argument keys, which must be a list or
353 tuple of keys. In this case it returns only the values corresponding
360 tuple of keys. In this case it returns only the values corresponding
354 to those keys (allowing a form of 'slicing' for Structs)."""
361 to those keys (allowing a form of 'slicing' for Structs)."""
355 if not keys:
362 if not keys:
356 return self.__dict__.values()
363 return self.__dict__.values()
357 else:
364 else:
358 ret=[]
365 ret=[]
359 for k in keys:
366 for k in keys:
360 ret.append(self[k])
367 ret.append(self[k])
361 return ret
368 return ret
362
369
363 def get(self,attr,val=None):
370 def get(self,attr,val=None):
364 """S.get(k[,d]) -> S[k] if S.has_key(k), else d. d defaults to None."""
371 """S.get(k[,d]) -> S[k] if S.has_key(k), else d. d defaults to None."""
365 try:
372 try:
366 return self[attr]
373 return self[attr]
367 except KeyError:
374 except KeyError:
368 return val
375 return val
369
376
370 def setdefault(self,attr,val=None):
377 def setdefault(self,attr,val=None):
371 """S.setdefault(k[,d]) -> S.get(k,d), also set S[k]=d if not S.has_key(k)"""
378 """S.setdefault(k[,d]) -> S.get(k,d), also set S[k]=d if not S.has_key(k)"""
372 if not self.has_key(attr):
379 if not self.has_key(attr):
373 self[attr] = val
380 self[attr] = val
374 return self.get(attr,val)
381 return self.get(attr,val)
382
383 def allow_new_attr(self, allow = True):
384 """ Set whether new attributes can be created inside struct
385
386 This can be used to catch typos by verifying that the attribute user tries to
387 change already exists in this Struct.
388 """
389 self['__allownew'] = allow
390
391
375 # end class Struct
392 # end class Struct
376
393
General Comments 0
You need to be logged in to leave comments. Login now