##// END OF EJS Templates
initialization (no svn history)
ville -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,18
1 syntax: glob
2
3 *~
4 *.tmp
5 *.pyc
6 *.bak
7 *.tgz
8 *.org
9 *.rej
10 .svn/
11 .bzr/
12 .settings/
13 .project
14 *.diff
15 IPython_crash_report.txt
16
17 syntax: regexp
18 .*\#.*\#$
@@ -0,0 +1,1
1 link doc/ChangeLog No newline at end of file
@@ -0,0 +1,170
1 # -*- coding: utf-8 -*-
2 """Tools for coloring text in ANSI terminals.
3
4 $Id: ColorANSI.py 2167 2007-03-21 06:57:50Z fperez $"""
5
6 #*****************************************************************************
7 # Copyright (C) 2002-2006 Fernando Perez. <fperez@colorado.edu>
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #*****************************************************************************
12
13 from IPython import Release
14 __author__ = '%s <%s>' % Release.authors['Fernando']
15 __license__ = Release.license
16
17 __all__ = ['TermColors','InputTermColors','ColorScheme','ColorSchemeTable']
18
19 import os
20
21 from IPython.ipstruct import Struct
22
23 def make_color_table(in_class):
24 """Build a set of color attributes in a class.
25
26 Helper function for building the *TermColors classes."""
27
28 color_templates = (
29 ("Black" , "0;30"),
30 ("Red" , "0;31"),
31 ("Green" , "0;32"),
32 ("Brown" , "0;33"),
33 ("Blue" , "0;34"),
34 ("Purple" , "0;35"),
35 ("Cyan" , "0;36"),
36 ("LightGray" , "0;37"),
37 ("DarkGray" , "1;30"),
38 ("LightRed" , "1;31"),
39 ("LightGreen" , "1;32"),
40 ("Yellow" , "1;33"),
41 ("LightBlue" , "1;34"),
42 ("LightPurple" , "1;35"),
43 ("LightCyan" , "1;36"),
44 ("White" , "1;37"), )
45
46 for name,value in color_templates:
47 setattr(in_class,name,in_class._base % value)
48
49 class TermColors:
50 """Color escape sequences.
51
52 This class defines the escape sequences for all the standard (ANSI?)
53 colors in terminals. Also defines a NoColor escape which is just the null
54 string, suitable for defining 'dummy' color schemes in terminals which get
55 confused by color escapes.
56
57 This class should be used as a mixin for building color schemes."""
58
59 NoColor = '' # for color schemes in color-less terminals.
60 Normal = '\033[0m' # Reset normal coloring
61 _base = '\033[%sm' # Template for all other colors
62
63 # Build the actual color table as a set of class attributes:
64 make_color_table(TermColors)
65
66 class InputTermColors:
67 """Color escape sequences for input prompts.
68
69 This class is similar to TermColors, but the escapes are wrapped in \001
70 and \002 so that readline can properly know the length of each line and
71 can wrap lines accordingly. Use this class for any colored text which
72 needs to be used in input prompts, such as in calls to raw_input().
73
74 This class defines the escape sequences for all the standard (ANSI?)
75 colors in terminals. Also defines a NoColor escape which is just the null
76 string, suitable for defining 'dummy' color schemes in terminals which get
77 confused by color escapes.
78
79 This class should be used as a mixin for building color schemes."""
80
81 NoColor = '' # for color schemes in color-less terminals.
82
83 if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs':
84 # (X)emacs on W32 gets confused with \001 and \002 so we remove them
85 Normal = '\033[0m' # Reset normal coloring
86 _base = '\033[%sm' # Template for all other colors
87 else:
88 Normal = '\001\033[0m\002' # Reset normal coloring
89 _base = '\001\033[%sm\002' # Template for all other colors
90
91 # Build the actual color table as a set of class attributes:
92 make_color_table(InputTermColors)
93
94 class ColorScheme:
95 """Generic color scheme class. Just a name and a Struct."""
96 def __init__(self,__scheme_name_,colordict=None,**colormap):
97 self.name = __scheme_name_
98 if colordict is None:
99 self.colors = Struct(**colormap)
100 else:
101 self.colors = Struct(colordict)
102
103 def copy(self,name=None):
104 """Return a full copy of the object, optionally renaming it."""
105 if name is None:
106 name = self.name
107 return ColorScheme(name,self.colors.__dict__)
108
109 class ColorSchemeTable(dict):
110 """General class to handle tables of color schemes.
111
112 It's basically a dict of color schemes with a couple of shorthand
113 attributes and some convenient methods.
114
115 active_scheme_name -> obvious
116 active_colors -> actual color table of the active scheme"""
117
118 def __init__(self,scheme_list=None,default_scheme=''):
119 """Create a table of color schemes.
120
121 The table can be created empty and manually filled or it can be
122 created with a list of valid color schemes AND the specification for
123 the default active scheme.
124 """
125
126 # create object attributes to be set later
127 self.active_scheme_name = ''
128 self.active_colors = None
129
130 if scheme_list:
131 if default_scheme == '':
132 raise ValueError,'you must specify the default color scheme'
133 for scheme in scheme_list:
134 self.add_scheme(scheme)
135 self.set_active_scheme(default_scheme)
136
137 def copy(self):
138 """Return full copy of object"""
139 return ColorSchemeTable(self.values(),self.active_scheme_name)
140
141 def add_scheme(self,new_scheme):
142 """Add a new color scheme to the table."""
143 if not isinstance(new_scheme,ColorScheme):
144 raise ValueError,'ColorSchemeTable only accepts ColorScheme instances'
145 self[new_scheme.name] = new_scheme
146
147 def set_active_scheme(self,scheme,case_sensitive=0):
148 """Set the currently active scheme.
149
150 Names are by default compared in a case-insensitive way, but this can
151 be changed by setting the parameter case_sensitive to true."""
152
153 scheme_names = self.keys()
154 if case_sensitive:
155 valid_schemes = scheme_names
156 scheme_test = scheme
157 else:
158 valid_schemes = [s.lower() for s in scheme_names]
159 scheme_test = scheme.lower()
160 try:
161 scheme_idx = valid_schemes.index(scheme_test)
162 except ValueError:
163 raise ValueError,'Unrecognized color scheme: ' + scheme + \
164 '\nValid schemes: '+str(scheme_names).replace("'', ",'')
165 else:
166 active = scheme_names[scheme_idx]
167 self.active_scheme_name = active
168 self.active_colors = self[active].colors
169 # Now allow using '' as an index for the current active scheme
170 self[''] = self[active]
@@ -0,0 +1,116
1 # -*- coding: utf-8 -*-
2 """Configuration loader
3
4 $Id: ConfigLoader.py 1005 2006-01-12 08:39:26Z fperez $"""
5
6 #*****************************************************************************
7 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #*****************************************************************************
12
13 from IPython import Release
14 __author__ = '%s <%s>' % Release.authors['Fernando']
15 __license__ = Release.license
16
17 import exceptions
18 import os
19 from pprint import pprint
20
21 from IPython import ultraTB
22 from IPython.ipstruct import Struct
23 from IPython.genutils import *
24
25 class ConfigLoaderError(exceptions.Exception):
26 """Exception for ConfigLoader class."""
27
28 def __init__(self,args=None):
29 self.args = args
30
31 class ConfigLoader:
32
33 """Configuration file loader capable of handling recursive inclusions and
34 with parametrized conflict resolution for multiply found keys."""
35
36 def __init__(self,conflict=None,field_sep=None,reclimit=15):
37
38 """The reclimit parameter controls the number of recursive
39 configuration file inclusions. This way we can stop early on (before
40 python's own recursion limit is hit) if there is a circular
41 inclusion.
42
43 - conflict: dictionary for conflict resolutions (see Struct.merge())
44
45 """
46 self.conflict = conflict
47 self.field_sep = field_sep
48 self.reset(reclimit)
49
50 def reset(self,reclimit=15):
51 self.reclimit = reclimit
52 self.recdepth = 0
53 self.included = []
54
55 def load(self,fname,convert=None,recurse_key='',incpath = '.',**kw):
56 """Load a configuration file, return the resulting Struct.
57
58 Call: load_config(fname,convert=None,conflict=None,recurse_key='')
59
60 - fname: file to load from.
61 - convert: dictionary of type conversions (see read_dict())
62 - recurse_key: keyword in dictionary to trigger recursive file
63 inclusions.
64 """
65
66 if self.recdepth > self.reclimit:
67 raise ConfigLoaderError, 'maximum recursive inclusion of rcfiles '+\
68 'exceeded: ' + `self.recdepth` + \
69 '.\nMaybe you have a circular chain of inclusions?'
70 self.recdepth += 1
71 fname = filefind(fname,incpath)
72 data = Struct()
73 # avoid including the same file more than once
74 if fname in self.included:
75 return data
76 Xinfo = ultraTB.AutoFormattedTB()
77 if convert==None and recurse_key : convert = {qwflat:recurse_key}
78 # for production, change warn to 0:
79 data.merge(read_dict(fname,convert,fs=self.field_sep,strip=1,
80 warn=0,no_empty=0,**kw))
81 # keep track of successfully loaded files
82 self.included.append(fname)
83 if recurse_key in data.keys():
84 for incfilename in data[recurse_key]:
85 found=0
86 try:
87 incfile = filefind(incfilename,incpath)
88 except IOError:
89 if os.name in ['nt','dos']:
90 try:
91 # Try again with '.ini' extension
92 incfilename += '.ini'
93 incfile = filefind(incfilename,incpath)
94 except IOError:
95 found = 0
96 else:
97 found = 1
98 else:
99 found = 0
100 else:
101 found = 1
102 if found:
103 try:
104 data.merge(self.load(incfile,convert,recurse_key,
105 incpath,**kw),
106 self.conflict)
107 except:
108 Xinfo()
109 warn('Problem loading included file: '+
110 `incfilename` + '. Ignoring it...')
111 else:
112 warn('File `%s` not found. Included by %s' % (incfilename,fname))
113
114 return data
115
116 # end ConfigLoader
@@ -0,0 +1,228
1 # -*- coding: utf-8 -*-
2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
3
4 $Id: CrashHandler.py 2908 2007-12-30 21:07:46Z vivainio $"""
5
6 #*****************************************************************************
7 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #*****************************************************************************
12
13 from IPython import Release
14 __author__ = '%s <%s>' % Release.authors['Fernando']
15 __license__ = Release.license
16 __version__ = Release.version
17
18 #****************************************************************************
19 # Required modules
20
21 # From the standard library
22 import os
23 import sys
24 from pprint import pprint,pformat
25
26 # Homebrewed
27 from IPython.Itpl import Itpl,itpl,printpl
28 from IPython.ColorANSI import ColorScheme,ColorSchemeTable # too long names
29 from IPython import ultraTB
30 from IPython.genutils import *
31
32 #****************************************************************************
33 class CrashHandler:
34 """Customizable crash handlers for IPython-based systems.
35
36 Instances of this class provide a __call__ method which can be used as a
37 sys.excepthook, i.e., the __call__ signature is:
38
39 def __call__(self,etype, evalue, etb)
40
41 """
42
43 def __init__(self,IP,app_name,contact_name,contact_email,
44 bug_tracker,crash_report_fname,
45 show_crash_traceback=True):
46 """New crash handler.
47
48 Inputs:
49
50 - IP: a running IPython instance, which will be queried at crash time
51 for internal information.
52
53 - app_name: a string containing the name of your application.
54
55 - contact_name: a string with the name of the person to contact.
56
57 - contact_email: a string with the email address of the contact.
58
59 - bug_tracker: a string with the URL for your project's bug tracker.
60
61 - crash_report_fname: a string with the filename for the crash report
62 to be saved in. These reports are left in the ipython user directory
63 as determined by the running IPython instance.
64
65 Optional inputs:
66
67 - show_crash_traceback(True): if false, don't print the crash
68 traceback on stderr, only generate the on-disk report
69
70
71 Non-argument instance attributes:
72
73 These instances contain some non-argument attributes which allow for
74 further customization of the crash handler's behavior. Please see the
75 source for further details.
76 """
77
78 # apply args into instance
79 self.IP = IP # IPython instance
80 self.app_name = app_name
81 self.contact_name = contact_name
82 self.contact_email = contact_email
83 self.bug_tracker = bug_tracker
84 self.crash_report_fname = crash_report_fname
85 self.show_crash_traceback = show_crash_traceback
86
87 # Hardcoded defaults, which can be overridden either by subclasses or
88 # at runtime for the instance.
89
90 # Template for the user message. Subclasses which completely override
91 # this, or user apps, can modify it to suit their tastes. It gets
92 # expanded using itpl, so calls of the kind $self.foo are valid.
93 self.user_message_template = """
94 Oops, $self.app_name crashed. We do our best to make it stable, but...
95
96 A crash report was automatically generated with the following information:
97 - A verbatim copy of the crash traceback.
98 - A copy of your input history during this session.
99 - Data on your current $self.app_name configuration.
100
101 It was left in the file named:
102 \t'$self.crash_report_fname'
103 If you can email this file to the developers, the information in it will help
104 them in understanding and correcting the problem.
105
106 You can mail it to: $self.contact_name at $self.contact_email
107 with the subject '$self.app_name Crash Report'.
108
109 If you want to do it now, the following command will work (under Unix):
110 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
111
112 To ensure accurate tracking of this issue, please file a report about it at:
113 $self.bug_tracker
114 """
115
116 def __call__(self,etype, evalue, etb):
117 """Handle an exception, call for compatible with sys.excepthook"""
118
119 # Report tracebacks shouldn't use color in general (safer for users)
120 color_scheme = 'NoColor'
121
122 # Use this ONLY for developer debugging (keep commented out for release)
123 #color_scheme = 'Linux' # dbg
124
125 try:
126 rptdir = self.IP.rc.ipythondir
127 except:
128 rptdir = os.getcwd()
129 if not os.path.isdir(rptdir):
130 rptdir = os.getcwd()
131 report_name = os.path.join(rptdir,self.crash_report_fname)
132 # write the report filename into the instance dict so it can get
133 # properly expanded out in the user message template
134 self.crash_report_fname = report_name
135 TBhandler = ultraTB.VerboseTB(color_scheme=color_scheme,
136 long_header=1)
137 traceback = TBhandler.text(etype,evalue,etb,context=31)
138
139 # print traceback to screen
140 if self.show_crash_traceback:
141 print >> sys.stderr, traceback
142
143 # and generate a complete report on disk
144 try:
145 report = open(report_name,'w')
146 except:
147 print >> sys.stderr, 'Could not create crash report on disk.'
148 return
149
150 # Inform user on stderr of what happened
151 msg = itpl('\n'+'*'*70+'\n'+self.user_message_template)
152 print >> sys.stderr, msg
153
154 # Construct report on disk
155 report.write(self.make_report(traceback))
156 report.close()
157 raw_input("Press enter to exit:")
158
159 def make_report(self,traceback):
160 """Return a string containing a crash report."""
161
162 sec_sep = '\n\n'+'*'*75+'\n\n'
163
164 report = []
165 rpt_add = report.append
166
167 rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
168 rpt_add('IPython version: %s \n\n' % Release.version)
169 rpt_add('SVN revision : %s \n\n' % Release.revision)
170 rpt_add('Platform info : os.name -> %s, sys.platform -> %s' %
171 (os.name,sys.platform) )
172 rpt_add(sec_sep+'Current user configuration structure:\n\n')
173 rpt_add(pformat(self.IP.rc.dict()))
174 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
175 try:
176 rpt_add(sec_sep+"History of session input:")
177 for line in self.IP.user_ns['_ih']:
178 rpt_add(line)
179 rpt_add('\n*** Last line of input (may not be in above history):\n')
180 rpt_add(self.IP._last_input_line+'\n')
181 except:
182 pass
183
184 return ''.join(report)
185
186 class IPythonCrashHandler(CrashHandler):
187 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
188
189 def __init__(self,IP):
190
191 # Set here which of the IPython authors should be listed as contact
192 AUTHOR_CONTACT = 'Ville'
193
194 # Set argument defaults
195 app_name = 'IPython'
196 bug_tracker = 'http://projects.scipy.org/ipython/ipython/report'
197 contact_name,contact_email = Release.authors[AUTHOR_CONTACT][:2]
198 crash_report_fname = 'IPython_crash_report.txt'
199 # Call parent constructor
200 CrashHandler.__init__(self,IP,app_name,contact_name,contact_email,
201 bug_tracker,crash_report_fname)
202
203 def make_report(self,traceback):
204 """Return a string containing a crash report."""
205
206 sec_sep = '\n\n'+'*'*75+'\n\n'
207
208 report = []
209 rpt_add = report.append
210
211 rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
212 rpt_add('IPython version: %s \n\n' % Release.version)
213 rpt_add('SVN revision : %s \n\n' % Release.revision)
214 rpt_add('Platform info : os.name -> %s, sys.platform -> %s' %
215 (os.name,sys.platform) )
216 rpt_add(sec_sep+'Current user configuration structure:\n\n')
217 rpt_add(pformat(self.IP.rc.dict()))
218 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
219 try:
220 rpt_add(sec_sep+"History of session input:")
221 for line in self.IP.user_ns['_ih']:
222 rpt_add(line)
223 rpt_add('\n*** Last line of input (may not be in above history):\n')
224 rpt_add(self.IP._last_input_line+'\n')
225 except:
226 pass
227
228 return ''.join(report)
This diff has been collapsed as it changes many lines, (693 lines changed) Show them Hide them
@@ -0,0 +1,693
1 # -*- coding: utf-8 -*-
2 """DPyGetOpt -- Demiurge Python GetOptions Module
3
4 $Id: DPyGetOpt.py 2872 2007-11-25 17:58:05Z fperez $
5
6 This module is modeled after perl's Getopt::Long module-- which
7 is, in turn, modeled after GNU's extended getopt() function.
8
9 Upon instantiation, the option specification should be a sequence
10 (list) of option definitions.
11
12 Options that take no arguments should simply contain the name of
13 the option. If a ! is post-pended, the option can be negated by
14 prepending 'no'; ie 'debug!' specifies that -debug and -nodebug
15 should be accepted.
16
17 Mandatory arguments to options are specified using a postpended
18 '=' + a type specifier. '=s' specifies a mandatory string
19 argument, '=i' specifies a mandatory integer argument, and '=f'
20 specifies a mandatory real number. In all cases, the '=' can be
21 substituted with ':' to specify that the argument is optional.
22
23 Dashes '-' in option names are allowed.
24
25 If an option has the character '@' postpended (after the
26 argumentation specification), it can appear multiple times within
27 each argument list that is processed. The results will be stored
28 in a list.
29
30 The option name can actually be a list of names separated by '|'
31 characters; ie-- 'foo|bar|baz=f@' specifies that all -foo, -bar,
32 and -baz options that appear on within the parsed argument list
33 must have a real number argument and that the accumulated list
34 of values will be available under the name 'foo'
35
36 $Id: DPyGetOpt.py 2872 2007-11-25 17:58:05Z fperez $"""
37
38 #*****************************************************************************
39 #
40 # Copyright (c) 2001 Bill Bumgarner <bbum@friday.com>
41 #
42 #
43 # Published under the terms of the MIT license, hereby reproduced:
44 #
45 # Permission is hereby granted, free of charge, to any person obtaining a copy
46 # of this software and associated documentation files (the "Software"), to
47 # deal in the Software without restriction, including without limitation the
48 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
49 # sell copies of the Software, and to permit persons to whom the Software is
50 # furnished to do so, subject to the following conditions:
51 #
52 # The above copyright notice and this permission notice shall be included in
53 # all copies or substantial portions of the Software.
54 #
55 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
56 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
57 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
58 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
59 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
60 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
61 # IN THE SOFTWARE.
62 #
63 #*****************************************************************************
64
65 __author__ = 'Bill Bumgarner <bbum@friday.com>'
66 __license__ = 'MIT'
67 __version__ = '1.2'
68
69 # Modified to use re instead of regex and regsub modules.
70 # 2001/5/7, Jonathan Hogg <jonathan@onegoodidea.com>
71
72 import re
73 import string
74 import sys
75 import types
76
77 class Error(Exception):
78 """Base class for exceptions in the DPyGetOpt module."""
79
80 class ArgumentError(Error):
81 """Exception indicating an error in the arguments passed to
82 DPyGetOpt.processArguments."""
83
84 class SpecificationError(Error):
85 """Exception indicating an error with an option specification."""
86
87 class TerminationError(Error):
88 """Exception indicating an error with an option processing terminator."""
89
90 specificationExpr = re.compile('(?P<required>.)(?P<type>.)(?P<multi>@?)')
91
92 ArgRequired = 'Requires an Argument'
93 ArgOptional = 'Argument Optional'
94
95 # The types modules is not used for these identifiers because there
96 # is no identifier for 'boolean' or 'generic'
97 StringArgType = 'String Argument Type'
98 IntegerArgType = 'Integer Argument Type'
99 RealArgType = 'Real Argument Type'
100 BooleanArgType = 'Boolean Argument Type'
101 GenericArgType = 'Generic Argument Type'
102
103 # dictionary of conversion functions-- boolean and generic options
104 # do not accept arguments and do not need conversion functions;
105 # the identity function is used purely for convenience.
106 ConversionFunctions = {
107 StringArgType : lambda x: x,
108 IntegerArgType : string.atoi,
109 RealArgType : string.atof,
110 BooleanArgType : lambda x: x,
111 GenericArgType : lambda x: x,
112 }
113
114 class DPyGetOpt:
115
116 def __init__(self, spec = None, terminators = ['--']):
117 """
118 Declare and intialize instance variables
119
120 Yes, declaration is not necessary... but one of the things
121 I sorely miss from C/Obj-C is the concept of having an
122 interface definition that clearly declares all instance
123 variables and methods without providing any implementation
124 details. it is a useful reference!
125
126 all instance variables are initialized to 0/Null/None of
127 the appropriate type-- not even the default value...
128 """
129
130 # sys.stderr.write(string.join(spec) + "\n")
131
132 self.allowAbbreviations = 1 # boolean, 1 if abbreviations will
133 # be expanded
134 self.freeValues = [] # list, contains free values
135 self.ignoreCase = 0 # boolean, YES if ignoring case
136 self.needsParse = 0 # boolean, YES if need to reparse parameter spec
137 self.optionNames = {} # dict, all option names-- value is index of tuple
138 self.optionStartExpr = None # regexp defining the start of an option (ie; '-', '--')
139 self.optionTuples = [] # list o' tuples containing defn of options AND aliases
140 self.optionValues = {} # dict, option names (after alias expansion) -> option value(s)
141 self.orderMixed = 0 # boolean, YES if options can be mixed with args
142 self.posixCompliance = 0 # boolean, YES indicates posix like behaviour
143 self.spec = [] # list, raw specs (in case it must be reparsed)
144 self.terminators = terminators # list, strings that terminate argument processing
145 self.termValues = [] # list, values after terminator
146 self.terminator = None # full name of terminator that ended
147 # option processing
148
149 # set up defaults
150 self.setPosixCompliance()
151 self.setIgnoreCase()
152 self.setAllowAbbreviations()
153
154 # parse spec-- if present
155 if spec:
156 self.parseConfiguration(spec)
157
158 def setPosixCompliance(self, aFlag = 0):
159 """
160 Enables and disables posix compliance.
161
162 When enabled, '+' can be used as an option prefix and free
163 values can be mixed with options.
164 """
165 self.posixCompliance = aFlag
166 self.needsParse = 1
167
168 if self.posixCompliance:
169 self.optionStartExpr = re.compile('(--|-)(?P<option>[A-Za-z0-9_-]+)(?P<arg>=.*)?')
170 self.orderMixed = 0
171 else:
172 self.optionStartExpr = re.compile('(--|-|\+)(?P<option>[A-Za-z0-9_-]+)(?P<arg>=.*)?')
173 self.orderMixed = 1
174
175 def isPosixCompliant(self):
176 """
177 Returns the value of the posix compliance flag.
178 """
179 return self.posixCompliance
180
181 def setIgnoreCase(self, aFlag = 1):
182 """
183 Enables and disables ignoring case during option processing.
184 """
185 self.needsParse = 1
186 self.ignoreCase = aFlag
187
188 def ignoreCase(self):
189 """
190 Returns 1 if the option processor will ignore case when
191 processing options.
192 """
193 return self.ignoreCase
194
195 def setAllowAbbreviations(self, aFlag = 1):
196 """
197 Enables and disables the expansion of abbreviations during
198 option processing.
199 """
200 self.allowAbbreviations = aFlag
201
202 def willAllowAbbreviations(self):
203 """
204 Returns 1 if abbreviated options will be automatically
205 expanded to the non-abbreviated form (instead of causing an
206 unrecognized option error).
207 """
208 return self.allowAbbreviations
209
210 def addTerminator(self, newTerm):
211 """
212 Adds newTerm as terminator of option processing.
213
214 Whenever the option processor encounters one of the terminators
215 during option processing, the processing of options terminates
216 immediately, all remaining options are stored in the termValues
217 instance variable and the full name of the terminator is stored
218 in the terminator instance variable.
219 """
220 self.terminators = self.terminators + [newTerm]
221
222 def _addOption(self, oTuple):
223 """
224 Adds the option described by oTuple (name, (type, mode,
225 default), alias) to optionTuples. Adds index keyed under name
226 to optionNames. Raises SpecificationError if name already in
227 optionNames
228 """
229 (name, (type, mode, default, multi), realName) = oTuple
230
231 # verify name and add to option names dictionary
232 if self.optionNames.has_key(name):
233 if realName:
234 raise SpecificationError('Alias \'' + name + '\' for \'' +
235 realName +
236 '\' already used for another option or alias.')
237 else:
238 raise SpecificationError('Option named \'' + name +
239 '\' specified more than once. Specification: '
240 + option)
241
242 # validated. add to optionNames
243 self.optionNames[name] = self.tupleIndex
244 self.tupleIndex = self.tupleIndex + 1
245
246 # add to optionTuples
247 self.optionTuples = self.optionTuples + [oTuple]
248
249 # if type is boolean, add negation
250 if type == BooleanArgType:
251 alias = 'no' + name
252 specTuple = (type, mode, 0, multi)
253 oTuple = (alias, specTuple, name)
254
255 # verify name and add to option names dictionary
256 if self.optionNames.has_key(alias):
257 if realName:
258 raise SpecificationError('Negated alias \'' + name +
259 '\' for \'' + realName +
260 '\' already used for another option or alias.')
261 else:
262 raise SpecificationError('Negated option named \'' + name +
263 '\' specified more than once. Specification: '
264 + option)
265
266 # validated. add to optionNames
267 self.optionNames[alias] = self.tupleIndex
268 self.tupleIndex = self.tupleIndex + 1
269
270 # add to optionTuples
271 self.optionTuples = self.optionTuples + [oTuple]
272
273 def addOptionConfigurationTuple(self, oTuple):
274 (name, argSpec, realName) = oTuple
275 if self.ignoreCase:
276 name = string.lower(name)
277 if realName:
278 realName = string.lower(realName)
279 else:
280 realName = name
281
282 oTuple = (name, argSpec, realName)
283
284 # add option
285 self._addOption(oTuple)
286
287 def addOptionConfigurationTuples(self, oTuple):
288 if type(oTuple) is ListType:
289 for t in oTuple:
290 self.addOptionConfigurationTuple(t)
291 else:
292 self.addOptionConfigurationTuple(oTuple)
293
294 def parseConfiguration(self, spec):
295 # destroy previous stored information + store raw spec
296 self.spec = spec
297 self.optionTuples = []
298 self.optionNames = {}
299 self.tupleIndex = 0
300
301 tupleIndex = 0
302
303 # create some regex's for parsing each spec
304 splitExpr = \
305 re.compile('(?P<names>\w+[-A-Za-z0-9|]*)?(?P<spec>!|[=:][infs]@?)?')
306 for option in spec:
307 # push to lower case (does not negatively affect
308 # specification)
309 if self.ignoreCase:
310 option = string.lower(option)
311
312 # break into names, specification
313 match = splitExpr.match(option)
314 if match is None:
315 raise SpecificationError('Invalid specification {' + option +
316 '}')
317
318 names = match.group('names')
319 specification = match.group('spec')
320
321 # break name into name, aliases
322 nlist = string.split(names, '|')
323
324 # get name
325 name = nlist[0]
326 aliases = nlist[1:]
327
328 # specificationExpr = regex.symcomp('\(<required>.\)\(<type>.\)\(<multi>@?\)')
329 if not specification:
330 #spec tuple is ('type', 'arg mode', 'default value', 'multiple')
331 argType = GenericArgType
332 argMode = None
333 argDefault = 1
334 argMultiple = 0
335 elif specification == '!':
336 argType = BooleanArgType
337 argMode = None
338 argDefault = 1
339 argMultiple = 0
340 else:
341 # parse
342 match = specificationExpr.match(specification)
343 if match is None:
344 # failed to parse, die
345 raise SpecificationError('Invalid configuration for option \''
346 + option + '\'')
347
348 # determine mode
349 required = match.group('required')
350 if required == '=':
351 argMode = ArgRequired
352 elif required == ':':
353 argMode = ArgOptional
354 else:
355 raise SpecificationError('Unknown requirement configuration \''
356 + required + '\'')
357
358 # determine type
359 type = match.group('type')
360 if type == 's':
361 argType = StringArgType
362 argDefault = ''
363 elif type == 'i':
364 argType = IntegerArgType
365 argDefault = 1
366 elif type == 'f' or type == 'n':
367 argType = RealArgType
368 argDefault = 1
369 else:
370 raise SpecificationError('Unknown type specifier \'' +
371 type + '\'')
372
373 # determine quantity
374 if match.group('multi') == '@':
375 argMultiple = 1
376 else:
377 argMultiple = 0
378 ## end else (of not specification)
379
380 # construct specification tuple
381 specTuple = (argType, argMode, argDefault, argMultiple)
382
383 # add the option-- option tuple is (name, specTuple, real name)
384 oTuple = (name, specTuple, name)
385 self._addOption(oTuple)
386
387 for alias in aliases:
388 # drop to all lower (if configured to do so)
389 if self.ignoreCase:
390 alias = string.lower(alias)
391 # create configuration tuple
392 oTuple = (alias, specTuple, name)
393 # add
394 self._addOption(oTuple)
395
396 # successfully parsed....
397 self.needsParse = 0
398
399 def _getArgTuple(self, argName):
400 """
401 Returns a list containing all the specification tuples that
402 match argName. If none match, None is returned. If one
403 matches, a list with one tuple is returned. If more than one
404 match, a list containing all the tuples that matched is
405 returned.
406
407 In other words, this function does not pass judgement upon the
408 validity of multiple matches.
409 """
410 # is it in the optionNames dict?
411
412 try:
413 # sys.stderr.write(argName + string.join(self.optionNames.keys()) + "\n")
414
415 # yes, get index
416 tupleIndex = self.optionNames[argName]
417 # and return tuple as element of list
418 return [self.optionTuples[tupleIndex]]
419 except KeyError:
420 # are abbreviations allowed?
421 if not self.allowAbbreviations:
422 # No! terefore, this cannot be valid argument-- nothing found
423 return None
424
425 # argName might be an abbreviation (and, abbreviations must
426 # be allowed... or this would not have been reached!)
427
428 # create regex for argName
429 argExpr = re.compile('^' + argName)
430
431 tuples = filter(lambda x, argExpr=argExpr: argExpr.search(x[0]) is not None,
432 self.optionTuples)
433
434 if not len(tuples):
435 return None
436 else:
437 return tuples
438
439 def _isTerminator(self, optionName):
440 """
441 Returns the full name of the terminator if optionName is a valid
442 terminator. If it is, sets self.terminator to the full name of
443 the terminator.
444
445 If more than one terminator matched, raises a TerminationError with a
446 string describing the ambiguity.
447 """
448
449 # sys.stderr.write(optionName + "\n")
450 # sys.stderr.write(repr(self.terminators))
451
452 if optionName in self.terminators:
453 self.terminator = optionName
454 elif not self.allowAbbreviations:
455 return None
456
457 # regex thing in bogus
458 # termExpr = regex.compile('^' + optionName)
459
460 terms = filter(lambda x, on=optionName: string.find(x,on) == 0, self.terminators)
461
462 if not len(terms):
463 return None
464 elif len(terms) > 1:
465 raise TerminationError('Ambiguous terminator \'' + optionName +
466 '\' matches ' + repr(terms))
467
468 self.terminator = terms[0]
469 return self.terminator
470
471 def processArguments(self, args = None):
472 """
473 Processes args, a list of arguments (including options).
474
475 If args is the same as sys.argv, automatically trims the first
476 argument (the executable name/path).
477
478 If an exception is not raised, the argument list was parsed
479 correctly.
480
481 Upon successful completion, the freeValues instance variable
482 will contain all the arguments that were not associated with an
483 option in the order they were encountered. optionValues is a
484 dictionary containing the value of each option-- the method
485 valueForOption() can be used to query this dictionary.
486 terminator will contain the argument encountered that terminated
487 option processing (or None, if a terminator was never
488 encountered) and termValues will contain all of the options that
489 appeared after the Terminator (or an empty list).
490 """
491
492 if hasattr(sys, "argv") and args == sys.argv:
493 args = sys.argv[1:]
494
495 max = len(args) # maximum index + 1
496 self.freeValues = [] # array to hold return values
497 self.optionValues= {}
498 index = 0 # initial index
499 self.terminator = None
500 self.termValues = []
501
502 while index < max:
503 # obtain argument
504 arg = args[index]
505 # increment index -- REMEMBER; it is NOW incremented
506 index = index + 1
507
508 # terminate immediately if option terminator encountered
509 if self._isTerminator(arg):
510 self.freeValues = self.freeValues + args[index:]
511 self.termValues = args[index:]
512 return
513
514 # is this possibly an option?
515 match = self.optionStartExpr.match(arg)
516 if match is None:
517 # not an option-- add to freeValues
518 self.freeValues = self.freeValues + [arg]
519 if not self.orderMixed:
520 # mixing not allowed; add rest of args as freeValues
521 self.freeValues = self.freeValues + args[index:]
522 # return to caller
523 return
524 else:
525 continue
526
527 # grab name
528 optName = match.group('option')
529
530 # obtain next argument-- index has already been incremented
531 nextArg = match.group('arg')
532 if nextArg:
533 nextArg = nextArg[1:]
534 index = index - 1 # put it back
535 else:
536 try:
537 nextArg = args[index]
538 except:
539 nextArg = None
540
541 # transpose to lower case, if necessary
542 if self.ignoreCase:
543 optName = string.lower(optName)
544
545 # obtain defining tuple
546 tuples = self._getArgTuple(optName)
547
548 if tuples == None:
549 raise ArgumentError('Illegal option \'' + arg + '\'')
550 elif len(tuples) > 1:
551 raise ArgumentError('Ambiguous option \'' + arg +
552 '\'; matches ' +
553 repr(map(lambda x: x[0], tuples)))
554 else:
555 config = tuples[0]
556
557 # config is now set to the configuration tuple for the
558 # argument
559 (fullName, spec, realName) = config
560 (optType, optMode, optDefault, optMultiple) = spec
561
562 # if opt mode required, but nextArg is none, raise an error
563 if (optMode == ArgRequired):
564 if (not nextArg) or self._isTerminator(nextArg):
565 # print nextArg
566 raise ArgumentError('Option \'' + arg +
567 '\' requires an argument of type ' +
568 optType)
569
570 if (not optMode == None) and nextArg and (not self._isTerminator(nextArg)):
571 # nextArg defined, option configured to possibly consume arg
572 try:
573 # grab conversion function-- the try is more for internal diagnostics
574 func = ConversionFunctions[optType]
575 try:
576 optionValue = func(nextArg)
577 index = index + 1
578 except:
579 # only raise conversion error if REQUIRED to consume argument
580 if optMode == ArgRequired:
581 raise ArgumentError('Invalid argument to option \''
582 + arg + '\'; should be \'' +
583 optType + '\'')
584 else:
585 optionValue = optDefault
586 except ArgumentError:
587 raise
588 except:
589 raise ArgumentError('(' + arg +
590 ') Conversion function for \'' +
591 optType + '\' not found.')
592 else:
593 optionValue = optDefault
594
595 # add value to options dictionary
596 if optMultiple:
597 # can be multiple values
598 try:
599 # try to append element
600 self.optionValues[realName] = self.optionValues[realName] + [optionValue]
601 except:
602 # failed-- must not exist; add it
603 self.optionValues[realName] = [optionValue]
604 else:
605 # only one value per
606 if self.isPosixCompliant and self.optionValues.has_key(realName):
607 raise ArgumentError('Argument \'' + arg +
608 '\' occurs multiple times.')
609
610 self.optionValues[realName] = optionValue
611
612 def valueForOption(self, optionName, defaultValue = None):
613 """
614 Return the value associated with optionName. If optionName was
615 not encountered during parsing of the arguments, returns the
616 defaultValue (which defaults to None).
617 """
618 try:
619 optionValue = self.optionValues[optionName]
620 except:
621 optionValue = defaultValue
622
623 return optionValue
624
625 ##
626 ## test/example section
627 ##
628 test_error = 'Test Run Amok!'
629 def _test():
630 """
631 A relatively complete test suite.
632 """
633 try:
634 DPyGetOpt(['foo', 'bar=s', 'foo'])
635 except Error, exc:
636 print 'EXCEPTION (should be \'foo\' already used..): %s' % exc
637
638 try:
639 DPyGetOpt(['foo|bar|apple=s@', 'baz|apple!'])
640 except Error, exc:
641 print 'EXCEPTION (should be duplicate alias/name error): %s' % exc
642
643 x = DPyGetOpt(['apple|atlas=i@', 'application|executable=f@'])
644 try:
645 x.processArguments(['-app', '29.3'])
646 except Error, exc:
647 print 'EXCEPTION (should be ambiguous argument): %s' % exc
648
649 x = DPyGetOpt(['foo'], ['antigravity', 'antithesis'])
650 try:
651 x.processArguments(['-foo', 'anti'])
652 except Error, exc:
653 print 'EXCEPTION (should be ambiguous terminator): %s' % exc
654
655 profile = ['plain-option',
656 'boolean-option!',
657 'list-of-integers=i@',
658 'list-real-option|list-real-alias|list-real-pseudonym=f@',
659 'optional-string-option:s',
660 'abbreviated-string-list=s@']
661
662 terminators = ['terminator']
663
664 args = ['-plain-option',
665 '+noboolean-option',
666 '--list-of-integers', '1',
667 '+list-of-integers', '2',
668 '-list-of-integers', '3',
669 'freeargone',
670 '-list-real-option', '1.1',
671 '+list-real-alias', '1.2',
672 '--list-real-pseudonym', '1.3',
673 'freeargtwo',
674 '-abbreviated-string-list', 'String1',
675 '--abbreviated-s', 'String2',
676 '-abbrev', 'String3',
677 '-a', 'String4',
678 '-optional-string-option',
679 'term',
680 'next option should look like an invalid arg',
681 '-a']
682
683
684 print 'Using profile: ' + repr(profile)
685 print 'With terminator: ' + repr(terminators)
686 print 'Processing arguments: ' + repr(args)
687
688 go = DPyGetOpt(profile, terminators)
689 go.processArguments(args)
690
691 print 'Options (and values): ' + repr(go.optionValues)
692 print 'free args: ' + repr(go.freeValues)
693 print 'term args: ' + repr(go.termValues)
This diff has been collapsed as it changes many lines, (517 lines changed) Show them Hide them
@@ -0,0 +1,517
1 # -*- coding: utf-8 -*-
2 """
3 Pdb debugger class.
4
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
6 the command line completion of other programs which include this isn't
7 damaged.
8
9 In the future, this class will be expanded with improvements over the standard
10 pdb.
11
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
13 changes. Licensing should therefore be under the standard Python terms. For
14 details on the PSF (Python Software Foundation) standard license, see:
15
16 http://www.python.org/2.2.3/license.html
17
18 $Id: Debugger.py 2913 2007-12-31 12:42:14Z vivainio $"""
19
20 #*****************************************************************************
21 #
22 # This file is licensed under the PSF license.
23 #
24 # Copyright (C) 2001 Python Software Foundation, www.python.org
25 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
26 #
27 #
28 #*****************************************************************************
29
30 from IPython import Release
31 __author__ = '%s <%s>' % Release.authors['Fernando']
32 __license__ = 'Python'
33
34 import bdb
35 import cmd
36 import linecache
37 import os
38 import sys
39
40 from IPython import PyColorize, ColorANSI, ipapi
41 from IPython.genutils import Term
42 from IPython.excolors import ExceptionColors
43
44 # See if we can use pydb.
45 has_pydb = False
46 prompt = 'ipdb> '
47 #We have to check this directly from sys.argv, config struct not yet available
48 if '-pydb' in sys.argv:
49 try:
50 import pydb
51 if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
52 # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
53 # better protect against it.
54 has_pydb = True
55 except ImportError:
56 print "Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available"
57
58 if has_pydb:
59 from pydb import Pdb as OldPdb
60 #print "Using pydb for %run -d and post-mortem" #dbg
61 prompt = 'ipydb> '
62 else:
63 from pdb import Pdb as OldPdb
64
65 # Allow the set_trace code to operate outside of an ipython instance, even if
66 # it does so with some limitations. The rest of this support is implemented in
67 # the Tracer constructor.
68 def BdbQuit_excepthook(et,ev,tb):
69 if et==bdb.BdbQuit:
70 print 'Exiting Debugger.'
71 else:
72 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
73
74 def BdbQuit_IPython_excepthook(self,et,ev,tb):
75 print 'Exiting Debugger.'
76
77 class Tracer(object):
78 """Class for local debugging, similar to pdb.set_trace.
79
80 Instances of this class, when called, behave like pdb.set_trace, but
81 providing IPython's enhanced capabilities.
82
83 This is implemented as a class which must be initialized in your own code
84 and not as a standalone function because we need to detect at runtime
85 whether IPython is already active or not. That detection is done in the
86 constructor, ensuring that this code plays nicely with a running IPython,
87 while functioning acceptably (though with limitations) if outside of it.
88 """
89
90 def __init__(self,colors=None):
91 """Create a local debugger instance.
92
93 :Parameters:
94
95 - `colors` (None): a string containing the name of the color scheme to
96 use, it must be one of IPython's valid color schemes. If not given, the
97 function will default to the current IPython scheme when running inside
98 IPython, and to 'NoColor' otherwise.
99
100 Usage example:
101
102 from IPython.Debugger import Tracer; debug_here = Tracer()
103
104 ... later in your code
105 debug_here() # -> will open up the debugger at that point.
106
107 Once the debugger activates, you can use all of its regular commands to
108 step through code, set breakpoints, etc. See the pdb documentation
109 from the Python standard library for usage details.
110 """
111
112 global __IPYTHON__
113 try:
114 __IPYTHON__
115 except NameError:
116 # Outside of ipython, we set our own exception hook manually
117 __IPYTHON__ = ipapi.get(True,False)
118 BdbQuit_excepthook.excepthook_ori = sys.excepthook
119 sys.excepthook = BdbQuit_excepthook
120 def_colors = 'NoColor'
121 try:
122 # Limited tab completion support
123 import rlcompleter,readline
124 readline.parse_and_bind('tab: complete')
125 except ImportError:
126 pass
127 else:
128 # In ipython, we use its custom exception handler mechanism
129 ip = ipapi.get()
130 def_colors = ip.options.colors
131 ip.set_custom_exc((bdb.BdbQuit,),BdbQuit_IPython_excepthook)
132
133 if colors is None:
134 colors = def_colors
135 self.debugger = Pdb(colors)
136
137 def __call__(self):
138 """Starts an interactive debugger at the point where called.
139
140 This is similar to the pdb.set_trace() function from the std lib, but
141 using IPython's enhanced debugger."""
142
143 self.debugger.set_trace(sys._getframe().f_back)
144
145 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
146 """Make new_fn have old_fn's doc string. This is particularly useful
147 for the do_... commands that hook into the help system.
148 Adapted from from a comp.lang.python posting
149 by Duncan Booth."""
150 def wrapper(*args, **kw):
151 return new_fn(*args, **kw)
152 if old_fn.__doc__:
153 wrapper.__doc__ = old_fn.__doc__ + additional_text
154 return wrapper
155
156 def _file_lines(fname):
157 """Return the contents of a named file as a list of lines.
158
159 This function never raises an IOError exception: if the file can't be
160 read, it simply returns an empty list."""
161
162 try:
163 outfile = open(fname)
164 except IOError:
165 return []
166 else:
167 out = outfile.readlines()
168 outfile.close()
169 return out
170
171 class Pdb(OldPdb):
172 """Modified Pdb class, does not load readline."""
173
174 if sys.version[:3] >= '2.5' or has_pydb:
175 def __init__(self,color_scheme='NoColor',completekey=None,
176 stdin=None, stdout=None):
177
178 # Parent constructor:
179 if has_pydb and completekey is None:
180 OldPdb.__init__(self,stdin=stdin,stdout=Term.cout)
181 else:
182 OldPdb.__init__(self,completekey,stdin,stdout)
183
184 self.prompt = prompt # The default prompt is '(Pdb)'
185
186 # IPython changes...
187 self.is_pydb = has_pydb
188
189 if self.is_pydb:
190
191 # iplib.py's ipalias seems to want pdb's checkline
192 # which located in pydb.fn
193 import pydb.fns
194 self.checkline = lambda filename, lineno: \
195 pydb.fns.checkline(self, filename, lineno)
196
197 self.curframe = None
198 self.do_restart = self.new_do_restart
199
200 self.old_all_completions = __IPYTHON__.Completer.all_completions
201 __IPYTHON__.Completer.all_completions=self.all_completions
202
203 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
204 OldPdb.do_list)
205 self.do_l = self.do_list
206 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
207 OldPdb.do_frame)
208
209 self.aliases = {}
210
211 # Create color table: we copy the default one from the traceback
212 # module and add a few attributes needed for debugging
213 self.color_scheme_table = ExceptionColors.copy()
214
215 # shorthands
216 C = ColorANSI.TermColors
217 cst = self.color_scheme_table
218
219 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
220 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
221
222 cst['Linux'].colors.breakpoint_enabled = C.LightRed
223 cst['Linux'].colors.breakpoint_disabled = C.Red
224
225 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
226 cst['LightBG'].colors.breakpoint_disabled = C.Red
227
228 self.set_colors(color_scheme)
229
230 # Add a python parser so we can syntax highlight source while
231 # debugging.
232 self.parser = PyColorize.Parser()
233
234
235 else:
236 # Ugly hack: for Python 2.3-2.4, we can't call the parent constructor,
237 # because it binds readline and breaks tab-completion. This means we
238 # have to COPY the constructor here.
239 def __init__(self,color_scheme='NoColor'):
240 bdb.Bdb.__init__(self)
241 cmd.Cmd.__init__(self,completekey=None) # don't load readline
242 self.prompt = 'ipdb> ' # The default prompt is '(Pdb)'
243 self.aliases = {}
244
245 # These two lines are part of the py2.4 constructor, let's put them
246 # unconditionally here as they won't cause any problems in 2.3.
247 self.mainpyfile = ''
248 self._wait_for_mainpyfile = 0
249
250 # Read $HOME/.pdbrc and ./.pdbrc
251 try:
252 self.rcLines = _file_lines(os.path.join(os.environ['HOME'],
253 ".pdbrc"))
254 except KeyError:
255 self.rcLines = []
256 self.rcLines.extend(_file_lines(".pdbrc"))
257
258 # Create color table: we copy the default one from the traceback
259 # module and add a few attributes needed for debugging
260 ExceptionColors.set_active_scheme(color_scheme)
261 self.color_scheme_table = ExceptionColors.copy()
262
263 # shorthands
264 C = ColorANSI.TermColors
265 cst = self.color_scheme_table
266
267 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
268 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
269
270 cst['Linux'].colors.breakpoint_enabled = C.LightRed
271 cst['Linux'].colors.breakpoint_disabled = C.Red
272
273 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
274 cst['LightBG'].colors.breakpoint_disabled = C.Red
275
276 self.set_colors(color_scheme)
277
278 # Add a python parser so we can syntax highlight source while
279 # debugging.
280 self.parser = PyColorize.Parser()
281
282 def set_colors(self, scheme):
283 """Shorthand access to the color table scheme selector method."""
284 self.color_scheme_table.set_active_scheme(scheme)
285
286 def interaction(self, frame, traceback):
287 __IPYTHON__.set_completer_frame(frame)
288 OldPdb.interaction(self, frame, traceback)
289
290 def new_do_up(self, arg):
291 OldPdb.do_up(self, arg)
292 __IPYTHON__.set_completer_frame(self.curframe)
293 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
294
295 def new_do_down(self, arg):
296 OldPdb.do_down(self, arg)
297 __IPYTHON__.set_completer_frame(self.curframe)
298
299 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
300
301 def new_do_frame(self, arg):
302 OldPdb.do_frame(self, arg)
303 __IPYTHON__.set_completer_frame(self.curframe)
304
305 def new_do_quit(self, arg):
306
307 if hasattr(self, 'old_all_completions'):
308 __IPYTHON__.Completer.all_completions=self.old_all_completions
309
310
311 return OldPdb.do_quit(self, arg)
312
313 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
314
315 def new_do_restart(self, arg):
316 """Restart command. In the context of ipython this is exactly the same
317 thing as 'quit'."""
318 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
319 return self.do_quit(arg)
320
321 def postloop(self):
322 __IPYTHON__.set_completer_frame(None)
323
324 def print_stack_trace(self):
325 try:
326 for frame_lineno in self.stack:
327 self.print_stack_entry(frame_lineno, context = 5)
328 except KeyboardInterrupt:
329 pass
330
331 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
332 context = 3):
333 #frame, lineno = frame_lineno
334 print >>Term.cout, self.format_stack_entry(frame_lineno, '', context)
335
336 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
337 import linecache, repr
338
339 ret = []
340
341 Colors = self.color_scheme_table.active_colors
342 ColorsNormal = Colors.Normal
343 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
344 tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
345 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
346 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
347 ColorsNormal)
348
349 frame, lineno = frame_lineno
350
351 return_value = ''
352 if '__return__' in frame.f_locals:
353 rv = frame.f_locals['__return__']
354 #return_value += '->'
355 return_value += repr.repr(rv) + '\n'
356 ret.append(return_value)
357
358 #s = filename + '(' + `lineno` + ')'
359 filename = self.canonic(frame.f_code.co_filename)
360 link = tpl_link % filename
361
362 if frame.f_code.co_name:
363 func = frame.f_code.co_name
364 else:
365 func = "<lambda>"
366
367 call = ''
368 if func != '?':
369 if '__args__' in frame.f_locals:
370 args = repr.repr(frame.f_locals['__args__'])
371 else:
372 args = '()'
373 call = tpl_call % (func, args)
374
375 # The level info should be generated in the same format pdb uses, to
376 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
377 if frame is self.curframe:
378 ret.append('> ')
379 else:
380 ret.append(' ')
381 ret.append('%s(%s)%s\n' % (link,lineno,call))
382
383 start = lineno - 1 - context//2
384 lines = linecache.getlines(filename)
385 start = max(start, 0)
386 start = min(start, len(lines) - context)
387 lines = lines[start : start + context]
388
389 for i,line in enumerate(lines):
390 show_arrow = (start + 1 + i == lineno)
391 linetpl = (frame is self.curframe or show_arrow) \
392 and tpl_line_em \
393 or tpl_line
394 ret.append(self.__format_line(linetpl, filename,
395 start + 1 + i, line,
396 arrow = show_arrow) )
397
398 return ''.join(ret)
399
400 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
401 bp_mark = ""
402 bp_mark_color = ""
403
404 scheme = self.color_scheme_table.active_scheme_name
405 new_line, err = self.parser.format2(line, 'str', scheme)
406 if not err: line = new_line
407
408 bp = None
409 if lineno in self.get_file_breaks(filename):
410 bps = self.get_breaks(filename, lineno)
411 bp = bps[-1]
412
413 if bp:
414 Colors = self.color_scheme_table.active_colors
415 bp_mark = str(bp.number)
416 bp_mark_color = Colors.breakpoint_enabled
417 if not bp.enabled:
418 bp_mark_color = Colors.breakpoint_disabled
419
420 numbers_width = 7
421 if arrow:
422 # This is the line with the error
423 pad = numbers_width - len(str(lineno)) - len(bp_mark)
424 if pad >= 3:
425 marker = '-'*(pad-3) + '-> '
426 elif pad == 2:
427 marker = '> '
428 elif pad == 1:
429 marker = '>'
430 else:
431 marker = ''
432 num = '%s%s' % (marker, str(lineno))
433 line = tpl_line % (bp_mark_color + bp_mark, num, line)
434 else:
435 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
436 line = tpl_line % (bp_mark_color + bp_mark, num, line)
437
438 return line
439
440 def list_command_pydb(self, arg):
441 """List command to use if we have a newer pydb installed"""
442 filename, first, last = OldPdb.parse_list_cmd(self, arg)
443 if filename is not None:
444 self.print_list_lines(filename, first, last)
445
446 def print_list_lines(self, filename, first, last):
447 """The printing (as opposed to the parsing part of a 'list'
448 command."""
449 try:
450 Colors = self.color_scheme_table.active_colors
451 ColorsNormal = Colors.Normal
452 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
453 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
454 src = []
455 for lineno in range(first, last+1):
456 line = linecache.getline(filename, lineno)
457 if not line:
458 break
459
460 if lineno == self.curframe.f_lineno:
461 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
462 else:
463 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
464
465 src.append(line)
466 self.lineno = lineno
467
468 print >>Term.cout, ''.join(src)
469
470 except KeyboardInterrupt:
471 pass
472
473 def do_list(self, arg):
474 self.lastcmd = 'list'
475 last = None
476 if arg:
477 try:
478 x = eval(arg, {}, {})
479 if type(x) == type(()):
480 first, last = x
481 first = int(first)
482 last = int(last)
483 if last < first:
484 # Assume it's a count
485 last = first + last
486 else:
487 first = max(1, int(x) - 5)
488 except:
489 print '*** Error in argument:', `arg`
490 return
491 elif self.lineno is None:
492 first = max(1, self.curframe.f_lineno - 5)
493 else:
494 first = self.lineno + 1
495 if last is None:
496 last = first + 10
497 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
498
499 do_l = do_list
500
501 def do_pdef(self, arg):
502 """The debugger interface to magic_pdef"""
503 namespaces = [('Locals', self.curframe.f_locals),
504 ('Globals', self.curframe.f_globals)]
505 __IPYTHON__.magic_pdef(arg, namespaces=namespaces)
506
507 def do_pdoc(self, arg):
508 """The debugger interface to magic_pdoc"""
509 namespaces = [('Locals', self.curframe.f_locals),
510 ('Globals', self.curframe.f_globals)]
511 __IPYTHON__.magic_pdoc(arg, namespaces=namespaces)
512
513 def do_pinfo(self, arg):
514 """The debugger equivalant of ?obj"""
515 namespaces = [('Locals', self.curframe.f_locals),
516 ('Globals', self.curframe.f_globals)]
517 __IPYTHON__.magic_pinfo("pinfo %s" % arg, namespaces=namespaces)
@@ -0,0 +1,259
1 # -*- coding: utf-8 -*-
2 """Modified input prompt for executing files.
3
4 We define a special input line filter to allow typing lines which begin with
5 '~', '/' or '.'. If one of those strings is encountered, it is automatically
6 executed.
7
8 $Id: InterpreterExec.py 2724 2007-09-07 08:05:38Z fperez $"""
9
10 #*****************************************************************************
11 # Copyright (C) 2004 W.J. van der Laan <gnufnork@hetdigitalegat.nl>
12 # Copyright (C) 2004-2006 Fernando Perez <fperez@colorado.edu>
13 #
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
16 #*****************************************************************************
17
18 from IPython import Release
19 __author__ = 'W.J. van der Laan <gnufnork@hetdigitalegat.nl>, '\
20 '%s <%s>' % Release.authors['Fernando']
21 __license__ = Release.license
22
23 # TODO: deprecated
24 def prefilter_shell(self,line,continuation):
25 """Alternate prefilter, modified for shell-like functionality.
26
27 - Execute all lines beginning with '~', '/' or '.'
28 - $var=cmd <=> %sc var=cmd
29 - $$var=cmd <=> %sc -l var=cmd
30 """
31
32 if line:
33 l0 = line[0]
34 if l0 in '~/.':
35 return self._prefilter("!%s"%line,continuation)
36 elif l0=='$':
37 lrest = line[1:]
38 if lrest.startswith('$'):
39 # $$var=cmd <=> %sc -l var=cmd
40 return self._prefilter("%ssc -l %s" % (self.ESC_MAGIC,lrest[1:]),
41 continuation)
42 else:
43 # $var=cmd <=> %sc var=cmd
44 return self._prefilter("%ssc %s" % (self.ESC_MAGIC,lrest),
45 continuation)
46 else:
47 return self._prefilter(line,continuation)
48 else:
49 return self._prefilter(line,continuation)
50
51 # Rebind this to be the new IPython prefilter:
52 from IPython.iplib import InteractiveShell
53 InteractiveShell.prefilter = prefilter_shell
54 # Clean up the namespace.
55 del InteractiveShell,prefilter_shell
56
57 # Provide pysh and further shell-oriented services
58 import os,sys,shutil
59 from IPython.genutils import system,shell,getoutput,getoutputerror
60
61 # Short aliases for getting shell output as a string and a list
62 sout = getoutput
63 lout = lambda cmd: getoutput(cmd,split=1)
64
65 # Empty function, meant as a docstring holder so help(pysh) works.
66 def pysh():
67 """Pysh is a set of modules and extensions to IPython which make shell-like
68 usage with Python syntax more convenient. Keep in mind that pysh is NOT a
69 full-blown shell, so don't try to make it your /etc/passwd entry!
70
71 In particular, it has no job control, so if you type Ctrl-Z (under Unix),
72 you'll suspend pysh itself, not the process you just started.
73
74 Since pysh is really nothing but a customized IPython, you should
75 familiarize yourself with IPython's features. This brief help mainly
76 documents areas in which pysh differs from the normal IPython.
77
78 ALIASES
79 -------
80 All of your $PATH has been loaded as IPython aliases, so you should be
81 able to type any normal system command and have it executed. See %alias?
82 and %unalias? for details on the alias facilities.
83
84 SPECIAL SYNTAX
85 --------------
86 Any lines which begin with '~', '/' and '.' will be executed as shell
87 commands instead of as Python code. The special escapes below are also
88 recognized. !cmd is valid in single or multi-line input, all others are
89 only valid in single-line input:
90
91 !cmd - pass 'cmd' directly to the shell
92 !!cmd - execute 'cmd' and return output as a list (split on '\\n')
93 $var=cmd - capture output of cmd into var, as a string
94 $$var=cmd - capture output of cmd into var, as a list (split on '\\n')
95
96 The $/$$ syntaxes make Python variables from system output, which you can
97 later use for further scripting. The converse is also possible: when
98 executing an alias or calling to the system via !/!!, you can expand any
99 python variable or expression by prepending it with $. Full details of
100 the allowed syntax can be found in Python's PEP 215.
101
102 A few brief examples will illustrate these:
103
104 fperez[~/test]|3> !ls *s.py
105 scopes.py strings.py
106
107 ls is an internal alias, so there's no need to use !:
108 fperez[~/test]|4> ls *s.py
109 scopes.py* strings.py
110
111 !!ls will return the output into a Python variable:
112 fperez[~/test]|5> !!ls *s.py
113 <5> ['scopes.py', 'strings.py']
114 fperez[~/test]|6> print _5
115 ['scopes.py', 'strings.py']
116
117 $ and $$ allow direct capture to named variables:
118 fperez[~/test]|7> $astr = ls *s.py
119 fperez[~/test]|8> astr
120 <8> 'scopes.py\\nstrings.py'
121
122 fperez[~/test]|9> $$alist = ls *s.py
123 fperez[~/test]|10> alist
124 <10> ['scopes.py', 'strings.py']
125
126 alist is now a normal python list you can loop over. Using $ will expand
127 back the python values when alias calls are made:
128 fperez[~/test]|11> for f in alist:
129 |..> print 'file',f,
130 |..> wc -l $f
131 |..>
132 file scopes.py 13 scopes.py
133 file strings.py 4 strings.py
134
135 Note that you may need to protect your variables with braces if you want
136 to append strings to their names. To copy all files in alist to .bak
137 extensions, you must use:
138 fperez[~/test]|12> for f in alist:
139 |..> cp $f ${f}.bak
140
141 If you try using $f.bak, you'll get an AttributeError exception saying
142 that your string object doesn't have a .bak attribute. This is because
143 the $ expansion mechanism allows you to expand full Python expressions:
144 fperez[~/test]|13> echo "sys.platform is: $sys.platform"
145 sys.platform is: linux2
146
147 IPython's input history handling is still active, which allows you to
148 rerun a single block of multi-line input by simply using exec:
149 fperez[~/test]|14> $$alist = ls *.eps
150 fperez[~/test]|15> exec _i11
151 file image2.eps 921 image2.eps
152 file image.eps 921 image.eps
153
154 While these are new special-case syntaxes, they are designed to allow very
155 efficient use of the shell with minimal typing. At an interactive shell
156 prompt, conciseness of expression wins over readability.
157
158 USEFUL FUNCTIONS AND MODULES
159 ----------------------------
160 The os, sys and shutil modules from the Python standard library are
161 automatically loaded. Some additional functions, useful for shell usage,
162 are listed below. You can request more help about them with '?'.
163
164 shell - execute a command in the underlying system shell
165 system - like shell(), but return the exit status of the command
166 sout - capture the output of a command as a string
167 lout - capture the output of a command as a list (split on '\\n')
168 getoutputerror - capture (output,error) of a shell command
169
170 sout/lout are the functional equivalents of $/$$. They are provided to
171 allow you to capture system output in the middle of true python code,
172 function definitions, etc (where $ and $$ are invalid).
173
174 DIRECTORY MANAGEMENT
175 --------------------
176 Since each command passed by pysh to the underlying system is executed in
177 a subshell which exits immediately, you can NOT use !cd to navigate the
178 filesystem.
179
180 Pysh provides its own builtin '%cd' magic command to move in the
181 filesystem (the % is not required with automagic on). It also maintains a
182 list of visited directories (use %dhist to see it) and allows direct
183 switching to any of them. Type 'cd?' for more details.
184
185 %pushd, %popd and %dirs are provided for directory stack handling.
186
187 PROMPT CUSTOMIZATION
188 --------------------
189
190 The supplied ipythonrc-pysh profile comes with an example of a very
191 colored and detailed prompt, mainly to serve as an illustration. The
192 valid escape sequences, besides color names, are:
193
194 \\# - Prompt number.
195 \\D - Dots, as many as there are digits in \\# (so they align).
196 \\w - Current working directory (cwd).
197 \\W - Basename of current working directory.
198 \\XN - Where N=0..5. N terms of the cwd, with $HOME written as ~.
199 \\YN - Where N=0..5. Like XN, but if ~ is term N+1 it's also shown.
200 \\u - Username.
201 \\H - Full hostname.
202 \\h - Hostname up to first '.'
203 \\$ - Root symbol ($ or #).
204 \\t - Current time, in H:M:S format.
205 \\v - IPython release version.
206 \\n - Newline.
207 \\r - Carriage return.
208 \\\\ - An explicitly escaped '\\'.
209
210 You can configure your prompt colors using any ANSI color escape. Each
211 color escape sets the color for any subsequent text, until another escape
212 comes in and changes things. The valid color escapes are:
213
214 \\C_Black
215 \\C_Blue
216 \\C_Brown
217 \\C_Cyan
218 \\C_DarkGray
219 \\C_Green
220 \\C_LightBlue
221 \\C_LightCyan
222 \\C_LightGray
223 \\C_LightGreen
224 \\C_LightPurple
225 \\C_LightRed
226 \\C_Purple
227 \\C_Red
228 \\C_White
229 \\C_Yellow
230 \\C_Normal - Stop coloring, defaults to your terminal settings.
231 """
232 pass
233
234 # Configure a few things. Much of this is fairly hackish, since IPython
235 # doesn't really expose a clean API for it. Be careful if you start making
236 # many modifications here.
237
238
239 # Set the 'cd' command to quiet mode, a more shell-like behavior
240 __IPYTHON__.default_option('cd','-q')
241
242 # This is redundant, ipy_user_conf.py will determine this
243 # Load all of $PATH as aliases
244 __IPYTHON__.magic_rehashx()
245
246 # Remove %sc,%sx if present as aliases
247 __IPYTHON__.magic_unalias('sc')
248 __IPYTHON__.magic_unalias('sx')
249
250 # We need different criteria for line-splitting, so that aliases such as
251 # 'gnome-terminal' are interpreted as a single alias instead of variable
252 # 'gnome' minus variable 'terminal'.
253 import re
254 __IPYTHON__.line_split = re.compile(r'^([\s*,;/])'
255 r'([\?\w\.\-\+]+\w*\s*)'
256 r'(\(?.*$)')
257
258 # Namespace cleanup
259 del re
@@ -0,0 +1,122
1 # -*- coding: utf-8 -*-
2 """Modified input prompt for entering text with >>> or ... at the start.
3
4 We define a special input line filter to allow typing lines which begin with
5 '>>> ' or '... '. These two strings, if present at the start of the input
6 line, are stripped. This allows for direct pasting of code from examples such
7 as those available in the standard Python tutorial.
8
9 Normally pasting such code is one chunk is impossible because of the
10 extraneous >>> and ..., requiring one to do a line by line paste with careful
11 removal of those characters. This module allows pasting that kind of
12 multi-line examples in one pass.
13
14 Here is an 'screenshot' of a section of the tutorial pasted into IPython with
15 this feature enabled:
16
17 In [1]: >>> def fib2(n): # return Fibonacci series up to n
18 ...: ... '''Return a list containing the Fibonacci series up to n.'''
19 ...: ... result = []
20 ...: ... a, b = 0, 1
21 ...: ... while b < n:
22 ...: ... result.append(b) # see below
23 ...: ... a, b = b, a+b
24 ...: ... return result
25 ...:
26
27 In [2]: fib2(10)
28 Out[2]: [1, 1, 2, 3, 5, 8]
29
30 The >>> and ... are stripped from the input so that the python interpreter
31 only sees the real part of the code.
32
33 All other input is processed normally.
34
35 Notes
36 =====
37
38 * You can even paste code that has extra initial spaces, such as is common in
39 doctests:
40
41 In [3]: >>> a = ['Mary', 'had', 'a', 'little', 'lamb']
42
43 In [4]: >>> for i in range(len(a)):
44 ...: ... print i, a[i]
45 ...: ...
46 0 Mary
47 1 had
48 2 a
49 3 little
50 4 lamb
51 """
52
53 #*****************************************************************************
54 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
55 #
56 # Distributed under the terms of the BSD License. The full license is in
57 # the file COPYING, distributed as part of this software.
58 #*****************************************************************************
59
60 from IPython import Release
61 __author__ = '%s <%s>' % Release.authors['Fernando']
62 __license__ = Release.license
63
64 # This file is an example of how to modify IPython's line-processing behavior
65 # without touching the internal code. We'll define an alternate pre-processing
66 # stage which allows a special form of input (which is invalid Python syntax)
67 # for certain quantities, rewrites a line of proper Python in those cases, and
68 # then passes it off to IPython's normal processor for further work.
69
70 # With this kind of customization, IPython can be adapted for many
71 # special-purpose scenarios providing alternate input syntaxes.
72
73 # This file can be imported like a regular module.
74
75 # IPython has a prefilter() function that analyzes each input line. We redefine
76 # it here to first pre-process certain forms of input
77
78 # The prototype of any alternate prefilter must be like this one (the name
79 # doesn't matter):
80 # - line is a string containing the user input line.
81 # - continuation is a parameter which tells us if we are processing a first
82 # line of user input or the second or higher of a multi-line statement.
83
84 import re
85
86 from IPython.iplib import InteractiveShell
87
88 PROMPT_RE = re.compile(r'(^[ \t]*>>> |^[ \t]*\.\.\. )')
89
90 def prefilter_paste(self,line,continuation):
91 """Alternate prefilter for input of pasted code from an interpreter.
92 """
93 if not line:
94 return ''
95 m = PROMPT_RE.match(line)
96 if m:
97 # In the end, always call the default IPython _prefilter() function.
98 # Note that self must be passed explicitly, b/c we're calling the
99 # unbound class method (since this method will overwrite the instance
100 # prefilter())
101 return self._prefilter(line[len(m.group(0)):],continuation)
102 elif line.strip() == '...':
103 return self._prefilter('',continuation)
104 elif line.isspace():
105 # This allows us to recognize multiple input prompts separated by blank
106 # lines and pasted in a single chunk, very common when pasting doctests
107 # or long tutorial passages.
108 return ''
109 else:
110 return self._prefilter(line,continuation)
111
112 def activate_prefilter():
113 """Rebind the input-pasting filter to be the new IPython prefilter"""
114 InteractiveShell.prefilter = prefilter_paste
115
116 def deactivate_prefilter():
117 """Reset the filter."""
118 InteractiveShell.prefilter = InteractiveShell._prefilter
119
120 # Just a heads up at the console
121 activate_prefilter()
122 print '*** Pasting of code with ">>>" or "..." has been enabled.'
@@ -0,0 +1,83
1 # -*- coding: utf-8 -*-
2 """Modified input prompt for entering quantities with units.
3
4 Modify the behavior of the interactive interpreter to allow direct input of
5 quantities with units without having to make a function call.
6
7 Now the following forms are accepted:
8
9 x = 4 m
10 y = -.45e3 m/s
11 g = 9.8 m/s**2
12 a = 2.3 m/s^2 # ^ -> ** automatically
13
14 All other input is processed normally.
15 """
16 #*****************************************************************************
17 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
18 #
19 # Distributed under the terms of the BSD License. The full license is in
20 # the file COPYING, distributed as part of this software.
21 #*****************************************************************************
22
23 from IPython import Release
24 __author__ = '%s <%s>' % Release.authors['Fernando']
25 __license__ = Release.license
26
27 # This file is an example of how to modify IPython's line-processing behavior
28 # without touching the internal code. We'll define an alternate pre-processing
29 # stage which allows a special form of input (which is invalid Python syntax)
30 # for certain quantities, rewrites a line of proper Python in those cases, and
31 # then passes it off to IPython's normal processor for further work.
32
33 # With this kind of customization, IPython can be adapted for many
34 # special-purpose scenarios providing alternate input syntaxes.
35
36 # This file can be imported like a regular module.
37
38 # IPython has a prefilter() function that analyzes each input line. We redefine
39 # it here to first pre-process certain forms of input
40
41 # The prototype of any alternate prefilter must be like this one (the name
42 # doesn't matter):
43 # - line is a string containing the user input line.
44 # - continuation is a parameter which tells us if we are processing a first line of
45 # user input or the second or higher of a multi-line statement.
46
47 def prefilter_PQ(self,line,continuation):
48 """Alternate prefilter for input of PhysicalQuantityInteractive objects.
49
50 This assumes that the function PhysicalQuantityInteractive() has been
51 imported."""
52
53 from re import match
54 from IPython.iplib import InteractiveShell
55
56 # This regexp is what does the real work
57 unit_split = match(r'\s*(\w+)\s*=\s*(-?\d*\.?\d*[eE]?-?\d*)\s+([a-zA-Z].*)',
58 line)
59
60 # If special input was ecnountered, process it:
61 if unit_split:
62 var,val,units = unit_split.groups()
63 if var and val and units:
64 units = units.replace('^','**')
65 # Now a valid line needs to be constructed for IPython to process:
66 line = var +" = PhysicalQuantityInteractive(" + val + ", '" + \
67 units + "')"
68 #print 'New line:',line # dbg
69
70 # In the end, always call the default IPython _prefilter() function. Note
71 # that self must be passed explicitly, b/c we're calling the unbound class
72 # method (since this method will overwrite the instance prefilter())
73 return InteractiveShell._prefilter(self,line,continuation)
74
75 # Rebind this to be the new IPython prefilter:
76 from IPython.iplib import InteractiveShell
77 InteractiveShell.prefilter = prefilter_PQ
78
79 # Clean up the namespace.
80 del InteractiveShell,prefilter_PQ
81
82 # Just a heads up at the console
83 print '*** Simplified input for physical quantities enabled.'
@@ -0,0 +1,88
1 # -*- coding: utf-8 -*-
2 """Modify the PhysicalQuantities class for more convenient interactive use.
3
4 Also redefine some math functions to operate on PhysQties with no need for
5 special method syntax. This just means moving them out to the global
6 namespace.
7
8 This module should always be loaded *after* math or Numeric, so it can
9 overwrite math functions with the versions that handle units."""
10
11 #*****************************************************************************
12 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
13 #
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
16 #*****************************************************************************
17
18 from IPython import Release
19 __author__ = '%s <%s>' % Release.authors['Fernando']
20 __license__ = Release.license
21
22 from Scientific.Physics.PhysicalQuantities import PhysicalQuantity
23
24 # This code can be set up to work with Numeric or with math for providing the
25 # mathematical functions. Uncomment the one you prefer to use below.
26
27 # If you use math, sin(x) won't work for x an array, only float or PhysQty
28 import math
29
30 # If you use Numeric, sin(x) works for x a float, PhysQty an array.
31 #import Numeric as math
32
33 class PhysicalQuantityFunction:
34 """Generic function wrapper for PhysicalQuantity instances.
35
36 Calls functions from either the math library or the instance's methods as
37 required. Allows using sin(theta) or sqrt(v**2) syntax irrespective of
38 whether theta is a pure number or a PhysicalQuantity.
39
40 This is *slow*. It's meant for convenient interactive use, not for
41 speed."""
42
43 def __init__(self,name):
44 self.name = name
45
46 def __call__(self,x):
47 if isinstance(x,PhysicalQuantity):
48 return PhysicalQuantity.__dict__[self.name](x)
49 else:
50 return math.__dict__[self.name](x)
51
52 class PhysicalQuantityInteractive(PhysicalQuantity):
53 """Physical quantity with units - modified for Interactive use.
54
55 Basically, the __str__ and __repr__ methods have been swapped for more
56 convenient interactive use. Powers are shown as ^ instead of ** and only 4
57 significant figures are shown.
58
59 Also adds the following aliases for commonly used methods:
60 b = PhysicalQuantity.inBaseUnits
61 u = PhysicalQuantity.inUnitsOf
62
63 These are useful when doing a lot of interactive calculations.
64 """
65
66 # shorthands for the most useful unit conversions
67 b = PhysicalQuantity.inBaseUnits # so you can just type x.b to get base units
68 u = PhysicalQuantity.inUnitsOf
69
70 # This can be done, but it can get dangerous when coupled with IPython's
71 # auto-calling. Everything ends up shown in baseunits and things like x*2
72 # get automatically converted to k(*2), which doesn't work.
73 # Probably not a good idea in general...
74 #__call__ = b
75
76 def __str__(self):
77 return PhysicalQuantity.__repr__(self)
78
79 def __repr__(self):
80 value = '%.4G' % self.value
81 units = self.unit.name().replace('**','^')
82 return value + ' ' + units
83
84 # implement the methods defined in PhysicalQuantity as PhysicalQuantityFunctions
85 sin = PhysicalQuantityFunction('sin')
86 cos = PhysicalQuantityFunction('cos')
87 tan = PhysicalQuantityFunction('tan')
88 sqrt = PhysicalQuantityFunction('sqrt')
@@ -0,0 +1,13
1 # -*- coding: utf-8 -*-
2 """This directory is meant for special-purpose extensions to IPython.
3
4 This can include things which alter the syntax processing stage (see
5 PhysicalQ_Input for an example of how to do this).
6
7 Any file located here can be called with an 'execfile =' option as
8
9 execfile = Extensions/filename.py
10
11 since the IPython directory itself is already part of the search path for
12 files listed as 'execfile ='.
13 """
@@ -0,0 +1,398
1 """
2 ``astyle`` provides classes for adding style (foreground and background color;
3 bold; blink; etc.) to terminal and curses output.
4 """
5
6
7 import sys, os
8
9 try:
10 import curses
11 except ImportError:
12 curses = None
13
14
15 COLOR_BLACK = 0
16 COLOR_RED = 1
17 COLOR_GREEN = 2
18 COLOR_YELLOW = 3
19 COLOR_BLUE = 4
20 COLOR_MAGENTA = 5
21 COLOR_CYAN = 6
22 COLOR_WHITE = 7
23
24 A_BLINK = 1<<0 # Blinking text
25 A_BOLD = 1<<1 # Extra bright or bold text
26 A_DIM = 1<<2 # Half bright text
27 A_REVERSE = 1<<3 # Reverse-video text
28 A_STANDOUT = 1<<4 # The best highlighting mode available
29 A_UNDERLINE = 1<<5 # Underlined text
30
31
32 class Style(object):
33 """
34 Store foreground color, background color and attribute (bold, underlined
35 etc.).
36 """
37 __slots__ = ("fg", "bg", "attrs")
38
39 COLORNAMES = {
40 "black": COLOR_BLACK,
41 "red": COLOR_RED,
42 "green": COLOR_GREEN,
43 "yellow": COLOR_YELLOW,
44 "blue": COLOR_BLUE,
45 "magenta": COLOR_MAGENTA,
46 "cyan": COLOR_CYAN,
47 "white": COLOR_WHITE,
48 }
49 ATTRNAMES = {
50 "blink": A_BLINK,
51 "bold": A_BOLD,
52 "dim": A_DIM,
53 "reverse": A_REVERSE,
54 "standout": A_STANDOUT,
55 "underline": A_UNDERLINE,
56 }
57
58 def __init__(self, fg, bg, attrs=0):
59 """
60 Create a ``Style`` object with ``fg`` as the foreground color,
61 ``bg`` as the background color and ``attrs`` as the attributes.
62
63 Examples:
64
65 >>> Style(COLOR_RED, COLOR_BLACK)
66 >>> Style(COLOR_YELLOW, COLOR_BLUE, A_BOLD|A_UNDERLINE)
67 """
68 self.fg = fg
69 self.bg = bg
70 self.attrs = attrs
71
72 def __call__(self, *args):
73 text = Text()
74 for arg in args:
75 if isinstance(arg, Text):
76 text.extend(arg)
77 else:
78 text.append((self, arg))
79 return text
80
81 def __eq__(self, other):
82 return self.fg == other.fg and self.bg == other.bg and self.attrs == other.attrs
83
84 def __neq__(self, other):
85 return self.fg != other.fg or self.bg != other.bg or self.attrs != other.attrs
86
87 def __repr__(self):
88 color2name = ("black", "red", "green", "yellow", "blue", "magenta", "cyan", "white")
89 attrs2name = ("blink", "bold", "dim", "reverse", "standout", "underline")
90
91 return "<%s fg=%s bg=%s attrs=%s>" % (
92 self.__class__.__name__, color2name[self.fg], color2name[self.bg],
93 "|".join([attrs2name[b] for b in xrange(6) if self.attrs&(1<<b)]) or 0)
94
95 def fromstr(cls, value):
96 """
97 Create a ``Style`` object from a string. The format looks like this:
98 ``"red:black:bold|blink"``.
99 """
100 # defaults
101 fg = COLOR_WHITE
102 bg = COLOR_BLACK
103 attrs = 0
104
105 parts = value.split(":")
106 if len(parts) > 0:
107 fg = cls.COLORNAMES[parts[0].lower()]
108 if len(parts) > 1:
109 bg = cls.COLORNAMES[parts[1].lower()]
110 if len(parts) > 2:
111 for strattr in parts[2].split("|"):
112 attrs |= cls.ATTRNAMES[strattr.lower()]
113 return cls(fg, bg, attrs)
114 fromstr = classmethod(fromstr)
115
116 def fromenv(cls, name, default):
117 """
118 Create a ``Style`` from an environment variable named ``name``
119 (using ``default`` if the environment variable doesn't exist).
120 """
121 return cls.fromstr(os.environ.get(name, default))
122 fromenv = classmethod(fromenv)
123
124
125 def switchstyle(s1, s2):
126 """
127 Return the ANSI escape sequence needed to switch from style ``s1`` to
128 style ``s2``.
129 """
130 attrmask = (A_BLINK|A_BOLD|A_UNDERLINE|A_REVERSE)
131 a1 = s1.attrs & attrmask
132 a2 = s2.attrs & attrmask
133
134 args = []
135 if s1 != s2:
136 # do we have to get rid of the bold/underline/blink bit?
137 # (can only be done by a reset)
138 # use reset when our target color is the default color
139 # (this is shorter than 37;40)
140 if (a1 & ~a2 or s2==style_default):
141 args.append("0")
142 s1 = style_default
143 a1 = 0
144
145 # now we know that old and new color have the same boldness,
146 # or the new color is bold and the old isn't,
147 # i.e. we only might have to switch bold on, not off
148 if not (a1 & A_BOLD) and (a2 & A_BOLD):
149 args.append("1")
150
151 # Fix underline
152 if not (a1 & A_UNDERLINE) and (a2 & A_UNDERLINE):
153 args.append("4")
154
155 # Fix blink
156 if not (a1 & A_BLINK) and (a2 & A_BLINK):
157 args.append("5")
158
159 # Fix reverse
160 if not (a1 & A_REVERSE) and (a2 & A_REVERSE):
161 args.append("7")
162
163 # Fix foreground color
164 if s1.fg != s2.fg:
165 args.append("3%d" % s2.fg)
166
167 # Finally fix the background color
168 if s1.bg != s2.bg:
169 args.append("4%d" % s2.bg)
170
171 if args:
172 return "\033[%sm" % ";".join(args)
173 return ""
174
175
176 class Text(list):
177 """
178 A colored string. A ``Text`` object is a sequence, the sequence
179 items will be ``(style, string)`` tuples.
180 """
181
182 def __init__(self, *args):
183 list.__init__(self)
184 self.append(*args)
185
186 def __repr__(self):
187 return "%s.%s(%s)" % (
188 self.__class__.__module__, self.__class__.__name__,
189 list.__repr__(self)[1:-1])
190
191 def append(self, *args):
192 for arg in args:
193 if isinstance(arg, Text):
194 self.extend(arg)
195 elif isinstance(arg, tuple): # must be (style, string)
196 list.append(self, arg)
197 elif isinstance(arg, unicode):
198 list.append(self, (style_default, arg))
199 else:
200 list.append(self, (style_default, str(arg)))
201
202 def insert(self, index, *args):
203 self[index:index] = Text(*args)
204
205 def __add__(self, other):
206 new = Text()
207 new.append(self)
208 new.append(other)
209 return new
210
211 def __iadd__(self, other):
212 self.append(other)
213 return self
214
215 def format(self, styled=True):
216 """
217 This generator yields the strings that will make up the final
218 colorized string.
219 """
220 if styled:
221 oldstyle = style_default
222 for (style, string) in self:
223 if not isinstance(style, (int, long)):
224 switch = switchstyle(oldstyle, style)
225 if switch:
226 yield switch
227 if string:
228 yield string
229 oldstyle = style
230 switch = switchstyle(oldstyle, style_default)
231 if switch:
232 yield switch
233 else:
234 for (style, string) in self:
235 if not isinstance(style, (int, long)):
236 yield string
237
238 def string(self, styled=True):
239 """
240 Return the resulting string (with escape sequences, if ``styled``
241 is true).
242 """
243 return "".join(self.format(styled))
244
245 def __str__(self):
246 """
247 Return ``self`` as a string (without ANSI escape sequences).
248 """
249 return self.string(False)
250
251 def write(self, stream, styled=True):
252 """
253 Write ``self`` to the output stream ``stream`` (with escape sequences,
254 if ``styled`` is true).
255 """
256 for part in self.format(styled):
257 stream.write(part)
258
259
260 try:
261 import ipipe
262 except ImportError:
263 pass
264 else:
265 def xrepr_astyle_text(self, mode="default"):
266 yield (-1, True)
267 for info in self:
268 yield info
269 ipipe.xrepr.when_type(Text)(xrepr_astyle_text)
270
271
272 def streamstyle(stream, styled=None):
273 """
274 If ``styled`` is ``None``, return whether ``stream`` refers to a terminal.
275 If this can't be determined (either because ``stream`` doesn't refer to a
276 real OS file, or because you're on Windows) return ``False``. If ``styled``
277 is not ``None`` ``styled`` will be returned unchanged.
278 """
279 if styled is None:
280 try:
281 styled = os.isatty(stream.fileno())
282 except (KeyboardInterrupt, SystemExit):
283 raise
284 except Exception:
285 styled = False
286 return styled
287
288
289 def write(stream, styled, *texts):
290 """
291 Write ``texts`` to ``stream``.
292 """
293 text = Text(*texts)
294 text.write(stream, streamstyle(stream, styled))
295
296
297 def writeln(stream, styled, *texts):
298 """
299 Write ``texts`` to ``stream`` and finish with a line feed.
300 """
301 write(stream, styled, *texts)
302 stream.write("\n")
303
304
305 class Stream(object):
306 """
307 Stream wrapper that adds color output.
308 """
309 def __init__(self, stream, styled=None):
310 self.stream = stream
311 self.styled = streamstyle(stream, styled)
312
313 def write(self, *texts):
314 write(self.stream, self.styled, *texts)
315
316 def writeln(self, *texts):
317 writeln(self.stream, self.styled, *texts)
318
319 def __getattr__(self, name):
320 return getattr(self.stream, name)
321
322
323 class stdout(object):
324 """
325 Stream wrapper for ``sys.stdout`` that adds color output.
326 """
327 def write(self, *texts):
328 write(sys.stdout, None, *texts)
329
330 def writeln(self, *texts):
331 writeln(sys.stdout, None, *texts)
332
333 def __getattr__(self, name):
334 return getattr(sys.stdout, name)
335 stdout = stdout()
336
337
338 class stderr(object):
339 """
340 Stream wrapper for ``sys.stderr`` that adds color output.
341 """
342 def write(self, *texts):
343 write(sys.stderr, None, *texts)
344
345 def writeln(self, *texts):
346 writeln(sys.stderr, None, *texts)
347
348 def __getattr__(self, name):
349 return getattr(sys.stdout, name)
350 stderr = stderr()
351
352
353 if curses is not None:
354 # This is probably just range(8)
355 COLOR2CURSES = [
356 COLOR_BLACK,
357 COLOR_RED,
358 COLOR_GREEN,
359 COLOR_YELLOW,
360 COLOR_BLUE,
361 COLOR_MAGENTA,
362 COLOR_CYAN,
363 COLOR_WHITE,
364 ]
365
366 A2CURSES = {
367 A_BLINK: curses.A_BLINK,
368 A_BOLD: curses.A_BOLD,
369 A_DIM: curses.A_DIM,
370 A_REVERSE: curses.A_REVERSE,
371 A_STANDOUT: curses.A_STANDOUT,
372 A_UNDERLINE: curses.A_UNDERLINE,
373 }
374
375
376 # default style
377 style_default = Style.fromstr("white:black")
378
379 # Styles for datatypes
380 style_type_none = Style.fromstr("magenta:black")
381 style_type_bool = Style.fromstr("magenta:black")
382 style_type_number = Style.fromstr("yellow:black")
383 style_type_datetime = Style.fromstr("magenta:black")
384 style_type_type = Style.fromstr("cyan:black")
385
386 # Style for URLs and file/directory names
387 style_url = Style.fromstr("green:black")
388 style_dir = Style.fromstr("cyan:black")
389 style_file = Style.fromstr("green:black")
390
391 # Style for ellipsis (when an output has been shortened
392 style_ellisis = Style.fromstr("red:black")
393
394 # Style for displaying exceptions
395 style_error = Style.fromstr("red:black")
396
397 # Style for displaying non-existing attributes
398 style_nodata = Style.fromstr("red:black")
@@ -0,0 +1,66
1 # -*- coding: utf-8 -*-
2 """ IPython extension: add %clear magic """
3
4 import IPython.ipapi
5 import gc
6 ip = IPython.ipapi.get()
7
8
9 def clear_f(self,arg):
10 """ Clear various data (e.g. stored history data)
11
12 %clear out - clear output history
13 %clear in - clear input history
14 %clear shadow_compress - Compresses shadow history (to speed up ipython)
15 %clear shadow_nuke - permanently erase all entries in shadow history
16 %clear dhist - clear dir history
17 """
18
19 api = self.getapi()
20 for target in arg.split():
21 if target == 'out':
22 print "Flushing output cache (%d entries)" % len(api.user_ns['_oh'])
23 self.outputcache.flush()
24 elif target == 'in':
25 print "Flushing input history"
26 from IPython import iplib
27 pc = self.outputcache.prompt_count + 1
28 for n in range(1, pc):
29 key = '_i'+`n`
30 try:
31 del self.user_ns[key]
32 except: pass
33 # must be done in-place
34 self.input_hist[:] = ['\n'] * pc
35 self.input_hist_raw[:] = ['\n'] * pc
36 elif target == 'array':
37 try:
38 pylab=ip.IP.pylab
39 for x in self.user_ns.keys():
40 if isinstance(self.user_ns[x],pylab.arraytype):
41 del self.user_ns[x]
42 except AttributeError:
43 print "Clear array only available in -pylab mode"
44 gc.collect()
45
46 elif target == 'shadow_compress':
47 print "Compressing shadow history"
48 api.db.hcompress('shadowhist')
49
50 elif target == 'shadow_nuke':
51 print "Erased all keys from shadow history "
52 for k in ip.db.keys('shadowhist/*'):
53 del ip.db[k]
54 elif target == 'dhist':
55 print "Clearing directory history"
56 del ip.user_ns['_dh'][:]
57
58
59 ip.expose_magic("clear",clear_f)
60 import ipy_completers
61 ipy_completers.quick_completer(
62 '%clear','in out shadow_nuke shadow_compress dhist')
63
64
65
66
@@ -0,0 +1,90
1 # -*- coding: utf-8 -*-
2 """ %env magic command for storing environment variables persistently
3 """
4
5 import IPython.ipapi
6 ip = IPython.ipapi.get()
7
8 import os,sys
9
10 def restore_env(self):
11 ip = self.getapi()
12 env = ip.db.get('stored_env', {'set' : {}, 'add' : [], 'pre' : []})
13 for k,v in env['set'].items():
14 os.environ[k] = v
15 for k,v in env['add']:
16 os.environ[k] = os.environ.get(k,"") + v
17 for k,v in env['pre']:
18 os.environ[k] = v + os.environ.get(k,"")
19 raise IPython.ipapi.TryNext
20
21 ip.set_hook('late_startup_hook', restore_env)
22
23 def persist_env(self, parameter_s=''):
24 """ Store environment variables persistently
25
26 IPython remembers the values across sessions, which is handy to avoid
27 editing startup files.
28
29 %env - Show all environment variables
30 %env VISUAL=jed - set VISUAL to jed
31 %env PATH+=;/foo - append ;foo to PATH
32 %env PATH+=;/bar - also append ;bar to PATH
33 %env PATH-=/wbin; - prepend /wbin; to PATH
34 %env -d VISUAL - forget VISUAL persistent val
35 %env -p - print all persistent env modifications
36 """
37
38 if not parameter_s.strip():
39 return os.environ.data
40
41 ip = self.getapi()
42 db = ip.db
43 env = ip.db.get('stored_env', {'set' : {}, 'add' : [], 'pre' : []})
44
45 if parameter_s.startswith('-p'):
46 return env
47
48 elif parameter_s.startswith('-d'):
49 parts = (parameter_s.split()[1], '<del>')
50
51 else:
52 parts = parameter_s.strip().split('=')
53
54 if len(parts) == 2:
55 k,v = [p.strip() for p in parts]
56
57 if v == '<del>':
58 if k in env['set']:
59 del env['set'][k]
60 env['add'] = [el for el in env['add'] if el[0] != k]
61 env['pre'] = [el for el in env['pre'] if el[0] != k]
62
63 print "Forgot '%s' (for next session)" % k
64
65 elif k.endswith('+'):
66 k = k[:-1]
67 env['add'].append((k,v))
68 os.environ[k] += v
69 print k,"after append =",os.environ[k]
70 elif k.endswith('-'):
71 k = k[:-1]
72 env['pre'].append((k,v))
73 os.environ[k] = v + os.environ.get(k,"")
74 print k,"after prepend =",os.environ[k]
75
76
77 else:
78 env['set'][k] = v
79 print "Setting",k,"to",v
80 os.environ[k] = v
81
82 db['stored_env'] = env
83
84 def env_completer(self,event):
85 """ Custom completer that lists all env vars """
86 return os.environ.keys()
87
88 ip.expose_magic('env', persist_env)
89 ip.set_hook('complete_command',env_completer, str_key = '%env')
90
@@ -0,0 +1,66
1 # -*- coding: utf-8 -*-
2 """ IPython extension: new prefilters for output grabbing
3
4 Provides
5
6 var = %magic blah blah
7
8 var = !ls
9
10 $Id: genutils.py 1077 2006-01-24 18:15:27Z vivainio $
11
12 """
13
14 import IPython.ipapi
15 from IPython.genutils import *
16
17 ip = IPython.ipapi.get()
18
19 import re
20
21 def hnd_magic(line,mo):
22 """ Handle a = %mymagic blah blah """
23 #cmd = genutils.make_quoted_expr(mo.group('syscmd'))
24 #mag = 'ipmagic
25 #return "%s = %s"
26 var = mo.group('varname')
27 cmd = mo.group('cmd')
28 expr = make_quoted_expr(cmd)
29 return itpl('$var = _ip.magic($expr)')
30
31 def hnd_syscmd(line,mo):
32 """ Handle a = !ls """
33 #cmd = genutils.make_quoted_expr(mo.group('syscmd'))
34 #mag = 'ipmagic
35 #return "%s = %s"
36 var = mo.group('varname')
37 cmd = mo.group('cmd')
38 expr = make_quoted_expr(itpl("sc -l =$cmd"))
39 return itpl('$var = _ip.magic($expr)')
40
41 def install_re_handler(pat, hnd):
42 ip.meta.re_prefilters.append((re.compile(pat), hnd))
43
44 def init_handlers():
45
46 ip.meta.re_prefilters = []
47
48 install_re_handler('(?P<varname>[\w\.]+)\s*=\s*%(?P<cmd>.*)',
49 hnd_magic
50 )
51
52 install_re_handler('(?P<varname>[\w\.]+)\s*=\s*!(?P<cmd>.*)',
53 hnd_syscmd
54 )
55
56 init_handlers()
57
58 def regex_prefilter_f(self,line):
59 for pat, handler in ip.meta.re_prefilters:
60 mo = pat.match(line)
61 if mo:
62 return handler(line,mo)
63
64 raise IPython.ipapi.TryNext
65
66 ip.set_hook('input_prefilter', regex_prefilter_f)
This diff has been collapsed as it changes many lines, (1767 lines changed) Show them Hide them
@@ -0,0 +1,1767
1 # -*- coding: iso-8859-1 -*-
2
3 import curses, fcntl, signal, struct, tty, textwrap, inspect
4
5 from IPython import ipapi
6
7 import astyle, ipipe
8
9
10 # Python 2.3 compatibility
11 try:
12 set
13 except NameError:
14 import sets
15 set = sets.Set
16
17 # Python 2.3 compatibility
18 try:
19 sorted
20 except NameError:
21 from ipipe import sorted
22
23
24 class UnassignedKeyError(Exception):
25 """
26 Exception that is used for reporting unassigned keys.
27 """
28
29
30 class UnknownCommandError(Exception):
31 """
32 Exception that is used for reporting unknown commands (this should never
33 happen).
34 """
35
36
37 class CommandError(Exception):
38 """
39 Exception that is used for reporting that a command can't be executed.
40 """
41
42
43 class Keymap(dict):
44 """
45 Stores mapping of keys to commands.
46 """
47 def __init__(self):
48 self._keymap = {}
49
50 def __setitem__(self, key, command):
51 if isinstance(key, str):
52 for c in key:
53 dict.__setitem__(self, ord(c), command)
54 else:
55 dict.__setitem__(self, key, command)
56
57 def __getitem__(self, key):
58 if isinstance(key, str):
59 key = ord(key)
60 return dict.__getitem__(self, key)
61
62 def __detitem__(self, key):
63 if isinstance(key, str):
64 key = ord(key)
65 dict.__detitem__(self, key)
66
67 def register(self, command, *keys):
68 for key in keys:
69 self[key] = command
70
71 def get(self, key, default=None):
72 if isinstance(key, str):
73 key = ord(key)
74 return dict.get(self, key, default)
75
76 def findkey(self, command, default=ipipe.noitem):
77 for (key, commandcandidate) in self.iteritems():
78 if commandcandidate == command:
79 return key
80 if default is ipipe.noitem:
81 raise KeyError(command)
82 return default
83
84
85 class _BrowserCachedItem(object):
86 # This is used internally by ``ibrowse`` to store a item together with its
87 # marked status.
88 __slots__ = ("item", "marked")
89
90 def __init__(self, item):
91 self.item = item
92 self.marked = False
93
94
95 class _BrowserHelp(object):
96 style_header = astyle.Style.fromstr("yellow:black:bold")
97 # This is used internally by ``ibrowse`` for displaying the help screen.
98 def __init__(self, browser):
99 self.browser = browser
100
101 def __xrepr__(self, mode):
102 yield (-1, True)
103 if mode == "header" or mode == "footer":
104 yield (astyle.style_default, "ibrowse help screen")
105 else:
106 yield (astyle.style_default, repr(self))
107
108 def __iter__(self):
109 # Get reverse key mapping
110 allkeys = {}
111 for (key, cmd) in self.browser.keymap.iteritems():
112 allkeys.setdefault(cmd, []).append(key)
113
114 fields = ("key", "description")
115
116 commands = []
117 for name in dir(self.browser):
118 if name.startswith("cmd_"):
119 command = getattr(self.browser, name)
120 commands.append((inspect.getsourcelines(command)[-1], name[4:], command))
121 commands.sort()
122 commands = [(c[1], c[2]) for c in commands]
123 for (i, (name, command)) in enumerate(commands):
124 if i:
125 yield ipipe.Fields(fields, key="", description="")
126
127 description = command.__doc__
128 if description is None:
129 lines = []
130 else:
131 lines = [l.strip() for l in description.splitlines() if l.strip()]
132 description = "\n".join(lines)
133 lines = textwrap.wrap(description, 60)
134 keys = allkeys.get(name, [])
135
136 yield ipipe.Fields(fields, key="", description=astyle.Text((self.style_header, name)))
137 for i in xrange(max(len(keys), len(lines))):
138 try:
139 key = self.browser.keylabel(keys[i])
140 except IndexError:
141 key = ""
142 try:
143 line = lines[i]
144 except IndexError:
145 line = ""
146 yield ipipe.Fields(fields, key=key, description=line)
147
148
149 class _BrowserLevel(object):
150 # This is used internally to store the state (iterator, fetch items,
151 # position of cursor and screen, etc.) of one browser level
152 # An ``ibrowse`` object keeps multiple ``_BrowserLevel`` objects in
153 # a stack.
154 def __init__(self, browser, input, mainsizey, *attrs):
155 self.browser = browser
156 self.input = input
157 self.header = [x for x in ipipe.xrepr(input, "header") if not isinstance(x[0], int)]
158 # iterator for the input
159 self.iterator = ipipe.xiter(input)
160
161 # is the iterator exhausted?
162 self.exhausted = False
163
164 # attributes to be display (autodetected if empty)
165 self.attrs = attrs
166
167 # fetched items (+ marked flag)
168 self.items = ipipe.deque()
169
170 # Number of marked objects
171 self.marked = 0
172
173 # Vertical cursor position
174 self.cury = 0
175
176 # Horizontal cursor position
177 self.curx = 0
178
179 # Index of first data column
180 self.datastartx = 0
181
182 # Index of first data line
183 self.datastarty = 0
184
185 # height of the data display area
186 self.mainsizey = mainsizey
187
188 # width of the data display area (changes when scrolling)
189 self.mainsizex = 0
190
191 # Size of row number (changes when scrolling)
192 self.numbersizex = 0
193
194 # Attributes to display (in this order)
195 self.displayattrs = []
196
197 # index and attribute under the cursor
198 self.displayattr = (None, ipipe.noitem)
199
200 # Maps attributes to column widths
201 self.colwidths = {}
202
203 # Set of hidden attributes
204 self.hiddenattrs = set()
205
206 # This takes care of all the caches etc.
207 self.moveto(0, 0, refresh=True)
208
209 def fetch(self, count):
210 # Try to fill ``self.items`` with at least ``count`` objects.
211 have = len(self.items)
212 while not self.exhausted and have < count:
213 try:
214 item = self.iterator.next()
215 except StopIteration:
216 self.exhausted = True
217 break
218 except (KeyboardInterrupt, SystemExit):
219 raise
220 except Exception, exc:
221 have += 1
222 self.items.append(_BrowserCachedItem(exc))
223 self.exhausted = True
224 break
225 else:
226 have += 1
227 self.items.append(_BrowserCachedItem(item))
228
229 def calcdisplayattrs(self):
230 # Calculate which attributes are available from the objects that are
231 # currently visible on screen (and store it in ``self.displayattrs``)
232
233 attrs = set()
234 self.displayattrs = []
235 if self.attrs:
236 # If the browser object specifies a fixed list of attributes,
237 # simply use it (removing hidden attributes).
238 for attr in self.attrs:
239 attr = ipipe.upgradexattr(attr)
240 if attr not in attrs and attr not in self.hiddenattrs:
241 self.displayattrs.append(attr)
242 attrs.add(attr)
243 else:
244 endy = min(self.datastarty+self.mainsizey, len(self.items))
245 for i in xrange(self.datastarty, endy):
246 for attr in ipipe.xattrs(self.items[i].item, "default"):
247 if attr not in attrs and attr not in self.hiddenattrs:
248 self.displayattrs.append(attr)
249 attrs.add(attr)
250
251 def getrow(self, i):
252 # Return a dictionary with the attributes for the object
253 # ``self.items[i]``. Attribute names are taken from
254 # ``self.displayattrs`` so ``calcdisplayattrs()`` must have been
255 # called before.
256 row = {}
257 item = self.items[i].item
258 for attr in self.displayattrs:
259 try:
260 value = attr.value(item)
261 except (KeyboardInterrupt, SystemExit):
262 raise
263 except Exception, exc:
264 value = exc
265 # only store attribute if it exists (or we got an exception)
266 if value is not ipipe.noitem:
267 # remember alignment, length and colored text
268 row[attr] = ipipe.xformat(value, "cell", self.browser.maxattrlength)
269 return row
270
271 def calcwidths(self):
272 # Recalculate the displayed fields and their widths.
273 # ``calcdisplayattrs()'' must have been called and the cache
274 # for attributes of the objects on screen (``self.displayrows``)
275 # must have been filled. This sets ``self.colwidths`` which maps
276 # attribute descriptors to widths.
277 self.colwidths = {}
278 for row in self.displayrows:
279 for attr in self.displayattrs:
280 try:
281 length = row[attr][1]
282 except KeyError:
283 length = 0
284 # always add attribute to colwidths, even if it doesn't exist
285 if attr not in self.colwidths:
286 self.colwidths[attr] = len(attr.name())
287 newwidth = max(self.colwidths[attr], length)
288 self.colwidths[attr] = newwidth
289
290 # How many characters do we need to paint the largest item number?
291 self.numbersizex = len(str(self.datastarty+self.mainsizey-1))
292 # How must space have we got to display data?
293 self.mainsizex = self.browser.scrsizex-self.numbersizex-3
294 # width of all columns
295 self.datasizex = sum(self.colwidths.itervalues()) + len(self.colwidths)
296
297 def calcdisplayattr(self):
298 # Find out which attribute the cursor is on and store this
299 # information in ``self.displayattr``.
300 pos = 0
301 for (i, attr) in enumerate(self.displayattrs):
302 if pos+self.colwidths[attr] >= self.curx:
303 self.displayattr = (i, attr)
304 break
305 pos += self.colwidths[attr]+1
306 else:
307 self.displayattr = (None, ipipe.noitem)
308
309 def moveto(self, x, y, refresh=False):
310 # Move the cursor to the position ``(x,y)`` (in data coordinates,
311 # not in screen coordinates). If ``refresh`` is true, all cached
312 # values will be recalculated (e.g. because the list has been
313 # resorted, so screen positions etc. are no longer valid).
314 olddatastarty = self.datastarty
315 oldx = self.curx
316 oldy = self.cury
317 x = int(x+0.5)
318 y = int(y+0.5)
319 newx = x # remember where we wanted to move
320 newy = y # remember where we wanted to move
321
322 scrollbordery = min(self.browser.scrollbordery, self.mainsizey//2)
323 scrollborderx = min(self.browser.scrollborderx, self.mainsizex//2)
324
325 # Make sure that the cursor didn't leave the main area vertically
326 if y < 0:
327 y = 0
328 # try to get enough items to fill the screen
329 self.fetch(max(y+scrollbordery+1, self.mainsizey))
330 if y >= len(self.items):
331 y = max(0, len(self.items)-1)
332
333 # Make sure that the cursor stays on screen vertically
334 if y < self.datastarty+scrollbordery:
335 self.datastarty = max(0, y-scrollbordery)
336 elif y >= self.datastarty+self.mainsizey-scrollbordery:
337 self.datastarty = max(0, min(y-self.mainsizey+scrollbordery+1,
338 len(self.items)-self.mainsizey))
339
340 if refresh: # Do we need to refresh the complete display?
341 self.calcdisplayattrs()
342 endy = min(self.datastarty+self.mainsizey, len(self.items))
343 self.displayrows = map(self.getrow, xrange(self.datastarty, endy))
344 self.calcwidths()
345 # Did we scroll vertically => update displayrows
346 # and various other attributes
347 elif self.datastarty != olddatastarty:
348 # Recalculate which attributes we have to display
349 olddisplayattrs = self.displayattrs
350 self.calcdisplayattrs()
351 # If there are new attributes, recreate the cache
352 if self.displayattrs != olddisplayattrs:
353 endy = min(self.datastarty+self.mainsizey, len(self.items))
354 self.displayrows = map(self.getrow, xrange(self.datastarty, endy))
355 elif self.datastarty<olddatastarty: # we did scroll up
356 # drop rows from the end
357 del self.displayrows[self.datastarty-olddatastarty:]
358 # fetch new items
359 for i in xrange(min(olddatastarty, self.datastarty+self.mainsizey)-1,
360 self.datastarty-1, -1):
361 try:
362 row = self.getrow(i)
363 except IndexError:
364 # we didn't have enough objects to fill the screen
365 break
366 self.displayrows.insert(0, row)
367 else: # we did scroll down
368 # drop rows from the start
369 del self.displayrows[:self.datastarty-olddatastarty]
370 # fetch new items
371 for i in xrange(max(olddatastarty+self.mainsizey, self.datastarty),
372 self.datastarty+self.mainsizey):
373 try:
374 row = self.getrow(i)
375 except IndexError:
376 # we didn't have enough objects to fill the screen
377 break
378 self.displayrows.append(row)
379 self.calcwidths()
380
381 # Make sure that the cursor didn't leave the data area horizontally
382 if x < 0:
383 x = 0
384 elif x >= self.datasizex:
385 x = max(0, self.datasizex-1)
386
387 # Make sure that the cursor stays on screen horizontally
388 if x < self.datastartx+scrollborderx:
389 self.datastartx = max(0, x-scrollborderx)
390 elif x >= self.datastartx+self.mainsizex-scrollborderx:
391 self.datastartx = max(0, min(x-self.mainsizex+scrollborderx+1,
392 self.datasizex-self.mainsizex))
393
394 if x == oldx and y == oldy and (x != newx or y != newy): # couldn't move
395 self.browser.beep()
396 else:
397 self.curx = x
398 self.cury = y
399 self.calcdisplayattr()
400
401 def sort(self, key, reverse=False):
402 """
403 Sort the currently list of items using the key function ``key``. If
404 ``reverse`` is true the sort order is reversed.
405 """
406 curitem = self.items[self.cury] # Remember where the cursor is now
407
408 # Sort items
409 def realkey(item):
410 return key(item.item)
411 self.items = ipipe.deque(sorted(self.items, key=realkey, reverse=reverse))
412
413 # Find out where the object under the cursor went
414 cury = self.cury
415 for (i, item) in enumerate(self.items):
416 if item is curitem:
417 cury = i
418 break
419
420 self.moveto(self.curx, cury, refresh=True)
421
422 def refresh(self):
423 """
424 Restart iterating the input.
425 """
426 self.iterator = ipipe.xiter(self.input)
427 self.items.clear()
428 self.exhausted = False
429 self.datastartx = self.datastarty = 0
430 self.moveto(0, 0, refresh=True)
431
432 def refreshfind(self):
433 """
434 Restart iterating the input and go back to the same object as before
435 (if it can be found in the new iterator).
436 """
437 try:
438 oldobject = self.items[self.cury].item
439 except IndexError:
440 oldobject = ipipe.noitem
441 self.iterator = ipipe.xiter(self.input)
442 self.items.clear()
443 self.exhausted = False
444 while True:
445 self.fetch(len(self.items)+1)
446 if self.exhausted:
447 curses.beep()
448 self.datastartx = self.datastarty = 0
449 self.moveto(self.curx, 0, refresh=True)
450 break
451 if self.items[-1].item == oldobject:
452 self.datastartx = self.datastarty = 0
453 self.moveto(self.curx, len(self.items)-1, refresh=True)
454 break
455
456
457 class _CommandInput(object):
458 keymap = Keymap()
459 keymap.register("left", curses.KEY_LEFT)
460 keymap.register("right", curses.KEY_RIGHT)
461 keymap.register("home", curses.KEY_HOME, "\x01") # Ctrl-A
462 keymap.register("end", curses.KEY_END, "\x05") # Ctrl-E
463 # FIXME: What's happening here?
464 keymap.register("backspace", curses.KEY_BACKSPACE, "\x08\x7f")
465 keymap.register("delete", curses.KEY_DC)
466 keymap.register("delend", 0x0b) # Ctrl-K
467 keymap.register("execute", "\r\n")
468 keymap.register("up", curses.KEY_UP)
469 keymap.register("down", curses.KEY_DOWN)
470 keymap.register("incsearchup", curses.KEY_PPAGE)
471 keymap.register("incsearchdown", curses.KEY_NPAGE)
472 keymap.register("exit", "\x18"), # Ctrl-X
473
474 def __init__(self, prompt):
475 self.prompt = prompt
476 self.history = []
477 self.maxhistory = 100
478 self.input = ""
479 self.curx = 0
480 self.cury = -1 # blank line
481
482 def start(self):
483 self.input = ""
484 self.curx = 0
485 self.cury = -1 # blank line
486
487 def handlekey(self, browser, key):
488 cmdname = self.keymap.get(key, None)
489 if cmdname is not None:
490 cmdfunc = getattr(self, "cmd_%s" % cmdname, None)
491 if cmdfunc is not None:
492 return cmdfunc(browser)
493 curses.beep()
494 elif key != -1:
495 try:
496 char = chr(key)
497 except ValueError:
498 curses.beep()
499 else:
500 return self.handlechar(browser, char)
501
502 def handlechar(self, browser, char):
503 self.input = self.input[:self.curx] + char + self.input[self.curx:]
504 self.curx += 1
505 return True
506
507 def dohistory(self):
508 self.history.insert(0, self.input)
509 del self.history[:-self.maxhistory]
510
511 def cmd_backspace(self, browser):
512 if self.curx:
513 self.input = self.input[:self.curx-1] + self.input[self.curx:]
514 self.curx -= 1
515 return True
516 else:
517 curses.beep()
518
519 def cmd_delete(self, browser):
520 if self.curx<len(self.input):
521 self.input = self.input[:self.curx] + self.input[self.curx+1:]
522 return True
523 else:
524 curses.beep()
525
526 def cmd_delend(self, browser):
527 if self.curx<len(self.input):
528 self.input = self.input[:self.curx]
529 return True
530
531 def cmd_left(self, browser):
532 if self.curx:
533 self.curx -= 1
534 return True
535 else:
536 curses.beep()
537
538 def cmd_right(self, browser):
539 if self.curx < len(self.input):
540 self.curx += 1
541 return True
542 else:
543 curses.beep()
544
545 def cmd_home(self, browser):
546 if self.curx:
547 self.curx = 0
548 return True
549 else:
550 curses.beep()
551
552 def cmd_end(self, browser):
553 if self.curx < len(self.input):
554 self.curx = len(self.input)
555 return True
556 else:
557 curses.beep()
558
559 def cmd_up(self, browser):
560 if self.cury < len(self.history)-1:
561 self.cury += 1
562 self.input = self.history[self.cury]
563 self.curx = len(self.input)
564 return True
565 else:
566 curses.beep()
567
568 def cmd_down(self, browser):
569 if self.cury >= 0:
570 self.cury -= 1
571 if self.cury>=0:
572 self.input = self.history[self.cury]
573 else:
574 self.input = ""
575 self.curx = len(self.input)
576 return True
577 else:
578 curses.beep()
579
580 def cmd_incsearchup(self, browser):
581 prefix = self.input[:self.curx]
582 cury = self.cury
583 while True:
584 cury += 1
585 if cury >= len(self.history):
586 break
587 if self.history[cury].startswith(prefix):
588 self.input = self.history[cury]
589 self.cury = cury
590 return True
591 curses.beep()
592
593 def cmd_incsearchdown(self, browser):
594 prefix = self.input[:self.curx]
595 cury = self.cury
596 while True:
597 cury -= 1
598 if cury <= 0:
599 break
600 if self.history[cury].startswith(prefix):
601 self.input = self.history[self.cury]
602 self.cury = cury
603 return True
604 curses.beep()
605
606 def cmd_exit(self, browser):
607 browser.mode = "default"
608 return True
609
610 def cmd_execute(self, browser):
611 raise NotImplementedError
612
613
614 class _CommandGoto(_CommandInput):
615 def __init__(self):
616 _CommandInput.__init__(self, "goto object #")
617
618 def handlechar(self, browser, char):
619 # Only accept digits
620 if not "0" <= char <= "9":
621 curses.beep()
622 else:
623 return _CommandInput.handlechar(self, browser, char)
624
625 def cmd_execute(self, browser):
626 level = browser.levels[-1]
627 if self.input:
628 self.dohistory()
629 level.moveto(level.curx, int(self.input))
630 browser.mode = "default"
631 return True
632
633
634 class _CommandFind(_CommandInput):
635 def __init__(self):
636 _CommandInput.__init__(self, "find expression")
637
638 def cmd_execute(self, browser):
639 level = browser.levels[-1]
640 if self.input:
641 self.dohistory()
642 while True:
643 cury = level.cury
644 level.moveto(level.curx, cury+1)
645 if cury == level.cury:
646 curses.beep()
647 break # hit end
648 item = level.items[level.cury].item
649 try:
650 globals = ipipe.getglobals(None)
651 if eval(self.input, globals, ipipe.AttrNamespace(item)):
652 break # found something
653 except (KeyboardInterrupt, SystemExit):
654 raise
655 except Exception, exc:
656 browser.report(exc)
657 curses.beep()
658 break # break on error
659 browser.mode = "default"
660 return True
661
662
663 class _CommandFindBackwards(_CommandInput):
664 def __init__(self):
665 _CommandInput.__init__(self, "find backwards expression")
666
667 def cmd_execute(self, browser):
668 level = browser.levels[-1]
669 if self.input:
670 self.dohistory()
671 while level.cury:
672 level.moveto(level.curx, level.cury-1)
673 item = level.items[level.cury].item
674 try:
675 globals = ipipe.getglobals(None)
676 if eval(self.input, globals, ipipe.AttrNamespace(item)):
677 break # found something
678 except (KeyboardInterrupt, SystemExit):
679 raise
680 except Exception, exc:
681 browser.report(exc)
682 curses.beep()
683 break # break on error
684 else:
685 curses.beep()
686 browser.mode = "default"
687 return True
688
689
690 class ibrowse(ipipe.Display):
691 # Show this many lines from the previous screen when paging horizontally
692 pageoverlapx = 1
693
694 # Show this many lines from the previous screen when paging vertically
695 pageoverlapy = 1
696
697 # Start scrolling when the cursor is less than this number of columns
698 # away from the left or right screen edge
699 scrollborderx = 10
700
701 # Start scrolling when the cursor is less than this number of lines
702 # away from the top or bottom screen edge
703 scrollbordery = 5
704
705 # Accelerate by this factor when scrolling horizontally
706 acceleratex = 1.05
707
708 # Accelerate by this factor when scrolling vertically
709 acceleratey = 1.05
710
711 # The maximum horizontal scroll speed
712 # (as a factor of the screen width (i.e. 0.5 == half a screen width)
713 maxspeedx = 0.5
714
715 # The maximum vertical scroll speed
716 # (as a factor of the screen height (i.e. 0.5 == half a screen height)
717 maxspeedy = 0.5
718
719 # The maximum number of header lines for browser level
720 # if the nesting is deeper, only the innermost levels are displayed
721 maxheaders = 5
722
723 # The approximate maximum length of a column entry
724 maxattrlength = 200
725
726 # Styles for various parts of the GUI
727 style_objheadertext = astyle.Style.fromstr("white:black:bold|reverse")
728 style_objheadernumber = astyle.Style.fromstr("white:blue:bold|reverse")
729 style_objheaderobject = astyle.Style.fromstr("white:black:reverse")
730 style_colheader = astyle.Style.fromstr("blue:white:reverse")
731 style_colheaderhere = astyle.Style.fromstr("green:black:bold|reverse")
732 style_colheadersep = astyle.Style.fromstr("blue:black:reverse")
733 style_number = astyle.Style.fromstr("blue:white:reverse")
734 style_numberhere = astyle.Style.fromstr("green:black:bold|reverse")
735 style_sep = astyle.Style.fromstr("blue:black")
736 style_data = astyle.Style.fromstr("white:black")
737 style_datapad = astyle.Style.fromstr("blue:black:bold")
738 style_footer = astyle.Style.fromstr("black:white")
739 style_report = astyle.Style.fromstr("white:black")
740
741 # Column separator in header
742 headersepchar = "|"
743
744 # Character for padding data cell entries
745 datapadchar = "."
746
747 # Column separator in data area
748 datasepchar = "|"
749
750 # Character to use for "empty" cell (i.e. for non-existing attributes)
751 nodatachar = "-"
752
753 # Prompts for modes that require keyboard input
754 prompts = {
755 "goto": _CommandGoto(),
756 "find": _CommandFind(),
757 "findbackwards": _CommandFindBackwards()
758 }
759
760 # Maps curses key codes to "function" names
761 keymap = Keymap()
762 keymap.register("quit", "q")
763 keymap.register("up", curses.KEY_UP)
764 keymap.register("down", curses.KEY_DOWN)
765 keymap.register("pageup", curses.KEY_PPAGE)
766 keymap.register("pagedown", curses.KEY_NPAGE)
767 keymap.register("left", curses.KEY_LEFT)
768 keymap.register("right", curses.KEY_RIGHT)
769 keymap.register("home", curses.KEY_HOME, "\x01")
770 keymap.register("end", curses.KEY_END, "\x05")
771 keymap.register("prevattr", "<\x1b")
772 keymap.register("nextattr", ">\t")
773 keymap.register("pick", "p")
774 keymap.register("pickattr", "P")
775 keymap.register("pickallattrs", "C")
776 keymap.register("pickmarked", "m")
777 keymap.register("pickmarkedattr", "M")
778 keymap.register("pickinput", "i")
779 keymap.register("pickinputattr", "I")
780 keymap.register("hideattr", "h")
781 keymap.register("unhideattrs", "H")
782 keymap.register("help", "?")
783 keymap.register("enter", "\r\n")
784 keymap.register("enterattr", "E")
785 # FIXME: What's happening here?
786 keymap.register("leave", curses.KEY_BACKSPACE, "x\x08\x7f")
787 keymap.register("detail", "d")
788 keymap.register("detailattr", "D")
789 keymap.register("tooglemark", " ")
790 keymap.register("markrange", "%")
791 keymap.register("sortattrasc", "v")
792 keymap.register("sortattrdesc", "V")
793 keymap.register("goto", "g")
794 keymap.register("find", "f")
795 keymap.register("findbackwards", "b")
796 keymap.register("refresh", "r")
797 keymap.register("refreshfind", "R")
798
799 def __init__(self, input=None, *attrs):
800 """
801 Create a new browser. If ``attrs`` is not empty, it is the list
802 of attributes that will be displayed in the browser, otherwise
803 these will be determined by the objects on screen.
804 """
805 ipipe.Display.__init__(self, input)
806
807 self.attrs = attrs
808
809 # Stack of browser levels
810 self.levels = []
811 # how many colums to scroll (Changes when accelerating)
812 self.stepx = 1.
813
814 # how many rows to scroll (Changes when accelerating)
815 self.stepy = 1.
816
817 # Beep on the edges of the data area? (Will be set to ``False``
818 # once the cursor hits the edge of the screen, so we don't get
819 # multiple beeps).
820 self._dobeep = True
821
822 # Cache for registered ``curses`` colors and styles.
823 self._styles = {}
824 self._colors = {}
825 self._maxcolor = 1
826
827 # How many header lines do we want to paint (the numbers of levels
828 # we have, but with an upper bound)
829 self._headerlines = 1
830
831 # Index of first header line
832 self._firstheaderline = 0
833
834 # curses window
835 self.scr = None
836 # report in the footer line (error, executed command etc.)
837 self._report = None
838
839 # value to be returned to the caller (set by commands)
840 self.returnvalue = None
841
842 # The mode the browser is in
843 # e.g. normal browsing or entering an argument for a command
844 self.mode = "default"
845
846 # set by the SIGWINCH signal handler
847 self.resized = False
848
849 def nextstepx(self, step):
850 """
851 Accelerate horizontally.
852 """
853 return max(1., min(step*self.acceleratex,
854 self.maxspeedx*self.levels[-1].mainsizex))
855
856 def nextstepy(self, step):
857 """
858 Accelerate vertically.
859 """
860 return max(1., min(step*self.acceleratey,
861 self.maxspeedy*self.levels[-1].mainsizey))
862
863 def getstyle(self, style):
864 """
865 Register the ``style`` with ``curses`` or get it from the cache,
866 if it has been registered before.
867 """
868 try:
869 return self._styles[style.fg, style.bg, style.attrs]
870 except KeyError:
871 attrs = 0
872 for b in astyle.A2CURSES:
873 if style.attrs & b:
874 attrs |= astyle.A2CURSES[b]
875 try:
876 color = self._colors[style.fg, style.bg]
877 except KeyError:
878 curses.init_pair(
879 self._maxcolor,
880 astyle.COLOR2CURSES[style.fg],
881 astyle.COLOR2CURSES[style.bg]
882 )
883 color = curses.color_pair(self._maxcolor)
884 self._colors[style.fg, style.bg] = color
885 self._maxcolor += 1
886 c = color | attrs
887 self._styles[style.fg, style.bg, style.attrs] = c
888 return c
889
890 def addstr(self, y, x, begx, endx, text, style):
891 """
892 A version of ``curses.addstr()`` that can handle ``x`` coordinates
893 that are outside the screen.
894 """
895 text2 = text[max(0, begx-x):max(0, endx-x)]
896 if text2:
897 self.scr.addstr(y, max(x, begx), text2, self.getstyle(style))
898 return len(text)
899
900 def addchr(self, y, x, begx, endx, c, l, style):
901 x0 = max(x, begx)
902 x1 = min(x+l, endx)
903 if x1>x0:
904 self.scr.addstr(y, x0, c*(x1-x0), self.getstyle(style))
905 return l
906
907 def _calcheaderlines(self, levels):
908 # Calculate how many headerlines do we have to display, if we have
909 # ``levels`` browser levels
910 if levels is None:
911 levels = len(self.levels)
912 self._headerlines = min(self.maxheaders, levels)
913 self._firstheaderline = levels-self._headerlines
914
915 def getstylehere(self, style):
916 """
917 Return a style for displaying the original style ``style``
918 in the row the cursor is on.
919 """
920 return astyle.Style(style.fg, astyle.COLOR_BLUE, style.attrs | astyle.A_BOLD)
921
922 def report(self, msg):
923 """
924 Store the message ``msg`` for display below the footer line. This
925 will be displayed as soon as the screen is redrawn.
926 """
927 self._report = msg
928
929 def enter(self, item, *attrs):
930 """
931 Enter the object ``item``. If ``attrs`` is specified, it will be used
932 as a fixed list of attributes to display.
933 """
934 if self.levels and item is self.levels[-1].input:
935 curses.beep()
936 self.report(CommandError("Recursion on input object"))
937 else:
938 oldlevels = len(self.levels)
939 self._calcheaderlines(oldlevels+1)
940 try:
941 level = _BrowserLevel(
942 self,
943 item,
944 self.scrsizey-1-self._headerlines-2,
945 *attrs
946 )
947 except (KeyboardInterrupt, SystemExit):
948 raise
949 except Exception, exc:
950 if not self.levels:
951 raise
952 self._calcheaderlines(oldlevels)
953 curses.beep()
954 self.report(exc)
955 else:
956 self.levels.append(level)
957
958 def startkeyboardinput(self, mode):
959 """
960 Enter mode ``mode``, which requires keyboard input.
961 """
962 self.mode = mode
963 self.prompts[mode].start()
964
965 def keylabel(self, keycode):
966 """
967 Return a pretty name for the ``curses`` key ``keycode`` (used in the
968 help screen and in reports about unassigned keys).
969 """
970 if keycode <= 0xff:
971 specialsnames = {
972 ord("\n"): "RETURN",
973 ord(" "): "SPACE",
974 ord("\t"): "TAB",
975 ord("\x7f"): "DELETE",
976 ord("\x08"): "BACKSPACE",
977 }
978 if keycode in specialsnames:
979 return specialsnames[keycode]
980 elif 0x00 < keycode < 0x20:
981 return "CTRL-%s" % chr(keycode + 64)
982 return repr(chr(keycode))
983 for name in dir(curses):
984 if name.startswith("KEY_") and getattr(curses, name) == keycode:
985 return name
986 return str(keycode)
987
988 def beep(self, force=False):
989 if force or self._dobeep:
990 curses.beep()
991 # don't beep again (as long as the same key is pressed)
992 self._dobeep = False
993
994 def cmd_up(self):
995 """
996 Move the cursor to the previous row.
997 """
998 level = self.levels[-1]
999 self.report("up")
1000 level.moveto(level.curx, level.cury-self.stepy)
1001
1002 def cmd_down(self):
1003 """
1004 Move the cursor to the next row.
1005 """
1006 level = self.levels[-1]
1007 self.report("down")
1008 level.moveto(level.curx, level.cury+self.stepy)
1009
1010 def cmd_pageup(self):
1011 """
1012 Move the cursor up one page.
1013 """
1014 level = self.levels[-1]
1015 self.report("page up")
1016 level.moveto(level.curx, level.cury-level.mainsizey+self.pageoverlapy)
1017
1018 def cmd_pagedown(self):
1019 """
1020 Move the cursor down one page.
1021 """
1022 level = self.levels[-1]
1023 self.report("page down")
1024 level.moveto(level.curx, level.cury+level.mainsizey-self.pageoverlapy)
1025
1026 def cmd_left(self):
1027 """
1028 Move the cursor left.
1029 """
1030 level = self.levels[-1]
1031 self.report("left")
1032 level.moveto(level.curx-self.stepx, level.cury)
1033
1034 def cmd_right(self):
1035 """
1036 Move the cursor right.
1037 """
1038 level = self.levels[-1]
1039 self.report("right")
1040 level.moveto(level.curx+self.stepx, level.cury)
1041
1042 def cmd_home(self):
1043 """
1044 Move the cursor to the first column.
1045 """
1046 level = self.levels[-1]
1047 self.report("home")
1048 level.moveto(0, level.cury)
1049
1050 def cmd_end(self):
1051 """
1052 Move the cursor to the last column.
1053 """
1054 level = self.levels[-1]
1055 self.report("end")
1056 level.moveto(level.datasizex+level.mainsizey-self.pageoverlapx, level.cury)
1057
1058 def cmd_prevattr(self):
1059 """
1060 Move the cursor one attribute column to the left.
1061 """
1062 level = self.levels[-1]
1063 if level.displayattr[0] is None or level.displayattr[0] == 0:
1064 self.beep()
1065 else:
1066 self.report("prevattr")
1067 pos = 0
1068 for (i, attrname) in enumerate(level.displayattrs):
1069 if i == level.displayattr[0]-1:
1070 break
1071 pos += level.colwidths[attrname] + 1
1072 level.moveto(pos, level.cury)
1073
1074 def cmd_nextattr(self):
1075 """
1076 Move the cursor one attribute column to the right.
1077 """
1078 level = self.levels[-1]
1079 if level.displayattr[0] is None or level.displayattr[0] == len(level.displayattrs)-1:
1080 self.beep()
1081 else:
1082 self.report("nextattr")
1083 pos = 0
1084 for (i, attrname) in enumerate(level.displayattrs):
1085 if i == level.displayattr[0]+1:
1086 break
1087 pos += level.colwidths[attrname] + 1
1088 level.moveto(pos, level.cury)
1089
1090 def cmd_pick(self):
1091 """
1092 'Pick' the object under the cursor (i.e. the row the cursor is on).
1093 This leaves the browser and returns the picked object to the caller.
1094 (In IPython this object will be available as the ``_`` variable.)
1095 """
1096 level = self.levels[-1]
1097 self.returnvalue = level.items[level.cury].item
1098 return True
1099
1100 def cmd_pickattr(self):
1101 """
1102 'Pick' the attribute under the cursor (i.e. the row/column the
1103 cursor is on).
1104 """
1105 level = self.levels[-1]
1106 attr = level.displayattr[1]
1107 if attr is ipipe.noitem:
1108 curses.beep()
1109 self.report(CommandError("no column under cursor"))
1110 return
1111 value = attr.value(level.items[level.cury].item)
1112 if value is ipipe.noitem:
1113 curses.beep()
1114 self.report(AttributeError(attr.name()))
1115 else:
1116 self.returnvalue = value
1117 return True
1118
1119 def cmd_pickallattrs(self):
1120 """
1121 Pick' the complete column under the cursor (i.e. the attribute under
1122 the cursor) from all currently fetched objects. These attributes
1123 will be returned as a list.
1124 """
1125 level = self.levels[-1]
1126 attr = level.displayattr[1]
1127 if attr is ipipe.noitem:
1128 curses.beep()
1129 self.report(CommandError("no column under cursor"))
1130 return
1131 result = []
1132 for cache in level.items:
1133 value = attr.value(cache.item)
1134 if value is not ipipe.noitem:
1135 result.append(value)
1136 self.returnvalue = result
1137 return True
1138
1139 def cmd_pickmarked(self):
1140 """
1141 'Pick' marked objects. Marked objects will be returned as a list.
1142 """
1143 level = self.levels[-1]
1144 self.returnvalue = [cache.item for cache in level.items if cache.marked]
1145 return True
1146
1147 def cmd_pickmarkedattr(self):
1148 """
1149 'Pick' the attribute under the cursor from all marked objects
1150 (This returns a list).
1151 """
1152
1153 level = self.levels[-1]
1154 attr = level.displayattr[1]
1155 if attr is ipipe.noitem:
1156 curses.beep()
1157 self.report(CommandError("no column under cursor"))
1158 return
1159 result = []
1160 for cache in level.items:
1161 if cache.marked:
1162 value = attr.value(cache.item)
1163 if value is not ipipe.noitem:
1164 result.append(value)
1165 self.returnvalue = result
1166 return True
1167
1168 def cmd_pickinput(self):
1169 """
1170 Use the object under the cursor (i.e. the row the cursor is on) as
1171 the next input line. This leaves the browser and puts the picked object
1172 in the input.
1173 """
1174 level = self.levels[-1]
1175 value = level.items[level.cury].item
1176 self.returnvalue = None
1177 api = ipapi.get()
1178 api.set_next_input(str(value))
1179 return True
1180
1181 def cmd_pickinputattr(self):
1182 """
1183 Use the attribute under the cursor i.e. the row/column the cursor is on)
1184 as the next input line. This leaves the browser and puts the picked
1185 object in the input.
1186 """
1187 level = self.levels[-1]
1188 attr = level.displayattr[1]
1189 if attr is ipipe.noitem:
1190 curses.beep()
1191 self.report(CommandError("no column under cursor"))
1192 return
1193 value = attr.value(level.items[level.cury].item)
1194 if value is ipipe.noitem:
1195 curses.beep()
1196 self.report(AttributeError(attr.name()))
1197 self.returnvalue = None
1198 api = ipapi.get()
1199 api.set_next_input(str(value))
1200 return True
1201
1202 def cmd_markrange(self):
1203 """
1204 Mark all objects from the last marked object before the current cursor
1205 position to the cursor position.
1206 """
1207 level = self.levels[-1]
1208 self.report("markrange")
1209 start = None
1210 if level.items:
1211 for i in xrange(level.cury, -1, -1):
1212 if level.items[i].marked:
1213 start = i
1214 break
1215 if start is None:
1216 self.report(CommandError("no mark before cursor"))
1217 curses.beep()
1218 else:
1219 for i in xrange(start, level.cury+1):
1220 cache = level.items[i]
1221 if not cache.marked:
1222 cache.marked = True
1223 level.marked += 1
1224
1225 def cmd_enter(self):
1226 """
1227 Enter the object under the cursor. (what this mean depends on the object
1228 itself (i.e. how it implements iteration). This opens a new browser 'level'.
1229 """
1230 level = self.levels[-1]
1231 try:
1232 item = level.items[level.cury].item
1233 except IndexError:
1234 self.report(CommandError("No object"))
1235 curses.beep()
1236 else:
1237 self.report("entering object...")
1238 self.enter(item)
1239
1240 def cmd_leave(self):
1241 """
1242 Leave the current browser level and go back to the previous one.
1243 """
1244 self.report("leave")
1245 if len(self.levels) > 1:
1246 self._calcheaderlines(len(self.levels)-1)
1247 self.levels.pop(-1)
1248 else:
1249 self.report(CommandError("This is the last level"))
1250 curses.beep()
1251
1252 def cmd_enterattr(self):
1253 """
1254 Enter the attribute under the cursor.
1255 """
1256 level = self.levels[-1]
1257 attr = level.displayattr[1]
1258 if attr is ipipe.noitem:
1259 curses.beep()
1260 self.report(CommandError("no column under cursor"))
1261 return
1262 try:
1263 item = level.items[level.cury].item
1264 except IndexError:
1265 self.report(CommandError("No object"))
1266 curses.beep()
1267 else:
1268 value = attr.value(item)
1269 name = attr.name()
1270 if value is ipipe.noitem:
1271 self.report(AttributeError(name))
1272 else:
1273 self.report("entering object attribute %s..." % name)
1274 self.enter(value)
1275
1276 def cmd_detail(self):
1277 """
1278 Show a detail view of the object under the cursor. This shows the
1279 name, type, doc string and value of the object attributes (and it
1280 might show more attributes than in the list view, depending on
1281 the object).
1282 """
1283 level = self.levels[-1]
1284 try:
1285 item = level.items[level.cury].item
1286 except IndexError:
1287 self.report(CommandError("No object"))
1288 curses.beep()
1289 else:
1290 self.report("entering detail view for object...")
1291 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
1292 self.enter(attrs)
1293
1294 def cmd_detailattr(self):
1295 """
1296 Show a detail view of the attribute under the cursor.
1297 """
1298 level = self.levels[-1]
1299 attr = level.displayattr[1]
1300 if attr is ipipe.noitem:
1301 curses.beep()
1302 self.report(CommandError("no attribute"))
1303 return
1304 try:
1305 item = level.items[level.cury].item
1306 except IndexError:
1307 self.report(CommandError("No object"))
1308 curses.beep()
1309 else:
1310 try:
1311 item = attr.value(item)
1312 except (KeyboardInterrupt, SystemExit):
1313 raise
1314 except Exception, exc:
1315 self.report(exc)
1316 else:
1317 self.report("entering detail view for attribute %s..." % attr.name())
1318 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
1319 self.enter(attrs)
1320
1321 def cmd_tooglemark(self):
1322 """
1323 Mark/unmark the object under the cursor. Marked objects have a '!'
1324 after the row number).
1325 """
1326 level = self.levels[-1]
1327 self.report("toggle mark")
1328 try:
1329 item = level.items[level.cury]
1330 except IndexError: # no items?
1331 pass
1332 else:
1333 if item.marked:
1334 item.marked = False
1335 level.marked -= 1
1336 else:
1337 item.marked = True
1338 level.marked += 1
1339
1340 def cmd_sortattrasc(self):
1341 """
1342 Sort the objects (in ascending order) using the attribute under
1343 the cursor as the sort key.
1344 """
1345 level = self.levels[-1]
1346 attr = level.displayattr[1]
1347 if attr is ipipe.noitem:
1348 curses.beep()
1349 self.report(CommandError("no column under cursor"))
1350 return
1351 self.report("sort by %s (ascending)" % attr.name())
1352 def key(item):
1353 try:
1354 return attr.value(item)
1355 except (KeyboardInterrupt, SystemExit):
1356 raise
1357 except Exception:
1358 return None
1359 level.sort(key)
1360
1361 def cmd_sortattrdesc(self):
1362 """
1363 Sort the objects (in descending order) using the attribute under
1364 the cursor as the sort key.
1365 """
1366 level = self.levels[-1]
1367 attr = level.displayattr[1]
1368 if attr is ipipe.noitem:
1369 curses.beep()
1370 self.report(CommandError("no column under cursor"))
1371 return
1372 self.report("sort by %s (descending)" % attr.name())
1373 def key(item):
1374 try:
1375 return attr.value(item)
1376 except (KeyboardInterrupt, SystemExit):
1377 raise
1378 except Exception:
1379 return None
1380 level.sort(key, reverse=True)
1381
1382 def cmd_hideattr(self):
1383 """
1384 Hide the attribute under the cursor.
1385 """
1386 level = self.levels[-1]
1387 if level.displayattr[0] is None:
1388 self.beep()
1389 else:
1390 self.report("hideattr")
1391 level.hiddenattrs.add(level.displayattr[1])
1392 level.moveto(level.curx, level.cury, refresh=True)
1393
1394 def cmd_unhideattrs(self):
1395 """
1396 Make all attributes visible again.
1397 """
1398 level = self.levels[-1]
1399 self.report("unhideattrs")
1400 level.hiddenattrs.clear()
1401 level.moveto(level.curx, level.cury, refresh=True)
1402
1403 def cmd_goto(self):
1404 """
1405 Jump to a row. The row number can be entered at the
1406 bottom of the screen.
1407 """
1408 self.startkeyboardinput("goto")
1409
1410 def cmd_find(self):
1411 """
1412 Search forward for a row. The search condition can be entered at the
1413 bottom of the screen.
1414 """
1415 self.startkeyboardinput("find")
1416
1417 def cmd_findbackwards(self):
1418 """
1419 Search backward for a row. The search condition can be entered at the
1420 bottom of the screen.
1421 """
1422 self.startkeyboardinput("findbackwards")
1423
1424 def cmd_refresh(self):
1425 """
1426 Refreshes the display by restarting the iterator.
1427 """
1428 level = self.levels[-1]
1429 self.report("refresh")
1430 level.refresh()
1431
1432 def cmd_refreshfind(self):
1433 """
1434 Refreshes the display by restarting the iterator and goes back to the
1435 same object the cursor was on before restarting (if this object can't be
1436 found the cursor jumps back to the first object).
1437 """
1438 level = self.levels[-1]
1439 self.report("refreshfind")
1440 level.refreshfind()
1441
1442 def cmd_help(self):
1443 """
1444 Opens the help screen as a new browser level, describing keyboard
1445 shortcuts.
1446 """
1447 for level in self.levels:
1448 if isinstance(level.input, _BrowserHelp):
1449 curses.beep()
1450 self.report(CommandError("help already active"))
1451 return
1452
1453 self.enter(_BrowserHelp(self))
1454
1455 def cmd_quit(self):
1456 """
1457 Quit the browser and return to the IPython prompt.
1458 """
1459 self.returnvalue = None
1460 return True
1461
1462 def sigwinchhandler(self, signal, frame):
1463 self.resized = True
1464
1465 def _dodisplay(self, scr):
1466 """
1467 This method is the workhorse of the browser. It handles screen
1468 drawing and the keyboard.
1469 """
1470 self.scr = scr
1471 curses.halfdelay(1)
1472 footery = 2
1473
1474 keys = []
1475 for cmd in ("quit", "help"):
1476 key = self.keymap.findkey(cmd, None)
1477 if key is not None:
1478 keys.append("%s=%s" % (self.keylabel(key), cmd))
1479 helpmsg = " | %s" % " ".join(keys)
1480
1481 scr.clear()
1482 msg = "Fetching first batch of objects..."
1483 (self.scrsizey, self.scrsizex) = scr.getmaxyx()
1484 scr.addstr(self.scrsizey//2, (self.scrsizex-len(msg))//2, msg)
1485 scr.refresh()
1486
1487 lastc = -1
1488
1489 self.levels = []
1490 # enter the first level
1491 self.enter(self.input, *self.attrs)
1492
1493 self._calcheaderlines(None)
1494
1495 while True:
1496 level = self.levels[-1]
1497 (self.scrsizey, self.scrsizex) = scr.getmaxyx()
1498 level.mainsizey = self.scrsizey-1-self._headerlines-footery
1499
1500 # Paint object header
1501 for i in xrange(self._firstheaderline, self._firstheaderline+self._headerlines):
1502 lv = self.levels[i]
1503 posx = 0
1504 posy = i-self._firstheaderline
1505 endx = self.scrsizex
1506 if i: # not the first level
1507 msg = " (%d/%d" % (self.levels[i-1].cury, len(self.levels[i-1].items))
1508 if not self.levels[i-1].exhausted:
1509 msg += "+"
1510 msg += ") "
1511 endx -= len(msg)+1
1512 posx += self.addstr(posy, posx, 0, endx, " ibrowse #%d: " % i, self.style_objheadertext)
1513 for (style, text) in lv.header:
1514 posx += self.addstr(posy, posx, 0, endx, text, self.style_objheaderobject)
1515 if posx >= endx:
1516 break
1517 if i:
1518 posx += self.addstr(posy, posx, 0, self.scrsizex, msg, self.style_objheadernumber)
1519 posx += self.addchr(posy, posx, 0, self.scrsizex, " ", self.scrsizex-posx, self.style_objheadernumber)
1520
1521 if not level.items:
1522 self.addchr(self._headerlines, 0, 0, self.scrsizex, " ", self.scrsizex, self.style_colheader)
1523 self.addstr(self._headerlines+1, 0, 0, self.scrsizex, " <empty>", astyle.style_error)
1524 scr.clrtobot()
1525 else:
1526 # Paint column headers
1527 scr.move(self._headerlines, 0)
1528 scr.addstr(" %*s " % (level.numbersizex, "#"), self.getstyle(self.style_colheader))
1529 scr.addstr(self.headersepchar, self.getstyle(self.style_colheadersep))
1530 begx = level.numbersizex+3
1531 posx = begx-level.datastartx
1532 for attr in level.displayattrs:
1533 attrname = attr.name()
1534 cwidth = level.colwidths[attr]
1535 header = attrname.ljust(cwidth)
1536 if attr is level.displayattr[1]:
1537 style = self.style_colheaderhere
1538 else:
1539 style = self.style_colheader
1540 posx += self.addstr(self._headerlines, posx, begx, self.scrsizex, header, style)
1541 posx += self.addstr(self._headerlines, posx, begx, self.scrsizex, self.headersepchar, self.style_colheadersep)
1542 if posx >= self.scrsizex:
1543 break
1544 else:
1545 scr.addstr(" "*(self.scrsizex-posx), self.getstyle(self.style_colheader))
1546
1547 # Paint rows
1548 posy = self._headerlines+1+level.datastarty
1549 for i in xrange(level.datastarty, min(level.datastarty+level.mainsizey, len(level.items))):
1550 cache = level.items[i]
1551 if i == level.cury:
1552 style = self.style_numberhere
1553 else:
1554 style = self.style_number
1555
1556 posy = self._headerlines+1+i-level.datastarty
1557 posx = begx-level.datastartx
1558
1559 scr.move(posy, 0)
1560 scr.addstr(" %*d%s" % (level.numbersizex, i, " !"[cache.marked]), self.getstyle(style))
1561 scr.addstr(self.headersepchar, self.getstyle(self.style_sep))
1562
1563 for attrname in level.displayattrs:
1564 cwidth = level.colwidths[attrname]
1565 try:
1566 (align, length, parts) = level.displayrows[i-level.datastarty][attrname]
1567 except KeyError:
1568 align = 2
1569 style = astyle.style_nodata
1570 if i == level.cury:
1571 style = self.getstylehere(style)
1572 padstyle = self.style_datapad
1573 sepstyle = self.style_sep
1574 if i == level.cury:
1575 padstyle = self.getstylehere(padstyle)
1576 sepstyle = self.getstylehere(sepstyle)
1577 if align == 2:
1578 posx += self.addchr(posy, posx, begx, self.scrsizex, self.nodatachar, cwidth, style)
1579 else:
1580 if align == 1:
1581 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, cwidth-length, padstyle)
1582 elif align == 0:
1583 pad1 = (cwidth-length)//2
1584 pad2 = cwidth-length-len(pad1)
1585 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, pad1, padstyle)
1586 for (style, text) in parts:
1587 if i == level.cury:
1588 style = self.getstylehere(style)
1589 posx += self.addstr(posy, posx, begx, self.scrsizex, text, style)
1590 if posx >= self.scrsizex:
1591 break
1592 if align == -1:
1593 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, cwidth-length, padstyle)
1594 elif align == 0:
1595 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, pad2, padstyle)
1596 posx += self.addstr(posy, posx, begx, self.scrsizex, self.datasepchar, sepstyle)
1597 else:
1598 scr.clrtoeol()
1599
1600 # Add blank row headers for the rest of the screen
1601 for posy in xrange(posy+1, self.scrsizey-2):
1602 scr.addstr(posy, 0, " " * (level.numbersizex+2), self.getstyle(self.style_colheader))
1603 scr.clrtoeol()
1604
1605 posy = self.scrsizey-footery
1606 # Display footer
1607 scr.addstr(posy, 0, " "*self.scrsizex, self.getstyle(self.style_footer))
1608
1609 if level.exhausted:
1610 flag = ""
1611 else:
1612 flag = "+"
1613
1614 endx = self.scrsizex-len(helpmsg)-1
1615 scr.addstr(posy, endx, helpmsg, self.getstyle(self.style_footer))
1616
1617 posx = 0
1618 msg = " %d%s objects (%d marked): " % (len(level.items), flag, level.marked)
1619 posx += self.addstr(posy, posx, 0, endx, msg, self.style_footer)
1620 try:
1621 item = level.items[level.cury].item
1622 except IndexError: # empty
1623 pass
1624 else:
1625 for (nostyle, text) in ipipe.xrepr(item, "footer"):
1626 if not isinstance(nostyle, int):
1627 posx += self.addstr(posy, posx, 0, endx, text, self.style_footer)
1628 if posx >= endx:
1629 break
1630
1631 attrstyle = [(astyle.style_default, "no attribute")]
1632 attr = level.displayattr[1]
1633 if attr is not ipipe.noitem and not isinstance(attr, ipipe.SelfDescriptor):
1634 posx += self.addstr(posy, posx, 0, endx, " | ", self.style_footer)
1635 posx += self.addstr(posy, posx, 0, endx, attr.name(), self.style_footer)
1636 posx += self.addstr(posy, posx, 0, endx, ": ", self.style_footer)
1637 try:
1638 value = attr.value(item)
1639 except (SystemExit, KeyboardInterrupt):
1640 raise
1641 except Exception, exc:
1642 value = exc
1643 if value is not ipipe.noitem:
1644 attrstyle = ipipe.xrepr(value, "footer")
1645 for (nostyle, text) in attrstyle:
1646 if not isinstance(nostyle, int):
1647 posx += self.addstr(posy, posx, 0, endx, text, self.style_footer)
1648 if posx >= endx:
1649 break
1650
1651 try:
1652 # Display input prompt
1653 if self.mode in self.prompts:
1654 history = self.prompts[self.mode]
1655 posx = 0
1656 posy = self.scrsizey-1
1657 posx += self.addstr(posy, posx, 0, endx, history.prompt, astyle.style_default)
1658 posx += self.addstr(posy, posx, 0, endx, " [", astyle.style_default)
1659 if history.cury==-1:
1660 text = "new"
1661 else:
1662 text = str(history.cury+1)
1663 posx += self.addstr(posy, posx, 0, endx, text, astyle.style_type_number)
1664 if history.history:
1665 posx += self.addstr(posy, posx, 0, endx, "/", astyle.style_default)
1666 posx += self.addstr(posy, posx, 0, endx, str(len(history.history)), astyle.style_type_number)
1667 posx += self.addstr(posy, posx, 0, endx, "]: ", astyle.style_default)
1668 inputstartx = posx
1669 posx += self.addstr(posy, posx, 0, endx, history.input, astyle.style_default)
1670 # Display report
1671 else:
1672 if self._report is not None:
1673 if isinstance(self._report, Exception):
1674 style = self.getstyle(astyle.style_error)
1675 if self._report.__class__.__module__ == "exceptions":
1676 msg = "%s: %s" % \
1677 (self._report.__class__.__name__, self._report)
1678 else:
1679 msg = "%s.%s: %s" % \
1680 (self._report.__class__.__module__,
1681 self._report.__class__.__name__, self._report)
1682 else:
1683 style = self.getstyle(self.style_report)
1684 msg = self._report
1685 scr.addstr(self.scrsizey-1, 0, msg[:self.scrsizex], style)
1686 self._report = None
1687 else:
1688 scr.move(self.scrsizey-1, 0)
1689 except curses.error:
1690 # Protect against errors from writing to the last line
1691 pass
1692 scr.clrtoeol()
1693
1694 # Position cursor
1695 if self.mode in self.prompts:
1696 history = self.prompts[self.mode]
1697 scr.move(self.scrsizey-1, inputstartx+history.curx)
1698 else:
1699 scr.move(
1700 1+self._headerlines+level.cury-level.datastarty,
1701 level.numbersizex+3+level.curx-level.datastartx
1702 )
1703 scr.refresh()
1704
1705 # Check keyboard
1706 while True:
1707 c = scr.getch()
1708 if self.resized:
1709 size = fcntl.ioctl(0, tty.TIOCGWINSZ, "12345678")
1710 size = struct.unpack("4H", size)
1711 oldsize = scr.getmaxyx()
1712 scr.erase()
1713 curses.resize_term(size[0], size[1])
1714 newsize = scr.getmaxyx()
1715 scr.erase()
1716 for l in self.levels:
1717 l.mainsizey += newsize[0]-oldsize[0]
1718 l.moveto(l.curx, l.cury, refresh=True)
1719 scr.refresh()
1720 self.resized = False
1721 break # Redisplay
1722 if self.mode in self.prompts:
1723 if self.prompts[self.mode].handlekey(self, c):
1724 break # Redisplay
1725 else:
1726 # if no key is pressed slow down and beep again
1727 if c == -1:
1728 self.stepx = 1.
1729 self.stepy = 1.
1730 self._dobeep = True
1731 else:
1732 # if a different key was pressed slow down and beep too
1733 if c != lastc:
1734 lastc = c
1735 self.stepx = 1.
1736 self.stepy = 1.
1737 self._dobeep = True
1738 cmdname = self.keymap.get(c, None)
1739 if cmdname is None:
1740 self.report(
1741 UnassignedKeyError("Unassigned key %s" %
1742 self.keylabel(c)))
1743 else:
1744 cmdfunc = getattr(self, "cmd_%s" % cmdname, None)
1745 if cmdfunc is None:
1746 self.report(
1747 UnknownCommandError("Unknown command %r" %
1748 (cmdname,)))
1749 elif cmdfunc():
1750 returnvalue = self.returnvalue
1751 self.returnvalue = None
1752 return returnvalue
1753 self.stepx = self.nextstepx(self.stepx)
1754 self.stepy = self.nextstepy(self.stepy)
1755 curses.flushinp() # get rid of type ahead
1756 break # Redisplay
1757 self.scr = None
1758
1759 def display(self):
1760 if hasattr(curses, "resize_term"):
1761 oldhandler = signal.signal(signal.SIGWINCH, self.sigwinchhandler)
1762 try:
1763 return curses.wrapper(self._dodisplay)
1764 finally:
1765 signal.signal(signal.SIGWINCH, oldhandler)
1766 else:
1767 return curses.wrapper(self._dodisplay)
This diff has been collapsed as it changes many lines, (1126 lines changed) Show them Hide them
@@ -0,0 +1,1126
1 # -*- coding: iso-8859-1 -*-
2
3 import ipipe, os, webbrowser, urllib
4 from IPython import ipapi
5 import wx
6 import wx.grid, wx.html
7
8 try:
9 sorted
10 except NameError:
11 from ipipe import sorted
12 try:
13 set
14 except:
15 from sets import Set as set
16
17
18 __all__ = ["igrid"]
19
20
21 help = """
22 <?xml version='1.0' encoding='iso-8859-1'?>
23 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
24 <html>
25 <head>
26 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
27 <link rel="stylesheet" href="igrid_help.css" type="text/css" />
28 <title>igrid help</title>
29 </head>
30 <body>
31 <h1>igrid help</h1>
32
33
34 <h2>Commands</h2>
35
36
37 <h3>pick (P)</h3>
38 <p>Pick the whole row (object is available as "_")</p>
39
40 <h3>pickattr (Shift-P)</h3>
41 <p>Pick the attribute under the cursor</p>
42
43 <h3>pickallattrs (Shift-C)</h3>
44 <p>Pick the complete column under the cursor (i.e. the attribute under the
45 cursor) from all currently fetched objects. These attributes will be returned
46 as a list.</p>
47
48 <h3>pickinput (I)</h3>
49 <p>Pick the current row as next input line in IPython. Additionally the row is stored as "_"</p>
50
51 <h3>pickinputattr (Shift-I)</h3>
52 <p>Pick the attribute under the cursor as next input line in IPython. Additionally the row is stored as "_"</p>
53
54 <h3>enter (E)</h3>
55 <p>Enter the object under the cursor. (what this mean depends on the object
56 itself, i.e. how it implements iteration). This opens a new browser 'level'.</p>
57
58 <h3>enterattr (Shift-E)</h3>
59 <p>Enter the attribute under the cursor.</p>
60
61 <h3>detail (D)</h3>
62 <p>Show a detail view of the object under the cursor. This shows the name,
63 type, doc string and value of the object attributes (and it might show more
64 attributes than in the list view, depending on the object).</p>
65
66 <h3>detailattr (Shift-D)</h3>
67 <p>Show a detail view of the attribute under the cursor.</p>
68
69 <h3>pickrows (M)</h3>
70 <p>Pick multiple selected rows (M)</p>
71
72 <h3>pickrowsattr (CTRL-M)</h3>
73 <p>From multiple selected rows pick the cells matching the attribute the cursor is in (CTRL-M)</p>
74
75 <h3>find (CTRL-F)</h3>
76 <p>Find text</p>
77
78 <h3>find_expression (CTRL-Shift-F)</h3>
79 <p>Find entries matching an expression</p>
80
81 <h3>find_next (F3)</h3>
82 <p>Find next occurrence</p>
83
84 <h3>find_previous (Shift-F3)</h3>
85 <p>Find previous occurrence</p>
86
87 <h3>sortattrasc (V)</h3>
88 <p>Sort the objects (in ascending order) using the attribute under the cursor as the sort key.</p>
89
90 <h3>sortattrdesc (Shift-V)</h3>
91 <p>Sort the objects (in descending order) using the attribute under the cursor as the sort key.</p>
92
93 <h3>refresh_once (R, F5)</h3>
94 <p>Refreshes the display by restarting the iterator</p>
95
96 <h3>refresh_every_second</h3>
97 <p>Refreshes the display by restarting the iterator every second until stopped by stop_refresh.</p>
98
99 <h3>refresh_interval</h3>
100 <p>Refreshes the display by restarting the iterator every X ms (X is a custom interval set by the user) until stopped by stop_refresh.</p>
101
102 <h3>stop_refresh</h3>
103 <p>Stops all refresh timers.</p>
104
105 <h3>leave (Backspace, DEL, X)</h3>
106 <p>Close current tab (and all the tabs to the right of the current one).</h3>
107
108 <h3>quit (ESC,Q)</h3>
109 <p>Quit igrid and return to the IPython prompt.</p>
110
111
112 <h2>Navigation</h2>
113
114
115 <h3>Jump to the last column of the current row (END, CTRL-E, CTRL-Right)</h3>
116
117 <h3>Jump to the first column of the current row (HOME, CTRL-A, CTRL-Left)</h3>
118
119 <h3>Move the cursor one column to the left (&lt;)</h3>
120
121 <h3>Move the cursor one column to the right (&gt;)</h3>
122
123 <h3>Jump to the first row in the current column (CTRL-Up)</h3>
124
125 <h3>Jump to the last row in the current column (CTRL-Down)</h3>
126
127 </body>
128 </html>
129
130 """
131
132
133 class IGridRenderer(wx.grid.PyGridCellRenderer):
134 """
135 This is a custom renderer for our IGridGrid
136 """
137 def __init__(self, table):
138 self.maxchars = 200
139 self.table = table
140 self.colormap = (
141 ( 0, 0, 0),
142 (174, 0, 0),
143 ( 0, 174, 0),
144 (174, 174, 0),
145 ( 0, 0, 174),
146 (174, 0, 174),
147 ( 0, 174, 174),
148 ( 64, 64, 64)
149 )
150
151 wx.grid.PyGridCellRenderer.__init__(self)
152
153 def _getvalue(self, row, col):
154 try:
155 value = self.table._displayattrs[col].value(self.table.items[row])
156 (align, width, text) = ipipe.xformat(value, "cell", self.maxchars)
157 except Exception, exc:
158 (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars)
159 return (align, text)
160
161 def GetBestSize(self, grid, attr, dc, row, col):
162 text = grid.GetCellValue(row, col)
163 (align, text) = self._getvalue(row, col)
164 dc.SetFont(attr.GetFont())
165 (w, h) = dc.GetTextExtent(str(text))
166 return wx.Size(min(w+2, 600), h+2) # add border
167
168 def Draw(self, grid, attr, dc, rect, row, col, isSelected):
169 """
170 Takes care of drawing everything in the cell; aligns the text
171 """
172 text = grid.GetCellValue(row, col)
173 (align, text) = self._getvalue(row, col)
174 if isSelected:
175 bg = grid.GetSelectionBackground()
176 else:
177 bg = ["white", (240, 240, 240)][row%2]
178 dc.SetTextBackground(bg)
179 dc.SetBrush(wx.Brush(bg, wx.SOLID))
180 dc.SetPen(wx.TRANSPARENT_PEN)
181 dc.SetFont(attr.GetFont())
182 dc.DrawRectangleRect(rect)
183 dc.SetClippingRect(rect)
184 # Format the text
185 if align == -1: # left alignment
186 (width, height) = dc.GetTextExtent(str(text))
187 x = rect[0]+1
188 y = rect[1]+0.5*(rect[3]-height)
189
190 for (style, part) in text:
191 if isSelected:
192 fg = grid.GetSelectionForeground()
193 else:
194 fg = self.colormap[style.fg]
195 dc.SetTextForeground(fg)
196 (w, h) = dc.GetTextExtent(part)
197 dc.DrawText(part, x, y)
198 x += w
199 elif align == 0: # center alignment
200 (width, height) = dc.GetTextExtent(str(text))
201 x = rect[0]+0.5*(rect[2]-width)
202 y = rect[1]+0.5*(rect[3]-height)
203 for (style, part) in text:
204 if isSelected:
205 fg = grid.GetSelectionForeground()
206 else:
207 fg = self.colormap[style.fg]
208 dc.SetTextForeground(fg)
209 (w, h) = dc.GetTextExtent(part)
210 dc.DrawText(part, x, y)
211 x += w
212 else: # right alignment
213 (width, height) = dc.GetTextExtent(str(text))
214 x = rect[0]+rect[2]-1
215 y = rect[1]+0.5*(rect[3]-height)
216 for (style, part) in reversed(text):
217 (w, h) = dc.GetTextExtent(part)
218 x -= w
219 if isSelected:
220 fg = grid.GetSelectionForeground()
221 else:
222 fg = self.colormap[style.fg]
223 dc.SetTextForeground(fg)
224 dc.DrawText(part, x, y)
225 dc.DestroyClippingRegion()
226
227 def Clone(self):
228 return IGridRenderer(self.table)
229
230
231 class IGridTable(wx.grid.PyGridTableBase):
232 # The data table for the ``IGridGrid``. Some dirty tricks were used here:
233 # ``GetValue()`` does not get any values (or at least it does not return
234 # anything, accessing the values is done by the renderer)
235 # but rather tries to fetch the objects which were requested into the table.
236 # General behaviour is: Fetch the first X objects. If the user scrolls down
237 # to the last object another bunch of X objects is fetched (if possible)
238 def __init__(self, input, fontsize, *attrs):
239 wx.grid.PyGridTableBase.__init__(self)
240 self.input = input
241 self.iterator = ipipe.xiter(input)
242 self.items = []
243 self.attrs = [ipipe.upgradexattr(attr) for attr in attrs]
244 self._displayattrs = self.attrs[:]
245 self._displayattrset = set(self.attrs)
246 self.fontsize = fontsize
247 self._fetch(1)
248 self.timer = wx.Timer()
249 self.timer.Bind(wx.EVT_TIMER, self.refresh_content)
250
251 def GetAttr(self, *args):
252 attr = wx.grid.GridCellAttr()
253 attr.SetFont(wx.Font(self.fontsize, wx.TELETYPE, wx.NORMAL, wx.NORMAL))
254 return attr
255
256 def GetNumberRows(self):
257 return len(self.items)
258
259 def GetNumberCols(self):
260 return len(self._displayattrs)
261
262 def GetColLabelValue(self, col):
263 if col < len(self._displayattrs):
264 return self._displayattrs[col].name()
265 else:
266 return ""
267
268 def GetRowLabelValue(self, row):
269 return str(row)
270
271 def IsEmptyCell(self, row, col):
272 return False
273
274 def _append(self, item):
275 self.items.append(item)
276 # Nothing to do if the set of attributes has been fixed by the user
277 if not self.attrs:
278 for attr in ipipe.xattrs(item):
279 attr = ipipe.upgradexattr(attr)
280 if attr not in self._displayattrset:
281 self._displayattrs.append(attr)
282 self._displayattrset.add(attr)
283
284 def _fetch(self, count):
285 # Try to fill ``self.items`` with at least ``count`` objects.
286 have = len(self.items)
287 while self.iterator is not None and have < count:
288 try:
289 item = self.iterator.next()
290 except StopIteration:
291 self.iterator = None
292 break
293 except (KeyboardInterrupt, SystemExit):
294 raise
295 except Exception, exc:
296 have += 1
297 self._append(exc)
298 self.iterator = None
299 break
300 else:
301 have += 1
302 self._append(item)
303
304 def GetValue(self, row, col):
305 # some kind of dummy-function: does not return anything but "";
306 # (The value isn't use anyway)
307 # its main task is to trigger the fetch of new objects
308 sizing_needed = False
309 had_cols = len(self._displayattrs)
310 had_rows = len(self.items)
311 if row == had_rows - 1 and self.iterator is not None:
312 self._fetch(row + 20)
313 sizing_needed = True
314 have_rows = len(self.items)
315 have_cols = len(self._displayattrs)
316 if have_rows > had_rows:
317 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED, have_rows - had_rows)
318 self.GetView().ProcessTableMessage(msg)
319 sizing_needed = True
320 if row >= have_rows:
321 return ""
322 if have_cols != had_cols:
323 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED, have_cols - had_cols)
324 self.GetView().ProcessTableMessage(msg)
325 sizing_needed = True
326 if sizing_needed:
327 self.GetView().AutoSizeColumns(False)
328 return ""
329
330 def SetValue(self, row, col, value):
331 pass
332
333 def refresh_content(self, event):
334 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, 0, self.GetNumberRows())
335 self.GetView().ProcessTableMessage(msg)
336 self.iterator = ipipe.xiter(self.input)
337 self.items = []
338 self.attrs = [] # _append will calculate new displayattrs
339 self._fetch(1) # fetch one...
340 if self.items:
341 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED, 1)
342 self.GetView().ProcessTableMessage(msg)
343 self.GetValue(0, 0) # and trigger "fetch next 20"
344 item = self.items[0]
345 self.GetView().AutoSizeColumns(False)
346 panel = self.GetView().GetParent()
347 nb = panel.GetParent()
348 current = nb.GetSelection()
349 if nb.GetPage(current) == panel:
350 self.GetView().set_footer(item)
351
352 class IGridGrid(wx.grid.Grid):
353 # The actual grid
354 # all methods for selecting/sorting/picking/... data are implemented here
355 def __init__(self, panel, input, *attrs):
356 wx.grid.Grid.__init__(self, panel)
357 fontsize = 9
358 self.input = input
359 self.table = IGridTable(self.input, fontsize, *attrs)
360 self.SetTable(self.table, True)
361 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
362 self.SetDefaultRenderer(IGridRenderer(self.table))
363 self.EnableEditing(False)
364 self.Bind(wx.EVT_KEY_DOWN, self.key_pressed)
365 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.cell_doubleclicked)
366 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.cell_leftclicked)
367 self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_DCLICK, self.label_doubleclicked)
368 self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.on_label_leftclick)
369 self.Bind(wx.grid.EVT_GRID_RANGE_SELECT, self._on_selected_range)
370 self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self._on_selected_cell)
371 self.current_selection = set()
372 self.maxchars = 200
373
374 def on_label_leftclick(self, event):
375 event.Skip()
376
377 def error_output(self, text):
378 wx.Bell()
379 frame = self.GetParent().GetParent().GetParent()
380 frame.SetStatusText(str(text))
381
382 def _on_selected_range(self, event):
383 # Internal update to the selection tracking lists
384 if event.Selecting():
385 # adding to the list...
386 self.current_selection.update(xrange(event.GetTopRow(), event.GetBottomRow()+1))
387 else:
388 # removal from list
389 for index in xrange(event.GetTopRow(), event.GetBottomRow()+1):
390 self.current_selection.discard(index)
391 event.Skip()
392
393 def _on_selected_cell(self, event):
394 # Internal update to the selection tracking list
395 self.current_selection = set([event.GetRow()])
396 event.Skip()
397
398 def sort(self, key, reverse=False):
399 """
400 Sort the current list of items using the key function ``key``. If
401 ``reverse`` is true the sort order is reversed.
402 """
403 row = self.GetGridCursorRow()
404 col = self.GetGridCursorCol()
405 curitem = self.table.items[row] # Remember where the cursor is now
406 # Sort items
407 def realkey(item):
408 try:
409 return key(item)
410 except (KeyboardInterrupt, SystemExit):
411 raise
412 except Exception:
413 return None
414 try:
415 self.table.items = ipipe.deque(sorted(self.table.items, key=realkey, reverse=reverse))
416 except TypeError, exc:
417 self.error_output("Exception encountered: %s" % exc)
418 return
419 # Find out where the object under the cursor went
420 for (i, item) in enumerate(self.table.items):
421 if item is curitem:
422 self.SetGridCursor(i,col)
423 self.MakeCellVisible(i,col)
424 self.Refresh()
425
426 def sortattrasc(self):
427 """
428 Sort in ascending order; sorting criteria is the current attribute
429 """
430 col = self.GetGridCursorCol()
431 attr = self.table._displayattrs[col]
432 frame = self.GetParent().GetParent().GetParent()
433 if attr is ipipe.noitem:
434 self.error_output("no column under cursor")
435 return
436 frame.SetStatusText("sort by %s (ascending)" % attr.name())
437 def key(item):
438 try:
439 return attr.value(item)
440 except (KeyboardInterrupt, SystemExit):
441 raise
442 except Exception:
443 return None
444 self.sort(key)
445
446 def sortattrdesc(self):
447 """
448 Sort in descending order; sorting criteria is the current attribute
449 """
450 col = self.GetGridCursorCol()
451 attr = self.table._displayattrs[col]
452 frame = self.GetParent().GetParent().GetParent()
453 if attr is ipipe.noitem:
454 self.error_output("no column under cursor")
455 return
456 frame.SetStatusText("sort by %s (descending)" % attr.name())
457 def key(item):
458 try:
459 return attr.value(item)
460 except (KeyboardInterrupt, SystemExit):
461 raise
462 except Exception:
463 return None
464 self.sort(key, reverse=True)
465
466 def label_doubleclicked(self, event):
467 row = event.GetRow()
468 col = event.GetCol()
469 if col == -1:
470 self.enter(row)
471
472 def _getvalue(self, row, col):
473 """
474 Gets the text which is displayed at ``(row, col)``
475 """
476 try:
477 value = self.table._displayattrs[col].value(self.table.items[row])
478 (align, width, text) = ipipe.xformat(value, "cell", self.maxchars)
479 except IndexError:
480 raise IndexError
481 except Exception, exc:
482 (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars)
483 return text
484
485 def searchexpression(self, searchexp, startrow=None, search_forward=True ):
486 """
487 Find by expression
488 """
489 frame = self.GetParent().GetParent().GetParent()
490 if searchexp:
491 if search_forward:
492 if not startrow:
493 row = self.GetGridCursorRow()+1
494 else:
495 row = startrow + 1
496 while True:
497 try:
498 foo = self.table.GetValue(row, 0)
499 item = self.table.items[row]
500 try:
501 globals = ipipe.getglobals(None)
502 if eval(searchexp, globals, ipipe.AttrNamespace(item)):
503 self.SetGridCursor(row, 0) # found something
504 self.MakeCellVisible(row, 0)
505 break
506 except (KeyboardInterrupt, SystemExit):
507 raise
508 except Exception, exc:
509 frame.SetStatusText(str(exc))
510 wx.Bell()
511 break # break on error
512 except IndexError:
513 return
514 row += 1
515 else:
516 if not startrow:
517 row = self.GetGridCursorRow() - 1
518 else:
519 row = startrow - 1
520 while True:
521 try:
522 foo = self.table.GetValue(row, 0)
523 item = self.table.items[row]
524 try:
525 globals = ipipe.getglobals(None)
526 if eval(searchexp, globals, ipipe.AttrNamespace(item)):
527 self.SetGridCursor(row, 0) # found something
528 self.MakeCellVisible(row, 0)
529 break
530 except (KeyboardInterrupt, SystemExit):
531 raise
532 except Exception, exc:
533 frame.SetStatusText(str(exc))
534 wx.Bell()
535 break # break on error
536 except IndexError:
537 return
538 row -= 1
539
540
541 def search(self, searchtext, startrow=None, startcol=None, search_forward=True):
542 """
543 search for ``searchtext``, starting in ``(startrow, startcol)``;
544 if ``search_forward`` is true the direction is "forward"
545 """
546 searchtext = searchtext.lower()
547 if search_forward:
548 if startrow is not None and startcol is not None:
549 row = startrow
550 else:
551 startcol = self.GetGridCursorCol() + 1
552 row = self.GetGridCursorRow()
553 if startcol >= self.GetNumberCols():
554 startcol = 0
555 row += 1
556 while True:
557 for col in xrange(startcol, self.table.GetNumberCols()):
558 try:
559 foo = self.table.GetValue(row, col)
560 text = self._getvalue(row, col)
561 if searchtext in text.string().lower():
562 self.SetGridCursor(row, col)
563 self.MakeCellVisible(row, col)
564 return
565 except IndexError:
566 return
567 startcol = 0
568 row += 1
569 else:
570 if startrow is not None and startcol is not None:
571 row = startrow
572 else:
573 startcol = self.GetGridCursorCol() - 1
574 row = self.GetGridCursorRow()
575 if startcol < 0:
576 startcol = self.GetNumberCols() - 1
577 row -= 1
578 while True:
579 for col in xrange(startcol, -1, -1):
580 try:
581 foo = self.table.GetValue(row, col)
582 text = self._getvalue(row, col)
583 if searchtext in text.string().lower():
584 self.SetGridCursor(row, col)
585 self.MakeCellVisible(row, col)
586 return
587 except IndexError:
588 return
589 startcol = self.table.GetNumberCols()-1
590 row -= 1
591
592 def key_pressed(self, event):
593 """
594 Maps pressed keys to functions
595 """
596 frame = self.GetParent().GetParent().GetParent()
597 frame.SetStatusText("")
598 sh = event.ShiftDown()
599 ctrl = event.ControlDown()
600
601 keycode = event.GetKeyCode()
602 if keycode == ord("P"):
603 row = self.GetGridCursorRow()
604 if sh:
605 col = self.GetGridCursorCol()
606 self.pickattr(row, col)
607 else:
608 self.pick(row)
609 elif keycode == ord("M"):
610 if ctrl:
611 col = self.GetGridCursorCol()
612 self.pickrowsattr(sorted(self.current_selection), col)
613 else:
614 self.pickrows(sorted(self.current_selection))
615 elif keycode in (wx.WXK_BACK, wx.WXK_DELETE, ord("X")) and not (ctrl or sh):
616 self.delete_current_notebook()
617 elif keycode in (ord("E"), ord("\r")):
618 row = self.GetGridCursorRow()
619 if sh:
620 col = self.GetGridCursorCol()
621 self.enterattr(row, col)
622 else:
623 self.enter(row)
624 elif keycode == ord("E") and ctrl:
625 row = self.GetGridCursorRow()
626 self.SetGridCursor(row, self.GetNumberCols()-1)
627 elif keycode == wx.WXK_HOME or (keycode == ord("A") and ctrl):
628 row = self.GetGridCursorRow()
629 self.SetGridCursor(row, 0)
630 elif keycode == ord("C") and sh:
631 col = self.GetGridCursorCol()
632 attr = self.table._displayattrs[col]
633 result = []
634 for i in xrange(self.GetNumberRows()):
635 result.append(self.table._displayattrs[col].value(self.table.items[i]))
636 self.quit(result)
637 elif keycode in (wx.WXK_ESCAPE, ord("Q")) and not (ctrl or sh):
638 self.quit()
639 elif keycode == ord("<"):
640 row = self.GetGridCursorRow()
641 col = self.GetGridCursorCol()
642 if not event.ShiftDown():
643 newcol = col - 1
644 if newcol >= 0:
645 self.SetGridCursor(row, col - 1)
646 else:
647 newcol = col + 1
648 if newcol < self.GetNumberCols():
649 self.SetGridCursor(row, col + 1)
650 elif keycode == ord("D"):
651 col = self.GetGridCursorCol()
652 row = self.GetGridCursorRow()
653 if not sh:
654 self.detail(row, col)
655 else:
656 self.detail_attr(row, col)
657 elif keycode == ord("F") and ctrl:
658 if sh:
659 frame.enter_searchexpression(event)
660 else:
661 frame.enter_searchtext(event)
662 elif keycode == wx.WXK_F3:
663 if sh:
664 frame.find_previous(event)
665 else:
666 frame.find_next(event)
667 elif keycode == ord("V"):
668 if sh:
669 self.sortattrdesc()
670 else:
671 self.sortattrasc()
672 elif keycode == wx.WXK_DOWN:
673 row = self.GetGridCursorRow()
674 try:
675 item = self.table.items[row+1]
676 except IndexError:
677 item = self.table.items[row]
678 self.set_footer(item)
679 event.Skip()
680 elif keycode == wx.WXK_UP:
681 row = self.GetGridCursorRow()
682 if row >= 1:
683 item = self.table.items[row-1]
684 else:
685 item = self.table.items[row]
686 self.set_footer(item)
687 event.Skip()
688 elif keycode == wx.WXK_RIGHT:
689 row = self.GetGridCursorRow()
690 item = self.table.items[row]
691 self.set_footer(item)
692 event.Skip()
693 elif keycode == wx.WXK_LEFT:
694 row = self.GetGridCursorRow()
695 item = self.table.items[row]
696 self.set_footer(item)
697 event.Skip()
698 elif keycode == ord("R") or keycode == wx.WXK_F5:
699 self.table.refresh_content(event)
700 elif keycode == ord("I"):
701 row = self.GetGridCursorRow()
702 if not sh:
703 self.pickinput(row)
704 else:
705 col = self.GetGridCursorCol()
706 self.pickinputattr(row, col)
707 else:
708 event.Skip()
709
710 def delete_current_notebook(self):
711 """
712 deletes the current notebook tab
713 """
714 panel = self.GetParent()
715 nb = panel.GetParent()
716 current = nb.GetSelection()
717 count = nb.GetPageCount()
718 if count > 1:
719 for i in xrange(count-1, current-1, -1):
720 nb.DeletePage(i)
721 nb.GetCurrentPage().grid.SetFocus()
722 else:
723 frame = nb.GetParent()
724 frame.SetStatusText("This is the last level!")
725
726 def _doenter(self, value, *attrs):
727 """
728 "enter" a special item resulting in a new notebook tab
729 """
730 panel = self.GetParent()
731 nb = panel.GetParent()
732 frame = nb.GetParent()
733 current = nb.GetSelection()
734 count = nb.GetPageCount()
735 try: # if we want to enter something non-iterable, e.g. a function
736 if current + 1 == count and value is not self.input: # we have an event in the last tab
737 frame._add_notebook(value, *attrs)
738 elif value != self.input: # we have to delete all tabs newer than [panel] first
739 for i in xrange(count-1, current, -1): # some tabs don't close if we don't close in *reverse* order
740 nb.DeletePage(i)
741 frame._add_notebook(value)
742 except TypeError, exc:
743 if exc.__class__.__module__ == "exceptions":
744 msg = "%s: %s" % (exc.__class__.__name__, exc)
745 else:
746 msg = "%s.%s: %s" % (exc.__class__.__module__, exc.__class__.__name__, exc)
747 frame.SetStatusText(msg)
748
749 def enterattr(self, row, col):
750 try:
751 attr = self.table._displayattrs[col]
752 value = attr.value(self.table.items[row])
753 except Exception, exc:
754 self.error_output(str(exc))
755 else:
756 self._doenter(value)
757
758 def set_footer(self, item):
759 frame = self.GetParent().GetParent().GetParent()
760 frame.SetStatusText(" ".join([str(text) for (style, text) in ipipe.xformat(item, "footer", 20)[2]]), 0)
761
762 def enter(self, row):
763 try:
764 value = self.table.items[row]
765 except Exception, exc:
766 self.error_output(str(exc))
767 else:
768 self._doenter(value)
769
770 def detail(self, row, col):
771 """
772 shows a detail-view of the current cell
773 """
774 try:
775 attr = self.table._displayattrs[col]
776 item = self.table.items[row]
777 except Exception, exc:
778 self.error_output(str(exc))
779 else:
780 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
781 self._doenter(attrs)
782
783 def detail_attr(self, row, col):
784 try:
785 attr = self.table._displayattrs[col]
786 item = attr.value(self.table.items[row])
787 except Exception, exc:
788 self.error_output(str(exc))
789 else:
790 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
791 self._doenter(attrs)
792
793 def quit(self, result=None):
794 """
795 quit
796 """
797 frame = self.GetParent().GetParent().GetParent()
798 if frame.helpdialog:
799 frame.helpdialog.Destroy()
800 app = frame.parent
801 if app is not None:
802 app.result = result
803 frame.Close()
804 frame.Destroy()
805
806 def cell_doubleclicked(self, event):
807 self.enterattr(event.GetRow(), event.GetCol())
808 event.Skip()
809
810 def cell_leftclicked(self, event):
811 row = event.GetRow()
812 item = self.table.items[row]
813 self.set_footer(item)
814 event.Skip()
815
816 def pick(self, row):
817 """
818 pick a single row and return to the IPython prompt
819 """
820 try:
821 value = self.table.items[row]
822 except Exception, exc:
823 self.error_output(str(exc))
824 else:
825 self.quit(value)
826
827 def pickinput(self, row):
828 try:
829 value = self.table.items[row]
830 except Exception, exc:
831 self.error_output(str(exc))
832 else:
833 api = ipapi.get()
834 api.set_next_input(str(value))
835 self.quit(value)
836
837 def pickinputattr(self, row, col):
838 try:
839 attr = self.table._displayattrs[col]
840 value = attr.value(self.table.items[row])
841 except Exception, exc:
842 self.error_output(str(exc))
843 else:
844 api = ipapi.get()
845 api.set_next_input(str(value))
846 self.quit(value)
847
848 def pickrows(self, rows):
849 """
850 pick multiple rows and return to the IPython prompt
851 """
852 try:
853 value = [self.table.items[row] for row in rows]
854 except Exception, exc:
855 self.error_output(str(exc))
856 else:
857 self.quit(value)
858
859 def pickrowsattr(self, rows, col):
860 """"
861 pick one column from multiple rows
862 """
863 values = []
864 try:
865 attr = self.table._displayattrs[col]
866 for row in rows:
867 try:
868 values.append(attr.value(self.table.items[row]))
869 except (SystemExit, KeyboardInterrupt):
870 raise
871 except Exception:
872 raise #pass
873 except Exception, exc:
874 self.error_output(str(exc))
875 else:
876 self.quit(values)
877
878 def pickattr(self, row, col):
879 try:
880 attr = self.table._displayattrs[col]
881 value = attr.value(self.table.items[row])
882 except Exception, exc:
883 self.error_output(str(exc))
884 else:
885 self.quit(value)
886
887
888 class IGridPanel(wx.Panel):
889 # Each IGridPanel contains an IGridGrid
890 def __init__(self, parent, input, *attrs):
891 wx.Panel.__init__(self, parent, -1)
892 self.grid = IGridGrid(self, input, *attrs)
893 self.grid.FitInside()
894 sizer = wx.BoxSizer(wx.VERTICAL)
895 sizer.Add(self.grid, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)
896 self.SetSizer(sizer)
897 sizer.Fit(self)
898 sizer.SetSizeHints(self)
899
900
901 class IGridHTMLHelp(wx.Frame):
902 def __init__(self, parent, title, size):
903 wx.Frame.__init__(self, parent, -1, title, size=size)
904 html = wx.html.HtmlWindow(self)
905 if "gtk2" in wx.PlatformInfo:
906 html.SetStandardFonts()
907 html.SetPage(help)
908
909
910 class IGridFrame(wx.Frame):
911 maxtitlelen = 30
912
913 def __init__(self, parent, input):
914 title = " ".join([str(text) for (style, text) in ipipe.xformat(input, "header", 20)[2]])
915 wx.Frame.__init__(self, None, title=title, size=(640, 480))
916 self.menubar = wx.MenuBar()
917 self.menucounter = 100
918 self.m_help = wx.Menu()
919 self.m_search = wx.Menu()
920 self.m_sort = wx.Menu()
921 self.m_refresh = wx.Menu()
922 self.notebook = wx.Notebook(self, -1, style=0)
923 self.statusbar = self.CreateStatusBar(1, wx.ST_SIZEGRIP)
924 self.statusbar.SetFieldsCount(2)
925 self.SetStatusWidths([-1, 200])
926 self.parent = parent
927 self._add_notebook(input)
928 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
929 self.makemenu(self.m_sort, "&Sort (asc)\tV", "Sort ascending", self.sortasc)
930 self.makemenu(self.m_sort, "Sort (&desc)\tShift-V", "Sort descending", self.sortdesc)
931 self.makemenu(self.m_help, "&Help\tF1", "Help", self.display_help)
932 # self.makemenu(self.m_help, "&Show help in browser", "Show help in browser", self.display_help_in_browser)
933 self.makemenu(self.m_search, "&Find text\tCTRL-F", "Find text", self.enter_searchtext)
934 self.makemenu(self.m_search, "Find by &expression\tCTRL-Shift-F", "Find by expression", self.enter_searchexpression)
935 self.makemenu(self.m_search, "Find &next\tF3", "Find next", self.find_next)
936 self.makemenu(self.m_search, "Find &previous\tShift-F3", "Find previous", self.find_previous)
937 self.makemenu(self.m_refresh, "&Refresh once \tF5", "Refresh once", self.refresh_once)
938 self.makemenu(self.m_refresh, "Refresh every &1s", "Refresh every second", self.refresh_every_second)
939 self.makemenu(self.m_refresh, "Refresh every &X seconds", "Refresh every X seconds", self.refresh_interval)
940 self.makemenu(self.m_refresh, "&Stop all refresh timers", "Stop refresh timers", self.stop_refresh)
941 self.menubar.Append(self.m_search, "&Find")
942 self.menubar.Append(self.m_sort, "&Sort")
943 self.menubar.Append(self.m_refresh, "&Refresh")
944 self.menubar.Append(self.m_help, "&Help")
945 self.SetMenuBar(self.menubar)
946 self.searchtext = ""
947 self.searchexpression = ""
948 self.helpdialog = None
949 self.refresh_interval = 1000
950 self.SetStatusText("Refreshing inactive", 1)
951
952 def refresh_once(self, event):
953 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
954 table.refresh_content(event)
955
956 def refresh_interval(self, event):
957 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
958 dlg = wx.TextEntryDialog(self, "Enter refresh interval (milliseconds):", "Refresh timer:", defaultValue=str(self.refresh_interval))
959 if dlg.ShowModal() == wx.ID_OK:
960 try:
961 milliseconds = int(dlg.GetValue())
962 except ValueError, exc:
963 self.SetStatusText(str(exc))
964 else:
965 table.timer.Start(milliseconds=milliseconds, oneShot=False)
966 self.SetStatusText("Refresh timer set to %s ms" % milliseconds)
967 self.SetStatusText("Refresh interval: %s ms" % milliseconds, 1)
968 self.refresh_interval = milliseconds
969 dlg.Destroy()
970
971 def stop_refresh(self, event):
972 for i in xrange(self.notebook.GetPageCount()):
973 nb = self.notebook.GetPage(i)
974 nb.grid.table.timer.Stop()
975 self.SetStatusText("Refreshing inactive", 1)
976
977 def refresh_every_second(self, event):
978 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
979 table.timer.Start(milliseconds=1000, oneShot=False)
980 self.SetStatusText("Refresh interval: 1000 ms", 1)
981
982 def sortasc(self, event):
983 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
984 grid.sortattrasc()
985
986 def sortdesc(self, event):
987 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
988 grid.sortattrdesc()
989
990 def find_previous(self, event):
991 """
992 find previous occurrences
993 """
994 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
995 if self.searchtext:
996 row = grid.GetGridCursorRow()
997 col = grid.GetGridCursorCol()
998 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
999 if col-1 >= 0:
1000 grid.search(self.searchtext, row, col-1, False)
1001 else:
1002 grid.search(self.searchtext, row-1, grid.table.GetNumberCols()-1, False)
1003 elif self.searchexpression:
1004 self.SetStatusText("Search mode: expression; looking for %s" % repr(self.searchexpression)[2:-1])
1005 grid.searchexpression(searchexp=self.searchexpression, search_forward=False)
1006 else:
1007 self.SetStatusText("No search yet: please enter search-text or -expression")
1008
1009 def find_next(self, event):
1010 """
1011 find the next occurrence
1012 """
1013 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
1014 if self.searchtext != "":
1015 row = grid.GetGridCursorRow()
1016 col = grid.GetGridCursorCol()
1017 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
1018 if col+1 < grid.table.GetNumberCols():
1019 grid.search(self.searchtext, row, col+1)
1020 else:
1021 grid.search(self.searchtext, row+1, 0)
1022 elif self.searchexpression != "":
1023 self.SetStatusText('Search mode: expression; looking for %s' % repr(self.searchexpression)[2:-1])
1024 grid.searchexpression(searchexp=self.searchexpression)
1025 else:
1026 self.SetStatusText("No search yet: please enter search-text or -expression")
1027
1028 def display_help(self, event):
1029 """
1030 Display a help dialog
1031 """
1032 if self.helpdialog:
1033 self.helpdialog.Destroy()
1034 self.helpdialog = IGridHTMLHelp(None, title="Help", size=wx.Size(600,400))
1035 self.helpdialog.Show()
1036
1037 def display_help_in_browser(self, event):
1038 """
1039 Show the help-HTML in a browser (as a ``HtmlWindow`` does not understand
1040 CSS this looks better)
1041 """
1042 filename = urllib.pathname2url(os.path.abspath(os.path.join(os.path.dirname(__file__), "igrid_help.html")))
1043 if not filename.startswith("file"):
1044 filename = "file:" + filename
1045 webbrowser.open(filename, new=1, autoraise=True)
1046
1047 def enter_searchexpression(self, event):
1048 dlg = wx.TextEntryDialog(self, "Find:", "Find matching expression:", defaultValue=self.searchexpression)
1049 if dlg.ShowModal() == wx.ID_OK:
1050 self.searchexpression = dlg.GetValue()
1051 self.searchtext = ""
1052 self.SetStatusText('Search mode: expression; looking for %s' % repr(self.searchexpression)[2:-1])
1053 self.notebook.GetPage(self.notebook.GetSelection()).grid.searchexpression(self.searchexpression)
1054 dlg.Destroy()
1055
1056 def makemenu(self, menu, label, help, cmd):
1057 menu.Append(self.menucounter, label, help)
1058 self.Bind(wx.EVT_MENU, cmd, id=self.menucounter)
1059 self.menucounter += 1
1060
1061 def _add_notebook(self, input, *attrs):
1062 # Adds another notebook which has the starting object ``input``
1063 panel = IGridPanel(self.notebook, input, *attrs)
1064 text = str(ipipe.xformat(input, "header", self.maxtitlelen)[2])
1065 if len(text) >= self.maxtitlelen:
1066 text = text[:self.maxtitlelen].rstrip(".") + "..."
1067 self.notebook.AddPage(panel, text, True)
1068 panel.grid.SetFocus()
1069 self.Layout()
1070
1071 def OnCloseWindow(self, event):
1072 self.Destroy()
1073
1074 def enter_searchtext(self, event):
1075 # Displays a dialog asking for the searchtext
1076 dlg = wx.TextEntryDialog(self, "Find:", "Find in list", defaultValue=self.searchtext)
1077 if dlg.ShowModal() == wx.ID_OK:
1078 self.searchtext = dlg.GetValue()
1079 self.searchexpression = ""
1080 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
1081 self.notebook.GetPage(self.notebook.GetSelection()).grid.search(self.searchtext)
1082 dlg.Destroy()
1083
1084
1085 class App(wx.App):
1086 def __init__(self, input):
1087 self.input = input
1088 self.result = None # Result to be returned to IPython. Set by quit().
1089 wx.App.__init__(self)
1090
1091 def OnInit(self):
1092 frame = IGridFrame(self, self.input)
1093 frame.Show()
1094 self.SetTopWindow(frame)
1095 frame.Raise()
1096 return True
1097
1098
1099 class igrid(ipipe.Display):
1100 """
1101 This is a wx-based display object that can be used instead of ``ibrowse``
1102 (which is curses-based) or ``idump`` (which simply does a print).
1103 """
1104
1105 if wx.VERSION < (2, 7):
1106 def display(self):
1107 try:
1108 # Try to create a "standalone" frame. If this works we're probably
1109 # running with -wthread.
1110 # Note that this sets the parent of the frame to None, but we can't
1111 # pass a result object back to the shell anyway.
1112 frame = IGridFrame(None, self.input)
1113 frame.Show()
1114 frame.Raise()
1115 except wx.PyNoAppError:
1116 # There's no wx application yet => create one.
1117 app = App(self.input)
1118 app.MainLoop()
1119 return app.result
1120 else:
1121 # With wx 2.7 it gets simpler.
1122 def display(self):
1123 app = App(self.input)
1124 app.MainLoop()
1125 return app.result
1126
@@ -0,0 +1,45
1 body
2 {
3 background-color: #fff;
4 color: #000;
5 font-family: "Verdana", "Arial", "XHelvetica", "Helvetica", sans-serif;
6 padding: 20px 30px;
7 margin: 0px;
8 font-size: 11px;
9 }
10 h1
11 {
12 font-family: "Trebuchet MS", sans-serif;
13 font-size: 24px;
14 margin: -20px -30px 4px -30px;
15 padding: 6px 30px;
16 font-weight: normal;
17 border-bottom: 1px solid #000;
18 letter-spacing: 1px;
19 background-color: #666;
20 color: #fff;
21 }
22 h2
23 {
24 font-family: "Trebuchet MS", sans-serif;
25 font-size: 20px;
26 padding: 14px 0px 2px 0px;
27 margin: 0px;
28 font-weight: bold;
29 color: #333;
30 }
31 h3
32 {
33 font-family: "Trebuchet MS", sans-serif;
34 font-size: 14px;
35 padding: 12px 0px 2px 0px;
36 margin: 0px;
37 font-weight: bold;
38 color: #333;
39 }
40 p
41 {
42 line-height: 120%;
43 margin: 0px 0px 6px 0px;
44 padding: 0px;
45 }
@@ -0,0 +1,86
1 <?xml version='1.0' encoding='iso-8859-1'?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3 <html>
4 <head>
5 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
6 <link rel="stylesheet" href="igrid_help.css" type="text/css" />
7 <title>igrid help</title>
8 </head>
9 <body>
10 <h1>igrid help</h1>
11
12
13 <h2>Commands</h2>
14
15
16 <h3>pick (P)</h3>
17 <p>Pick the whole row (object is available as "_")</p>
18
19 <h3>pickattr (Shift-P)</h3>
20 <p>Pick the attribute under the cursor</p>
21
22 <h3>pickallattrs (Shift-C)</h3>
23 <p>Pick' the complete column under the cursor (i.e. the attribute under the
24 cursor) from all currently fetched objects. These attributes will be returned
25 as a list.</p>
26
27 <h3>enter (E)</h3>
28 <p>Enter the object under the cursor. (what this mean depends on the object
29 itself, i.e. how it implements iteration). This opens a new browser 'level'.</p>
30
31 <h3>enterattr (Shift-E)</h3>
32 <p>Enter the attribute under the cursor.</p>
33
34 <h3>detail (D)</h3>
35 <p>Show a detail view of the object under the cursor. This shows the name,
36 type, doc string and value of the object attributes (and it might show more
37 attributes than in the list view, depending on the object).</p>
38
39 <h3>detailattr (Shift-D)</h3>
40 <p>Show a detail view of the attribute under the cursor.</p>
41
42 <h3>pickrows (M)</h3>
43 <p>Pick multiple selected rows (M)</p>
44
45 <h3>pickrowsattr (CTRL-M)</h3>
46 <p>From multiple selected rows pick the cells matching the attribute the cursor is in (CTRL-M)</p>
47
48 <h3>find (CTRL-F)</h3>
49 <p>Find text</p>
50
51 <h3>find_next (F3)</h3>
52 <p>Find next occurrence of the searchtext</p>
53
54 <h3>find_previous (Shift-F3)</h3>
55 <p>Find previous occurrence of the searchtext </p>
56
57 <h3>sortattrasc (V)</h3>
58 <p>Sort the objects (in ascending order) using the attribute under the cursor as the sort key.</p>
59
60 <h3>sortattrdesc (Shift-V)</h3>
61 <p>Sort the objects (in descending order) using the attribute under the cursor as the sort key.</p>
62
63 <h3>leave (Backspace, DEL, X)</h3>
64 <p>Close current tab (and all the tabs to the right of the current one).</h3>
65
66 <h3>quit (ESC,Q)</h3>
67 <p>Quit igrid and return to the IPython prompt.</p>
68
69
70 <h2>Navigation</h2>
71
72
73 <h3>Jump to the last column of the current row (END, CTRL-E, CTRL-Right)</h3>
74
75 <h3>Jump to the first column of the current row (HOME, CTRL-A, CTRL-Left)</h3>
76
77 <h3>Move the cursor one column to the left (&lt;)</h3>
78
79 <h3>Move the cursor one column to the right (&gt;)</h3>
80
81 <h3>Jump to the first row in the current column (CTRL-Up)</h3>
82
83 <h3>Jump to the last row in the current column (CTRL-Down)</h3>
84
85 </body>
86 </html>
This diff has been collapsed as it changes many lines, (2292 lines changed) Show them Hide them
@@ -0,0 +1,2292
1 # -*- coding: iso-8859-1 -*-
2
3 """
4 ``ipipe`` provides classes to be used in an interactive Python session. Doing a
5 ``from ipipe import *`` is the preferred way to do this. The name of all
6 objects imported this way starts with ``i`` to minimize collisions.
7
8 ``ipipe`` supports "pipeline expressions", which is something resembling Unix
9 pipes. An example is::
10
11 >>> ienv | isort("key.lower()")
12
13 This gives a listing of all environment variables sorted by name.
14
15
16 There are three types of objects in a pipeline expression:
17
18 * ``Table``s: These objects produce items. Examples are ``ils`` (listing the
19 current directory, ``ienv`` (listing environment variables), ``ipwd`` (listing
20 user accounts) and ``igrp`` (listing user groups). A ``Table`` must be the
21 first object in a pipe expression.
22
23 * ``Pipe``s: These objects sit in the middle of a pipe expression. They
24 transform the input in some way (e.g. filtering or sorting it). Examples are:
25 ``ifilter`` (which filters the input pipe), ``isort`` (which sorts the input
26 pipe) and ``ieval`` (which evaluates a function or expression for each object
27 in the input pipe).
28
29 * ``Display``s: These objects can be put as the last object in a pipeline
30 expression. There are responsible for displaying the result of the pipeline
31 expression. If a pipeline expression doesn't end in a display object a default
32 display objects will be used. One example is ``ibrowse`` which is a ``curses``
33 based browser.
34
35
36 Adding support for pipeline expressions to your own objects can be done through
37 three extensions points (all of them optional):
38
39 * An object that will be displayed as a row by a ``Display`` object should
40 implement the method ``__xattrs__(self, mode)`` method or register an
41 implementation of the generic function ``xattrs``. For more info see ``xattrs``.
42
43 * When an object ``foo`` is displayed by a ``Display`` object, the generic
44 function ``xrepr`` is used.
45
46 * Objects that can be iterated by ``Pipe``s must iterable. For special cases,
47 where iteration for display is different than the normal iteration a special
48 implementation can be registered with the generic function ``xiter``. This
49 makes it possible to use dictionaries and modules in pipeline expressions,
50 for example::
51
52 >>> import sys
53 >>> sys | ifilter("isinstance(value, int)") | idump
54 key |value
55 api_version| 1012
56 dllhandle | 503316480
57 hexversion | 33817328
58 maxint |2147483647
59 maxunicode | 65535
60 >>> sys.modules | ifilter("_.value is not None") | isort("_.key.lower()")
61 ...
62
63 Note: The expression strings passed to ``ifilter()`` and ``isort()`` can
64 refer to the object to be filtered or sorted via the variable ``_`` and to any
65 of the attributes of the object, i.e.::
66
67 >>> sys.modules | ifilter("_.value is not None") | isort("_.key.lower()")
68
69 does the same as::
70
71 >>> sys.modules | ifilter("value is not None") | isort("key.lower()")
72
73 In addition to expression strings, it's possible to pass callables (taking
74 the object as an argument) to ``ifilter()``, ``isort()`` and ``ieval()``::
75
76 >>> sys | ifilter(lambda _:isinstance(_.value, int)) \
77 ... | ieval(lambda _: (_.key, hex(_.value))) | idump
78 0 |1
79 api_version|0x3f4
80 dllhandle |0x1e000000
81 hexversion |0x20402f0
82 maxint |0x7fffffff
83 maxunicode |0xffff
84 """
85
86 import sys, os, os.path, stat, glob, new, csv, datetime, types
87 import itertools, mimetypes, StringIO
88
89 try: # Python 2.3 compatibility
90 import collections
91 except ImportError:
92 deque = list
93 else:
94 deque = collections.deque
95
96 try: # Python 2.3 compatibility
97 set
98 except NameError:
99 import sets
100 set = sets.Set
101
102 try: # Python 2.3 compatibility
103 sorted
104 except NameError:
105 def sorted(iterator, key=None, reverse=False):
106 items = list(iterator)
107 if key is not None:
108 items.sort(lambda i1, i2: cmp(key(i1), key(i2)))
109 else:
110 items.sort()
111 if reverse:
112 items.reverse()
113 return items
114
115 try:
116 import pwd
117 except ImportError:
118 pwd = None
119
120 try:
121 import grp
122 except ImportError:
123 grp = None
124
125 from IPython.external import simplegeneric
126
127 import path
128
129 try:
130 from IPython import genutils, generics
131 except ImportError:
132 genutils = None
133 generics = None
134
135 from IPython import ipapi
136
137
138 __all__ = [
139 "ifile", "ils", "iglob", "iwalk", "ipwdentry", "ipwd", "igrpentry", "igrp",
140 "icsv", "ix", "ichain", "isort", "ifilter", "ieval", "ienum",
141 "ienv", "ihist", "ialias", "icap", "idump", "iless"
142 ]
143
144
145 os.stat_float_times(True) # enable microseconds
146
147
148 class AttrNamespace(object):
149 """
150 Helper class that is used for providing a namespace for evaluating
151 expressions containing attribute names of an object.
152 """
153 def __init__(self, wrapped):
154 self.wrapped = wrapped
155
156 def __getitem__(self, name):
157 if name == "_":
158 return self.wrapped
159 try:
160 return getattr(self.wrapped, name)
161 except AttributeError:
162 raise KeyError(name)
163
164 # Python 2.3 compatibility
165 # use eval workaround to find out which names are used in the
166 # eval string and put them into the locals. This works for most
167 # normal uses case, bizarre ones like accessing the locals()
168 # will fail
169 try:
170 eval("_", None, AttrNamespace(None))
171 except TypeError:
172 real_eval = eval
173 def eval(codestring, _globals, _locals):
174 """
175 eval(source[, globals[, locals]]) -> value
176
177 Evaluate the source in the context of globals and locals.
178 The source may be a string representing a Python expression
179 or a code object as returned by compile().
180 The globals must be a dictionary and locals can be any mappping.
181
182 This function is a workaround for the shortcomings of
183 Python 2.3's eval.
184 """
185
186 if isinstance(codestring, basestring):
187 code = compile(codestring, "_eval", "eval")
188 else:
189 code = codestring
190 newlocals = {}
191 for name in code.co_names:
192 try:
193 newlocals[name] = _locals[name]
194 except KeyError:
195 pass
196 return real_eval(code, _globals, newlocals)
197
198
199 noitem = object()
200
201
202 def item(iterator, index, default=noitem):
203 """
204 Return the ``index``th item from the iterator ``iterator``.
205 ``index`` must be an integer (negative integers are relative to the
206 end (i.e. the last items produced by the iterator)).
207
208 If ``default`` is given, this will be the default value when
209 the iterator doesn't contain an item at this position. Otherwise an
210 ``IndexError`` will be raised.
211
212 Note that using this function will partially or totally exhaust the
213 iterator.
214 """
215 i = index
216 if i>=0:
217 for item in iterator:
218 if not i:
219 return item
220 i -= 1
221 else:
222 i = -index
223 cache = deque()
224 for item in iterator:
225 cache.append(item)
226 if len(cache)>i:
227 cache.popleft()
228 if len(cache)==i:
229 return cache.popleft()
230 if default is noitem:
231 raise IndexError(index)
232 else:
233 return default
234
235
236 def getglobals(g):
237 """
238 Return the global namespace that is used for expression strings in
239 ``ifilter`` and others. This is ``g`` or (if ``g`` is ``None``) IPython's
240 user namespace.
241 """
242 if g is None:
243 if ipapi is not None:
244 api = ipapi.get()
245 if api is not None:
246 return api.user_ns
247 return globals()
248 return g
249
250
251 class Descriptor(object):
252 """
253 A ``Descriptor`` object is used for describing the attributes of objects.
254 """
255 def __hash__(self):
256 return hash(self.__class__) ^ hash(self.key())
257
258 def __eq__(self, other):
259 return self.__class__ is other.__class__ and self.key() == other.key()
260
261 def __ne__(self, other):
262 return self.__class__ is not other.__class__ or self.key() != other.key()
263
264 def key(self):
265 pass
266
267 def name(self):
268 """
269 Return the name of this attribute for display by a ``Display`` object
270 (e.g. as a column title).
271 """
272 key = self.key()
273 if key is None:
274 return "_"
275 return str(key)
276
277 def attrtype(self, obj):
278 """
279 Return the type of this attribute (i.e. something like "attribute" or
280 "method").
281 """
282
283 def valuetype(self, obj):
284 """
285 Return the type of this attribute value of the object ``obj``.
286 """
287
288 def value(self, obj):
289 """
290 Return the value of this attribute of the object ``obj``.
291 """
292
293 def doc(self, obj):
294 """
295 Return the documentation for this attribute.
296 """
297
298 def shortdoc(self, obj):
299 """
300 Return a short documentation for this attribute (defaulting to the
301 first line).
302 """
303 doc = self.doc(obj)
304 if doc is not None:
305 doc = doc.strip().splitlines()[0].strip()
306 return doc
307
308 def iter(self, obj):
309 """
310 Return an iterator for this attribute of the object ``obj``.
311 """
312 return xiter(self.value(obj))
313
314
315 class SelfDescriptor(Descriptor):
316 """
317 A ``SelfDescriptor`` describes the object itself.
318 """
319 def key(self):
320 return None
321
322 def attrtype(self, obj):
323 return "self"
324
325 def valuetype(self, obj):
326 return type(obj)
327
328 def value(self, obj):
329 return obj
330
331 def __repr__(self):
332 return "Self"
333
334 selfdescriptor = SelfDescriptor() # there's no need for more than one
335
336
337 class AttributeDescriptor(Descriptor):
338 """
339 An ``AttributeDescriptor`` describes a simple attribute of an object.
340 """
341 __slots__ = ("_name", "_doc")
342
343 def __init__(self, name, doc=None):
344 self._name = name
345 self._doc = doc
346
347 def key(self):
348 return self._name
349
350 def doc(self, obj):
351 return self._doc
352
353 def attrtype(self, obj):
354 return "attr"
355
356 def valuetype(self, obj):
357 return type(getattr(obj, self._name))
358
359 def value(self, obj):
360 return getattr(obj, self._name)
361
362 def __repr__(self):
363 if self._doc is None:
364 return "Attribute(%r)" % self._name
365 else:
366 return "Attribute(%r, %r)" % (self._name, self._doc)
367
368
369 class IndexDescriptor(Descriptor):
370 """
371 An ``IndexDescriptor`` describes an "attribute" of an object that is fetched
372 via ``__getitem__``.
373 """
374 __slots__ = ("_index",)
375
376 def __init__(self, index):
377 self._index = index
378
379 def key(self):
380 return self._index
381
382 def attrtype(self, obj):
383 return "item"
384
385 def valuetype(self, obj):
386 return type(obj[self._index])
387
388 def value(self, obj):
389 return obj[self._index]
390
391 def __repr__(self):
392 return "Index(%r)" % self._index
393
394
395 class MethodDescriptor(Descriptor):
396 """
397 A ``MethodDescriptor`` describes a method of an object that can be called
398 without argument. Note that this method shouldn't change the object.
399 """
400 __slots__ = ("_name", "_doc")
401
402 def __init__(self, name, doc=None):
403 self._name = name
404 self._doc = doc
405
406 def key(self):
407 return self._name
408
409 def doc(self, obj):
410 if self._doc is None:
411 return getattr(obj, self._name).__doc__
412 return self._doc
413
414 def attrtype(self, obj):
415 return "method"
416
417 def valuetype(self, obj):
418 return type(self.value(obj))
419
420 def value(self, obj):
421 return getattr(obj, self._name)()
422
423 def __repr__(self):
424 if self._doc is None:
425 return "Method(%r)" % self._name
426 else:
427 return "Method(%r, %r)" % (self._name, self._doc)
428
429
430 class IterAttributeDescriptor(Descriptor):
431 """
432 An ``IterAttributeDescriptor`` works like an ``AttributeDescriptor`` but
433 doesn't return an attribute values (because this value might be e.g. a large
434 list).
435 """
436 __slots__ = ("_name", "_doc")
437
438 def __init__(self, name, doc=None):
439 self._name = name
440 self._doc = doc
441
442 def key(self):
443 return self._name
444
445 def doc(self, obj):
446 return self._doc
447
448 def attrtype(self, obj):
449 return "iter"
450
451 def valuetype(self, obj):
452 return noitem
453
454 def value(self, obj):
455 return noitem
456
457 def iter(self, obj):
458 return xiter(getattr(obj, self._name))
459
460 def __repr__(self):
461 if self._doc is None:
462 return "IterAttribute(%r)" % self._name
463 else:
464 return "IterAttribute(%r, %r)" % (self._name, self._doc)
465
466
467 class IterMethodDescriptor(Descriptor):
468 """
469 An ``IterMethodDescriptor`` works like an ``MethodDescriptor`` but doesn't
470 return an attribute values (because this value might be e.g. a large list).
471 """
472 __slots__ = ("_name", "_doc")
473
474 def __init__(self, name, doc=None):
475 self._name = name
476 self._doc = doc
477
478 def key(self):
479 return self._name
480
481 def doc(self, obj):
482 if self._doc is None:
483 return getattr(obj, self._name).__doc__
484 return self._doc
485
486 def attrtype(self, obj):
487 return "itermethod"
488
489 def valuetype(self, obj):
490 return noitem
491
492 def value(self, obj):
493 return noitem
494
495 def iter(self, obj):
496 return xiter(getattr(obj, self._name)())
497
498 def __repr__(self):
499 if self._doc is None:
500 return "IterMethod(%r)" % self._name
501 else:
502 return "IterMethod(%r, %r)" % (self._name, self._doc)
503
504
505 class FunctionDescriptor(Descriptor):
506 """
507 A ``FunctionDescriptor`` turns a function into a descriptor. The function
508 will be called with the object to get the type and value of the attribute.
509 """
510 __slots__ = ("_function", "_name", "_doc")
511
512 def __init__(self, function, name=None, doc=None):
513 self._function = function
514 self._name = name
515 self._doc = doc
516
517 def key(self):
518 return self._function
519
520 def name(self):
521 if self._name is not None:
522 return self._name
523 return getattr(self._function, "__xname__", self._function.__name__)
524
525 def doc(self, obj):
526 if self._doc is None:
527 return self._function.__doc__
528 return self._doc
529
530 def attrtype(self, obj):
531 return "function"
532
533 def valuetype(self, obj):
534 return type(self._function(obj))
535
536 def value(self, obj):
537 return self._function(obj)
538
539 def __repr__(self):
540 if self._doc is None:
541 return "Function(%r)" % self._name
542 else:
543 return "Function(%r, %r)" % (self._name, self._doc)
544
545
546 class Table(object):
547 """
548 A ``Table`` is an object that produces items (just like a normal Python
549 iterator/generator does) and can be used as the first object in a pipeline
550 expression. The displayhook will open the default browser for such an object
551 (instead of simply printing the ``repr()`` result).
552 """
553
554 # We want to support ``foo`` and ``foo()`` in pipeline expression:
555 # So we implement the required operators (``|`` and ``+``) in the metaclass,
556 # instantiate the class and forward the operator to the instance
557 class __metaclass__(type):
558 def __iter__(self):
559 return iter(self())
560
561 def __or__(self, other):
562 return self() | other
563
564 def __add__(self, other):
565 return self() + other
566
567 def __radd__(self, other):
568 return other + self()
569
570 def __getitem__(self, index):
571 return self()[index]
572
573 def __getitem__(self, index):
574 return item(self, index)
575
576 def __contains__(self, item):
577 for haveitem in self:
578 if item == haveitem:
579 return True
580 return False
581
582 def __or__(self, other):
583 # autoinstantiate right hand side
584 if isinstance(other, type) and issubclass(other, (Table, Display)):
585 other = other()
586 # treat simple strings and functions as ``ieval`` instances
587 elif not isinstance(other, Display) and not isinstance(other, Table):
588 other = ieval(other)
589 # forward operations to the right hand side
590 return other.__ror__(self)
591
592 def __add__(self, other):
593 # autoinstantiate right hand side
594 if isinstance(other, type) and issubclass(other, Table):
595 other = other()
596 return ichain(self, other)
597
598 def __radd__(self, other):
599 # autoinstantiate left hand side
600 if isinstance(other, type) and issubclass(other, Table):
601 other = other()
602 return ichain(other, self)
603
604
605 class Pipe(Table):
606 """
607 A ``Pipe`` is an object that can be used in a pipeline expression. It
608 processes the objects it gets from its input ``Table``/``Pipe``. Note that
609 a ``Pipe`` object can't be used as the first object in a pipeline
610 expression, as it doesn't produces items itself.
611 """
612 class __metaclass__(Table.__metaclass__):
613 def __ror__(self, input):
614 return input | self()
615
616 def __ror__(self, input):
617 # autoinstantiate left hand side
618 if isinstance(input, type) and issubclass(input, Table):
619 input = input()
620 self.input = input
621 return self
622
623
624 def xrepr(item, mode="default"):
625 """
626 Generic function that adds color output and different display modes to ``repr``.
627
628 The result of an ``xrepr`` call is iterable and consists of ``(style, string)``
629 tuples. The ``style`` in this tuple must be a ``Style`` object from the
630 ``astring`` module. To reconfigure the output the first yielded tuple can be
631 a ``(aligment, full)`` tuple instead of a ``(style, string)`` tuple.
632 ``alignment`` can be -1 for left aligned, 0 for centered and 1 for right
633 aligned (the default is left alignment). ``full`` is a boolean that specifies
634 whether the complete output must be displayed or the ``Display`` object is
635 allowed to stop output after enough text has been produced (e.g. a syntax
636 highlighted text line would use ``True``, but for a large data structure
637 (i.e. a nested list, tuple or dictionary) ``False`` would be used).
638 The default is full output.
639
640 There are four different possible values for ``mode`` depending on where
641 the ``Display`` object will display ``item``:
642
643 ``"header"``
644 ``item`` will be displayed in a header line (this is used by ``ibrowse``).
645
646 ``"footer"``
647 ``item`` will be displayed in a footer line (this is used by ``ibrowse``).
648
649 ``"cell"``
650 ``item`` will be displayed in a table cell/list.
651
652 ``"default"``
653 default mode. If an ``xrepr`` implementation recursively outputs objects,
654 ``"default"`` must be passed in the recursive calls to ``xrepr``.
655
656 If no implementation is registered for ``item``, ``xrepr`` will try the
657 ``__xrepr__`` method on ``item``. If ``item`` doesn't have an ``__xrepr__``
658 method it falls back to ``repr``/``__repr__`` for all modes.
659 """
660 try:
661 func = item.__xrepr__
662 except AttributeError:
663 yield (astyle.style_default, repr(item))
664 else:
665 try:
666 for x in func(mode):
667 yield x
668 except (KeyboardInterrupt, SystemExit):
669 raise
670 except Exception:
671 yield (astyle.style_default, repr(item))
672 xrepr = simplegeneric.generic(xrepr)
673
674
675 def xrepr_none(self, mode="default"):
676 yield (astyle.style_type_none, repr(self))
677 xrepr.when_object(None)(xrepr_none)
678
679
680 def xrepr_noitem(self, mode="default"):
681 yield (2, True)
682 yield (astyle.style_nodata, "<?>")
683 xrepr.when_object(noitem)(xrepr_noitem)
684
685
686 def xrepr_bool(self, mode="default"):
687 yield (astyle.style_type_bool, repr(self))
688 xrepr.when_type(bool)(xrepr_bool)
689
690
691 def xrepr_str(self, mode="default"):
692 if mode == "cell":
693 yield (astyle.style_default, repr(self.expandtabs(tab))[1:-1])
694 else:
695 yield (astyle.style_default, repr(self))
696 xrepr.when_type(str)(xrepr_str)
697
698
699 def xrepr_unicode(self, mode="default"):
700 if mode == "cell":
701 yield (astyle.style_default, repr(self.expandtabs(tab))[2:-1])
702 else:
703 yield (astyle.style_default, repr(self))
704 xrepr.when_type(unicode)(xrepr_unicode)
705
706
707 def xrepr_number(self, mode="default"):
708 yield (1, True)
709 yield (astyle.style_type_number, repr(self))
710 xrepr.when_type(int)(xrepr_number)
711 xrepr.when_type(long)(xrepr_number)
712 xrepr.when_type(float)(xrepr_number)
713
714
715 def xrepr_complex(self, mode="default"):
716 yield (astyle.style_type_number, repr(self))
717 xrepr.when_type(complex)(xrepr_number)
718
719
720 def xrepr_datetime(self, mode="default"):
721 if mode == "cell":
722 # Don't use strftime() here, as this requires year >= 1900
723 yield (astyle.style_type_datetime,
724 "%04d-%02d-%02d %02d:%02d:%02d.%06d" % \
725 (self.year, self.month, self.day,
726 self.hour, self.minute, self.second,
727 self.microsecond),
728 )
729 else:
730 yield (astyle.style_type_datetime, repr(self))
731 xrepr.when_type(datetime.datetime)(xrepr_datetime)
732
733
734 def xrepr_date(self, mode="default"):
735 if mode == "cell":
736 yield (astyle.style_type_datetime,
737 "%04d-%02d-%02d" % (self.year, self.month, self.day))
738 else:
739 yield (astyle.style_type_datetime, repr(self))
740 xrepr.when_type(datetime.date)(xrepr_date)
741
742
743 def xrepr_time(self, mode="default"):
744 if mode == "cell":
745 yield (astyle.style_type_datetime,
746 "%02d:%02d:%02d.%06d" % \
747 (self.hour, self.minute, self.second, self.microsecond))
748 else:
749 yield (astyle.style_type_datetime, repr(self))
750 xrepr.when_type(datetime.time)(xrepr_time)
751
752
753 def xrepr_timedelta(self, mode="default"):
754 yield (astyle.style_type_datetime, repr(self))
755 xrepr.when_type(datetime.timedelta)(xrepr_timedelta)
756
757
758 def xrepr_type(self, mode="default"):
759 if self.__module__ == "__builtin__":
760 yield (astyle.style_type_type, self.__name__)
761 else:
762 yield (astyle.style_type_type, "%s.%s" % (self.__module__, self.__name__))
763 xrepr.when_type(type)(xrepr_type)
764
765
766 def xrepr_exception(self, mode="default"):
767 if self.__class__.__module__ == "exceptions":
768 classname = self.__class__.__name__
769 else:
770 classname = "%s.%s" % \
771 (self.__class__.__module__, self.__class__.__name__)
772 if mode == "header" or mode == "footer":
773 yield (astyle.style_error, "%s: %s" % (classname, self))
774 else:
775 yield (astyle.style_error, classname)
776 xrepr.when_type(Exception)(xrepr_exception)
777
778
779 def xrepr_listtuple(self, mode="default"):
780 if mode == "header" or mode == "footer":
781 if self.__class__.__module__ == "__builtin__":
782 classname = self.__class__.__name__
783 else:
784 classname = "%s.%s" % \
785 (self.__class__.__module__,self.__class__.__name__)
786 yield (astyle.style_default,
787 "<%s object with %d items at 0x%x>" % \
788 (classname, len(self), id(self)))
789 else:
790 yield (-1, False)
791 if isinstance(self, list):
792 yield (astyle.style_default, "[")
793 end = "]"
794 else:
795 yield (astyle.style_default, "(")
796 end = ")"
797 for (i, subself) in enumerate(self):
798 if i:
799 yield (astyle.style_default, ", ")
800 for part in xrepr(subself, "default"):
801 yield part
802 yield (astyle.style_default, end)
803 xrepr.when_type(list)(xrepr_listtuple)
804 xrepr.when_type(tuple)(xrepr_listtuple)
805
806
807 def xrepr_dict(self, mode="default"):
808 if mode == "header" or mode == "footer":
809 if self.__class__.__module__ == "__builtin__":
810 classname = self.__class__.__name__
811 else:
812 classname = "%s.%s" % \
813 (self.__class__.__module__,self.__class__.__name__)
814 yield (astyle.style_default,
815 "<%s object with %d items at 0x%x>" % \
816 (classname, len(self), id(self)))
817 else:
818 yield (-1, False)
819 if isinstance(self, dict):
820 yield (astyle.style_default, "{")
821 end = "}"
822 else:
823 yield (astyle.style_default, "dictproxy((")
824 end = "})"
825 for (i, (key, value)) in enumerate(self.iteritems()):
826 if i:
827 yield (astyle.style_default, ", ")
828 for part in xrepr(key, "default"):
829 yield part
830 yield (astyle.style_default, ": ")
831 for part in xrepr(value, "default"):
832 yield part
833 yield (astyle.style_default, end)
834 xrepr.when_type(dict)(xrepr_dict)
835 xrepr.when_type(types.DictProxyType)(xrepr_dict)
836
837
838 def upgradexattr(attr):
839 """
840 Convert an attribute descriptor string to a real descriptor object.
841
842 If attr already is a descriptor object return if unmodified. A
843 ``SelfDescriptor`` will be returned if ``attr`` is ``None``. ``"foo"``
844 returns an ``AttributeDescriptor`` for the attribute named ``"foo"``.
845 ``"foo()"`` returns a ``MethodDescriptor`` for the method named ``"foo"``.
846 ``"-foo"`` will return an ``IterAttributeDescriptor`` for the attribute
847 named ``"foo"`` and ``"-foo()"`` will return an ``IterMethodDescriptor``
848 for the method named ``"foo"``. Furthermore integer will return the appropriate
849 ``IndexDescriptor`` and callables will return a ``FunctionDescriptor``.
850 """
851 if attr is None:
852 return selfdescriptor
853 elif isinstance(attr, Descriptor):
854 return attr
855 elif isinstance(attr, str):
856 if attr.endswith("()"):
857 if attr.startswith("-"):
858 return IterMethodDescriptor(attr[1:-2])
859 else:
860 return MethodDescriptor(attr[:-2])
861 else:
862 if attr.startswith("-"):
863 return IterAttributeDescriptor(attr[1:])
864 else:
865 return AttributeDescriptor(attr)
866 elif isinstance(attr, (int, long)):
867 return IndexDescriptor(attr)
868 elif callable(attr):
869 return FunctionDescriptor(attr)
870 else:
871 raise TypeError("can't handle descriptor %r" % attr)
872
873
874 def xattrs(item, mode="default"):
875 """
876 Generic function that returns an iterable of attribute descriptors
877 to be used for displaying the attributes ob the object ``item`` in display
878 mode ``mode``.
879
880 There are two possible modes:
881
882 ``"detail"``
883 The ``Display`` object wants to display a detailed list of the object
884 attributes.
885
886 ``"default"``
887 The ``Display`` object wants to display the object in a list view.
888
889 If no implementation is registered for the object ``item`` ``xattrs`` falls
890 back to trying the ``__xattrs__`` method of the object. If this doesn't
891 exist either, ``dir(item)`` is used for ``"detail"`` mode and ``(None,)``
892 for ``"default"`` mode.
893
894 The implementation must yield attribute descriptors (see the class
895 ``Descriptor`` for more info). The ``__xattrs__`` method may also return
896 attribute descriptor strings (and ``None``) which will be converted to real
897 descriptors by ``upgradexattr()``.
898 """
899 try:
900 func = item.__xattrs__
901 except AttributeError:
902 if mode == "detail":
903 for attrname in dir(item):
904 yield AttributeDescriptor(attrname)
905 else:
906 yield selfdescriptor
907 else:
908 for attr in func(mode):
909 yield upgradexattr(attr)
910 xattrs = simplegeneric.generic(xattrs)
911
912
913 def xattrs_complex(self, mode="default"):
914 if mode == "detail":
915 return (AttributeDescriptor("real"), AttributeDescriptor("imag"))
916 return (selfdescriptor,)
917 xattrs.when_type(complex)(xattrs_complex)
918
919
920 def _isdict(item):
921 try:
922 itermeth = item.__class__.__iter__
923 except (AttributeError, TypeError):
924 return False
925 return itermeth is dict.__iter__ or itermeth is types.DictProxyType.__iter__
926
927
928 def _isstr(item):
929 if not isinstance(item, basestring):
930 return False
931 try:
932 itermeth = item.__class__.__iter__
933 except AttributeError:
934 return True
935 return False # ``__iter__`` has been redefined
936
937
938 def xiter(item):
939 """
940 Generic function that implements iteration for pipeline expression. If no
941 implementation is registered for ``item`` ``xiter`` falls back to ``iter``.
942 """
943 try:
944 func = item.__xiter__
945 except AttributeError:
946 if _isdict(item):
947 def items(item):
948 fields = ("key", "value")
949 for (key, value) in item.iteritems():
950 yield Fields(fields, key=key, value=value)
951 return items(item)
952 elif isinstance(item, new.module):
953 def items(item):
954 fields = ("key", "value")
955 for key in sorted(item.__dict__):
956 yield Fields(fields, key=key, value=getattr(item, key))
957 return items(item)
958 elif _isstr(item):
959 if not item:
960 raise ValueError("can't enter empty string")
961 lines = item.splitlines()
962 if len(lines) == 1:
963 def iterone(item):
964 yield item
965 return iterone(item)
966 else:
967 return iter(lines)
968 return iter(item)
969 else:
970 return iter(func()) # iter() just to be safe
971 xiter = simplegeneric.generic(xiter)
972
973
974 class ichain(Pipe):
975 """
976 Chains multiple ``Table``s into one.
977 """
978
979 def __init__(self, *iters):
980 self.iters = iters
981
982 def __iter__(self):
983 return itertools.chain(*self.iters)
984
985 def __xrepr__(self, mode="default"):
986 if mode == "header" or mode == "footer":
987 for (i, item) in enumerate(self.iters):
988 if i:
989 yield (astyle.style_default, "+")
990 if isinstance(item, Pipe):
991 yield (astyle.style_default, "(")
992 for part in xrepr(item, mode):
993 yield part
994 if isinstance(item, Pipe):
995 yield (astyle.style_default, ")")
996 else:
997 yield (astyle.style_default, repr(self))
998
999 def __repr__(self):
1000 args = ", ".join([repr(it) for it in self.iters])
1001 return "%s.%s(%s)" % \
1002 (self.__class__.__module__, self.__class__.__name__, args)
1003
1004
1005 class ifile(path.path):
1006 """
1007 file (or directory) object.
1008 """
1009
1010 def getmode(self):
1011 return self.stat().st_mode
1012 mode = property(getmode, None, None, "Access mode")
1013
1014 def gettype(self):
1015 data = [
1016 (stat.S_ISREG, "file"),
1017 (stat.S_ISDIR, "dir"),
1018 (stat.S_ISCHR, "chardev"),
1019 (stat.S_ISBLK, "blockdev"),
1020 (stat.S_ISFIFO, "fifo"),
1021 (stat.S_ISLNK, "symlink"),
1022 (stat.S_ISSOCK,"socket"),
1023 ]
1024 lstat = self.lstat()
1025 if lstat is not None:
1026 types = set([text for (func, text) in data if func(lstat.st_mode)])
1027 else:
1028 types = set()
1029 m = self.mode
1030 types.update([text for (func, text) in data if func(m)])
1031 return ", ".join(types)
1032 type = property(gettype, None, None, "file type (file, directory, link, etc.)")
1033
1034 def getmodestr(self):
1035 m = self.mode
1036 data = [
1037 (stat.S_IRUSR, "-r"),
1038 (stat.S_IWUSR, "-w"),
1039 (stat.S_IXUSR, "-x"),
1040 (stat.S_IRGRP, "-r"),
1041 (stat.S_IWGRP, "-w"),
1042 (stat.S_IXGRP, "-x"),
1043 (stat.S_IROTH, "-r"),
1044 (stat.S_IWOTH, "-w"),
1045 (stat.S_IXOTH, "-x"),
1046 ]
1047 return "".join([text[bool(m&bit)] for (bit, text) in data])
1048
1049 modestr = property(getmodestr, None, None, "Access mode as string")
1050
1051 def getblocks(self):
1052 return self.stat().st_blocks
1053 blocks = property(getblocks, None, None, "File size in blocks")
1054
1055 def getblksize(self):
1056 return self.stat().st_blksize
1057 blksize = property(getblksize, None, None, "Filesystem block size")
1058
1059 def getdev(self):
1060 return self.stat().st_dev
1061 dev = property(getdev)
1062
1063 def getnlink(self):
1064 return self.stat().st_nlink
1065 nlink = property(getnlink, None, None, "Number of links")
1066
1067 def getuid(self):
1068 return self.stat().st_uid
1069 uid = property(getuid, None, None, "User id of file owner")
1070
1071 def getgid(self):
1072 return self.stat().st_gid
1073 gid = property(getgid, None, None, "Group id of file owner")
1074
1075 def getowner(self):
1076 stat = self.stat()
1077 try:
1078 return pwd.getpwuid(stat.st_uid).pw_name
1079 except KeyError:
1080 return stat.st_uid
1081 owner = property(getowner, None, None, "Owner name (or id)")
1082
1083 def getgroup(self):
1084 stat = self.stat()
1085 try:
1086 return grp.getgrgid(stat.st_gid).gr_name
1087 except KeyError:
1088 return stat.st_gid
1089 group = property(getgroup, None, None, "Group name (or id)")
1090
1091 def getadate(self):
1092 return datetime.datetime.utcfromtimestamp(self.atime)
1093 adate = property(getadate, None, None, "Access date")
1094
1095 def getcdate(self):
1096 return datetime.datetime.utcfromtimestamp(self.ctime)
1097 cdate = property(getcdate, None, None, "Creation date")
1098
1099 def getmdate(self):
1100 return datetime.datetime.utcfromtimestamp(self.mtime)
1101 mdate = property(getmdate, None, None, "Modification date")
1102
1103 def mimetype(self):
1104 """
1105 Return MIME type guessed from the extension.
1106 """
1107 return mimetypes.guess_type(self.basename())[0]
1108
1109 def encoding(self):
1110 """
1111 Return guessed compression (like "compress" or "gzip").
1112 """
1113 return mimetypes.guess_type(self.basename())[1]
1114
1115 def __repr__(self):
1116 return "ifile(%s)" % path._base.__repr__(self)
1117
1118 if sys.platform == "win32":
1119 defaultattrs = (None, "type", "size", "modestr", "mdate")
1120 else:
1121 defaultattrs = (None, "type", "size", "modestr", "owner", "group", "mdate")
1122
1123 def __xattrs__(self, mode="default"):
1124 if mode == "detail":
1125 return (
1126 "name",
1127 "basename()",
1128 "abspath()",
1129 "realpath()",
1130 "type",
1131 "mode",
1132 "modestr",
1133 "stat()",
1134 "lstat()",
1135 "uid",
1136 "gid",
1137 "owner",
1138 "group",
1139 "dev",
1140 "nlink",
1141 "ctime",
1142 "mtime",
1143 "atime",
1144 "cdate",
1145 "mdate",
1146 "adate",
1147 "size",
1148 "blocks",
1149 "blksize",
1150 "isdir()",
1151 "islink()",
1152 "mimetype()",
1153 "encoding()",
1154 "-listdir()",
1155 "-dirs()",
1156 "-files()",
1157 "-walk()",
1158 "-walkdirs()",
1159 "-walkfiles()",
1160 )
1161 else:
1162 return self.defaultattrs
1163
1164
1165 def xiter_ifile(self):
1166 if self.isdir():
1167 yield (self / os.pardir).abspath()
1168 for child in sorted(self.listdir()):
1169 yield child
1170 else:
1171 f = self.open("rb")
1172 for line in f:
1173 yield line
1174 f.close()
1175 xiter.when_type(ifile)(xiter_ifile)
1176
1177
1178 # We need to implement ``xrepr`` for ``ifile`` as a generic function, because
1179 # otherwise ``xrepr_str`` would kick in.
1180 def xrepr_ifile(self, mode="default"):
1181 try:
1182 if self.isdir():
1183 name = "idir"
1184 style = astyle.style_dir
1185 else:
1186 name = "ifile"
1187 style = astyle.style_file
1188 except IOError:
1189 name = "ifile"
1190 style = astyle.style_default
1191 if mode in ("cell", "header", "footer"):
1192 abspath = repr(path._base(self.normpath()))
1193 if abspath.startswith("u"):
1194 abspath = abspath[2:-1]
1195 else:
1196 abspath = abspath[1:-1]
1197 if mode == "cell":
1198 yield (style, abspath)
1199 else:
1200 yield (style, "%s(%s)" % (name, abspath))
1201 else:
1202 yield (style, repr(self))
1203 xrepr.when_type(ifile)(xrepr_ifile)
1204
1205
1206 class ils(Table):
1207 """
1208 List the current (or a specified) directory.
1209
1210 Examples::
1211
1212 >>> ils
1213 >>> ils("/usr/local/lib/python2.4")
1214 >>> ils("~")
1215 """
1216 def __init__(self, base=os.curdir, dirs=True, files=True):
1217 self.base = os.path.expanduser(base)
1218 self.dirs = dirs
1219 self.files = files
1220
1221 def __iter__(self):
1222 base = ifile(self.base)
1223 yield (base / os.pardir).abspath()
1224 for child in sorted(base.listdir()):
1225 if self.dirs:
1226 if self.files:
1227 yield child
1228 else:
1229 if child.isdir():
1230 yield child
1231 elif self.files:
1232 if not child.isdir():
1233 yield child
1234
1235 def __xrepr__(self, mode="default"):
1236 return xrepr(ifile(self.base), mode)
1237
1238 def __repr__(self):
1239 return "%s.%s(%r)" % \
1240 (self.__class__.__module__, self.__class__.__name__, self.base)
1241
1242
1243 class iglob(Table):
1244 """
1245 List all files and directories matching a specified pattern.
1246 (See ``glob.glob()`` for more info.).
1247
1248 Examples::
1249
1250 >>> iglob("*.py")
1251 """
1252 def __init__(self, glob):
1253 self.glob = glob
1254
1255 def __iter__(self):
1256 for name in glob.glob(self.glob):
1257 yield ifile(name)
1258
1259 def __xrepr__(self, mode="default"):
1260 if mode == "header" or mode == "footer" or mode == "cell":
1261 yield (astyle.style_default,
1262 "%s(%r)" % (self.__class__.__name__, self.glob))
1263 else:
1264 yield (astyle.style_default, repr(self))
1265
1266 def __repr__(self):
1267 return "%s.%s(%r)" % \
1268 (self.__class__.__module__, self.__class__.__name__, self.glob)
1269
1270
1271 class iwalk(Table):
1272 """
1273 List all files and directories in a directory and it's subdirectory::
1274
1275 >>> iwalk
1276 >>> iwalk("/usr/local/lib/python2.4")
1277 >>> iwalk("~")
1278 """
1279 def __init__(self, base=os.curdir, dirs=True, files=True):
1280 self.base = os.path.expanduser(base)
1281 self.dirs = dirs
1282 self.files = files
1283
1284 def __iter__(self):
1285 for (dirpath, dirnames, filenames) in os.walk(self.base):
1286 if self.dirs:
1287 for name in sorted(dirnames):
1288 yield ifile(os.path.join(dirpath, name))
1289 if self.files:
1290 for name in sorted(filenames):
1291 yield ifile(os.path.join(dirpath, name))
1292
1293 def __xrepr__(self, mode="default"):
1294 if mode == "header" or mode == "footer" or mode == "cell":
1295 yield (astyle.style_default,
1296 "%s(%r)" % (self.__class__.__name__, self.base))
1297 else:
1298 yield (astyle.style_default, repr(self))
1299
1300 def __repr__(self):
1301 return "%s.%s(%r)" % \
1302 (self.__class__.__module__, self.__class__.__name__, self.base)
1303
1304
1305 class ipwdentry(object):
1306 """
1307 ``ipwdentry`` objects encapsulate entries in the Unix user account and
1308 password database.
1309 """
1310 def __init__(self, id):
1311 self._id = id
1312 self._entry = None
1313
1314 def __eq__(self, other):
1315 return self.__class__ is other.__class__ and self._id == other._id
1316
1317 def __ne__(self, other):
1318 return self.__class__ is not other.__class__ or self._id != other._id
1319
1320 def _getentry(self):
1321 if self._entry is None:
1322 if isinstance(self._id, basestring):
1323 self._entry = pwd.getpwnam(self._id)
1324 else:
1325 self._entry = pwd.getpwuid(self._id)
1326 return self._entry
1327
1328 def getname(self):
1329 if isinstance(self._id, basestring):
1330 return self._id
1331 else:
1332 return self._getentry().pw_name
1333 name = property(getname, None, None, "User name")
1334
1335 def getpasswd(self):
1336 return self._getentry().pw_passwd
1337 passwd = property(getpasswd, None, None, "Password")
1338
1339 def getuid(self):
1340 if isinstance(self._id, basestring):
1341 return self._getentry().pw_uid
1342 else:
1343 return self._id
1344 uid = property(getuid, None, None, "User id")
1345
1346 def getgid(self):
1347 return self._getentry().pw_gid
1348 gid = property(getgid, None, None, "Primary group id")
1349
1350 def getgroup(self):
1351 return igrpentry(self.gid)
1352 group = property(getgroup, None, None, "Group")
1353
1354 def getgecos(self):
1355 return self._getentry().pw_gecos
1356 gecos = property(getgecos, None, None, "Information (e.g. full user name)")
1357
1358 def getdir(self):
1359 return self._getentry().pw_dir
1360 dir = property(getdir, None, None, "$HOME directory")
1361
1362 def getshell(self):
1363 return self._getentry().pw_shell
1364 shell = property(getshell, None, None, "Login shell")
1365
1366 def __xattrs__(self, mode="default"):
1367 return ("name", "passwd", "uid", "gid", "gecos", "dir", "shell")
1368
1369 def __repr__(self):
1370 return "%s.%s(%r)" % \
1371 (self.__class__.__module__, self.__class__.__name__, self._id)
1372
1373
1374 class ipwd(Table):
1375 """
1376 List all entries in the Unix user account and password database.
1377
1378 Example::
1379
1380 >>> ipwd | isort("uid")
1381 """
1382 def __iter__(self):
1383 for entry in pwd.getpwall():
1384 yield ipwdentry(entry.pw_name)
1385
1386 def __xrepr__(self, mode="default"):
1387 if mode == "header" or mode == "footer" or mode == "cell":
1388 yield (astyle.style_default, "%s()" % self.__class__.__name__)
1389 else:
1390 yield (astyle.style_default, repr(self))
1391
1392
1393 class igrpentry(object):
1394 """
1395 ``igrpentry`` objects encapsulate entries in the Unix group database.
1396 """
1397 def __init__(self, id):
1398 self._id = id
1399 self._entry = None
1400
1401 def __eq__(self, other):
1402 return self.__class__ is other.__class__ and self._id == other._id
1403
1404 def __ne__(self, other):
1405 return self.__class__ is not other.__class__ or self._id != other._id
1406
1407 def _getentry(self):
1408 if self._entry is None:
1409 if isinstance(self._id, basestring):
1410 self._entry = grp.getgrnam(self._id)
1411 else:
1412 self._entry = grp.getgrgid(self._id)
1413 return self._entry
1414
1415 def getname(self):
1416 if isinstance(self._id, basestring):
1417 return self._id
1418 else:
1419 return self._getentry().gr_name
1420 name = property(getname, None, None, "Group name")
1421
1422 def getpasswd(self):
1423 return self._getentry().gr_passwd
1424 passwd = property(getpasswd, None, None, "Password")
1425
1426 def getgid(self):
1427 if isinstance(self._id, basestring):
1428 return self._getentry().gr_gid
1429 else:
1430 return self._id
1431 gid = property(getgid, None, None, "Group id")
1432
1433 def getmem(self):
1434 return self._getentry().gr_mem
1435 mem = property(getmem, None, None, "Members")
1436
1437 def __xattrs__(self, mode="default"):
1438 return ("name", "passwd", "gid", "mem")
1439
1440 def __xrepr__(self, mode="default"):
1441 if mode == "header" or mode == "footer" or mode == "cell":
1442 yield (astyle.style_default, "group ")
1443 try:
1444 yield (astyle.style_default, self.name)
1445 except KeyError:
1446 if isinstance(self._id, basestring):
1447 yield (astyle.style_default, self.name_id)
1448 else:
1449 yield (astyle.style_type_number, str(self._id))
1450 else:
1451 yield (astyle.style_default, repr(self))
1452
1453 def __iter__(self):
1454 for member in self.mem:
1455 yield ipwdentry(member)
1456
1457 def __repr__(self):
1458 return "%s.%s(%r)" % \
1459 (self.__class__.__module__, self.__class__.__name__, self._id)
1460
1461
1462 class igrp(Table):
1463 """
1464 This ``Table`` lists all entries in the Unix group database.
1465 """
1466 def __iter__(self):
1467 for entry in grp.getgrall():
1468 yield igrpentry(entry.gr_name)
1469
1470 def __xrepr__(self, mode="default"):
1471 if mode == "header" or mode == "footer":
1472 yield (astyle.style_default, "%s()" % self.__class__.__name__)
1473 else:
1474 yield (astyle.style_default, repr(self))
1475
1476
1477 class Fields(object):
1478 def __init__(self, fieldnames, **fields):
1479 self.__fieldnames = [upgradexattr(fieldname) for fieldname in fieldnames]
1480 for (key, value) in fields.iteritems():
1481 setattr(self, key, value)
1482
1483 def __xattrs__(self, mode="default"):
1484 return self.__fieldnames
1485
1486 def __xrepr__(self, mode="default"):
1487 yield (-1, False)
1488 if mode == "header" or mode == "cell":
1489 yield (astyle.style_default, self.__class__.__name__)
1490 yield (astyle.style_default, "(")
1491 for (i, f) in enumerate(self.__fieldnames):
1492 if i:
1493 yield (astyle.style_default, ", ")
1494 yield (astyle.style_default, f.name())
1495 yield (astyle.style_default, "=")
1496 for part in xrepr(getattr(self, f), "default"):
1497 yield part
1498 yield (astyle.style_default, ")")
1499 elif mode == "footer":
1500 yield (astyle.style_default, self.__class__.__name__)
1501 yield (astyle.style_default, "(")
1502 for (i, f) in enumerate(self.__fieldnames):
1503 if i:
1504 yield (astyle.style_default, ", ")
1505 yield (astyle.style_default, f.name())
1506 yield (astyle.style_default, ")")
1507 else:
1508 yield (astyle.style_default, repr(self))
1509
1510
1511 class FieldTable(Table, list):
1512 def __init__(self, *fields):
1513 Table.__init__(self)
1514 list.__init__(self)
1515 self.fields = fields
1516
1517 def add(self, **fields):
1518 self.append(Fields(self.fields, **fields))
1519
1520 def __xrepr__(self, mode="default"):
1521 yield (-1, False)
1522 if mode == "header" or mode == "footer":
1523 yield (astyle.style_default, self.__class__.__name__)
1524 yield (astyle.style_default, "(")
1525 for (i, f) in enumerate(self.__fieldnames):
1526 if i:
1527 yield (astyle.style_default, ", ")
1528 yield (astyle.style_default, f)
1529 yield (astyle.style_default, ")")
1530 else:
1531 yield (astyle.style_default, repr(self))
1532
1533 def __repr__(self):
1534 return "<%s.%s object with fields=%r at 0x%x>" % \
1535 (self.__class__.__module__, self.__class__.__name__,
1536 ", ".join(map(repr, self.fields)), id(self))
1537
1538
1539 class List(list):
1540 def __xattrs__(self, mode="default"):
1541 return xrange(len(self))
1542
1543 def __xrepr__(self, mode="default"):
1544 yield (-1, False)
1545 if mode == "header" or mode == "cell" or mode == "footer" or mode == "default":
1546 yield (astyle.style_default, self.__class__.__name__)
1547 yield (astyle.style_default, "(")
1548 for (i, item) in enumerate(self):
1549 if i:
1550 yield (astyle.style_default, ", ")
1551 for part in xrepr(item, "default"):
1552 yield part
1553 yield (astyle.style_default, ")")
1554 else:
1555 yield (astyle.style_default, repr(self))
1556
1557
1558 class ienv(Table):
1559 """
1560 List environment variables.
1561
1562 Example::
1563
1564 >>> ienv
1565 """
1566
1567 def __iter__(self):
1568 fields = ("key", "value")
1569 for (key, value) in os.environ.iteritems():
1570 yield Fields(fields, key=key, value=value)
1571
1572 def __xrepr__(self, mode="default"):
1573 if mode == "header" or mode == "cell":
1574 yield (astyle.style_default, "%s()" % self.__class__.__name__)
1575 else:
1576 yield (astyle.style_default, repr(self))
1577
1578
1579 class ihist(Table):
1580 """
1581 IPython input history
1582
1583 Example::
1584
1585 >>> ihist
1586 >>> ihist(True) (raw mode)
1587 """
1588 def __init__(self, raw=True):
1589 self.raw = raw
1590
1591 def __iter__(self):
1592 api = ipapi.get()
1593 if self.raw:
1594 for line in api.IP.input_hist_raw:
1595 yield line.rstrip("\n")
1596 else:
1597 for line in api.IP.input_hist:
1598 yield line.rstrip("\n")
1599
1600
1601 class Alias(object):
1602 """
1603 Entry in the alias table
1604 """
1605 def __init__(self, name, args, command):
1606 self.name = name
1607 self.args = args
1608 self.command = command
1609
1610 def __xattrs__(self, mode="default"):
1611 return ("name", "args", "command")
1612
1613
1614 class ialias(Table):
1615 """
1616 IPython alias list
1617
1618 Example::
1619
1620 >>> ialias
1621 """
1622 def __iter__(self):
1623 api = ipapi.get()
1624
1625 for (name, (args, command)) in api.IP.alias_table.iteritems():
1626 yield Alias(name, args, command)
1627
1628
1629 class icsv(Pipe):
1630 """
1631 This ``Pipe`` turns the input (with must be a pipe outputting lines
1632 or an ``ifile``) into lines of CVS columns.
1633 """
1634 def __init__(self, **csvargs):
1635 """
1636 Create an ``icsv`` object. ``cvsargs`` will be passed through as
1637 keyword arguments to ``cvs.reader()``.
1638 """
1639 self.csvargs = csvargs
1640
1641 def __iter__(self):
1642 input = self.input
1643 if isinstance(input, ifile):
1644 input = input.open("rb")
1645 reader = csv.reader(input, **self.csvargs)
1646 for line in reader:
1647 yield List(line)
1648
1649 def __xrepr__(self, mode="default"):
1650 yield (-1, False)
1651 if mode == "header" or mode == "footer":
1652 input = getattr(self, "input", None)
1653 if input is not None:
1654 for part in xrepr(input, mode):
1655 yield part
1656 yield (astyle.style_default, " | ")
1657 yield (astyle.style_default, "%s(" % self.__class__.__name__)
1658 for (i, (name, value)) in enumerate(self.csvargs.iteritems()):
1659 if i:
1660 yield (astyle.style_default, ", ")
1661 yield (astyle.style_default, name)
1662 yield (astyle.style_default, "=")
1663 for part in xrepr(value, "default"):
1664 yield part
1665 yield (astyle.style_default, ")")
1666 else:
1667 yield (astyle.style_default, repr(self))
1668
1669 def __repr__(self):
1670 args = ", ".join(["%s=%r" % item for item in self.csvargs.iteritems()])
1671 return "<%s.%s %s at 0x%x>" % \
1672 (self.__class__.__module__, self.__class__.__name__, args, id(self))
1673
1674
1675 class ix(Table):
1676 """
1677 Execute a system command and list its output as lines
1678 (similar to ``os.popen()``).
1679
1680 Examples::
1681
1682 >>> ix("ps x")
1683 >>> ix("find .") | ifile
1684 """
1685 def __init__(self, cmd):
1686 self.cmd = cmd
1687 self._pipeout = None
1688
1689 def __iter__(self):
1690 (_pipein, self._pipeout) = os.popen4(self.cmd)
1691 _pipein.close()
1692 for l in self._pipeout:
1693 yield l.rstrip("\r\n")
1694 self._pipeout.close()
1695 self._pipeout = None
1696
1697 def __del__(self):
1698 if self._pipeout is not None and not self._pipeout.closed:
1699 self._pipeout.close()
1700 self._pipeout = None
1701
1702 def __xrepr__(self, mode="default"):
1703 if mode == "header" or mode == "footer":
1704 yield (astyle.style_default,
1705 "%s(%r)" % (self.__class__.__name__, self.cmd))
1706 else:
1707 yield (astyle.style_default, repr(self))
1708
1709 def __repr__(self):
1710 return "%s.%s(%r)" % \
1711 (self.__class__.__module__, self.__class__.__name__, self.cmd)
1712
1713
1714 class ifilter(Pipe):
1715 """
1716 Filter an input pipe. Only objects where an expression evaluates to true
1717 (and doesn't raise an exception) are listed.
1718
1719 Examples::
1720
1721 >>> ils | ifilter("_.isfile() and size>1000")
1722 >>> igrp | ifilter("len(mem)")
1723 >>> sys.modules | ifilter(lambda _:_.value is not None)
1724 """
1725
1726 def __init__(self, expr, globals=None, errors="raiseifallfail"):
1727 """
1728 Create an ``ifilter`` object. ``expr`` can be a callable or a string
1729 containing an expression. ``globals`` will be used as the global
1730 namespace for calling string expressions (defaulting to IPython's
1731 user namespace). ``errors`` specifies how exception during evaluation
1732 of ``expr`` are handled:
1733
1734 ``"drop"``
1735 drop all items that have errors;
1736
1737 ``"keep"``
1738 keep all items that have errors;
1739
1740 ``"keeperror"``
1741 keep the exception of all items that have errors;
1742
1743 ``"raise"``
1744 raise the exception;
1745
1746 ``"raiseifallfail"``
1747 raise the first exception if all items have errors; otherwise drop
1748 those with errors (this is the default).
1749 """
1750 self.expr = expr
1751 self.globals = globals
1752 self.errors = errors
1753
1754 def __iter__(self):
1755 if callable(self.expr):
1756 test = self.expr
1757 else:
1758 g = getglobals(self.globals)
1759 expr = compile(self.expr, "ipipe-expression", "eval")
1760 def test(item):
1761 return eval(expr, g, AttrNamespace(item))
1762
1763 ok = 0
1764 exc_info = None
1765 for item in xiter(self.input):
1766 try:
1767 if test(item):
1768 yield item
1769 ok += 1
1770 except (KeyboardInterrupt, SystemExit):
1771 raise
1772 except Exception, exc:
1773 if self.errors == "drop":
1774 pass # Ignore errors
1775 elif self.errors == "keep":
1776 yield item
1777 elif self.errors == "keeperror":
1778 yield exc
1779 elif self.errors == "raise":
1780 raise
1781 elif self.errors == "raiseifallfail":
1782 if exc_info is None:
1783 exc_info = sys.exc_info()
1784 if not ok and exc_info is not None:
1785 raise exc_info[0], exc_info[1], exc_info[2]
1786
1787 def __xrepr__(self, mode="default"):
1788 if mode == "header" or mode == "footer":
1789 input = getattr(self, "input", None)
1790 if input is not None:
1791 for part in xrepr(input, mode):
1792 yield part
1793 yield (astyle.style_default, " | ")
1794 yield (astyle.style_default, "%s(" % self.__class__.__name__)
1795 for part in xrepr(self.expr, "default"):
1796 yield part
1797 yield (astyle.style_default, ")")
1798 else:
1799 yield (astyle.style_default, repr(self))
1800
1801 def __repr__(self):
1802 return "<%s.%s expr=%r at 0x%x>" % \
1803 (self.__class__.__module__, self.__class__.__name__,
1804 self.expr, id(self))
1805
1806
1807 class ieval(Pipe):
1808 """
1809 Evaluate an expression for each object in the input pipe.
1810
1811 Examples::
1812
1813 >>> ils | ieval("_.abspath()")
1814 >>> sys.path | ieval(ifile)
1815 """
1816
1817 def __init__(self, expr, globals=None, errors="raiseifallfail"):
1818 """
1819 Create an ``ieval`` object. ``expr`` can be a callable or a string
1820 containing an expression. For the meaning of ``globals`` and
1821 ``errors`` see ``ifilter``.
1822 """
1823 self.expr = expr
1824 self.globals = globals
1825 self.errors = errors
1826
1827 def __iter__(self):
1828 if callable(self.expr):
1829 do = self.expr
1830 else:
1831 g = getglobals(self.globals)
1832 expr = compile(self.expr, "ipipe-expression", "eval")
1833 def do(item):
1834 return eval(expr, g, AttrNamespace(item))
1835
1836 ok = 0
1837 exc_info = None
1838 for item in xiter(self.input):
1839 try:
1840 yield do(item)
1841 except (KeyboardInterrupt, SystemExit):
1842 raise
1843 except Exception, exc:
1844 if self.errors == "drop":
1845 pass # Ignore errors
1846 elif self.errors == "keep":
1847 yield item
1848 elif self.errors == "keeperror":
1849 yield exc
1850 elif self.errors == "raise":
1851 raise
1852 elif self.errors == "raiseifallfail":
1853 if exc_info is None:
1854 exc_info = sys.exc_info()
1855 if not ok and exc_info is not None:
1856 raise exc_info[0], exc_info[1], exc_info[2]
1857
1858 def __xrepr__(self, mode="default"):
1859 if mode == "header" or mode == "footer":
1860 input = getattr(self, "input", None)
1861 if input is not None:
1862 for part in xrepr(input, mode):
1863 yield part
1864 yield (astyle.style_default, " | ")
1865 yield (astyle.style_default, "%s(" % self.__class__.__name__)
1866 for part in xrepr(self.expr, "default"):
1867 yield part
1868 yield (astyle.style_default, ")")
1869 else:
1870 yield (astyle.style_default, repr(self))
1871
1872 def __repr__(self):
1873 return "<%s.%s expr=%r at 0x%x>" % \
1874 (self.__class__.__module__, self.__class__.__name__,
1875 self.expr, id(self))
1876
1877
1878 class ienum(Pipe):
1879 """
1880 Enumerate the input pipe (i.e. wrap each input object in an object
1881 with ``index`` and ``object`` attributes).
1882
1883 Examples::
1884
1885 >>> xrange(20) | ieval("_,_*_") | ienum | ifilter("index % 2 == 0") | ieval("object")
1886 """
1887 def __iter__(self):
1888 fields = ("index", "object")
1889 for (index, object) in enumerate(xiter(self.input)):
1890 yield Fields(fields, index=index, object=object)
1891
1892
1893 class isort(Pipe):
1894 """
1895 Sorts the input pipe.
1896
1897 Examples::
1898
1899 >>> ils | isort("size")
1900 >>> ils | isort("_.isdir(), _.lower()", reverse=True)
1901 """
1902
1903 def __init__(self, key=None, globals=None, reverse=False):
1904 """
1905 Create an ``isort`` object. ``key`` can be a callable or a string
1906 containing an expression (or ``None`` in which case the items
1907 themselves will be sorted). If ``reverse`` is true the sort order
1908 will be reversed. For the meaning of ``globals`` see ``ifilter``.
1909 """
1910 self.key = key
1911 self.globals = globals
1912 self.reverse = reverse
1913
1914 def __iter__(self):
1915 if self.key is None:
1916 items = sorted(xiter(self.input), reverse=self.reverse)
1917 elif callable(self.key):
1918 items = sorted(xiter(self.input), key=self.key, reverse=self.reverse)
1919 else:
1920 g = getglobals(self.globals)
1921 key = compile(self.key, "ipipe-expression", "eval")
1922 def realkey(item):
1923 return eval(key, g, AttrNamespace(item))
1924 items = sorted(xiter(self.input), key=realkey, reverse=self.reverse)
1925 for item in items:
1926 yield item
1927
1928 def __xrepr__(self, mode="default"):
1929 if mode == "header" or mode == "footer":
1930 input = getattr(self, "input", None)
1931 if input is not None:
1932 for part in xrepr(input, mode):
1933 yield part
1934 yield (astyle.style_default, " | ")
1935 yield (astyle.style_default, "%s(" % self.__class__.__name__)
1936 for part in xrepr(self.key, "default"):
1937 yield part
1938 if self.reverse:
1939 yield (astyle.style_default, ", ")
1940 for part in xrepr(True, "default"):
1941 yield part
1942 yield (astyle.style_default, ")")
1943 else:
1944 yield (astyle.style_default, repr(self))
1945
1946 def __repr__(self):
1947 return "<%s.%s key=%r reverse=%r at 0x%x>" % \
1948 (self.__class__.__module__, self.__class__.__name__,
1949 self.key, self.reverse, id(self))
1950
1951
1952 tab = 3 # for expandtabs()
1953
1954 def _format(field):
1955 if isinstance(field, str):
1956 text = repr(field.expandtabs(tab))[1:-1]
1957 elif isinstance(field, unicode):
1958 text = repr(field.expandtabs(tab))[2:-1]
1959 elif isinstance(field, datetime.datetime):
1960 # Don't use strftime() here, as this requires year >= 1900
1961 text = "%04d-%02d-%02d %02d:%02d:%02d.%06d" % \
1962 (field.year, field.month, field.day,
1963 field.hour, field.minute, field.second, field.microsecond)
1964 elif isinstance(field, datetime.date):
1965 text = "%04d-%02d-%02d" % (field.year, field.month, field.day)
1966 else:
1967 text = repr(field)
1968 return text
1969
1970
1971 class Display(object):
1972 class __metaclass__(type):
1973 def __ror__(self, input):
1974 return input | self()
1975
1976 def __init__(self, input=None):
1977 self.input = input
1978
1979 def __ror__(self, input):
1980 self.input = input
1981 return self
1982
1983 def display(self):
1984 pass
1985
1986
1987 class iless(Display):
1988 cmd = "less --quit-if-one-screen --LONG-PROMPT --LINE-NUMBERS --chop-long-lines --shift=8 --RAW-CONTROL-CHARS"
1989
1990 def display(self):
1991 try:
1992 pager = os.popen(self.cmd, "w")
1993 try:
1994 for item in xiter(self.input):
1995 first = False
1996 for attr in xattrs(item, "default"):
1997 if first:
1998 first = False
1999 else:
2000 pager.write(" ")
2001 attr = upgradexattr(attr)
2002 if not isinstance(attr, SelfDescriptor):
2003 pager.write(attr.name())
2004 pager.write("=")
2005 pager.write(str(attr.value(item)))
2006 pager.write("\n")
2007 finally:
2008 pager.close()
2009 except Exception, exc:
2010 print "%s: %s" % (exc.__class__.__name__, str(exc))
2011
2012
2013 class _RedirectIO(object):
2014 def __init__(self,*args,**kwargs):
2015 """
2016 Map the system output streams to self.
2017 """
2018 self.stream = StringIO.StringIO()
2019 self.stdout = sys.stdout
2020 sys.stdout = self
2021 self.stderr = sys.stderr
2022 sys.stderr = self
2023
2024 def write(self, text):
2025 """
2026 Write both to screen and to self.
2027 """
2028 self.stream.write(text)
2029 self.stdout.write(text)
2030 if "\n" in text:
2031 self.stdout.flush()
2032
2033 def writelines(self, lines):
2034 """
2035 Write lines both to screen and to self.
2036 """
2037 self.stream.writelines(lines)
2038 self.stdout.writelines(lines)
2039 self.stdout.flush()
2040
2041 def restore(self):
2042 """
2043 Restore the default system streams.
2044 """
2045 self.stdout.flush()
2046 self.stderr.flush()
2047 sys.stdout = self.stdout
2048 sys.stderr = self.stderr
2049
2050
2051 class icap(Table):
2052 """
2053 Execute a python string and capture any output to stderr/stdout.
2054
2055 Examples::
2056
2057 >>> import time
2058 >>> icap("for i in range(10): print i, time.sleep(0.1)")
2059
2060 """
2061 def __init__(self, expr, globals=None):
2062 self.expr = expr
2063 self.globals = globals
2064 log = _RedirectIO()
2065 try:
2066 exec(expr, getglobals(globals))
2067 finally:
2068 log.restore()
2069 self.stream = log.stream
2070
2071 def __iter__(self):
2072 self.stream.seek(0)
2073 for line in self.stream:
2074 yield line.rstrip("\r\n")
2075
2076 def __xrepr__(self, mode="default"):
2077 if mode == "header" or mode == "footer":
2078 yield (astyle.style_default,
2079 "%s(%r)" % (self.__class__.__name__, self.expr))
2080 else:
2081 yield (astyle.style_default, repr(self))
2082
2083 def __repr__(self):
2084 return "%s.%s(%r)" % \
2085 (self.__class__.__module__, self.__class__.__name__, self.expr)
2086
2087
2088 def xformat(value, mode, maxlength):
2089 align = None
2090 full = True
2091 width = 0
2092 text = astyle.Text()
2093 for (style, part) in xrepr(value, mode):
2094 # only consider the first result
2095 if align is None:
2096 if isinstance(style, int):
2097 # (style, text) really is (alignment, stop)
2098 align = style
2099 full = part
2100 continue
2101 else:
2102 align = -1
2103 full = True
2104 if not isinstance(style, int):
2105 text.append((style, part))
2106 width += len(part)
2107 if width >= maxlength and not full:
2108 text.append((astyle.style_ellisis, "..."))
2109 width += 3
2110 break
2111 if align is None: # default to left alignment
2112 align = -1
2113 return (align, width, text)
2114
2115
2116
2117 import astyle
2118
2119 class idump(Display):
2120 # The approximate maximum length of a column entry
2121 maxattrlength = 200
2122
2123 # Style for column names
2124 style_header = astyle.Style.fromstr("white:black:bold")
2125
2126 def __init__(self, input=None, *attrs):
2127 Display.__init__(self, input)
2128 self.attrs = [upgradexattr(attr) for attr in attrs]
2129 self.headerpadchar = " "
2130 self.headersepchar = "|"
2131 self.datapadchar = " "
2132 self.datasepchar = "|"
2133
2134 def display(self):
2135 stream = genutils.Term.cout
2136 allattrs = []
2137 attrset = set()
2138 colwidths = {}
2139 rows = []
2140 for item in xiter(self.input):
2141 row = {}
2142 attrs = self.attrs
2143 if not attrs:
2144 attrs = xattrs(item, "default")
2145 for attr in attrs:
2146 if attr not in attrset:
2147 allattrs.append(attr)
2148 attrset.add(attr)
2149 colwidths[attr] = len(attr.name())
2150 try:
2151 value = attr.value(item)
2152 except (KeyboardInterrupt, SystemExit):
2153 raise
2154 except Exception, exc:
2155 value = exc
2156 (align, width, text) = xformat(value, "cell", self.maxattrlength)
2157 colwidths[attr] = max(colwidths[attr], width)
2158 # remember alignment, length and colored parts
2159 row[attr] = (align, width, text)
2160 rows.append(row)
2161
2162 stream.write("\n")
2163 for (i, attr) in enumerate(allattrs):
2164 attrname = attr.name()
2165 self.style_header(attrname).write(stream)
2166 spc = colwidths[attr] - len(attrname)
2167 if i < len(colwidths)-1:
2168 stream.write(self.headerpadchar*spc)
2169 stream.write(self.headersepchar)
2170 stream.write("\n")
2171
2172 for row in rows:
2173 for (i, attr) in enumerate(allattrs):
2174 (align, width, text) = row[attr]
2175 spc = colwidths[attr] - width
2176 if align == -1:
2177 text.write(stream)
2178 if i < len(colwidths)-1:
2179 stream.write(self.datapadchar*spc)
2180 elif align == 0:
2181 spc = colwidths[attr] - width
2182 spc1 = spc//2
2183 spc2 = spc-spc1
2184 stream.write(self.datapadchar*spc1)
2185 text.write(stream)
2186 if i < len(colwidths)-1:
2187 stream.write(self.datapadchar*spc2)
2188 else:
2189 stream.write(self.datapadchar*spc)
2190 text.write(stream)
2191 if i < len(colwidths)-1:
2192 stream.write(self.datasepchar)
2193 stream.write("\n")
2194
2195
2196 class AttributeDetail(Table):
2197 """
2198 ``AttributeDetail`` objects are use for displaying a detailed list of object
2199 attributes.
2200 """
2201 def __init__(self, object, descriptor):
2202 self.object = object
2203 self.descriptor = descriptor
2204
2205 def __iter__(self):
2206 return self.descriptor.iter(self.object)
2207
2208 def name(self):
2209 return self.descriptor.name()
2210
2211 def attrtype(self):
2212 return self.descriptor.attrtype(self.object)
2213
2214 def valuetype(self):
2215 return self.descriptor.valuetype(self.object)
2216
2217 def doc(self):
2218 return self.descriptor.doc(self.object)
2219
2220 def shortdoc(self):
2221 return self.descriptor.shortdoc(self.object)
2222
2223 def value(self):
2224 return self.descriptor.value(self.object)
2225
2226 def __xattrs__(self, mode="default"):
2227 attrs = ("name()", "attrtype()", "valuetype()", "value()", "shortdoc()")
2228 if mode == "detail":
2229 attrs += ("doc()",)
2230 return attrs
2231
2232 def __xrepr__(self, mode="default"):
2233 yield (-1, True)
2234 valuetype = self.valuetype()
2235 if valuetype is not noitem:
2236 for part in xrepr(valuetype):
2237 yield part
2238 yield (astyle.style_default, " ")
2239 yield (astyle.style_default, self.attrtype())
2240 yield (astyle.style_default, " ")
2241 yield (astyle.style_default, self.name())
2242 yield (astyle.style_default, " of ")
2243 for part in xrepr(self.object):
2244 yield part
2245
2246
2247 try:
2248 from ibrowse import ibrowse
2249 except ImportError:
2250 # No curses (probably Windows) => try igrid
2251 try:
2252 from igrid import igrid
2253 except ImportError:
2254 # no wx either => use ``idump`` as the default display.
2255 defaultdisplay = idump
2256 else:
2257 defaultdisplay = igrid
2258 __all__.append("igrid")
2259 else:
2260 defaultdisplay = ibrowse
2261 __all__.append("ibrowse")
2262
2263
2264 # If we're running under IPython, register our objects with IPython's
2265 # generic function ``result_display``, else install a displayhook
2266 # directly as sys.displayhook
2267 if generics is not None:
2268 def display_display(obj):
2269 return obj.display()
2270 generics.result_display.when_type(Display)(display_display)
2271
2272 def display_tableobject(obj):
2273 return display_display(defaultdisplay(obj))
2274 generics.result_display.when_type(Table)(display_tableobject)
2275
2276 def display_tableclass(obj):
2277 return display_tableobject(obj())
2278 generics.result_display.when_type(Table.__metaclass__)(display_tableclass)
2279 else:
2280 def installdisplayhook():
2281 _originalhook = sys.displayhook
2282 def displayhook(obj):
2283 if isinstance(obj, type) and issubclass(obj, Table):
2284 obj = obj()
2285 if isinstance(obj, Table):
2286 obj = defaultdisplay(obj)
2287 if isinstance(obj, Display):
2288 return obj.display()
2289 else:
2290 _originalhook(obj)
2291 sys.displayhook = displayhook
2292 installdisplayhook()
@@ -0,0 +1,17
1 """ Install various IPython completers
2
3 IPython extension that installs the completers related to external apps.
4
5 The actual implementations are in Extensions/ipy_completers.py
6
7 """
8 import IPython.ipapi
9
10 ip = IPython.ipapi.get()
11
12 from ipy_completers import *
13
14 ip.set_hook('complete_command', apt_completer, re_key = '.*apt-get')
15 ip.set_hook('complete_command', svn_completer, str_key = 'svn')
16 ip.set_hook('complete_command', hg_completer, str_key = 'hg')
17 ip.set_hook('complete_command', bzr_completer, str_key = 'bzr')
@@ -0,0 +1,359
1 #!/usr/bin/env python
2
3 """ Implementations for various useful completers
4
5 See Extensions/ipy_stock_completers.py on examples of how to enable a completer,
6 but the basic idea is to do:
7
8 ip.set_hook('complete_command', svn_completer, str_key = 'svn')
9
10 """
11 import IPython.ipapi
12 import glob,os,shlex,sys
13 import inspect
14 from time import time
15 ip = IPython.ipapi.get()
16
17 try:
18 set
19 except:
20 from sets import Set as set
21
22 TIMEOUT_STORAGE = 3 #Time in seconds after which the rootmodules will be stored
23 TIMEOUT_GIVEUP = 20 #Time in seconds after which we give up
24
25 def quick_completer(cmd, completions):
26 """ Easily create a trivial completer for a command.
27
28 Takes either a list of completions, or all completions in string
29 (that will be split on whitespace)
30
31 Example::
32
33 [d:\ipython]|1> import ipy_completers
34 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
35 [d:\ipython]|3> foo b<TAB>
36 bar baz
37 [d:\ipython]|3> foo ba
38 """
39 if isinstance(completions, basestring):
40
41 completions = completions.split()
42 def do_complete(self,event):
43 return completions
44
45 ip.set_hook('complete_command',do_complete, str_key = cmd)
46
47 def getRootModules():
48 """
49 Returns a list containing the names of all the modules available in the
50 folders of the pythonpath.
51 """
52 modules = []
53 if ip.db.has_key('rootmodules'):
54 return ip.db['rootmodules']
55 t = time()
56 store = False
57 for path in sys.path:
58 modules += moduleList(path)
59 if time() - t >= TIMEOUT_STORAGE and not store:
60 store = True
61 print "\nCaching the list of root modules, please wait!"
62 print "(This will only be done once - type '%rehashx' to " + \
63 "reset cache!)"
64 print
65 if time() - t > TIMEOUT_GIVEUP:
66 print "This is taking too long, we give up."
67 print
68 ip.db['rootmodules'] = []
69 return []
70
71 modules += sys.builtin_module_names
72
73 modules = list(set(modules))
74 if '__init__' in modules:
75 modules.remove('__init__')
76 modules = list(set(modules))
77 if store:
78 ip.db['rootmodules'] = modules
79 return modules
80
81 def moduleList(path):
82 """
83 Return the list containing the names of the modules available in the given
84 folder.
85 """
86
87 if os.path.isdir(path):
88 folder_list = os.listdir(path)
89 else:
90 folder_list = []
91 #folder_list = glob.glob(os.path.join(path,'*'))
92 folder_list = [p for p in folder_list \
93 if os.path.exists(os.path.join(path, p,'__init__.py'))\
94 or p[-3:] in ('.py','.so')\
95 or p[-4:] in ('.pyc','.pyo')]
96
97 folder_list = [os.path.basename(p).split('.')[0] for p in folder_list]
98 return folder_list
99
100 def moduleCompletion(line):
101 """
102 Returns a list containing the completion possibilities for an import line.
103 The line looks like this :
104 'import xml.d'
105 'from xml.dom import'
106 """
107 def tryImport(mod, only_modules=False):
108 def isImportable(module, attr):
109 if only_modules:
110 return inspect.ismodule(getattr(module, attr))
111 else:
112 return not(attr[:2] == '__' and attr[-2:] == '__')
113 try:
114 m = __import__(mod)
115 except:
116 return []
117 mods = mod.split('.')
118 for module in mods[1:]:
119 m = getattr(m,module)
120 if (not hasattr(m, '__file__')) or (not only_modules) or\
121 (hasattr(m, '__file__') and '__init__' in m.__file__):
122 completion_list = [attr for attr in dir(m) if isImportable(m, attr)]
123 completion_list.extend(getattr(m,'__all__',[]))
124 if hasattr(m, '__file__') and '__init__' in m.__file__:
125 completion_list.extend(moduleList(os.path.dirname(m.__file__)))
126 completion_list = list(set(completion_list))
127 if '__init__' in completion_list:
128 completion_list.remove('__init__')
129 return completion_list
130
131 words = line.split(' ')
132 if len(words) == 3 and words[0] == 'from':
133 return ['import ']
134 if len(words) < 3 and (words[0] in ['import','from']) :
135 if len(words) == 1:
136 return getRootModules()
137 mod = words[1].split('.')
138 if len(mod) < 2:
139 return getRootModules()
140 completion_list = tryImport('.'.join(mod[:-1]), True)
141 completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
142 return completion_list
143 if len(words) >= 3 and words[0] == 'from':
144 mod = words[1]
145 return tryImport(mod)
146
147 def vcs_completer(commands, event):
148 """ utility to make writing typical version control app completers easier
149
150 VCS command line apps typically have the format:
151
152 [sudo ]PROGNAME [help] [command] file file...
153
154 """
155
156
157 cmd_param = event.line.split()
158 if event.line.endswith(' '):
159 cmd_param.append('')
160
161 if cmd_param[0] == 'sudo':
162 cmd_param = cmd_param[1:]
163
164 if len(cmd_param) == 2 or 'help' in cmd_param:
165 return commands.split()
166
167 return ip.IP.Completer.file_matches(event.symbol)
168
169
170 pkg_cache = None
171
172 def module_completer(self,event):
173 """ Give completions after user has typed 'import ...' or 'from ...'"""
174
175 # This works in all versions of python. While 2.5 has
176 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
177 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
178 # of possibly problematic side effects.
179 # This search the folders in the sys.path for available modules.
180
181 return moduleCompletion(event.line)
182
183
184 svn_commands = """\
185 add blame praise annotate ann cat checkout co cleanup commit ci copy
186 cp delete del remove rm diff di export help ? h import info list ls
187 lock log merge mkdir move mv rename ren propdel pdel pd propedit pedit
188 pe propget pget pg proplist plist pl propset pset ps resolved revert
189 status stat st switch sw unlock update
190 """
191
192 def svn_completer(self,event):
193 return vcs_completer(svn_commands, event)
194
195
196 hg_commands = """
197 add addremove annotate archive backout branch branches bundle cat
198 clone commit copy diff export grep heads help identify import incoming
199 init locate log manifest merge outgoing parents paths pull push
200 qapplied qclone qcommit qdelete qdiff qfold qguard qheader qimport
201 qinit qnew qnext qpop qprev qpush qrefresh qrename qrestore qsave
202 qselect qseries qtop qunapplied recover remove rename revert rollback
203 root serve showconfig status strip tag tags tip unbundle update verify
204 version
205 """
206
207 def hg_completer(self,event):
208 """ Completer for mercurial commands """
209
210 return vcs_completer(hg_commands, event)
211
212
213
214 bzr_commands = """
215 add annotate bind branch break-lock bundle-revisions cat check
216 checkout commit conflicts deleted diff export gannotate gbranch
217 gcommit gdiff help ignore ignored info init init-repository inventory
218 log merge missing mkdir mv nick pull push reconcile register-branch
219 remerge remove renames resolve revert revno root serve sign-my-commits
220 status testament unbind uncommit unknowns update upgrade version
221 version-info visualise whoami
222 """
223
224 def bzr_completer(self,event):
225 """ Completer for bazaar commands """
226 cmd_param = event.line.split()
227 if event.line.endswith(' '):
228 cmd_param.append('')
229
230 if len(cmd_param) > 2:
231 cmd = cmd_param[1]
232 param = cmd_param[-1]
233 output_file = (param == '--output=')
234 if cmd == 'help':
235 return bzr_commands.split()
236 elif cmd in ['bundle-revisions','conflicts',
237 'deleted','nick','register-branch',
238 'serve','unbind','upgrade','version',
239 'whoami'] and not output_file:
240 return []
241 else:
242 # the rest are probably file names
243 return ip.IP.Completer.file_matches(event.symbol)
244
245 return bzr_commands.split()
246
247
248 def shlex_split(x):
249 """Helper function to split lines into segments."""
250 #shlex.split raise exception if syntax error in sh syntax
251 #for example if no closing " is found. This function keeps dropping
252 #the last character of the line until shlex.split does not raise
253 #exception. Adds end of the line to the result of shlex.split
254 #example: %run "c:/python -> ['%run','"c:/python']
255 endofline=[]
256 while x!="":
257 try:
258 comps=shlex.split(x)
259 if len(endofline)>=1:
260 comps.append("".join(endofline))
261 return comps
262 except ValueError:
263 endofline=[x[-1:]]+endofline
264 x=x[:-1]
265 return ["".join(endofline)]
266
267 def runlistpy(self, event):
268 comps = shlex_split(event.line)
269 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
270
271 #print "\nev=",event # dbg
272 #print "rp=",relpath # dbg
273 #print 'comps=',comps # dbg
274
275 lglob = glob.glob
276 isdir = os.path.isdir
277 if relpath.startswith('~'):
278 relpath = os.path.expanduser(relpath)
279 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*')
280 if isdir(f)]
281
282 # Find if the user has already typed the first filename, after which we
283 # should complete on all files, since after the first one other files may
284 # be arguments to the input script.
285 #filter(
286 if filter(lambda f: f.endswith('.py') or f.endswith('.ipy') or
287 f.endswith('.pyw'),comps):
288 pys = [f.replace('\\','/') for f in lglob('*')]
289 else:
290 pys = [f.replace('\\','/')
291 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
292 lglob(relpath + '*.pyw')]
293 return dirs + pys
294
295
296 def cd_completer(self, event):
297 relpath = event.symbol
298 #print event # dbg
299 if '-b' in event.line:
300 # return only bookmark completions
301 bkms = self.db.get('bookmarks',{})
302 return bkms.keys()
303
304
305 if event.symbol == '-':
306 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
307 # jump in directory history by number
308 fmt = '-%0' + width_dh +'d [%s]'
309 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
310 if len(ents) > 1:
311 return ents
312 return []
313
314 if relpath.startswith('~'):
315 relpath = os.path.expanduser(relpath).replace('\\','/')
316 found = []
317 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
318 if os.path.isdir(f)]:
319 if ' ' in d:
320 # we don't want to deal with any of that, complex code
321 # for this is elsewhere
322 raise IPython.ipapi.TryNext
323 found.append( d )
324
325 if not found:
326 if os.path.isdir(relpath):
327 return [relpath]
328 raise IPython.ipapi.TryNext
329 return found
330
331 def apt_get_packages(prefix):
332 out = os.popen('apt-cache pkgnames')
333 for p in out:
334 if p.startswith(prefix):
335 yield p.rstrip()
336
337
338 apt_commands = """\
339 update upgrade install remove purge source build-dep dist-upgrade
340 dselect-upgrade clean autoclean check"""
341
342 def apt_completer(self, event):
343 """ Completer for apt-get (uses apt-cache internally)
344
345 """
346
347
348 cmd_param = event.line.split()
349 if event.line.endswith(' '):
350 cmd_param.append('')
351
352 if cmd_param[0] == 'sudo':
353 cmd_param = cmd_param[1:]
354
355 if len(cmd_param) == 2 or 'help' in cmd_param:
356 return apt_commands.split()
357
358 return list(apt_get_packages(event.symbol))
359
This diff has been collapsed as it changes many lines, (669 lines changed) Show them Hide them
@@ -0,0 +1,669
1 """ Module with physical constants for use with ipython, profile
2 "physics".
3
4 Definition of Fundamental Physical Constants, CODATA Recommended Values
5
6 Source, Peter J. Mohr and Barry N. Taylor,
7 CODATA Recommended Values of the Fundamental
8 Physical Constants, 1998
9
10 Website: physics.nist.gov/constants
11 """
12 # License: BSD-like
13 # Copyright: Gael Varoquaux (gael.varoquaux@normalesup.org)
14
15 # inspired by maxima's physconst.mac by Cliff Yapp
16
17 #from math import * # math MUST be imported BEFORE PhysicalQInteractive
18 from IPython.Extensions.PhysicalQInteractive import PhysicalQuantityInteractive
19
20 # Math constants:
21
22 # Pi mathematical constants
23 pi = 3.141592653589793238462643383279502884197169399375105820974944592
24
25 # Universal Constants
26 #-------------------------------------------------------------------------
27
28 c = PhysicalQuantityInteractive(299792458 , 'm/s')
29 c.__doc__ = """speed of light in vacuum"""
30 c.__doc__ = "speed of light in vacuum"
31
32 u_0 = PhysicalQuantityInteractive(4*pi*1E-7 , 'N/(A**2)')
33 u_0.__doc__ = """magnetic constant"""
34 mu_0 = PhysicalQuantityInteractive(4*pi*1E-7 , 'N/(A**2)')
35
36 epsilon_0 = PhysicalQuantityInteractive(8.854187817E-12 , 'F/m')
37 epsilon_0.__doc__ = """electric constant """
38
39 Z_0 = PhysicalQuantityInteractive(376.730313461 , 'ohm')
40 Z_0.__doc__ = """characteristic impedance of vacuum """
41
42 G = PhysicalQuantityInteractive(6.673E-11 , 'm**3/(kg*s**2)')
43 G.__doc__ = """Newtonian constant of gravitation """
44
45
46 h = PhysicalQuantityInteractive(6.62606876E-34 , 'J*s')
47 h.__doc__ = """Planck constant """
48
49
50 h_eV = PhysicalQuantityInteractive(4.13566727E-15 , 'eV*s')
51 h_eV.__doc__ = """Planck constant in eVs """
52
53
54 h_bar = PhysicalQuantityInteractive(1.054571596E-34 , 'J*s')
55 h_bar.__doc__ = """Hbar"""
56
57
58 h_bar_eV = PhysicalQuantityInteractive(6.58211889E-16 , 'eV*s')
59 h_bar_eV.__doc__ = """Hbar in eV"""
60
61
62 P_m = PhysicalQuantityInteractive(2.1767E-8 , 'kg')
63 P_m.__doc__ = """Planck mass"""
64
65
66 P_l = PhysicalQuantityInteractive(1.6160E-35 , 'm')
67 P_l.__doc__ = """Planck length """
68
69
70 P_t = PhysicalQuantityInteractive(5.3906E-44 , 's')
71 P_t.__doc__ = """Planck time """
72
73 # Electromagnetic Constants
74 #------------------------------------------------------------------------
75
76 _e = PhysicalQuantityInteractive(1.602176462E-19 , 'C')
77 _e.__doc__ = """elementary charge"""
78 q = _e
79
80
81 capitalphi_0 = PhysicalQuantityInteractive(2.067833636E-15 , 'Wb')
82 capitalphi_0.__doc__ = """magnetic flux quantum """
83 mfq_0 = PhysicalQuantityInteractive(2.067833636E-15 , 'Wb')
84
85
86 G_0 = PhysicalQuantityInteractive(7.748091696E-5 , 'S')
87 G_0.__doc__ = """conductance quantum """
88
89
90 K_J = PhysicalQuantityInteractive(483597.898E9 , 'Hz/V')
91 K_J.__doc__ = """Josephson constant"""
92
93
94 R_K = PhysicalQuantityInteractive(25812.807572 , 'ohm')
95 R_K.__doc__ = """von Klitzing constant"""
96
97
98 u_B = PhysicalQuantityInteractive(927.400899E-26 , 'J/T')
99 u_B.__doc__ = """Bohr magneton"""
100
101 ueVT_B = PhysicalQuantityInteractive(5.788381749E-5 , 'eV/T')
102 ueVT_B.__doc__ = """Bohr magneton in eV T-1"""
103
104
105 u_N = PhysicalQuantityInteractive(5.05078317E-27 , 'J/T')
106 u_N.__doc__ = """nuclear magneton """
107
108 ueVT_N = PhysicalQuantityInteractive(3.152451238E-8 , 'eV/T')
109 ueVT_N.__doc__ = """nuclear magneton in eV T-1 """
110
111 # Atomic and Nuclear Constants
112 # General
113 #-------------------------------------------------------------------------
114 # fine-structure constant
115 alpha = 7.297352533E-3
116
117
118 Ry = PhysicalQuantityInteractive(10973731.568549 , '1/m')
119 Ry.__doc__ = """Rydberg constant """
120 Ry_INF = PhysicalQuantityInteractive(10973731.568549 , '1/m')
121
122
123 a_0 = PhysicalQuantityInteractive(0.5291772083E-10 , 'm')
124 a_0.__doc__ = """Bohr radius """
125
126
127 E_h = PhysicalQuantityInteractive(4.35974381E-18 , 'J')
128 E_h.__doc__ = """Hartree energy """
129
130 Eev_h = PhysicalQuantityInteractive(27.2113834 , 'eV')
131 Eev_h.__doc__ = """Hartree energy in eV """
132
133
134 qcir2 = PhysicalQuantityInteractive(3.636947516E-4 , 'm**2/s')
135 qcir2.__doc__ = """quantum of circulation h/(2me) """
136
137 qcir = PhysicalQuantityInteractive(7.273895032E-4 , 'm**2/s')
138 qcir.__doc__ = """quantum of circulation h/(me) """
139
140 # Electroweak
141 #-------------------------------------------------------------------------
142
143 Fcc = PhysicalQuantityInteractive(1.16639E-5 , '1/GeV**2')
144 Fcc.__doc__ = """Fermi coupling constant """
145 # weak mixing angled W (on-shell scheme)
146 wma_W = 0.2224
147
148 # Electron, e-
149 #-------------------------------------------------------------------------
150
151 m_e = PhysicalQuantityInteractive(9.10938188E-31 , 'kg')
152 m_e.__doc__ = """electron mass """
153
154 m_e_u = PhysicalQuantityInteractive(5.485799110E-4 , 'amu')
155 m_e_u.__doc__ = """electron mass (electron relative atomic mass times amu)"""
156
157 me_J = PhysicalQuantityInteractive(8.18710414E-14 , 'J')
158 me_J.__doc__ = """electron mass - energy equivalent """
159
160 me_MeV = PhysicalQuantityInteractive(0.510998902 , 'MeV')
161 me_MeV.__doc__ = """electron mass - energy equivalent in MeV"""
162
163 # electron-muon mass ratio
164 memu = 4.83633210E-3
165
166 # electron-tau mass ratio
167 metau = 2.87555E-4
168
169 # electron-proton mass ratio
170 memp = 5.446170232E-4
171
172 # electron-neutron mass ratio
173 memn = 5.438673462E-4
174
175 # electron-deuteron mass ratio
176 memd = 2.7244371170E-4
177
178 # electron to alpha particle mass ratio
179 memalpha = 1.3709335611E-4
180
181
182 echargeemass = PhysicalQuantityInteractive(-1.758820174E11 , 'C/kg')
183 echargeemass.__doc__ = """electron charge to mass quotient """
184
185
186 Molar_e = PhysicalQuantityInteractive(5.485799110E-7 , 'kg/mol')
187 Molar_e.__doc__ = """electron molar mass """
188
189
190 lambdaC = PhysicalQuantityInteractive(2.426310215E-12 , 'm')
191 lambdaC.__doc__ = """Compton wavelength """
192
193
194 r_e = PhysicalQuantityInteractive(2.817940285E-15 , 'm')
195 r_e.__doc__ = """classical electron radius """
196
197
198 sigma_e = PhysicalQuantityInteractive(0.665245854E-28 , 'm**2')
199 sigma_e.__doc__ = """Thomson cross section """
200
201
202 u_e = PhysicalQuantityInteractive(-928.476362E-26 , 'J/T')
203 u_e.__doc__ = """electron magnetic moment """
204
205 # electron magnetic moment to Bohr magneton ratio
206 ueuB = -1.0011596521869
207
208 # electron magnetic moment to nuclear magneton ratio
209 ueuN = -1838.2819660
210
211 # electron magnetic moment anomaly |ue|/uB - 1
212 a_e = 1.1596521869E-3
213
214 # electron g-factor
215 g_e = -2.0023193043737
216
217 # electron-muon magnetic moment ratio
218 ueuu = 206.7669720
219
220 # electron-proton magnetic moment ratio
221 ueup = -658.2106875
222
223 # electron to shielded proton magnetic moment ratio (H2O, sphere, 25 C)
224 ueusp = -658.2275954
225
226 # electron-neutron magnetic moment ratio
227 ueun = 960.92050
228
229 # electron-deuteron magnetic moment ratio
230 ueud = -2143.923498
231
232 # electron to shielded helione magnetic moment ratio (gas, sphere, 25 C)
233 ueush = 864.058255
234
235
236 gamma_e = PhysicalQuantityInteractive(1.760859794E11 , '1/(s*T)')
237 gamma_e.__doc__ = """electron gyromagnetic ratio """
238
239 # Muon, u-
240 #-------------------------------------------------------------------------
241
242 m_u = PhysicalQuantityInteractive(1.88353109E-28 , 'kg')
243 m_u.__doc__ = """muon mass """
244
245 mu_u = PhysicalQuantityInteractive(0.1134289168 , 'amu')
246 mu_u.__doc__ = """muon mass in muon relative atomic mass times amu """
247
248
249 muc2_J = PhysicalQuantityInteractive(1.69283332E-11 , 'J')
250 muc2_J.__doc__ = """energy equivalent """
251
252 muc2_MeV = PhysicalQuantityInteractive(105.6583568 , 'MeV')
253 muc2_MeV.__doc__ = """energy equivalent in MeV """
254
255 # muon-electron mass ratio
256 mume = 206.7682657
257
258 # muon-tau mass ratio
259 mum = 5.94572E-2
260
261 # muon-proton mass ratio
262 mump = 0.1126095173
263
264 # muon-neutron mass ratio
265 mumn = 0.1124545079
266
267
268 Molar_u = PhysicalQuantityInteractive(0.1134289168E-3 , 'kg/mol')
269 Molar_u.__doc__ = """muon molar mass """
270
271
272 lambda_C_u = PhysicalQuantityInteractive(11.73444197E-15 , 'm')
273 lambda_C_u.__doc__ = """muon Compton wavelength """
274
275
276 uu = PhysicalQuantityInteractive(-4.49044813E-26 , 'J/T')
277 uu.__doc__ = """muon magnetic moment """
278
279 # ratio of muon magnetic moment to Bohr magneton ratio
280 uuuB = -4.84197085E-3
281
282 # ratio of muon magnetic moment to nuclear magneton ratio
283 uuuN = -8.89059770
284
285 # muon magnetic moment anomaly |uu|/(e /2mu) - 1
286 a_u = 1.16591602E-3
287
288 # muon g-factor -2(1 + au)
289 g_u = -2.0023318320
290
291 # muon-proton magnetic moment ratio
292 uuup = -3.18334539
293
294 # Tau, tau-
295 #-------------------------------------------------------------------------
296
297 m_tau = PhysicalQuantityInteractive(3.16788E-27 , 'kg')
298 m_tau.__doc__ = """tau mass """
299
300 mu_tau = PhysicalQuantityInteractive(1.90774 , 'amu')
301 mu_tau.__doc__ = """tau mass (tau relative atomic mass times amu) """
302
303
304 mtauc2_J = PhysicalQuantityInteractive(2.84715E-10 , 'J')
305 mtauc2_J.__doc__ = """tau mass energy equivalent """
306
307
308 mtauc2_MeV = PhysicalQuantityInteractive(1777.05 , 'MeV')
309 mtauc2_MeV.__doc__ = """tau mass energy equivalent in MeV """
310
311 # tau-electron mass ratio
312 mtaume = 3477.60
313
314 # tau-muon mass ratio
315 mtaumu = 16.8188
316
317 # tau-proton mass ratio
318 mtaump = 1.89396
319
320 # tau-neutron mass ratio
321 mtaumn = 1.89135
322
323
324 Molar_tau = PhysicalQuantityInteractive(1.90774E-3 , 'kg/mol')
325 Molar_tau.__doc__ = """tau molar mass """
326
327
328 lambda_C_tau = PhysicalQuantityInteractive(0.69770E-15 , 'm')
329 lambda_C_tau.__doc__ = """tau Compton wavelength """
330
331 # Proton, p
332 #-------------------------------------------------------------------------
333
334 m_p = PhysicalQuantityInteractive(1.67262158E-27 , 'kg')
335 m_p.__doc__ = """proton mass """
336
337 mu_p = PhysicalQuantityInteractive(1.00727646688 , 'amu')
338 mu_p.__doc__ = """proton mass (proton relative atomic mass times amu) """
339
340
341 mpc2_J = PhysicalQuantityInteractive(1.50327731E-10 , 'J')
342 mpc2_J.__doc__ = """energy equivalent """
343
344 mpc2_MeV = PhysicalQuantityInteractive(938.271998 , 'MeV')
345 mpc2_MeV.__doc__ = """energy equivalent in MeV """
346
347 # proton-electron mass ratio
348 mpme = 1836.1526675
349
350 # proton-muon mass ratio
351 mpmu = 8.88024408
352
353 # proton-tau mass ratio
354 mpmtau = 0.527994
355
356 # proton-neutron mass ratio
357 mpmn = 0.99862347855
358
359
360 emp = PhysicalQuantityInteractive(9.57883408E7 , 'C/kg')
361 emp.__doc__ = """proton charge to mass quotient """
362
363
364 Molar_p = PhysicalQuantityInteractive(1.00727646688E-3 , 'kg/mol')
365 Molar_p.__doc__ = """proton molar mass """
366
367
368 lambda_C_p = PhysicalQuantityInteractive(1.321409847E-15 , 'm')
369 lambda_C_p.__doc__ = """proton Compton wavelength h/mpc """
370
371
372 up = PhysicalQuantityInteractive(1.410606633E-26 , 'J/T')
373 up.__doc__ = """proton magnetic moment """
374
375 # proton magnetic moment to Bohr magneton ratio
376 upuB = 1.521032203E-3
377
378 # proton magnetic moment to nuclear magneton ratio
379 upuN = 2.792847337
380
381 # proton g-factor 2up/uN
382 g_p = 5.585694675
383
384 # proton-neutron magnetic moment ratio
385 upun = -1.45989805
386
387
388 usp = PhysicalQuantityInteractive(1.410570399E-26 , 'J/T')
389 usp.__doc__ = """shielded proton magnetic moment (H2O, sphere, 25 C)"""
390
391 # shielded proton magnetic moment to Bohr magneton ratio
392 uspuB = 1.520993132E-3
393
394 # shielded proton magnetic moment to nuclear magneton ratio
395 uspuN = 2.792775597
396
397 # proton magnetic shielding correction 1 - u p/up (H2O, sphere, 25 C)
398 spc = 25.687E-6
399
400
401 gamma_p = PhysicalQuantityInteractive(2.67522212E8 , '1/(s*T)')
402 gamma_p.__doc__ = """proton gyromagnetic ratio """
403
404
405 gamma_sp = PhysicalQuantityInteractive(2.67515341E8 , '1/(s*T)')
406 gamma_sp.__doc__ = """shielded proton gyromagnetic ratio (H2O, sphere, 25 C)"""
407
408 # Neutron, n
409 #-------------------------------------------------------------------------
410
411 m_n = PhysicalQuantityInteractive(1.67492716E-27 , 'kg')
412 m_n.__doc__ = """neutron mass """
413
414 mu_n = PhysicalQuantityInteractive(1.00866491578 , 'amu')
415 mu_n.__doc__ = """neutron mass (neutron relative atomic mass times amu) """
416
417
418 mnc2_J = PhysicalQuantityInteractive(1.50534946E-10 , 'J')
419 mnc2_J.__doc__ = """neutron mass energy equivalent """
420
421
422 mnc2_MeV = PhysicalQuantityInteractive(939.565330 , 'MeV')
423 mnc2_MeV.__doc__ = """neutron mass energy equivalent in MeV """
424
425 # neutron-electron mass ratio
426 mnme = 1838.6836550
427
428 # neutron-muon mass ratio
429 mnmu = 8.89248478
430
431 # neutron-tau mass ratio
432 mnm = 0.528722
433
434 # neutron-proton mass ratio
435 mnmp = 1.00137841887
436
437
438 Molar_n = PhysicalQuantityInteractive(1.00866491578E-3 , 'kg/mol')
439 Molar_n.__doc__ = """neutron molar mass """
440
441
442 lambda_C_n = PhysicalQuantityInteractive(1.319590898E-15 , 'm')
443 lambda_C_n.__doc__ = """neutron Compton wavelength"""
444
445
446 un = PhysicalQuantityInteractive(-0.96623640E-26 , 'J/T')
447 un.__doc__ = """neutron magnetic moment """
448
449 # neutron magnetic moment to Bohr magneton ratio
450 unuB = -1.04187563E-3
451
452 # neutron magnetic moment to nuclear magneton ratio
453 unuN = -1.91304272
454
455 # neutron g-factor
456 g_n = -3.82608545
457
458 # neutron-electron magnetic moment ratio
459 unue = 1.04066882E-3
460
461 # neutron-proton magnetic moment ratio
462 unup = -0.68497934
463
464 # neutron to shielded proton magnetic moment ratio (H2O, sphere, 25 C)
465 unusp = -0.68499694
466
467
468 gamma_n = PhysicalQuantityInteractive(1.83247188E8 , '1/(s*T)')
469 gamma_n.__doc__ = """neutron gyromagnetic ratio """
470
471 # Deuteron, d
472 #-------------------------------------------------------------------------
473
474 m_d = PhysicalQuantityInteractive(3.34358309E-27 , 'kg')
475 m_d.__doc__ = """deuteron mass """
476
477
478 mu_d = PhysicalQuantityInteractive(2.01355321271 , 'amu')
479 mu_d.__doc__ = """deuteron mass (deuteron relative atomic mass times amu) """
480
481
482 mdc2_J = PhysicalQuantityInteractive(3.00506262E-10 , 'J')
483 mdc2_J.__doc__ = """deuteron mass energy equivalent """
484
485
486 mdc2_eV = PhysicalQuantityInteractive(1875.612762 , 'MeV')
487 mdc2_eV.__doc__ = """deuteron mass energy equivalent in MeV """
488
489 # deuteron-electron mass ratio
490 mdme = 3670.4829550
491
492 # deuteron-proton mass ratio
493 mdmp = 1.99900750083
494
495
496 Molar_d = PhysicalQuantityInteractive(2.01355321271E-3 , 'kg/mol')
497 Molar_d.__doc__ = """deuteron molar mass """
498
499
500 ud = PhysicalQuantityInteractive(0.433073457E-26 , 'J/T')
501 ud.__doc__ = """deuteron magnetic moment """
502
503 # deuteron magnetic moment to Bohr magneton ratio
504 uduB = 0.4669754556E-3
505
506 # deuteron magnetic moment to nuclear magneton ratio
507 uduN = 0.8574382284
508
509 # deuteron-electron magnetic moment ratio
510 udue = -4.664345537E-4
511
512 # deuteron-proton magnetic moment ratio
513 udup = 0.3070122083
514
515 # deuteron-neutron magnetic moment ratio
516 udun = -0.44820652
517
518 # Helion, h
519 #-------------------------------------------------------------------------
520
521 m_h = PhysicalQuantityInteractive(5.00641174E-27 , 'kg')
522 m_h.__doc__ = """helion mass """
523
524
525 mu_h = PhysicalQuantityInteractive(3.01493223469 , 'amu')
526 mu_h.__doc__ = """helion mass (helion relative atomic mass times amu) """
527
528
529 mhc2_J = PhysicalQuantityInteractive(4.49953848E-10 , 'J')
530 mhc2_J.__doc__ = """helion mass energy equivalent """
531
532 mhc2_MeV = PhysicalQuantityInteractive(2808.39132 , 'MeV')
533 mhc2_MeV.__doc__ = """helion mass energy equivalent in MeV """
534
535 # helion-electron mass ratio
536 mhme = 5495.885238
537
538 # helion-proton mass ratio
539 mhmp = 2.99315265850
540
541
542 Molar_h = PhysicalQuantityInteractive(3.01493223469E-3 , 'kg/mol')
543 Molar_h.__doc__ = """helion molar mass """
544
545
546 ush = PhysicalQuantityInteractive(-1.074552967E-26 , 'J/T')
547 ush.__doc__ = """shielded helion magnetic moment (gas, sphere, 25 C)"""
548
549 # shielded helion magnetic moment to Bohr magneton ratio
550 ushuB = -1.158671474E-3
551
552 # shielded helion magnetic moment to nuclear magneton ratio
553 ushuN = -2.127497718
554
555 # shielded helion to proton magnetic moment ratio (gas, sphere, 25 C)
556 ushup = -0.761766563
557
558 # shielded helion to shielded proton magnetic moment ratio (gas/H2O, spheres, 25 C)
559 ushusp = -0.7617861313
560
561
562 gamma_h = PhysicalQuantityInteractive(2.037894764E8 , '1/(s*T)')
563 gamma_h.__doc__ = """shielded helion gyromagnetic (gas, sphere, 25 C) """
564
565 # Alpha particle,
566 #-------------------------------------------------------------------------
567
568 m_alpha = PhysicalQuantityInteractive(6.64465598E-27 , 'kg')
569 m_alpha.__doc__ = """alpha particle mass """
570
571 mu_alpha = PhysicalQuantityInteractive(4.0015061747 , 'amu')
572 mu_alpha.__doc__ = """alpha particle mass (alpha particle relative atomic mass times amu) """
573
574
575 malphac2_J = PhysicalQuantityInteractive(5.97191897E-10 , 'J')
576 malphac2_J.__doc__ = """alpha particle mass energy equivalent """
577
578
579 malphac2_MeV = PhysicalQuantityInteractive(3727.37904 , 'MeV')
580 malphac2_MeV.__doc__ = """alpha particle mass energy equivalent in MeV """
581
582 # alpha particle to electron mass ratio
583 malphame = 7294.299508
584
585 # alpha particle to proton mass ratio
586 malphamp = 3.9725996846
587
588
589 Molar_alpha = PhysicalQuantityInteractive(4.0015061747E-3 , 'kg/mol')
590 Molar_alpha.__doc__ = """alpha particle molar mass"""
591
592 # PHYSICO-CHEMICAL
593 #-------------------------------------------------------------------------
594
595 N_A = PhysicalQuantityInteractive(6.02214199E23 , '1/mol')
596 N_A.__doc__ = """Avogadro constant """
597 L = PhysicalQuantityInteractive(6.02214199E23 , '1/mol')
598
599
600 m_u = PhysicalQuantityInteractive(1.66053873E-27 , 'kg')
601 m_u.__doc__ = """atomic mass constant mu = 112m(12C) = 1 u = 10E-3 kg mol-1/NA"""
602 # atomic mass constant mu = 112m(12C) = 1 u = 10E-3 kg mol-1/NA
603 amu = m_u
604
605
606 muc2_J = PhysicalQuantityInteractive(1.49241778E-10 , 'J')
607 muc2_J.__doc__ = """energy equivalent of the atomic mass constant"""
608
609
610 muc2_MeV = PhysicalQuantityInteractive(931.494013 , 'MeV')
611 muc2_MeV.__doc__ = """energy equivalent of the atomic mass constant in MeV """
612
613
614 F = PhysicalQuantityInteractive(96485.3415 , 'C/mol')
615 F.__doc__ = """Faraday constant"""
616
617
618 N_Ah = PhysicalQuantityInteractive(3.990312689E-10 , 'J*s/mol')
619 N_Ah.__doc__ = """molar Planck constant """
620
621
622 R = PhysicalQuantityInteractive(8.314472 , 'J/(mol*K)')
623 R.__doc__ = """molar gas constant """
624
625
626 k_J = PhysicalQuantityInteractive(1.3806503E-23 , 'J/K')
627 k_J.__doc__ = """Boltzmann constant """
628
629
630 k_eV = PhysicalQuantityInteractive(8.617342E-5 , 'eV/K')
631 k_eV.__doc__ = """Boltzmann constant in eV """
632
633
634 n_0 = PhysicalQuantityInteractive(2.6867775E25 , '1/m**3')
635 n_0.__doc__ = """Loschmidt constant NA/Vm """
636
637
638 Vm_1 = PhysicalQuantityInteractive(22.413996E-3 , 'm**3/mol')
639 Vm_1.__doc__ = """molar volume of ideal gas RT/p T = 273.15 K, p = 101.325 kPa """
640
641 Vm_2 = PhysicalQuantityInteractive(22.710981E-3 , 'm**3/mol')
642 Vm_2.__doc__ = """molar volume of ideal gas RT/p T = 273.15 K, p = 100 kPa """
643
644 # Sackur-Tetrode constant (absolute entropy constant) 52 + ln_(2 mukT1/h2)3/2kT1/p0
645 # T1 = 1 K, p0 = 100 kPa
646 S_0R_1 = -1.1517048
647 # T1 = 1 K, p0 = 101.325 kPa
648 S_0R_2 = -1.1648678
649
650
651 sigma = PhysicalQuantityInteractive(5.670400E-8 , 'W/(m**2*K**4)')
652 sigma.__doc__ = """Stefan-Boltzmann constant """
653
654
655 c_1 = PhysicalQuantityInteractive(3.74177107E-16 , 'W*m**2')
656 c_1.__doc__ = """first radiation constant"""
657
658
659 c_1L = PhysicalQuantityInteractive(1.191042722E-16 , 'W*m**2/sr')
660 c_1L.__doc__ = """first radiation constant for spectral radiance"""
661
662
663 c_2 = PhysicalQuantityInteractive(1.4387752E-2 , 'm*K')
664 c_2.__doc__ = """second radiation constant"""
665
666
667 b = PhysicalQuantityInteractive(2.8977686E-3 , 'm*K')
668 b.__doc__ = """Wien displacement law constant b = maxT = c2/4.965 114231... """
669
@@ -0,0 +1,62
1 """ Set default options for IPython.
2
3 Just import this module to get reasonable defaults for everything.
4
5 These configurations used to be performed in ipythonrc (or ipythonrc.ini).
6 Therefore importing this in your config files makes ipython basically
7 ignore your ipythonrc. This is *not* imported by default, you need to import
8 this manually in one of your config files.
9
10 You can further override these defaults in e.g. your ipy_user_config.py,
11 ipy_profile_PROFILENAME etc.
12
13 """
14
15 import IPython.rlineimpl as readline
16 import IPython.ipapi
17 ip = IPython.ipapi.get()
18
19 o = ip.options
20
21 o.colors = "Linux"
22 o.color_info=1
23 o.confirm_exit=1
24 o.pprint=1
25 o.multi_line_specials=1
26 o.xmode="Context"
27
28
29 o.prompt_in1='In [\#]: '
30 o.prompt_in2 =' .\D.: '
31 o.prompt_out = 'Out[\#]: '
32 o.prompts_pad_left=1
33
34 o.autoindent = 1
35
36 o.readline_remove_delims="-/~"
37 o.readline_merge_completions=1
38
39 o.readline = 1
40
41 rlopts = """\
42 tab: complete
43 "\C-l": possible-completions
44 set show-all-if-ambiguous on
45 "\C-o": tab-insert
46 "\M-i": " "
47 "\M-o": "\d\d\d\d"
48 "\M-I": "\d\d\d\d"
49 "\C-r": reverse-search-history
50 "\C-s": forward-search-history
51 "\C-p": history-search-backward
52 "\C-n": history-search-forward
53 "\e[A": history-search-backward
54 "\e[B": history-search-forward
55 "\C-k": kill-line
56 "\C-u": unix-line-discard"""
57
58 if readline.have_readline:
59 for cmd in rlopts.split('\n'):
60 readline.parse_and_bind(cmd)
61
62
@@ -0,0 +1,84
1 """ 'editor' hooks for common editors that work well with ipython
2
3 They should honor the line number argument, at least.
4
5 Contributions are *very* welcome.
6 """
7
8 import IPython.ipapi
9 ip = IPython.ipapi.get()
10
11 from IPython.Itpl import itplns
12 import os
13
14 def install_editor(run_template, wait = False):
15 """ Gets a template in format "myeditor bah bah $file bah bah $line"
16
17 $file will be replaced by file name, $line by line number (or 0).
18 Installs the editor that is called by IPython, instead of the default
19 notepad or vi.
20
21 If wait is true, wait until the user presses enter before returning,
22 to facilitate non-blocking editors that exit immediately after
23 the call.
24 """
25
26 def call_editor(self, file, line=0):
27 if line is None:
28 line = 0
29 cmd = itplns(run_template, locals())
30 print ">",cmd
31 os.system(cmd)
32 if wait:
33 raw_input("Press Enter when done editing:")
34
35 ip.set_hook('editor',call_editor)
36
37
38 # in these, exe is always the path/name of the executable. Useful
39 # if you don't have the editor directory in your path
40
41 def komodo(exe = 'komodo'):
42 """ Activestate Komodo [Edit] """
43 install_editor(exe + ' -l $line "$file"', wait = True)
44
45 def scite(exe = "scite"):
46 """ SciTE or Sc1 """
47 install_editor(exe + ' "$file" -goto:$line')
48
49 def notepadplusplus(exe = 'notepad++'):
50 """ Notepad++ http://notepad-plus.sourceforge.net """
51 install_editor(exe + ' -n$line "$file"')
52
53 def jed(exe = 'jed'):
54 """ JED, the lightweight emacsish editor """
55 install_editor(exe + ' +$line "$file"')
56
57 def idle(exe = None):
58 """ Idle, the editor bundled with python
59
60 Should be pretty smart about finding the executable.
61 """
62 if exe is None:
63 import idlelib
64 p = os.path.dirname(idlelib.__file__)
65 exe = p + '/idle.py'
66 install_editor(exe + ' "$file"')
67
68
69 # these are untested, report any problems
70
71 def emacs(exe = 'emacs'):
72 install_editor(exe + ' +$line "$file"')
73
74 def gnuclient(exe= 'gnuclient'):
75 install_editor(exe + ' -nw +$line "$file"')
76
77 def crimson_editor(exe = 'cedt.exe'):
78 install_editor(exe + ' /L:$line "$file"')
79
80 def kate(exe = 'kate'):
81 install_editor(exe + ' -u -l $line "$file"')
82
83
84 No newline at end of file
@@ -0,0 +1,76
1 import IPython.ipapi
2 ip = IPython.ipapi.get()
3
4 import os,pprint
5
6 def export(filename = None):
7
8 lines = ['import IPython.ipapi', 'ip = IPython.ipapi.get()','']
9
10 vars = ip.db.keys('autorestore/*')
11 vars.sort()
12 varstomove = []
13 get = ip.db.get
14
15 macros = []
16 variables = []
17
18 for var in vars:
19 k = os.path.basename(var)
20 v = get(var)
21
22 if k.startswith('_'):
23 continue
24 if isinstance(v, IPython.macro.Macro):
25 macros.append((k,v))
26 if type(v) in [int, str, float]:
27 variables.append((k,v))
28
29
30
31 if macros:
32 lines.extend(['# === Macros ===' ,''])
33 for k,v in macros:
34 lines.append("ip.defmacro('%s'," % k)
35 for line in v.value.splitlines():
36 lines.append(' ' + repr(line+'\n'))
37 lines.extend([')', ''])
38
39 if variables:
40 lines.extend(['','# === Variables ===',''])
41 for k,v in variables:
42 varstomove.append(k)
43 lines.append('%s = %s' % (k,repr(v)))
44
45 lines.append('ip.to_user_ns("%s")' % (' '.join(varstomove)))
46
47 bkms = ip.db.get('bookmarks',{})
48
49 if bkms:
50 lines.extend(['','# === Bookmarks ===',''])
51 lines.append("ip.db['bookmarks'] = %s " % pprint.pformat(bkms, indent = 2) )
52
53 aliases = ip.db.get('stored_aliases', {} )
54
55 if aliases:
56 lines.extend(['','# === Alias definitions ===',''])
57 for k,v in aliases.items():
58 try:
59 lines.append("ip.defalias('%s', %s)" % (k, repr(v[1])))
60 except (AttributeError, TypeError):
61 pass
62
63 env = ip.db.get('stored_env')
64 if env:
65 lines.extend(['','# === Stored env vars ===',''])
66 lines.append("ip.db['stored_env'] = %s " % pprint.pformat(env, indent = 2) )
67
68
69
70 out = '\n'.join(lines)
71
72 if filename:
73 open(filename,'w').write(out)
74 else:
75 print out
76
@@ -0,0 +1,43
1 """ IPython extension management tools.
2
3 After installation, you'll have the 'extutil' object in your namespace.
4 to.
5 """
6
7 # for the purposes of this module, every module that has the name 'ip' globally
8 # installed as below is an IPython extension
9
10 import IPython.ipapi
11 ip = IPython.ipapi.get()
12
13 import sys,textwrap,inspect
14
15 def indent(s, ind= ' '):
16 return '\n'.join([ind +l for l in s.splitlines()])
17
18 class ExtUtil:
19 """ IPython extensios (ipy_* etc.) management utilities """
20
21 def describe(self):
22 for n,mod in self._active():
23 doc = inspect.getdoc(mod)
24 if doc:
25 print '== %s ==' % n
26 print indent(doc)
27
28
29 def ls(self):
30 """ Show list of installed extensions. """
31 for n,m in self._active():
32 print '%-20s %s' % (n,m.__file__.replace('\\','/'))
33 def _active(self):
34 act = []
35 for mname,m in sys.modules.items():
36 o = getattr(m, 'ip', None)
37 if isinstance(o, IPython.ipapi.IPApi):
38 act.append((mname,m))
39 act.sort()
40 return act
41
42 extutil = ExtUtil()
43 ip.to_user_ns('extutil')
@@ -0,0 +1,244
1 """ File system operations
2
3 Contains: Simple variants of normal unix shell commands (icp, imv, irm,
4 imkdir, igrep).
5
6 Some "otherwise handy" utils ('collect' for gathering files to
7 ~/_ipython/collect, 'inote' for collecting single note lines to
8 ~/_ipython/note.txt)
9
10 Mostly of use for bare windows installations where cygwin/equivalent is not
11 installed and you would otherwise need to deal with dos versions of the
12 commands (that e.g. don't understand / as path separator). These can
13 do some useful tricks on their own, though (like use 'mglob' patterns).
14
15 Not to be confused with ipipe commands (ils etc.) that also start with i.
16 """
17
18 import IPython.ipapi
19 ip = IPython.ipapi.get()
20
21 import shutil,os,shlex
22 from IPython.external import mglob
23 from IPython.external.path import path
24 from IPython.ipapi import UsageError
25
26 def parse_args(args):
27 """ Given arg string 'CMD files... target', return ([files], target) """
28
29 tup = args.split(None, 1)
30 if len(tup) == 1:
31 raise UsageError("Expected arguments for " + tup[0])
32
33 tup2 = shlex.split(tup[1])
34
35 flist, trg = mglob.expand(tup2[0:-1]), tup2[-1]
36 if not flist:
37 raise UsageError("No files found:" + str(tup2[0:-1]))
38 return flist, trg
39
40 def icp(ip,arg):
41 """ icp files... targetdir
42
43 Copy all files to target, creating dirs for target if necessary
44
45 icp srcdir dstdir
46
47 Copy srcdir to distdir
48
49 """
50 import distutils.dir_util
51
52 fs, targetdir = parse_args(arg)
53 if not os.path.isdir(targetdir) and len(fs) > 1:
54 distutils.dir_util.mkpath(targetdir,verbose =1)
55 for f in fs:
56 if os.path.isdir(f):
57 shutil.copytree(f, targetdir)
58 else:
59 shutil.copy2(f,targetdir)
60 return fs
61 ip.defalias("icp",icp)
62
63 def imv(ip,arg):
64 """ imv src tgt
65
66 Move source to target.
67 """
68
69 fs, target = parse_args(arg)
70 if len(fs) > 1:
71 assert os.path.isdir(target)
72 for f in fs:
73 shutil.move(f, target)
74 return fs
75 ip.defalias("imv",imv)
76
77 def irm(ip,arg):
78 """ irm path[s]...
79
80 Remove file[s] or dir[s] path. Dirs are deleted recursively.
81 """
82 try:
83 paths = mglob.expand(arg.split(None,1)[1])
84 except IndexError:
85 raise UsageError("%irm paths...")
86 import distutils.dir_util
87 for p in paths:
88 print "rm",p
89 if os.path.isdir(p):
90 distutils.dir_util.remove_tree(p, verbose = 1)
91 else:
92 os.remove(p)
93
94 ip.defalias("irm",irm)
95
96 def imkdir(ip,arg):
97 """ imkdir path
98
99 Creates dir path, and all dirs on the road
100 """
101 import distutils.dir_util
102 targetdir = arg.split(None,1)[1]
103 distutils.dir_util.mkpath(targetdir,verbose =1)
104
105 ip.defalias("imkdir",imkdir)
106
107 def igrep(ip,arg):
108 """ igrep PAT files...
109
110 Very dumb file scan, case-insensitive.
111
112 e.g.
113
114 igrep "test this" rec:*.py
115
116 """
117 elems = shlex.split(arg)
118 dummy, pat, fs = elems[0], elems[1], mglob.expand(elems[2:])
119 res = []
120 for f in fs:
121 found = False
122 for l in open(f):
123 if pat.lower() in l.lower():
124 if not found:
125 print "[[",f,"]]"
126 found = True
127 res.append(f)
128 print l.rstrip()
129 return res
130
131 ip.defalias("igrep",igrep)
132
133 def collect(ip,arg):
134 """ collect foo/a.txt rec:bar=*.py
135
136 Copies foo/a.txt to ~/_ipython/collect/foo/a.txt and *.py from bar,
137 likewise
138
139 Without args, try to open ~/_ipython/collect dir (in win32 at least).
140 """
141 from IPython.external.path import path
142 basedir = path(ip.options.ipythondir + '/collect')
143 try:
144 fs = mglob.expand(arg.split(None,1)[1])
145 except IndexError:
146 os.startfile(basedir)
147 return
148 for f in fs:
149 f = path(f)
150 trg = basedir / f.splitdrive()[1].lstrip('/\\')
151 if f.isdir():
152 print "mkdir",trg
153 trg.makedirs()
154 continue
155 dname = trg.dirname()
156 if not dname.isdir():
157 dname.makedirs()
158 print f,"=>",trg
159 shutil.copy2(f,trg)
160
161 ip.defalias("collect",collect)
162
163 def inote(ip,arg):
164 """ inote Hello world
165
166 Adds timestamp and Hello world to ~/_ipython/notes.txt
167
168 Without args, opens notes.txt for editing.
169 """
170 import time
171 fname = ip.options.ipythondir + '/notes.txt'
172
173 try:
174 entry = " === " + time.asctime() + ': ===\n' + arg.split(None,1)[1] + '\n'
175 f= open(fname, 'a').write(entry)
176 except IndexError:
177 ip.IP.hooks.editor(fname)
178
179 ip.defalias("inote",inote)
180
181 def pathobj_mangle(p):
182 return p.replace(' ', '__').replace('.','DOT')
183 def pathobj_unmangle(s):
184 return s.replace('__',' ').replace('DOT','.')
185
186
187
188 class PathObj(path):
189 def __init__(self,p):
190 self.path = p
191 if p != '.':
192 self.ents = [pathobj_mangle(ent) for ent in os.listdir(p)]
193 else:
194 self.ents = None
195 def __complete__(self):
196 if self.path != '.':
197 return self.ents
198 self.ents = [pathobj_mangle(ent) for ent in os.listdir('.')]
199 return self.ents
200 def __getattr__(self,name):
201 if name in self.ents:
202 if self.path.endswith('/'):
203 sep = ''
204 else:
205 sep = '/'
206
207 tgt = self.path + sep + pathobj_unmangle(name)
208 #print "tgt",tgt
209 if os.path.isdir(tgt):
210 return PathObj(tgt)
211 if os.path.isfile(tgt):
212 return path(tgt)
213
214 raise AttributeError, name # <<< DON'T FORGET THIS LINE !!
215 def __str__(self):
216 return self.path
217
218 def __repr__(self):
219 return "<PathObj to %s>" % self.path
220
221 def __call__(self):
222 print "cd:",self.path
223 os.chdir(self.path)
224
225 def complete_pathobj(obj, prev_completions):
226 if hasattr(obj,'__complete__'):
227 res = obj.__complete__()
228 if res:
229 return res
230 # just return normal attributes of 'path' object if the dir is empty
231 raise IPython.ipapi.TryNext
232
233 complete_pathobj = IPython.generics.complete_object.when_type(PathObj)(complete_pathobj)
234
235 def test_pathobj():
236 #p = PathObj('c:/prj')
237 #p2 = p.cgi
238 #print p,p2
239 rootdir = PathObj("/")
240 startmenu = PathObj("d:/Documents and Settings/All Users/Start Menu/Programs")
241 cwd = PathObj('.')
242 ip.to_user_ns("rootdir startmenu cwd")
243
244 #test_pathobj() No newline at end of file
@@ -0,0 +1,38
1 #!/usr/bin/env python
2
3
4 """
5 Add %global magic for GNU Global usage.
6
7 http://www.gnu.org/software/global/
8
9 """
10
11 import IPython.ipapi
12 ip = IPython.ipapi.get()
13 import os
14
15 # alter to your liking
16 global_bin = 'd:/opt/global/bin/global'
17
18 def global_f(self,cmdline):
19 simple = 0
20 if '-' not in cmdline:
21 cmdline = '-rx ' + cmdline
22 simple = 1
23
24 lines = [l.rstrip() for l in os.popen( global_bin + ' ' + cmdline ).readlines()]
25
26 if simple:
27 parts = [l.split(None,3) for l in lines]
28 lines = ['%s [%s]\n%s' % (p[2].rjust(70),p[1],p[3].rstrip()) for p in parts]
29 print "\n".join(lines)
30
31 ip.expose_magic('global', global_f)
32
33 def global_completer(self,event):
34 compl = [l.rstrip() for l in os.popen(global_bin + ' -c ' + event.symbol).readlines()]
35 return compl
36
37 ip.set_hook('complete_command', global_completer, str_key = '%global')
38
@@ -0,0 +1,80
1 import os,sys
2
3 import ipy_rehashdir,glob
4 from ipy_rehashdir import selflaunch, PyLauncher
5
6 def pylaunchers():
7 """Create launchers for python scripts in cwd and store them in alias table
8
9 This is useful if you want to invoke .py scripts from ipykit session,
10 just adding .py files in PATH does not work without file association.
11
12 .ipy files will be run like macros.
13
14 """
15 fs = glob.glob('*.py') + glob.glob('*.ipy')
16 for f in fs:
17 l = PyLauncher(f)
18 n = os.path.splitext(f)[0]
19 ip.defalias(n, l)
20 ip.magic('store '+n)
21
22
23 def exta_imports():
24 # add some modules that you'd want to be bundled in the ipykit
25 # library zip file here. Do this if you get ImportErrors from scripts you
26 # try to launch with 'py' or pylaunchers. In theory you could include
27 # the whole stdlib here for full script coverage
28
29 # note that this is never run, it's just here for py2exe
30 import distutils.dir_util
31
32 def kitroot():
33 return os.environ.get('IPYKITROOT', None)
34
35 def main():
36
37 if not kitroot():
38 print "Can't configure ipykit, IPYKITROOT should be set."
39 return
40
41 os.environ["PATH"] = os.environ["PATH"] + ";" + kitroot() + "\\bin;"
42 ip.to_user_ns("pylaunchers")
43 cmds = ip.db.get('syscmdlist', None)
44 if cmds is None:
45 ip.magic('rehashx')
46 cmds = ip.db.get('syscmdlist', [])
47 #print cmds
48 if 'sc1' in cmds:
49 print "Default editor: Sc1"
50 import ipy_editors
51 ipy_editors.scite('sc1')
52
53 # for icp, imv, imkdir, etc.
54 import ipy_fsops
55
56 greeting = """\n\n === Welcome to ipykit ===
57
58 %quickref - learn quickly about IPython.
59
60 """
61
62 def ipython_firstrun(ip):
63
64 print "First run of ipykit - configuring"
65
66 ip.defalias('py',selflaunch)
67 ip.defalias('d','dir /w /og /on')
68 ip.magic('store py')
69 ip.magic('store d')
70
71 bins = kitroot() +'/bin'
72
73 print greeting
74
75 def init_ipython(ipy):
76 global ip
77 ip = ipy
78 main()
79
80
@@ -0,0 +1,62
1 """ Legacy stuff
2
3 Various stuff that are there for historical / familiarity reasons.
4
5 This is automatically imported by default profile, though not other profiles
6 (e.g. 'sh' profile).
7
8 Stuff that is considered obsolete / redundant is gradually moved here.
9
10 """
11
12 import IPython.ipapi
13 ip = IPython.ipapi.get()
14
15 import os,sys
16
17 from IPython.genutils import *
18
19 # use rehashx
20
21 def magic_rehash(self, parameter_s = ''):
22 """Update the alias table with all entries in $PATH.
23
24 This version does no checks on execute permissions or whether the
25 contents of $PATH are truly files (instead of directories or something
26 else). For such a safer (but slower) version, use %rehashx."""
27
28 # This function (and rehashx) manipulate the alias_table directly
29 # rather than calling magic_alias, for speed reasons. A rehash on a
30 # typical Linux box involves several thousand entries, so efficiency
31 # here is a top concern.
32
33 path = filter(os.path.isdir,os.environ.get('PATH','').split(os.pathsep))
34 alias_table = self.shell.alias_table
35 for pdir in path:
36 for ff in os.listdir(pdir):
37 # each entry in the alias table must be (N,name), where
38 # N is the number of positional arguments of the alias.
39 alias_table[ff] = (0,ff)
40 # Make sure the alias table doesn't contain keywords or builtins
41 self.shell.alias_table_validate()
42 # Call again init_auto_alias() so we get 'rm -i' and other modified
43 # aliases since %rehash will probably clobber them
44 self.shell.init_auto_alias()
45
46 ip.expose_magic("rehash", magic_rehash)
47
48 # Exit
49 def magic_Quit(self, parameter_s=''):
50 """Exit IPython without confirmation (like %Exit)."""
51
52 self.shell.exit_now = True
53
54 ip.expose_magic("Quit", magic_Quit)
55
56
57 # make it autocallable fn if you really need it
58 def magic_p(self, parameter_s=''):
59 """Just a short alias for Python's 'print'."""
60 exec 'print ' + parameter_s in self.shell.user_ns
61
62 ip.expose_magic("p", magic_p)
@@ -0,0 +1,215
1 """ Leo plugin for IPython
2
3 Example use:
4
5 nodes.foo = "hello world"
6
7 -> create '@ipy foo' node with text "hello world"
8
9 Access works also, and so does tab completion.
10
11 """
12 import IPython.ipapi
13 import IPython.genutils
14 import IPython.generics
15 import re
16
17
18
19 ip = IPython.ipapi.get()
20 leo = ip.user_ns['leox']
21 c,g = leo.c, leo.g
22
23 # will probably be overwritten by user, but handy for experimentation early on
24 ip.user_ns['c'] = c
25 ip.user_ns['g'] = g
26
27
28 from IPython.external.simplegeneric import generic
29 import pprint
30
31 @generic
32 def format_for_leo(obj):
33 """ Convert obj to string representiation (for editing in Leo)"""
34 return pprint.pformat(obj)
35
36 @format_for_leo.when_type(list)
37 def format_list(obj):
38 return "\n".join(str(s) for s in obj)
39
40 nodename_re = r'(@ipy?[\w-]+)?\s?(\w+)'
41
42 def all_cells():
43 d = {}
44 for p in c.allNodes_iter():
45 h = p.headString()
46 if h.startswith('@') and len(h.split()) == 1:
47 continue
48 mo = re.match(nodename_re, h)
49 if not mo:
50 continue
51 d[mo.group(2)] = p.copy()
52 return d
53
54
55 class TrivialLeoWorkbook:
56 """ class to find cells """
57 def __getattr__(self, key):
58 cells = all_cells()
59 p = cells[key]
60 body = p.bodyString()
61 return eval_body(body)
62 def __setattr__(self,key,val):
63 cells = all_cells()
64 p = cells.get(key,None)
65 if p is None:
66 add_var(key,val)
67 else:
68 c.setBodyString(p,format_for_leo(val))
69 def __str__(self):
70 return "<TrivialLeoWorkbook>"
71 __repr__ = __str__
72
73 ip.user_ns['nodes'] = TrivialLeoWorkbook()
74
75
76 class LeoNode(object):
77 def __init__(self,p):
78 self.p = p.copy()
79
80 def get_h(self): return self.p.headString()
81 def set_h(self,val):
82 print "set head",val
83 c.setHeadString(self.p,val)
84
85 h = property( get_h, set_h)
86
87 def get_b(self): return self.p.bodyString()
88 def set_b(self,val):
89 print "set body",val
90 c.setBodyString(self.p, val)
91
92 b = property(get_b, set_b)
93
94 def set_val(self, val):
95 self.b = pprint.pformat(val)
96
97 val = property(lambda self: ip.ev(self.b.strip()), set_val)
98
99 def set_l(self,val):
100 self.b = '\n'.join(val )
101 l = property(lambda self : IPython.genutils.SList(self.b.splitlines()),
102 set_l)
103
104 def __iter__(self):
105 return (LeoNode(p) for p in self.p.children_iter())
106
107
108 class LeoWorkbook:
109 """ class for 'advanced' node access """
110 def __getattr__(self, key):
111 if key.startswith('_') or key == 'trait_names':
112 raise AttributeError
113 cells = all_cells()
114 p = cells.get(key, None)
115 if p is None:
116 p = add_var(key,None)
117
118 return LeoNode(p)
119
120 def __str__(self):
121 return "<LeoWorkbook>"
122 __repr__ = __str__
123 ip.user_ns['wb'] = LeoWorkbook()
124
125
126 _dummyval = object()
127 @IPython.generics.complete_object.when_type(LeoWorkbook)
128 def workbook_complete(obj, prev):
129 return all_cells().keys()
130
131
132 def add_var(varname, value = _dummyval):
133 nodename = '@ipy-var ' + varname
134 p2 = g.findNodeAnywhere(c,nodename)
135 if not c.positionExists(p2):
136 p2 = c.currentPosition().insertAfter()
137 c.setHeadString(p2,'@ipy ' + varname)
138
139 c.setCurrentPosition(p2)
140 if value is _dummyval:
141 val = ip.user_ns[varname]
142 else:
143 val = value
144 if val is not None:
145 formatted = format_for_leo(val)
146 c.setBodyString(p2,formatted)
147 return p2
148
149 def add_file(self,fname):
150 p2 = c.currentPosition().insertAfter()
151
152 def push_script(p):
153 ohist = ip.IP.output_hist
154 hstart = len(ip.IP.input_hist)
155 script = g.getScript(c,p,useSelectedText=False,forcePythonSentinels=False,useSentinels=False)
156
157 script = g.splitLines(script + '\n')
158 script = ''.join(z for z in script if z.strip())
159
160 ip.runlines(script)
161
162 has_output = False
163 for idx in range(hstart,len(ip.IP.input_hist)):
164 val = ohist.get(idx,None)
165 if val is None:
166 continue
167 has_output = True
168 inp = ip.IP.input_hist[idx]
169 if inp.strip():
170 g.es('In: %s' % (inp[:40], ), tabName = 'IPython')
171
172 g.es('<%d> %s' % (idx, pprint.pformat(ohist[idx],width = 40)), tabName = 'IPython')
173
174 if not has_output:
175 g.es('ipy run: %s' %( p.headString(),), tabName = 'IPython')
176
177
178 def eval_body(body):
179 try:
180 val = ip.ev(body)
181 except:
182 # just use stringlist if it's not completely legal python expression
183 val = IPython.genutils.SList(body.splitlines())
184 return val
185
186 def push_variable(p,varname):
187 body = p.bodyString()
188 val = eval_body(body.strip())
189 ip.user_ns[varname] = val
190 g.es('ipy var: %s' % (varname,), tabName = "IPython")
191
192 def push_from_leo(p):
193 # headstring without @ are just scripts
194 if not p.headString().startswith('@'):
195 push_script(p)
196 return
197 tup = p.headString().split(None,1)
198 # @ipy foo is variable foo
199 if len(tup) == 2 and tup[0] == '@ipy':
200 varname = tup[1]
201 push_variable(p,varname)
202 return
203
204 ip.user_ns['leox'].push = push_from_leo
205
206 def leo_f(self,s):
207 ip = self.getapi()
208 s = s.strip()
209 if s in ip.user_ns:
210 add_var(s)
211 elif os.path.isfile(s):
212 # todo open file
213 pass
214
215 ip.expose_magic('leo',leo_f)
@@ -0,0 +1,47
1 # -*- coding: utf-8 -*-
2 """
3 Add %p4 magic for pythonic p4 (Perforce) usage.
4 """
5
6 import IPython.ipapi
7 ip = IPython.ipapi.get()
8
9 import os,sys,marshal
10
11 import ipy_stock_completers
12
13 def p4_f(self, parameter_s=''):
14 cmd = 'p4 -G ' + parameter_s
15 fobj = os.popen(cmd)
16 out = []
17 while 1:
18 try:
19 out.append(marshal.load(fobj))
20 except EOFError:
21 break
22
23 return out
24
25 def p4d(fname):
26 return os.popen('p4 where ' + fname).read().split()[0]
27
28 ip.to_user_ns("p4d")
29
30 ip.expose_magic('p4', p4_f)
31
32 p4_commands = """\
33 add admin annotate branch branches change changes changelist
34 changelists client clients counter counters delete depot depots
35 describe diff diff2 dirs edit filelog files fix fixes flush fstat
36 group groups have help info integrate integrated job jobs jobspec
37 label labels labelsync lock logger login logout monitor obliterate
38 opened passwd print protect rename reopen resolve resolved revert
39 review reviews set submit sync tag tickets triggers typemap unlock
40 user users verify workspace workspaces where"""
41
42 def p4_completer(self,event):
43 return ipy_stock_completers.vcs_completer(p4_commands, event)
44
45 ip.set_hook('complete_command', p4_completer, str_key = '%p4')
46 ip.set_hook('complete_command', p4_completer, str_key = 'p4')
47
@@ -0,0 +1,46
1 """Config file for 'doctest' profile.
2
3 This profile modifies the prompts to be the standard Python ones, so that you
4 can generate easily doctests from an IPython session.
5
6 But more importantly, it enables pasting of code with '>>>' prompts and
7 arbitrary initial whitespace, as is typical of doctests in reST files and
8 docstrings. This allows you to easily re-run existing doctests and iteratively
9 work on them as part of your development workflow.
10
11 The exception mode is also set to 'plain' so the generated exceptions are as
12 similar as possible to the default Python ones, for inclusion in doctests."""
13
14 # get various stuff that are there for historical / familiarity reasons
15 import ipy_legacy
16
17 from IPython import ipapi
18
19 from IPython.Extensions import InterpreterPasteInput
20
21 def main():
22 ip = ipapi.get()
23 o = ip.options
24
25 # Set the prompts similar to the defaults
26 o.prompt_in1 = '>>> '
27 o.prompt_in2 = '... '
28 o.prompt_out = ''
29
30 # Add a blank line before each new set of inputs. This is needed by
31 # doctest to distinguish each test from the next.
32 o.separate_in = '\n'
33 o.separate_out = ''
34 o.separate_out2 = ''
35
36 # Disable pprint, so that outputs are printed as similarly to standard
37 # python as possible
38 o.pprint = False
39
40 # Use plain exceptions, to also resemble normal pyhton.
41 o.xmode = 'plain'
42
43 # Store the activity flag in the metadata bag from the running shell
44 ip.IP.meta.doctest_mode = True
45
46 main()
@@ -0,0 +1,4
1 """ Config file for 'default' profile """
2
3 # get various stuff that are there for historical / familiarity reasons
4 import ipy_legacy No newline at end of file
@@ -0,0 +1,25
1 """ IPython 'sci' profile
2
3 Replaces the old scipy profile.
4
5 """
6
7
8 import IPython.ipapi
9 import ipy_defaults
10
11 def main():
12 ip = IPython.ipapi.get()
13
14 try:
15 ip.ex("import numpy")
16 ip.ex("import scipy")
17
18 ip.ex("from numpy import *")
19 ip.ex("from scipy import *")
20 print "SciPy profile successfully loaded."
21 except ImportError:
22 print "Unable to start scipy profile, are scipy and numpy installed?"
23
24
25 main()
@@ -0,0 +1,226
1 """Shell mode for IPython.
2
3 Start ipython in shell mode by invoking "ipython -p sh"
4
5 (the old version, "ipython -p pysh" still works but this is the more "modern"
6 shell mode and is recommended for users who don't care about pysh-mode
7 compatibility)
8 """
9
10 from IPython import ipapi
11 import os,textwrap
12
13 # The import below effectively obsoletes your old-style ipythonrc[.ini],
14 # so consider yourself warned!
15
16 import ipy_defaults
17
18 def main():
19 ip = ipapi.get()
20 o = ip.options
21 # autocall to "full" mode (smart mode is default, I like full mode)
22
23 o.autocall = 2
24
25 # Jason Orendorff's path class is handy to have in user namespace
26 # if you are doing shell-like stuff
27 try:
28 ip.ex("from IPython.external.path import path" )
29 except ImportError:
30 pass
31
32 # beefed up %env is handy in shell mode
33 import envpersist
34
35 # To see where mycmd resides (in path/aliases), do %which mycmd
36 import ipy_which
37
38 # tab completers for hg, svn, ...
39 import ipy_app_completers
40
41 # To make executables foo and bar in mybin usable without PATH change, do:
42 # %rehashdir c:/mybin
43 # %store foo
44 # %store bar
45 import ipy_rehashdir
46 import ipy_signals
47
48 ip.ex('import os')
49 ip.ex("def up(): os.chdir('..')")
50
51 # Nice prompt
52
53 o.prompt_in1= r'\C_LightBlue[\C_LightCyan\Y2\C_LightBlue]\C_Green|\#> '
54 o.prompt_in2= r'\C_Green|\C_LightGreen\D\C_Green> '
55 o.prompt_out= '<\#> '
56
57 from IPython import Release
58
59 import sys
60 # Non-chatty banner
61 o.banner = "IPython %s [on Py %s]\n" % (Release.version,sys.version.split(None,1)[0])
62
63
64 ip.IP.default_option('cd','-q')
65 ip.IP.default_option('macro', '-r')
66 # If you only rarely want to execute the things you %edit...
67 #ip.IP.default_option('edit','-x')
68
69
70 o.prompts_pad_left="1"
71 # Remove all blank lines in between prompts, like a normal shell.
72 o.separate_in="0"
73 o.separate_out="0"
74 o.separate_out2="0"
75
76 # now alias all syscommands
77
78 db = ip.db
79
80 syscmds = db.get("syscmdlist",[] )
81 if not syscmds:
82 print textwrap.dedent("""
83 System command list not initialized, probably the first run...
84 running %rehashx to refresh the command list. Run %rehashx
85 again to refresh command list (after installing new software etc.)
86 """)
87 ip.magic('rehashx')
88 syscmds = db.get("syscmdlist")
89
90 # lowcase aliases on win32 only
91 if os.name == 'posix':
92 mapper = lambda s:s
93 else:
94 def mapper(s): return s.lower()
95
96 for cmd in syscmds:
97 # print "sys",cmd #dbg
98 noext, ext = os.path.splitext(cmd)
99 key = mapper(noext)
100 if key not in ip.IP.alias_table:
101 ip.defalias(key, cmd)
102
103 # mglob combines 'find', recursion, exclusion... '%mglob?' to learn more
104 ip.load("IPython.external.mglob")
105
106 # win32 is crippled w/o cygwin, try to help it a little bit
107 if sys.platform == 'win32':
108 if 'cygwin' in os.environ['PATH'].lower():
109 # use the colors of cygwin ls (recommended)
110 ip.defalias('d', 'ls -F --color=auto')
111 else:
112 # get icp, imv, imkdir, igrep, irm,...
113 ip.load('ipy_fsops')
114
115 # and the next best thing to real 'ls -F'
116 ip.defalias('d','dir /w /og /on')
117
118 extend_shell_behavior(ip)
119
120 # XXX You do not need to understand the next function!
121 # This should probably be moved out of profile
122
123 def extend_shell_behavior(ip):
124
125 # Instead of making signature a global variable tie it to IPSHELL.
126 # In future if it is required to distinguish between different
127 # shells we can assign a signature per shell basis
128 ip.IP.__sig__ = 0xa005
129 # mark the IPSHELL with this signature
130 ip.IP.user_ns['__builtins__'].__dict__['__sig__'] = ip.IP.__sig__
131
132 from IPython.Itpl import ItplNS
133 from IPython.genutils import shell
134 # utility to expand user variables via Itpl
135 # xxx do something sensible with depth?
136 ip.IP.var_expand = lambda cmd, lvars=None, depth=2: \
137 str(ItplNS(cmd, ip.IP.user_ns, get_locals()))
138
139 def get_locals():
140 """ Substituting a variable through Itpl deep inside the IPSHELL stack
141 requires the knowledge of all the variables in scope upto the last
142 IPSHELL frame. This routine simply merges all the local variables
143 on the IPSHELL stack without worrying about their scope rules
144 """
145 import sys
146 # note lambda expression constitues a function call
147 # hence fno should be incremented by one
148 getsig = lambda fno: sys._getframe(fno+1).f_globals \
149 ['__builtins__'].__dict__['__sig__']
150 getlvars = lambda fno: sys._getframe(fno+1).f_locals
151 # trackback until we enter the IPSHELL
152 frame_no = 1
153 sig = ip.IP.__sig__
154 fsig = ~sig
155 while fsig != sig :
156 try:
157 fsig = getsig(frame_no)
158 except (AttributeError, KeyError):
159 frame_no += 1
160 except ValueError:
161 # stack is depleted
162 # call did not originate from IPSHELL
163 return {}
164 first_frame = frame_no
165 # walk further back until we exit from IPSHELL or deplete stack
166 try:
167 while(sig == getsig(frame_no+1)):
168 frame_no += 1
169 except (AttributeError, KeyError, ValueError):
170 pass
171 # merge the locals from top down hence overriding
172 # any re-definitions of variables, functions etc.
173 lvars = {}
174 for fno in range(frame_no, first_frame-1, -1):
175 lvars.update(getlvars(fno))
176 #print '\n'*5, first_frame, frame_no, '\n', lvars, '\n'*5 #dbg
177 return lvars
178
179 def _runlines(lines):
180 """Run a string of one or more lines of source.
181
182 This method is capable of running a string containing multiple source
183 lines, as if they had been entered at the IPython prompt. Since it
184 exposes IPython's processing machinery, the given strings can contain
185 magic calls (%magic), special shell access (!cmd), etc."""
186
187 # We must start with a clean buffer, in case this is run from an
188 # interactive IPython session (via a magic, for example).
189 ip.IP.resetbuffer()
190 lines = lines.split('\n')
191 more = 0
192 command = ''
193 for line in lines:
194 # skip blank lines so we don't mess up the prompt counter, but do
195 # NOT skip even a blank line if we are in a code block (more is
196 # true)
197 # if command is not empty trim the line
198 if command != '' :
199 line = line.strip()
200 # add the broken line to the command
201 if line and line[-1] == '\\' :
202 command += line[0:-1] + ' '
203 more = True
204 continue
205 else :
206 # add the last (current) line to the command
207 command += line
208 if command or more:
209 # push to raw history, so hist line numbers stay in sync
210 ip.IP.input_hist_raw.append("# " + command + "\n")
211
212 more = ip.IP.push(ip.IP.prefilter(command,more))
213 command = ''
214 # IPython's runsource returns None if there was an error
215 # compiling the code. This allows us to stop processing right
216 # away, so the user gets the error message at the right place.
217 if more is None:
218 break
219 # final newline in case the input didn't have it, so that the code
220 # actually does get executed
221 if more:
222 ip.IP.push('\n')
223
224 ip.IP.runlines = _runlines
225
226 main()
@@ -0,0 +1,317
1 # -*- coding: utf-8 -*-
2 #
3 # File: ipy_profile_zope.py
4 #
5 # Copyright (c) InQuant GmbH
6 #
7 # An ipython profile for zope and plone. Some ideas
8 # stolen from http://www.tomster.org.
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12
13 __author__ = """Stefan Eletzhofer <stefan.eletzhofer@inquant.de>"""
14 __docformat__ = 'plaintext'
15 __revision__ = "$Revision$"
16
17 from IPython import ipapi
18 from IPython import Release
19 from types import StringType
20 import sys
21 import os
22 import textwrap
23
24 # The import below effectively obsoletes your old-style ipythonrc[.ini],
25 # so consider yourself warned!
26 # import ipy_defaults
27
28 _marker = []
29 def shasattr(obj, attr, acquire=False):
30 """ See Archetypes/utils.py
31 """
32 if not acquire:
33 obj = obj.aq_base
34 return getattr(obj, attr, _marker) is not _marker
35
36 class ZopeDebug(object):
37 def __init__(self):
38
39 self.instancehome = os.environ.get( "INSTANCE_HOME" )
40
41 configfile = os.environ.get( "CONFIG_FILE" )
42 if configfile is None and self.instancehome is not None:
43 configfile = os.path.join( self.instancehome, "etc", "zope.conf" )
44
45 if configfile is None:
46 raise RuntimeError( "CONFIG_FILE env not set" )
47
48 print "CONFIG_FILE=", configfile
49 print "INSTANCE_HOME=", self.instancehome
50
51 self.configfile = configfile
52
53 try:
54 from Zope2 import configure
55 except ImportError:
56 from Zope import configure
57
58 configure( configfile )
59
60 try:
61 import Zope2
62 app = Zope2.app()
63 except ImportError:
64 import Zope
65 app = Zope.app()
66
67 from Testing.makerequest import makerequest
68 self.app = makerequest( app )
69
70 try:
71 self._make_permissive()
72 print "Permissive security installed"
73 except:
74 print "Permissive security NOT installed"
75
76 self._pwd = self.portal or self.app
77
78 try:
79 from zope.component import getSiteManager
80 from zope.component import getGlobalSiteManager
81 from zope.app.component.hooks import setSite
82
83 if self.portal is not None:
84 setSite( self.portal )
85
86 gsm = getGlobalSiteManager()
87 sm = getSiteManager()
88
89 if sm is gsm:
90 print "ERROR SETTING SITE!"
91 except:
92 pass
93
94
95 @property
96 def utils(self):
97 class Utils(object):
98 commit = self.commit
99 sync = self.sync
100 objectInfo = self.objectInfo
101 ls = self.ls
102 pwd = self.pwd
103 cd = self.cd
104 su = self.su
105 getCatalogInfo = self.getCatalogInfo
106
107 @property
108 def cwd(self):
109 return self.pwd()
110
111 return Utils()
112
113 @property
114 def namespace(self):
115 return dict( utils=self.utils, app=self.app, portal=self.portal )
116
117 @property
118 def portal(self):
119 portals = self.app.objectValues( "Plone Site" )
120 if len(portals):
121 return portals[0]
122 else:
123 raise KeyError( "No Plone Site found.")
124
125 def pwd(self):
126 return self._pwd
127
128 def _make_permissive(self):
129 """
130 Make a permissive security manager with all rights. Hell,
131 we're developers, aren't we? Security is for whimps. :)
132 """
133 from Products.CMFCore.tests.base.security import PermissiveSecurityPolicy
134 import AccessControl
135 from AccessControl.SecurityManagement import newSecurityManager
136 from AccessControl.SecurityManager import setSecurityPolicy
137
138 _policy = PermissiveSecurityPolicy()
139 self.oldpolicy = setSecurityPolicy(_policy)
140 newSecurityManager(None, AccessControl.User.system)
141
142 def su(self, username):
143 """ Change to named user.
144 """
145 # TODO Make it easy to change back to permissive security.
146 user = self.portal.acl_users.getUser(username)
147 if not user:
148 print "Can't find %s in %s" % (username, self.portal.acl_users)
149 return
150
151 from AccessControl import ZopeSecurityPolicy
152 import AccessControl
153 from AccessControl.SecurityManagement import newSecurityManager, getSecurityManager
154 from AccessControl.SecurityManager import setSecurityPolicy
155
156 _policy = ZopeSecurityPolicy
157 self.oldpolicy = setSecurityPolicy(_policy)
158 wrapped_user = user.__of__(self.portal.acl_users)
159 newSecurityManager(None, user)
160 print 'User changed.'
161 return getSecurityManager().getUser()
162
163 def getCatalogInfo(self, obj=None, catalog='portal_catalog', query=None, sort_on='created', sort_order='reverse' ):
164 """ Inspect portal_catalog. Pass an object or object id for a
165 default query on that object, or pass an explicit query.
166 """
167 if obj and query:
168 print "Ignoring %s, using query." % obj
169
170 catalog = self.portal.get(catalog)
171 if not catalog:
172 return 'No catalog'
173
174 indexes = catalog._catalog.indexes
175 if not query:
176 if type(obj) is StringType:
177 cwd = self.pwd()
178 obj = cwd.unrestrictedTraverse( obj )
179 # If the default in the signature is mutable, its value will
180 # persist across invocations.
181 query = {}
182 if indexes.get('path'):
183 from string import join
184 path = join(obj.getPhysicalPath(), '/')
185 query.update({'path': path})
186 if indexes.get('getID'):
187 query.update({'getID': obj.id, })
188 if indexes.get('UID') and shasattr(obj, 'UID'):
189 query.update({'UID': obj.UID(), })
190 if indexes.get(sort_on):
191 query.update({'sort_on': sort_on, 'sort_order': sort_order})
192 if not query:
193 return 'Empty query'
194 results = catalog(**query)
195
196 result_info = []
197 for r in results:
198 rid = r.getRID()
199 if rid:
200 result_info.append(
201 {'path': catalog.getpath(rid),
202 'metadata': catalog.getMetadataForRID(rid),
203 'indexes': catalog.getIndexDataForRID(rid), }
204 )
205 else:
206 result_info.append({'missing': rid})
207
208 if len(result_info) == 1:
209 return result_info[0]
210 return result_info
211
212 def commit(self):
213 """
214 Commit the transaction.
215 """
216 try:
217 import transaction
218 transaction.get().commit()
219 except ImportError:
220 get_transaction().commit()
221
222 def sync(self):
223 """
224 Sync the app's view of the zodb.
225 """
226 self.app._p_jar.sync()
227
228 def objectInfo( self, o ):
229 """
230 Return a descriptive string of an object
231 """
232 Title = ""
233 t = getattr( o, 'Title', None )
234 if t:
235 Title = t()
236 return {'id': o.getId(),
237 'Title': Title,
238 'portal_type': getattr( o, 'portal_type', o.meta_type),
239 'folderish': o.isPrincipiaFolderish
240 }
241
242 def cd( self, path ):
243 """
244 Change current dir to a specific folder.
245
246 cd( ".." )
247 cd( "/plone/Members/admin" )
248 cd( portal.Members.admin )
249 etc.
250 """
251 if type(path) is not StringType:
252 path = '/'.join(path.getPhysicalPath())
253 cwd = self.pwd()
254 x = cwd.unrestrictedTraverse( path )
255 if x is None:
256 raise KeyError( "Can't cd to %s" % path )
257
258 print "%s -> %s" % ( self.pwd().getId(), x.getId() )
259 self._pwd = x
260
261 def ls( self, x=None ):
262 """
263 List object(s)
264 """
265 if type(x) is StringType:
266 cwd = self.pwd()
267 x = cwd.unrestrictedTraverse( x )
268 if x is None:
269 x = self.pwd()
270 if x.isPrincipiaFolderish:
271 return [self.objectInfo(o) for id, o in x.objectItems()]
272 else:
273 return self.objectInfo( x )
274
275 zope_debug = None
276
277 def ipy_set_trace():
278 import IPython; IPython.Debugger.Pdb().set_trace()
279
280 def main():
281 global zope_debug
282 ip = ipapi.get()
283 o = ip.options
284 # autocall to "full" mode (smart mode is default, I like full mode)
285
286 SOFTWARE_HOME = os.environ.get( "SOFTWARE_HOME" )
287 sys.path.append( SOFTWARE_HOME )
288 print "SOFTWARE_HOME=%s\n" % SOFTWARE_HOME
289
290 zope_debug = ZopeDebug()
291
292 # <HACK ALERT>
293 import pdb;
294 pdb.set_trace = ipy_set_trace
295 # </HACK ALERT>
296
297 # I like my banner minimal.
298 o.banner = "ZOPE Py %s IPy %s\n" % (sys.version.split('\n')[0],Release.version)
299
300 print textwrap.dedent("""\
301 ZOPE mode iPython shell.
302
303 Bound names:
304 app
305 portal
306 utils.{ %s }
307
308 Uses the $SOFTWARE_HOME and $CONFIG_FILE environment
309 variables.
310 """ % ( ",".join([ x for x in dir(zope_debug.utils) if not x.startswith("_") ] ) ) )
311
312
313 ip.user_ns.update( zope_debug.namespace )
314
315
316 main()
317 # vim: set ft=python ts=4 sw=4 expandtab :
@@ -0,0 +1,31
1 import inspect
2 import IPython.ipapi
3 from IPython.genutils import arg_split
4 ip = IPython.ipapi.get()
5
6 from IPython import Debugger
7
8 def call_pydb(self, args):
9 """Invoke pydb with the supplied parameters."""
10 try:
11 import pydb
12 except ImportError:
13 raise ImportError("pydb doesn't seem to be installed.")
14
15 if not hasattr(pydb.pydb, "runv"):
16 raise ImportError("You need pydb version 1.19 or later installed.")
17
18 argl = arg_split(args)
19 # print argl # dbg
20 if len(inspect.getargspec(pydb.runv)[0]) == 2:
21 pdb = Debugger.Pdb(color_scheme=self.rc.colors)
22 ip.IP.history_saving_wrapper( lambda : pydb.runv(argl, pdb) )()
23 else:
24 ip.IP.history_saving_wrapper( lambda : pydb.runv(argl) )()
25
26
27 ip.expose_magic("pydb",call_pydb)
28
29
30
31
@@ -0,0 +1,140
1 # -*- coding: utf-8 -*-
2 """ IPython extension: add %rehashdir magic
3
4 Usage:
5
6 %rehashdir c:/bin c:/tools
7 - Add all executables under c:/bin and c:/tools to alias table, in
8 order to make them directly executable from any directory.
9
10 This also serves as an example on how to extend ipython
11 with new magic functions.
12
13 Unlike rest of ipython, this requires Python 2.4 (optional
14 extensions are allowed to do that).
15
16 """
17
18 import IPython.ipapi
19 ip = IPython.ipapi.get()
20
21
22 import os,re,fnmatch,sys
23
24 def selflaunch(ip,line):
25 """ Launch python script with 'this' interpreter
26
27 e.g. d:\foo\ipykit.exe a.py
28
29 """
30
31 tup = line.split(None,1)
32 if len(tup) == 1:
33 print "Launching nested ipython session"
34 os.system(sys.executable)
35 return
36
37 cmd = sys.executable + ' ' + tup[1]
38 print ">",cmd
39 os.system(cmd)
40
41 class PyLauncher:
42 """ Invoke selflanucher on the specified script
43
44 This is mostly useful for associating with scripts using::
45 _ip.defalias('foo',PyLauncher('foo_script.py'))
46
47 """
48 def __init__(self,script):
49 self.script = os.path.abspath(script)
50 def __call__(self, ip, line):
51 if self.script.endswith('.ipy'):
52 ip.runlines(open(self.script).read())
53 else:
54 # first word is the script/alias name itself, strip it
55 tup = line.split(None,1)
56 if len(tup) == 2:
57 tail = ' ' + tup[1]
58 else:
59 tail = ''
60
61 selflaunch(ip,"py " + self.script + tail)
62 def __repr__(self):
63 return 'PyLauncher("%s")' % self.script
64
65 def rehashdir_f(self,arg):
66 """ Add executables in all specified dirs to alias table
67
68 Usage:
69
70 %rehashdir c:/bin;c:/tools
71 - Add all executables under c:/bin and c:/tools to alias table, in
72 order to make them directly executable from any directory.
73
74 Without arguments, add all executables in current directory.
75
76 """
77
78 # most of the code copied from Magic.magic_rehashx
79
80 def isjunk(fname):
81 junk = ['*~']
82 for j in junk:
83 if fnmatch.fnmatch(fname, j):
84 return True
85 return False
86
87 created = []
88 if not arg:
89 arg = '.'
90 path = map(os.path.abspath,arg.split(';'))
91 alias_table = self.shell.alias_table
92
93 if os.name == 'posix':
94 isexec = lambda fname:os.path.isfile(fname) and \
95 os.access(fname,os.X_OK)
96 else:
97
98 try:
99 winext = os.environ['pathext'].replace(';','|').replace('.','')
100 except KeyError:
101 winext = 'exe|com|bat|py'
102 if 'py' not in winext:
103 winext += '|py'
104
105 execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
106 isexec = lambda fname:os.path.isfile(fname) and execre.match(fname)
107 savedir = os.getcwd()
108 try:
109 # write the whole loop for posix/Windows so we don't have an if in
110 # the innermost part
111 if os.name == 'posix':
112 for pdir in path:
113 os.chdir(pdir)
114 for ff in os.listdir(pdir):
115 if isexec(ff) and not isjunk(ff):
116 # each entry in the alias table must be (N,name),
117 # where N is the number of positional arguments of the
118 # alias.
119 src,tgt = os.path.splitext(ff)[0], os.path.abspath(ff)
120 created.append(src)
121 alias_table[src] = (0,tgt)
122 else:
123 for pdir in path:
124 os.chdir(pdir)
125 for ff in os.listdir(pdir):
126 if isexec(ff) and not isjunk(ff):
127 src, tgt = execre.sub(r'\1',ff), os.path.abspath(ff)
128 src = src.lower()
129 created.append(src)
130 alias_table[src] = (0,tgt)
131 # Make sure the alias table doesn't contain keywords or builtins
132 self.shell.alias_table_validate()
133 # Call again init_auto_alias() so we get 'rm -i' and other
134 # modified aliases since %rehashx will probably clobber them
135 # self.shell.init_auto_alias()
136 finally:
137 os.chdir(savedir)
138 return created
139
140 ip.expose_magic("rehashdir",rehashdir_f)
@@ -0,0 +1,68
1 #!/usr/bin/env python
2
3 """ IPython extension: Render templates from variables and paste to clipbard """
4
5 import IPython.ipapi
6
7 ip = IPython.ipapi.get()
8
9 from string import Template
10 import sys,os
11
12 from IPython.Itpl import itplns
13
14 def toclip_w32(s):
15 """ Places contents of s to clipboard
16
17 Needs pyvin32 to work:
18 http://sourceforge.net/projects/pywin32/
19 """
20 import win32clipboard as cl
21 import win32con
22 cl.OpenClipboard()
23 cl.EmptyClipboard()
24 cl.SetClipboardText( s.replace('\n','\r\n' ))
25 cl.CloseClipboard()
26
27 try:
28 import win32clipboard
29 toclip = toclip_w32
30 except ImportError:
31 def toclip(s): pass
32
33
34 def render(tmpl):
35 """ Render a template (Itpl format) from ipython variables
36
37 Example:
38
39 $ import ipy_render
40 $ my_name = 'Bob' # %store this for convenience
41 $ t_submission_form = "Submission report, author: $my_name" # %store also
42 $ render t_submission_form
43
44 => returns "Submission report, author: Bob" and copies to clipboard on win32
45
46 # if template exist as a file, read it. Note: ;f hei vaan => f("hei vaan")
47 $ ;render c:/templates/greeting.txt
48
49 Template examples (Ka-Ping Yee's Itpl library):
50
51 Here is a $string.
52 Here is a $module.member.
53 Here is an $object.member.
54 Here is a $functioncall(with, arguments).
55 Here is an ${arbitrary + expression}.
56 Here is an $array[3] member.
57 Here is a $dictionary['member'].
58 """
59
60 if os.path.isfile(tmpl):
61 tmpl = open(tmpl).read()
62
63 res = itplns(tmpl, ip.user_ns)
64 toclip(res)
65 return res
66
67 ip.to_user_ns('render')
68 No newline at end of file
@@ -0,0 +1,38
1 """ Simple TCP socket server that executes statements in IPython instance.
2
3 Usage:
4
5 import ipy_server
6 ipy_server.serve_thread(16455)
7
8 Now, to execute the statements in this ipython instance, open a TCP socket
9 (port 16455), write out the statements, and close the socket.
10 You can use e.g. "telnet localhost 16455" or a script to do this.
11
12 This is a bit like 'M-x server-start" or gnuserv in the emacs world.
13
14 """
15
16 import IPython.ipapi
17 ip = IPython.ipapi.get()
18
19 import SocketServer
20
21 # user-accessible port
22 PORT = 8099
23
24 class IPythonRequestHandler(SocketServer.StreamRequestHandler):
25 def handle(self):
26 #print "connection from", self.client_address
27 inp = self.rfile.read().replace('\r\n','\n')
28 #print "Execute",inp
29 ip.runlines(inp)
30
31 def serve(port = PORT):
32 server = SocketServer.TCPServer(("", port), IPythonRequestHandler)
33 print "ipy_server on TCP port", port
34 server.serve_forever()
35
36 def serve_thread(port = PORT):
37 import thread
38 thread.start_new_thread(serve, (port,)) No newline at end of file
@@ -0,0 +1,62
1 """ Advanced signal (e.g. ctrl+C) handling for IPython
2
3 So far, this only ignores ctrl + C in IPython file a subprocess
4 is executing, to get closer to how a "proper" shell behaves.
5
6 Other signal processing may be implemented later on.
7
8 If _ip.options.verbose is true, show exit status if nonzero
9
10 """
11
12 import signal,os,sys
13 import IPython.ipapi
14 import subprocess
15
16 ip = IPython.ipapi.get()
17
18 def new_ipsystem_posix(cmd):
19 """ ctrl+c ignoring replacement for system() command in iplib.
20
21 Ignore ctrl + c in IPython process during the command execution.
22 The subprocess will still get the ctrl + c signal.
23
24 posix implementation
25 """
26
27 p = subprocess.Popen(cmd, shell = True)
28
29 old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
30 pid,status = os.waitpid(p.pid,0)
31 signal.signal(signal.SIGINT, old_handler)
32 if status and ip.options.verbose:
33 print "[exit status: %d]" % status
34
35 def new_ipsystem_win32(cmd):
36 """ ctrl+c ignoring replacement for system() command in iplib.
37
38 Ignore ctrl + c in IPython process during the command execution.
39 The subprocess will still get the ctrl + c signal.
40
41 win32 implementation
42 """
43 old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
44 status = os.system(cmd)
45 signal.signal(signal.SIGINT, old_handler)
46 if status and ip.options.verbose:
47 print "[exit status: %d]" % status
48
49
50 def init():
51 o = ip.options
52 try:
53 o.verbose
54 except AttributeError:
55 o.allow_new_attr (True )
56 o.verbose = 0
57
58 ip.IP.system = (sys.platform == 'win32' and new_ipsystem_win32 or
59 new_ipsystem_posix)
60
61 init()
62 No newline at end of file
@@ -0,0 +1,17
1 """ Install various IPython completers
2
3 IPython extension that installs completers related to core ipython behaviour.
4
5 The actual implementations are in Extensions/ipy_completers.py
6
7 """
8 import IPython.ipapi
9
10 ip = IPython.ipapi.get()
11
12 from ipy_completers import *
13
14 ip.set_hook('complete_command', module_completer, str_key = 'import')
15 ip.set_hook('complete_command', module_completer, str_key = 'from')
16 ip.set_hook('complete_command', runlistpy, str_key = '%run')
17 ip.set_hook('complete_command', cd_completer, str_key = '%cd')
@@ -0,0 +1,24
1 """ System wide configuration file for IPython.
2
3 This will be imported by ipython for all users.
4
5 After this ipy_user_conf.py is imported, user specific configuration
6 should reside there.
7
8 """
9
10 import IPython.ipapi
11 ip = IPython.ipapi.get()
12
13 # add system wide configuration information, import extensions etc. here.
14 # nothing here is essential
15
16 import sys
17
18 import ext_rescapture # var = !ls and var = %magic
19 import pspersistence # %store magic
20 import clearcmd # %clear
21
22 import ipy_stock_completers
23
24 ip.load('IPython.history')
@@ -0,0 +1,184
1 """Traits-aware tab completion.
2
3 This module provides a custom tab-completer that intelligently hides the names
4 that the enthought.traits library (http://code.enthought.com/traits)
5 automatically adds to all objects that inherit from its base HasTraits class.
6
7
8 Activation
9 ==========
10
11 To use this, put in your ~/.ipython/ipy_user_conf.py file:
12
13 from ipy_traits_completer import activate
14 activate([complete_threshold])
15
16 The optional complete_threshold argument is the minimal length of text you need
17 to type for tab-completion to list names that are automatically generated by
18 traits. The default value is 3. Note that at runtime, you can change this
19 value simply by doing:
20
21 import ipy_traits_completer
22 ipy_traits_completer.COMPLETE_THRESHOLD = 4
23
24
25 Usage
26 =====
27
28 The system works as follows. If t is an empty object that HasTraits, then
29 (assuming the threshold is at the default value of 3):
30
31 In [7]: t.ed<TAB>
32
33 doesn't show anything at all, but:
34
35 In [7]: t.edi<TAB>
36 t.edit_traits t.editable_traits
37
38 shows these two names that come from traits. This allows you to complete on
39 the traits-specific names by typing at least 3 letters from them (or whatever
40 you set your threshold to), but to otherwise not see them in normal completion.
41
42
43 Notes
44 =====
45
46 - This requires Python 2.4 to work (I use sets). I don't think anyone is
47 using traits with 2.3 anyway, so that's OK.
48 """
49
50 #############################################################################
51 # External imports
52 from enthought.traits import api as T
53
54 # IPython imports
55 from IPython.ipapi import TryNext, get as ipget
56 from IPython.genutils import dir2
57 try:
58 set
59 except:
60 from sets import Set as set
61
62 #############################################################################
63 # Module constants
64
65 # The completion threshold
66 # This is currently implemented as a module global, since this sytem isn't
67 # likely to be modified at runtime by multiple instances. If needed in the
68 # future, we can always make it local to the completer as a function attribute.
69 COMPLETE_THRESHOLD = 3
70
71 # Set of names that Traits automatically adds to ANY traits-inheriting object.
72 # These are the names we'll filter out.
73 TRAIT_NAMES = set( dir2(T.HasTraits()) ) - set( dir2(object()) )
74
75 #############################################################################
76 # Code begins
77
78 def trait_completer(self,event):
79 """A custom IPython tab-completer that is traits-aware.
80
81 It tries to hide the internal traits attributes, and reveal them only when
82 it can reasonably guess that the user really is after one of them.
83 """
84
85 #print '\nevent is:',event # dbg
86 symbol_parts = event.symbol.split('.')
87 base = '.'.join(symbol_parts[:-1])
88 #print 'base:',base # dbg
89
90 oinfo = self._ofind(base)
91 if not oinfo['found']:
92 raise TryNext
93
94 obj = oinfo['obj']
95 # OK, we got the object. See if it's traits, else punt
96 if not isinstance(obj,T.HasTraits):
97 raise TryNext
98
99 # it's a traits object, don't show the tr* attributes unless the completion
100 # begins with 'tr'
101 attrs = dir2(obj)
102 # Now, filter out the attributes that start with the user's request
103 attr_start = symbol_parts[-1]
104 if attr_start:
105 attrs = [a for a in attrs if a.startswith(attr_start)]
106
107 # Let's also respect the user's readline_omit__names setting:
108 omit__names = ipget().options.readline_omit__names
109 if omit__names == 1:
110 attrs = [a for a in attrs if not a.startswith('__')]
111 elif omit__names == 2:
112 attrs = [a for a in attrs if not a.startswith('_')]
113
114 #print '\nastart:<%r>' % attr_start # dbg
115
116 if len(attr_start)<COMPLETE_THRESHOLD:
117 attrs = list(set(attrs) - TRAIT_NAMES)
118
119 # The base of the completion, so we can form the final results list
120 bdot = base+'.'
121
122 tcomp = [bdot+a for a in attrs]
123 #print 'tcomp:',tcomp
124 return tcomp
125
126 def activate(complete_threshold = COMPLETE_THRESHOLD):
127 """Activate the Traits completer.
128
129 :Keywords:
130 complete_threshold : int
131 The minimum number of letters that a user must type in order to
132 activate completion of traits-private names."""
133
134 if not (isinstance(complete_threshold,int) and
135 complete_threshold>0):
136 e='complete_threshold must be a positive integer, not %r' % \
137 complete_threshold
138 raise ValueError(e)
139
140 # Set the module global
141 global COMPLETE_THRESHOLD
142 COMPLETE_THRESHOLD = complete_threshold
143
144 # Activate the traits aware completer
145 ip = ipget()
146 ip.set_hook('complete_command', trait_completer, re_key = '.*')
147
148
149 #############################################################################
150 if __name__ == '__main__':
151 # Testing/debugging
152
153 # A sorted list of the names we'll filter out
154 TNL = list(TRAIT_NAMES)
155 TNL.sort()
156
157 # Make a few objects for testing
158 class TClean(T.HasTraits): pass
159 class Bunch(object): pass
160 # A clean traits object
161 t = TClean()
162 # A nested object containing t
163 f = Bunch()
164 f.t = t
165 # And a naked new-style object
166 o = object()
167
168 ip = ipget().IP
169
170 # A few simplistic tests
171
172 # Reset the threshold to the default, in case the test is running inside an
173 # instance of ipython that changed it
174 import ipy_traits_completer
175 ipy_traits_completer.COMPLETE_THRESHOLD = 3
176
177 assert ip.complete('t.ed') ==[]
178
179 # For some bizarre reason, these fail on the first time I run them, but not
180 # afterwards. Traits does some really weird stuff at object instantiation
181 # time...
182 ta = ip.complete('t.edi')
183 assert ta == ['t.edit_traits', 't.editable_traits']
184 print 'Tests OK'
@@ -0,0 +1,76
1 r""" %which magic command
2
3 %which <cmd> => search PATH for files matching PATH. Also scans aliases
4
5 """
6
7 import IPython.ipapi
8 ip = IPython.ipapi.get()
9
10 import os,sys
11 from fnmatch import fnmatch
12 def which(fname):
13 fullpath = filter(os.path.isdir,os.environ['PATH'].split(os.pathsep))
14
15 if '.' not in fullpath:
16 fullpath = ['.'] + fullpath
17 fn = fname
18 for p in fullpath:
19 for f in os.listdir(p):
20 head, ext = os.path.splitext(f)
21 if f == fn or fnmatch(head, fn):
22 yield os.path.join(p,f)
23 return
24
25 def which_alias(fname):
26 for al, tgt in ip.IP.alias_table.items():
27 if not (al == fname or fnmatch(al, fname)):
28 continue
29 if callable(tgt):
30 print "Callable alias",tgt
31 d = tgt.__doc__
32 if d:
33 print "Docstring:\n",d
34 continue
35 trg = tgt[1]
36
37 trans = ip.expand_alias(trg)
38 cmd = trans.split(None,1)[0]
39 print al,"->",trans
40 for realcmd in which(cmd):
41 print " ==",realcmd
42
43 def which_f(self, arg):
44 r""" %which <cmd> => search PATH for files matching cmd. Also scans aliases.
45
46 Traverses PATH and prints all files (not just executables!) that match the
47 pattern on command line. Probably more useful in finding stuff
48 interactively than 'which', which only prints the first matching item.
49
50 Also discovers and expands aliases, so you'll see what will be executed
51 when you call an alias.
52
53 Example:
54
55 [~]|62> %which d
56 d -> ls -F --color=auto
57 == c:\cygwin\bin\ls.exe
58 c:\cygwin\bin\d.exe
59
60 [~]|64> %which diff*
61 diff3 -> diff3
62 == c:\cygwin\bin\diff3.exe
63 diff -> diff
64 == c:\cygwin\bin\diff.exe
65 c:\cygwin\bin\diff.exe
66 c:\cygwin\bin\diff3.exe
67
68 """
69
70 which_alias(arg)
71
72 for e in which(arg):
73 print e
74
75 ip.expose_magic("which",which_f)
76
@@ -0,0 +1,43
1 #!/usr/bin/env python
2
3 import IPython.ipapi
4 ip = IPython.ipapi.get()
5
6 import os, subprocess
7
8 workdir = None
9 def workdir_f(ip,line):
10 """ Exceute commands residing in cwd elsewhere
11
12 Example::
13
14 workdir /myfiles
15 cd bin
16 workdir myscript.py
17
18 executes myscript.py (stored in bin, but not in path) in /myfiles
19 """
20 global workdir
21 dummy,cmd = line.split(None,1)
22 if os.path.isdir(cmd):
23 workdir = os.path.abspath(cmd)
24 print "Set workdir",workdir
25 elif workdir is None:
26 print "Please set workdir first by doing e.g. 'workdir q:/'"
27 else:
28 sp = cmd.split(None,1)
29 if len(sp) == 1:
30 head, tail = cmd, ''
31 else:
32 head, tail = sp
33 if os.path.isfile(head):
34 cmd = os.path.abspath(head) + ' ' + tail
35 print "Execute command '" + cmd+ "' in",workdir
36 olddir = os.getcwd()
37 os.chdir(workdir)
38 try:
39 os.system(cmd)
40 finally:
41 os.chdir(olddir)
42
43 ip.defalias("workdir",workdir_f)
@@ -0,0 +1,162
1 """ Preliminary "job control" extensions for IPython
2
3 requires python 2.4 (or separate 'subprocess' module
4
5 This provides 2 features, launching background jobs and killing foreground jobs from another IPython instance.
6
7 Launching background jobs:
8
9 Usage:
10
11 [ipython]|2> import jobctrl
12 [ipython]|3> &ls
13 <3> <jobctrl.IpyPopen object at 0x00D87FD0>
14 [ipython]|4> _3.go
15 -----------> _3.go()
16 ChangeLog
17 IPython
18 MANIFEST.in
19 README
20 README_Windows.txt
21
22 ...
23
24 Killing foreground tasks:
25
26 Launch IPython instance, run a blocking command:
27
28 [Q:/ipython]|1> import jobctrl
29 [Q:/ipython]|2> cat
30
31 Now launch a new IPython prompt and kill the process:
32
33 IPython 0.8.3.svn.r2919 [on Py 2.5]
34 [Q:/ipython]|1> import jobctrl
35 [Q:/ipython]|2> %tasks
36 6020: 'cat ' (Q:\ipython)
37 [Q:/ipython]|3> %kill
38 SUCCESS: The process with PID 6020 has been terminated.
39 [Q:/ipython]|4>
40
41 (you don't need to specify PID for %kill if only one task is running)
42 """
43
44 from subprocess import Popen,PIPE
45 import os,shlex,sys,time
46
47 from IPython import genutils
48
49 import IPython.ipapi
50
51 if os.name == 'nt':
52 def kill_process(pid):
53 os.system('taskkill /F /PID %d' % pid)
54 else:
55 def kill_process(pid):
56 os.system('kill -9 %d' % pid)
57
58
59
60 class IpyPopen(Popen):
61 def go(self):
62 print self.communicate()[0]
63 def __repr__(self):
64 return '<IPython job "%s" PID=%d>' % (self.line, self.pid)
65
66 def kill(self):
67 kill_process(self.pid)
68
69 def startjob(job):
70 p = IpyPopen(shlex.split(job), stdout=PIPE, shell = False)
71 p.line = job
72 return p
73
74 def jobctrl_prefilter_f(self,line):
75 if line.startswith('&'):
76 pre,fn,rest = self.split_user_input(line[1:])
77
78 line = ip.IP.expand_aliases(fn,rest)
79 return '_ip.startjob(%s)' % genutils.make_quoted_expr(line)
80
81 raise IPython.ipapi.TryNext
82
83
84 def job_list(ip):
85 keys = ip.db.keys('tasks/*')
86 ents = [ip.db[k] for k in keys]
87 return ents
88
89 def magic_tasks(self,line):
90 """ Show a list of tasks.
91
92 A 'task' is a process that has been started in IPython when 'jobctrl' extension is enabled.
93 Tasks can be killed with %kill.
94 """
95 ip = self.getapi()
96 ents = job_list(ip)
97 if not ents:
98 print "No tasks running"
99 for pid,cmd,cwd,t in ents:
100 dur = int(time.time()-t)
101 print "%d: '%s' (%s) %d:%02d" % (pid,cmd,cwd, dur / 60,dur%60)
102
103 def magic_kill(self,line):
104 """ Kill a task
105
106 Without args, either kill one task (if only one running) or show list (if many)
107 With arg, assume it's the process id.
108
109 %kill is typically (much) more powerful than trying to terminate a process with ctrl+C.
110 """
111 ip = self.getapi()
112 jobs = job_list(ip)
113
114 if not line.strip():
115 if len(jobs) == 1:
116 kill_process(jobs[0][0])
117 else:
118 magic_tasks(self,line)
119 return
120
121 try:
122 pid = int(line)
123 kill_process(pid)
124 except ValueError:
125 magic_tasks(self,line)
126
127 if sys.platform == 'win32':
128 shell_internal_commands = 'break chcp cls copy ctty date del erase dir md mkdir path prompt rd rmdir time type ver vol'.split()
129 else:
130 # todo linux commands
131 shell_internal_commands = []
132
133
134 def jobctrl_shellcmd(ip,cmd):
135 """ os.system replacement that stores process info to db['tasks/t1234'] """
136 cmdname = cmd.split(None,1)[0]
137 if cmdname in shell_internal_commands:
138 use_shell = True
139 else:
140 use_shell = False
141
142 p = Popen(cmd,shell = use_shell)
143 jobentry = 'tasks/t' + str(p.pid)
144
145 try:
146 ip.db[jobentry] = (p.pid,cmd,os.getcwd(),time.time())
147 p.communicate()
148 finally:
149 del ip.db[jobentry]
150
151
152 def install():
153 global ip
154 ip = IPython.ipapi.get()
155 # needed to make startjob visible as _ip.startjob('blah')
156 ip.startjob = startjob
157 ip.set_hook('input_prefilter', jobctrl_prefilter_f)
158 ip.set_hook('shell_hook', jobctrl_shellcmd)
159 ip.expose_magic('kill',magic_kill)
160 ip.expose_magic('tasks',magic_tasks)
161
162 install()
@@ -0,0 +1,98
1 """ Fun magic line editor for ipython
2
3 Use this to easily edit lists of strings gradually without crafting long
4 list comprehensions.
5
6 'l' is the magic variable name for every line (array element). Save the current
7 result (or more exactly, retrieve the last ipython computation result into
8 %led work area) by running '%led s'. Just run '%led' to show the current work
9 area data.
10
11 Example use:
12
13 [ipython]|25> setups = !ls *setup*.py
14 ==
15 ['eggsetup.py', 'setup.py', 'setup_bdist_egg.py']
16 [ipython]|26> setups
17 <26> ['eggsetup.py', 'setup.py', 'setup_bdist_egg.py']
18 [ipython]|27> %led s
19 Data set from last result (_)
20 <27> ['eggsetup.py', 'setup.py', 'setup_bdist_egg.py']
21 [ipython]|28> %led upper
22 cmd translated => l.upper()
23 <28> ['EGGSETUP.PY', 'SETUP.PY', 'SETUP_BDIST_EGG.PY']
24 [ipython]|29> %led
25 Magic line editor (for lists of strings)
26 current data is:
27 ['eggsetup.py', 'setup.py', 'setup_bdist_egg.py']
28 [ipython]|30> %led upper
29 cmd translated => l.upper()
30 <30> ['EGGSETUP.PY', 'SETUP.PY', 'SETUP_BDIST_EGG.PY']
31 [ipython]|31> %led s
32 Data set from last result (_)
33 <31> ['EGGSETUP.PY', 'SETUP.PY', 'SETUP_BDIST_EGG.PY']
34 [ipython]|32> %led "n:" + l
35 <32> ['n:EGGSETUP.PY', 'n:SETUP.PY', 'n:SETUP_BDIST_EGG.PY']
36 [ipython]|33> %led s
37 Data set from last result (_)
38 <33> ['n:EGGSETUP.PY', 'n:SETUP.PY', 'n:SETUP_BDIST_EGG.PY']
39 [ipython]|34> %led l.
40 l.__add__ l.__gt__ l.__reduce_ex__ l.endswith l.join l.rstrip
41 l.__class__ l.__hash__ l.__repr__ l.expandtabs l.ljust l.split
42
43 ... (completions for string variable shown ) ...
44
45 """
46 import IPython.ipapi
47 import pprint
48 ip = IPython.ipapi.get()
49
50 curdata = []
51
52 def line_edit_f(self, cmd ):
53 global curdata
54
55 if not cmd:
56
57 print "Magic line editor (for lists of strings)"
58 if curdata:
59 print "current data is:"
60 pprint.pprint(curdata)
61 else:
62 print "No current data, you should set it by running '%led s'"
63 print "When you have your data in _ (result of last computation)."
64 return
65
66 if cmd == 's':
67 curdata = ip.ev('_')
68 print "Data set from last result (_)"
69 newlines = curdata
70
71 else:
72 # simple method call, e.g. upper
73 if cmd.isalpha():
74 cmd = 'l.' + cmd + '()'
75 print "cmd translated =>",cmd
76
77 newlines = []
78 for l in curdata:
79 try:
80 l2 = eval(cmd)
81 except Exception,e:
82 print "Dropping exception",e,"on line:",l
83 continue
84 newlines.append(l2)
85
86
87 return newlines
88
89 def line_edit_complete_f(self,event):
90 """ Show all string methods in completions """
91 if event.symbol.startswith('l.'):
92 return ['l.' + func for func in dir('')]
93
94 return dir('') + ['l.' + func for func in dir('')]
95
96 ip.set_hook('complete_command', line_edit_complete_f , str_key = '%led')
97
98 ip.expose_magic('led', line_edit_f) No newline at end of file
@@ -0,0 +1,43
1 # -*- coding: utf-8 -*-
2 """
3 Extension for printing Numeric Arrays in flexible ways.
4 """
5
6 from Numeric import ArrayType
7
8 def num_display(self,arg):
9 """Display method for printing which treats Numeric arrays specially.
10 """
11
12 # Non-numpy variables are printed using the system default
13 if type(arg) != ArrayType:
14 self._display(arg)
15 return
16 # Otherwise, we do work.
17 format = __IPYTHON__.runtime_rc.numarray_print_format
18 print 'NumPy array, format:',format
19 # Here is where all the printing logic needs to be implemented
20 print arg # nothing yet :)
21
22
23 def magic_format(self,parameter_s=''):
24 """Specifies format of numerical output.
25
26 This command is similar to Ocave's format command.
27 """
28
29 valid_formats = ['long','short']
30
31 if parameter_s in valid_formats:
32 self.runtime_rc.numarray_print_format = parameter_s
33 print 'Numeric output format is now:',parameter_s
34 else:
35 print 'Invalid format:',parameter_s
36 print 'Valid formats:',valid_formats
37
38 # setup default format
39 __IPYTHON__.runtime_rc.numarray_print_format = 'long'
40
41 # Bind our new functions to the interpreter
42 __IPYTHON__.__class__.magic_format = magic_format
43 __IPYTHON__.hooks.display = num_display
@@ -0,0 +1,359
1 #!/usr/bin/env python
2
3 """ PickleShare - a small 'shelve' like datastore with concurrency support
4
5 Like shelve, a PickleShareDB object acts like a normal dictionary. Unlike
6 shelve, many processes can access the database simultaneously. Changing a
7 value in database is immediately visible to other processes accessing the
8 same database.
9
10 Concurrency is possible because the values are stored in separate files. Hence
11 the "database" is a directory where *all* files are governed by PickleShare.
12
13 Example usage::
14
15 from pickleshare import *
16 db = PickleShareDB('~/testpickleshare')
17 db.clear()
18 print "Should be empty:",db.items()
19 db['hello'] = 15
20 db['aku ankka'] = [1,2,313]
21 db['paths/are/ok/key'] = [1,(5,46)]
22 print db.keys()
23 del db['aku ankka']
24
25 This module is certainly not ZODB, but can be used for low-load
26 (non-mission-critical) situations where tiny code size trumps the
27 advanced features of a "real" object database.
28
29 Installation guide: easy_install pickleshare
30
31 Author: Ville Vainio <vivainio@gmail.com>
32 License: MIT open source license.
33
34 """
35
36 from IPython.external.path import path as Path
37 import os,stat,time
38 import cPickle as pickle
39 import UserDict
40 import warnings
41 import glob
42
43 from sets import Set as set
44
45 def gethashfile(key):
46 return ("%02x" % abs(hash(key) % 256))[-2:]
47
48 _sentinel = object()
49
50 class PickleShareDB(UserDict.DictMixin):
51 """ The main 'connection' object for PickleShare database """
52 def __init__(self,root):
53 """ Return a db object that will manage the specied directory"""
54 self.root = Path(root).expanduser().abspath()
55 if not self.root.isdir():
56 self.root.makedirs()
57 # cache has { 'key' : (obj, orig_mod_time) }
58 self.cache = {}
59
60
61 def __getitem__(self,key):
62 """ db['key'] reading """
63 fil = self.root / key
64 try:
65 mtime = (fil.stat()[stat.ST_MTIME])
66 except OSError:
67 raise KeyError(key)
68
69 if fil in self.cache and mtime == self.cache[fil][1]:
70 return self.cache[fil][0]
71 try:
72 # The cached item has expired, need to read
73 obj = pickle.load(fil.open())
74 except:
75 raise KeyError(key)
76
77 self.cache[fil] = (obj,mtime)
78 return obj
79
80 def __setitem__(self,key,value):
81 """ db['key'] = 5 """
82 fil = self.root / key
83 parent = fil.parent
84 if parent and not parent.isdir():
85 parent.makedirs()
86 pickled = pickle.dump(value,fil.open('w'))
87 try:
88 self.cache[fil] = (value,fil.mtime)
89 except OSError,e:
90 if e.errno != 2:
91 raise
92
93 def hset(self, hashroot, key, value):
94 """ hashed set """
95 hroot = self.root / hashroot
96 if not hroot.isdir():
97 hroot.makedirs()
98 hfile = hroot / gethashfile(key)
99 d = self.get(hfile, {})
100 d.update( {key : value})
101 self[hfile] = d
102
103
104
105 def hget(self, hashroot, key, default = _sentinel, fast_only = True):
106 """ hashed get """
107 hroot = self.root / hashroot
108 hfile = hroot / gethashfile(key)
109
110 d = self.get(hfile, _sentinel )
111 #print "got dict",d,"from",hfile
112 if d is _sentinel:
113 if fast_only:
114 if default is _sentinel:
115 raise KeyError(key)
116
117 return default
118
119 # slow mode ok, works even after hcompress()
120 d = self.hdict(hashroot)
121
122 return d.get(key, default)
123
124 def hdict(self, hashroot):
125 """ Get all data contained in hashed category 'hashroot' as dict """
126 hfiles = self.keys(hashroot + "/*")
127 hfiles.sort()
128 last = len(hfiles) and hfiles[-1] or ''
129 if last.endswith('xx'):
130 # print "using xx"
131 hfiles = [last] + hfiles[:-1]
132
133 all = {}
134
135 for f in hfiles:
136 # print "using",f
137 try:
138 all.update(self[f])
139 except KeyError:
140 print "Corrupt",f,"deleted - hset is not threadsafe!"
141 del self[f]
142
143 self.uncache(f)
144
145 return all
146
147 def hcompress(self, hashroot):
148 """ Compress category 'hashroot', so hset is fast again
149
150 hget will fail if fast_only is True for compressed items (that were
151 hset before hcompress).
152
153 """
154 hfiles = self.keys(hashroot + "/*")
155 all = {}
156 for f in hfiles:
157 # print "using",f
158 all.update(self[f])
159 self.uncache(f)
160
161 self[hashroot + '/xx'] = all
162 for f in hfiles:
163 p = self.root / f
164 if p.basename() == 'xx':
165 continue
166 p.remove()
167
168
169
170 def __delitem__(self,key):
171 """ del db["key"] """
172 fil = self.root / key
173 self.cache.pop(fil,None)
174 try:
175 fil.remove()
176 except OSError:
177 # notfound and permission denied are ok - we
178 # lost, the other process wins the conflict
179 pass
180
181 def _normalized(self, p):
182 """ Make a key suitable for user's eyes """
183 return str(self.root.relpathto(p)).replace('\\','/')
184
185 def keys(self, globpat = None):
186 """ All keys in DB, or all keys matching a glob"""
187
188 if globpat is None:
189 files = self.root.walkfiles()
190 else:
191 files = [Path(p) for p in glob.glob(self.root/globpat)]
192 return [self._normalized(p) for p in files if p.isfile()]
193
194 def uncache(self,*items):
195 """ Removes all, or specified items from cache
196
197 Use this after reading a large amount of large objects
198 to free up memory, when you won't be needing the objects
199 for a while.
200
201 """
202 if not items:
203 self.cache = {}
204 for it in items:
205 self.cache.pop(it,None)
206
207 def waitget(self,key, maxwaittime = 60 ):
208 """ Wait (poll) for a key to get a value
209
210 Will wait for `maxwaittime` seconds before raising a KeyError.
211 The call exits normally if the `key` field in db gets a value
212 within the timeout period.
213
214 Use this for synchronizing different processes or for ensuring
215 that an unfortunately timed "db['key'] = newvalue" operation
216 in another process (which causes all 'get' operation to cause a
217 KeyError for the duration of pickling) won't screw up your program
218 logic.
219 """
220
221 wtimes = [0.2] * 3 + [0.5] * 2 + [1]
222 tries = 0
223 waited = 0
224 while 1:
225 try:
226 val = self[key]
227 return val
228 except KeyError:
229 pass
230
231 if waited > maxwaittime:
232 raise KeyError(key)
233
234 time.sleep(wtimes[tries])
235 waited+=wtimes[tries]
236 if tries < len(wtimes) -1:
237 tries+=1
238
239 def getlink(self,folder):
240 """ Get a convenient link for accessing items """
241 return PickleShareLink(self, folder)
242
243 def __repr__(self):
244 return "PickleShareDB('%s')" % self.root
245
246
247
248 class PickleShareLink:
249 """ A shortdand for accessing nested PickleShare data conveniently.
250
251 Created through PickleShareDB.getlink(), example::
252
253 lnk = db.getlink('myobjects/test')
254 lnk.foo = 2
255 lnk.bar = lnk.foo + 5
256
257 """
258 def __init__(self, db, keydir ):
259 self.__dict__.update(locals())
260
261 def __getattr__(self,key):
262 return self.__dict__['db'][self.__dict__['keydir']+'/' + key]
263 def __setattr__(self,key,val):
264 self.db[self.keydir+'/' + key] = val
265 def __repr__(self):
266 db = self.__dict__['db']
267 keys = db.keys( self.__dict__['keydir'] +"/*")
268 return "<PickleShareLink '%s': %s>" % (
269 self.__dict__['keydir'],
270 ";".join([Path(k).basename() for k in keys]))
271
272
273 def test():
274 db = PickleShareDB('~/testpickleshare')
275 db.clear()
276 print "Should be empty:",db.items()
277 db['hello'] = 15
278 db['aku ankka'] = [1,2,313]
279 db['paths/nest/ok/keyname'] = [1,(5,46)]
280 db.hset('hash', 'aku', 12)
281 db.hset('hash', 'ankka', 313)
282 print "12 =",db.hget('hash','aku')
283 print "313 =",db.hget('hash','ankka')
284 print "all hashed",db.hdict('hash')
285 print db.keys()
286 print db.keys('paths/nest/ok/k*')
287 print dict(db) # snapsot of whole db
288 db.uncache() # frees memory, causes re-reads later
289
290 # shorthand for accessing deeply nested files
291 lnk = db.getlink('myobjects/test')
292 lnk.foo = 2
293 lnk.bar = lnk.foo + 5
294 print lnk.bar # 7
295
296 def stress():
297 db = PickleShareDB('~/fsdbtest')
298 import time,sys
299 for i in range(1000):
300 for j in range(1000):
301 if i % 15 == 0 and i < 200:
302 if str(j) in db:
303 del db[str(j)]
304 continue
305
306 if j%33 == 0:
307 time.sleep(0.02)
308
309 db[str(j)] = db.get(str(j), []) + [(i,j,"proc %d" % os.getpid())]
310 db.hset('hash',j, db.hget('hash',j,15) + 1 )
311
312 print i,
313 sys.stdout.flush()
314 if i % 10 == 0:
315 db.uncache()
316
317 def main():
318 import textwrap
319 usage = textwrap.dedent("""\
320 pickleshare - manage PickleShare databases
321
322 Usage:
323
324 pickleshare dump /path/to/db > dump.txt
325 pickleshare load /path/to/db < dump.txt
326 pickleshare test /path/to/db
327 """)
328 DB = PickleShareDB
329 import sys
330 if len(sys.argv) < 2:
331 print usage
332 return
333
334 cmd = sys.argv[1]
335 args = sys.argv[2:]
336 if cmd == 'dump':
337 if not args: args= ['.']
338 db = DB(args[0])
339 import pprint
340 pprint.pprint(db.items())
341 elif cmd == 'load':
342 cont = sys.stdin.read()
343 db = DB(args[0])
344 data = eval(cont)
345 db.clear()
346 for k,v in db.items():
347 db[k] = v
348 elif cmd == 'testwait':
349 db = DB(args[0])
350 db.clear()
351 print db.waitget('250')
352 elif cmd == 'test':
353 test()
354 stress()
355
356 if __name__== "__main__":
357 main()
358
359
@@ -0,0 +1,183
1 # -*- coding: utf-8 -*-
2 """
3 %store magic for lightweight persistence.
4
5 Stores variables, aliases etc. in PickleShare database.
6
7 $Id: iplib.py 1107 2006-01-30 19:02:20Z vivainio $
8 """
9
10 import IPython.ipapi
11 ip = IPython.ipapi.get()
12
13 import pickleshare
14
15 import inspect,pickle,os,sys,textwrap
16 from IPython.FakeModule import FakeModule
17
18 def restore_aliases(self):
19 ip = self.getapi()
20 staliases = ip.db.get('stored_aliases', {})
21 for k,v in staliases.items():
22 #print "restore alias",k,v # dbg
23 #self.alias_table[k] = v
24 ip.defalias(k,v)
25
26
27 def refresh_variables(ip):
28 db = ip.db
29 for key in db.keys('autorestore/*'):
30 # strip autorestore
31 justkey = os.path.basename(key)
32 try:
33 obj = db[key]
34 except KeyError:
35 print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey
36 print "The error was:",sys.exc_info()[0]
37 else:
38 #print "restored",justkey,"=",obj #dbg
39 ip.user_ns[justkey] = obj
40
41
42 def restore_dhist(ip):
43 db = ip.db
44 ip.user_ns['_dh'] = db.get('dhist',[])
45
46 def restore_data(self):
47 ip = self.getapi()
48 refresh_variables(ip)
49 restore_aliases(self)
50 restore_dhist(self)
51 raise IPython.ipapi.TryNext
52
53 ip.set_hook('late_startup_hook', restore_data)
54
55 def magic_store(self, parameter_s=''):
56 """Lightweight persistence for python variables.
57
58 Example:
59
60 ville@badger[~]|1> A = ['hello',10,'world']\\
61 ville@badger[~]|2> %store A\\
62 ville@badger[~]|3> Exit
63
64 (IPython session is closed and started again...)
65
66 ville@badger:~$ ipython -p pysh\\
67 ville@badger[~]|1> print A
68
69 ['hello', 10, 'world']
70
71 Usage:
72
73 %store - Show list of all variables and their current values\\
74 %store <var> - Store the *current* value of the variable to disk\\
75 %store -d <var> - Remove the variable and its value from storage\\
76 %store -z - Remove all variables from storage\\
77 %store -r - Refresh all variables from store (delete current vals)\\
78 %store foo >a.txt - Store value of foo to new file a.txt\\
79 %store foo >>a.txt - Append value of foo to file a.txt\\
80
81 It should be noted that if you change the value of a variable, you
82 need to %store it again if you want to persist the new value.
83
84 Note also that the variables will need to be pickleable; most basic
85 python types can be safely %stored.
86
87 Also aliases can be %store'd across sessions.
88 """
89
90 opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
91 args = argsl.split(None,1)
92 ip = self.getapi()
93 db = ip.db
94 # delete
95 if opts.has_key('d'):
96 try:
97 todel = args[0]
98 except IndexError:
99 error('You must provide the variable to forget')
100 else:
101 try:
102 del db['autorestore/' + todel]
103 except:
104 error("Can't delete variable '%s'" % todel)
105 # reset
106 elif opts.has_key('z'):
107 for k in db.keys('autorestore/*'):
108 del db[k]
109
110 elif opts.has_key('r'):
111 refresh_variables(ip)
112
113
114 # run without arguments -> list variables & values
115 elif not args:
116 vars = self.db.keys('autorestore/*')
117 vars.sort()
118 if vars:
119 size = max(map(len,vars))
120 else:
121 size = 0
122
123 print 'Stored variables and their in-db values:'
124 fmt = '%-'+str(size)+'s -> %s'
125 get = db.get
126 for var in vars:
127 justkey = os.path.basename(var)
128 # print 30 first characters from every var
129 print fmt % (justkey,repr(get(var,'<unavailable>'))[:50])
130
131 # default action - store the variable
132 else:
133 # %store foo >file.txt or >>file.txt
134 if len(args) > 1 and args[1].startswith('>'):
135 fnam = os.path.expanduser(args[1].lstrip('>').lstrip())
136 if args[1].startswith('>>'):
137 fil = open(fnam,'a')
138 else:
139 fil = open(fnam,'w')
140 obj = ip.ev(args[0])
141 print "Writing '%s' (%s) to file '%s'." % (args[0],
142 obj.__class__.__name__, fnam)
143
144
145 if not isinstance (obj,basestring):
146 from pprint import pprint
147 pprint(obj,fil)
148 else:
149 fil.write(obj)
150 if not obj.endswith('\n'):
151 fil.write('\n')
152
153 fil.close()
154 return
155
156 # %store foo
157 try:
158 obj = ip.user_ns[args[0]]
159 except KeyError:
160 # it might be an alias
161 if args[0] in self.alias_table:
162 staliases = db.get('stored_aliases',{})
163 staliases[ args[0] ] = self.alias_table[ args[0] ]
164 db['stored_aliases'] = staliases
165 print "Alias stored:", args[0], self.alias_table[ args[0] ]
166 return
167 else:
168 print "Error: unknown variable '%s'" % args[0]
169
170 else:
171 if isinstance(inspect.getmodule(obj), FakeModule):
172 print textwrap.dedent("""\
173 Warning:%s is %s
174 Proper storage of interactively declared classes (or instances
175 of those classes) is not possible! Only instances
176 of classes in real modules on file system can be %%store'd.
177 """ % (args[0], obj) )
178 return
179 #pickled = pickle.dumps(obj)
180 self.db[ 'autorestore/' + args[0] ] = obj
181 print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
182
183 ip.expose_magic('store',magic_store)
@@ -0,0 +1,45
1 import IPython.ipapi
2
3 ip = IPython.ipapi.get()
4
5 def clip_f( self, parameter_s = '' ):
6 """Save a set of lines to the clipboard.
7
8 Usage:\\
9 %clip n1-n2 n3-n4 ... n5 .. n6 ...
10
11 This function uses the same syntax as %macro for line extraction, but
12 instead of creating a macro it saves the resulting string to the
13 clipboard.
14
15 When used without arguments, this returns the text contents of the clipboard.
16 E.g.
17
18 mytext = %clip
19
20 """
21
22 import win32clipboard as cl
23 import win32con
24 args = parameter_s.split()
25 cl.OpenClipboard()
26 if len( args ) == 0:
27 data = cl.GetClipboardData( win32con.CF_TEXT )
28 cl.CloseClipboard()
29 return data
30 api = self.getapi()
31
32 if parameter_s.lstrip().startswith('='):
33 rest = parameter_s[parameter_s.index('=')+1:].strip()
34 val = str(api.ev(rest))
35 else:
36 ranges = args[0:]
37 val = ''.join( self.extract_input_slices( ranges ) )
38
39 cl.EmptyClipboard()
40 cl.SetClipboardText( val )
41 cl.CloseClipboard()
42 print 'The following text was written to the clipboard'
43 print val
44
45 ip.expose_magic( "clip", clip_f )
@@ -0,0 +1,43
1 # -*- coding: utf-8 -*-
2 """
3 Class which mimics a module.
4
5 Needed to allow pickle to correctly resolve namespaces during IPython
6 sessions.
7
8 $Id: FakeModule.py 2754 2007-09-09 10:16:59Z fperez $"""
9
10 #*****************************************************************************
11 # Copyright (C) 2002-2004 Fernando Perez. <fperez@colorado.edu>
12 #
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
15 #*****************************************************************************
16
17 import types
18
19 class FakeModule(types.ModuleType):
20 """Simple class with attribute access to fake a module.
21
22 This is not meant to replace a module, but to allow inserting a fake
23 module in sys.modules so that systems which rely on run-time module
24 importing (like shelve and pickle) work correctly in interactive IPython
25 sessions.
26
27 Do NOT use this code for anything other than this IPython private hack."""
28
29 def __init__(self,adict=None):
30
31 # tmp to force __dict__ instance creation, else self.__dict__ fails
32 self.__iptmp = None
33
34 # It seems pydoc (and perhaps others) needs any module instance to
35 # implement a __nonzero__ method, so we add it if missing:
36 self.__dict__.setdefault('__nonzero__',lambda : True)
37 self.__dict__.setdefault('__file__',__file__)
38
39 # cleanup our temp trick
40 del self.__iptmp
41
42 if adict is not None:
43 self.__dict__.update(adict)
This diff has been collapsed as it changes many lines, (666 lines changed) Show them Hide them
@@ -0,0 +1,666
1 # -*- coding: utf-8 -*-
2 """Improved replacement for the Gnuplot.Gnuplot class.
3
4 This module imports Gnuplot and replaces some of its functionality with
5 improved versions. They add better handling of arrays for plotting and more
6 convenient PostScript generation, plus some fixes for hardcopy().
7
8 It also adds a convenient plot2 method for plotting dictionaries and
9 lists/tuples of arrays.
10
11 This module is meant to be used as a drop-in replacement to the original
12 Gnuplot, so it should be safe to do:
13
14 import IPython.Gnuplot2 as Gnuplot
15
16 $Id: Gnuplot2.py 1210 2006-03-13 01:19:31Z fperez $"""
17
18 import cStringIO
19 import os
20 import string
21 import sys
22 import tempfile
23 import time
24 import types
25
26 import Gnuplot as Gnuplot_ori
27 import Numeric
28
29 from IPython.genutils import popkey,xsys
30
31 # needed by hardcopy():
32 gp = Gnuplot_ori.gp
33
34 # Patch for Gnuplot.py 1.6 compatibility.
35 # Thanks to Hayden Callow <h.callow@elec.canterbury.ac.nz>
36 try:
37 OptionException = Gnuplot_ori.PlotItems.OptionException
38 except AttributeError:
39 OptionException = Gnuplot_ori.Errors.OptionError
40
41 # exhibit a similar interface to Gnuplot so it can be somewhat drop-in
42 Data = Gnuplot_ori.Data
43 Func = Gnuplot_ori.Func
44 GridData = Gnuplot_ori.GridData
45 PlotItem = Gnuplot_ori.PlotItem
46 PlotItems = Gnuplot_ori.PlotItems
47
48 # Modify some of Gnuplot's functions with improved versions (or bugfixed, in
49 # hardcopy's case). In order to preserve the docstrings at runtime, I've
50 # copied them from the original code.
51
52 # After some significant changes in v 1.7 of Gnuplot.py, we need to do a bit
53 # of version checking.
54
55 if Gnuplot_ori.__version__ <= '1.6':
56 _BaseFileItem = PlotItems.File
57 _BaseTempFileItem = PlotItems.TempFile
58
59 # Fix the File class to add the 'index' option for Gnuplot versions < 1.7
60 class File(_BaseFileItem):
61
62 _option_list = _BaseFileItem._option_list.copy()
63 _option_list.update({
64 'index' : lambda self, index: self.set_option_index(index),
65 })
66
67 # A new initializer is needed b/c we want to add a modified
68 # _option_sequence list which includes 'index' in the right place.
69 def __init__(self,*args,**kw):
70 self._option_sequence = ['binary', 'index', 'using', 'smooth', 'axes',
71 'title', 'with']
72
73 _BaseFileItem.__init__(self,*args,**kw)
74
75 # Let's fix the constructor docstring
76 __newdoc = \
77 """Additional Keyword arguments added by IPython:
78
79 'index=<int>' -- similar to the `index` keyword in Gnuplot.
80 This allows only some of the datasets in a file to be
81 plotted. Datasets within a file are assumed to be separated
82 by _pairs_ of blank lines, and the first one is numbered as
83 0 (similar to C/Python usage)."""
84 __init__.__doc__ = PlotItems.File.__init__.__doc__ + __newdoc
85
86 def set_option_index(self, index):
87 if index is None:
88 self.clear_option('index')
89 elif type(index) in [type(''), type(1)]:
90 self._options['index'] = (index, 'index %s' % index)
91 elif type(index) is type(()):
92 self._options['index'] = (index,'index %s' %
93 string.join(map(repr, index), ':'))
94 else:
95 raise OptionException('index=%s' % (index,))
96
97 # We need a FileClass with a different name from 'File', which is a
98 # factory function in 1.7, so that our String class can subclass FileClass
99 # in any version.
100 _FileClass = File
101
102 elif Gnuplot_ori.__version__ =='1.7':
103 _FileClass = _BaseFileItem = PlotItems._FileItem
104 _BaseTempFileItem = PlotItems._TempFileItem
105 File = PlotItems.File
106
107 else: # changes in the newer version (svn as of March'06)
108 _FileClass = _BaseFileItem = PlotItems._FileItem
109 _BaseTempFileItem = PlotItems._NewFileItem
110 File = PlotItems.File
111
112
113 # Now, we can add our generic code which is version independent
114
115 # First some useful utilities
116 def eps_fix_bbox(fname):
117 """Fix the bounding box of an eps file by running ps2eps on it.
118
119 If its name ends in .eps, the original file is removed.
120
121 This is particularly useful for plots made by Gnuplot with square aspect
122 ratio: there is a bug in Gnuplot which makes it generate a bounding box
123 which is far wider than the actual plot.
124
125 This function assumes that ps2eps is installed in your system."""
126
127 # note: ps2ps and eps2eps do NOT work, ONLY ps2eps works correctly. The
128 # others make output with bitmapped fonts, which looks horrible.
129 print 'Fixing eps file: <%s>' % fname
130 xsys('ps2eps -f -q -l %s' % fname)
131 if fname.endswith('.eps'):
132 os.rename(fname+'.eps',fname)
133
134 def is_list1d(x,containers = [types.ListType,types.TupleType]):
135 """Returns true if x appears to be a 1d list/tuple/array.
136
137 The heuristics are: identify Numeric arrays, or lists/tuples whose first
138 element is not itself a list/tuple. This way zipped lists should work like
139 the original Gnuplot. There's no inexpensive way to know if a list doesn't
140 have a composite object after its first element, so that kind of input
141 will produce an error. But it should work well in most cases.
142 """
143 x_type = type(x)
144
145 return x_type == Numeric.ArrayType and len(x.shape)==1 or \
146 (x_type in containers and
147 type(x[0]) not in containers + [Numeric.ArrayType])
148
149 def zip_items(items,titles=None):
150 """zip together neighboring 1-d arrays, and zip standalone ones
151 with their index. Leave other plot items alone."""
152
153 class StandaloneItem(Exception): pass
154
155 def get_titles(titles):
156 """Return the next title and the input titles array.
157
158 The input array may be changed to None when no titles are left to
159 prevent extra unnecessary calls to this function."""
160
161 try:
162 title = titles[tit_ct[0]] # tit_ct[0] is in zip_items'scope
163 except IndexError:
164 titles = None # so we don't enter again
165 title = None
166 else:
167 tit_ct[0] += 1
168 return title,titles
169
170 new_items = []
171
172 if titles:
173 # Initialize counter. It was put in a list as a hack to allow the
174 # nested get_titles to modify it without raising a NameError.
175 tit_ct = [0]
176
177 n = 0 # this loop needs to be done by hand
178 while n < len(items):
179 item = items[n]
180 try:
181 if is_list1d(item):
182 if n==len(items)-1: # last in list
183 raise StandaloneItem
184 else: # check the next item and zip together if needed
185 next_item = items[n+1]
186 if next_item is None:
187 n += 1
188 raise StandaloneItem
189 elif is_list1d(next_item):
190 # this would be best done with an iterator
191 if titles:
192 title,titles = get_titles(titles)
193 else:
194 title = None
195 new_items.append(Data(zip(item,next_item),
196 title=title))
197 n += 1 # avoid double-inclusion of next item
198 else: # can't zip with next, zip with own index list
199 raise StandaloneItem
200 else: # not 1-d array
201 new_items.append(item)
202 except StandaloneItem:
203 if titles:
204 title,titles = get_titles(titles)
205 else:
206 title = None
207 new_items.append(Data(zip(range(len(item)),item),title=title))
208 except AttributeError:
209 new_items.append(item)
210 n+=1
211
212 return new_items
213
214 # And some classes with enhanced functionality.
215 class String(_FileClass):
216 """Make a PlotItem from data in a string with the same format as a File.
217
218 This allows writing data directly inside python scripts using the exact
219 same format and manipulation options which would be used for external
220 files."""
221
222 def __init__(self, data_str, **keyw):
223 """Construct a String object.
224
225 <data_str> is a string formatted exactly like a valid Gnuplot data
226 file would be. All options from the File constructor are valid here.
227
228 Warning: when used for interactive plotting in scripts which exit
229 immediately, you may get an error because the temporary file used to
230 hold the string data was deleted before Gnuplot had a chance to see
231 it. You can work around this problem by putting a raw_input() call at
232 the end of the script.
233
234 This problem does not appear when generating PostScript output, only
235 with Gnuplot windows."""
236
237 self.tmpfile = _BaseTempFileItem()
238 tmpfile = file(self.tmpfile.filename,'w')
239 tmpfile.write(data_str)
240 _BaseFileItem.__init__(self,self.tmpfile,**keyw)
241
242
243 class Gnuplot(Gnuplot_ori.Gnuplot):
244 """Improved Gnuplot class.
245
246 Enhancements: better plot,replot and hardcopy methods. New methods for
247 quick range setting.
248 """
249
250 def xrange(self,min='*',max='*'):
251 """Set xrange. If min/max is omitted, it is set to '*' (auto).
252
253 Note that this is different from the regular Gnuplot behavior, where
254 an unspecified limit means no change. Here any unspecified limit is
255 set to autoscaling, allowing these functions to be used for full
256 autoscaling when called with no arguments.
257
258 To preserve one limit's current value while changing the other, an
259 explicit '' argument must be given as the limit to be kept.
260
261 Similar functions exist for [y{2}z{2}rtuv]range."""
262
263 self('set xrange [%s:%s]' % (min,max))
264
265 def yrange(self,min='*',max='*'):
266 self('set yrange [%s:%s]' % (min,max))
267
268 def zrange(self,min='*',max='*'):
269 self('set zrange [%s:%s]' % (min,max))
270
271 def x2range(self,min='*',max='*'):
272 self('set xrange [%s:%s]' % (min,max))
273
274 def y2range(self,min='*',max='*'):
275 self('set yrange [%s:%s]' % (min,max))
276
277 def z2range(self,min='*',max='*'):
278 self('set zrange [%s:%s]' % (min,max))
279
280 def rrange(self,min='*',max='*'):
281 self('set rrange [%s:%s]' % (min,max))
282
283 def trange(self,min='*',max='*'):
284 self('set trange [%s:%s]' % (min,max))
285
286 def urange(self,min='*',max='*'):
287 self('set urange [%s:%s]' % (min,max))
288
289 def vrange(self,min='*',max='*'):
290 self('set vrange [%s:%s]' % (min,max))
291
292 def set_ps(self,option):
293 """Set an option for the PostScript terminal and reset default term."""
294
295 self('set terminal postscript %s ' % option)
296 self('set terminal %s' % gp.GnuplotOpts.default_term)
297
298 def __plot_ps(self, plot_method,*items, **keyw):
299 """Wrapper for plot/splot/replot, with processing of hardcopy options.
300
301 For internal use only."""
302
303 # Filter out PostScript options which will crash the normal plot/replot
304 psargs = {'filename':None,
305 'mode':None,
306 'eps':None,
307 'enhanced':None,
308 'color':None,
309 'solid':None,
310 'duplexing':None,
311 'fontname':None,
312 'fontsize':None,
313 'debug':0 }
314
315 for k in psargs.keys():
316 if keyw.has_key(k):
317 psargs[k] = keyw[k]
318 del keyw[k]
319
320 # Filter out other options the original plot doesn't know
321 hardcopy = popkey(keyw,'hardcopy',psargs['filename'] is not None)
322 titles = popkey(keyw,'titles',0)
323
324 # the filename keyword should control hardcopy generation, this is an
325 # override switch only which needs to be explicitly set to zero
326 if hardcopy:
327 if psargs['filename'] is None:
328 raise ValueError, \
329 'If you request hardcopy, you must give a filename.'
330
331 # set null output so nothing goes to screen. hardcopy() restores output
332 self('set term dumb')
333 # I don't know how to prevent screen output in Windows
334 if os.name == 'posix':
335 self('set output "/dev/null"')
336
337 new_items = zip_items(items,titles)
338 # plot_method is either plot or replot from the original Gnuplot class:
339 plot_method(self,*new_items,**keyw)
340
341 # Do hardcopy if requested
342 if hardcopy:
343 if psargs['filename'].endswith('.eps'):
344 psargs['eps'] = 1
345 self.hardcopy(**psargs)
346
347 def plot(self, *items, **keyw):
348 """Draw a new plot.
349
350 Clear the current plot and create a new 2-d plot containing
351 the specified items. Each arguments should be of the
352 following types:
353
354 'PlotItem' (e.g., 'Data', 'File', 'Func') -- This is the most
355 flexible way to call plot because the PlotItems can
356 contain suboptions. Moreover, PlotItems can be saved to
357 variables so that their lifetime is longer than one plot
358 command; thus they can be replotted with minimal overhead.
359
360 'string' (e.g., 'sin(x)') -- The string is interpreted as
361 'Func(string)' (a function that is computed by gnuplot).
362
363 Anything else -- The object, which should be convertible to an
364 array, is passed to the 'Data' constructor, and thus
365 plotted as data. If the conversion fails, an exception is
366 raised.
367
368
369 This is a modified version of plot(). Compared to the original in
370 Gnuplot.py, this version has several enhancements, listed below.
371
372
373 Modifications to the input arguments
374 ------------------------------------
375
376 (1-d array means Numeric array, list or tuple):
377
378 (i) Any 1-d array which is NOT followed by another 1-d array, is
379 automatically zipped with range(len(array_1d)). Typing g.plot(y) will
380 plot y against its indices.
381
382 (ii) If two 1-d arrays are contiguous in the argument list, they are
383 automatically zipped together. So g.plot(x,y) plots y vs. x, and
384 g.plot(x1,y1,x2,y2) plots y1 vs. x1 and y2 vs. x2.
385
386 (iii) Any 1-d array which is followed by None is automatically zipped
387 with range(len(array_1d)). In this form, typing g.plot(y1,None,y2)
388 will plot both y1 and y2 against their respective indices (and NOT
389 versus one another). The None prevents zipping y1 and y2 together, and
390 since y2 is unpaired it is automatically zipped to its indices by (i)
391
392 (iv) Any other arguments which don't match these cases are left alone and
393 passed to the code below.
394
395 For lists or tuples, the heuristics used to determine whether they are
396 in fact 1-d is fairly simplistic: their first element is checked, and
397 if it is not a list or tuple itself, it is assumed that the whole
398 object is one-dimensional.
399
400 An additional optional keyword 'titles' has been added: it must be a
401 list of strings to be used as labels for the individual plots which
402 are NOT PlotItem objects (since those objects carry their own labels
403 within).
404
405
406 PostScript generation
407 ---------------------
408
409 This version of plot() also handles automatically the production of
410 PostScript output. The main options are (given as keyword arguments):
411
412 - filename: a string, typically ending in .eps. If given, the plot is
413 sent to this file in PostScript format.
414
415 - hardcopy: this can be set to 0 to override 'filename'. It does not
416 need to be given to produce PostScript, its purpose is to allow
417 switching PostScript output off globally in scripts without having to
418 manually change 'filename' values in multiple calls.
419
420 All other keywords accepted by Gnuplot.hardcopy() are transparently
421 passed, and safely ignored if output is sent to the screen instead of
422 PostScript.
423
424 For example:
425
426 In [1]: x=frange(0,2*pi,npts=100)
427
428 Generate a plot in file 'sin.eps':
429
430 In [2]: plot(x,sin(x),filename = 'sin.eps')
431
432 Plot to screen instead, without having to change the filename:
433
434 In [3]: plot(x,sin(x),filename = 'sin.eps',hardcopy=0)
435
436 Pass the 'color=0' option to hardcopy for monochrome output:
437
438 In [4]: plot(x,sin(x),filename = 'sin.eps',color=0)
439
440 PostScript generation through plot() is useful mainly for scripting
441 uses where you are not interested in interactive plotting. For
442 interactive use, the hardcopy() function is typically more convenient:
443
444 In [5]: plot(x,sin(x))
445
446 In [6]: hardcopy('sin.eps') """
447
448 self.__plot_ps(Gnuplot_ori.Gnuplot.plot,*items,**keyw)
449
450 def plot2(self,arg,**kw):
451 """Plot the entries of a dictionary or a list/tuple of arrays.
452
453 This simple utility calls plot() with a list of Gnuplot.Data objects
454 constructed either from the values of the input dictionary, or the entries
455 in it if it is a tuple or list. Each item gets labeled with the key/index
456 in the Gnuplot legend.
457
458 Each item is plotted by zipping it with a list of its indices.
459
460 Any keywords are passed directly to plot()."""
461
462 if hasattr(arg,'keys'):
463 keys = arg.keys()
464 keys.sort()
465 else:
466 keys = range(len(arg))
467
468 pitems = [Data(zip(range(len(arg[k])),arg[k]),title=`k`) for k in keys]
469 self.plot(*pitems,**kw)
470
471 def splot(self, *items, **keyw):
472 """Draw a new three-dimensional plot.
473
474 Clear the current plot and create a new 3-d plot containing
475 the specified items. Arguments can be of the following types:
476
477 'PlotItem' (e.g., 'Data', 'File', 'Func', 'GridData' ) -- This
478 is the most flexible way to call plot because the
479 PlotItems can contain suboptions. Moreover, PlotItems can
480 be saved to variables so that their lifetime is longer
481 than one plot command--thus they can be replotted with
482 minimal overhead.
483
484 'string' (e.g., 'sin(x*y)') -- The string is interpreted as a
485 'Func()' (a function that is computed by gnuplot).
486
487 Anything else -- The object is converted to a Data() item, and
488 thus plotted as data. Note that each data point should
489 normally have at least three values associated with it
490 (i.e., x, y, and z). If the conversion fails, an
491 exception is raised.
492
493 This is a modified version of splot(). Compared to the original in
494 Gnuplot.py, this version has several enhancements, listed in the
495 plot() documentation.
496 """
497
498 self.__plot_ps(Gnuplot_ori.Gnuplot.splot,*items,**keyw)
499
500 def replot(self, *items, **keyw):
501 """Replot the data, possibly adding new 'PlotItem's.
502
503 Replot the existing graph, using the items in the current
504 itemlist. If arguments are specified, they are interpreted as
505 additional items to be plotted alongside the existing items on
506 the same graph. See 'plot' for details.
507
508 If you want to replot to a postscript file, you MUST give the
509 'filename' keyword argument in each call to replot. The Gnuplot python
510 interface has no way of knowing that your previous call to
511 Gnuplot.plot() was meant for PostScript output."""
512
513 self.__plot_ps(Gnuplot_ori.Gnuplot.replot,*items,**keyw)
514
515 # The original hardcopy has a bug. See fix at the end. The rest of the code
516 # was lifted verbatim from the original, so that people using IPython get the
517 # benefits without having to manually patch Gnuplot.py
518 def hardcopy(self, filename=None,
519 mode=None,
520 eps=None,
521 enhanced=None,
522 color=None,
523 solid=None,
524 duplexing=None,
525 fontname=None,
526 fontsize=None,
527 debug = 0,
528 ):
529 """Create a hardcopy of the current plot.
530
531 Create a postscript hardcopy of the current plot to the
532 default printer (if configured) or to the specified filename.
533
534 Note that gnuplot remembers the postscript suboptions across
535 terminal changes. Therefore if you set, for example, color=1
536 for one hardcopy then the next hardcopy will also be color
537 unless you explicitly choose color=0. Alternately you can
538 force all of the options to their defaults by setting
539 mode='default'. I consider this to be a bug in gnuplot.
540
541 Keyword arguments:
542
543 'filename=<string>' -- if a filename is specified, save the
544 output in that file; otherwise print it immediately
545 using the 'default_lpr' configuration option. If the
546 filename ends in '.eps', EPS mode is automatically
547 selected (like manually specifying eps=1 or mode='eps').
548
549 'mode=<string>' -- set the postscript submode ('landscape',
550 'portrait', 'eps', or 'default'). The default is
551 to leave this option unspecified.
552
553 'eps=<bool>' -- shorthand for 'mode="eps"'; asks gnuplot to
554 generate encapsulated postscript.
555
556 'enhanced=<bool>' -- if set (the default), then generate
557 enhanced postscript, which allows extra features like
558 font-switching, superscripts, and subscripts in axis
559 labels. (Some old gnuplot versions do not support
560 enhanced postscript; if this is the case set
561 gp.GnuplotOpts.prefer_enhanced_postscript=None.)
562
563 'color=<bool>' -- if set, create a plot with color. Default
564 is to leave this option unchanged.
565
566 'solid=<bool>' -- if set, force lines to be solid (i.e., not
567 dashed).
568
569 'duplexing=<string>' -- set duplexing option ('defaultplex',
570 'simplex', or 'duplex'). Only request double-sided
571 printing if your printer can handle it. Actually this
572 option is probably meaningless since hardcopy() can only
573 print a single plot at a time.
574
575 'fontname=<string>' -- set the default font to <string>,
576 which must be a valid postscript font. The default is
577 to leave this option unspecified.
578
579 'fontsize=<double>' -- set the default font size, in
580 postscript points.
581
582 'debug=<bool>' -- print extra debugging information (useful if
583 your PostScript files are misteriously not being created).
584 """
585
586 if filename is None:
587 assert gp.GnuplotOpts.default_lpr is not None, \
588 OptionException('default_lpr is not set, so you can only '
589 'print to a file.')
590 filename = gp.GnuplotOpts.default_lpr
591 lpr_output = 1
592 else:
593 if filename.endswith('.eps'):
594 eps = 1
595 lpr_output = 0
596
597 # Be careful processing the options. If the user didn't
598 # request an option explicitly, do not specify it on the 'set
599 # terminal' line (don't even specify the default value for the
600 # option). This is to avoid confusing older versions of
601 # gnuplot that do not support all of these options. The
602 # exception is 'enhanced', which is just too useful to have to
603 # specify each time!
604
605 setterm = ['set', 'terminal', 'postscript']
606 if eps:
607 assert mode is None or mode=='eps', \
608 OptionException('eps option and mode are incompatible')
609 setterm.append('eps')
610 else:
611 if mode is not None:
612 assert mode in ['landscape', 'portrait', 'eps', 'default'], \
613 OptionException('illegal mode "%s"' % mode)
614 setterm.append(mode)
615 if enhanced is None:
616 enhanced = gp.GnuplotOpts.prefer_enhanced_postscript
617 if enhanced is not None:
618 if enhanced: setterm.append('enhanced')
619 else: setterm.append('noenhanced')
620 if color is not None:
621 if color: setterm.append('color')
622 else: setterm.append('monochrome')
623 if solid is not None:
624 if solid: setterm.append('solid')
625 else: setterm.append('dashed')
626 if duplexing is not None:
627 assert duplexing in ['defaultplex', 'simplex', 'duplex'], \
628 OptionException('illegal duplexing mode "%s"' % duplexing)
629 setterm.append(duplexing)
630 if fontname is not None:
631 setterm.append('"%s"' % fontname)
632 if fontsize is not None:
633 setterm.append('%s' % fontsize)
634
635 self(string.join(setterm))
636 self.set_string('output', filename)
637 # replot the current figure (to the printer):
638 self.refresh()
639
640 # fperez. Ugly kludge: often for some reason the file is NOT created
641 # and we must reissue the creation commands. I have no idea why!
642 if not lpr_output:
643 #print 'Hardcopy <%s>' % filename # dbg
644 maxtries = 20
645 delay = 0.1 # delay (in seconds) between print attempts
646 for i in range(maxtries):
647 time.sleep(0.05) # safety, very small delay
648 if os.path.isfile(filename):
649 if debug:
650 print 'Hardcopy to file <%s> success at attempt #%s.' \
651 % (filename,i+1)
652 break
653 time.sleep(delay)
654 # try again, issue all commands just in case
655 self(string.join(setterm))
656 self.set_string('output', filename)
657 self.refresh()
658 if not os.path.isfile(filename):
659 print >> sys.stderr,'ERROR: Tried %s times and failed to '\
660 'create hardcopy file `%s`' % (maxtries,filename)
661
662 # reset the terminal to its `default' setting:
663 self('set terminal %s' % gp.GnuplotOpts.default_term)
664 self.set_string('output')
665
666 #********************** End of file <Gnuplot2.py> ************************
@@ -0,0 +1,148
1 # -*- coding: utf-8 -*-
2 """Interactive functions and magic functions for Gnuplot usage.
3
4 This requires the Gnuplot.py module for interfacing python with Gnuplot, which
5 can be downloaded from:
6
7 http://gnuplot-py.sourceforge.net/
8
9 See gphelp() below for details on the services offered by this module.
10
11 Inspired by a suggestion/request from Arnd Baecker.
12
13 $Id: GnuplotInteractive.py 389 2004-10-09 07:59:30Z fperez $"""
14
15 __all__ = ['Gnuplot','gp','gp_new','plot','plot2','splot','replot',
16 'hardcopy','gpdata','gpfile','gpstring','gpfunc','gpgrid',
17 'gphelp']
18
19 import IPython.GnuplotRuntime as GRun
20 from IPython.genutils import page,warn
21
22 # Set global names for interactive use
23 Gnuplot = GRun.Gnuplot
24 gp_new = GRun.gp_new
25 gp = GRun.gp
26 plot = gp.plot
27 plot2 = gp.plot2
28 splot = gp.splot
29 replot = gp.replot
30 hardcopy = gp.hardcopy
31
32 # Accessors for the main plot object constructors:
33 gpdata = Gnuplot.Data
34 gpfile = Gnuplot.File
35 gpstring = Gnuplot.String
36 gpfunc = Gnuplot.Func
37 gpgrid = Gnuplot.GridData
38
39 def gphelp():
40 """Print information about the Gnuplot facilities in IPython."""
41
42 page("""
43 IPython provides an interface to access the Gnuplot scientific plotting
44 system, in an environment similar to that of Mathematica or Matlab.
45
46 New top-level global objects
47 ----------------------------
48
49 Please see their respective docstrings for further details.
50
51 - gp: a running Gnuplot instance. You can access its methods as
52 gp.<method>. gp(`a string`) will execute the given string as if it had been
53 typed in an interactive gnuplot window.
54
55 - plot, splot, replot and hardcopy: aliases to the methods of the same name in
56 the global running Gnuplot instance gp. These allow you to simply type:
57
58 In [1]: plot(x,sin(x),title='Sin(x)') # assuming x is a Numeric array
59
60 and obtain a plot of sin(x) vs x with the title 'Sin(x)'.
61
62 - gp_new: a function which returns a new Gnuplot instance. This can be used to
63 have multiple Gnuplot instances running in your session to compare different
64 plots, each in a separate window.
65
66 - Gnuplot: alias to the Gnuplot2 module, an improved drop-in replacement for
67 the original Gnuplot.py. Gnuplot2 needs Gnuplot but redefines several of its
68 functions with improved versions (Gnuplot2 comes with IPython).
69
70 - gpdata, gpfile, gpstring, gpfunc, gpgrid: aliases to Gnuplot.Data,
71 Gnuplot.File, Gnuplot.String, Gnuplot.Func and Gnuplot.GridData
72 respectively. These functions create objects which can then be passed to the
73 plotting commands. See the Gnuplot.py documentation for details.
74
75 Keep in mind that all commands passed to a Gnuplot instance are executed in
76 the Gnuplot namespace, where no Python variables exist. For example, for
77 plotting sin(x) vs x as above, typing
78
79 In [2]: gp('plot x,sin(x)')
80
81 would not work. Instead, you would get the plot of BOTH the functions 'x' and
82 'sin(x)', since Gnuplot doesn't know about the 'x' Python array. The plot()
83 method lives in python and does know about these variables.
84
85
86 New magic functions
87 -------------------
88
89 %gpc: pass one command to Gnuplot and execute it or open a Gnuplot shell where
90 each line of input is executed.
91
92 %gp_set_default: reset the value of IPython's global Gnuplot instance.""")
93
94 # Code below is all for IPython use
95 # Define the magic functions for communicating with the above gnuplot instance.
96 def magic_gpc(self,parameter_s=''):
97 """Execute a gnuplot command or open a gnuplot shell.
98
99 Usage (omit the % if automagic is on). There are two ways to use it:
100
101 1) %gpc 'command' -> passes 'command' directly to the gnuplot instance.
102
103 2) %gpc -> will open up a prompt (gnuplot>>>) which takes input like the
104 standard gnuplot interactive prompt. If you need to type a multi-line
105 command, use \\ at the end of each intermediate line.
106
107 Upon exiting of the gnuplot sub-shell, you return to your IPython
108 session (the gnuplot sub-shell can be invoked as many times as needed).
109 """
110
111 if parameter_s.strip():
112 self.shell.gnuplot(parameter_s)
113 else:
114 self.shell.gnuplot.interact()
115
116 def magic_gp_set_default(self,parameter_s=''):
117 """Set the default gnuplot instance accessed by the %gp magic function.
118
119 %gp_set_default name
120
121 Call with the name of the new instance at the command line. If you want to
122 set this instance in your own code (using an embedded IPython, for
123 example), simply set the variable __IPYTHON__.gnuplot to your own gnuplot
124 instance object."""
125
126 gname = parameter_s.strip()
127 G = eval(gname,self.shell.user_ns)
128 self.shell.gnuplot = G
129 self.shell.user_ns.update({'plot':G.plot,'splot':G.splot,'plot2':G.plot2,
130 'replot':G.replot,'hardcopy':G.hardcopy})
131
132 try:
133 __IPYTHON__
134 except NameError:
135 pass
136 else:
137 # make the global Gnuplot instance known to IPython
138 __IPYTHON__.gnuplot = GRun.gp
139 __IPYTHON__.gnuplot.shell_first_time = 1
140
141 print """*** Type `gphelp` for help on the Gnuplot integration features."""
142
143 # Add the new magic functions to the class dict
144 from IPython.iplib import InteractiveShell
145 InteractiveShell.magic_gpc = magic_gpc
146 InteractiveShell.magic_gp_set_default = magic_gp_set_default
147
148 #********************** End of file <GnuplotInteractive.py> *******************
@@ -0,0 +1,147
1 # -*- coding: utf-8 -*-
2 """Basic Gnuplot functionality for inclusion in other code.
3
4 This module creates a running Gnuplot instance called 'gp' and builds other
5 convenient globals for quick use in running scripts. It is intended to allow
6 you to script plotting tasks in Python with a minimum of effort. A typical
7 usage would be:
8
9 import IPython.GnuplotRuntime as GP # or some other short name
10 GP.gp.plot(GP.File('your_data.dat'))
11
12
13 This module exposes the following objects:
14
15 - gp: a running Gnuplot instance. You can access its methods as
16 gp.<method>. gp(`a string`) will execute the given string as if it had been
17 typed in an interactive gnuplot window.
18
19 - gp_new: a function which returns a new Gnuplot instance. This can be used to
20 have multiple Gnuplot instances running in your session to compare different
21 plots.
22
23 - Gnuplot: alias to the Gnuplot2 module, an improved drop-in replacement for
24 the original Gnuplot.py. Gnuplot2 needs Gnuplot but redefines several of its
25 functions with improved versions (Gnuplot2 comes with IPython).
26
27 - Data: alias to Gnuplot.Data, makes a PlotItem from array data.
28
29 - File: alias to Gnuplot.File, makes a PlotItem from a file.
30
31 - String: alias to Gnuplot.String, makes a PlotItem from a string formatted
32 exactly like a file for Gnuplot.File would be.
33
34 - Func: alias to Gnuplot.Func, makes a PlotItem from a function string.
35
36 - GridData: alias to Gnuplot.GridData, makes a PlotItem from grid data.
37
38 - pm3d_config: a string with Gnuplot commands to set up the pm3d mode for
39 surface plotting. You can activate it simply by calling gp(pm3d_config).
40
41 - eps_fix_bbox: A Unix-only function to fix eps files with bad bounding boxes
42 (which Gnuplot generates when the plot size is set to square).
43
44 This requires the Gnuplot.py module for interfacing Python with Gnuplot, which
45 can be downloaded from:
46
47 http://gnuplot-py.sourceforge.net/
48
49 Inspired by a suggestion/request from Arnd Baecker.
50
51 $Id: GnuplotRuntime.py 389 2004-10-09 07:59:30Z fperez $"""
52
53 __all__ = ['Gnuplot','gp','gp_new','Data','File','Func','GridData',
54 'pm3d_config','eps_fix_bbox']
55
56 import os,tempfile,sys
57 from IPython.genutils import getoutput
58
59 #---------------------------------------------------------------------------
60 # Notes on mouse support for Gnuplot.py
61
62 # If you do not have a mouse-enabled gnuplot, set gnuplot_mouse to 0. If you
63 # use gnuplot, you should really grab a recent, mouse enabled copy. It is an
64 # extremely useful feature. Mouse support is official as of gnuplot 4.0,
65 # released in April 2004.
66
67 # For the mouse features to work correctly, you MUST set your Gnuplot.py
68 # module to use temporary files instead of 'inline data' for data
69 # communication. Note that this is the default, so unless you've manually
70 # fiddled with it you should be ok. If you need to make changes, in the
71 # Gnuplot module directory, loook for the gp_unix.py file and make sure the
72 # prefer_inline_data variable is set to 0. If you set it to 1 Gnuplot.py will
73 # try to pass the data to gnuplot via standard input, which completely
74 # confuses the mouse control system (even though it may be a bit faster than
75 # using temp files).
76
77 # As of Gnuplot.py v1.7, a new option was added to use FIFOs (pipes). This
78 # mechanism, while fast, also breaks the mouse system. You must therefore set
79 # the variable prefer_fifo_data to 0 in gp_unix.py.
80
81 tmpname = tempfile.mktemp()
82 open(tmpname,'w').write('set mouse')
83 gnu_out = getoutput('gnuplot '+ tmpname)
84 os.unlink(tmpname)
85 if gnu_out: # Gnuplot won't print anything if it has mouse support
86 print "*** Your version of Gnuplot appears not to have mouse support."
87 gnuplot_mouse = 0
88 else:
89 gnuplot_mouse = 1
90 del tmpname,gnu_out
91
92 # Default state for persistence of new gnuplot instances
93 if os.name in ['nt','dos'] or sys.platform == 'cygwin':
94 gnuplot_persist = 0
95 else:
96 gnuplot_persist = 1
97
98 import IPython.Gnuplot2 as Gnuplot
99
100 class NotGiven: pass
101
102 def gp_new(mouse=NotGiven,persist=NotGiven):
103 """Return a new Gnuplot instance.
104
105 The instance returned uses the improved methods defined in Gnuplot2.
106
107 Options (boolean):
108
109 - mouse: if unspecified, the module global gnuplot_mouse is used.
110
111 - persist: if unspecified, the module global gnuplot_persist is used."""
112
113 if mouse is NotGiven:
114 mouse = gnuplot_mouse
115 if persist is NotGiven:
116 persist = gnuplot_persist
117 g = Gnuplot.Gnuplot(persist=persist)
118 if mouse:
119 g('set mouse')
120 return g
121
122 # Global-level names.
123
124 # A global Gnuplot instance for interactive use:
125 gp = gp_new()
126
127 # Accessors for the main plot object constructors:
128 Data = Gnuplot.Data
129 File = Gnuplot.File
130 Func = Gnuplot.Func
131 String = Gnuplot.String
132 GridData = Gnuplot.GridData
133
134 # A Unix-only function to fix eps files with bad bounding boxes (which Gnuplot
135 # generates when the plot size is set to square):
136 eps_fix_bbox = Gnuplot.eps_fix_bbox
137
138 # String for configuring pm3d. Simply call g(pm3d_config) to execute it. pm3d
139 # is a very nice mode for plotting colormaps on surfaces. Modify the defaults
140 # below to suit your taste.
141 pm3d_config = """
142 set pm3d solid
143 set hidden3d
144 unset surface
145 set isosamples 50
146 """
147 #******************** End of file <GnuplotRuntime.py> ******************
@@ -0,0 +1,284
1 # -*- coding: utf-8 -*-
2 """String interpolation for Python (by Ka-Ping Yee, 14 Feb 2000).
3
4 This module lets you quickly and conveniently interpolate values into
5 strings (in the flavour of Perl or Tcl, but with less extraneous
6 punctuation). You get a bit more power than in the other languages,
7 because this module allows subscripting, slicing, function calls,
8 attribute lookup, or arbitrary expressions. Variables and expressions
9 are evaluated in the namespace of the caller.
10
11 The itpl() function returns the result of interpolating a string, and
12 printpl() prints out an interpolated string. Here are some examples:
13
14 from Itpl import printpl
15 printpl("Here is a $string.")
16 printpl("Here is a $module.member.")
17 printpl("Here is an $object.member.")
18 printpl("Here is a $functioncall(with, arguments).")
19 printpl("Here is an ${arbitrary + expression}.")
20 printpl("Here is an $array[3] member.")
21 printpl("Here is a $dictionary['member'].")
22
23 The filter() function filters a file object so that output through it
24 is interpolated. This lets you produce the illusion that Python knows
25 how to do interpolation:
26
27 import Itpl
28 sys.stdout = Itpl.filter()
29 f = "fancy"
30 print "Isn't this $f?"
31 print "Standard output has been replaced with a $sys.stdout object."
32 sys.stdout = Itpl.unfilter()
33 print "Okay, back $to $normal."
34
35 Under the hood, the Itpl class represents a string that knows how to
36 interpolate values. An instance of the class parses the string once
37 upon initialization; the evaluation and substitution can then be done
38 each time the instance is evaluated with str(instance). For example:
39
40 from Itpl import Itpl
41 s = Itpl("Here is $foo.")
42 foo = 5
43 print str(s)
44 foo = "bar"
45 print str(s)
46
47 $Id: Itpl.py 2918 2007-12-31 14:34:47Z vivainio $
48 """ # ' -> close an open quote for stupid emacs
49
50 #*****************************************************************************
51 #
52 # Copyright (c) 2001 Ka-Ping Yee <ping@lfw.org>
53 #
54 #
55 # Published under the terms of the MIT license, hereby reproduced:
56 #
57 # Permission is hereby granted, free of charge, to any person obtaining a copy
58 # of this software and associated documentation files (the "Software"), to
59 # deal in the Software without restriction, including without limitation the
60 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
61 # sell copies of the Software, and to permit persons to whom the Software is
62 # furnished to do so, subject to the following conditions:
63 #
64 # The above copyright notice and this permission notice shall be included in
65 # all copies or substantial portions of the Software.
66 #
67 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
68 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
69 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
70 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
71 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
72 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
73 # IN THE SOFTWARE.
74 #
75 #*****************************************************************************
76
77 __author__ = 'Ka-Ping Yee <ping@lfw.org>'
78 __license__ = 'MIT'
79
80 import string
81 import sys
82 from tokenize import tokenprog
83 from types import StringType
84
85 class ItplError(ValueError):
86 def __init__(self, text, pos):
87 self.text = text
88 self.pos = pos
89 def __str__(self):
90 return "unfinished expression in %s at char %d" % (
91 repr(self.text), self.pos)
92
93 def matchorfail(text, pos):
94 match = tokenprog.match(text, pos)
95 if match is None:
96 raise ItplError(text, pos)
97 return match, match.end()
98
99 class Itpl:
100 """Class representing a string with interpolation abilities.
101
102 Upon creation, an instance works out what parts of the format
103 string are literal and what parts need to be evaluated. The
104 evaluation and substitution happens in the namespace of the
105 caller when str(instance) is called."""
106
107 def __init__(self, format,codec=sys.stdin.encoding,encoding_errors='backslashreplace'):
108 """The single mandatory argument to this constructor is a format
109 string.
110
111 The format string is parsed according to the following rules:
112
113 1. A dollar sign and a name, possibly followed by any of:
114 - an open-paren, and anything up to the matching paren
115 - an open-bracket, and anything up to the matching bracket
116 - a period and a name
117 any number of times, is evaluated as a Python expression.
118
119 2. A dollar sign immediately followed by an open-brace, and
120 anything up to the matching close-brace, is evaluated as
121 a Python expression.
122
123 3. Outside of the expressions described in the above two rules,
124 two dollar signs in a row give you one literal dollar sign.
125
126 Optional arguments:
127
128 - codec('utf_8'): a string containing the name of a valid Python
129 codec.
130
131 - encoding_errors('backslashreplace'): a string with a valid error handling
132 policy. See the codecs module documentation for details.
133
134 These are used to encode the format string if a call to str() fails on
135 the expanded result."""
136
137 if not isinstance(format,basestring):
138 raise TypeError, "needs string initializer"
139 self.format = format
140 self.codec = codec
141 self.encoding_errors = encoding_errors
142
143 namechars = "abcdefghijklmnopqrstuvwxyz" \
144 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
145 chunks = []
146 pos = 0
147
148 while 1:
149 dollar = string.find(format, "$", pos)
150 if dollar < 0: break
151 nextchar = format[dollar+1]
152
153 if nextchar == "{":
154 chunks.append((0, format[pos:dollar]))
155 pos, level = dollar+2, 1
156 while level:
157 match, pos = matchorfail(format, pos)
158 tstart, tend = match.regs[3]
159 token = format[tstart:tend]
160 if token == "{": level = level+1
161 elif token == "}": level = level-1
162 chunks.append((1, format[dollar+2:pos-1]))
163
164 elif nextchar in namechars:
165 chunks.append((0, format[pos:dollar]))
166 match, pos = matchorfail(format, dollar+1)
167 while pos < len(format):
168 if format[pos] == "." and \
169 pos+1 < len(format) and format[pos+1] in namechars:
170 match, pos = matchorfail(format, pos+1)
171 elif format[pos] in "([":
172 pos, level = pos+1, 1
173 while level:
174 match, pos = matchorfail(format, pos)
175 tstart, tend = match.regs[3]
176 token = format[tstart:tend]
177 if token[0] in "([": level = level+1
178 elif token[0] in ")]": level = level-1
179 else: break
180 chunks.append((1, format[dollar+1:pos]))
181
182 else:
183 chunks.append((0, format[pos:dollar+1]))
184 pos = dollar + 1 + (nextchar == "$")
185
186 if pos < len(format): chunks.append((0, format[pos:]))
187 self.chunks = chunks
188
189 def __repr__(self):
190 return "<Itpl %s >" % repr(self.format)
191
192 def _str(self,glob,loc):
193 """Evaluate to a string in the given globals/locals.
194
195 The final output is built by calling str(), but if this fails, the
196 result is encoded with the instance's codec and error handling policy,
197 via a call to out.encode(self.codec,self.encoding_errors)"""
198 result = []
199 app = result.append
200 for live, chunk in self.chunks:
201 if live:
202 val = eval(chunk,glob,loc)
203 try:
204 app(str(val))
205 except UnicodeEncodeError:
206 app(unicode(val))
207
208 else: app(chunk)
209 out = ''.join(result)
210 try:
211 return str(out)
212 except UnicodeError:
213 return out.encode(self.codec,self.encoding_errors)
214
215 def __str__(self):
216 """Evaluate and substitute the appropriate parts of the string."""
217
218 # We need to skip enough frames to get to the actual caller outside of
219 # Itpl.
220 frame = sys._getframe(1)
221 while frame.f_globals["__name__"] == __name__: frame = frame.f_back
222 loc, glob = frame.f_locals, frame.f_globals
223
224 return self._str(glob,loc)
225
226 class ItplNS(Itpl):
227 """Class representing a string with interpolation abilities.
228
229 This inherits from Itpl, but at creation time a namespace is provided
230 where the evaluation will occur. The interpolation becomes a bit more
231 efficient, as no traceback needs to be extracte. It also allows the
232 caller to supply a different namespace for the interpolation to occur than
233 its own."""
234
235 def __init__(self, format,globals,locals=None,
236 codec='utf_8',encoding_errors='backslashreplace'):
237 """ItplNS(format,globals[,locals]) -> interpolating string instance.
238
239 This constructor, besides a format string, takes a globals dictionary
240 and optionally a locals (which defaults to globals if not provided).
241
242 For further details, see the Itpl constructor."""
243
244 if locals is None:
245 locals = globals
246 self.globals = globals
247 self.locals = locals
248 Itpl.__init__(self,format,codec,encoding_errors)
249
250 def __str__(self):
251 """Evaluate and substitute the appropriate parts of the string."""
252 return self._str(self.globals,self.locals)
253
254 def __repr__(self):
255 return "<ItplNS %s >" % repr(self.format)
256
257 # utilities for fast printing
258 def itpl(text): return str(Itpl(text))
259 def printpl(text): print itpl(text)
260 # versions with namespace
261 def itplns(text,globals,locals=None): return str(ItplNS(text,globals,locals))
262 def printplns(text,globals,locals=None): print itplns(text,globals,locals)
263
264 class ItplFile:
265 """A file object that filters each write() through an interpolator."""
266 def __init__(self, file): self.file = file
267 def __repr__(self): return "<interpolated " + repr(self.file) + ">"
268 def __getattr__(self, attr): return getattr(self.file, attr)
269 def write(self, text): self.file.write(str(Itpl(text)))
270
271 def filter(file=sys.stdout):
272 """Return an ItplFile that filters writes to the given file object.
273
274 'file = filter(file)' replaces 'file' with a filtered object that
275 has a write() method. When called with no argument, this creates
276 a filter to sys.stdout."""
277 return ItplFile(file)
278
279 def unfilter(ifile=None):
280 """Return the original file that corresponds to the given ItplFile.
281
282 'file = unfilter(file)' undoes the effect of 'file = filter(file)'.
283 'sys.stdout = unfilter()' undoes the effect of 'sys.stdout = filter()'."""
284 return ifile and ifile.file or sys.stdout.file
@@ -0,0 +1,270
1 # -*- coding: utf-8 -*-
2 """
3 Logger class for IPython's logging facilities.
4
5 $Id: Logger.py 2875 2007-11-26 08:37:39Z fperez $
6 """
7
8 #*****************************************************************************
9 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
10 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
11 #
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
14 #*****************************************************************************
15
16 #****************************************************************************
17 # Modules and globals
18
19 from IPython import Release
20 __author__ = '%s <%s>\n%s <%s>' % \
21 ( Release.authors['Janko'] + Release.authors['Fernando'] )
22 __license__ = Release.license
23
24 # Python standard modules
25 import glob
26 import os
27 import time
28
29 #****************************************************************************
30 # FIXME: This class isn't a mixin anymore, but it still needs attributes from
31 # ipython and does input cache management. Finish cleanup later...
32
33 class Logger(object):
34 """A Logfile class with different policies for file creation"""
35
36 def __init__(self,shell,logfname='Logger.log',loghead='',logmode='over'):
37
38 self._i00,self._i,self._ii,self._iii = '','','',''
39
40 # this is the full ipython instance, we need some attributes from it
41 # which won't exist until later. What a mess, clean up later...
42 self.shell = shell
43
44 self.logfname = logfname
45 self.loghead = loghead
46 self.logmode = logmode
47 self.logfile = None
48
49 # Whether to log raw or processed input
50 self.log_raw_input = False
51
52 # whether to also log output
53 self.log_output = False
54
55 # whether to put timestamps before each log entry
56 self.timestamp = False
57
58 # activity control flags
59 self.log_active = False
60
61 # logmode is a validated property
62 def _set_mode(self,mode):
63 if mode not in ['append','backup','global','over','rotate']:
64 raise ValueError,'invalid log mode %s given' % mode
65 self._logmode = mode
66
67 def _get_mode(self):
68 return self._logmode
69
70 logmode = property(_get_mode,_set_mode)
71
72 def logstart(self,logfname=None,loghead=None,logmode=None,
73 log_output=False,timestamp=False,log_raw_input=False):
74 """Generate a new log-file with a default header.
75
76 Raises RuntimeError if the log has already been started"""
77
78 if self.logfile is not None:
79 raise RuntimeError('Log file is already active: %s' %
80 self.logfname)
81
82 self.log_active = True
83
84 # The parameters can override constructor defaults
85 if logfname is not None: self.logfname = logfname
86 if loghead is not None: self.loghead = loghead
87 if logmode is not None: self.logmode = logmode
88
89 # Parameters not part of the constructor
90 self.timestamp = timestamp
91 self.log_output = log_output
92 self.log_raw_input = log_raw_input
93
94 # init depending on the log mode requested
95 isfile = os.path.isfile
96 logmode = self.logmode
97
98 if logmode == 'append':
99 self.logfile = open(self.logfname,'a')
100
101 elif logmode == 'backup':
102 if isfile(self.logfname):
103 backup_logname = self.logfname+'~'
104 # Manually remove any old backup, since os.rename may fail
105 # under Windows.
106 if isfile(backup_logname):
107 os.remove(backup_logname)
108 os.rename(self.logfname,backup_logname)
109 self.logfile = open(self.logfname,'w')
110
111 elif logmode == 'global':
112 self.logfname = os.path.join(self.shell.home_dir,self.logfname)
113 self.logfile = open(self.logfname, 'a')
114
115 elif logmode == 'over':
116 if isfile(self.logfname):
117 os.remove(self.logfname)
118 self.logfile = open(self.logfname,'w')
119
120 elif logmode == 'rotate':
121 if isfile(self.logfname):
122 if isfile(self.logfname+'.001~'):
123 old = glob.glob(self.logfname+'.*~')
124 old.sort()
125 old.reverse()
126 for f in old:
127 root, ext = os.path.splitext(f)
128 num = int(ext[1:-1])+1
129 os.rename(f, root+'.'+`num`.zfill(3)+'~')
130 os.rename(self.logfname, self.logfname+'.001~')
131 self.logfile = open(self.logfname,'w')
132
133 if logmode != 'append':
134 self.logfile.write(self.loghead)
135
136 self.logfile.flush()
137
138 def switch_log(self,val):
139 """Switch logging on/off. val should be ONLY a boolean."""
140
141 if val not in [False,True,0,1]:
142 raise ValueError, \
143 'Call switch_log ONLY with a boolean argument, not with:',val
144
145 label = {0:'OFF',1:'ON',False:'OFF',True:'ON'}
146
147 if self.logfile is None:
148 print """
149 Logging hasn't been started yet (use logstart for that).
150
151 %logon/%logoff are for temporarily starting and stopping logging for a logfile
152 which already exists. But you must first start the logging process with
153 %logstart (optionally giving a logfile name)."""
154
155 else:
156 if self.log_active == val:
157 print 'Logging is already',label[val]
158 else:
159 print 'Switching logging',label[val]
160 self.log_active = not self.log_active
161 self.log_active_out = self.log_active
162
163 def logstate(self):
164 """Print a status message about the logger."""
165 if self.logfile is None:
166 print 'Logging has not been activated.'
167 else:
168 state = self.log_active and 'active' or 'temporarily suspended'
169 print 'Filename :',self.logfname
170 print 'Mode :',self.logmode
171 print 'Output logging :',self.log_output
172 print 'Raw input log :',self.log_raw_input
173 print 'Timestamping :',self.timestamp
174 print 'State :',state
175
176 def log(self,line_ori,line_mod,continuation=None):
177 """Write the line to a log and create input cache variables _i*.
178
179 Inputs:
180
181 - line_ori: unmodified input line from the user. This is not
182 necessarily valid Python.
183
184 - line_mod: possibly modified input, such as the transformations made
185 by input prefilters or input handlers of various kinds. This should
186 always be valid Python.
187
188 - continuation: if True, indicates this is part of multi-line input."""
189
190 # update the auto _i tables
191 #print '***logging line',line_mod # dbg
192 #print '***cache_count', self.shell.outputcache.prompt_count # dbg
193 try:
194 input_hist = self.shell.user_ns['_ih']
195 except:
196 print 'userns:',self.shell.user_ns.keys()
197 return
198
199 out_cache = self.shell.outputcache
200
201 # add blank lines if the input cache fell out of sync.
202 if out_cache.do_full_cache and \
203 out_cache.prompt_count +1 > len(input_hist):
204 input_hist.extend(['\n'] * (out_cache.prompt_count - len(input_hist)))
205
206 if not continuation and line_mod:
207 self._iii = self._ii
208 self._ii = self._i
209 self._i = self._i00
210 # put back the final \n of every input line
211 self._i00 = line_mod+'\n'
212 #print 'Logging input:<%s>' % line_mod # dbg
213 input_hist.append(self._i00)
214 #print '---[%s]' % (len(input_hist)-1,) # dbg
215
216 # hackish access to top-level namespace to create _i1,_i2... dynamically
217 to_main = {'_i':self._i,'_ii':self._ii,'_iii':self._iii}
218 if self.shell.outputcache.do_full_cache:
219 in_num = self.shell.outputcache.prompt_count
220
221 # but if the opposite is true (a macro can produce multiple inputs
222 # with no output display called), then bring the output counter in
223 # sync:
224 last_num = len(input_hist)-1
225 if in_num != last_num:
226 in_num = self.shell.outputcache.prompt_count = last_num
227 new_i = '_i%s' % in_num
228 if continuation:
229 self._i00 = '%s%s\n' % (self.shell.user_ns[new_i],line_mod)
230 input_hist[in_num] = self._i00
231 to_main[new_i] = self._i00
232 self.shell.user_ns.update(to_main)
233
234 # Write the log line, but decide which one according to the
235 # log_raw_input flag, set when the log is started.
236 if self.log_raw_input:
237 self.log_write(line_ori)
238 else:
239 self.log_write(line_mod)
240
241 def log_write(self,data,kind='input'):
242 """Write data to the log file, if active"""
243
244 #print 'data: %r' % data # dbg
245 if self.log_active and data:
246 write = self.logfile.write
247 if kind=='input':
248 if self.timestamp:
249 write(time.strftime('# %a, %d %b %Y %H:%M:%S\n',
250 time.localtime()))
251 write('%s\n' % data)
252 elif kind=='output' and self.log_output:
253 odata = '\n'.join(['#[Out]# %s' % s
254 for s in data.split('\n')])
255 write('%s\n' % odata)
256 self.logfile.flush()
257
258 def logstop(self):
259 """Fully stop logging and close log file.
260
261 In order to start logging again, a new logstart() call needs to be
262 made, possibly (though not necessarily) with a new filename, mode and
263 other options."""
264
265 self.logfile.close()
266 self.logfile = None
267 self.log_active = False
268
269 # For backwards compatibility, in case anyone was using this.
270 close_log = logstop
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now