Show More
The requested changes are too big and content was truncated. Show full diff
@@ -0,0 +1,18 b'' | |||
|
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 b'' | |||
|
1 | link doc/ChangeLog No newline at end of file |
@@ -0,0 +1,170 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 (<)</h3> | |
|
120 | ||
|
121 | <h3>Move the cursor one column to the right (>)</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 b'' | |||
|
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 b'' | |||
|
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 (<)</h3> | |
|
78 | ||
|
79 | <h3>Move the cursor one column to the right (>)</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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 | ||
|
65 | if time() - t > TIMEOUT_GIVEUP: | |
|
66 | print "This is taking too long, we give up." | |
|
67 | ||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 b'' | |||
|
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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644, binary diff hidden |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | 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