Show More
The requested changes are too big and content was truncated. Show full diff
@@ -0,0 +1,18 | |||||
|
1 | syntax: glob | |||
|
2 | ||||
|
3 | *~ | |||
|
4 | *.tmp | |||
|
5 | *.pyc | |||
|
6 | *.bak | |||
|
7 | *.tgz | |||
|
8 | *.org | |||
|
9 | *.rej | |||
|
10 | .svn/ | |||
|
11 | .bzr/ | |||
|
12 | .settings/ | |||
|
13 | .project | |||
|
14 | *.diff | |||
|
15 | IPython_crash_report.txt | |||
|
16 | ||||
|
17 | syntax: regexp | |||
|
18 | .*\#.*\#$ |
@@ -0,0 +1,1 | |||||
|
1 | link doc/ChangeLog No newline at end of file |
@@ -0,0 +1,170 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """Tools for coloring text in ANSI terminals. | |||
|
3 | ||||
|
4 | $Id: ColorANSI.py 2167 2007-03-21 06:57:50Z fperez $""" | |||
|
5 | ||||
|
6 | #***************************************************************************** | |||
|
7 | # Copyright (C) 2002-2006 Fernando Perez. <fperez@colorado.edu> | |||
|
8 | # | |||
|
9 | # Distributed under the terms of the BSD License. The full license is in | |||
|
10 | # the file COPYING, distributed as part of this software. | |||
|
11 | #***************************************************************************** | |||
|
12 | ||||
|
13 | from IPython import Release | |||
|
14 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |||
|
15 | __license__ = Release.license | |||
|
16 | ||||
|
17 | __all__ = ['TermColors','InputTermColors','ColorScheme','ColorSchemeTable'] | |||
|
18 | ||||
|
19 | import os | |||
|
20 | ||||
|
21 | from IPython.ipstruct import Struct | |||
|
22 | ||||
|
23 | def make_color_table(in_class): | |||
|
24 | """Build a set of color attributes in a class. | |||
|
25 | ||||
|
26 | Helper function for building the *TermColors classes.""" | |||
|
27 | ||||
|
28 | color_templates = ( | |||
|
29 | ("Black" , "0;30"), | |||
|
30 | ("Red" , "0;31"), | |||
|
31 | ("Green" , "0;32"), | |||
|
32 | ("Brown" , "0;33"), | |||
|
33 | ("Blue" , "0;34"), | |||
|
34 | ("Purple" , "0;35"), | |||
|
35 | ("Cyan" , "0;36"), | |||
|
36 | ("LightGray" , "0;37"), | |||
|
37 | ("DarkGray" , "1;30"), | |||
|
38 | ("LightRed" , "1;31"), | |||
|
39 | ("LightGreen" , "1;32"), | |||
|
40 | ("Yellow" , "1;33"), | |||
|
41 | ("LightBlue" , "1;34"), | |||
|
42 | ("LightPurple" , "1;35"), | |||
|
43 | ("LightCyan" , "1;36"), | |||
|
44 | ("White" , "1;37"), ) | |||
|
45 | ||||
|
46 | for name,value in color_templates: | |||
|
47 | setattr(in_class,name,in_class._base % value) | |||
|
48 | ||||
|
49 | class TermColors: | |||
|
50 | """Color escape sequences. | |||
|
51 | ||||
|
52 | This class defines the escape sequences for all the standard (ANSI?) | |||
|
53 | colors in terminals. Also defines a NoColor escape which is just the null | |||
|
54 | string, suitable for defining 'dummy' color schemes in terminals which get | |||
|
55 | confused by color escapes. | |||
|
56 | ||||
|
57 | This class should be used as a mixin for building color schemes.""" | |||
|
58 | ||||
|
59 | NoColor = '' # for color schemes in color-less terminals. | |||
|
60 | Normal = '\033[0m' # Reset normal coloring | |||
|
61 | _base = '\033[%sm' # Template for all other colors | |||
|
62 | ||||
|
63 | # Build the actual color table as a set of class attributes: | |||
|
64 | make_color_table(TermColors) | |||
|
65 | ||||
|
66 | class InputTermColors: | |||
|
67 | """Color escape sequences for input prompts. | |||
|
68 | ||||
|
69 | This class is similar to TermColors, but the escapes are wrapped in \001 | |||
|
70 | and \002 so that readline can properly know the length of each line and | |||
|
71 | can wrap lines accordingly. Use this class for any colored text which | |||
|
72 | needs to be used in input prompts, such as in calls to raw_input(). | |||
|
73 | ||||
|
74 | This class defines the escape sequences for all the standard (ANSI?) | |||
|
75 | colors in terminals. Also defines a NoColor escape which is just the null | |||
|
76 | string, suitable for defining 'dummy' color schemes in terminals which get | |||
|
77 | confused by color escapes. | |||
|
78 | ||||
|
79 | This class should be used as a mixin for building color schemes.""" | |||
|
80 | ||||
|
81 | NoColor = '' # for color schemes in color-less terminals. | |||
|
82 | ||||
|
83 | if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs': | |||
|
84 | # (X)emacs on W32 gets confused with \001 and \002 so we remove them | |||
|
85 | Normal = '\033[0m' # Reset normal coloring | |||
|
86 | _base = '\033[%sm' # Template for all other colors | |||
|
87 | else: | |||
|
88 | Normal = '\001\033[0m\002' # Reset normal coloring | |||
|
89 | _base = '\001\033[%sm\002' # Template for all other colors | |||
|
90 | ||||
|
91 | # Build the actual color table as a set of class attributes: | |||
|
92 | make_color_table(InputTermColors) | |||
|
93 | ||||
|
94 | class ColorScheme: | |||
|
95 | """Generic color scheme class. Just a name and a Struct.""" | |||
|
96 | def __init__(self,__scheme_name_,colordict=None,**colormap): | |||
|
97 | self.name = __scheme_name_ | |||
|
98 | if colordict is None: | |||
|
99 | self.colors = Struct(**colormap) | |||
|
100 | else: | |||
|
101 | self.colors = Struct(colordict) | |||
|
102 | ||||
|
103 | def copy(self,name=None): | |||
|
104 | """Return a full copy of the object, optionally renaming it.""" | |||
|
105 | if name is None: | |||
|
106 | name = self.name | |||
|
107 | return ColorScheme(name,self.colors.__dict__) | |||
|
108 | ||||
|
109 | class ColorSchemeTable(dict): | |||
|
110 | """General class to handle tables of color schemes. | |||
|
111 | ||||
|
112 | It's basically a dict of color schemes with a couple of shorthand | |||
|
113 | attributes and some convenient methods. | |||
|
114 | ||||
|
115 | active_scheme_name -> obvious | |||
|
116 | active_colors -> actual color table of the active scheme""" | |||
|
117 | ||||
|
118 | def __init__(self,scheme_list=None,default_scheme=''): | |||
|
119 | """Create a table of color schemes. | |||
|
120 | ||||
|
121 | The table can be created empty and manually filled or it can be | |||
|
122 | created with a list of valid color schemes AND the specification for | |||
|
123 | the default active scheme. | |||
|
124 | """ | |||
|
125 | ||||
|
126 | # create object attributes to be set later | |||
|
127 | self.active_scheme_name = '' | |||
|
128 | self.active_colors = None | |||
|
129 | ||||
|
130 | if scheme_list: | |||
|
131 | if default_scheme == '': | |||
|
132 | raise ValueError,'you must specify the default color scheme' | |||
|
133 | for scheme in scheme_list: | |||
|
134 | self.add_scheme(scheme) | |||
|
135 | self.set_active_scheme(default_scheme) | |||
|
136 | ||||
|
137 | def copy(self): | |||
|
138 | """Return full copy of object""" | |||
|
139 | return ColorSchemeTable(self.values(),self.active_scheme_name) | |||
|
140 | ||||
|
141 | def add_scheme(self,new_scheme): | |||
|
142 | """Add a new color scheme to the table.""" | |||
|
143 | if not isinstance(new_scheme,ColorScheme): | |||
|
144 | raise ValueError,'ColorSchemeTable only accepts ColorScheme instances' | |||
|
145 | self[new_scheme.name] = new_scheme | |||
|
146 | ||||
|
147 | def set_active_scheme(self,scheme,case_sensitive=0): | |||
|
148 | """Set the currently active scheme. | |||
|
149 | ||||
|
150 | Names are by default compared in a case-insensitive way, but this can | |||
|
151 | be changed by setting the parameter case_sensitive to true.""" | |||
|
152 | ||||
|
153 | scheme_names = self.keys() | |||
|
154 | if case_sensitive: | |||
|
155 | valid_schemes = scheme_names | |||
|
156 | scheme_test = scheme | |||
|
157 | else: | |||
|
158 | valid_schemes = [s.lower() for s in scheme_names] | |||
|
159 | scheme_test = scheme.lower() | |||
|
160 | try: | |||
|
161 | scheme_idx = valid_schemes.index(scheme_test) | |||
|
162 | except ValueError: | |||
|
163 | raise ValueError,'Unrecognized color scheme: ' + scheme + \ | |||
|
164 | '\nValid schemes: '+str(scheme_names).replace("'', ",'') | |||
|
165 | else: | |||
|
166 | active = scheme_names[scheme_idx] | |||
|
167 | self.active_scheme_name = active | |||
|
168 | self.active_colors = self[active].colors | |||
|
169 | # Now allow using '' as an index for the current active scheme | |||
|
170 | self[''] = self[active] |
@@ -0,0 +1,116 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """Configuration loader | |||
|
3 | ||||
|
4 | $Id: ConfigLoader.py 1005 2006-01-12 08:39:26Z fperez $""" | |||
|
5 | ||||
|
6 | #***************************************************************************** | |||
|
7 | # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu> | |||
|
8 | # | |||
|
9 | # Distributed under the terms of the BSD License. The full license is in | |||
|
10 | # the file COPYING, distributed as part of this software. | |||
|
11 | #***************************************************************************** | |||
|
12 | ||||
|
13 | from IPython import Release | |||
|
14 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |||
|
15 | __license__ = Release.license | |||
|
16 | ||||
|
17 | import exceptions | |||
|
18 | import os | |||
|
19 | from pprint import pprint | |||
|
20 | ||||
|
21 | from IPython import ultraTB | |||
|
22 | from IPython.ipstruct import Struct | |||
|
23 | from IPython.genutils import * | |||
|
24 | ||||
|
25 | class ConfigLoaderError(exceptions.Exception): | |||
|
26 | """Exception for ConfigLoader class.""" | |||
|
27 | ||||
|
28 | def __init__(self,args=None): | |||
|
29 | self.args = args | |||
|
30 | ||||
|
31 | class ConfigLoader: | |||
|
32 | ||||
|
33 | """Configuration file loader capable of handling recursive inclusions and | |||
|
34 | with parametrized conflict resolution for multiply found keys.""" | |||
|
35 | ||||
|
36 | def __init__(self,conflict=None,field_sep=None,reclimit=15): | |||
|
37 | ||||
|
38 | """The reclimit parameter controls the number of recursive | |||
|
39 | configuration file inclusions. This way we can stop early on (before | |||
|
40 | python's own recursion limit is hit) if there is a circular | |||
|
41 | inclusion. | |||
|
42 | ||||
|
43 | - conflict: dictionary for conflict resolutions (see Struct.merge()) | |||
|
44 | ||||
|
45 | """ | |||
|
46 | self.conflict = conflict | |||
|
47 | self.field_sep = field_sep | |||
|
48 | self.reset(reclimit) | |||
|
49 | ||||
|
50 | def reset(self,reclimit=15): | |||
|
51 | self.reclimit = reclimit | |||
|
52 | self.recdepth = 0 | |||
|
53 | self.included = [] | |||
|
54 | ||||
|
55 | def load(self,fname,convert=None,recurse_key='',incpath = '.',**kw): | |||
|
56 | """Load a configuration file, return the resulting Struct. | |||
|
57 | ||||
|
58 | Call: load_config(fname,convert=None,conflict=None,recurse_key='') | |||
|
59 | ||||
|
60 | - fname: file to load from. | |||
|
61 | - convert: dictionary of type conversions (see read_dict()) | |||
|
62 | - recurse_key: keyword in dictionary to trigger recursive file | |||
|
63 | inclusions. | |||
|
64 | """ | |||
|
65 | ||||
|
66 | if self.recdepth > self.reclimit: | |||
|
67 | raise ConfigLoaderError, 'maximum recursive inclusion of rcfiles '+\ | |||
|
68 | 'exceeded: ' + `self.recdepth` + \ | |||
|
69 | '.\nMaybe you have a circular chain of inclusions?' | |||
|
70 | self.recdepth += 1 | |||
|
71 | fname = filefind(fname,incpath) | |||
|
72 | data = Struct() | |||
|
73 | # avoid including the same file more than once | |||
|
74 | if fname in self.included: | |||
|
75 | return data | |||
|
76 | Xinfo = ultraTB.AutoFormattedTB() | |||
|
77 | if convert==None and recurse_key : convert = {qwflat:recurse_key} | |||
|
78 | # for production, change warn to 0: | |||
|
79 | data.merge(read_dict(fname,convert,fs=self.field_sep,strip=1, | |||
|
80 | warn=0,no_empty=0,**kw)) | |||
|
81 | # keep track of successfully loaded files | |||
|
82 | self.included.append(fname) | |||
|
83 | if recurse_key in data.keys(): | |||
|
84 | for incfilename in data[recurse_key]: | |||
|
85 | found=0 | |||
|
86 | try: | |||
|
87 | incfile = filefind(incfilename,incpath) | |||
|
88 | except IOError: | |||
|
89 | if os.name in ['nt','dos']: | |||
|
90 | try: | |||
|
91 | # Try again with '.ini' extension | |||
|
92 | incfilename += '.ini' | |||
|
93 | incfile = filefind(incfilename,incpath) | |||
|
94 | except IOError: | |||
|
95 | found = 0 | |||
|
96 | else: | |||
|
97 | found = 1 | |||
|
98 | else: | |||
|
99 | found = 0 | |||
|
100 | else: | |||
|
101 | found = 1 | |||
|
102 | if found: | |||
|
103 | try: | |||
|
104 | data.merge(self.load(incfile,convert,recurse_key, | |||
|
105 | incpath,**kw), | |||
|
106 | self.conflict) | |||
|
107 | except: | |||
|
108 | Xinfo() | |||
|
109 | warn('Problem loading included file: '+ | |||
|
110 | `incfilename` + '. Ignoring it...') | |||
|
111 | else: | |||
|
112 | warn('File `%s` not found. Included by %s' % (incfilename,fname)) | |||
|
113 | ||||
|
114 | return data | |||
|
115 | ||||
|
116 | # end ConfigLoader |
@@ -0,0 +1,228 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """sys.excepthook for IPython itself, leaves a detailed report on disk. | |||
|
3 | ||||
|
4 | $Id: CrashHandler.py 2908 2007-12-30 21:07:46Z vivainio $""" | |||
|
5 | ||||
|
6 | #***************************************************************************** | |||
|
7 | # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu> | |||
|
8 | # | |||
|
9 | # Distributed under the terms of the BSD License. The full license is in | |||
|
10 | # the file COPYING, distributed as part of this software. | |||
|
11 | #***************************************************************************** | |||
|
12 | ||||
|
13 | from IPython import Release | |||
|
14 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |||
|
15 | __license__ = Release.license | |||
|
16 | __version__ = Release.version | |||
|
17 | ||||
|
18 | #**************************************************************************** | |||
|
19 | # Required modules | |||
|
20 | ||||
|
21 | # From the standard library | |||
|
22 | import os | |||
|
23 | import sys | |||
|
24 | from pprint import pprint,pformat | |||
|
25 | ||||
|
26 | # Homebrewed | |||
|
27 | from IPython.Itpl import Itpl,itpl,printpl | |||
|
28 | from IPython.ColorANSI import ColorScheme,ColorSchemeTable # too long names | |||
|
29 | from IPython import ultraTB | |||
|
30 | from IPython.genutils import * | |||
|
31 | ||||
|
32 | #**************************************************************************** | |||
|
33 | class CrashHandler: | |||
|
34 | """Customizable crash handlers for IPython-based systems. | |||
|
35 | ||||
|
36 | Instances of this class provide a __call__ method which can be used as a | |||
|
37 | sys.excepthook, i.e., the __call__ signature is: | |||
|
38 | ||||
|
39 | def __call__(self,etype, evalue, etb) | |||
|
40 | ||||
|
41 | """ | |||
|
42 | ||||
|
43 | def __init__(self,IP,app_name,contact_name,contact_email, | |||
|
44 | bug_tracker,crash_report_fname, | |||
|
45 | show_crash_traceback=True): | |||
|
46 | """New crash handler. | |||
|
47 | ||||
|
48 | Inputs: | |||
|
49 | ||||
|
50 | - IP: a running IPython instance, which will be queried at crash time | |||
|
51 | for internal information. | |||
|
52 | ||||
|
53 | - app_name: a string containing the name of your application. | |||
|
54 | ||||
|
55 | - contact_name: a string with the name of the person to contact. | |||
|
56 | ||||
|
57 | - contact_email: a string with the email address of the contact. | |||
|
58 | ||||
|
59 | - bug_tracker: a string with the URL for your project's bug tracker. | |||
|
60 | ||||
|
61 | - crash_report_fname: a string with the filename for the crash report | |||
|
62 | to be saved in. These reports are left in the ipython user directory | |||
|
63 | as determined by the running IPython instance. | |||
|
64 | ||||
|
65 | Optional inputs: | |||
|
66 | ||||
|
67 | - show_crash_traceback(True): if false, don't print the crash | |||
|
68 | traceback on stderr, only generate the on-disk report | |||
|
69 | ||||
|
70 | ||||
|
71 | Non-argument instance attributes: | |||
|
72 | ||||
|
73 | These instances contain some non-argument attributes which allow for | |||
|
74 | further customization of the crash handler's behavior. Please see the | |||
|
75 | source for further details. | |||
|
76 | """ | |||
|
77 | ||||
|
78 | # apply args into instance | |||
|
79 | self.IP = IP # IPython instance | |||
|
80 | self.app_name = app_name | |||
|
81 | self.contact_name = contact_name | |||
|
82 | self.contact_email = contact_email | |||
|
83 | self.bug_tracker = bug_tracker | |||
|
84 | self.crash_report_fname = crash_report_fname | |||
|
85 | self.show_crash_traceback = show_crash_traceback | |||
|
86 | ||||
|
87 | # Hardcoded defaults, which can be overridden either by subclasses or | |||
|
88 | # at runtime for the instance. | |||
|
89 | ||||
|
90 | # Template for the user message. Subclasses which completely override | |||
|
91 | # this, or user apps, can modify it to suit their tastes. It gets | |||
|
92 | # expanded using itpl, so calls of the kind $self.foo are valid. | |||
|
93 | self.user_message_template = """ | |||
|
94 | Oops, $self.app_name crashed. We do our best to make it stable, but... | |||
|
95 | ||||
|
96 | A crash report was automatically generated with the following information: | |||
|
97 | - A verbatim copy of the crash traceback. | |||
|
98 | - A copy of your input history during this session. | |||
|
99 | - Data on your current $self.app_name configuration. | |||
|
100 | ||||
|
101 | It was left in the file named: | |||
|
102 | \t'$self.crash_report_fname' | |||
|
103 | If you can email this file to the developers, the information in it will help | |||
|
104 | them in understanding and correcting the problem. | |||
|
105 | ||||
|
106 | You can mail it to: $self.contact_name at $self.contact_email | |||
|
107 | with the subject '$self.app_name Crash Report'. | |||
|
108 | ||||
|
109 | If you want to do it now, the following command will work (under Unix): | |||
|
110 | mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname | |||
|
111 | ||||
|
112 | To ensure accurate tracking of this issue, please file a report about it at: | |||
|
113 | $self.bug_tracker | |||
|
114 | """ | |||
|
115 | ||||
|
116 | def __call__(self,etype, evalue, etb): | |||
|
117 | """Handle an exception, call for compatible with sys.excepthook""" | |||
|
118 | ||||
|
119 | # Report tracebacks shouldn't use color in general (safer for users) | |||
|
120 | color_scheme = 'NoColor' | |||
|
121 | ||||
|
122 | # Use this ONLY for developer debugging (keep commented out for release) | |||
|
123 | #color_scheme = 'Linux' # dbg | |||
|
124 | ||||
|
125 | try: | |||
|
126 | rptdir = self.IP.rc.ipythondir | |||
|
127 | except: | |||
|
128 | rptdir = os.getcwd() | |||
|
129 | if not os.path.isdir(rptdir): | |||
|
130 | rptdir = os.getcwd() | |||
|
131 | report_name = os.path.join(rptdir,self.crash_report_fname) | |||
|
132 | # write the report filename into the instance dict so it can get | |||
|
133 | # properly expanded out in the user message template | |||
|
134 | self.crash_report_fname = report_name | |||
|
135 | TBhandler = ultraTB.VerboseTB(color_scheme=color_scheme, | |||
|
136 | long_header=1) | |||
|
137 | traceback = TBhandler.text(etype,evalue,etb,context=31) | |||
|
138 | ||||
|
139 | # print traceback to screen | |||
|
140 | if self.show_crash_traceback: | |||
|
141 | print >> sys.stderr, traceback | |||
|
142 | ||||
|
143 | # and generate a complete report on disk | |||
|
144 | try: | |||
|
145 | report = open(report_name,'w') | |||
|
146 | except: | |||
|
147 | print >> sys.stderr, 'Could not create crash report on disk.' | |||
|
148 | return | |||
|
149 | ||||
|
150 | # Inform user on stderr of what happened | |||
|
151 | msg = itpl('\n'+'*'*70+'\n'+self.user_message_template) | |||
|
152 | print >> sys.stderr, msg | |||
|
153 | ||||
|
154 | # Construct report on disk | |||
|
155 | report.write(self.make_report(traceback)) | |||
|
156 | report.close() | |||
|
157 | raw_input("Press enter to exit:") | |||
|
158 | ||||
|
159 | def make_report(self,traceback): | |||
|
160 | """Return a string containing a crash report.""" | |||
|
161 | ||||
|
162 | sec_sep = '\n\n'+'*'*75+'\n\n' | |||
|
163 | ||||
|
164 | report = [] | |||
|
165 | rpt_add = report.append | |||
|
166 | ||||
|
167 | rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n') | |||
|
168 | rpt_add('IPython version: %s \n\n' % Release.version) | |||
|
169 | rpt_add('SVN revision : %s \n\n' % Release.revision) | |||
|
170 | rpt_add('Platform info : os.name -> %s, sys.platform -> %s' % | |||
|
171 | (os.name,sys.platform) ) | |||
|
172 | rpt_add(sec_sep+'Current user configuration structure:\n\n') | |||
|
173 | rpt_add(pformat(self.IP.rc.dict())) | |||
|
174 | rpt_add(sec_sep+'Crash traceback:\n\n' + traceback) | |||
|
175 | try: | |||
|
176 | rpt_add(sec_sep+"History of session input:") | |||
|
177 | for line in self.IP.user_ns['_ih']: | |||
|
178 | rpt_add(line) | |||
|
179 | rpt_add('\n*** Last line of input (may not be in above history):\n') | |||
|
180 | rpt_add(self.IP._last_input_line+'\n') | |||
|
181 | except: | |||
|
182 | pass | |||
|
183 | ||||
|
184 | return ''.join(report) | |||
|
185 | ||||
|
186 | class IPythonCrashHandler(CrashHandler): | |||
|
187 | """sys.excepthook for IPython itself, leaves a detailed report on disk.""" | |||
|
188 | ||||
|
189 | def __init__(self,IP): | |||
|
190 | ||||
|
191 | # Set here which of the IPython authors should be listed as contact | |||
|
192 | AUTHOR_CONTACT = 'Ville' | |||
|
193 | ||||
|
194 | # Set argument defaults | |||
|
195 | app_name = 'IPython' | |||
|
196 | bug_tracker = 'http://projects.scipy.org/ipython/ipython/report' | |||
|
197 | contact_name,contact_email = Release.authors[AUTHOR_CONTACT][:2] | |||
|
198 | crash_report_fname = 'IPython_crash_report.txt' | |||
|
199 | # Call parent constructor | |||
|
200 | CrashHandler.__init__(self,IP,app_name,contact_name,contact_email, | |||
|
201 | bug_tracker,crash_report_fname) | |||
|
202 | ||||
|
203 | def make_report(self,traceback): | |||
|
204 | """Return a string containing a crash report.""" | |||
|
205 | ||||
|
206 | sec_sep = '\n\n'+'*'*75+'\n\n' | |||
|
207 | ||||
|
208 | report = [] | |||
|
209 | rpt_add = report.append | |||
|
210 | ||||
|
211 | rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n') | |||
|
212 | rpt_add('IPython version: %s \n\n' % Release.version) | |||
|
213 | rpt_add('SVN revision : %s \n\n' % Release.revision) | |||
|
214 | rpt_add('Platform info : os.name -> %s, sys.platform -> %s' % | |||
|
215 | (os.name,sys.platform) ) | |||
|
216 | rpt_add(sec_sep+'Current user configuration structure:\n\n') | |||
|
217 | rpt_add(pformat(self.IP.rc.dict())) | |||
|
218 | rpt_add(sec_sep+'Crash traceback:\n\n' + traceback) | |||
|
219 | try: | |||
|
220 | rpt_add(sec_sep+"History of session input:") | |||
|
221 | for line in self.IP.user_ns['_ih']: | |||
|
222 | rpt_add(line) | |||
|
223 | rpt_add('\n*** Last line of input (may not be in above history):\n') | |||
|
224 | rpt_add(self.IP._last_input_line+'\n') | |||
|
225 | except: | |||
|
226 | pass | |||
|
227 | ||||
|
228 | return ''.join(report) |
This diff has been collapsed as it changes many lines, (693 lines changed) Show them Hide them | |||||
@@ -0,0 +1,693 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """DPyGetOpt -- Demiurge Python GetOptions Module | |||
|
3 | ||||
|
4 | $Id: DPyGetOpt.py 2872 2007-11-25 17:58:05Z fperez $ | |||
|
5 | ||||
|
6 | This module is modeled after perl's Getopt::Long module-- which | |||
|
7 | is, in turn, modeled after GNU's extended getopt() function. | |||
|
8 | ||||
|
9 | Upon instantiation, the option specification should be a sequence | |||
|
10 | (list) of option definitions. | |||
|
11 | ||||
|
12 | Options that take no arguments should simply contain the name of | |||
|
13 | the option. If a ! is post-pended, the option can be negated by | |||
|
14 | prepending 'no'; ie 'debug!' specifies that -debug and -nodebug | |||
|
15 | should be accepted. | |||
|
16 | ||||
|
17 | Mandatory arguments to options are specified using a postpended | |||
|
18 | '=' + a type specifier. '=s' specifies a mandatory string | |||
|
19 | argument, '=i' specifies a mandatory integer argument, and '=f' | |||
|
20 | specifies a mandatory real number. In all cases, the '=' can be | |||
|
21 | substituted with ':' to specify that the argument is optional. | |||
|
22 | ||||
|
23 | Dashes '-' in option names are allowed. | |||
|
24 | ||||
|
25 | If an option has the character '@' postpended (after the | |||
|
26 | argumentation specification), it can appear multiple times within | |||
|
27 | each argument list that is processed. The results will be stored | |||
|
28 | in a list. | |||
|
29 | ||||
|
30 | The option name can actually be a list of names separated by '|' | |||
|
31 | characters; ie-- 'foo|bar|baz=f@' specifies that all -foo, -bar, | |||
|
32 | and -baz options that appear on within the parsed argument list | |||
|
33 | must have a real number argument and that the accumulated list | |||
|
34 | of values will be available under the name 'foo' | |||
|
35 | ||||
|
36 | $Id: DPyGetOpt.py 2872 2007-11-25 17:58:05Z fperez $""" | |||
|
37 | ||||
|
38 | #***************************************************************************** | |||
|
39 | # | |||
|
40 | # Copyright (c) 2001 Bill Bumgarner <bbum@friday.com> | |||
|
41 | # | |||
|
42 | # | |||
|
43 | # Published under the terms of the MIT license, hereby reproduced: | |||
|
44 | # | |||
|
45 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |||
|
46 | # of this software and associated documentation files (the "Software"), to | |||
|
47 | # deal in the Software without restriction, including without limitation the | |||
|
48 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |||
|
49 | # sell copies of the Software, and to permit persons to whom the Software is | |||
|
50 | # furnished to do so, subject to the following conditions: | |||
|
51 | # | |||
|
52 | # The above copyright notice and this permission notice shall be included in | |||
|
53 | # all copies or substantial portions of the Software. | |||
|
54 | # | |||
|
55 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
|
56 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
|
57 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
|
58 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
|
59 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
|
60 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |||
|
61 | # IN THE SOFTWARE. | |||
|
62 | # | |||
|
63 | #***************************************************************************** | |||
|
64 | ||||
|
65 | __author__ = 'Bill Bumgarner <bbum@friday.com>' | |||
|
66 | __license__ = 'MIT' | |||
|
67 | __version__ = '1.2' | |||
|
68 | ||||
|
69 | # Modified to use re instead of regex and regsub modules. | |||
|
70 | # 2001/5/7, Jonathan Hogg <jonathan@onegoodidea.com> | |||
|
71 | ||||
|
72 | import re | |||
|
73 | import string | |||
|
74 | import sys | |||
|
75 | import types | |||
|
76 | ||||
|
77 | class Error(Exception): | |||
|
78 | """Base class for exceptions in the DPyGetOpt module.""" | |||
|
79 | ||||
|
80 | class ArgumentError(Error): | |||
|
81 | """Exception indicating an error in the arguments passed to | |||
|
82 | DPyGetOpt.processArguments.""" | |||
|
83 | ||||
|
84 | class SpecificationError(Error): | |||
|
85 | """Exception indicating an error with an option specification.""" | |||
|
86 | ||||
|
87 | class TerminationError(Error): | |||
|
88 | """Exception indicating an error with an option processing terminator.""" | |||
|
89 | ||||
|
90 | specificationExpr = re.compile('(?P<required>.)(?P<type>.)(?P<multi>@?)') | |||
|
91 | ||||
|
92 | ArgRequired = 'Requires an Argument' | |||
|
93 | ArgOptional = 'Argument Optional' | |||
|
94 | ||||
|
95 | # The types modules is not used for these identifiers because there | |||
|
96 | # is no identifier for 'boolean' or 'generic' | |||
|
97 | StringArgType = 'String Argument Type' | |||
|
98 | IntegerArgType = 'Integer Argument Type' | |||
|
99 | RealArgType = 'Real Argument Type' | |||
|
100 | BooleanArgType = 'Boolean Argument Type' | |||
|
101 | GenericArgType = 'Generic Argument Type' | |||
|
102 | ||||
|
103 | # dictionary of conversion functions-- boolean and generic options | |||
|
104 | # do not accept arguments and do not need conversion functions; | |||
|
105 | # the identity function is used purely for convenience. | |||
|
106 | ConversionFunctions = { | |||
|
107 | StringArgType : lambda x: x, | |||
|
108 | IntegerArgType : string.atoi, | |||
|
109 | RealArgType : string.atof, | |||
|
110 | BooleanArgType : lambda x: x, | |||
|
111 | GenericArgType : lambda x: x, | |||
|
112 | } | |||
|
113 | ||||
|
114 | class DPyGetOpt: | |||
|
115 | ||||
|
116 | def __init__(self, spec = None, terminators = ['--']): | |||
|
117 | """ | |||
|
118 | Declare and intialize instance variables | |||
|
119 | ||||
|
120 | Yes, declaration is not necessary... but one of the things | |||
|
121 | I sorely miss from C/Obj-C is the concept of having an | |||
|
122 | interface definition that clearly declares all instance | |||
|
123 | variables and methods without providing any implementation | |||
|
124 | details. it is a useful reference! | |||
|
125 | ||||
|
126 | all instance variables are initialized to 0/Null/None of | |||
|
127 | the appropriate type-- not even the default value... | |||
|
128 | """ | |||
|
129 | ||||
|
130 | # sys.stderr.write(string.join(spec) + "\n") | |||
|
131 | ||||
|
132 | self.allowAbbreviations = 1 # boolean, 1 if abbreviations will | |||
|
133 | # be expanded | |||
|
134 | self.freeValues = [] # list, contains free values | |||
|
135 | self.ignoreCase = 0 # boolean, YES if ignoring case | |||
|
136 | self.needsParse = 0 # boolean, YES if need to reparse parameter spec | |||
|
137 | self.optionNames = {} # dict, all option names-- value is index of tuple | |||
|
138 | self.optionStartExpr = None # regexp defining the start of an option (ie; '-', '--') | |||
|
139 | self.optionTuples = [] # list o' tuples containing defn of options AND aliases | |||
|
140 | self.optionValues = {} # dict, option names (after alias expansion) -> option value(s) | |||
|
141 | self.orderMixed = 0 # boolean, YES if options can be mixed with args | |||
|
142 | self.posixCompliance = 0 # boolean, YES indicates posix like behaviour | |||
|
143 | self.spec = [] # list, raw specs (in case it must be reparsed) | |||
|
144 | self.terminators = terminators # list, strings that terminate argument processing | |||
|
145 | self.termValues = [] # list, values after terminator | |||
|
146 | self.terminator = None # full name of terminator that ended | |||
|
147 | # option processing | |||
|
148 | ||||
|
149 | # set up defaults | |||
|
150 | self.setPosixCompliance() | |||
|
151 | self.setIgnoreCase() | |||
|
152 | self.setAllowAbbreviations() | |||
|
153 | ||||
|
154 | # parse spec-- if present | |||
|
155 | if spec: | |||
|
156 | self.parseConfiguration(spec) | |||
|
157 | ||||
|
158 | def setPosixCompliance(self, aFlag = 0): | |||
|
159 | """ | |||
|
160 | Enables and disables posix compliance. | |||
|
161 | ||||
|
162 | When enabled, '+' can be used as an option prefix and free | |||
|
163 | values can be mixed with options. | |||
|
164 | """ | |||
|
165 | self.posixCompliance = aFlag | |||
|
166 | self.needsParse = 1 | |||
|
167 | ||||
|
168 | if self.posixCompliance: | |||
|
169 | self.optionStartExpr = re.compile('(--|-)(?P<option>[A-Za-z0-9_-]+)(?P<arg>=.*)?') | |||
|
170 | self.orderMixed = 0 | |||
|
171 | else: | |||
|
172 | self.optionStartExpr = re.compile('(--|-|\+)(?P<option>[A-Za-z0-9_-]+)(?P<arg>=.*)?') | |||
|
173 | self.orderMixed = 1 | |||
|
174 | ||||
|
175 | def isPosixCompliant(self): | |||
|
176 | """ | |||
|
177 | Returns the value of the posix compliance flag. | |||
|
178 | """ | |||
|
179 | return self.posixCompliance | |||
|
180 | ||||
|
181 | def setIgnoreCase(self, aFlag = 1): | |||
|
182 | """ | |||
|
183 | Enables and disables ignoring case during option processing. | |||
|
184 | """ | |||
|
185 | self.needsParse = 1 | |||
|
186 | self.ignoreCase = aFlag | |||
|
187 | ||||
|
188 | def ignoreCase(self): | |||
|
189 | """ | |||
|
190 | Returns 1 if the option processor will ignore case when | |||
|
191 | processing options. | |||
|
192 | """ | |||
|
193 | return self.ignoreCase | |||
|
194 | ||||
|
195 | def setAllowAbbreviations(self, aFlag = 1): | |||
|
196 | """ | |||
|
197 | Enables and disables the expansion of abbreviations during | |||
|
198 | option processing. | |||
|
199 | """ | |||
|
200 | self.allowAbbreviations = aFlag | |||
|
201 | ||||
|
202 | def willAllowAbbreviations(self): | |||
|
203 | """ | |||
|
204 | Returns 1 if abbreviated options will be automatically | |||
|
205 | expanded to the non-abbreviated form (instead of causing an | |||
|
206 | unrecognized option error). | |||
|
207 | """ | |||
|
208 | return self.allowAbbreviations | |||
|
209 | ||||
|
210 | def addTerminator(self, newTerm): | |||
|
211 | """ | |||
|
212 | Adds newTerm as terminator of option processing. | |||
|
213 | ||||
|
214 | Whenever the option processor encounters one of the terminators | |||
|
215 | during option processing, the processing of options terminates | |||
|
216 | immediately, all remaining options are stored in the termValues | |||
|
217 | instance variable and the full name of the terminator is stored | |||
|
218 | in the terminator instance variable. | |||
|
219 | """ | |||
|
220 | self.terminators = self.terminators + [newTerm] | |||
|
221 | ||||
|
222 | def _addOption(self, oTuple): | |||
|
223 | """ | |||
|
224 | Adds the option described by oTuple (name, (type, mode, | |||
|
225 | default), alias) to optionTuples. Adds index keyed under name | |||
|
226 | to optionNames. Raises SpecificationError if name already in | |||
|
227 | optionNames | |||
|
228 | """ | |||
|
229 | (name, (type, mode, default, multi), realName) = oTuple | |||
|
230 | ||||
|
231 | # verify name and add to option names dictionary | |||
|
232 | if self.optionNames.has_key(name): | |||
|
233 | if realName: | |||
|
234 | raise SpecificationError('Alias \'' + name + '\' for \'' + | |||
|
235 | realName + | |||
|
236 | '\' already used for another option or alias.') | |||
|
237 | else: | |||
|
238 | raise SpecificationError('Option named \'' + name + | |||
|
239 | '\' specified more than once. Specification: ' | |||
|
240 | + option) | |||
|
241 | ||||
|
242 | # validated. add to optionNames | |||
|
243 | self.optionNames[name] = self.tupleIndex | |||
|
244 | self.tupleIndex = self.tupleIndex + 1 | |||
|
245 | ||||
|
246 | # add to optionTuples | |||
|
247 | self.optionTuples = self.optionTuples + [oTuple] | |||
|
248 | ||||
|
249 | # if type is boolean, add negation | |||
|
250 | if type == BooleanArgType: | |||
|
251 | alias = 'no' + name | |||
|
252 | specTuple = (type, mode, 0, multi) | |||
|
253 | oTuple = (alias, specTuple, name) | |||
|
254 | ||||
|
255 | # verify name and add to option names dictionary | |||
|
256 | if self.optionNames.has_key(alias): | |||
|
257 | if realName: | |||
|
258 | raise SpecificationError('Negated alias \'' + name + | |||
|
259 | '\' for \'' + realName + | |||
|
260 | '\' already used for another option or alias.') | |||
|
261 | else: | |||
|
262 | raise SpecificationError('Negated option named \'' + name + | |||
|
263 | '\' specified more than once. Specification: ' | |||
|
264 | + option) | |||
|
265 | ||||
|
266 | # validated. add to optionNames | |||
|
267 | self.optionNames[alias] = self.tupleIndex | |||
|
268 | self.tupleIndex = self.tupleIndex + 1 | |||
|
269 | ||||
|
270 | # add to optionTuples | |||
|
271 | self.optionTuples = self.optionTuples + [oTuple] | |||
|
272 | ||||
|
273 | def addOptionConfigurationTuple(self, oTuple): | |||
|
274 | (name, argSpec, realName) = oTuple | |||
|
275 | if self.ignoreCase: | |||
|
276 | name = string.lower(name) | |||
|
277 | if realName: | |||
|
278 | realName = string.lower(realName) | |||
|
279 | else: | |||
|
280 | realName = name | |||
|
281 | ||||
|
282 | oTuple = (name, argSpec, realName) | |||
|
283 | ||||
|
284 | # add option | |||
|
285 | self._addOption(oTuple) | |||
|
286 | ||||
|
287 | def addOptionConfigurationTuples(self, oTuple): | |||
|
288 | if type(oTuple) is ListType: | |||
|
289 | for t in oTuple: | |||
|
290 | self.addOptionConfigurationTuple(t) | |||
|
291 | else: | |||
|
292 | self.addOptionConfigurationTuple(oTuple) | |||
|
293 | ||||
|
294 | def parseConfiguration(self, spec): | |||
|
295 | # destroy previous stored information + store raw spec | |||
|
296 | self.spec = spec | |||
|
297 | self.optionTuples = [] | |||
|
298 | self.optionNames = {} | |||
|
299 | self.tupleIndex = 0 | |||
|
300 | ||||
|
301 | tupleIndex = 0 | |||
|
302 | ||||
|
303 | # create some regex's for parsing each spec | |||
|
304 | splitExpr = \ | |||
|
305 | re.compile('(?P<names>\w+[-A-Za-z0-9|]*)?(?P<spec>!|[=:][infs]@?)?') | |||
|
306 | for option in spec: | |||
|
307 | # push to lower case (does not negatively affect | |||
|
308 | # specification) | |||
|
309 | if self.ignoreCase: | |||
|
310 | option = string.lower(option) | |||
|
311 | ||||
|
312 | # break into names, specification | |||
|
313 | match = splitExpr.match(option) | |||
|
314 | if match is None: | |||
|
315 | raise SpecificationError('Invalid specification {' + option + | |||
|
316 | '}') | |||
|
317 | ||||
|
318 | names = match.group('names') | |||
|
319 | specification = match.group('spec') | |||
|
320 | ||||
|
321 | # break name into name, aliases | |||
|
322 | nlist = string.split(names, '|') | |||
|
323 | ||||
|
324 | # get name | |||
|
325 | name = nlist[0] | |||
|
326 | aliases = nlist[1:] | |||
|
327 | ||||
|
328 | # specificationExpr = regex.symcomp('\(<required>.\)\(<type>.\)\(<multi>@?\)') | |||
|
329 | if not specification: | |||
|
330 | #spec tuple is ('type', 'arg mode', 'default value', 'multiple') | |||
|
331 | argType = GenericArgType | |||
|
332 | argMode = None | |||
|
333 | argDefault = 1 | |||
|
334 | argMultiple = 0 | |||
|
335 | elif specification == '!': | |||
|
336 | argType = BooleanArgType | |||
|
337 | argMode = None | |||
|
338 | argDefault = 1 | |||
|
339 | argMultiple = 0 | |||
|
340 | else: | |||
|
341 | # parse | |||
|
342 | match = specificationExpr.match(specification) | |||
|
343 | if match is None: | |||
|
344 | # failed to parse, die | |||
|
345 | raise SpecificationError('Invalid configuration for option \'' | |||
|
346 | + option + '\'') | |||
|
347 | ||||
|
348 | # determine mode | |||
|
349 | required = match.group('required') | |||
|
350 | if required == '=': | |||
|
351 | argMode = ArgRequired | |||
|
352 | elif required == ':': | |||
|
353 | argMode = ArgOptional | |||
|
354 | else: | |||
|
355 | raise SpecificationError('Unknown requirement configuration \'' | |||
|
356 | + required + '\'') | |||
|
357 | ||||
|
358 | # determine type | |||
|
359 | type = match.group('type') | |||
|
360 | if type == 's': | |||
|
361 | argType = StringArgType | |||
|
362 | argDefault = '' | |||
|
363 | elif type == 'i': | |||
|
364 | argType = IntegerArgType | |||
|
365 | argDefault = 1 | |||
|
366 | elif type == 'f' or type == 'n': | |||
|
367 | argType = RealArgType | |||
|
368 | argDefault = 1 | |||
|
369 | else: | |||
|
370 | raise SpecificationError('Unknown type specifier \'' + | |||
|
371 | type + '\'') | |||
|
372 | ||||
|
373 | # determine quantity | |||
|
374 | if match.group('multi') == '@': | |||
|
375 | argMultiple = 1 | |||
|
376 | else: | |||
|
377 | argMultiple = 0 | |||
|
378 | ## end else (of not specification) | |||
|
379 | ||||
|
380 | # construct specification tuple | |||
|
381 | specTuple = (argType, argMode, argDefault, argMultiple) | |||
|
382 | ||||
|
383 | # add the option-- option tuple is (name, specTuple, real name) | |||
|
384 | oTuple = (name, specTuple, name) | |||
|
385 | self._addOption(oTuple) | |||
|
386 | ||||
|
387 | for alias in aliases: | |||
|
388 | # drop to all lower (if configured to do so) | |||
|
389 | if self.ignoreCase: | |||
|
390 | alias = string.lower(alias) | |||
|
391 | # create configuration tuple | |||
|
392 | oTuple = (alias, specTuple, name) | |||
|
393 | # add | |||
|
394 | self._addOption(oTuple) | |||
|
395 | ||||
|
396 | # successfully parsed.... | |||
|
397 | self.needsParse = 0 | |||
|
398 | ||||
|
399 | def _getArgTuple(self, argName): | |||
|
400 | """ | |||
|
401 | Returns a list containing all the specification tuples that | |||
|
402 | match argName. If none match, None is returned. If one | |||
|
403 | matches, a list with one tuple is returned. If more than one | |||
|
404 | match, a list containing all the tuples that matched is | |||
|
405 | returned. | |||
|
406 | ||||
|
407 | In other words, this function does not pass judgement upon the | |||
|
408 | validity of multiple matches. | |||
|
409 | """ | |||
|
410 | # is it in the optionNames dict? | |||
|
411 | ||||
|
412 | try: | |||
|
413 | # sys.stderr.write(argName + string.join(self.optionNames.keys()) + "\n") | |||
|
414 | ||||
|
415 | # yes, get index | |||
|
416 | tupleIndex = self.optionNames[argName] | |||
|
417 | # and return tuple as element of list | |||
|
418 | return [self.optionTuples[tupleIndex]] | |||
|
419 | except KeyError: | |||
|
420 | # are abbreviations allowed? | |||
|
421 | if not self.allowAbbreviations: | |||
|
422 | # No! terefore, this cannot be valid argument-- nothing found | |||
|
423 | return None | |||
|
424 | ||||
|
425 | # argName might be an abbreviation (and, abbreviations must | |||
|
426 | # be allowed... or this would not have been reached!) | |||
|
427 | ||||
|
428 | # create regex for argName | |||
|
429 | argExpr = re.compile('^' + argName) | |||
|
430 | ||||
|
431 | tuples = filter(lambda x, argExpr=argExpr: argExpr.search(x[0]) is not None, | |||
|
432 | self.optionTuples) | |||
|
433 | ||||
|
434 | if not len(tuples): | |||
|
435 | return None | |||
|
436 | else: | |||
|
437 | return tuples | |||
|
438 | ||||
|
439 | def _isTerminator(self, optionName): | |||
|
440 | """ | |||
|
441 | Returns the full name of the terminator if optionName is a valid | |||
|
442 | terminator. If it is, sets self.terminator to the full name of | |||
|
443 | the terminator. | |||
|
444 | ||||
|
445 | If more than one terminator matched, raises a TerminationError with a | |||
|
446 | string describing the ambiguity. | |||
|
447 | """ | |||
|
448 | ||||
|
449 | # sys.stderr.write(optionName + "\n") | |||
|
450 | # sys.stderr.write(repr(self.terminators)) | |||
|
451 | ||||
|
452 | if optionName in self.terminators: | |||
|
453 | self.terminator = optionName | |||
|
454 | elif not self.allowAbbreviations: | |||
|
455 | return None | |||
|
456 | ||||
|
457 | # regex thing in bogus | |||
|
458 | # termExpr = regex.compile('^' + optionName) | |||
|
459 | ||||
|
460 | terms = filter(lambda x, on=optionName: string.find(x,on) == 0, self.terminators) | |||
|
461 | ||||
|
462 | if not len(terms): | |||
|
463 | return None | |||
|
464 | elif len(terms) > 1: | |||
|
465 | raise TerminationError('Ambiguous terminator \'' + optionName + | |||
|
466 | '\' matches ' + repr(terms)) | |||
|
467 | ||||
|
468 | self.terminator = terms[0] | |||
|
469 | return self.terminator | |||
|
470 | ||||
|
471 | def processArguments(self, args = None): | |||
|
472 | """ | |||
|
473 | Processes args, a list of arguments (including options). | |||
|
474 | ||||
|
475 | If args is the same as sys.argv, automatically trims the first | |||
|
476 | argument (the executable name/path). | |||
|
477 | ||||
|
478 | If an exception is not raised, the argument list was parsed | |||
|
479 | correctly. | |||
|
480 | ||||
|
481 | Upon successful completion, the freeValues instance variable | |||
|
482 | will contain all the arguments that were not associated with an | |||
|
483 | option in the order they were encountered. optionValues is a | |||
|
484 | dictionary containing the value of each option-- the method | |||
|
485 | valueForOption() can be used to query this dictionary. | |||
|
486 | terminator will contain the argument encountered that terminated | |||
|
487 | option processing (or None, if a terminator was never | |||
|
488 | encountered) and termValues will contain all of the options that | |||
|
489 | appeared after the Terminator (or an empty list). | |||
|
490 | """ | |||
|
491 | ||||
|
492 | if hasattr(sys, "argv") and args == sys.argv: | |||
|
493 | args = sys.argv[1:] | |||
|
494 | ||||
|
495 | max = len(args) # maximum index + 1 | |||
|
496 | self.freeValues = [] # array to hold return values | |||
|
497 | self.optionValues= {} | |||
|
498 | index = 0 # initial index | |||
|
499 | self.terminator = None | |||
|
500 | self.termValues = [] | |||
|
501 | ||||
|
502 | while index < max: | |||
|
503 | # obtain argument | |||
|
504 | arg = args[index] | |||
|
505 | # increment index -- REMEMBER; it is NOW incremented | |||
|
506 | index = index + 1 | |||
|
507 | ||||
|
508 | # terminate immediately if option terminator encountered | |||
|
509 | if self._isTerminator(arg): | |||
|
510 | self.freeValues = self.freeValues + args[index:] | |||
|
511 | self.termValues = args[index:] | |||
|
512 | return | |||
|
513 | ||||
|
514 | # is this possibly an option? | |||
|
515 | match = self.optionStartExpr.match(arg) | |||
|
516 | if match is None: | |||
|
517 | # not an option-- add to freeValues | |||
|
518 | self.freeValues = self.freeValues + [arg] | |||
|
519 | if not self.orderMixed: | |||
|
520 | # mixing not allowed; add rest of args as freeValues | |||
|
521 | self.freeValues = self.freeValues + args[index:] | |||
|
522 | # return to caller | |||
|
523 | return | |||
|
524 | else: | |||
|
525 | continue | |||
|
526 | ||||
|
527 | # grab name | |||
|
528 | optName = match.group('option') | |||
|
529 | ||||
|
530 | # obtain next argument-- index has already been incremented | |||
|
531 | nextArg = match.group('arg') | |||
|
532 | if nextArg: | |||
|
533 | nextArg = nextArg[1:] | |||
|
534 | index = index - 1 # put it back | |||
|
535 | else: | |||
|
536 | try: | |||
|
537 | nextArg = args[index] | |||
|
538 | except: | |||
|
539 | nextArg = None | |||
|
540 | ||||
|
541 | # transpose to lower case, if necessary | |||
|
542 | if self.ignoreCase: | |||
|
543 | optName = string.lower(optName) | |||
|
544 | ||||
|
545 | # obtain defining tuple | |||
|
546 | tuples = self._getArgTuple(optName) | |||
|
547 | ||||
|
548 | if tuples == None: | |||
|
549 | raise ArgumentError('Illegal option \'' + arg + '\'') | |||
|
550 | elif len(tuples) > 1: | |||
|
551 | raise ArgumentError('Ambiguous option \'' + arg + | |||
|
552 | '\'; matches ' + | |||
|
553 | repr(map(lambda x: x[0], tuples))) | |||
|
554 | else: | |||
|
555 | config = tuples[0] | |||
|
556 | ||||
|
557 | # config is now set to the configuration tuple for the | |||
|
558 | # argument | |||
|
559 | (fullName, spec, realName) = config | |||
|
560 | (optType, optMode, optDefault, optMultiple) = spec | |||
|
561 | ||||
|
562 | # if opt mode required, but nextArg is none, raise an error | |||
|
563 | if (optMode == ArgRequired): | |||
|
564 | if (not nextArg) or self._isTerminator(nextArg): | |||
|
565 | # print nextArg | |||
|
566 | raise ArgumentError('Option \'' + arg + | |||
|
567 | '\' requires an argument of type ' + | |||
|
568 | optType) | |||
|
569 | ||||
|
570 | if (not optMode == None) and nextArg and (not self._isTerminator(nextArg)): | |||
|
571 | # nextArg defined, option configured to possibly consume arg | |||
|
572 | try: | |||
|
573 | # grab conversion function-- the try is more for internal diagnostics | |||
|
574 | func = ConversionFunctions[optType] | |||
|
575 | try: | |||
|
576 | optionValue = func(nextArg) | |||
|
577 | index = index + 1 | |||
|
578 | except: | |||
|
579 | # only raise conversion error if REQUIRED to consume argument | |||
|
580 | if optMode == ArgRequired: | |||
|
581 | raise ArgumentError('Invalid argument to option \'' | |||
|
582 | + arg + '\'; should be \'' + | |||
|
583 | optType + '\'') | |||
|
584 | else: | |||
|
585 | optionValue = optDefault | |||
|
586 | except ArgumentError: | |||
|
587 | raise | |||
|
588 | except: | |||
|
589 | raise ArgumentError('(' + arg + | |||
|
590 | ') Conversion function for \'' + | |||
|
591 | optType + '\' not found.') | |||
|
592 | else: | |||
|
593 | optionValue = optDefault | |||
|
594 | ||||
|
595 | # add value to options dictionary | |||
|
596 | if optMultiple: | |||
|
597 | # can be multiple values | |||
|
598 | try: | |||
|
599 | # try to append element | |||
|
600 | self.optionValues[realName] = self.optionValues[realName] + [optionValue] | |||
|
601 | except: | |||
|
602 | # failed-- must not exist; add it | |||
|
603 | self.optionValues[realName] = [optionValue] | |||
|
604 | else: | |||
|
605 | # only one value per | |||
|
606 | if self.isPosixCompliant and self.optionValues.has_key(realName): | |||
|
607 | raise ArgumentError('Argument \'' + arg + | |||
|
608 | '\' occurs multiple times.') | |||
|
609 | ||||
|
610 | self.optionValues[realName] = optionValue | |||
|
611 | ||||
|
612 | def valueForOption(self, optionName, defaultValue = None): | |||
|
613 | """ | |||
|
614 | Return the value associated with optionName. If optionName was | |||
|
615 | not encountered during parsing of the arguments, returns the | |||
|
616 | defaultValue (which defaults to None). | |||
|
617 | """ | |||
|
618 | try: | |||
|
619 | optionValue = self.optionValues[optionName] | |||
|
620 | except: | |||
|
621 | optionValue = defaultValue | |||
|
622 | ||||
|
623 | return optionValue | |||
|
624 | ||||
|
625 | ## | |||
|
626 | ## test/example section | |||
|
627 | ## | |||
|
628 | test_error = 'Test Run Amok!' | |||
|
629 | def _test(): | |||
|
630 | """ | |||
|
631 | A relatively complete test suite. | |||
|
632 | """ | |||
|
633 | try: | |||
|
634 | DPyGetOpt(['foo', 'bar=s', 'foo']) | |||
|
635 | except Error, exc: | |||
|
636 | print 'EXCEPTION (should be \'foo\' already used..): %s' % exc | |||
|
637 | ||||
|
638 | try: | |||
|
639 | DPyGetOpt(['foo|bar|apple=s@', 'baz|apple!']) | |||
|
640 | except Error, exc: | |||
|
641 | print 'EXCEPTION (should be duplicate alias/name error): %s' % exc | |||
|
642 | ||||
|
643 | x = DPyGetOpt(['apple|atlas=i@', 'application|executable=f@']) | |||
|
644 | try: | |||
|
645 | x.processArguments(['-app', '29.3']) | |||
|
646 | except Error, exc: | |||
|
647 | print 'EXCEPTION (should be ambiguous argument): %s' % exc | |||
|
648 | ||||
|
649 | x = DPyGetOpt(['foo'], ['antigravity', 'antithesis']) | |||
|
650 | try: | |||
|
651 | x.processArguments(['-foo', 'anti']) | |||
|
652 | except Error, exc: | |||
|
653 | print 'EXCEPTION (should be ambiguous terminator): %s' % exc | |||
|
654 | ||||
|
655 | profile = ['plain-option', | |||
|
656 | 'boolean-option!', | |||
|
657 | 'list-of-integers=i@', | |||
|
658 | 'list-real-option|list-real-alias|list-real-pseudonym=f@', | |||
|
659 | 'optional-string-option:s', | |||
|
660 | 'abbreviated-string-list=s@'] | |||
|
661 | ||||
|
662 | terminators = ['terminator'] | |||
|
663 | ||||
|
664 | args = ['-plain-option', | |||
|
665 | '+noboolean-option', | |||
|
666 | '--list-of-integers', '1', | |||
|
667 | '+list-of-integers', '2', | |||
|
668 | '-list-of-integers', '3', | |||
|
669 | 'freeargone', | |||
|
670 | '-list-real-option', '1.1', | |||
|
671 | '+list-real-alias', '1.2', | |||
|
672 | '--list-real-pseudonym', '1.3', | |||
|
673 | 'freeargtwo', | |||
|
674 | '-abbreviated-string-list', 'String1', | |||
|
675 | '--abbreviated-s', 'String2', | |||
|
676 | '-abbrev', 'String3', | |||
|
677 | '-a', 'String4', | |||
|
678 | '-optional-string-option', | |||
|
679 | 'term', | |||
|
680 | 'next option should look like an invalid arg', | |||
|
681 | '-a'] | |||
|
682 | ||||
|
683 | ||||
|
684 | print 'Using profile: ' + repr(profile) | |||
|
685 | print 'With terminator: ' + repr(terminators) | |||
|
686 | print 'Processing arguments: ' + repr(args) | |||
|
687 | ||||
|
688 | go = DPyGetOpt(profile, terminators) | |||
|
689 | go.processArguments(args) | |||
|
690 | ||||
|
691 | print 'Options (and values): ' + repr(go.optionValues) | |||
|
692 | print 'free args: ' + repr(go.freeValues) | |||
|
693 | print 'term args: ' + repr(go.termValues) |
This diff has been collapsed as it changes many lines, (517 lines changed) Show them Hide them | |||||
@@ -0,0 +1,517 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ | |||
|
3 | Pdb debugger class. | |||
|
4 | ||||
|
5 | Modified from the standard pdb.Pdb class to avoid including readline, so that | |||
|
6 | the command line completion of other programs which include this isn't | |||
|
7 | damaged. | |||
|
8 | ||||
|
9 | In the future, this class will be expanded with improvements over the standard | |||
|
10 | pdb. | |||
|
11 | ||||
|
12 | The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor | |||
|
13 | changes. Licensing should therefore be under the standard Python terms. For | |||
|
14 | details on the PSF (Python Software Foundation) standard license, see: | |||
|
15 | ||||
|
16 | http://www.python.org/2.2.3/license.html | |||
|
17 | ||||
|
18 | $Id: Debugger.py 2913 2007-12-31 12:42:14Z vivainio $""" | |||
|
19 | ||||
|
20 | #***************************************************************************** | |||
|
21 | # | |||
|
22 | # This file is licensed under the PSF license. | |||
|
23 | # | |||
|
24 | # Copyright (C) 2001 Python Software Foundation, www.python.org | |||
|
25 | # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu> | |||
|
26 | # | |||
|
27 | # | |||
|
28 | #***************************************************************************** | |||
|
29 | ||||
|
30 | from IPython import Release | |||
|
31 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |||
|
32 | __license__ = 'Python' | |||
|
33 | ||||
|
34 | import bdb | |||
|
35 | import cmd | |||
|
36 | import linecache | |||
|
37 | import os | |||
|
38 | import sys | |||
|
39 | ||||
|
40 | from IPython import PyColorize, ColorANSI, ipapi | |||
|
41 | from IPython.genutils import Term | |||
|
42 | from IPython.excolors import ExceptionColors | |||
|
43 | ||||
|
44 | # See if we can use pydb. | |||
|
45 | has_pydb = False | |||
|
46 | prompt = 'ipdb> ' | |||
|
47 | #We have to check this directly from sys.argv, config struct not yet available | |||
|
48 | if '-pydb' in sys.argv: | |||
|
49 | try: | |||
|
50 | import pydb | |||
|
51 | if hasattr(pydb.pydb, "runl") and pydb.version>'1.17': | |||
|
52 | # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we | |||
|
53 | # better protect against it. | |||
|
54 | has_pydb = True | |||
|
55 | except ImportError: | |||
|
56 | print "Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available" | |||
|
57 | ||||
|
58 | if has_pydb: | |||
|
59 | from pydb import Pdb as OldPdb | |||
|
60 | #print "Using pydb for %run -d and post-mortem" #dbg | |||
|
61 | prompt = 'ipydb> ' | |||
|
62 | else: | |||
|
63 | from pdb import Pdb as OldPdb | |||
|
64 | ||||
|
65 | # Allow the set_trace code to operate outside of an ipython instance, even if | |||
|
66 | # it does so with some limitations. The rest of this support is implemented in | |||
|
67 | # the Tracer constructor. | |||
|
68 | def BdbQuit_excepthook(et,ev,tb): | |||
|
69 | if et==bdb.BdbQuit: | |||
|
70 | print 'Exiting Debugger.' | |||
|
71 | else: | |||
|
72 | BdbQuit_excepthook.excepthook_ori(et,ev,tb) | |||
|
73 | ||||
|
74 | def BdbQuit_IPython_excepthook(self,et,ev,tb): | |||
|
75 | print 'Exiting Debugger.' | |||
|
76 | ||||
|
77 | class Tracer(object): | |||
|
78 | """Class for local debugging, similar to pdb.set_trace. | |||
|
79 | ||||
|
80 | Instances of this class, when called, behave like pdb.set_trace, but | |||
|
81 | providing IPython's enhanced capabilities. | |||
|
82 | ||||
|
83 | This is implemented as a class which must be initialized in your own code | |||
|
84 | and not as a standalone function because we need to detect at runtime | |||
|
85 | whether IPython is already active or not. That detection is done in the | |||
|
86 | constructor, ensuring that this code plays nicely with a running IPython, | |||
|
87 | while functioning acceptably (though with limitations) if outside of it. | |||
|
88 | """ | |||
|
89 | ||||
|
90 | def __init__(self,colors=None): | |||
|
91 | """Create a local debugger instance. | |||
|
92 | ||||
|
93 | :Parameters: | |||
|
94 | ||||
|
95 | - `colors` (None): a string containing the name of the color scheme to | |||
|
96 | use, it must be one of IPython's valid color schemes. If not given, the | |||
|
97 | function will default to the current IPython scheme when running inside | |||
|
98 | IPython, and to 'NoColor' otherwise. | |||
|
99 | ||||
|
100 | Usage example: | |||
|
101 | ||||
|
102 | from IPython.Debugger import Tracer; debug_here = Tracer() | |||
|
103 | ||||
|
104 | ... later in your code | |||
|
105 | debug_here() # -> will open up the debugger at that point. | |||
|
106 | ||||
|
107 | Once the debugger activates, you can use all of its regular commands to | |||
|
108 | step through code, set breakpoints, etc. See the pdb documentation | |||
|
109 | from the Python standard library for usage details. | |||
|
110 | """ | |||
|
111 | ||||
|
112 | global __IPYTHON__ | |||
|
113 | try: | |||
|
114 | __IPYTHON__ | |||
|
115 | except NameError: | |||
|
116 | # Outside of ipython, we set our own exception hook manually | |||
|
117 | __IPYTHON__ = ipapi.get(True,False) | |||
|
118 | BdbQuit_excepthook.excepthook_ori = sys.excepthook | |||
|
119 | sys.excepthook = BdbQuit_excepthook | |||
|
120 | def_colors = 'NoColor' | |||
|
121 | try: | |||
|
122 | # Limited tab completion support | |||
|
123 | import rlcompleter,readline | |||
|
124 | readline.parse_and_bind('tab: complete') | |||
|
125 | except ImportError: | |||
|
126 | pass | |||
|
127 | else: | |||
|
128 | # In ipython, we use its custom exception handler mechanism | |||
|
129 | ip = ipapi.get() | |||
|
130 | def_colors = ip.options.colors | |||
|
131 | ip.set_custom_exc((bdb.BdbQuit,),BdbQuit_IPython_excepthook) | |||
|
132 | ||||
|
133 | if colors is None: | |||
|
134 | colors = def_colors | |||
|
135 | self.debugger = Pdb(colors) | |||
|
136 | ||||
|
137 | def __call__(self): | |||
|
138 | """Starts an interactive debugger at the point where called. | |||
|
139 | ||||
|
140 | This is similar to the pdb.set_trace() function from the std lib, but | |||
|
141 | using IPython's enhanced debugger.""" | |||
|
142 | ||||
|
143 | self.debugger.set_trace(sys._getframe().f_back) | |||
|
144 | ||||
|
145 | def decorate_fn_with_doc(new_fn, old_fn, additional_text=""): | |||
|
146 | """Make new_fn have old_fn's doc string. This is particularly useful | |||
|
147 | for the do_... commands that hook into the help system. | |||
|
148 | Adapted from from a comp.lang.python posting | |||
|
149 | by Duncan Booth.""" | |||
|
150 | def wrapper(*args, **kw): | |||
|
151 | return new_fn(*args, **kw) | |||
|
152 | if old_fn.__doc__: | |||
|
153 | wrapper.__doc__ = old_fn.__doc__ + additional_text | |||
|
154 | return wrapper | |||
|
155 | ||||
|
156 | def _file_lines(fname): | |||
|
157 | """Return the contents of a named file as a list of lines. | |||
|
158 | ||||
|
159 | This function never raises an IOError exception: if the file can't be | |||
|
160 | read, it simply returns an empty list.""" | |||
|
161 | ||||
|
162 | try: | |||
|
163 | outfile = open(fname) | |||
|
164 | except IOError: | |||
|
165 | return [] | |||
|
166 | else: | |||
|
167 | out = outfile.readlines() | |||
|
168 | outfile.close() | |||
|
169 | return out | |||
|
170 | ||||
|
171 | class Pdb(OldPdb): | |||
|
172 | """Modified Pdb class, does not load readline.""" | |||
|
173 | ||||
|
174 | if sys.version[:3] >= '2.5' or has_pydb: | |||
|
175 | def __init__(self,color_scheme='NoColor',completekey=None, | |||
|
176 | stdin=None, stdout=None): | |||
|
177 | ||||
|
178 | # Parent constructor: | |||
|
179 | if has_pydb and completekey is None: | |||
|
180 | OldPdb.__init__(self,stdin=stdin,stdout=Term.cout) | |||
|
181 | else: | |||
|
182 | OldPdb.__init__(self,completekey,stdin,stdout) | |||
|
183 | ||||
|
184 | self.prompt = prompt # The default prompt is '(Pdb)' | |||
|
185 | ||||
|
186 | # IPython changes... | |||
|
187 | self.is_pydb = has_pydb | |||
|
188 | ||||
|
189 | if self.is_pydb: | |||
|
190 | ||||
|
191 | # iplib.py's ipalias seems to want pdb's checkline | |||
|
192 | # which located in pydb.fn | |||
|
193 | import pydb.fns | |||
|
194 | self.checkline = lambda filename, lineno: \ | |||
|
195 | pydb.fns.checkline(self, filename, lineno) | |||
|
196 | ||||
|
197 | self.curframe = None | |||
|
198 | self.do_restart = self.new_do_restart | |||
|
199 | ||||
|
200 | self.old_all_completions = __IPYTHON__.Completer.all_completions | |||
|
201 | __IPYTHON__.Completer.all_completions=self.all_completions | |||
|
202 | ||||
|
203 | self.do_list = decorate_fn_with_doc(self.list_command_pydb, | |||
|
204 | OldPdb.do_list) | |||
|
205 | self.do_l = self.do_list | |||
|
206 | self.do_frame = decorate_fn_with_doc(self.new_do_frame, | |||
|
207 | OldPdb.do_frame) | |||
|
208 | ||||
|
209 | self.aliases = {} | |||
|
210 | ||||
|
211 | # Create color table: we copy the default one from the traceback | |||
|
212 | # module and add a few attributes needed for debugging | |||
|
213 | self.color_scheme_table = ExceptionColors.copy() | |||
|
214 | ||||
|
215 | # shorthands | |||
|
216 | C = ColorANSI.TermColors | |||
|
217 | cst = self.color_scheme_table | |||
|
218 | ||||
|
219 | cst['NoColor'].colors.breakpoint_enabled = C.NoColor | |||
|
220 | cst['NoColor'].colors.breakpoint_disabled = C.NoColor | |||
|
221 | ||||
|
222 | cst['Linux'].colors.breakpoint_enabled = C.LightRed | |||
|
223 | cst['Linux'].colors.breakpoint_disabled = C.Red | |||
|
224 | ||||
|
225 | cst['LightBG'].colors.breakpoint_enabled = C.LightRed | |||
|
226 | cst['LightBG'].colors.breakpoint_disabled = C.Red | |||
|
227 | ||||
|
228 | self.set_colors(color_scheme) | |||
|
229 | ||||
|
230 | # Add a python parser so we can syntax highlight source while | |||
|
231 | # debugging. | |||
|
232 | self.parser = PyColorize.Parser() | |||
|
233 | ||||
|
234 | ||||
|
235 | else: | |||
|
236 | # Ugly hack: for Python 2.3-2.4, we can't call the parent constructor, | |||
|
237 | # because it binds readline and breaks tab-completion. This means we | |||
|
238 | # have to COPY the constructor here. | |||
|
239 | def __init__(self,color_scheme='NoColor'): | |||
|
240 | bdb.Bdb.__init__(self) | |||
|
241 | cmd.Cmd.__init__(self,completekey=None) # don't load readline | |||
|
242 | self.prompt = 'ipdb> ' # The default prompt is '(Pdb)' | |||
|
243 | self.aliases = {} | |||
|
244 | ||||
|
245 | # These two lines are part of the py2.4 constructor, let's put them | |||
|
246 | # unconditionally here as they won't cause any problems in 2.3. | |||
|
247 | self.mainpyfile = '' | |||
|
248 | self._wait_for_mainpyfile = 0 | |||
|
249 | ||||
|
250 | # Read $HOME/.pdbrc and ./.pdbrc | |||
|
251 | try: | |||
|
252 | self.rcLines = _file_lines(os.path.join(os.environ['HOME'], | |||
|
253 | ".pdbrc")) | |||
|
254 | except KeyError: | |||
|
255 | self.rcLines = [] | |||
|
256 | self.rcLines.extend(_file_lines(".pdbrc")) | |||
|
257 | ||||
|
258 | # Create color table: we copy the default one from the traceback | |||
|
259 | # module and add a few attributes needed for debugging | |||
|
260 | ExceptionColors.set_active_scheme(color_scheme) | |||
|
261 | self.color_scheme_table = ExceptionColors.copy() | |||
|
262 | ||||
|
263 | # shorthands | |||
|
264 | C = ColorANSI.TermColors | |||
|
265 | cst = self.color_scheme_table | |||
|
266 | ||||
|
267 | cst['NoColor'].colors.breakpoint_enabled = C.NoColor | |||
|
268 | cst['NoColor'].colors.breakpoint_disabled = C.NoColor | |||
|
269 | ||||
|
270 | cst['Linux'].colors.breakpoint_enabled = C.LightRed | |||
|
271 | cst['Linux'].colors.breakpoint_disabled = C.Red | |||
|
272 | ||||
|
273 | cst['LightBG'].colors.breakpoint_enabled = C.LightRed | |||
|
274 | cst['LightBG'].colors.breakpoint_disabled = C.Red | |||
|
275 | ||||
|
276 | self.set_colors(color_scheme) | |||
|
277 | ||||
|
278 | # Add a python parser so we can syntax highlight source while | |||
|
279 | # debugging. | |||
|
280 | self.parser = PyColorize.Parser() | |||
|
281 | ||||
|
282 | def set_colors(self, scheme): | |||
|
283 | """Shorthand access to the color table scheme selector method.""" | |||
|
284 | self.color_scheme_table.set_active_scheme(scheme) | |||
|
285 | ||||
|
286 | def interaction(self, frame, traceback): | |||
|
287 | __IPYTHON__.set_completer_frame(frame) | |||
|
288 | OldPdb.interaction(self, frame, traceback) | |||
|
289 | ||||
|
290 | def new_do_up(self, arg): | |||
|
291 | OldPdb.do_up(self, arg) | |||
|
292 | __IPYTHON__.set_completer_frame(self.curframe) | |||
|
293 | do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up) | |||
|
294 | ||||
|
295 | def new_do_down(self, arg): | |||
|
296 | OldPdb.do_down(self, arg) | |||
|
297 | __IPYTHON__.set_completer_frame(self.curframe) | |||
|
298 | ||||
|
299 | do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down) | |||
|
300 | ||||
|
301 | def new_do_frame(self, arg): | |||
|
302 | OldPdb.do_frame(self, arg) | |||
|
303 | __IPYTHON__.set_completer_frame(self.curframe) | |||
|
304 | ||||
|
305 | def new_do_quit(self, arg): | |||
|
306 | ||||
|
307 | if hasattr(self, 'old_all_completions'): | |||
|
308 | __IPYTHON__.Completer.all_completions=self.old_all_completions | |||
|
309 | ||||
|
310 | ||||
|
311 | return OldPdb.do_quit(self, arg) | |||
|
312 | ||||
|
313 | do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit) | |||
|
314 | ||||
|
315 | def new_do_restart(self, arg): | |||
|
316 | """Restart command. In the context of ipython this is exactly the same | |||
|
317 | thing as 'quit'.""" | |||
|
318 | self.msg("Restart doesn't make sense here. Using 'quit' instead.") | |||
|
319 | return self.do_quit(arg) | |||
|
320 | ||||
|
321 | def postloop(self): | |||
|
322 | __IPYTHON__.set_completer_frame(None) | |||
|
323 | ||||
|
324 | def print_stack_trace(self): | |||
|
325 | try: | |||
|
326 | for frame_lineno in self.stack: | |||
|
327 | self.print_stack_entry(frame_lineno, context = 5) | |||
|
328 | except KeyboardInterrupt: | |||
|
329 | pass | |||
|
330 | ||||
|
331 | def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ', | |||
|
332 | context = 3): | |||
|
333 | #frame, lineno = frame_lineno | |||
|
334 | print >>Term.cout, self.format_stack_entry(frame_lineno, '', context) | |||
|
335 | ||||
|
336 | def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3): | |||
|
337 | import linecache, repr | |||
|
338 | ||||
|
339 | ret = [] | |||
|
340 | ||||
|
341 | Colors = self.color_scheme_table.active_colors | |||
|
342 | ColorsNormal = Colors.Normal | |||
|
343 | tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal) | |||
|
344 | tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal) | |||
|
345 | tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) | |||
|
346 | tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, | |||
|
347 | ColorsNormal) | |||
|
348 | ||||
|
349 | frame, lineno = frame_lineno | |||
|
350 | ||||
|
351 | return_value = '' | |||
|
352 | if '__return__' in frame.f_locals: | |||
|
353 | rv = frame.f_locals['__return__'] | |||
|
354 | #return_value += '->' | |||
|
355 | return_value += repr.repr(rv) + '\n' | |||
|
356 | ret.append(return_value) | |||
|
357 | ||||
|
358 | #s = filename + '(' + `lineno` + ')' | |||
|
359 | filename = self.canonic(frame.f_code.co_filename) | |||
|
360 | link = tpl_link % filename | |||
|
361 | ||||
|
362 | if frame.f_code.co_name: | |||
|
363 | func = frame.f_code.co_name | |||
|
364 | else: | |||
|
365 | func = "<lambda>" | |||
|
366 | ||||
|
367 | call = '' | |||
|
368 | if func != '?': | |||
|
369 | if '__args__' in frame.f_locals: | |||
|
370 | args = repr.repr(frame.f_locals['__args__']) | |||
|
371 | else: | |||
|
372 | args = '()' | |||
|
373 | call = tpl_call % (func, args) | |||
|
374 | ||||
|
375 | # The level info should be generated in the same format pdb uses, to | |||
|
376 | # avoid breaking the pdbtrack functionality of python-mode in *emacs. | |||
|
377 | if frame is self.curframe: | |||
|
378 | ret.append('> ') | |||
|
379 | else: | |||
|
380 | ret.append(' ') | |||
|
381 | ret.append('%s(%s)%s\n' % (link,lineno,call)) | |||
|
382 | ||||
|
383 | start = lineno - 1 - context//2 | |||
|
384 | lines = linecache.getlines(filename) | |||
|
385 | start = max(start, 0) | |||
|
386 | start = min(start, len(lines) - context) | |||
|
387 | lines = lines[start : start + context] | |||
|
388 | ||||
|
389 | for i,line in enumerate(lines): | |||
|
390 | show_arrow = (start + 1 + i == lineno) | |||
|
391 | linetpl = (frame is self.curframe or show_arrow) \ | |||
|
392 | and tpl_line_em \ | |||
|
393 | or tpl_line | |||
|
394 | ret.append(self.__format_line(linetpl, filename, | |||
|
395 | start + 1 + i, line, | |||
|
396 | arrow = show_arrow) ) | |||
|
397 | ||||
|
398 | return ''.join(ret) | |||
|
399 | ||||
|
400 | def __format_line(self, tpl_line, filename, lineno, line, arrow = False): | |||
|
401 | bp_mark = "" | |||
|
402 | bp_mark_color = "" | |||
|
403 | ||||
|
404 | scheme = self.color_scheme_table.active_scheme_name | |||
|
405 | new_line, err = self.parser.format2(line, 'str', scheme) | |||
|
406 | if not err: line = new_line | |||
|
407 | ||||
|
408 | bp = None | |||
|
409 | if lineno in self.get_file_breaks(filename): | |||
|
410 | bps = self.get_breaks(filename, lineno) | |||
|
411 | bp = bps[-1] | |||
|
412 | ||||
|
413 | if bp: | |||
|
414 | Colors = self.color_scheme_table.active_colors | |||
|
415 | bp_mark = str(bp.number) | |||
|
416 | bp_mark_color = Colors.breakpoint_enabled | |||
|
417 | if not bp.enabled: | |||
|
418 | bp_mark_color = Colors.breakpoint_disabled | |||
|
419 | ||||
|
420 | numbers_width = 7 | |||
|
421 | if arrow: | |||
|
422 | # This is the line with the error | |||
|
423 | pad = numbers_width - len(str(lineno)) - len(bp_mark) | |||
|
424 | if pad >= 3: | |||
|
425 | marker = '-'*(pad-3) + '-> ' | |||
|
426 | elif pad == 2: | |||
|
427 | marker = '> ' | |||
|
428 | elif pad == 1: | |||
|
429 | marker = '>' | |||
|
430 | else: | |||
|
431 | marker = '' | |||
|
432 | num = '%s%s' % (marker, str(lineno)) | |||
|
433 | line = tpl_line % (bp_mark_color + bp_mark, num, line) | |||
|
434 | else: | |||
|
435 | num = '%*s' % (numbers_width - len(bp_mark), str(lineno)) | |||
|
436 | line = tpl_line % (bp_mark_color + bp_mark, num, line) | |||
|
437 | ||||
|
438 | return line | |||
|
439 | ||||
|
440 | def list_command_pydb(self, arg): | |||
|
441 | """List command to use if we have a newer pydb installed""" | |||
|
442 | filename, first, last = OldPdb.parse_list_cmd(self, arg) | |||
|
443 | if filename is not None: | |||
|
444 | self.print_list_lines(filename, first, last) | |||
|
445 | ||||
|
446 | def print_list_lines(self, filename, first, last): | |||
|
447 | """The printing (as opposed to the parsing part of a 'list' | |||
|
448 | command.""" | |||
|
449 | try: | |||
|
450 | Colors = self.color_scheme_table.active_colors | |||
|
451 | ColorsNormal = Colors.Normal | |||
|
452 | tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) | |||
|
453 | tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal) | |||
|
454 | src = [] | |||
|
455 | for lineno in range(first, last+1): | |||
|
456 | line = linecache.getline(filename, lineno) | |||
|
457 | if not line: | |||
|
458 | break | |||
|
459 | ||||
|
460 | if lineno == self.curframe.f_lineno: | |||
|
461 | line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True) | |||
|
462 | else: | |||
|
463 | line = self.__format_line(tpl_line, filename, lineno, line, arrow = False) | |||
|
464 | ||||
|
465 | src.append(line) | |||
|
466 | self.lineno = lineno | |||
|
467 | ||||
|
468 | print >>Term.cout, ''.join(src) | |||
|
469 | ||||
|
470 | except KeyboardInterrupt: | |||
|
471 | pass | |||
|
472 | ||||
|
473 | def do_list(self, arg): | |||
|
474 | self.lastcmd = 'list' | |||
|
475 | last = None | |||
|
476 | if arg: | |||
|
477 | try: | |||
|
478 | x = eval(arg, {}, {}) | |||
|
479 | if type(x) == type(()): | |||
|
480 | first, last = x | |||
|
481 | first = int(first) | |||
|
482 | last = int(last) | |||
|
483 | if last < first: | |||
|
484 | # Assume it's a count | |||
|
485 | last = first + last | |||
|
486 | else: | |||
|
487 | first = max(1, int(x) - 5) | |||
|
488 | except: | |||
|
489 | print '*** Error in argument:', `arg` | |||
|
490 | return | |||
|
491 | elif self.lineno is None: | |||
|
492 | first = max(1, self.curframe.f_lineno - 5) | |||
|
493 | else: | |||
|
494 | first = self.lineno + 1 | |||
|
495 | if last is None: | |||
|
496 | last = first + 10 | |||
|
497 | self.print_list_lines(self.curframe.f_code.co_filename, first, last) | |||
|
498 | ||||
|
499 | do_l = do_list | |||
|
500 | ||||
|
501 | def do_pdef(self, arg): | |||
|
502 | """The debugger interface to magic_pdef""" | |||
|
503 | namespaces = [('Locals', self.curframe.f_locals), | |||
|
504 | ('Globals', self.curframe.f_globals)] | |||
|
505 | __IPYTHON__.magic_pdef(arg, namespaces=namespaces) | |||
|
506 | ||||
|
507 | def do_pdoc(self, arg): | |||
|
508 | """The debugger interface to magic_pdoc""" | |||
|
509 | namespaces = [('Locals', self.curframe.f_locals), | |||
|
510 | ('Globals', self.curframe.f_globals)] | |||
|
511 | __IPYTHON__.magic_pdoc(arg, namespaces=namespaces) | |||
|
512 | ||||
|
513 | def do_pinfo(self, arg): | |||
|
514 | """The debugger equivalant of ?obj""" | |||
|
515 | namespaces = [('Locals', self.curframe.f_locals), | |||
|
516 | ('Globals', self.curframe.f_globals)] | |||
|
517 | __IPYTHON__.magic_pinfo("pinfo %s" % arg, namespaces=namespaces) |
@@ -0,0 +1,259 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """Modified input prompt for executing files. | |||
|
3 | ||||
|
4 | We define a special input line filter to allow typing lines which begin with | |||
|
5 | '~', '/' or '.'. If one of those strings is encountered, it is automatically | |||
|
6 | executed. | |||
|
7 | ||||
|
8 | $Id: InterpreterExec.py 2724 2007-09-07 08:05:38Z fperez $""" | |||
|
9 | ||||
|
10 | #***************************************************************************** | |||
|
11 | # Copyright (C) 2004 W.J. van der Laan <gnufnork@hetdigitalegat.nl> | |||
|
12 | # Copyright (C) 2004-2006 Fernando Perez <fperez@colorado.edu> | |||
|
13 | # | |||
|
14 | # Distributed under the terms of the BSD License. The full license is in | |||
|
15 | # the file COPYING, distributed as part of this software. | |||
|
16 | #***************************************************************************** | |||
|
17 | ||||
|
18 | from IPython import Release | |||
|
19 | __author__ = 'W.J. van der Laan <gnufnork@hetdigitalegat.nl>, '\ | |||
|
20 | '%s <%s>' % Release.authors['Fernando'] | |||
|
21 | __license__ = Release.license | |||
|
22 | ||||
|
23 | # TODO: deprecated | |||
|
24 | def prefilter_shell(self,line,continuation): | |||
|
25 | """Alternate prefilter, modified for shell-like functionality. | |||
|
26 | ||||
|
27 | - Execute all lines beginning with '~', '/' or '.' | |||
|
28 | - $var=cmd <=> %sc var=cmd | |||
|
29 | - $$var=cmd <=> %sc -l var=cmd | |||
|
30 | """ | |||
|
31 | ||||
|
32 | if line: | |||
|
33 | l0 = line[0] | |||
|
34 | if l0 in '~/.': | |||
|
35 | return self._prefilter("!%s"%line,continuation) | |||
|
36 | elif l0=='$': | |||
|
37 | lrest = line[1:] | |||
|
38 | if lrest.startswith('$'): | |||
|
39 | # $$var=cmd <=> %sc -l var=cmd | |||
|
40 | return self._prefilter("%ssc -l %s" % (self.ESC_MAGIC,lrest[1:]), | |||
|
41 | continuation) | |||
|
42 | else: | |||
|
43 | # $var=cmd <=> %sc var=cmd | |||
|
44 | return self._prefilter("%ssc %s" % (self.ESC_MAGIC,lrest), | |||
|
45 | continuation) | |||
|
46 | else: | |||
|
47 | return self._prefilter(line,continuation) | |||
|
48 | else: | |||
|
49 | return self._prefilter(line,continuation) | |||
|
50 | ||||
|
51 | # Rebind this to be the new IPython prefilter: | |||
|
52 | from IPython.iplib import InteractiveShell | |||
|
53 | InteractiveShell.prefilter = prefilter_shell | |||
|
54 | # Clean up the namespace. | |||
|
55 | del InteractiveShell,prefilter_shell | |||
|
56 | ||||
|
57 | # Provide pysh and further shell-oriented services | |||
|
58 | import os,sys,shutil | |||
|
59 | from IPython.genutils import system,shell,getoutput,getoutputerror | |||
|
60 | ||||
|
61 | # Short aliases for getting shell output as a string and a list | |||
|
62 | sout = getoutput | |||
|
63 | lout = lambda cmd: getoutput(cmd,split=1) | |||
|
64 | ||||
|
65 | # Empty function, meant as a docstring holder so help(pysh) works. | |||
|
66 | def pysh(): | |||
|
67 | """Pysh is a set of modules and extensions to IPython which make shell-like | |||
|
68 | usage with Python syntax more convenient. Keep in mind that pysh is NOT a | |||
|
69 | full-blown shell, so don't try to make it your /etc/passwd entry! | |||
|
70 | ||||
|
71 | In particular, it has no job control, so if you type Ctrl-Z (under Unix), | |||
|
72 | you'll suspend pysh itself, not the process you just started. | |||
|
73 | ||||
|
74 | Since pysh is really nothing but a customized IPython, you should | |||
|
75 | familiarize yourself with IPython's features. This brief help mainly | |||
|
76 | documents areas in which pysh differs from the normal IPython. | |||
|
77 | ||||
|
78 | ALIASES | |||
|
79 | ------- | |||
|
80 | All of your $PATH has been loaded as IPython aliases, so you should be | |||
|
81 | able to type any normal system command and have it executed. See %alias? | |||
|
82 | and %unalias? for details on the alias facilities. | |||
|
83 | ||||
|
84 | SPECIAL SYNTAX | |||
|
85 | -------------- | |||
|
86 | Any lines which begin with '~', '/' and '.' will be executed as shell | |||
|
87 | commands instead of as Python code. The special escapes below are also | |||
|
88 | recognized. !cmd is valid in single or multi-line input, all others are | |||
|
89 | only valid in single-line input: | |||
|
90 | ||||
|
91 | !cmd - pass 'cmd' directly to the shell | |||
|
92 | !!cmd - execute 'cmd' and return output as a list (split on '\\n') | |||
|
93 | $var=cmd - capture output of cmd into var, as a string | |||
|
94 | $$var=cmd - capture output of cmd into var, as a list (split on '\\n') | |||
|
95 | ||||
|
96 | The $/$$ syntaxes make Python variables from system output, which you can | |||
|
97 | later use for further scripting. The converse is also possible: when | |||
|
98 | executing an alias or calling to the system via !/!!, you can expand any | |||
|
99 | python variable or expression by prepending it with $. Full details of | |||
|
100 | the allowed syntax can be found in Python's PEP 215. | |||
|
101 | ||||
|
102 | A few brief examples will illustrate these: | |||
|
103 | ||||
|
104 | fperez[~/test]|3> !ls *s.py | |||
|
105 | scopes.py strings.py | |||
|
106 | ||||
|
107 | ls is an internal alias, so there's no need to use !: | |||
|
108 | fperez[~/test]|4> ls *s.py | |||
|
109 | scopes.py* strings.py | |||
|
110 | ||||
|
111 | !!ls will return the output into a Python variable: | |||
|
112 | fperez[~/test]|5> !!ls *s.py | |||
|
113 | <5> ['scopes.py', 'strings.py'] | |||
|
114 | fperez[~/test]|6> print _5 | |||
|
115 | ['scopes.py', 'strings.py'] | |||
|
116 | ||||
|
117 | $ and $$ allow direct capture to named variables: | |||
|
118 | fperez[~/test]|7> $astr = ls *s.py | |||
|
119 | fperez[~/test]|8> astr | |||
|
120 | <8> 'scopes.py\\nstrings.py' | |||
|
121 | ||||
|
122 | fperez[~/test]|9> $$alist = ls *s.py | |||
|
123 | fperez[~/test]|10> alist | |||
|
124 | <10> ['scopes.py', 'strings.py'] | |||
|
125 | ||||
|
126 | alist is now a normal python list you can loop over. Using $ will expand | |||
|
127 | back the python values when alias calls are made: | |||
|
128 | fperez[~/test]|11> for f in alist: | |||
|
129 | |..> print 'file',f, | |||
|
130 | |..> wc -l $f | |||
|
131 | |..> | |||
|
132 | file scopes.py 13 scopes.py | |||
|
133 | file strings.py 4 strings.py | |||
|
134 | ||||
|
135 | Note that you may need to protect your variables with braces if you want | |||
|
136 | to append strings to their names. To copy all files in alist to .bak | |||
|
137 | extensions, you must use: | |||
|
138 | fperez[~/test]|12> for f in alist: | |||
|
139 | |..> cp $f ${f}.bak | |||
|
140 | ||||
|
141 | If you try using $f.bak, you'll get an AttributeError exception saying | |||
|
142 | that your string object doesn't have a .bak attribute. This is because | |||
|
143 | the $ expansion mechanism allows you to expand full Python expressions: | |||
|
144 | fperez[~/test]|13> echo "sys.platform is: $sys.platform" | |||
|
145 | sys.platform is: linux2 | |||
|
146 | ||||
|
147 | IPython's input history handling is still active, which allows you to | |||
|
148 | rerun a single block of multi-line input by simply using exec: | |||
|
149 | fperez[~/test]|14> $$alist = ls *.eps | |||
|
150 | fperez[~/test]|15> exec _i11 | |||
|
151 | file image2.eps 921 image2.eps | |||
|
152 | file image.eps 921 image.eps | |||
|
153 | ||||
|
154 | While these are new special-case syntaxes, they are designed to allow very | |||
|
155 | efficient use of the shell with minimal typing. At an interactive shell | |||
|
156 | prompt, conciseness of expression wins over readability. | |||
|
157 | ||||
|
158 | USEFUL FUNCTIONS AND MODULES | |||
|
159 | ---------------------------- | |||
|
160 | The os, sys and shutil modules from the Python standard library are | |||
|
161 | automatically loaded. Some additional functions, useful for shell usage, | |||
|
162 | are listed below. You can request more help about them with '?'. | |||
|
163 | ||||
|
164 | shell - execute a command in the underlying system shell | |||
|
165 | system - like shell(), but return the exit status of the command | |||
|
166 | sout - capture the output of a command as a string | |||
|
167 | lout - capture the output of a command as a list (split on '\\n') | |||
|
168 | getoutputerror - capture (output,error) of a shell command | |||
|
169 | ||||
|
170 | sout/lout are the functional equivalents of $/$$. They are provided to | |||
|
171 | allow you to capture system output in the middle of true python code, | |||
|
172 | function definitions, etc (where $ and $$ are invalid). | |||
|
173 | ||||
|
174 | DIRECTORY MANAGEMENT | |||
|
175 | -------------------- | |||
|
176 | Since each command passed by pysh to the underlying system is executed in | |||
|
177 | a subshell which exits immediately, you can NOT use !cd to navigate the | |||
|
178 | filesystem. | |||
|
179 | ||||
|
180 | Pysh provides its own builtin '%cd' magic command to move in the | |||
|
181 | filesystem (the % is not required with automagic on). It also maintains a | |||
|
182 | list of visited directories (use %dhist to see it) and allows direct | |||
|
183 | switching to any of them. Type 'cd?' for more details. | |||
|
184 | ||||
|
185 | %pushd, %popd and %dirs are provided for directory stack handling. | |||
|
186 | ||||
|
187 | PROMPT CUSTOMIZATION | |||
|
188 | -------------------- | |||
|
189 | ||||
|
190 | The supplied ipythonrc-pysh profile comes with an example of a very | |||
|
191 | colored and detailed prompt, mainly to serve as an illustration. The | |||
|
192 | valid escape sequences, besides color names, are: | |||
|
193 | ||||
|
194 | \\# - Prompt number. | |||
|
195 | \\D - Dots, as many as there are digits in \\# (so they align). | |||
|
196 | \\w - Current working directory (cwd). | |||
|
197 | \\W - Basename of current working directory. | |||
|
198 | \\XN - Where N=0..5. N terms of the cwd, with $HOME written as ~. | |||
|
199 | \\YN - Where N=0..5. Like XN, but if ~ is term N+1 it's also shown. | |||
|
200 | \\u - Username. | |||
|
201 | \\H - Full hostname. | |||
|
202 | \\h - Hostname up to first '.' | |||
|
203 | \\$ - Root symbol ($ or #). | |||
|
204 | \\t - Current time, in H:M:S format. | |||
|
205 | \\v - IPython release version. | |||
|
206 | \\n - Newline. | |||
|
207 | \\r - Carriage return. | |||
|
208 | \\\\ - An explicitly escaped '\\'. | |||
|
209 | ||||
|
210 | You can configure your prompt colors using any ANSI color escape. Each | |||
|
211 | color escape sets the color for any subsequent text, until another escape | |||
|
212 | comes in and changes things. The valid color escapes are: | |||
|
213 | ||||
|
214 | \\C_Black | |||
|
215 | \\C_Blue | |||
|
216 | \\C_Brown | |||
|
217 | \\C_Cyan | |||
|
218 | \\C_DarkGray | |||
|
219 | \\C_Green | |||
|
220 | \\C_LightBlue | |||
|
221 | \\C_LightCyan | |||
|
222 | \\C_LightGray | |||
|
223 | \\C_LightGreen | |||
|
224 | \\C_LightPurple | |||
|
225 | \\C_LightRed | |||
|
226 | \\C_Purple | |||
|
227 | \\C_Red | |||
|
228 | \\C_White | |||
|
229 | \\C_Yellow | |||
|
230 | \\C_Normal - Stop coloring, defaults to your terminal settings. | |||
|
231 | """ | |||
|
232 | pass | |||
|
233 | ||||
|
234 | # Configure a few things. Much of this is fairly hackish, since IPython | |||
|
235 | # doesn't really expose a clean API for it. Be careful if you start making | |||
|
236 | # many modifications here. | |||
|
237 | ||||
|
238 | ||||
|
239 | # Set the 'cd' command to quiet mode, a more shell-like behavior | |||
|
240 | __IPYTHON__.default_option('cd','-q') | |||
|
241 | ||||
|
242 | # This is redundant, ipy_user_conf.py will determine this | |||
|
243 | # Load all of $PATH as aliases | |||
|
244 | __IPYTHON__.magic_rehashx() | |||
|
245 | ||||
|
246 | # Remove %sc,%sx if present as aliases | |||
|
247 | __IPYTHON__.magic_unalias('sc') | |||
|
248 | __IPYTHON__.magic_unalias('sx') | |||
|
249 | ||||
|
250 | # We need different criteria for line-splitting, so that aliases such as | |||
|
251 | # 'gnome-terminal' are interpreted as a single alias instead of variable | |||
|
252 | # 'gnome' minus variable 'terminal'. | |||
|
253 | import re | |||
|
254 | __IPYTHON__.line_split = re.compile(r'^([\s*,;/])' | |||
|
255 | r'([\?\w\.\-\+]+\w*\s*)' | |||
|
256 | r'(\(?.*$)') | |||
|
257 | ||||
|
258 | # Namespace cleanup | |||
|
259 | del re |
@@ -0,0 +1,122 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """Modified input prompt for entering text with >>> or ... at the start. | |||
|
3 | ||||
|
4 | We define a special input line filter to allow typing lines which begin with | |||
|
5 | '>>> ' or '... '. These two strings, if present at the start of the input | |||
|
6 | line, are stripped. This allows for direct pasting of code from examples such | |||
|
7 | as those available in the standard Python tutorial. | |||
|
8 | ||||
|
9 | Normally pasting such code is one chunk is impossible because of the | |||
|
10 | extraneous >>> and ..., requiring one to do a line by line paste with careful | |||
|
11 | removal of those characters. This module allows pasting that kind of | |||
|
12 | multi-line examples in one pass. | |||
|
13 | ||||
|
14 | Here is an 'screenshot' of a section of the tutorial pasted into IPython with | |||
|
15 | this feature enabled: | |||
|
16 | ||||
|
17 | In [1]: >>> def fib2(n): # return Fibonacci series up to n | |||
|
18 | ...: ... '''Return a list containing the Fibonacci series up to n.''' | |||
|
19 | ...: ... result = [] | |||
|
20 | ...: ... a, b = 0, 1 | |||
|
21 | ...: ... while b < n: | |||
|
22 | ...: ... result.append(b) # see below | |||
|
23 | ...: ... a, b = b, a+b | |||
|
24 | ...: ... return result | |||
|
25 | ...: | |||
|
26 | ||||
|
27 | In [2]: fib2(10) | |||
|
28 | Out[2]: [1, 1, 2, 3, 5, 8] | |||
|
29 | ||||
|
30 | The >>> and ... are stripped from the input so that the python interpreter | |||
|
31 | only sees the real part of the code. | |||
|
32 | ||||
|
33 | All other input is processed normally. | |||
|
34 | ||||
|
35 | Notes | |||
|
36 | ===== | |||
|
37 | ||||
|
38 | * You can even paste code that has extra initial spaces, such as is common in | |||
|
39 | doctests: | |||
|
40 | ||||
|
41 | In [3]: >>> a = ['Mary', 'had', 'a', 'little', 'lamb'] | |||
|
42 | ||||
|
43 | In [4]: >>> for i in range(len(a)): | |||
|
44 | ...: ... print i, a[i] | |||
|
45 | ...: ... | |||
|
46 | 0 Mary | |||
|
47 | 1 had | |||
|
48 | 2 a | |||
|
49 | 3 little | |||
|
50 | 4 lamb | |||
|
51 | """ | |||
|
52 | ||||
|
53 | #***************************************************************************** | |||
|
54 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> | |||
|
55 | # | |||
|
56 | # Distributed under the terms of the BSD License. The full license is in | |||
|
57 | # the file COPYING, distributed as part of this software. | |||
|
58 | #***************************************************************************** | |||
|
59 | ||||
|
60 | from IPython import Release | |||
|
61 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |||
|
62 | __license__ = Release.license | |||
|
63 | ||||
|
64 | # This file is an example of how to modify IPython's line-processing behavior | |||
|
65 | # without touching the internal code. We'll define an alternate pre-processing | |||
|
66 | # stage which allows a special form of input (which is invalid Python syntax) | |||
|
67 | # for certain quantities, rewrites a line of proper Python in those cases, and | |||
|
68 | # then passes it off to IPython's normal processor for further work. | |||
|
69 | ||||
|
70 | # With this kind of customization, IPython can be adapted for many | |||
|
71 | # special-purpose scenarios providing alternate input syntaxes. | |||
|
72 | ||||
|
73 | # This file can be imported like a regular module. | |||
|
74 | ||||
|
75 | # IPython has a prefilter() function that analyzes each input line. We redefine | |||
|
76 | # it here to first pre-process certain forms of input | |||
|
77 | ||||
|
78 | # The prototype of any alternate prefilter must be like this one (the name | |||
|
79 | # doesn't matter): | |||
|
80 | # - line is a string containing the user input line. | |||
|
81 | # - continuation is a parameter which tells us if we are processing a first | |||
|
82 | # line of user input or the second or higher of a multi-line statement. | |||
|
83 | ||||
|
84 | import re | |||
|
85 | ||||
|
86 | from IPython.iplib import InteractiveShell | |||
|
87 | ||||
|
88 | PROMPT_RE = re.compile(r'(^[ \t]*>>> |^[ \t]*\.\.\. )') | |||
|
89 | ||||
|
90 | def prefilter_paste(self,line,continuation): | |||
|
91 | """Alternate prefilter for input of pasted code from an interpreter. | |||
|
92 | """ | |||
|
93 | if not line: | |||
|
94 | return '' | |||
|
95 | m = PROMPT_RE.match(line) | |||
|
96 | if m: | |||
|
97 | # In the end, always call the default IPython _prefilter() function. | |||
|
98 | # Note that self must be passed explicitly, b/c we're calling the | |||
|
99 | # unbound class method (since this method will overwrite the instance | |||
|
100 | # prefilter()) | |||
|
101 | return self._prefilter(line[len(m.group(0)):],continuation) | |||
|
102 | elif line.strip() == '...': | |||
|
103 | return self._prefilter('',continuation) | |||
|
104 | elif line.isspace(): | |||
|
105 | # This allows us to recognize multiple input prompts separated by blank | |||
|
106 | # lines and pasted in a single chunk, very common when pasting doctests | |||
|
107 | # or long tutorial passages. | |||
|
108 | return '' | |||
|
109 | else: | |||
|
110 | return self._prefilter(line,continuation) | |||
|
111 | ||||
|
112 | def activate_prefilter(): | |||
|
113 | """Rebind the input-pasting filter to be the new IPython prefilter""" | |||
|
114 | InteractiveShell.prefilter = prefilter_paste | |||
|
115 | ||||
|
116 | def deactivate_prefilter(): | |||
|
117 | """Reset the filter.""" | |||
|
118 | InteractiveShell.prefilter = InteractiveShell._prefilter | |||
|
119 | ||||
|
120 | # Just a heads up at the console | |||
|
121 | activate_prefilter() | |||
|
122 | print '*** Pasting of code with ">>>" or "..." has been enabled.' |
@@ -0,0 +1,83 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """Modified input prompt for entering quantities with units. | |||
|
3 | ||||
|
4 | Modify the behavior of the interactive interpreter to allow direct input of | |||
|
5 | quantities with units without having to make a function call. | |||
|
6 | ||||
|
7 | Now the following forms are accepted: | |||
|
8 | ||||
|
9 | x = 4 m | |||
|
10 | y = -.45e3 m/s | |||
|
11 | g = 9.8 m/s**2 | |||
|
12 | a = 2.3 m/s^2 # ^ -> ** automatically | |||
|
13 | ||||
|
14 | All other input is processed normally. | |||
|
15 | """ | |||
|
16 | #***************************************************************************** | |||
|
17 | # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu> | |||
|
18 | # | |||
|
19 | # Distributed under the terms of the BSD License. The full license is in | |||
|
20 | # the file COPYING, distributed as part of this software. | |||
|
21 | #***************************************************************************** | |||
|
22 | ||||
|
23 | from IPython import Release | |||
|
24 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |||
|
25 | __license__ = Release.license | |||
|
26 | ||||
|
27 | # This file is an example of how to modify IPython's line-processing behavior | |||
|
28 | # without touching the internal code. We'll define an alternate pre-processing | |||
|
29 | # stage which allows a special form of input (which is invalid Python syntax) | |||
|
30 | # for certain quantities, rewrites a line of proper Python in those cases, and | |||
|
31 | # then passes it off to IPython's normal processor for further work. | |||
|
32 | ||||
|
33 | # With this kind of customization, IPython can be adapted for many | |||
|
34 | # special-purpose scenarios providing alternate input syntaxes. | |||
|
35 | ||||
|
36 | # This file can be imported like a regular module. | |||
|
37 | ||||
|
38 | # IPython has a prefilter() function that analyzes each input line. We redefine | |||
|
39 | # it here to first pre-process certain forms of input | |||
|
40 | ||||
|
41 | # The prototype of any alternate prefilter must be like this one (the name | |||
|
42 | # doesn't matter): | |||
|
43 | # - line is a string containing the user input line. | |||
|
44 | # - continuation is a parameter which tells us if we are processing a first line of | |||
|
45 | # user input or the second or higher of a multi-line statement. | |||
|
46 | ||||
|
47 | def prefilter_PQ(self,line,continuation): | |||
|
48 | """Alternate prefilter for input of PhysicalQuantityInteractive objects. | |||
|
49 | ||||
|
50 | This assumes that the function PhysicalQuantityInteractive() has been | |||
|
51 | imported.""" | |||
|
52 | ||||
|
53 | from re import match | |||
|
54 | from IPython.iplib import InteractiveShell | |||
|
55 | ||||
|
56 | # This regexp is what does the real work | |||
|
57 | unit_split = match(r'\s*(\w+)\s*=\s*(-?\d*\.?\d*[eE]?-?\d*)\s+([a-zA-Z].*)', | |||
|
58 | line) | |||
|
59 | ||||
|
60 | # If special input was ecnountered, process it: | |||
|
61 | if unit_split: | |||
|
62 | var,val,units = unit_split.groups() | |||
|
63 | if var and val and units: | |||
|
64 | units = units.replace('^','**') | |||
|
65 | # Now a valid line needs to be constructed for IPython to process: | |||
|
66 | line = var +" = PhysicalQuantityInteractive(" + val + ", '" + \ | |||
|
67 | units + "')" | |||
|
68 | #print 'New line:',line # dbg | |||
|
69 | ||||
|
70 | # In the end, always call the default IPython _prefilter() function. Note | |||
|
71 | # that self must be passed explicitly, b/c we're calling the unbound class | |||
|
72 | # method (since this method will overwrite the instance prefilter()) | |||
|
73 | return InteractiveShell._prefilter(self,line,continuation) | |||
|
74 | ||||
|
75 | # Rebind this to be the new IPython prefilter: | |||
|
76 | from IPython.iplib import InteractiveShell | |||
|
77 | InteractiveShell.prefilter = prefilter_PQ | |||
|
78 | ||||
|
79 | # Clean up the namespace. | |||
|
80 | del InteractiveShell,prefilter_PQ | |||
|
81 | ||||
|
82 | # Just a heads up at the console | |||
|
83 | print '*** Simplified input for physical quantities enabled.' |
@@ -0,0 +1,88 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """Modify the PhysicalQuantities class for more convenient interactive use. | |||
|
3 | ||||
|
4 | Also redefine some math functions to operate on PhysQties with no need for | |||
|
5 | special method syntax. This just means moving them out to the global | |||
|
6 | namespace. | |||
|
7 | ||||
|
8 | This module should always be loaded *after* math or Numeric, so it can | |||
|
9 | overwrite math functions with the versions that handle units.""" | |||
|
10 | ||||
|
11 | #***************************************************************************** | |||
|
12 | # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu> | |||
|
13 | # | |||
|
14 | # Distributed under the terms of the BSD License. The full license is in | |||
|
15 | # the file COPYING, distributed as part of this software. | |||
|
16 | #***************************************************************************** | |||
|
17 | ||||
|
18 | from IPython import Release | |||
|
19 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |||
|
20 | __license__ = Release.license | |||
|
21 | ||||
|
22 | from Scientific.Physics.PhysicalQuantities import PhysicalQuantity | |||
|
23 | ||||
|
24 | # This code can be set up to work with Numeric or with math for providing the | |||
|
25 | # mathematical functions. Uncomment the one you prefer to use below. | |||
|
26 | ||||
|
27 | # If you use math, sin(x) won't work for x an array, only float or PhysQty | |||
|
28 | import math | |||
|
29 | ||||
|
30 | # If you use Numeric, sin(x) works for x a float, PhysQty an array. | |||
|
31 | #import Numeric as math | |||
|
32 | ||||
|
33 | class PhysicalQuantityFunction: | |||
|
34 | """Generic function wrapper for PhysicalQuantity instances. | |||
|
35 | ||||
|
36 | Calls functions from either the math library or the instance's methods as | |||
|
37 | required. Allows using sin(theta) or sqrt(v**2) syntax irrespective of | |||
|
38 | whether theta is a pure number or a PhysicalQuantity. | |||
|
39 | ||||
|
40 | This is *slow*. It's meant for convenient interactive use, not for | |||
|
41 | speed.""" | |||
|
42 | ||||
|
43 | def __init__(self,name): | |||
|
44 | self.name = name | |||
|
45 | ||||
|
46 | def __call__(self,x): | |||
|
47 | if isinstance(x,PhysicalQuantity): | |||
|
48 | return PhysicalQuantity.__dict__[self.name](x) | |||
|
49 | else: | |||
|
50 | return math.__dict__[self.name](x) | |||
|
51 | ||||
|
52 | class PhysicalQuantityInteractive(PhysicalQuantity): | |||
|
53 | """Physical quantity with units - modified for Interactive use. | |||
|
54 | ||||
|
55 | Basically, the __str__ and __repr__ methods have been swapped for more | |||
|
56 | convenient interactive use. Powers are shown as ^ instead of ** and only 4 | |||
|
57 | significant figures are shown. | |||
|
58 | ||||
|
59 | Also adds the following aliases for commonly used methods: | |||
|
60 | b = PhysicalQuantity.inBaseUnits | |||
|
61 | u = PhysicalQuantity.inUnitsOf | |||
|
62 | ||||
|
63 | These are useful when doing a lot of interactive calculations. | |||
|
64 | """ | |||
|
65 | ||||
|
66 | # shorthands for the most useful unit conversions | |||
|
67 | b = PhysicalQuantity.inBaseUnits # so you can just type x.b to get base units | |||
|
68 | u = PhysicalQuantity.inUnitsOf | |||
|
69 | ||||
|
70 | # This can be done, but it can get dangerous when coupled with IPython's | |||
|
71 | # auto-calling. Everything ends up shown in baseunits and things like x*2 | |||
|
72 | # get automatically converted to k(*2), which doesn't work. | |||
|
73 | # Probably not a good idea in general... | |||
|
74 | #__call__ = b | |||
|
75 | ||||
|
76 | def __str__(self): | |||
|
77 | return PhysicalQuantity.__repr__(self) | |||
|
78 | ||||
|
79 | def __repr__(self): | |||
|
80 | value = '%.4G' % self.value | |||
|
81 | units = self.unit.name().replace('**','^') | |||
|
82 | return value + ' ' + units | |||
|
83 | ||||
|
84 | # implement the methods defined in PhysicalQuantity as PhysicalQuantityFunctions | |||
|
85 | sin = PhysicalQuantityFunction('sin') | |||
|
86 | cos = PhysicalQuantityFunction('cos') | |||
|
87 | tan = PhysicalQuantityFunction('tan') | |||
|
88 | sqrt = PhysicalQuantityFunction('sqrt') |
@@ -0,0 +1,13 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """This directory is meant for special-purpose extensions to IPython. | |||
|
3 | ||||
|
4 | This can include things which alter the syntax processing stage (see | |||
|
5 | PhysicalQ_Input for an example of how to do this). | |||
|
6 | ||||
|
7 | Any file located here can be called with an 'execfile =' option as | |||
|
8 | ||||
|
9 | execfile = Extensions/filename.py | |||
|
10 | ||||
|
11 | since the IPython directory itself is already part of the search path for | |||
|
12 | files listed as 'execfile ='. | |||
|
13 | """ |
@@ -0,0 +1,398 | |||||
|
1 | """ | |||
|
2 | ``astyle`` provides classes for adding style (foreground and background color; | |||
|
3 | bold; blink; etc.) to terminal and curses output. | |||
|
4 | """ | |||
|
5 | ||||
|
6 | ||||
|
7 | import sys, os | |||
|
8 | ||||
|
9 | try: | |||
|
10 | import curses | |||
|
11 | except ImportError: | |||
|
12 | curses = None | |||
|
13 | ||||
|
14 | ||||
|
15 | COLOR_BLACK = 0 | |||
|
16 | COLOR_RED = 1 | |||
|
17 | COLOR_GREEN = 2 | |||
|
18 | COLOR_YELLOW = 3 | |||
|
19 | COLOR_BLUE = 4 | |||
|
20 | COLOR_MAGENTA = 5 | |||
|
21 | COLOR_CYAN = 6 | |||
|
22 | COLOR_WHITE = 7 | |||
|
23 | ||||
|
24 | A_BLINK = 1<<0 # Blinking text | |||
|
25 | A_BOLD = 1<<1 # Extra bright or bold text | |||
|
26 | A_DIM = 1<<2 # Half bright text | |||
|
27 | A_REVERSE = 1<<3 # Reverse-video text | |||
|
28 | A_STANDOUT = 1<<4 # The best highlighting mode available | |||
|
29 | A_UNDERLINE = 1<<5 # Underlined text | |||
|
30 | ||||
|
31 | ||||
|
32 | class Style(object): | |||
|
33 | """ | |||
|
34 | Store foreground color, background color and attribute (bold, underlined | |||
|
35 | etc.). | |||
|
36 | """ | |||
|
37 | __slots__ = ("fg", "bg", "attrs") | |||
|
38 | ||||
|
39 | COLORNAMES = { | |||
|
40 | "black": COLOR_BLACK, | |||
|
41 | "red": COLOR_RED, | |||
|
42 | "green": COLOR_GREEN, | |||
|
43 | "yellow": COLOR_YELLOW, | |||
|
44 | "blue": COLOR_BLUE, | |||
|
45 | "magenta": COLOR_MAGENTA, | |||
|
46 | "cyan": COLOR_CYAN, | |||
|
47 | "white": COLOR_WHITE, | |||
|
48 | } | |||
|
49 | ATTRNAMES = { | |||
|
50 | "blink": A_BLINK, | |||
|
51 | "bold": A_BOLD, | |||
|
52 | "dim": A_DIM, | |||
|
53 | "reverse": A_REVERSE, | |||
|
54 | "standout": A_STANDOUT, | |||
|
55 | "underline": A_UNDERLINE, | |||
|
56 | } | |||
|
57 | ||||
|
58 | def __init__(self, fg, bg, attrs=0): | |||
|
59 | """ | |||
|
60 | Create a ``Style`` object with ``fg`` as the foreground color, | |||
|
61 | ``bg`` as the background color and ``attrs`` as the attributes. | |||
|
62 | ||||
|
63 | Examples: | |||
|
64 | ||||
|
65 | >>> Style(COLOR_RED, COLOR_BLACK) | |||
|
66 | >>> Style(COLOR_YELLOW, COLOR_BLUE, A_BOLD|A_UNDERLINE) | |||
|
67 | """ | |||
|
68 | self.fg = fg | |||
|
69 | self.bg = bg | |||
|
70 | self.attrs = attrs | |||
|
71 | ||||
|
72 | def __call__(self, *args): | |||
|
73 | text = Text() | |||
|
74 | for arg in args: | |||
|
75 | if isinstance(arg, Text): | |||
|
76 | text.extend(arg) | |||
|
77 | else: | |||
|
78 | text.append((self, arg)) | |||
|
79 | return text | |||
|
80 | ||||
|
81 | def __eq__(self, other): | |||
|
82 | return self.fg == other.fg and self.bg == other.bg and self.attrs == other.attrs | |||
|
83 | ||||
|
84 | def __neq__(self, other): | |||
|
85 | return self.fg != other.fg or self.bg != other.bg or self.attrs != other.attrs | |||
|
86 | ||||
|
87 | def __repr__(self): | |||
|
88 | color2name = ("black", "red", "green", "yellow", "blue", "magenta", "cyan", "white") | |||
|
89 | attrs2name = ("blink", "bold", "dim", "reverse", "standout", "underline") | |||
|
90 | ||||
|
91 | return "<%s fg=%s bg=%s attrs=%s>" % ( | |||
|
92 | self.__class__.__name__, color2name[self.fg], color2name[self.bg], | |||
|
93 | "|".join([attrs2name[b] for b in xrange(6) if self.attrs&(1<<b)]) or 0) | |||
|
94 | ||||
|
95 | def fromstr(cls, value): | |||
|
96 | """ | |||
|
97 | Create a ``Style`` object from a string. The format looks like this: | |||
|
98 | ``"red:black:bold|blink"``. | |||
|
99 | """ | |||
|
100 | # defaults | |||
|
101 | fg = COLOR_WHITE | |||
|
102 | bg = COLOR_BLACK | |||
|
103 | attrs = 0 | |||
|
104 | ||||
|
105 | parts = value.split(":") | |||
|
106 | if len(parts) > 0: | |||
|
107 | fg = cls.COLORNAMES[parts[0].lower()] | |||
|
108 | if len(parts) > 1: | |||
|
109 | bg = cls.COLORNAMES[parts[1].lower()] | |||
|
110 | if len(parts) > 2: | |||
|
111 | for strattr in parts[2].split("|"): | |||
|
112 | attrs |= cls.ATTRNAMES[strattr.lower()] | |||
|
113 | return cls(fg, bg, attrs) | |||
|
114 | fromstr = classmethod(fromstr) | |||
|
115 | ||||
|
116 | def fromenv(cls, name, default): | |||
|
117 | """ | |||
|
118 | Create a ``Style`` from an environment variable named ``name`` | |||
|
119 | (using ``default`` if the environment variable doesn't exist). | |||
|
120 | """ | |||
|
121 | return cls.fromstr(os.environ.get(name, default)) | |||
|
122 | fromenv = classmethod(fromenv) | |||
|
123 | ||||
|
124 | ||||
|
125 | def switchstyle(s1, s2): | |||
|
126 | """ | |||
|
127 | Return the ANSI escape sequence needed to switch from style ``s1`` to | |||
|
128 | style ``s2``. | |||
|
129 | """ | |||
|
130 | attrmask = (A_BLINK|A_BOLD|A_UNDERLINE|A_REVERSE) | |||
|
131 | a1 = s1.attrs & attrmask | |||
|
132 | a2 = s2.attrs & attrmask | |||
|
133 | ||||
|
134 | args = [] | |||
|
135 | if s1 != s2: | |||
|
136 | # do we have to get rid of the bold/underline/blink bit? | |||
|
137 | # (can only be done by a reset) | |||
|
138 | # use reset when our target color is the default color | |||
|
139 | # (this is shorter than 37;40) | |||
|
140 | if (a1 & ~a2 or s2==style_default): | |||
|
141 | args.append("0") | |||
|
142 | s1 = style_default | |||
|
143 | a1 = 0 | |||
|
144 | ||||
|
145 | # now we know that old and new color have the same boldness, | |||
|
146 | # or the new color is bold and the old isn't, | |||
|
147 | # i.e. we only might have to switch bold on, not off | |||
|
148 | if not (a1 & A_BOLD) and (a2 & A_BOLD): | |||
|
149 | args.append("1") | |||
|
150 | ||||
|
151 | # Fix underline | |||
|
152 | if not (a1 & A_UNDERLINE) and (a2 & A_UNDERLINE): | |||
|
153 | args.append("4") | |||
|
154 | ||||
|
155 | # Fix blink | |||
|
156 | if not (a1 & A_BLINK) and (a2 & A_BLINK): | |||
|
157 | args.append("5") | |||
|
158 | ||||
|
159 | # Fix reverse | |||
|
160 | if not (a1 & A_REVERSE) and (a2 & A_REVERSE): | |||
|
161 | args.append("7") | |||
|
162 | ||||
|
163 | # Fix foreground color | |||
|
164 | if s1.fg != s2.fg: | |||
|
165 | args.append("3%d" % s2.fg) | |||
|
166 | ||||
|
167 | # Finally fix the background color | |||
|
168 | if s1.bg != s2.bg: | |||
|
169 | args.append("4%d" % s2.bg) | |||
|
170 | ||||
|
171 | if args: | |||
|
172 | return "\033[%sm" % ";".join(args) | |||
|
173 | return "" | |||
|
174 | ||||
|
175 | ||||
|
176 | class Text(list): | |||
|
177 | """ | |||
|
178 | A colored string. A ``Text`` object is a sequence, the sequence | |||
|
179 | items will be ``(style, string)`` tuples. | |||
|
180 | """ | |||
|
181 | ||||
|
182 | def __init__(self, *args): | |||
|
183 | list.__init__(self) | |||
|
184 | self.append(*args) | |||
|
185 | ||||
|
186 | def __repr__(self): | |||
|
187 | return "%s.%s(%s)" % ( | |||
|
188 | self.__class__.__module__, self.__class__.__name__, | |||
|
189 | list.__repr__(self)[1:-1]) | |||
|
190 | ||||
|
191 | def append(self, *args): | |||
|
192 | for arg in args: | |||
|
193 | if isinstance(arg, Text): | |||
|
194 | self.extend(arg) | |||
|
195 | elif isinstance(arg, tuple): # must be (style, string) | |||
|
196 | list.append(self, arg) | |||
|
197 | elif isinstance(arg, unicode): | |||
|
198 | list.append(self, (style_default, arg)) | |||
|
199 | else: | |||
|
200 | list.append(self, (style_default, str(arg))) | |||
|
201 | ||||
|
202 | def insert(self, index, *args): | |||
|
203 | self[index:index] = Text(*args) | |||
|
204 | ||||
|
205 | def __add__(self, other): | |||
|
206 | new = Text() | |||
|
207 | new.append(self) | |||
|
208 | new.append(other) | |||
|
209 | return new | |||
|
210 | ||||
|
211 | def __iadd__(self, other): | |||
|
212 | self.append(other) | |||
|
213 | return self | |||
|
214 | ||||
|
215 | def format(self, styled=True): | |||
|
216 | """ | |||
|
217 | This generator yields the strings that will make up the final | |||
|
218 | colorized string. | |||
|
219 | """ | |||
|
220 | if styled: | |||
|
221 | oldstyle = style_default | |||
|
222 | for (style, string) in self: | |||
|
223 | if not isinstance(style, (int, long)): | |||
|
224 | switch = switchstyle(oldstyle, style) | |||
|
225 | if switch: | |||
|
226 | yield switch | |||
|
227 | if string: | |||
|
228 | yield string | |||
|
229 | oldstyle = style | |||
|
230 | switch = switchstyle(oldstyle, style_default) | |||
|
231 | if switch: | |||
|
232 | yield switch | |||
|
233 | else: | |||
|
234 | for (style, string) in self: | |||
|
235 | if not isinstance(style, (int, long)): | |||
|
236 | yield string | |||
|
237 | ||||
|
238 | def string(self, styled=True): | |||
|
239 | """ | |||
|
240 | Return the resulting string (with escape sequences, if ``styled`` | |||
|
241 | is true). | |||
|
242 | """ | |||
|
243 | return "".join(self.format(styled)) | |||
|
244 | ||||
|
245 | def __str__(self): | |||
|
246 | """ | |||
|
247 | Return ``self`` as a string (without ANSI escape sequences). | |||
|
248 | """ | |||
|
249 | return self.string(False) | |||
|
250 | ||||
|
251 | def write(self, stream, styled=True): | |||
|
252 | """ | |||
|
253 | Write ``self`` to the output stream ``stream`` (with escape sequences, | |||
|
254 | if ``styled`` is true). | |||
|
255 | """ | |||
|
256 | for part in self.format(styled): | |||
|
257 | stream.write(part) | |||
|
258 | ||||
|
259 | ||||
|
260 | try: | |||
|
261 | import ipipe | |||
|
262 | except ImportError: | |||
|
263 | pass | |||
|
264 | else: | |||
|
265 | def xrepr_astyle_text(self, mode="default"): | |||
|
266 | yield (-1, True) | |||
|
267 | for info in self: | |||
|
268 | yield info | |||
|
269 | ipipe.xrepr.when_type(Text)(xrepr_astyle_text) | |||
|
270 | ||||
|
271 | ||||
|
272 | def streamstyle(stream, styled=None): | |||
|
273 | """ | |||
|
274 | If ``styled`` is ``None``, return whether ``stream`` refers to a terminal. | |||
|
275 | If this can't be determined (either because ``stream`` doesn't refer to a | |||
|
276 | real OS file, or because you're on Windows) return ``False``. If ``styled`` | |||
|
277 | is not ``None`` ``styled`` will be returned unchanged. | |||
|
278 | """ | |||
|
279 | if styled is None: | |||
|
280 | try: | |||
|
281 | styled = os.isatty(stream.fileno()) | |||
|
282 | except (KeyboardInterrupt, SystemExit): | |||
|
283 | raise | |||
|
284 | except Exception: | |||
|
285 | styled = False | |||
|
286 | return styled | |||
|
287 | ||||
|
288 | ||||
|
289 | def write(stream, styled, *texts): | |||
|
290 | """ | |||
|
291 | Write ``texts`` to ``stream``. | |||
|
292 | """ | |||
|
293 | text = Text(*texts) | |||
|
294 | text.write(stream, streamstyle(stream, styled)) | |||
|
295 | ||||
|
296 | ||||
|
297 | def writeln(stream, styled, *texts): | |||
|
298 | """ | |||
|
299 | Write ``texts`` to ``stream`` and finish with a line feed. | |||
|
300 | """ | |||
|
301 | write(stream, styled, *texts) | |||
|
302 | stream.write("\n") | |||
|
303 | ||||
|
304 | ||||
|
305 | class Stream(object): | |||
|
306 | """ | |||
|
307 | Stream wrapper that adds color output. | |||
|
308 | """ | |||
|
309 | def __init__(self, stream, styled=None): | |||
|
310 | self.stream = stream | |||
|
311 | self.styled = streamstyle(stream, styled) | |||
|
312 | ||||
|
313 | def write(self, *texts): | |||
|
314 | write(self.stream, self.styled, *texts) | |||
|
315 | ||||
|
316 | def writeln(self, *texts): | |||
|
317 | writeln(self.stream, self.styled, *texts) | |||
|
318 | ||||
|
319 | def __getattr__(self, name): | |||
|
320 | return getattr(self.stream, name) | |||
|
321 | ||||
|
322 | ||||
|
323 | class stdout(object): | |||
|
324 | """ | |||
|
325 | Stream wrapper for ``sys.stdout`` that adds color output. | |||
|
326 | """ | |||
|
327 | def write(self, *texts): | |||
|
328 | write(sys.stdout, None, *texts) | |||
|
329 | ||||
|
330 | def writeln(self, *texts): | |||
|
331 | writeln(sys.stdout, None, *texts) | |||
|
332 | ||||
|
333 | def __getattr__(self, name): | |||
|
334 | return getattr(sys.stdout, name) | |||
|
335 | stdout = stdout() | |||
|
336 | ||||
|
337 | ||||
|
338 | class stderr(object): | |||
|
339 | """ | |||
|
340 | Stream wrapper for ``sys.stderr`` that adds color output. | |||
|
341 | """ | |||
|
342 | def write(self, *texts): | |||
|
343 | write(sys.stderr, None, *texts) | |||
|
344 | ||||
|
345 | def writeln(self, *texts): | |||
|
346 | writeln(sys.stderr, None, *texts) | |||
|
347 | ||||
|
348 | def __getattr__(self, name): | |||
|
349 | return getattr(sys.stdout, name) | |||
|
350 | stderr = stderr() | |||
|
351 | ||||
|
352 | ||||
|
353 | if curses is not None: | |||
|
354 | # This is probably just range(8) | |||
|
355 | COLOR2CURSES = [ | |||
|
356 | COLOR_BLACK, | |||
|
357 | COLOR_RED, | |||
|
358 | COLOR_GREEN, | |||
|
359 | COLOR_YELLOW, | |||
|
360 | COLOR_BLUE, | |||
|
361 | COLOR_MAGENTA, | |||
|
362 | COLOR_CYAN, | |||
|
363 | COLOR_WHITE, | |||
|
364 | ] | |||
|
365 | ||||
|
366 | A2CURSES = { | |||
|
367 | A_BLINK: curses.A_BLINK, | |||
|
368 | A_BOLD: curses.A_BOLD, | |||
|
369 | A_DIM: curses.A_DIM, | |||
|
370 | A_REVERSE: curses.A_REVERSE, | |||
|
371 | A_STANDOUT: curses.A_STANDOUT, | |||
|
372 | A_UNDERLINE: curses.A_UNDERLINE, | |||
|
373 | } | |||
|
374 | ||||
|
375 | ||||
|
376 | # default style | |||
|
377 | style_default = Style.fromstr("white:black") | |||
|
378 | ||||
|
379 | # Styles for datatypes | |||
|
380 | style_type_none = Style.fromstr("magenta:black") | |||
|
381 | style_type_bool = Style.fromstr("magenta:black") | |||
|
382 | style_type_number = Style.fromstr("yellow:black") | |||
|
383 | style_type_datetime = Style.fromstr("magenta:black") | |||
|
384 | style_type_type = Style.fromstr("cyan:black") | |||
|
385 | ||||
|
386 | # Style for URLs and file/directory names | |||
|
387 | style_url = Style.fromstr("green:black") | |||
|
388 | style_dir = Style.fromstr("cyan:black") | |||
|
389 | style_file = Style.fromstr("green:black") | |||
|
390 | ||||
|
391 | # Style for ellipsis (when an output has been shortened | |||
|
392 | style_ellisis = Style.fromstr("red:black") | |||
|
393 | ||||
|
394 | # Style for displaying exceptions | |||
|
395 | style_error = Style.fromstr("red:black") | |||
|
396 | ||||
|
397 | # Style for displaying non-existing attributes | |||
|
398 | style_nodata = Style.fromstr("red:black") |
@@ -0,0 +1,66 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ IPython extension: add %clear magic """ | |||
|
3 | ||||
|
4 | import IPython.ipapi | |||
|
5 | import gc | |||
|
6 | ip = IPython.ipapi.get() | |||
|
7 | ||||
|
8 | ||||
|
9 | def clear_f(self,arg): | |||
|
10 | """ Clear various data (e.g. stored history data) | |||
|
11 | ||||
|
12 | %clear out - clear output history | |||
|
13 | %clear in - clear input history | |||
|
14 | %clear shadow_compress - Compresses shadow history (to speed up ipython) | |||
|
15 | %clear shadow_nuke - permanently erase all entries in shadow history | |||
|
16 | %clear dhist - clear dir history | |||
|
17 | """ | |||
|
18 | ||||
|
19 | api = self.getapi() | |||
|
20 | for target in arg.split(): | |||
|
21 | if target == 'out': | |||
|
22 | print "Flushing output cache (%d entries)" % len(api.user_ns['_oh']) | |||
|
23 | self.outputcache.flush() | |||
|
24 | elif target == 'in': | |||
|
25 | print "Flushing input history" | |||
|
26 | from IPython import iplib | |||
|
27 | pc = self.outputcache.prompt_count + 1 | |||
|
28 | for n in range(1, pc): | |||
|
29 | key = '_i'+`n` | |||
|
30 | try: | |||
|
31 | del self.user_ns[key] | |||
|
32 | except: pass | |||
|
33 | # must be done in-place | |||
|
34 | self.input_hist[:] = ['\n'] * pc | |||
|
35 | self.input_hist_raw[:] = ['\n'] * pc | |||
|
36 | elif target == 'array': | |||
|
37 | try: | |||
|
38 | pylab=ip.IP.pylab | |||
|
39 | for x in self.user_ns.keys(): | |||
|
40 | if isinstance(self.user_ns[x],pylab.arraytype): | |||
|
41 | del self.user_ns[x] | |||
|
42 | except AttributeError: | |||
|
43 | print "Clear array only available in -pylab mode" | |||
|
44 | gc.collect() | |||
|
45 | ||||
|
46 | elif target == 'shadow_compress': | |||
|
47 | print "Compressing shadow history" | |||
|
48 | api.db.hcompress('shadowhist') | |||
|
49 | ||||
|
50 | elif target == 'shadow_nuke': | |||
|
51 | print "Erased all keys from shadow history " | |||
|
52 | for k in ip.db.keys('shadowhist/*'): | |||
|
53 | del ip.db[k] | |||
|
54 | elif target == 'dhist': | |||
|
55 | print "Clearing directory history" | |||
|
56 | del ip.user_ns['_dh'][:] | |||
|
57 | ||||
|
58 | ||||
|
59 | ip.expose_magic("clear",clear_f) | |||
|
60 | import ipy_completers | |||
|
61 | ipy_completers.quick_completer( | |||
|
62 | '%clear','in out shadow_nuke shadow_compress dhist') | |||
|
63 | ||||
|
64 | ||||
|
65 | ||||
|
66 |
@@ -0,0 +1,90 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ %env magic command for storing environment variables persistently | |||
|
3 | """ | |||
|
4 | ||||
|
5 | import IPython.ipapi | |||
|
6 | ip = IPython.ipapi.get() | |||
|
7 | ||||
|
8 | import os,sys | |||
|
9 | ||||
|
10 | def restore_env(self): | |||
|
11 | ip = self.getapi() | |||
|
12 | env = ip.db.get('stored_env', {'set' : {}, 'add' : [], 'pre' : []}) | |||
|
13 | for k,v in env['set'].items(): | |||
|
14 | os.environ[k] = v | |||
|
15 | for k,v in env['add']: | |||
|
16 | os.environ[k] = os.environ.get(k,"") + v | |||
|
17 | for k,v in env['pre']: | |||
|
18 | os.environ[k] = v + os.environ.get(k,"") | |||
|
19 | raise IPython.ipapi.TryNext | |||
|
20 | ||||
|
21 | ip.set_hook('late_startup_hook', restore_env) | |||
|
22 | ||||
|
23 | def persist_env(self, parameter_s=''): | |||
|
24 | """ Store environment variables persistently | |||
|
25 | ||||
|
26 | IPython remembers the values across sessions, which is handy to avoid | |||
|
27 | editing startup files. | |||
|
28 | ||||
|
29 | %env - Show all environment variables | |||
|
30 | %env VISUAL=jed - set VISUAL to jed | |||
|
31 | %env PATH+=;/foo - append ;foo to PATH | |||
|
32 | %env PATH+=;/bar - also append ;bar to PATH | |||
|
33 | %env PATH-=/wbin; - prepend /wbin; to PATH | |||
|
34 | %env -d VISUAL - forget VISUAL persistent val | |||
|
35 | %env -p - print all persistent env modifications | |||
|
36 | """ | |||
|
37 | ||||
|
38 | if not parameter_s.strip(): | |||
|
39 | return os.environ.data | |||
|
40 | ||||
|
41 | ip = self.getapi() | |||
|
42 | db = ip.db | |||
|
43 | env = ip.db.get('stored_env', {'set' : {}, 'add' : [], 'pre' : []}) | |||
|
44 | ||||
|
45 | if parameter_s.startswith('-p'): | |||
|
46 | return env | |||
|
47 | ||||
|
48 | elif parameter_s.startswith('-d'): | |||
|
49 | parts = (parameter_s.split()[1], '<del>') | |||
|
50 | ||||
|
51 | else: | |||
|
52 | parts = parameter_s.strip().split('=') | |||
|
53 | ||||
|
54 | if len(parts) == 2: | |||
|
55 | k,v = [p.strip() for p in parts] | |||
|
56 | ||||
|
57 | if v == '<del>': | |||
|
58 | if k in env['set']: | |||
|
59 | del env['set'][k] | |||
|
60 | env['add'] = [el for el in env['add'] if el[0] != k] | |||
|
61 | env['pre'] = [el for el in env['pre'] if el[0] != k] | |||
|
62 | ||||
|
63 | print "Forgot '%s' (for next session)" % k | |||
|
64 | ||||
|
65 | elif k.endswith('+'): | |||
|
66 | k = k[:-1] | |||
|
67 | env['add'].append((k,v)) | |||
|
68 | os.environ[k] += v | |||
|
69 | print k,"after append =",os.environ[k] | |||
|
70 | elif k.endswith('-'): | |||
|
71 | k = k[:-1] | |||
|
72 | env['pre'].append((k,v)) | |||
|
73 | os.environ[k] = v + os.environ.get(k,"") | |||
|
74 | print k,"after prepend =",os.environ[k] | |||
|
75 | ||||
|
76 | ||||
|
77 | else: | |||
|
78 | env['set'][k] = v | |||
|
79 | print "Setting",k,"to",v | |||
|
80 | os.environ[k] = v | |||
|
81 | ||||
|
82 | db['stored_env'] = env | |||
|
83 | ||||
|
84 | def env_completer(self,event): | |||
|
85 | """ Custom completer that lists all env vars """ | |||
|
86 | return os.environ.keys() | |||
|
87 | ||||
|
88 | ip.expose_magic('env', persist_env) | |||
|
89 | ip.set_hook('complete_command',env_completer, str_key = '%env') | |||
|
90 |
@@ -0,0 +1,66 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ IPython extension: new prefilters for output grabbing | |||
|
3 | ||||
|
4 | Provides | |||
|
5 | ||||
|
6 | var = %magic blah blah | |||
|
7 | ||||
|
8 | var = !ls | |||
|
9 | ||||
|
10 | $Id: genutils.py 1077 2006-01-24 18:15:27Z vivainio $ | |||
|
11 | ||||
|
12 | """ | |||
|
13 | ||||
|
14 | import IPython.ipapi | |||
|
15 | from IPython.genutils import * | |||
|
16 | ||||
|
17 | ip = IPython.ipapi.get() | |||
|
18 | ||||
|
19 | import re | |||
|
20 | ||||
|
21 | def hnd_magic(line,mo): | |||
|
22 | """ Handle a = %mymagic blah blah """ | |||
|
23 | #cmd = genutils.make_quoted_expr(mo.group('syscmd')) | |||
|
24 | #mag = 'ipmagic | |||
|
25 | #return "%s = %s" | |||
|
26 | var = mo.group('varname') | |||
|
27 | cmd = mo.group('cmd') | |||
|
28 | expr = make_quoted_expr(cmd) | |||
|
29 | return itpl('$var = _ip.magic($expr)') | |||
|
30 | ||||
|
31 | def hnd_syscmd(line,mo): | |||
|
32 | """ Handle a = !ls """ | |||
|
33 | #cmd = genutils.make_quoted_expr(mo.group('syscmd')) | |||
|
34 | #mag = 'ipmagic | |||
|
35 | #return "%s = %s" | |||
|
36 | var = mo.group('varname') | |||
|
37 | cmd = mo.group('cmd') | |||
|
38 | expr = make_quoted_expr(itpl("sc -l =$cmd")) | |||
|
39 | return itpl('$var = _ip.magic($expr)') | |||
|
40 | ||||
|
41 | def install_re_handler(pat, hnd): | |||
|
42 | ip.meta.re_prefilters.append((re.compile(pat), hnd)) | |||
|
43 | ||||
|
44 | def init_handlers(): | |||
|
45 | ||||
|
46 | ip.meta.re_prefilters = [] | |||
|
47 | ||||
|
48 | install_re_handler('(?P<varname>[\w\.]+)\s*=\s*%(?P<cmd>.*)', | |||
|
49 | hnd_magic | |||
|
50 | ) | |||
|
51 | ||||
|
52 | install_re_handler('(?P<varname>[\w\.]+)\s*=\s*!(?P<cmd>.*)', | |||
|
53 | hnd_syscmd | |||
|
54 | ) | |||
|
55 | ||||
|
56 | init_handlers() | |||
|
57 | ||||
|
58 | def regex_prefilter_f(self,line): | |||
|
59 | for pat, handler in ip.meta.re_prefilters: | |||
|
60 | mo = pat.match(line) | |||
|
61 | if mo: | |||
|
62 | return handler(line,mo) | |||
|
63 | ||||
|
64 | raise IPython.ipapi.TryNext | |||
|
65 | ||||
|
66 | ip.set_hook('input_prefilter', regex_prefilter_f) |
This diff has been collapsed as it changes many lines, (1767 lines changed) Show them Hide them | |||||
@@ -0,0 +1,1767 | |||||
|
1 | # -*- coding: iso-8859-1 -*- | |||
|
2 | ||||
|
3 | import curses, fcntl, signal, struct, tty, textwrap, inspect | |||
|
4 | ||||
|
5 | from IPython import ipapi | |||
|
6 | ||||
|
7 | import astyle, ipipe | |||
|
8 | ||||
|
9 | ||||
|
10 | # Python 2.3 compatibility | |||
|
11 | try: | |||
|
12 | set | |||
|
13 | except NameError: | |||
|
14 | import sets | |||
|
15 | set = sets.Set | |||
|
16 | ||||
|
17 | # Python 2.3 compatibility | |||
|
18 | try: | |||
|
19 | sorted | |||
|
20 | except NameError: | |||
|
21 | from ipipe import sorted | |||
|
22 | ||||
|
23 | ||||
|
24 | class UnassignedKeyError(Exception): | |||
|
25 | """ | |||
|
26 | Exception that is used for reporting unassigned keys. | |||
|
27 | """ | |||
|
28 | ||||
|
29 | ||||
|
30 | class UnknownCommandError(Exception): | |||
|
31 | """ | |||
|
32 | Exception that is used for reporting unknown commands (this should never | |||
|
33 | happen). | |||
|
34 | """ | |||
|
35 | ||||
|
36 | ||||
|
37 | class CommandError(Exception): | |||
|
38 | """ | |||
|
39 | Exception that is used for reporting that a command can't be executed. | |||
|
40 | """ | |||
|
41 | ||||
|
42 | ||||
|
43 | class Keymap(dict): | |||
|
44 | """ | |||
|
45 | Stores mapping of keys to commands. | |||
|
46 | """ | |||
|
47 | def __init__(self): | |||
|
48 | self._keymap = {} | |||
|
49 | ||||
|
50 | def __setitem__(self, key, command): | |||
|
51 | if isinstance(key, str): | |||
|
52 | for c in key: | |||
|
53 | dict.__setitem__(self, ord(c), command) | |||
|
54 | else: | |||
|
55 | dict.__setitem__(self, key, command) | |||
|
56 | ||||
|
57 | def __getitem__(self, key): | |||
|
58 | if isinstance(key, str): | |||
|
59 | key = ord(key) | |||
|
60 | return dict.__getitem__(self, key) | |||
|
61 | ||||
|
62 | def __detitem__(self, key): | |||
|
63 | if isinstance(key, str): | |||
|
64 | key = ord(key) | |||
|
65 | dict.__detitem__(self, key) | |||
|
66 | ||||
|
67 | def register(self, command, *keys): | |||
|
68 | for key in keys: | |||
|
69 | self[key] = command | |||
|
70 | ||||
|
71 | def get(self, key, default=None): | |||
|
72 | if isinstance(key, str): | |||
|
73 | key = ord(key) | |||
|
74 | return dict.get(self, key, default) | |||
|
75 | ||||
|
76 | def findkey(self, command, default=ipipe.noitem): | |||
|
77 | for (key, commandcandidate) in self.iteritems(): | |||
|
78 | if commandcandidate == command: | |||
|
79 | return key | |||
|
80 | if default is ipipe.noitem: | |||
|
81 | raise KeyError(command) | |||
|
82 | return default | |||
|
83 | ||||
|
84 | ||||
|
85 | class _BrowserCachedItem(object): | |||
|
86 | # This is used internally by ``ibrowse`` to store a item together with its | |||
|
87 | # marked status. | |||
|
88 | __slots__ = ("item", "marked") | |||
|
89 | ||||
|
90 | def __init__(self, item): | |||
|
91 | self.item = item | |||
|
92 | self.marked = False | |||
|
93 | ||||
|
94 | ||||
|
95 | class _BrowserHelp(object): | |||
|
96 | style_header = astyle.Style.fromstr("yellow:black:bold") | |||
|
97 | # This is used internally by ``ibrowse`` for displaying the help screen. | |||
|
98 | def __init__(self, browser): | |||
|
99 | self.browser = browser | |||
|
100 | ||||
|
101 | def __xrepr__(self, mode): | |||
|
102 | yield (-1, True) | |||
|
103 | if mode == "header" or mode == "footer": | |||
|
104 | yield (astyle.style_default, "ibrowse help screen") | |||
|
105 | else: | |||
|
106 | yield (astyle.style_default, repr(self)) | |||
|
107 | ||||
|
108 | def __iter__(self): | |||
|
109 | # Get reverse key mapping | |||
|
110 | allkeys = {} | |||
|
111 | for (key, cmd) in self.browser.keymap.iteritems(): | |||
|
112 | allkeys.setdefault(cmd, []).append(key) | |||
|
113 | ||||
|
114 | fields = ("key", "description") | |||
|
115 | ||||
|
116 | commands = [] | |||
|
117 | for name in dir(self.browser): | |||
|
118 | if name.startswith("cmd_"): | |||
|
119 | command = getattr(self.browser, name) | |||
|
120 | commands.append((inspect.getsourcelines(command)[-1], name[4:], command)) | |||
|
121 | commands.sort() | |||
|
122 | commands = [(c[1], c[2]) for c in commands] | |||
|
123 | for (i, (name, command)) in enumerate(commands): | |||
|
124 | if i: | |||
|
125 | yield ipipe.Fields(fields, key="", description="") | |||
|
126 | ||||
|
127 | description = command.__doc__ | |||
|
128 | if description is None: | |||
|
129 | lines = [] | |||
|
130 | else: | |||
|
131 | lines = [l.strip() for l in description.splitlines() if l.strip()] | |||
|
132 | description = "\n".join(lines) | |||
|
133 | lines = textwrap.wrap(description, 60) | |||
|
134 | keys = allkeys.get(name, []) | |||
|
135 | ||||
|
136 | yield ipipe.Fields(fields, key="", description=astyle.Text((self.style_header, name))) | |||
|
137 | for i in xrange(max(len(keys), len(lines))): | |||
|
138 | try: | |||
|
139 | key = self.browser.keylabel(keys[i]) | |||
|
140 | except IndexError: | |||
|
141 | key = "" | |||
|
142 | try: | |||
|
143 | line = lines[i] | |||
|
144 | except IndexError: | |||
|
145 | line = "" | |||
|
146 | yield ipipe.Fields(fields, key=key, description=line) | |||
|
147 | ||||
|
148 | ||||
|
149 | class _BrowserLevel(object): | |||
|
150 | # This is used internally to store the state (iterator, fetch items, | |||
|
151 | # position of cursor and screen, etc.) of one browser level | |||
|
152 | # An ``ibrowse`` object keeps multiple ``_BrowserLevel`` objects in | |||
|
153 | # a stack. | |||
|
154 | def __init__(self, browser, input, mainsizey, *attrs): | |||
|
155 | self.browser = browser | |||
|
156 | self.input = input | |||
|
157 | self.header = [x for x in ipipe.xrepr(input, "header") if not isinstance(x[0], int)] | |||
|
158 | # iterator for the input | |||
|
159 | self.iterator = ipipe.xiter(input) | |||
|
160 | ||||
|
161 | # is the iterator exhausted? | |||
|
162 | self.exhausted = False | |||
|
163 | ||||
|
164 | # attributes to be display (autodetected if empty) | |||
|
165 | self.attrs = attrs | |||
|
166 | ||||
|
167 | # fetched items (+ marked flag) | |||
|
168 | self.items = ipipe.deque() | |||
|
169 | ||||
|
170 | # Number of marked objects | |||
|
171 | self.marked = 0 | |||
|
172 | ||||
|
173 | # Vertical cursor position | |||
|
174 | self.cury = 0 | |||
|
175 | ||||
|
176 | # Horizontal cursor position | |||
|
177 | self.curx = 0 | |||
|
178 | ||||
|
179 | # Index of first data column | |||
|
180 | self.datastartx = 0 | |||
|
181 | ||||
|
182 | # Index of first data line | |||
|
183 | self.datastarty = 0 | |||
|
184 | ||||
|
185 | # height of the data display area | |||
|
186 | self.mainsizey = mainsizey | |||
|
187 | ||||
|
188 | # width of the data display area (changes when scrolling) | |||
|
189 | self.mainsizex = 0 | |||
|
190 | ||||
|
191 | # Size of row number (changes when scrolling) | |||
|
192 | self.numbersizex = 0 | |||
|
193 | ||||
|
194 | # Attributes to display (in this order) | |||
|
195 | self.displayattrs = [] | |||
|
196 | ||||
|
197 | # index and attribute under the cursor | |||
|
198 | self.displayattr = (None, ipipe.noitem) | |||
|
199 | ||||
|
200 | # Maps attributes to column widths | |||
|
201 | self.colwidths = {} | |||
|
202 | ||||
|
203 | # Set of hidden attributes | |||
|
204 | self.hiddenattrs = set() | |||
|
205 | ||||
|
206 | # This takes care of all the caches etc. | |||
|
207 | self.moveto(0, 0, refresh=True) | |||
|
208 | ||||
|
209 | def fetch(self, count): | |||
|
210 | # Try to fill ``self.items`` with at least ``count`` objects. | |||
|
211 | have = len(self.items) | |||
|
212 | while not self.exhausted and have < count: | |||
|
213 | try: | |||
|
214 | item = self.iterator.next() | |||
|
215 | except StopIteration: | |||
|
216 | self.exhausted = True | |||
|
217 | break | |||
|
218 | except (KeyboardInterrupt, SystemExit): | |||
|
219 | raise | |||
|
220 | except Exception, exc: | |||
|
221 | have += 1 | |||
|
222 | self.items.append(_BrowserCachedItem(exc)) | |||
|
223 | self.exhausted = True | |||
|
224 | break | |||
|
225 | else: | |||
|
226 | have += 1 | |||
|
227 | self.items.append(_BrowserCachedItem(item)) | |||
|
228 | ||||
|
229 | def calcdisplayattrs(self): | |||
|
230 | # Calculate which attributes are available from the objects that are | |||
|
231 | # currently visible on screen (and store it in ``self.displayattrs``) | |||
|
232 | ||||
|
233 | attrs = set() | |||
|
234 | self.displayattrs = [] | |||
|
235 | if self.attrs: | |||
|
236 | # If the browser object specifies a fixed list of attributes, | |||
|
237 | # simply use it (removing hidden attributes). | |||
|
238 | for attr in self.attrs: | |||
|
239 | attr = ipipe.upgradexattr(attr) | |||
|
240 | if attr not in attrs and attr not in self.hiddenattrs: | |||
|
241 | self.displayattrs.append(attr) | |||
|
242 | attrs.add(attr) | |||
|
243 | else: | |||
|
244 | endy = min(self.datastarty+self.mainsizey, len(self.items)) | |||
|
245 | for i in xrange(self.datastarty, endy): | |||
|
246 | for attr in ipipe.xattrs(self.items[i].item, "default"): | |||
|
247 | if attr not in attrs and attr not in self.hiddenattrs: | |||
|
248 | self.displayattrs.append(attr) | |||
|
249 | attrs.add(attr) | |||
|
250 | ||||
|
251 | def getrow(self, i): | |||
|
252 | # Return a dictionary with the attributes for the object | |||
|
253 | # ``self.items[i]``. Attribute names are taken from | |||
|
254 | # ``self.displayattrs`` so ``calcdisplayattrs()`` must have been | |||
|
255 | # called before. | |||
|
256 | row = {} | |||
|
257 | item = self.items[i].item | |||
|
258 | for attr in self.displayattrs: | |||
|
259 | try: | |||
|
260 | value = attr.value(item) | |||
|
261 | except (KeyboardInterrupt, SystemExit): | |||
|
262 | raise | |||
|
263 | except Exception, exc: | |||
|
264 | value = exc | |||
|
265 | # only store attribute if it exists (or we got an exception) | |||
|
266 | if value is not ipipe.noitem: | |||
|
267 | # remember alignment, length and colored text | |||
|
268 | row[attr] = ipipe.xformat(value, "cell", self.browser.maxattrlength) | |||
|
269 | return row | |||
|
270 | ||||
|
271 | def calcwidths(self): | |||
|
272 | # Recalculate the displayed fields and their widths. | |||
|
273 | # ``calcdisplayattrs()'' must have been called and the cache | |||
|
274 | # for attributes of the objects on screen (``self.displayrows``) | |||
|
275 | # must have been filled. This sets ``self.colwidths`` which maps | |||
|
276 | # attribute descriptors to widths. | |||
|
277 | self.colwidths = {} | |||
|
278 | for row in self.displayrows: | |||
|
279 | for attr in self.displayattrs: | |||
|
280 | try: | |||
|
281 | length = row[attr][1] | |||
|
282 | except KeyError: | |||
|
283 | length = 0 | |||
|
284 | # always add attribute to colwidths, even if it doesn't exist | |||
|
285 | if attr not in self.colwidths: | |||
|
286 | self.colwidths[attr] = len(attr.name()) | |||
|
287 | newwidth = max(self.colwidths[attr], length) | |||
|
288 | self.colwidths[attr] = newwidth | |||
|
289 | ||||
|
290 | # How many characters do we need to paint the largest item number? | |||
|
291 | self.numbersizex = len(str(self.datastarty+self.mainsizey-1)) | |||
|
292 | # How must space have we got to display data? | |||
|
293 | self.mainsizex = self.browser.scrsizex-self.numbersizex-3 | |||
|
294 | # width of all columns | |||
|
295 | self.datasizex = sum(self.colwidths.itervalues()) + len(self.colwidths) | |||
|
296 | ||||
|
297 | def calcdisplayattr(self): | |||
|
298 | # Find out which attribute the cursor is on and store this | |||
|
299 | # information in ``self.displayattr``. | |||
|
300 | pos = 0 | |||
|
301 | for (i, attr) in enumerate(self.displayattrs): | |||
|
302 | if pos+self.colwidths[attr] >= self.curx: | |||
|
303 | self.displayattr = (i, attr) | |||
|
304 | break | |||
|
305 | pos += self.colwidths[attr]+1 | |||
|
306 | else: | |||
|
307 | self.displayattr = (None, ipipe.noitem) | |||
|
308 | ||||
|
309 | def moveto(self, x, y, refresh=False): | |||
|
310 | # Move the cursor to the position ``(x,y)`` (in data coordinates, | |||
|
311 | # not in screen coordinates). If ``refresh`` is true, all cached | |||
|
312 | # values will be recalculated (e.g. because the list has been | |||
|
313 | # resorted, so screen positions etc. are no longer valid). | |||
|
314 | olddatastarty = self.datastarty | |||
|
315 | oldx = self.curx | |||
|
316 | oldy = self.cury | |||
|
317 | x = int(x+0.5) | |||
|
318 | y = int(y+0.5) | |||
|
319 | newx = x # remember where we wanted to move | |||
|
320 | newy = y # remember where we wanted to move | |||
|
321 | ||||
|
322 | scrollbordery = min(self.browser.scrollbordery, self.mainsizey//2) | |||
|
323 | scrollborderx = min(self.browser.scrollborderx, self.mainsizex//2) | |||
|
324 | ||||
|
325 | # Make sure that the cursor didn't leave the main area vertically | |||
|
326 | if y < 0: | |||
|
327 | y = 0 | |||
|
328 | # try to get enough items to fill the screen | |||
|
329 | self.fetch(max(y+scrollbordery+1, self.mainsizey)) | |||
|
330 | if y >= len(self.items): | |||
|
331 | y = max(0, len(self.items)-1) | |||
|
332 | ||||
|
333 | # Make sure that the cursor stays on screen vertically | |||
|
334 | if y < self.datastarty+scrollbordery: | |||
|
335 | self.datastarty = max(0, y-scrollbordery) | |||
|
336 | elif y >= self.datastarty+self.mainsizey-scrollbordery: | |||
|
337 | self.datastarty = max(0, min(y-self.mainsizey+scrollbordery+1, | |||
|
338 | len(self.items)-self.mainsizey)) | |||
|
339 | ||||
|
340 | if refresh: # Do we need to refresh the complete display? | |||
|
341 | self.calcdisplayattrs() | |||
|
342 | endy = min(self.datastarty+self.mainsizey, len(self.items)) | |||
|
343 | self.displayrows = map(self.getrow, xrange(self.datastarty, endy)) | |||
|
344 | self.calcwidths() | |||
|
345 | # Did we scroll vertically => update displayrows | |||
|
346 | # and various other attributes | |||
|
347 | elif self.datastarty != olddatastarty: | |||
|
348 | # Recalculate which attributes we have to display | |||
|
349 | olddisplayattrs = self.displayattrs | |||
|
350 | self.calcdisplayattrs() | |||
|
351 | # If there are new attributes, recreate the cache | |||
|
352 | if self.displayattrs != olddisplayattrs: | |||
|
353 | endy = min(self.datastarty+self.mainsizey, len(self.items)) | |||
|
354 | self.displayrows = map(self.getrow, xrange(self.datastarty, endy)) | |||
|
355 | elif self.datastarty<olddatastarty: # we did scroll up | |||
|
356 | # drop rows from the end | |||
|
357 | del self.displayrows[self.datastarty-olddatastarty:] | |||
|
358 | # fetch new items | |||
|
359 | for i in xrange(min(olddatastarty, self.datastarty+self.mainsizey)-1, | |||
|
360 | self.datastarty-1, -1): | |||
|
361 | try: | |||
|
362 | row = self.getrow(i) | |||
|
363 | except IndexError: | |||
|
364 | # we didn't have enough objects to fill the screen | |||
|
365 | break | |||
|
366 | self.displayrows.insert(0, row) | |||
|
367 | else: # we did scroll down | |||
|
368 | # drop rows from the start | |||
|
369 | del self.displayrows[:self.datastarty-olddatastarty] | |||
|
370 | # fetch new items | |||
|
371 | for i in xrange(max(olddatastarty+self.mainsizey, self.datastarty), | |||
|
372 | self.datastarty+self.mainsizey): | |||
|
373 | try: | |||
|
374 | row = self.getrow(i) | |||
|
375 | except IndexError: | |||
|
376 | # we didn't have enough objects to fill the screen | |||
|
377 | break | |||
|
378 | self.displayrows.append(row) | |||
|
379 | self.calcwidths() | |||
|
380 | ||||
|
381 | # Make sure that the cursor didn't leave the data area horizontally | |||
|
382 | if x < 0: | |||
|
383 | x = 0 | |||
|
384 | elif x >= self.datasizex: | |||
|
385 | x = max(0, self.datasizex-1) | |||
|
386 | ||||
|
387 | # Make sure that the cursor stays on screen horizontally | |||
|
388 | if x < self.datastartx+scrollborderx: | |||
|
389 | self.datastartx = max(0, x-scrollborderx) | |||
|
390 | elif x >= self.datastartx+self.mainsizex-scrollborderx: | |||
|
391 | self.datastartx = max(0, min(x-self.mainsizex+scrollborderx+1, | |||
|
392 | self.datasizex-self.mainsizex)) | |||
|
393 | ||||
|
394 | if x == oldx and y == oldy and (x != newx or y != newy): # couldn't move | |||
|
395 | self.browser.beep() | |||
|
396 | else: | |||
|
397 | self.curx = x | |||
|
398 | self.cury = y | |||
|
399 | self.calcdisplayattr() | |||
|
400 | ||||
|
401 | def sort(self, key, reverse=False): | |||
|
402 | """ | |||
|
403 | Sort the currently list of items using the key function ``key``. If | |||
|
404 | ``reverse`` is true the sort order is reversed. | |||
|
405 | """ | |||
|
406 | curitem = self.items[self.cury] # Remember where the cursor is now | |||
|
407 | ||||
|
408 | # Sort items | |||
|
409 | def realkey(item): | |||
|
410 | return key(item.item) | |||
|
411 | self.items = ipipe.deque(sorted(self.items, key=realkey, reverse=reverse)) | |||
|
412 | ||||
|
413 | # Find out where the object under the cursor went | |||
|
414 | cury = self.cury | |||
|
415 | for (i, item) in enumerate(self.items): | |||
|
416 | if item is curitem: | |||
|
417 | cury = i | |||
|
418 | break | |||
|
419 | ||||
|
420 | self.moveto(self.curx, cury, refresh=True) | |||
|
421 | ||||
|
422 | def refresh(self): | |||
|
423 | """ | |||
|
424 | Restart iterating the input. | |||
|
425 | """ | |||
|
426 | self.iterator = ipipe.xiter(self.input) | |||
|
427 | self.items.clear() | |||
|
428 | self.exhausted = False | |||
|
429 | self.datastartx = self.datastarty = 0 | |||
|
430 | self.moveto(0, 0, refresh=True) | |||
|
431 | ||||
|
432 | def refreshfind(self): | |||
|
433 | """ | |||
|
434 | Restart iterating the input and go back to the same object as before | |||
|
435 | (if it can be found in the new iterator). | |||
|
436 | """ | |||
|
437 | try: | |||
|
438 | oldobject = self.items[self.cury].item | |||
|
439 | except IndexError: | |||
|
440 | oldobject = ipipe.noitem | |||
|
441 | self.iterator = ipipe.xiter(self.input) | |||
|
442 | self.items.clear() | |||
|
443 | self.exhausted = False | |||
|
444 | while True: | |||
|
445 | self.fetch(len(self.items)+1) | |||
|
446 | if self.exhausted: | |||
|
447 | curses.beep() | |||
|
448 | self.datastartx = self.datastarty = 0 | |||
|
449 | self.moveto(self.curx, 0, refresh=True) | |||
|
450 | break | |||
|
451 | if self.items[-1].item == oldobject: | |||
|
452 | self.datastartx = self.datastarty = 0 | |||
|
453 | self.moveto(self.curx, len(self.items)-1, refresh=True) | |||
|
454 | break | |||
|
455 | ||||
|
456 | ||||
|
457 | class _CommandInput(object): | |||
|
458 | keymap = Keymap() | |||
|
459 | keymap.register("left", curses.KEY_LEFT) | |||
|
460 | keymap.register("right", curses.KEY_RIGHT) | |||
|
461 | keymap.register("home", curses.KEY_HOME, "\x01") # Ctrl-A | |||
|
462 | keymap.register("end", curses.KEY_END, "\x05") # Ctrl-E | |||
|
463 | # FIXME: What's happening here? | |||
|
464 | keymap.register("backspace", curses.KEY_BACKSPACE, "\x08\x7f") | |||
|
465 | keymap.register("delete", curses.KEY_DC) | |||
|
466 | keymap.register("delend", 0x0b) # Ctrl-K | |||
|
467 | keymap.register("execute", "\r\n") | |||
|
468 | keymap.register("up", curses.KEY_UP) | |||
|
469 | keymap.register("down", curses.KEY_DOWN) | |||
|
470 | keymap.register("incsearchup", curses.KEY_PPAGE) | |||
|
471 | keymap.register("incsearchdown", curses.KEY_NPAGE) | |||
|
472 | keymap.register("exit", "\x18"), # Ctrl-X | |||
|
473 | ||||
|
474 | def __init__(self, prompt): | |||
|
475 | self.prompt = prompt | |||
|
476 | self.history = [] | |||
|
477 | self.maxhistory = 100 | |||
|
478 | self.input = "" | |||
|
479 | self.curx = 0 | |||
|
480 | self.cury = -1 # blank line | |||
|
481 | ||||
|
482 | def start(self): | |||
|
483 | self.input = "" | |||
|
484 | self.curx = 0 | |||
|
485 | self.cury = -1 # blank line | |||
|
486 | ||||
|
487 | def handlekey(self, browser, key): | |||
|
488 | cmdname = self.keymap.get(key, None) | |||
|
489 | if cmdname is not None: | |||
|
490 | cmdfunc = getattr(self, "cmd_%s" % cmdname, None) | |||
|
491 | if cmdfunc is not None: | |||
|
492 | return cmdfunc(browser) | |||
|
493 | curses.beep() | |||
|
494 | elif key != -1: | |||
|
495 | try: | |||
|
496 | char = chr(key) | |||
|
497 | except ValueError: | |||
|
498 | curses.beep() | |||
|
499 | else: | |||
|
500 | return self.handlechar(browser, char) | |||
|
501 | ||||
|
502 | def handlechar(self, browser, char): | |||
|
503 | self.input = self.input[:self.curx] + char + self.input[self.curx:] | |||
|
504 | self.curx += 1 | |||
|
505 | return True | |||
|
506 | ||||
|
507 | def dohistory(self): | |||
|
508 | self.history.insert(0, self.input) | |||
|
509 | del self.history[:-self.maxhistory] | |||
|
510 | ||||
|
511 | def cmd_backspace(self, browser): | |||
|
512 | if self.curx: | |||
|
513 | self.input = self.input[:self.curx-1] + self.input[self.curx:] | |||
|
514 | self.curx -= 1 | |||
|
515 | return True | |||
|
516 | else: | |||
|
517 | curses.beep() | |||
|
518 | ||||
|
519 | def cmd_delete(self, browser): | |||
|
520 | if self.curx<len(self.input): | |||
|
521 | self.input = self.input[:self.curx] + self.input[self.curx+1:] | |||
|
522 | return True | |||
|
523 | else: | |||
|
524 | curses.beep() | |||
|
525 | ||||
|
526 | def cmd_delend(self, browser): | |||
|
527 | if self.curx<len(self.input): | |||
|
528 | self.input = self.input[:self.curx] | |||
|
529 | return True | |||
|
530 | ||||
|
531 | def cmd_left(self, browser): | |||
|
532 | if self.curx: | |||
|
533 | self.curx -= 1 | |||
|
534 | return True | |||
|
535 | else: | |||
|
536 | curses.beep() | |||
|
537 | ||||
|
538 | def cmd_right(self, browser): | |||
|
539 | if self.curx < len(self.input): | |||
|
540 | self.curx += 1 | |||
|
541 | return True | |||
|
542 | else: | |||
|
543 | curses.beep() | |||
|
544 | ||||
|
545 | def cmd_home(self, browser): | |||
|
546 | if self.curx: | |||
|
547 | self.curx = 0 | |||
|
548 | return True | |||
|
549 | else: | |||
|
550 | curses.beep() | |||
|
551 | ||||
|
552 | def cmd_end(self, browser): | |||
|
553 | if self.curx < len(self.input): | |||
|
554 | self.curx = len(self.input) | |||
|
555 | return True | |||
|
556 | else: | |||
|
557 | curses.beep() | |||
|
558 | ||||
|
559 | def cmd_up(self, browser): | |||
|
560 | if self.cury < len(self.history)-1: | |||
|
561 | self.cury += 1 | |||
|
562 | self.input = self.history[self.cury] | |||
|
563 | self.curx = len(self.input) | |||
|
564 | return True | |||
|
565 | else: | |||
|
566 | curses.beep() | |||
|
567 | ||||
|
568 | def cmd_down(self, browser): | |||
|
569 | if self.cury >= 0: | |||
|
570 | self.cury -= 1 | |||
|
571 | if self.cury>=0: | |||
|
572 | self.input = self.history[self.cury] | |||
|
573 | else: | |||
|
574 | self.input = "" | |||
|
575 | self.curx = len(self.input) | |||
|
576 | return True | |||
|
577 | else: | |||
|
578 | curses.beep() | |||
|
579 | ||||
|
580 | def cmd_incsearchup(self, browser): | |||
|
581 | prefix = self.input[:self.curx] | |||
|
582 | cury = self.cury | |||
|
583 | while True: | |||
|
584 | cury += 1 | |||
|
585 | if cury >= len(self.history): | |||
|
586 | break | |||
|
587 | if self.history[cury].startswith(prefix): | |||
|
588 | self.input = self.history[cury] | |||
|
589 | self.cury = cury | |||
|
590 | return True | |||
|
591 | curses.beep() | |||
|
592 | ||||
|
593 | def cmd_incsearchdown(self, browser): | |||
|
594 | prefix = self.input[:self.curx] | |||
|
595 | cury = self.cury | |||
|
596 | while True: | |||
|
597 | cury -= 1 | |||
|
598 | if cury <= 0: | |||
|
599 | break | |||
|
600 | if self.history[cury].startswith(prefix): | |||
|
601 | self.input = self.history[self.cury] | |||
|
602 | self.cury = cury | |||
|
603 | return True | |||
|
604 | curses.beep() | |||
|
605 | ||||
|
606 | def cmd_exit(self, browser): | |||
|
607 | browser.mode = "default" | |||
|
608 | return True | |||
|
609 | ||||
|
610 | def cmd_execute(self, browser): | |||
|
611 | raise NotImplementedError | |||
|
612 | ||||
|
613 | ||||
|
614 | class _CommandGoto(_CommandInput): | |||
|
615 | def __init__(self): | |||
|
616 | _CommandInput.__init__(self, "goto object #") | |||
|
617 | ||||
|
618 | def handlechar(self, browser, char): | |||
|
619 | # Only accept digits | |||
|
620 | if not "0" <= char <= "9": | |||
|
621 | curses.beep() | |||
|
622 | else: | |||
|
623 | return _CommandInput.handlechar(self, browser, char) | |||
|
624 | ||||
|
625 | def cmd_execute(self, browser): | |||
|
626 | level = browser.levels[-1] | |||
|
627 | if self.input: | |||
|
628 | self.dohistory() | |||
|
629 | level.moveto(level.curx, int(self.input)) | |||
|
630 | browser.mode = "default" | |||
|
631 | return True | |||
|
632 | ||||
|
633 | ||||
|
634 | class _CommandFind(_CommandInput): | |||
|
635 | def __init__(self): | |||
|
636 | _CommandInput.__init__(self, "find expression") | |||
|
637 | ||||
|
638 | def cmd_execute(self, browser): | |||
|
639 | level = browser.levels[-1] | |||
|
640 | if self.input: | |||
|
641 | self.dohistory() | |||
|
642 | while True: | |||
|
643 | cury = level.cury | |||
|
644 | level.moveto(level.curx, cury+1) | |||
|
645 | if cury == level.cury: | |||
|
646 | curses.beep() | |||
|
647 | break # hit end | |||
|
648 | item = level.items[level.cury].item | |||
|
649 | try: | |||
|
650 | globals = ipipe.getglobals(None) | |||
|
651 | if eval(self.input, globals, ipipe.AttrNamespace(item)): | |||
|
652 | break # found something | |||
|
653 | except (KeyboardInterrupt, SystemExit): | |||
|
654 | raise | |||
|
655 | except Exception, exc: | |||
|
656 | browser.report(exc) | |||
|
657 | curses.beep() | |||
|
658 | break # break on error | |||
|
659 | browser.mode = "default" | |||
|
660 | return True | |||
|
661 | ||||
|
662 | ||||
|
663 | class _CommandFindBackwards(_CommandInput): | |||
|
664 | def __init__(self): | |||
|
665 | _CommandInput.__init__(self, "find backwards expression") | |||
|
666 | ||||
|
667 | def cmd_execute(self, browser): | |||
|
668 | level = browser.levels[-1] | |||
|
669 | if self.input: | |||
|
670 | self.dohistory() | |||
|
671 | while level.cury: | |||
|
672 | level.moveto(level.curx, level.cury-1) | |||
|
673 | item = level.items[level.cury].item | |||
|
674 | try: | |||
|
675 | globals = ipipe.getglobals(None) | |||
|
676 | if eval(self.input, globals, ipipe.AttrNamespace(item)): | |||
|
677 | break # found something | |||
|
678 | except (KeyboardInterrupt, SystemExit): | |||
|
679 | raise | |||
|
680 | except Exception, exc: | |||
|
681 | browser.report(exc) | |||
|
682 | curses.beep() | |||
|
683 | break # break on error | |||
|
684 | else: | |||
|
685 | curses.beep() | |||
|
686 | browser.mode = "default" | |||
|
687 | return True | |||
|
688 | ||||
|
689 | ||||
|
690 | class ibrowse(ipipe.Display): | |||
|
691 | # Show this many lines from the previous screen when paging horizontally | |||
|
692 | pageoverlapx = 1 | |||
|
693 | ||||
|
694 | # Show this many lines from the previous screen when paging vertically | |||
|
695 | pageoverlapy = 1 | |||
|
696 | ||||
|
697 | # Start scrolling when the cursor is less than this number of columns | |||
|
698 | # away from the left or right screen edge | |||
|
699 | scrollborderx = 10 | |||
|
700 | ||||
|
701 | # Start scrolling when the cursor is less than this number of lines | |||
|
702 | # away from the top or bottom screen edge | |||
|
703 | scrollbordery = 5 | |||
|
704 | ||||
|
705 | # Accelerate by this factor when scrolling horizontally | |||
|
706 | acceleratex = 1.05 | |||
|
707 | ||||
|
708 | # Accelerate by this factor when scrolling vertically | |||
|
709 | acceleratey = 1.05 | |||
|
710 | ||||
|
711 | # The maximum horizontal scroll speed | |||
|
712 | # (as a factor of the screen width (i.e. 0.5 == half a screen width) | |||
|
713 | maxspeedx = 0.5 | |||
|
714 | ||||
|
715 | # The maximum vertical scroll speed | |||
|
716 | # (as a factor of the screen height (i.e. 0.5 == half a screen height) | |||
|
717 | maxspeedy = 0.5 | |||
|
718 | ||||
|
719 | # The maximum number of header lines for browser level | |||
|
720 | # if the nesting is deeper, only the innermost levels are displayed | |||
|
721 | maxheaders = 5 | |||
|
722 | ||||
|
723 | # The approximate maximum length of a column entry | |||
|
724 | maxattrlength = 200 | |||
|
725 | ||||
|
726 | # Styles for various parts of the GUI | |||
|
727 | style_objheadertext = astyle.Style.fromstr("white:black:bold|reverse") | |||
|
728 | style_objheadernumber = astyle.Style.fromstr("white:blue:bold|reverse") | |||
|
729 | style_objheaderobject = astyle.Style.fromstr("white:black:reverse") | |||
|
730 | style_colheader = astyle.Style.fromstr("blue:white:reverse") | |||
|
731 | style_colheaderhere = astyle.Style.fromstr("green:black:bold|reverse") | |||
|
732 | style_colheadersep = astyle.Style.fromstr("blue:black:reverse") | |||
|
733 | style_number = astyle.Style.fromstr("blue:white:reverse") | |||
|
734 | style_numberhere = astyle.Style.fromstr("green:black:bold|reverse") | |||
|
735 | style_sep = astyle.Style.fromstr("blue:black") | |||
|
736 | style_data = astyle.Style.fromstr("white:black") | |||
|
737 | style_datapad = astyle.Style.fromstr("blue:black:bold") | |||
|
738 | style_footer = astyle.Style.fromstr("black:white") | |||
|
739 | style_report = astyle.Style.fromstr("white:black") | |||
|
740 | ||||
|
741 | # Column separator in header | |||
|
742 | headersepchar = "|" | |||
|
743 | ||||
|
744 | # Character for padding data cell entries | |||
|
745 | datapadchar = "." | |||
|
746 | ||||
|
747 | # Column separator in data area | |||
|
748 | datasepchar = "|" | |||
|
749 | ||||
|
750 | # Character to use for "empty" cell (i.e. for non-existing attributes) | |||
|
751 | nodatachar = "-" | |||
|
752 | ||||
|
753 | # Prompts for modes that require keyboard input | |||
|
754 | prompts = { | |||
|
755 | "goto": _CommandGoto(), | |||
|
756 | "find": _CommandFind(), | |||
|
757 | "findbackwards": _CommandFindBackwards() | |||
|
758 | } | |||
|
759 | ||||
|
760 | # Maps curses key codes to "function" names | |||
|
761 | keymap = Keymap() | |||
|
762 | keymap.register("quit", "q") | |||
|
763 | keymap.register("up", curses.KEY_UP) | |||
|
764 | keymap.register("down", curses.KEY_DOWN) | |||
|
765 | keymap.register("pageup", curses.KEY_PPAGE) | |||
|
766 | keymap.register("pagedown", curses.KEY_NPAGE) | |||
|
767 | keymap.register("left", curses.KEY_LEFT) | |||
|
768 | keymap.register("right", curses.KEY_RIGHT) | |||
|
769 | keymap.register("home", curses.KEY_HOME, "\x01") | |||
|
770 | keymap.register("end", curses.KEY_END, "\x05") | |||
|
771 | keymap.register("prevattr", "<\x1b") | |||
|
772 | keymap.register("nextattr", ">\t") | |||
|
773 | keymap.register("pick", "p") | |||
|
774 | keymap.register("pickattr", "P") | |||
|
775 | keymap.register("pickallattrs", "C") | |||
|
776 | keymap.register("pickmarked", "m") | |||
|
777 | keymap.register("pickmarkedattr", "M") | |||
|
778 | keymap.register("pickinput", "i") | |||
|
779 | keymap.register("pickinputattr", "I") | |||
|
780 | keymap.register("hideattr", "h") | |||
|
781 | keymap.register("unhideattrs", "H") | |||
|
782 | keymap.register("help", "?") | |||
|
783 | keymap.register("enter", "\r\n") | |||
|
784 | keymap.register("enterattr", "E") | |||
|
785 | # FIXME: What's happening here? | |||
|
786 | keymap.register("leave", curses.KEY_BACKSPACE, "x\x08\x7f") | |||
|
787 | keymap.register("detail", "d") | |||
|
788 | keymap.register("detailattr", "D") | |||
|
789 | keymap.register("tooglemark", " ") | |||
|
790 | keymap.register("markrange", "%") | |||
|
791 | keymap.register("sortattrasc", "v") | |||
|
792 | keymap.register("sortattrdesc", "V") | |||
|
793 | keymap.register("goto", "g") | |||
|
794 | keymap.register("find", "f") | |||
|
795 | keymap.register("findbackwards", "b") | |||
|
796 | keymap.register("refresh", "r") | |||
|
797 | keymap.register("refreshfind", "R") | |||
|
798 | ||||
|
799 | def __init__(self, input=None, *attrs): | |||
|
800 | """ | |||
|
801 | Create a new browser. If ``attrs`` is not empty, it is the list | |||
|
802 | of attributes that will be displayed in the browser, otherwise | |||
|
803 | these will be determined by the objects on screen. | |||
|
804 | """ | |||
|
805 | ipipe.Display.__init__(self, input) | |||
|
806 | ||||
|
807 | self.attrs = attrs | |||
|
808 | ||||
|
809 | # Stack of browser levels | |||
|
810 | self.levels = [] | |||
|
811 | # how many colums to scroll (Changes when accelerating) | |||
|
812 | self.stepx = 1. | |||
|
813 | ||||
|
814 | # how many rows to scroll (Changes when accelerating) | |||
|
815 | self.stepy = 1. | |||
|
816 | ||||
|
817 | # Beep on the edges of the data area? (Will be set to ``False`` | |||
|
818 | # once the cursor hits the edge of the screen, so we don't get | |||
|
819 | # multiple beeps). | |||
|
820 | self._dobeep = True | |||
|
821 | ||||
|
822 | # Cache for registered ``curses`` colors and styles. | |||
|
823 | self._styles = {} | |||
|
824 | self._colors = {} | |||
|
825 | self._maxcolor = 1 | |||
|
826 | ||||
|
827 | # How many header lines do we want to paint (the numbers of levels | |||
|
828 | # we have, but with an upper bound) | |||
|
829 | self._headerlines = 1 | |||
|
830 | ||||
|
831 | # Index of first header line | |||
|
832 | self._firstheaderline = 0 | |||
|
833 | ||||
|
834 | # curses window | |||
|
835 | self.scr = None | |||
|
836 | # report in the footer line (error, executed command etc.) | |||
|
837 | self._report = None | |||
|
838 | ||||
|
839 | # value to be returned to the caller (set by commands) | |||
|
840 | self.returnvalue = None | |||
|
841 | ||||
|
842 | # The mode the browser is in | |||
|
843 | # e.g. normal browsing or entering an argument for a command | |||
|
844 | self.mode = "default" | |||
|
845 | ||||
|
846 | # set by the SIGWINCH signal handler | |||
|
847 | self.resized = False | |||
|
848 | ||||
|
849 | def nextstepx(self, step): | |||
|
850 | """ | |||
|
851 | Accelerate horizontally. | |||
|
852 | """ | |||
|
853 | return max(1., min(step*self.acceleratex, | |||
|
854 | self.maxspeedx*self.levels[-1].mainsizex)) | |||
|
855 | ||||
|
856 | def nextstepy(self, step): | |||
|
857 | """ | |||
|
858 | Accelerate vertically. | |||
|
859 | """ | |||
|
860 | return max(1., min(step*self.acceleratey, | |||
|
861 | self.maxspeedy*self.levels[-1].mainsizey)) | |||
|
862 | ||||
|
863 | def getstyle(self, style): | |||
|
864 | """ | |||
|
865 | Register the ``style`` with ``curses`` or get it from the cache, | |||
|
866 | if it has been registered before. | |||
|
867 | """ | |||
|
868 | try: | |||
|
869 | return self._styles[style.fg, style.bg, style.attrs] | |||
|
870 | except KeyError: | |||
|
871 | attrs = 0 | |||
|
872 | for b in astyle.A2CURSES: | |||
|
873 | if style.attrs & b: | |||
|
874 | attrs |= astyle.A2CURSES[b] | |||
|
875 | try: | |||
|
876 | color = self._colors[style.fg, style.bg] | |||
|
877 | except KeyError: | |||
|
878 | curses.init_pair( | |||
|
879 | self._maxcolor, | |||
|
880 | astyle.COLOR2CURSES[style.fg], | |||
|
881 | astyle.COLOR2CURSES[style.bg] | |||
|
882 | ) | |||
|
883 | color = curses.color_pair(self._maxcolor) | |||
|
884 | self._colors[style.fg, style.bg] = color | |||
|
885 | self._maxcolor += 1 | |||
|
886 | c = color | attrs | |||
|
887 | self._styles[style.fg, style.bg, style.attrs] = c | |||
|
888 | return c | |||
|
889 | ||||
|
890 | def addstr(self, y, x, begx, endx, text, style): | |||
|
891 | """ | |||
|
892 | A version of ``curses.addstr()`` that can handle ``x`` coordinates | |||
|
893 | that are outside the screen. | |||
|
894 | """ | |||
|
895 | text2 = text[max(0, begx-x):max(0, endx-x)] | |||
|
896 | if text2: | |||
|
897 | self.scr.addstr(y, max(x, begx), text2, self.getstyle(style)) | |||
|
898 | return len(text) | |||
|
899 | ||||
|
900 | def addchr(self, y, x, begx, endx, c, l, style): | |||
|
901 | x0 = max(x, begx) | |||
|
902 | x1 = min(x+l, endx) | |||
|
903 | if x1>x0: | |||
|
904 | self.scr.addstr(y, x0, c*(x1-x0), self.getstyle(style)) | |||
|
905 | return l | |||
|
906 | ||||
|
907 | def _calcheaderlines(self, levels): | |||
|
908 | # Calculate how many headerlines do we have to display, if we have | |||
|
909 | # ``levels`` browser levels | |||
|
910 | if levels is None: | |||
|
911 | levels = len(self.levels) | |||
|
912 | self._headerlines = min(self.maxheaders, levels) | |||
|
913 | self._firstheaderline = levels-self._headerlines | |||
|
914 | ||||
|
915 | def getstylehere(self, style): | |||
|
916 | """ | |||
|
917 | Return a style for displaying the original style ``style`` | |||
|
918 | in the row the cursor is on. | |||
|
919 | """ | |||
|
920 | return astyle.Style(style.fg, astyle.COLOR_BLUE, style.attrs | astyle.A_BOLD) | |||
|
921 | ||||
|
922 | def report(self, msg): | |||
|
923 | """ | |||
|
924 | Store the message ``msg`` for display below the footer line. This | |||
|
925 | will be displayed as soon as the screen is redrawn. | |||
|
926 | """ | |||
|
927 | self._report = msg | |||
|
928 | ||||
|
929 | def enter(self, item, *attrs): | |||
|
930 | """ | |||
|
931 | Enter the object ``item``. If ``attrs`` is specified, it will be used | |||
|
932 | as a fixed list of attributes to display. | |||
|
933 | """ | |||
|
934 | if self.levels and item is self.levels[-1].input: | |||
|
935 | curses.beep() | |||
|
936 | self.report(CommandError("Recursion on input object")) | |||
|
937 | else: | |||
|
938 | oldlevels = len(self.levels) | |||
|
939 | self._calcheaderlines(oldlevels+1) | |||
|
940 | try: | |||
|
941 | level = _BrowserLevel( | |||
|
942 | self, | |||
|
943 | item, | |||
|
944 | self.scrsizey-1-self._headerlines-2, | |||
|
945 | *attrs | |||
|
946 | ) | |||
|
947 | except (KeyboardInterrupt, SystemExit): | |||
|
948 | raise | |||
|
949 | except Exception, exc: | |||
|
950 | if not self.levels: | |||
|
951 | raise | |||
|
952 | self._calcheaderlines(oldlevels) | |||
|
953 | curses.beep() | |||
|
954 | self.report(exc) | |||
|
955 | else: | |||
|
956 | self.levels.append(level) | |||
|
957 | ||||
|
958 | def startkeyboardinput(self, mode): | |||
|
959 | """ | |||
|
960 | Enter mode ``mode``, which requires keyboard input. | |||
|
961 | """ | |||
|
962 | self.mode = mode | |||
|
963 | self.prompts[mode].start() | |||
|
964 | ||||
|
965 | def keylabel(self, keycode): | |||
|
966 | """ | |||
|
967 | Return a pretty name for the ``curses`` key ``keycode`` (used in the | |||
|
968 | help screen and in reports about unassigned keys). | |||
|
969 | """ | |||
|
970 | if keycode <= 0xff: | |||
|
971 | specialsnames = { | |||
|
972 | ord("\n"): "RETURN", | |||
|
973 | ord(" "): "SPACE", | |||
|
974 | ord("\t"): "TAB", | |||
|
975 | ord("\x7f"): "DELETE", | |||
|
976 | ord("\x08"): "BACKSPACE", | |||
|
977 | } | |||
|
978 | if keycode in specialsnames: | |||
|
979 | return specialsnames[keycode] | |||
|
980 | elif 0x00 < keycode < 0x20: | |||
|
981 | return "CTRL-%s" % chr(keycode + 64) | |||
|
982 | return repr(chr(keycode)) | |||
|
983 | for name in dir(curses): | |||
|
984 | if name.startswith("KEY_") and getattr(curses, name) == keycode: | |||
|
985 | return name | |||
|
986 | return str(keycode) | |||
|
987 | ||||
|
988 | def beep(self, force=False): | |||
|
989 | if force or self._dobeep: | |||
|
990 | curses.beep() | |||
|
991 | # don't beep again (as long as the same key is pressed) | |||
|
992 | self._dobeep = False | |||
|
993 | ||||
|
994 | def cmd_up(self): | |||
|
995 | """ | |||
|
996 | Move the cursor to the previous row. | |||
|
997 | """ | |||
|
998 | level = self.levels[-1] | |||
|
999 | self.report("up") | |||
|
1000 | level.moveto(level.curx, level.cury-self.stepy) | |||
|
1001 | ||||
|
1002 | def cmd_down(self): | |||
|
1003 | """ | |||
|
1004 | Move the cursor to the next row. | |||
|
1005 | """ | |||
|
1006 | level = self.levels[-1] | |||
|
1007 | self.report("down") | |||
|
1008 | level.moveto(level.curx, level.cury+self.stepy) | |||
|
1009 | ||||
|
1010 | def cmd_pageup(self): | |||
|
1011 | """ | |||
|
1012 | Move the cursor up one page. | |||
|
1013 | """ | |||
|
1014 | level = self.levels[-1] | |||
|
1015 | self.report("page up") | |||
|
1016 | level.moveto(level.curx, level.cury-level.mainsizey+self.pageoverlapy) | |||
|
1017 | ||||
|
1018 | def cmd_pagedown(self): | |||
|
1019 | """ | |||
|
1020 | Move the cursor down one page. | |||
|
1021 | """ | |||
|
1022 | level = self.levels[-1] | |||
|
1023 | self.report("page down") | |||
|
1024 | level.moveto(level.curx, level.cury+level.mainsizey-self.pageoverlapy) | |||
|
1025 | ||||
|
1026 | def cmd_left(self): | |||
|
1027 | """ | |||
|
1028 | Move the cursor left. | |||
|
1029 | """ | |||
|
1030 | level = self.levels[-1] | |||
|
1031 | self.report("left") | |||
|
1032 | level.moveto(level.curx-self.stepx, level.cury) | |||
|
1033 | ||||
|
1034 | def cmd_right(self): | |||
|
1035 | """ | |||
|
1036 | Move the cursor right. | |||
|
1037 | """ | |||
|
1038 | level = self.levels[-1] | |||
|
1039 | self.report("right") | |||
|
1040 | level.moveto(level.curx+self.stepx, level.cury) | |||
|
1041 | ||||
|
1042 | def cmd_home(self): | |||
|
1043 | """ | |||
|
1044 | Move the cursor to the first column. | |||
|
1045 | """ | |||
|
1046 | level = self.levels[-1] | |||
|
1047 | self.report("home") | |||
|
1048 | level.moveto(0, level.cury) | |||
|
1049 | ||||
|
1050 | def cmd_end(self): | |||
|
1051 | """ | |||
|
1052 | Move the cursor to the last column. | |||
|
1053 | """ | |||
|
1054 | level = self.levels[-1] | |||
|
1055 | self.report("end") | |||
|
1056 | level.moveto(level.datasizex+level.mainsizey-self.pageoverlapx, level.cury) | |||
|
1057 | ||||
|
1058 | def cmd_prevattr(self): | |||
|
1059 | """ | |||
|
1060 | Move the cursor one attribute column to the left. | |||
|
1061 | """ | |||
|
1062 | level = self.levels[-1] | |||
|
1063 | if level.displayattr[0] is None or level.displayattr[0] == 0: | |||
|
1064 | self.beep() | |||
|
1065 | else: | |||
|
1066 | self.report("prevattr") | |||
|
1067 | pos = 0 | |||
|
1068 | for (i, attrname) in enumerate(level.displayattrs): | |||
|
1069 | if i == level.displayattr[0]-1: | |||
|
1070 | break | |||
|
1071 | pos += level.colwidths[attrname] + 1 | |||
|
1072 | level.moveto(pos, level.cury) | |||
|
1073 | ||||
|
1074 | def cmd_nextattr(self): | |||
|
1075 | """ | |||
|
1076 | Move the cursor one attribute column to the right. | |||
|
1077 | """ | |||
|
1078 | level = self.levels[-1] | |||
|
1079 | if level.displayattr[0] is None or level.displayattr[0] == len(level.displayattrs)-1: | |||
|
1080 | self.beep() | |||
|
1081 | else: | |||
|
1082 | self.report("nextattr") | |||
|
1083 | pos = 0 | |||
|
1084 | for (i, attrname) in enumerate(level.displayattrs): | |||
|
1085 | if i == level.displayattr[0]+1: | |||
|
1086 | break | |||
|
1087 | pos += level.colwidths[attrname] + 1 | |||
|
1088 | level.moveto(pos, level.cury) | |||
|
1089 | ||||
|
1090 | def cmd_pick(self): | |||
|
1091 | """ | |||
|
1092 | 'Pick' the object under the cursor (i.e. the row the cursor is on). | |||
|
1093 | This leaves the browser and returns the picked object to the caller. | |||
|
1094 | (In IPython this object will be available as the ``_`` variable.) | |||
|
1095 | """ | |||
|
1096 | level = self.levels[-1] | |||
|
1097 | self.returnvalue = level.items[level.cury].item | |||
|
1098 | return True | |||
|
1099 | ||||
|
1100 | def cmd_pickattr(self): | |||
|
1101 | """ | |||
|
1102 | 'Pick' the attribute under the cursor (i.e. the row/column the | |||
|
1103 | cursor is on). | |||
|
1104 | """ | |||
|
1105 | level = self.levels[-1] | |||
|
1106 | attr = level.displayattr[1] | |||
|
1107 | if attr is ipipe.noitem: | |||
|
1108 | curses.beep() | |||
|
1109 | self.report(CommandError("no column under cursor")) | |||
|
1110 | return | |||
|
1111 | value = attr.value(level.items[level.cury].item) | |||
|
1112 | if value is ipipe.noitem: | |||
|
1113 | curses.beep() | |||
|
1114 | self.report(AttributeError(attr.name())) | |||
|
1115 | else: | |||
|
1116 | self.returnvalue = value | |||
|
1117 | return True | |||
|
1118 | ||||
|
1119 | def cmd_pickallattrs(self): | |||
|
1120 | """ | |||
|
1121 | Pick' the complete column under the cursor (i.e. the attribute under | |||
|
1122 | the cursor) from all currently fetched objects. These attributes | |||
|
1123 | will be returned as a list. | |||
|
1124 | """ | |||
|
1125 | level = self.levels[-1] | |||
|
1126 | attr = level.displayattr[1] | |||
|
1127 | if attr is ipipe.noitem: | |||
|
1128 | curses.beep() | |||
|
1129 | self.report(CommandError("no column under cursor")) | |||
|
1130 | return | |||
|
1131 | result = [] | |||
|
1132 | for cache in level.items: | |||
|
1133 | value = attr.value(cache.item) | |||
|
1134 | if value is not ipipe.noitem: | |||
|
1135 | result.append(value) | |||
|
1136 | self.returnvalue = result | |||
|
1137 | return True | |||
|
1138 | ||||
|
1139 | def cmd_pickmarked(self): | |||
|
1140 | """ | |||
|
1141 | 'Pick' marked objects. Marked objects will be returned as a list. | |||
|
1142 | """ | |||
|
1143 | level = self.levels[-1] | |||
|
1144 | self.returnvalue = [cache.item for cache in level.items if cache.marked] | |||
|
1145 | return True | |||
|
1146 | ||||
|
1147 | def cmd_pickmarkedattr(self): | |||
|
1148 | """ | |||
|
1149 | 'Pick' the attribute under the cursor from all marked objects | |||
|
1150 | (This returns a list). | |||
|
1151 | """ | |||
|
1152 | ||||
|
1153 | level = self.levels[-1] | |||
|
1154 | attr = level.displayattr[1] | |||
|
1155 | if attr is ipipe.noitem: | |||
|
1156 | curses.beep() | |||
|
1157 | self.report(CommandError("no column under cursor")) | |||
|
1158 | return | |||
|
1159 | result = [] | |||
|
1160 | for cache in level.items: | |||
|
1161 | if cache.marked: | |||
|
1162 | value = attr.value(cache.item) | |||
|
1163 | if value is not ipipe.noitem: | |||
|
1164 | result.append(value) | |||
|
1165 | self.returnvalue = result | |||
|
1166 | return True | |||
|
1167 | ||||
|
1168 | def cmd_pickinput(self): | |||
|
1169 | """ | |||
|
1170 | Use the object under the cursor (i.e. the row the cursor is on) as | |||
|
1171 | the next input line. This leaves the browser and puts the picked object | |||
|
1172 | in the input. | |||
|
1173 | """ | |||
|
1174 | level = self.levels[-1] | |||
|
1175 | value = level.items[level.cury].item | |||
|
1176 | self.returnvalue = None | |||
|
1177 | api = ipapi.get() | |||
|
1178 | api.set_next_input(str(value)) | |||
|
1179 | return True | |||
|
1180 | ||||
|
1181 | def cmd_pickinputattr(self): | |||
|
1182 | """ | |||
|
1183 | Use the attribute under the cursor i.e. the row/column the cursor is on) | |||
|
1184 | as the next input line. This leaves the browser and puts the picked | |||
|
1185 | object in the input. | |||
|
1186 | """ | |||
|
1187 | level = self.levels[-1] | |||
|
1188 | attr = level.displayattr[1] | |||
|
1189 | if attr is ipipe.noitem: | |||
|
1190 | curses.beep() | |||
|
1191 | self.report(CommandError("no column under cursor")) | |||
|
1192 | return | |||
|
1193 | value = attr.value(level.items[level.cury].item) | |||
|
1194 | if value is ipipe.noitem: | |||
|
1195 | curses.beep() | |||
|
1196 | self.report(AttributeError(attr.name())) | |||
|
1197 | self.returnvalue = None | |||
|
1198 | api = ipapi.get() | |||
|
1199 | api.set_next_input(str(value)) | |||
|
1200 | return True | |||
|
1201 | ||||
|
1202 | def cmd_markrange(self): | |||
|
1203 | """ | |||
|
1204 | Mark all objects from the last marked object before the current cursor | |||
|
1205 | position to the cursor position. | |||
|
1206 | """ | |||
|
1207 | level = self.levels[-1] | |||
|
1208 | self.report("markrange") | |||
|
1209 | start = None | |||
|
1210 | if level.items: | |||
|
1211 | for i in xrange(level.cury, -1, -1): | |||
|
1212 | if level.items[i].marked: | |||
|
1213 | start = i | |||
|
1214 | break | |||
|
1215 | if start is None: | |||
|
1216 | self.report(CommandError("no mark before cursor")) | |||
|
1217 | curses.beep() | |||
|
1218 | else: | |||
|
1219 | for i in xrange(start, level.cury+1): | |||
|
1220 | cache = level.items[i] | |||
|
1221 | if not cache.marked: | |||
|
1222 | cache.marked = True | |||
|
1223 | level.marked += 1 | |||
|
1224 | ||||
|
1225 | def cmd_enter(self): | |||
|
1226 | """ | |||
|
1227 | Enter the object under the cursor. (what this mean depends on the object | |||
|
1228 | itself (i.e. how it implements iteration). This opens a new browser 'level'. | |||
|
1229 | """ | |||
|
1230 | level = self.levels[-1] | |||
|
1231 | try: | |||
|
1232 | item = level.items[level.cury].item | |||
|
1233 | except IndexError: | |||
|
1234 | self.report(CommandError("No object")) | |||
|
1235 | curses.beep() | |||
|
1236 | else: | |||
|
1237 | self.report("entering object...") | |||
|
1238 | self.enter(item) | |||
|
1239 | ||||
|
1240 | def cmd_leave(self): | |||
|
1241 | """ | |||
|
1242 | Leave the current browser level and go back to the previous one. | |||
|
1243 | """ | |||
|
1244 | self.report("leave") | |||
|
1245 | if len(self.levels) > 1: | |||
|
1246 | self._calcheaderlines(len(self.levels)-1) | |||
|
1247 | self.levels.pop(-1) | |||
|
1248 | else: | |||
|
1249 | self.report(CommandError("This is the last level")) | |||
|
1250 | curses.beep() | |||
|
1251 | ||||
|
1252 | def cmd_enterattr(self): | |||
|
1253 | """ | |||
|
1254 | Enter the attribute under the cursor. | |||
|
1255 | """ | |||
|
1256 | level = self.levels[-1] | |||
|
1257 | attr = level.displayattr[1] | |||
|
1258 | if attr is ipipe.noitem: | |||
|
1259 | curses.beep() | |||
|
1260 | self.report(CommandError("no column under cursor")) | |||
|
1261 | return | |||
|
1262 | try: | |||
|
1263 | item = level.items[level.cury].item | |||
|
1264 | except IndexError: | |||
|
1265 | self.report(CommandError("No object")) | |||
|
1266 | curses.beep() | |||
|
1267 | else: | |||
|
1268 | value = attr.value(item) | |||
|
1269 | name = attr.name() | |||
|
1270 | if value is ipipe.noitem: | |||
|
1271 | self.report(AttributeError(name)) | |||
|
1272 | else: | |||
|
1273 | self.report("entering object attribute %s..." % name) | |||
|
1274 | self.enter(value) | |||
|
1275 | ||||
|
1276 | def cmd_detail(self): | |||
|
1277 | """ | |||
|
1278 | Show a detail view of the object under the cursor. This shows the | |||
|
1279 | name, type, doc string and value of the object attributes (and it | |||
|
1280 | might show more attributes than in the list view, depending on | |||
|
1281 | the object). | |||
|
1282 | """ | |||
|
1283 | level = self.levels[-1] | |||
|
1284 | try: | |||
|
1285 | item = level.items[level.cury].item | |||
|
1286 | except IndexError: | |||
|
1287 | self.report(CommandError("No object")) | |||
|
1288 | curses.beep() | |||
|
1289 | else: | |||
|
1290 | self.report("entering detail view for object...") | |||
|
1291 | attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")] | |||
|
1292 | self.enter(attrs) | |||
|
1293 | ||||
|
1294 | def cmd_detailattr(self): | |||
|
1295 | """ | |||
|
1296 | Show a detail view of the attribute under the cursor. | |||
|
1297 | """ | |||
|
1298 | level = self.levels[-1] | |||
|
1299 | attr = level.displayattr[1] | |||
|
1300 | if attr is ipipe.noitem: | |||
|
1301 | curses.beep() | |||
|
1302 | self.report(CommandError("no attribute")) | |||
|
1303 | return | |||
|
1304 | try: | |||
|
1305 | item = level.items[level.cury].item | |||
|
1306 | except IndexError: | |||
|
1307 | self.report(CommandError("No object")) | |||
|
1308 | curses.beep() | |||
|
1309 | else: | |||
|
1310 | try: | |||
|
1311 | item = attr.value(item) | |||
|
1312 | except (KeyboardInterrupt, SystemExit): | |||
|
1313 | raise | |||
|
1314 | except Exception, exc: | |||
|
1315 | self.report(exc) | |||
|
1316 | else: | |||
|
1317 | self.report("entering detail view for attribute %s..." % attr.name()) | |||
|
1318 | attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")] | |||
|
1319 | self.enter(attrs) | |||
|
1320 | ||||
|
1321 | def cmd_tooglemark(self): | |||
|
1322 | """ | |||
|
1323 | Mark/unmark the object under the cursor. Marked objects have a '!' | |||
|
1324 | after the row number). | |||
|
1325 | """ | |||
|
1326 | level = self.levels[-1] | |||
|
1327 | self.report("toggle mark") | |||
|
1328 | try: | |||
|
1329 | item = level.items[level.cury] | |||
|
1330 | except IndexError: # no items? | |||
|
1331 | pass | |||
|
1332 | else: | |||
|
1333 | if item.marked: | |||
|
1334 | item.marked = False | |||
|
1335 | level.marked -= 1 | |||
|
1336 | else: | |||
|
1337 | item.marked = True | |||
|
1338 | level.marked += 1 | |||
|
1339 | ||||
|
1340 | def cmd_sortattrasc(self): | |||
|
1341 | """ | |||
|
1342 | Sort the objects (in ascending order) using the attribute under | |||
|
1343 | the cursor as the sort key. | |||
|
1344 | """ | |||
|
1345 | level = self.levels[-1] | |||
|
1346 | attr = level.displayattr[1] | |||
|
1347 | if attr is ipipe.noitem: | |||
|
1348 | curses.beep() | |||
|
1349 | self.report(CommandError("no column under cursor")) | |||
|
1350 | return | |||
|
1351 | self.report("sort by %s (ascending)" % attr.name()) | |||
|
1352 | def key(item): | |||
|
1353 | try: | |||
|
1354 | return attr.value(item) | |||
|
1355 | except (KeyboardInterrupt, SystemExit): | |||
|
1356 | raise | |||
|
1357 | except Exception: | |||
|
1358 | return None | |||
|
1359 | level.sort(key) | |||
|
1360 | ||||
|
1361 | def cmd_sortattrdesc(self): | |||
|
1362 | """ | |||
|
1363 | Sort the objects (in descending order) using the attribute under | |||
|
1364 | the cursor as the sort key. | |||
|
1365 | """ | |||
|
1366 | level = self.levels[-1] | |||
|
1367 | attr = level.displayattr[1] | |||
|
1368 | if attr is ipipe.noitem: | |||
|
1369 | curses.beep() | |||
|
1370 | self.report(CommandError("no column under cursor")) | |||
|
1371 | return | |||
|
1372 | self.report("sort by %s (descending)" % attr.name()) | |||
|
1373 | def key(item): | |||
|
1374 | try: | |||
|
1375 | return attr.value(item) | |||
|
1376 | except (KeyboardInterrupt, SystemExit): | |||
|
1377 | raise | |||
|
1378 | except Exception: | |||
|
1379 | return None | |||
|
1380 | level.sort(key, reverse=True) | |||
|
1381 | ||||
|
1382 | def cmd_hideattr(self): | |||
|
1383 | """ | |||
|
1384 | Hide the attribute under the cursor. | |||
|
1385 | """ | |||
|
1386 | level = self.levels[-1] | |||
|
1387 | if level.displayattr[0] is None: | |||
|
1388 | self.beep() | |||
|
1389 | else: | |||
|
1390 | self.report("hideattr") | |||
|
1391 | level.hiddenattrs.add(level.displayattr[1]) | |||
|
1392 | level.moveto(level.curx, level.cury, refresh=True) | |||
|
1393 | ||||
|
1394 | def cmd_unhideattrs(self): | |||
|
1395 | """ | |||
|
1396 | Make all attributes visible again. | |||
|
1397 | """ | |||
|
1398 | level = self.levels[-1] | |||
|
1399 | self.report("unhideattrs") | |||
|
1400 | level.hiddenattrs.clear() | |||
|
1401 | level.moveto(level.curx, level.cury, refresh=True) | |||
|
1402 | ||||
|
1403 | def cmd_goto(self): | |||
|
1404 | """ | |||
|
1405 | Jump to a row. The row number can be entered at the | |||
|
1406 | bottom of the screen. | |||
|
1407 | """ | |||
|
1408 | self.startkeyboardinput("goto") | |||
|
1409 | ||||
|
1410 | def cmd_find(self): | |||
|
1411 | """ | |||
|
1412 | Search forward for a row. The search condition can be entered at the | |||
|
1413 | bottom of the screen. | |||
|
1414 | """ | |||
|
1415 | self.startkeyboardinput("find") | |||
|
1416 | ||||
|
1417 | def cmd_findbackwards(self): | |||
|
1418 | """ | |||
|
1419 | Search backward for a row. The search condition can be entered at the | |||
|
1420 | bottom of the screen. | |||
|
1421 | """ | |||
|
1422 | self.startkeyboardinput("findbackwards") | |||
|
1423 | ||||
|
1424 | def cmd_refresh(self): | |||
|
1425 | """ | |||
|
1426 | Refreshes the display by restarting the iterator. | |||
|
1427 | """ | |||
|
1428 | level = self.levels[-1] | |||
|
1429 | self.report("refresh") | |||
|
1430 | level.refresh() | |||
|
1431 | ||||
|
1432 | def cmd_refreshfind(self): | |||
|
1433 | """ | |||
|
1434 | Refreshes the display by restarting the iterator and goes back to the | |||
|
1435 | same object the cursor was on before restarting (if this object can't be | |||
|
1436 | found the cursor jumps back to the first object). | |||
|
1437 | """ | |||
|
1438 | level = self.levels[-1] | |||
|
1439 | self.report("refreshfind") | |||
|
1440 | level.refreshfind() | |||
|
1441 | ||||
|
1442 | def cmd_help(self): | |||
|
1443 | """ | |||
|
1444 | Opens the help screen as a new browser level, describing keyboard | |||
|
1445 | shortcuts. | |||
|
1446 | """ | |||
|
1447 | for level in self.levels: | |||
|
1448 | if isinstance(level.input, _BrowserHelp): | |||
|
1449 | curses.beep() | |||
|
1450 | self.report(CommandError("help already active")) | |||
|
1451 | return | |||
|
1452 | ||||
|
1453 | self.enter(_BrowserHelp(self)) | |||
|
1454 | ||||
|
1455 | def cmd_quit(self): | |||
|
1456 | """ | |||
|
1457 | Quit the browser and return to the IPython prompt. | |||
|
1458 | """ | |||
|
1459 | self.returnvalue = None | |||
|
1460 | return True | |||
|
1461 | ||||
|
1462 | def sigwinchhandler(self, signal, frame): | |||
|
1463 | self.resized = True | |||
|
1464 | ||||
|
1465 | def _dodisplay(self, scr): | |||
|
1466 | """ | |||
|
1467 | This method is the workhorse of the browser. It handles screen | |||
|
1468 | drawing and the keyboard. | |||
|
1469 | """ | |||
|
1470 | self.scr = scr | |||
|
1471 | curses.halfdelay(1) | |||
|
1472 | footery = 2 | |||
|
1473 | ||||
|
1474 | keys = [] | |||
|
1475 | for cmd in ("quit", "help"): | |||
|
1476 | key = self.keymap.findkey(cmd, None) | |||
|
1477 | if key is not None: | |||
|
1478 | keys.append("%s=%s" % (self.keylabel(key), cmd)) | |||
|
1479 | helpmsg = " | %s" % " ".join(keys) | |||
|
1480 | ||||
|
1481 | scr.clear() | |||
|
1482 | msg = "Fetching first batch of objects..." | |||
|
1483 | (self.scrsizey, self.scrsizex) = scr.getmaxyx() | |||
|
1484 | scr.addstr(self.scrsizey//2, (self.scrsizex-len(msg))//2, msg) | |||
|
1485 | scr.refresh() | |||
|
1486 | ||||
|
1487 | lastc = -1 | |||
|
1488 | ||||
|
1489 | self.levels = [] | |||
|
1490 | # enter the first level | |||
|
1491 | self.enter(self.input, *self.attrs) | |||
|
1492 | ||||
|
1493 | self._calcheaderlines(None) | |||
|
1494 | ||||
|
1495 | while True: | |||
|
1496 | level = self.levels[-1] | |||
|
1497 | (self.scrsizey, self.scrsizex) = scr.getmaxyx() | |||
|
1498 | level.mainsizey = self.scrsizey-1-self._headerlines-footery | |||
|
1499 | ||||
|
1500 | # Paint object header | |||
|
1501 | for i in xrange(self._firstheaderline, self._firstheaderline+self._headerlines): | |||
|
1502 | lv = self.levels[i] | |||
|
1503 | posx = 0 | |||
|
1504 | posy = i-self._firstheaderline | |||
|
1505 | endx = self.scrsizex | |||
|
1506 | if i: # not the first level | |||
|
1507 | msg = " (%d/%d" % (self.levels[i-1].cury, len(self.levels[i-1].items)) | |||
|
1508 | if not self.levels[i-1].exhausted: | |||
|
1509 | msg += "+" | |||
|
1510 | msg += ") " | |||
|
1511 | endx -= len(msg)+1 | |||
|
1512 | posx += self.addstr(posy, posx, 0, endx, " ibrowse #%d: " % i, self.style_objheadertext) | |||
|
1513 | for (style, text) in lv.header: | |||
|
1514 | posx += self.addstr(posy, posx, 0, endx, text, self.style_objheaderobject) | |||
|
1515 | if posx >= endx: | |||
|
1516 | break | |||
|
1517 | if i: | |||
|
1518 | posx += self.addstr(posy, posx, 0, self.scrsizex, msg, self.style_objheadernumber) | |||
|
1519 | posx += self.addchr(posy, posx, 0, self.scrsizex, " ", self.scrsizex-posx, self.style_objheadernumber) | |||
|
1520 | ||||
|
1521 | if not level.items: | |||
|
1522 | self.addchr(self._headerlines, 0, 0, self.scrsizex, " ", self.scrsizex, self.style_colheader) | |||
|
1523 | self.addstr(self._headerlines+1, 0, 0, self.scrsizex, " <empty>", astyle.style_error) | |||
|
1524 | scr.clrtobot() | |||
|
1525 | else: | |||
|
1526 | # Paint column headers | |||
|
1527 | scr.move(self._headerlines, 0) | |||
|
1528 | scr.addstr(" %*s " % (level.numbersizex, "#"), self.getstyle(self.style_colheader)) | |||
|
1529 | scr.addstr(self.headersepchar, self.getstyle(self.style_colheadersep)) | |||
|
1530 | begx = level.numbersizex+3 | |||
|
1531 | posx = begx-level.datastartx | |||
|
1532 | for attr in level.displayattrs: | |||
|
1533 | attrname = attr.name() | |||
|
1534 | cwidth = level.colwidths[attr] | |||
|
1535 | header = attrname.ljust(cwidth) | |||
|
1536 | if attr is level.displayattr[1]: | |||
|
1537 | style = self.style_colheaderhere | |||
|
1538 | else: | |||
|
1539 | style = self.style_colheader | |||
|
1540 | posx += self.addstr(self._headerlines, posx, begx, self.scrsizex, header, style) | |||
|
1541 | posx += self.addstr(self._headerlines, posx, begx, self.scrsizex, self.headersepchar, self.style_colheadersep) | |||
|
1542 | if posx >= self.scrsizex: | |||
|
1543 | break | |||
|
1544 | else: | |||
|
1545 | scr.addstr(" "*(self.scrsizex-posx), self.getstyle(self.style_colheader)) | |||
|
1546 | ||||
|
1547 | # Paint rows | |||
|
1548 | posy = self._headerlines+1+level.datastarty | |||
|
1549 | for i in xrange(level.datastarty, min(level.datastarty+level.mainsizey, len(level.items))): | |||
|
1550 | cache = level.items[i] | |||
|
1551 | if i == level.cury: | |||
|
1552 | style = self.style_numberhere | |||
|
1553 | else: | |||
|
1554 | style = self.style_number | |||
|
1555 | ||||
|
1556 | posy = self._headerlines+1+i-level.datastarty | |||
|
1557 | posx = begx-level.datastartx | |||
|
1558 | ||||
|
1559 | scr.move(posy, 0) | |||
|
1560 | scr.addstr(" %*d%s" % (level.numbersizex, i, " !"[cache.marked]), self.getstyle(style)) | |||
|
1561 | scr.addstr(self.headersepchar, self.getstyle(self.style_sep)) | |||
|
1562 | ||||
|
1563 | for attrname in level.displayattrs: | |||
|
1564 | cwidth = level.colwidths[attrname] | |||
|
1565 | try: | |||
|
1566 | (align, length, parts) = level.displayrows[i-level.datastarty][attrname] | |||
|
1567 | except KeyError: | |||
|
1568 | align = 2 | |||
|
1569 | style = astyle.style_nodata | |||
|
1570 | if i == level.cury: | |||
|
1571 | style = self.getstylehere(style) | |||
|
1572 | padstyle = self.style_datapad | |||
|
1573 | sepstyle = self.style_sep | |||
|
1574 | if i == level.cury: | |||
|
1575 | padstyle = self.getstylehere(padstyle) | |||
|
1576 | sepstyle = self.getstylehere(sepstyle) | |||
|
1577 | if align == 2: | |||
|
1578 | posx += self.addchr(posy, posx, begx, self.scrsizex, self.nodatachar, cwidth, style) | |||
|
1579 | else: | |||
|
1580 | if align == 1: | |||
|
1581 | posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, cwidth-length, padstyle) | |||
|
1582 | elif align == 0: | |||
|
1583 | pad1 = (cwidth-length)//2 | |||
|
1584 | pad2 = cwidth-length-len(pad1) | |||
|
1585 | posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, pad1, padstyle) | |||
|
1586 | for (style, text) in parts: | |||
|
1587 | if i == level.cury: | |||
|
1588 | style = self.getstylehere(style) | |||
|
1589 | posx += self.addstr(posy, posx, begx, self.scrsizex, text, style) | |||
|
1590 | if posx >= self.scrsizex: | |||
|
1591 | break | |||
|
1592 | if align == -1: | |||
|
1593 | posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, cwidth-length, padstyle) | |||
|
1594 | elif align == 0: | |||
|
1595 | posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, pad2, padstyle) | |||
|
1596 | posx += self.addstr(posy, posx, begx, self.scrsizex, self.datasepchar, sepstyle) | |||
|
1597 | else: | |||
|
1598 | scr.clrtoeol() | |||
|
1599 | ||||
|
1600 | # Add blank row headers for the rest of the screen | |||
|
1601 | for posy in xrange(posy+1, self.scrsizey-2): | |||
|
1602 | scr.addstr(posy, 0, " " * (level.numbersizex+2), self.getstyle(self.style_colheader)) | |||
|
1603 | scr.clrtoeol() | |||
|
1604 | ||||
|
1605 | posy = self.scrsizey-footery | |||
|
1606 | # Display footer | |||
|
1607 | scr.addstr(posy, 0, " "*self.scrsizex, self.getstyle(self.style_footer)) | |||
|
1608 | ||||
|
1609 | if level.exhausted: | |||
|
1610 | flag = "" | |||
|
1611 | else: | |||
|
1612 | flag = "+" | |||
|
1613 | ||||
|
1614 | endx = self.scrsizex-len(helpmsg)-1 | |||
|
1615 | scr.addstr(posy, endx, helpmsg, self.getstyle(self.style_footer)) | |||
|
1616 | ||||
|
1617 | posx = 0 | |||
|
1618 | msg = " %d%s objects (%d marked): " % (len(level.items), flag, level.marked) | |||
|
1619 | posx += self.addstr(posy, posx, 0, endx, msg, self.style_footer) | |||
|
1620 | try: | |||
|
1621 | item = level.items[level.cury].item | |||
|
1622 | except IndexError: # empty | |||
|
1623 | pass | |||
|
1624 | else: | |||
|
1625 | for (nostyle, text) in ipipe.xrepr(item, "footer"): | |||
|
1626 | if not isinstance(nostyle, int): | |||
|
1627 | posx += self.addstr(posy, posx, 0, endx, text, self.style_footer) | |||
|
1628 | if posx >= endx: | |||
|
1629 | break | |||
|
1630 | ||||
|
1631 | attrstyle = [(astyle.style_default, "no attribute")] | |||
|
1632 | attr = level.displayattr[1] | |||
|
1633 | if attr is not ipipe.noitem and not isinstance(attr, ipipe.SelfDescriptor): | |||
|
1634 | posx += self.addstr(posy, posx, 0, endx, " | ", self.style_footer) | |||
|
1635 | posx += self.addstr(posy, posx, 0, endx, attr.name(), self.style_footer) | |||
|
1636 | posx += self.addstr(posy, posx, 0, endx, ": ", self.style_footer) | |||
|
1637 | try: | |||
|
1638 | value = attr.value(item) | |||
|
1639 | except (SystemExit, KeyboardInterrupt): | |||
|
1640 | raise | |||
|
1641 | except Exception, exc: | |||
|
1642 | value = exc | |||
|
1643 | if value is not ipipe.noitem: | |||
|
1644 | attrstyle = ipipe.xrepr(value, "footer") | |||
|
1645 | for (nostyle, text) in attrstyle: | |||
|
1646 | if not isinstance(nostyle, int): | |||
|
1647 | posx += self.addstr(posy, posx, 0, endx, text, self.style_footer) | |||
|
1648 | if posx >= endx: | |||
|
1649 | break | |||
|
1650 | ||||
|
1651 | try: | |||
|
1652 | # Display input prompt | |||
|
1653 | if self.mode in self.prompts: | |||
|
1654 | history = self.prompts[self.mode] | |||
|
1655 | posx = 0 | |||
|
1656 | posy = self.scrsizey-1 | |||
|
1657 | posx += self.addstr(posy, posx, 0, endx, history.prompt, astyle.style_default) | |||
|
1658 | posx += self.addstr(posy, posx, 0, endx, " [", astyle.style_default) | |||
|
1659 | if history.cury==-1: | |||
|
1660 | text = "new" | |||
|
1661 | else: | |||
|
1662 | text = str(history.cury+1) | |||
|
1663 | posx += self.addstr(posy, posx, 0, endx, text, astyle.style_type_number) | |||
|
1664 | if history.history: | |||
|
1665 | posx += self.addstr(posy, posx, 0, endx, "/", astyle.style_default) | |||
|
1666 | posx += self.addstr(posy, posx, 0, endx, str(len(history.history)), astyle.style_type_number) | |||
|
1667 | posx += self.addstr(posy, posx, 0, endx, "]: ", astyle.style_default) | |||
|
1668 | inputstartx = posx | |||
|
1669 | posx += self.addstr(posy, posx, 0, endx, history.input, astyle.style_default) | |||
|
1670 | # Display report | |||
|
1671 | else: | |||
|
1672 | if self._report is not None: | |||
|
1673 | if isinstance(self._report, Exception): | |||
|
1674 | style = self.getstyle(astyle.style_error) | |||
|
1675 | if self._report.__class__.__module__ == "exceptions": | |||
|
1676 | msg = "%s: %s" % \ | |||
|
1677 | (self._report.__class__.__name__, self._report) | |||
|
1678 | else: | |||
|
1679 | msg = "%s.%s: %s" % \ | |||
|
1680 | (self._report.__class__.__module__, | |||
|
1681 | self._report.__class__.__name__, self._report) | |||
|
1682 | else: | |||
|
1683 | style = self.getstyle(self.style_report) | |||
|
1684 | msg = self._report | |||
|
1685 | scr.addstr(self.scrsizey-1, 0, msg[:self.scrsizex], style) | |||
|
1686 | self._report = None | |||
|
1687 | else: | |||
|
1688 | scr.move(self.scrsizey-1, 0) | |||
|
1689 | except curses.error: | |||
|
1690 | # Protect against errors from writing to the last line | |||
|
1691 | pass | |||
|
1692 | scr.clrtoeol() | |||
|
1693 | ||||
|
1694 | # Position cursor | |||
|
1695 | if self.mode in self.prompts: | |||
|
1696 | history = self.prompts[self.mode] | |||
|
1697 | scr.move(self.scrsizey-1, inputstartx+history.curx) | |||
|
1698 | else: | |||
|
1699 | scr.move( | |||
|
1700 | 1+self._headerlines+level.cury-level.datastarty, | |||
|
1701 | level.numbersizex+3+level.curx-level.datastartx | |||
|
1702 | ) | |||
|
1703 | scr.refresh() | |||
|
1704 | ||||
|
1705 | # Check keyboard | |||
|
1706 | while True: | |||
|
1707 | c = scr.getch() | |||
|
1708 | if self.resized: | |||
|
1709 | size = fcntl.ioctl(0, tty.TIOCGWINSZ, "12345678") | |||
|
1710 | size = struct.unpack("4H", size) | |||
|
1711 | oldsize = scr.getmaxyx() | |||
|
1712 | scr.erase() | |||
|
1713 | curses.resize_term(size[0], size[1]) | |||
|
1714 | newsize = scr.getmaxyx() | |||
|
1715 | scr.erase() | |||
|
1716 | for l in self.levels: | |||
|
1717 | l.mainsizey += newsize[0]-oldsize[0] | |||
|
1718 | l.moveto(l.curx, l.cury, refresh=True) | |||
|
1719 | scr.refresh() | |||
|
1720 | self.resized = False | |||
|
1721 | break # Redisplay | |||
|
1722 | if self.mode in self.prompts: | |||
|
1723 | if self.prompts[self.mode].handlekey(self, c): | |||
|
1724 | break # Redisplay | |||
|
1725 | else: | |||
|
1726 | # if no key is pressed slow down and beep again | |||
|
1727 | if c == -1: | |||
|
1728 | self.stepx = 1. | |||
|
1729 | self.stepy = 1. | |||
|
1730 | self._dobeep = True | |||
|
1731 | else: | |||
|
1732 | # if a different key was pressed slow down and beep too | |||
|
1733 | if c != lastc: | |||
|
1734 | lastc = c | |||
|
1735 | self.stepx = 1. | |||
|
1736 | self.stepy = 1. | |||
|
1737 | self._dobeep = True | |||
|
1738 | cmdname = self.keymap.get(c, None) | |||
|
1739 | if cmdname is None: | |||
|
1740 | self.report( | |||
|
1741 | UnassignedKeyError("Unassigned key %s" % | |||
|
1742 | self.keylabel(c))) | |||
|
1743 | else: | |||
|
1744 | cmdfunc = getattr(self, "cmd_%s" % cmdname, None) | |||
|
1745 | if cmdfunc is None: | |||
|
1746 | self.report( | |||
|
1747 | UnknownCommandError("Unknown command %r" % | |||
|
1748 | (cmdname,))) | |||
|
1749 | elif cmdfunc(): | |||
|
1750 | returnvalue = self.returnvalue | |||
|
1751 | self.returnvalue = None | |||
|
1752 | return returnvalue | |||
|
1753 | self.stepx = self.nextstepx(self.stepx) | |||
|
1754 | self.stepy = self.nextstepy(self.stepy) | |||
|
1755 | curses.flushinp() # get rid of type ahead | |||
|
1756 | break # Redisplay | |||
|
1757 | self.scr = None | |||
|
1758 | ||||
|
1759 | def display(self): | |||
|
1760 | if hasattr(curses, "resize_term"): | |||
|
1761 | oldhandler = signal.signal(signal.SIGWINCH, self.sigwinchhandler) | |||
|
1762 | try: | |||
|
1763 | return curses.wrapper(self._dodisplay) | |||
|
1764 | finally: | |||
|
1765 | signal.signal(signal.SIGWINCH, oldhandler) | |||
|
1766 | else: | |||
|
1767 | return curses.wrapper(self._dodisplay) |
This diff has been collapsed as it changes many lines, (1126 lines changed) Show them Hide them | |||||
@@ -0,0 +1,1126 | |||||
|
1 | # -*- coding: iso-8859-1 -*- | |||
|
2 | ||||
|
3 | import ipipe, os, webbrowser, urllib | |||
|
4 | from IPython import ipapi | |||
|
5 | import wx | |||
|
6 | import wx.grid, wx.html | |||
|
7 | ||||
|
8 | try: | |||
|
9 | sorted | |||
|
10 | except NameError: | |||
|
11 | from ipipe import sorted | |||
|
12 | try: | |||
|
13 | set | |||
|
14 | except: | |||
|
15 | from sets import Set as set | |||
|
16 | ||||
|
17 | ||||
|
18 | __all__ = ["igrid"] | |||
|
19 | ||||
|
20 | ||||
|
21 | help = """ | |||
|
22 | <?xml version='1.0' encoding='iso-8859-1'?> | |||
|
23 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |||
|
24 | <html> | |||
|
25 | <head> | |||
|
26 | <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> | |||
|
27 | <link rel="stylesheet" href="igrid_help.css" type="text/css" /> | |||
|
28 | <title>igrid help</title> | |||
|
29 | </head> | |||
|
30 | <body> | |||
|
31 | <h1>igrid help</h1> | |||
|
32 | ||||
|
33 | ||||
|
34 | <h2>Commands</h2> | |||
|
35 | ||||
|
36 | ||||
|
37 | <h3>pick (P)</h3> | |||
|
38 | <p>Pick the whole row (object is available as "_")</p> | |||
|
39 | ||||
|
40 | <h3>pickattr (Shift-P)</h3> | |||
|
41 | <p>Pick the attribute under the cursor</p> | |||
|
42 | ||||
|
43 | <h3>pickallattrs (Shift-C)</h3> | |||
|
44 | <p>Pick the complete column under the cursor (i.e. the attribute under the | |||
|
45 | cursor) from all currently fetched objects. These attributes will be returned | |||
|
46 | as a list.</p> | |||
|
47 | ||||
|
48 | <h3>pickinput (I)</h3> | |||
|
49 | <p>Pick the current row as next input line in IPython. Additionally the row is stored as "_"</p> | |||
|
50 | ||||
|
51 | <h3>pickinputattr (Shift-I)</h3> | |||
|
52 | <p>Pick the attribute under the cursor as next input line in IPython. Additionally the row is stored as "_"</p> | |||
|
53 | ||||
|
54 | <h3>enter (E)</h3> | |||
|
55 | <p>Enter the object under the cursor. (what this mean depends on the object | |||
|
56 | itself, i.e. how it implements iteration). This opens a new browser 'level'.</p> | |||
|
57 | ||||
|
58 | <h3>enterattr (Shift-E)</h3> | |||
|
59 | <p>Enter the attribute under the cursor.</p> | |||
|
60 | ||||
|
61 | <h3>detail (D)</h3> | |||
|
62 | <p>Show a detail view of the object under the cursor. This shows the name, | |||
|
63 | type, doc string and value of the object attributes (and it might show more | |||
|
64 | attributes than in the list view, depending on the object).</p> | |||
|
65 | ||||
|
66 | <h3>detailattr (Shift-D)</h3> | |||
|
67 | <p>Show a detail view of the attribute under the cursor.</p> | |||
|
68 | ||||
|
69 | <h3>pickrows (M)</h3> | |||
|
70 | <p>Pick multiple selected rows (M)</p> | |||
|
71 | ||||
|
72 | <h3>pickrowsattr (CTRL-M)</h3> | |||
|
73 | <p>From multiple selected rows pick the cells matching the attribute the cursor is in (CTRL-M)</p> | |||
|
74 | ||||
|
75 | <h3>find (CTRL-F)</h3> | |||
|
76 | <p>Find text</p> | |||
|
77 | ||||
|
78 | <h3>find_expression (CTRL-Shift-F)</h3> | |||
|
79 | <p>Find entries matching an expression</p> | |||
|
80 | ||||
|
81 | <h3>find_next (F3)</h3> | |||
|
82 | <p>Find next occurrence</p> | |||
|
83 | ||||
|
84 | <h3>find_previous (Shift-F3)</h3> | |||
|
85 | <p>Find previous occurrence</p> | |||
|
86 | ||||
|
87 | <h3>sortattrasc (V)</h3> | |||
|
88 | <p>Sort the objects (in ascending order) using the attribute under the cursor as the sort key.</p> | |||
|
89 | ||||
|
90 | <h3>sortattrdesc (Shift-V)</h3> | |||
|
91 | <p>Sort the objects (in descending order) using the attribute under the cursor as the sort key.</p> | |||
|
92 | ||||
|
93 | <h3>refresh_once (R, F5)</h3> | |||
|
94 | <p>Refreshes the display by restarting the iterator</p> | |||
|
95 | ||||
|
96 | <h3>refresh_every_second</h3> | |||
|
97 | <p>Refreshes the display by restarting the iterator every second until stopped by stop_refresh.</p> | |||
|
98 | ||||
|
99 | <h3>refresh_interval</h3> | |||
|
100 | <p>Refreshes the display by restarting the iterator every X ms (X is a custom interval set by the user) until stopped by stop_refresh.</p> | |||
|
101 | ||||
|
102 | <h3>stop_refresh</h3> | |||
|
103 | <p>Stops all refresh timers.</p> | |||
|
104 | ||||
|
105 | <h3>leave (Backspace, DEL, X)</h3> | |||
|
106 | <p>Close current tab (and all the tabs to the right of the current one).</h3> | |||
|
107 | ||||
|
108 | <h3>quit (ESC,Q)</h3> | |||
|
109 | <p>Quit igrid and return to the IPython prompt.</p> | |||
|
110 | ||||
|
111 | ||||
|
112 | <h2>Navigation</h2> | |||
|
113 | ||||
|
114 | ||||
|
115 | <h3>Jump to the last column of the current row (END, CTRL-E, CTRL-Right)</h3> | |||
|
116 | ||||
|
117 | <h3>Jump to the first column of the current row (HOME, CTRL-A, CTRL-Left)</h3> | |||
|
118 | ||||
|
119 | <h3>Move the cursor one column to the left (<)</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 | |||||
|
1 | body | |||
|
2 | { | |||
|
3 | background-color: #fff; | |||
|
4 | color: #000; | |||
|
5 | font-family: "Verdana", "Arial", "XHelvetica", "Helvetica", sans-serif; | |||
|
6 | padding: 20px 30px; | |||
|
7 | margin: 0px; | |||
|
8 | font-size: 11px; | |||
|
9 | } | |||
|
10 | h1 | |||
|
11 | { | |||
|
12 | font-family: "Trebuchet MS", sans-serif; | |||
|
13 | font-size: 24px; | |||
|
14 | margin: -20px -30px 4px -30px; | |||
|
15 | padding: 6px 30px; | |||
|
16 | font-weight: normal; | |||
|
17 | border-bottom: 1px solid #000; | |||
|
18 | letter-spacing: 1px; | |||
|
19 | background-color: #666; | |||
|
20 | color: #fff; | |||
|
21 | } | |||
|
22 | h2 | |||
|
23 | { | |||
|
24 | font-family: "Trebuchet MS", sans-serif; | |||
|
25 | font-size: 20px; | |||
|
26 | padding: 14px 0px 2px 0px; | |||
|
27 | margin: 0px; | |||
|
28 | font-weight: bold; | |||
|
29 | color: #333; | |||
|
30 | } | |||
|
31 | h3 | |||
|
32 | { | |||
|
33 | font-family: "Trebuchet MS", sans-serif; | |||
|
34 | font-size: 14px; | |||
|
35 | padding: 12px 0px 2px 0px; | |||
|
36 | margin: 0px; | |||
|
37 | font-weight: bold; | |||
|
38 | color: #333; | |||
|
39 | } | |||
|
40 | p | |||
|
41 | { | |||
|
42 | line-height: 120%; | |||
|
43 | margin: 0px 0px 6px 0px; | |||
|
44 | padding: 0px; | |||
|
45 | } |
@@ -0,0 +1,86 | |||||
|
1 | <?xml version='1.0' encoding='iso-8859-1'?> | |||
|
2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |||
|
3 | <html> | |||
|
4 | <head> | |||
|
5 | <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> | |||
|
6 | <link rel="stylesheet" href="igrid_help.css" type="text/css" /> | |||
|
7 | <title>igrid help</title> | |||
|
8 | </head> | |||
|
9 | <body> | |||
|
10 | <h1>igrid help</h1> | |||
|
11 | ||||
|
12 | ||||
|
13 | <h2>Commands</h2> | |||
|
14 | ||||
|
15 | ||||
|
16 | <h3>pick (P)</h3> | |||
|
17 | <p>Pick the whole row (object is available as "_")</p> | |||
|
18 | ||||
|
19 | <h3>pickattr (Shift-P)</h3> | |||
|
20 | <p>Pick the attribute under the cursor</p> | |||
|
21 | ||||
|
22 | <h3>pickallattrs (Shift-C)</h3> | |||
|
23 | <p>Pick' the complete column under the cursor (i.e. the attribute under the | |||
|
24 | cursor) from all currently fetched objects. These attributes will be returned | |||
|
25 | as a list.</p> | |||
|
26 | ||||
|
27 | <h3>enter (E)</h3> | |||
|
28 | <p>Enter the object under the cursor. (what this mean depends on the object | |||
|
29 | itself, i.e. how it implements iteration). This opens a new browser 'level'.</p> | |||
|
30 | ||||
|
31 | <h3>enterattr (Shift-E)</h3> | |||
|
32 | <p>Enter the attribute under the cursor.</p> | |||
|
33 | ||||
|
34 | <h3>detail (D)</h3> | |||
|
35 | <p>Show a detail view of the object under the cursor. This shows the name, | |||
|
36 | type, doc string and value of the object attributes (and it might show more | |||
|
37 | attributes than in the list view, depending on the object).</p> | |||
|
38 | ||||
|
39 | <h3>detailattr (Shift-D)</h3> | |||
|
40 | <p>Show a detail view of the attribute under the cursor.</p> | |||
|
41 | ||||
|
42 | <h3>pickrows (M)</h3> | |||
|
43 | <p>Pick multiple selected rows (M)</p> | |||
|
44 | ||||
|
45 | <h3>pickrowsattr (CTRL-M)</h3> | |||
|
46 | <p>From multiple selected rows pick the cells matching the attribute the cursor is in (CTRL-M)</p> | |||
|
47 | ||||
|
48 | <h3>find (CTRL-F)</h3> | |||
|
49 | <p>Find text</p> | |||
|
50 | ||||
|
51 | <h3>find_next (F3)</h3> | |||
|
52 | <p>Find next occurrence of the searchtext</p> | |||
|
53 | ||||
|
54 | <h3>find_previous (Shift-F3)</h3> | |||
|
55 | <p>Find previous occurrence of the searchtext </p> | |||
|
56 | ||||
|
57 | <h3>sortattrasc (V)</h3> | |||
|
58 | <p>Sort the objects (in ascending order) using the attribute under the cursor as the sort key.</p> | |||
|
59 | ||||
|
60 | <h3>sortattrdesc (Shift-V)</h3> | |||
|
61 | <p>Sort the objects (in descending order) using the attribute under the cursor as the sort key.</p> | |||
|
62 | ||||
|
63 | <h3>leave (Backspace, DEL, X)</h3> | |||
|
64 | <p>Close current tab (and all the tabs to the right of the current one).</h3> | |||
|
65 | ||||
|
66 | <h3>quit (ESC,Q)</h3> | |||
|
67 | <p>Quit igrid and return to the IPython prompt.</p> | |||
|
68 | ||||
|
69 | ||||
|
70 | <h2>Navigation</h2> | |||
|
71 | ||||
|
72 | ||||
|
73 | <h3>Jump to the last column of the current row (END, CTRL-E, CTRL-Right)</h3> | |||
|
74 | ||||
|
75 | <h3>Jump to the first column of the current row (HOME, CTRL-A, CTRL-Left)</h3> | |||
|
76 | ||||
|
77 | <h3>Move the cursor one column to the left (<)</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 | |||||
|
1 | # -*- coding: iso-8859-1 -*- | |||
|
2 | ||||
|
3 | """ | |||
|
4 | ``ipipe`` provides classes to be used in an interactive Python session. Doing a | |||
|
5 | ``from ipipe import *`` is the preferred way to do this. The name of all | |||
|
6 | objects imported this way starts with ``i`` to minimize collisions. | |||
|
7 | ||||
|
8 | ``ipipe`` supports "pipeline expressions", which is something resembling Unix | |||
|
9 | pipes. An example is:: | |||
|
10 | ||||
|
11 | >>> ienv | isort("key.lower()") | |||
|
12 | ||||
|
13 | This gives a listing of all environment variables sorted by name. | |||
|
14 | ||||
|
15 | ||||
|
16 | There are three types of objects in a pipeline expression: | |||
|
17 | ||||
|
18 | * ``Table``s: These objects produce items. Examples are ``ils`` (listing the | |||
|
19 | current directory, ``ienv`` (listing environment variables), ``ipwd`` (listing | |||
|
20 | user accounts) and ``igrp`` (listing user groups). A ``Table`` must be the | |||
|
21 | first object in a pipe expression. | |||
|
22 | ||||
|
23 | * ``Pipe``s: These objects sit in the middle of a pipe expression. They | |||
|
24 | transform the input in some way (e.g. filtering or sorting it). Examples are: | |||
|
25 | ``ifilter`` (which filters the input pipe), ``isort`` (which sorts the input | |||
|
26 | pipe) and ``ieval`` (which evaluates a function or expression for each object | |||
|
27 | in the input pipe). | |||
|
28 | ||||
|
29 | * ``Display``s: These objects can be put as the last object in a pipeline | |||
|
30 | expression. There are responsible for displaying the result of the pipeline | |||
|
31 | expression. If a pipeline expression doesn't end in a display object a default | |||
|
32 | display objects will be used. One example is ``ibrowse`` which is a ``curses`` | |||
|
33 | based browser. | |||
|
34 | ||||
|
35 | ||||
|
36 | Adding support for pipeline expressions to your own objects can be done through | |||
|
37 | three extensions points (all of them optional): | |||
|
38 | ||||
|
39 | * An object that will be displayed as a row by a ``Display`` object should | |||
|
40 | implement the method ``__xattrs__(self, mode)`` method or register an | |||
|
41 | implementation of the generic function ``xattrs``. For more info see ``xattrs``. | |||
|
42 | ||||
|
43 | * When an object ``foo`` is displayed by a ``Display`` object, the generic | |||
|
44 | function ``xrepr`` is used. | |||
|
45 | ||||
|
46 | * Objects that can be iterated by ``Pipe``s must iterable. For special cases, | |||
|
47 | where iteration for display is different than the normal iteration a special | |||
|
48 | implementation can be registered with the generic function ``xiter``. This | |||
|
49 | makes it possible to use dictionaries and modules in pipeline expressions, | |||
|
50 | for example:: | |||
|
51 | ||||
|
52 | >>> import sys | |||
|
53 | >>> sys | ifilter("isinstance(value, int)") | idump | |||
|
54 | key |value | |||
|
55 | api_version| 1012 | |||
|
56 | dllhandle | 503316480 | |||
|
57 | hexversion | 33817328 | |||
|
58 | maxint |2147483647 | |||
|
59 | maxunicode | 65535 | |||
|
60 | >>> sys.modules | ifilter("_.value is not None") | isort("_.key.lower()") | |||
|
61 | ... | |||
|
62 | ||||
|
63 | Note: The expression strings passed to ``ifilter()`` and ``isort()`` can | |||
|
64 | refer to the object to be filtered or sorted via the variable ``_`` and to any | |||
|
65 | of the attributes of the object, i.e.:: | |||
|
66 | ||||
|
67 | >>> sys.modules | ifilter("_.value is not None") | isort("_.key.lower()") | |||
|
68 | ||||
|
69 | does the same as:: | |||
|
70 | ||||
|
71 | >>> sys.modules | ifilter("value is not None") | isort("key.lower()") | |||
|
72 | ||||
|
73 | In addition to expression strings, it's possible to pass callables (taking | |||
|
74 | the object as an argument) to ``ifilter()``, ``isort()`` and ``ieval()``:: | |||
|
75 | ||||
|
76 | >>> sys | ifilter(lambda _:isinstance(_.value, int)) \ | |||
|
77 | ... | ieval(lambda _: (_.key, hex(_.value))) | idump | |||
|
78 | 0 |1 | |||
|
79 | api_version|0x3f4 | |||
|
80 | dllhandle |0x1e000000 | |||
|
81 | hexversion |0x20402f0 | |||
|
82 | maxint |0x7fffffff | |||
|
83 | maxunicode |0xffff | |||
|
84 | """ | |||
|
85 | ||||
|
86 | import sys, os, os.path, stat, glob, new, csv, datetime, types | |||
|
87 | import itertools, mimetypes, StringIO | |||
|
88 | ||||
|
89 | try: # Python 2.3 compatibility | |||
|
90 | import collections | |||
|
91 | except ImportError: | |||
|
92 | deque = list | |||
|
93 | else: | |||
|
94 | deque = collections.deque | |||
|
95 | ||||
|
96 | try: # Python 2.3 compatibility | |||
|
97 | set | |||
|
98 | except NameError: | |||
|
99 | import sets | |||
|
100 | set = sets.Set | |||
|
101 | ||||
|
102 | try: # Python 2.3 compatibility | |||
|
103 | sorted | |||
|
104 | except NameError: | |||
|
105 | def sorted(iterator, key=None, reverse=False): | |||
|
106 | items = list(iterator) | |||
|
107 | if key is not None: | |||
|
108 | items.sort(lambda i1, i2: cmp(key(i1), key(i2))) | |||
|
109 | else: | |||
|
110 | items.sort() | |||
|
111 | if reverse: | |||
|
112 | items.reverse() | |||
|
113 | return items | |||
|
114 | ||||
|
115 | try: | |||
|
116 | import pwd | |||
|
117 | except ImportError: | |||
|
118 | pwd = None | |||
|
119 | ||||
|
120 | try: | |||
|
121 | import grp | |||
|
122 | except ImportError: | |||
|
123 | grp = None | |||
|
124 | ||||
|
125 | from IPython.external import simplegeneric | |||
|
126 | ||||
|
127 | import path | |||
|
128 | ||||
|
129 | try: | |||
|
130 | from IPython import genutils, generics | |||
|
131 | except ImportError: | |||
|
132 | genutils = None | |||
|
133 | generics = None | |||
|
134 | ||||
|
135 | from IPython import ipapi | |||
|
136 | ||||
|
137 | ||||
|
138 | __all__ = [ | |||
|
139 | "ifile", "ils", "iglob", "iwalk", "ipwdentry", "ipwd", "igrpentry", "igrp", | |||
|
140 | "icsv", "ix", "ichain", "isort", "ifilter", "ieval", "ienum", | |||
|
141 | "ienv", "ihist", "ialias", "icap", "idump", "iless" | |||
|
142 | ] | |||
|
143 | ||||
|
144 | ||||
|
145 | os.stat_float_times(True) # enable microseconds | |||
|
146 | ||||
|
147 | ||||
|
148 | class AttrNamespace(object): | |||
|
149 | """ | |||
|
150 | Helper class that is used for providing a namespace for evaluating | |||
|
151 | expressions containing attribute names of an object. | |||
|
152 | """ | |||
|
153 | def __init__(self, wrapped): | |||
|
154 | self.wrapped = wrapped | |||
|
155 | ||||
|
156 | def __getitem__(self, name): | |||
|
157 | if name == "_": | |||
|
158 | return self.wrapped | |||
|
159 | try: | |||
|
160 | return getattr(self.wrapped, name) | |||
|
161 | except AttributeError: | |||
|
162 | raise KeyError(name) | |||
|
163 | ||||
|
164 | # Python 2.3 compatibility | |||
|
165 | # use eval workaround to find out which names are used in the | |||
|
166 | # eval string and put them into the locals. This works for most | |||
|
167 | # normal uses case, bizarre ones like accessing the locals() | |||
|
168 | # will fail | |||
|
169 | try: | |||
|
170 | eval("_", None, AttrNamespace(None)) | |||
|
171 | except TypeError: | |||
|
172 | real_eval = eval | |||
|
173 | def eval(codestring, _globals, _locals): | |||
|
174 | """ | |||
|
175 | eval(source[, globals[, locals]]) -> value | |||
|
176 | ||||
|
177 | Evaluate the source in the context of globals and locals. | |||
|
178 | The source may be a string representing a Python expression | |||
|
179 | or a code object as returned by compile(). | |||
|
180 | The globals must be a dictionary and locals can be any mappping. | |||
|
181 | ||||
|
182 | This function is a workaround for the shortcomings of | |||
|
183 | Python 2.3's eval. | |||
|
184 | """ | |||
|
185 | ||||
|
186 | if isinstance(codestring, basestring): | |||
|
187 | code = compile(codestring, "_eval", "eval") | |||
|
188 | else: | |||
|
189 | code = codestring | |||
|
190 | newlocals = {} | |||
|
191 | for name in code.co_names: | |||
|
192 | try: | |||
|
193 | newlocals[name] = _locals[name] | |||
|
194 | except KeyError: | |||
|
195 | pass | |||
|
196 | return real_eval(code, _globals, newlocals) | |||
|
197 | ||||
|
198 | ||||
|
199 | noitem = object() | |||
|
200 | ||||
|
201 | ||||
|
202 | def item(iterator, index, default=noitem): | |||
|
203 | """ | |||
|
204 | Return the ``index``th item from the iterator ``iterator``. | |||
|
205 | ``index`` must be an integer (negative integers are relative to the | |||
|
206 | end (i.e. the last items produced by the iterator)). | |||
|
207 | ||||
|
208 | If ``default`` is given, this will be the default value when | |||
|
209 | the iterator doesn't contain an item at this position. Otherwise an | |||
|
210 | ``IndexError`` will be raised. | |||
|
211 | ||||
|
212 | Note that using this function will partially or totally exhaust the | |||
|
213 | iterator. | |||
|
214 | """ | |||
|
215 | i = index | |||
|
216 | if i>=0: | |||
|
217 | for item in iterator: | |||
|
218 | if not i: | |||
|
219 | return item | |||
|
220 | i -= 1 | |||
|
221 | else: | |||
|
222 | i = -index | |||
|
223 | cache = deque() | |||
|
224 | for item in iterator: | |||
|
225 | cache.append(item) | |||
|
226 | if len(cache)>i: | |||
|
227 | cache.popleft() | |||
|
228 | if len(cache)==i: | |||
|
229 | return cache.popleft() | |||
|
230 | if default is noitem: | |||
|
231 | raise IndexError(index) | |||
|
232 | else: | |||
|
233 | return default | |||
|
234 | ||||
|
235 | ||||
|
236 | def getglobals(g): | |||
|
237 | """ | |||
|
238 | Return the global namespace that is used for expression strings in | |||
|
239 | ``ifilter`` and others. This is ``g`` or (if ``g`` is ``None``) IPython's | |||
|
240 | user namespace. | |||
|
241 | """ | |||
|
242 | if g is None: | |||
|
243 | if ipapi is not None: | |||
|
244 | api = ipapi.get() | |||
|
245 | if api is not None: | |||
|
246 | return api.user_ns | |||
|
247 | return globals() | |||
|
248 | return g | |||
|
249 | ||||
|
250 | ||||
|
251 | class Descriptor(object): | |||
|
252 | """ | |||
|
253 | A ``Descriptor`` object is used for describing the attributes of objects. | |||
|
254 | """ | |||
|
255 | def __hash__(self): | |||
|
256 | return hash(self.__class__) ^ hash(self.key()) | |||
|
257 | ||||
|
258 | def __eq__(self, other): | |||
|
259 | return self.__class__ is other.__class__ and self.key() == other.key() | |||
|
260 | ||||
|
261 | def __ne__(self, other): | |||
|
262 | return self.__class__ is not other.__class__ or self.key() != other.key() | |||
|
263 | ||||
|
264 | def key(self): | |||
|
265 | pass | |||
|
266 | ||||
|
267 | def name(self): | |||
|
268 | """ | |||
|
269 | Return the name of this attribute for display by a ``Display`` object | |||
|
270 | (e.g. as a column title). | |||
|
271 | """ | |||
|
272 | key = self.key() | |||
|
273 | if key is None: | |||
|
274 | return "_" | |||
|
275 | return str(key) | |||
|
276 | ||||
|
277 | def attrtype(self, obj): | |||
|
278 | """ | |||
|
279 | Return the type of this attribute (i.e. something like "attribute" or | |||
|
280 | "method"). | |||
|
281 | """ | |||
|
282 | ||||
|
283 | def valuetype(self, obj): | |||
|
284 | """ | |||
|
285 | Return the type of this attribute value of the object ``obj``. | |||
|
286 | """ | |||
|
287 | ||||
|
288 | def value(self, obj): | |||
|
289 | """ | |||
|
290 | Return the value of this attribute of the object ``obj``. | |||
|
291 | """ | |||
|
292 | ||||
|
293 | def doc(self, obj): | |||
|
294 | """ | |||
|
295 | Return the documentation for this attribute. | |||
|
296 | """ | |||
|
297 | ||||
|
298 | def shortdoc(self, obj): | |||
|
299 | """ | |||
|
300 | Return a short documentation for this attribute (defaulting to the | |||
|
301 | first line). | |||
|
302 | """ | |||
|
303 | doc = self.doc(obj) | |||
|
304 | if doc is not None: | |||
|
305 | doc = doc.strip().splitlines()[0].strip() | |||
|
306 | return doc | |||
|
307 | ||||
|
308 | def iter(self, obj): | |||
|
309 | """ | |||
|
310 | Return an iterator for this attribute of the object ``obj``. | |||
|
311 | """ | |||
|
312 | return xiter(self.value(obj)) | |||
|
313 | ||||
|
314 | ||||
|
315 | class SelfDescriptor(Descriptor): | |||
|
316 | """ | |||
|
317 | A ``SelfDescriptor`` describes the object itself. | |||
|
318 | """ | |||
|
319 | def key(self): | |||
|
320 | return None | |||
|
321 | ||||
|
322 | def attrtype(self, obj): | |||
|
323 | return "self" | |||
|
324 | ||||
|
325 | def valuetype(self, obj): | |||
|
326 | return type(obj) | |||
|
327 | ||||
|
328 | def value(self, obj): | |||
|
329 | return obj | |||
|
330 | ||||
|
331 | def __repr__(self): | |||
|
332 | return "Self" | |||
|
333 | ||||
|
334 | selfdescriptor = SelfDescriptor() # there's no need for more than one | |||
|
335 | ||||
|
336 | ||||
|
337 | class AttributeDescriptor(Descriptor): | |||
|
338 | """ | |||
|
339 | An ``AttributeDescriptor`` describes a simple attribute of an object. | |||
|
340 | """ | |||
|
341 | __slots__ = ("_name", "_doc") | |||
|
342 | ||||
|
343 | def __init__(self, name, doc=None): | |||
|
344 | self._name = name | |||
|
345 | self._doc = doc | |||
|
346 | ||||
|
347 | def key(self): | |||
|
348 | return self._name | |||
|
349 | ||||
|
350 | def doc(self, obj): | |||
|
351 | return self._doc | |||
|
352 | ||||
|
353 | def attrtype(self, obj): | |||
|
354 | return "attr" | |||
|
355 | ||||
|
356 | def valuetype(self, obj): | |||
|
357 | return type(getattr(obj, self._name)) | |||
|
358 | ||||
|
359 | def value(self, obj): | |||
|
360 | return getattr(obj, self._name) | |||
|
361 | ||||
|
362 | def __repr__(self): | |||
|
363 | if self._doc is None: | |||
|
364 | return "Attribute(%r)" % self._name | |||
|
365 | else: | |||
|
366 | return "Attribute(%r, %r)" % (self._name, self._doc) | |||
|
367 | ||||
|
368 | ||||
|
369 | class IndexDescriptor(Descriptor): | |||
|
370 | """ | |||
|
371 | An ``IndexDescriptor`` describes an "attribute" of an object that is fetched | |||
|
372 | via ``__getitem__``. | |||
|
373 | """ | |||
|
374 | __slots__ = ("_index",) | |||
|
375 | ||||
|
376 | def __init__(self, index): | |||
|
377 | self._index = index | |||
|
378 | ||||
|
379 | def key(self): | |||
|
380 | return self._index | |||
|
381 | ||||
|
382 | def attrtype(self, obj): | |||
|
383 | return "item" | |||
|
384 | ||||
|
385 | def valuetype(self, obj): | |||
|
386 | return type(obj[self._index]) | |||
|
387 | ||||
|
388 | def value(self, obj): | |||
|
389 | return obj[self._index] | |||
|
390 | ||||
|
391 | def __repr__(self): | |||
|
392 | return "Index(%r)" % self._index | |||
|
393 | ||||
|
394 | ||||
|
395 | class MethodDescriptor(Descriptor): | |||
|
396 | """ | |||
|
397 | A ``MethodDescriptor`` describes a method of an object that can be called | |||
|
398 | without argument. Note that this method shouldn't change the object. | |||
|
399 | """ | |||
|
400 | __slots__ = ("_name", "_doc") | |||
|
401 | ||||
|
402 | def __init__(self, name, doc=None): | |||
|
403 | self._name = name | |||
|
404 | self._doc = doc | |||
|
405 | ||||
|
406 | def key(self): | |||
|
407 | return self._name | |||
|
408 | ||||
|
409 | def doc(self, obj): | |||
|
410 | if self._doc is None: | |||
|
411 | return getattr(obj, self._name).__doc__ | |||
|
412 | return self._doc | |||
|
413 | ||||
|
414 | def attrtype(self, obj): | |||
|
415 | return "method" | |||
|
416 | ||||
|
417 | def valuetype(self, obj): | |||
|
418 | return type(self.value(obj)) | |||
|
419 | ||||
|
420 | def value(self, obj): | |||
|
421 | return getattr(obj, self._name)() | |||
|
422 | ||||
|
423 | def __repr__(self): | |||
|
424 | if self._doc is None: | |||
|
425 | return "Method(%r)" % self._name | |||
|
426 | else: | |||
|
427 | return "Method(%r, %r)" % (self._name, self._doc) | |||
|
428 | ||||
|
429 | ||||
|
430 | class IterAttributeDescriptor(Descriptor): | |||
|
431 | """ | |||
|
432 | An ``IterAttributeDescriptor`` works like an ``AttributeDescriptor`` but | |||
|
433 | doesn't return an attribute values (because this value might be e.g. a large | |||
|
434 | list). | |||
|
435 | """ | |||
|
436 | __slots__ = ("_name", "_doc") | |||
|
437 | ||||
|
438 | def __init__(self, name, doc=None): | |||
|
439 | self._name = name | |||
|
440 | self._doc = doc | |||
|
441 | ||||
|
442 | def key(self): | |||
|
443 | return self._name | |||
|
444 | ||||
|
445 | def doc(self, obj): | |||
|
446 | return self._doc | |||
|
447 | ||||
|
448 | def attrtype(self, obj): | |||
|
449 | return "iter" | |||
|
450 | ||||
|
451 | def valuetype(self, obj): | |||
|
452 | return noitem | |||
|
453 | ||||
|
454 | def value(self, obj): | |||
|
455 | return noitem | |||
|
456 | ||||
|
457 | def iter(self, obj): | |||
|
458 | return xiter(getattr(obj, self._name)) | |||
|
459 | ||||
|
460 | def __repr__(self): | |||
|
461 | if self._doc is None: | |||
|
462 | return "IterAttribute(%r)" % self._name | |||
|
463 | else: | |||
|
464 | return "IterAttribute(%r, %r)" % (self._name, self._doc) | |||
|
465 | ||||
|
466 | ||||
|
467 | class IterMethodDescriptor(Descriptor): | |||
|
468 | """ | |||
|
469 | An ``IterMethodDescriptor`` works like an ``MethodDescriptor`` but doesn't | |||
|
470 | return an attribute values (because this value might be e.g. a large list). | |||
|
471 | """ | |||
|
472 | __slots__ = ("_name", "_doc") | |||
|
473 | ||||
|
474 | def __init__(self, name, doc=None): | |||
|
475 | self._name = name | |||
|
476 | self._doc = doc | |||
|
477 | ||||
|
478 | def key(self): | |||
|
479 | return self._name | |||
|
480 | ||||
|
481 | def doc(self, obj): | |||
|
482 | if self._doc is None: | |||
|
483 | return getattr(obj, self._name).__doc__ | |||
|
484 | return self._doc | |||
|
485 | ||||
|
486 | def attrtype(self, obj): | |||
|
487 | return "itermethod" | |||
|
488 | ||||
|
489 | def valuetype(self, obj): | |||
|
490 | return noitem | |||
|
491 | ||||
|
492 | def value(self, obj): | |||
|
493 | return noitem | |||
|
494 | ||||
|
495 | def iter(self, obj): | |||
|
496 | return xiter(getattr(obj, self._name)()) | |||
|
497 | ||||
|
498 | def __repr__(self): | |||
|
499 | if self._doc is None: | |||
|
500 | return "IterMethod(%r)" % self._name | |||
|
501 | else: | |||
|
502 | return "IterMethod(%r, %r)" % (self._name, self._doc) | |||
|
503 | ||||
|
504 | ||||
|
505 | class FunctionDescriptor(Descriptor): | |||
|
506 | """ | |||
|
507 | A ``FunctionDescriptor`` turns a function into a descriptor. The function | |||
|
508 | will be called with the object to get the type and value of the attribute. | |||
|
509 | """ | |||
|
510 | __slots__ = ("_function", "_name", "_doc") | |||
|
511 | ||||
|
512 | def __init__(self, function, name=None, doc=None): | |||
|
513 | self._function = function | |||
|
514 | self._name = name | |||
|
515 | self._doc = doc | |||
|
516 | ||||
|
517 | def key(self): | |||
|
518 | return self._function | |||
|
519 | ||||
|
520 | def name(self): | |||
|
521 | if self._name is not None: | |||
|
522 | return self._name | |||
|
523 | return getattr(self._function, "__xname__", self._function.__name__) | |||
|
524 | ||||
|
525 | def doc(self, obj): | |||
|
526 | if self._doc is None: | |||
|
527 | return self._function.__doc__ | |||
|
528 | return self._doc | |||
|
529 | ||||
|
530 | def attrtype(self, obj): | |||
|
531 | return "function" | |||
|
532 | ||||
|
533 | def valuetype(self, obj): | |||
|
534 | return type(self._function(obj)) | |||
|
535 | ||||
|
536 | def value(self, obj): | |||
|
537 | return self._function(obj) | |||
|
538 | ||||
|
539 | def __repr__(self): | |||
|
540 | if self._doc is None: | |||
|
541 | return "Function(%r)" % self._name | |||
|
542 | else: | |||
|
543 | return "Function(%r, %r)" % (self._name, self._doc) | |||
|
544 | ||||
|
545 | ||||
|
546 | class Table(object): | |||
|
547 | """ | |||
|
548 | A ``Table`` is an object that produces items (just like a normal Python | |||
|
549 | iterator/generator does) and can be used as the first object in a pipeline | |||
|
550 | expression. The displayhook will open the default browser for such an object | |||
|
551 | (instead of simply printing the ``repr()`` result). | |||
|
552 | """ | |||
|
553 | ||||
|
554 | # We want to support ``foo`` and ``foo()`` in pipeline expression: | |||
|
555 | # So we implement the required operators (``|`` and ``+``) in the metaclass, | |||
|
556 | # instantiate the class and forward the operator to the instance | |||
|
557 | class __metaclass__(type): | |||
|
558 | def __iter__(self): | |||
|
559 | return iter(self()) | |||
|
560 | ||||
|
561 | def __or__(self, other): | |||
|
562 | return self() | other | |||
|
563 | ||||
|
564 | def __add__(self, other): | |||
|
565 | return self() + other | |||
|
566 | ||||
|
567 | def __radd__(self, other): | |||
|
568 | return other + self() | |||
|
569 | ||||
|
570 | def __getitem__(self, index): | |||
|
571 | return self()[index] | |||
|
572 | ||||
|
573 | def __getitem__(self, index): | |||
|
574 | return item(self, index) | |||
|
575 | ||||
|
576 | def __contains__(self, item): | |||
|
577 | for haveitem in self: | |||
|
578 | if item == haveitem: | |||
|
579 | return True | |||
|
580 | return False | |||
|
581 | ||||
|
582 | def __or__(self, other): | |||
|
583 | # autoinstantiate right hand side | |||
|
584 | if isinstance(other, type) and issubclass(other, (Table, Display)): | |||
|
585 | other = other() | |||
|
586 | # treat simple strings and functions as ``ieval`` instances | |||
|
587 | elif not isinstance(other, Display) and not isinstance(other, Table): | |||
|
588 | other = ieval(other) | |||
|
589 | # forward operations to the right hand side | |||
|
590 | return other.__ror__(self) | |||
|
591 | ||||
|
592 | def __add__(self, other): | |||
|
593 | # autoinstantiate right hand side | |||
|
594 | if isinstance(other, type) and issubclass(other, Table): | |||
|
595 | other = other() | |||
|
596 | return ichain(self, other) | |||
|
597 | ||||
|
598 | def __radd__(self, other): | |||
|
599 | # autoinstantiate left hand side | |||
|
600 | if isinstance(other, type) and issubclass(other, Table): | |||
|
601 | other = other() | |||
|
602 | return ichain(other, self) | |||
|
603 | ||||
|
604 | ||||
|
605 | class Pipe(Table): | |||
|
606 | """ | |||
|
607 | A ``Pipe`` is an object that can be used in a pipeline expression. It | |||
|
608 | processes the objects it gets from its input ``Table``/``Pipe``. Note that | |||
|
609 | a ``Pipe`` object can't be used as the first object in a pipeline | |||
|
610 | expression, as it doesn't produces items itself. | |||
|
611 | """ | |||
|
612 | class __metaclass__(Table.__metaclass__): | |||
|
613 | def __ror__(self, input): | |||
|
614 | return input | self() | |||
|
615 | ||||
|
616 | def __ror__(self, input): | |||
|
617 | # autoinstantiate left hand side | |||
|
618 | if isinstance(input, type) and issubclass(input, Table): | |||
|
619 | input = input() | |||
|
620 | self.input = input | |||
|
621 | return self | |||
|
622 | ||||
|
623 | ||||
|
624 | def xrepr(item, mode="default"): | |||
|
625 | """ | |||
|
626 | Generic function that adds color output and different display modes to ``repr``. | |||
|
627 | ||||
|
628 | The result of an ``xrepr`` call is iterable and consists of ``(style, string)`` | |||
|
629 | tuples. The ``style`` in this tuple must be a ``Style`` object from the | |||
|
630 | ``astring`` module. To reconfigure the output the first yielded tuple can be | |||
|
631 | a ``(aligment, full)`` tuple instead of a ``(style, string)`` tuple. | |||
|
632 | ``alignment`` can be -1 for left aligned, 0 for centered and 1 for right | |||
|
633 | aligned (the default is left alignment). ``full`` is a boolean that specifies | |||
|
634 | whether the complete output must be displayed or the ``Display`` object is | |||
|
635 | allowed to stop output after enough text has been produced (e.g. a syntax | |||
|
636 | highlighted text line would use ``True``, but for a large data structure | |||
|
637 | (i.e. a nested list, tuple or dictionary) ``False`` would be used). | |||
|
638 | The default is full output. | |||
|
639 | ||||
|
640 | There are four different possible values for ``mode`` depending on where | |||
|
641 | the ``Display`` object will display ``item``: | |||
|
642 | ||||
|
643 | ``"header"`` | |||
|
644 | ``item`` will be displayed in a header line (this is used by ``ibrowse``). | |||
|
645 | ||||
|
646 | ``"footer"`` | |||
|
647 | ``item`` will be displayed in a footer line (this is used by ``ibrowse``). | |||
|
648 | ||||
|
649 | ``"cell"`` | |||
|
650 | ``item`` will be displayed in a table cell/list. | |||
|
651 | ||||
|
652 | ``"default"`` | |||
|
653 | default mode. If an ``xrepr`` implementation recursively outputs objects, | |||
|
654 | ``"default"`` must be passed in the recursive calls to ``xrepr``. | |||
|
655 | ||||
|
656 | If no implementation is registered for ``item``, ``xrepr`` will try the | |||
|
657 | ``__xrepr__`` method on ``item``. If ``item`` doesn't have an ``__xrepr__`` | |||
|
658 | method it falls back to ``repr``/``__repr__`` for all modes. | |||
|
659 | """ | |||
|
660 | try: | |||
|
661 | func = item.__xrepr__ | |||
|
662 | except AttributeError: | |||
|
663 | yield (astyle.style_default, repr(item)) | |||
|
664 | else: | |||
|
665 | try: | |||
|
666 | for x in func(mode): | |||
|
667 | yield x | |||
|
668 | except (KeyboardInterrupt, SystemExit): | |||
|
669 | raise | |||
|
670 | except Exception: | |||
|
671 | yield (astyle.style_default, repr(item)) | |||
|
672 | xrepr = simplegeneric.generic(xrepr) | |||
|
673 | ||||
|
674 | ||||
|
675 | def xrepr_none(self, mode="default"): | |||
|
676 | yield (astyle.style_type_none, repr(self)) | |||
|
677 | xrepr.when_object(None)(xrepr_none) | |||
|
678 | ||||
|
679 | ||||
|
680 | def xrepr_noitem(self, mode="default"): | |||
|
681 | yield (2, True) | |||
|
682 | yield (astyle.style_nodata, "<?>") | |||
|
683 | xrepr.when_object(noitem)(xrepr_noitem) | |||
|
684 | ||||
|
685 | ||||
|
686 | def xrepr_bool(self, mode="default"): | |||
|
687 | yield (astyle.style_type_bool, repr(self)) | |||
|
688 | xrepr.when_type(bool)(xrepr_bool) | |||
|
689 | ||||
|
690 | ||||
|
691 | def xrepr_str(self, mode="default"): | |||
|
692 | if mode == "cell": | |||
|
693 | yield (astyle.style_default, repr(self.expandtabs(tab))[1:-1]) | |||
|
694 | else: | |||
|
695 | yield (astyle.style_default, repr(self)) | |||
|
696 | xrepr.when_type(str)(xrepr_str) | |||
|
697 | ||||
|
698 | ||||
|
699 | def xrepr_unicode(self, mode="default"): | |||
|
700 | if mode == "cell": | |||
|
701 | yield (astyle.style_default, repr(self.expandtabs(tab))[2:-1]) | |||
|
702 | else: | |||
|
703 | yield (astyle.style_default, repr(self)) | |||
|
704 | xrepr.when_type(unicode)(xrepr_unicode) | |||
|
705 | ||||
|
706 | ||||
|
707 | def xrepr_number(self, mode="default"): | |||
|
708 | yield (1, True) | |||
|
709 | yield (astyle.style_type_number, repr(self)) | |||
|
710 | xrepr.when_type(int)(xrepr_number) | |||
|
711 | xrepr.when_type(long)(xrepr_number) | |||
|
712 | xrepr.when_type(float)(xrepr_number) | |||
|
713 | ||||
|
714 | ||||
|
715 | def xrepr_complex(self, mode="default"): | |||
|
716 | yield (astyle.style_type_number, repr(self)) | |||
|
717 | xrepr.when_type(complex)(xrepr_number) | |||
|
718 | ||||
|
719 | ||||
|
720 | def xrepr_datetime(self, mode="default"): | |||
|
721 | if mode == "cell": | |||
|
722 | # Don't use strftime() here, as this requires year >= 1900 | |||
|
723 | yield (astyle.style_type_datetime, | |||
|
724 | "%04d-%02d-%02d %02d:%02d:%02d.%06d" % \ | |||
|
725 | (self.year, self.month, self.day, | |||
|
726 | self.hour, self.minute, self.second, | |||
|
727 | self.microsecond), | |||
|
728 | ) | |||
|
729 | else: | |||
|
730 | yield (astyle.style_type_datetime, repr(self)) | |||
|
731 | xrepr.when_type(datetime.datetime)(xrepr_datetime) | |||
|
732 | ||||
|
733 | ||||
|
734 | def xrepr_date(self, mode="default"): | |||
|
735 | if mode == "cell": | |||
|
736 | yield (astyle.style_type_datetime, | |||
|
737 | "%04d-%02d-%02d" % (self.year, self.month, self.day)) | |||
|
738 | else: | |||
|
739 | yield (astyle.style_type_datetime, repr(self)) | |||
|
740 | xrepr.when_type(datetime.date)(xrepr_date) | |||
|
741 | ||||
|
742 | ||||
|
743 | def xrepr_time(self, mode="default"): | |||
|
744 | if mode == "cell": | |||
|
745 | yield (astyle.style_type_datetime, | |||
|
746 | "%02d:%02d:%02d.%06d" % \ | |||
|
747 | (self.hour, self.minute, self.second, self.microsecond)) | |||
|
748 | else: | |||
|
749 | yield (astyle.style_type_datetime, repr(self)) | |||
|
750 | xrepr.when_type(datetime.time)(xrepr_time) | |||
|
751 | ||||
|
752 | ||||
|
753 | def xrepr_timedelta(self, mode="default"): | |||
|
754 | yield (astyle.style_type_datetime, repr(self)) | |||
|
755 | xrepr.when_type(datetime.timedelta)(xrepr_timedelta) | |||
|
756 | ||||
|
757 | ||||
|
758 | def xrepr_type(self, mode="default"): | |||
|
759 | if self.__module__ == "__builtin__": | |||
|
760 | yield (astyle.style_type_type, self.__name__) | |||
|
761 | else: | |||
|
762 | yield (astyle.style_type_type, "%s.%s" % (self.__module__, self.__name__)) | |||
|
763 | xrepr.when_type(type)(xrepr_type) | |||
|
764 | ||||
|
765 | ||||
|
766 | def xrepr_exception(self, mode="default"): | |||
|
767 | if self.__class__.__module__ == "exceptions": | |||
|
768 | classname = self.__class__.__name__ | |||
|
769 | else: | |||
|
770 | classname = "%s.%s" % \ | |||
|
771 | (self.__class__.__module__, self.__class__.__name__) | |||
|
772 | if mode == "header" or mode == "footer": | |||
|
773 | yield (astyle.style_error, "%s: %s" % (classname, self)) | |||
|
774 | else: | |||
|
775 | yield (astyle.style_error, classname) | |||
|
776 | xrepr.when_type(Exception)(xrepr_exception) | |||
|
777 | ||||
|
778 | ||||
|
779 | def xrepr_listtuple(self, mode="default"): | |||
|
780 | if mode == "header" or mode == "footer": | |||
|
781 | if self.__class__.__module__ == "__builtin__": | |||
|
782 | classname = self.__class__.__name__ | |||
|
783 | else: | |||
|
784 | classname = "%s.%s" % \ | |||
|
785 | (self.__class__.__module__,self.__class__.__name__) | |||
|
786 | yield (astyle.style_default, | |||
|
787 | "<%s object with %d items at 0x%x>" % \ | |||
|
788 | (classname, len(self), id(self))) | |||
|
789 | else: | |||
|
790 | yield (-1, False) | |||
|
791 | if isinstance(self, list): | |||
|
792 | yield (astyle.style_default, "[") | |||
|
793 | end = "]" | |||
|
794 | else: | |||
|
795 | yield (astyle.style_default, "(") | |||
|
796 | end = ")" | |||
|
797 | for (i, subself) in enumerate(self): | |||
|
798 | if i: | |||
|
799 | yield (astyle.style_default, ", ") | |||
|
800 | for part in xrepr(subself, "default"): | |||
|
801 | yield part | |||
|
802 | yield (astyle.style_default, end) | |||
|
803 | xrepr.when_type(list)(xrepr_listtuple) | |||
|
804 | xrepr.when_type(tuple)(xrepr_listtuple) | |||
|
805 | ||||
|
806 | ||||
|
807 | def xrepr_dict(self, mode="default"): | |||
|
808 | if mode == "header" or mode == "footer": | |||
|
809 | if self.__class__.__module__ == "__builtin__": | |||
|
810 | classname = self.__class__.__name__ | |||
|
811 | else: | |||
|
812 | classname = "%s.%s" % \ | |||
|
813 | (self.__class__.__module__,self.__class__.__name__) | |||
|
814 | yield (astyle.style_default, | |||
|
815 | "<%s object with %d items at 0x%x>" % \ | |||
|
816 | (classname, len(self), id(self))) | |||
|
817 | else: | |||
|
818 | yield (-1, False) | |||
|
819 | if isinstance(self, dict): | |||
|
820 | yield (astyle.style_default, "{") | |||
|
821 | end = "}" | |||
|
822 | else: | |||
|
823 | yield (astyle.style_default, "dictproxy((") | |||
|
824 | end = "})" | |||
|
825 | for (i, (key, value)) in enumerate(self.iteritems()): | |||
|
826 | if i: | |||
|
827 | yield (astyle.style_default, ", ") | |||
|
828 | for part in xrepr(key, "default"): | |||
|
829 | yield part | |||
|
830 | yield (astyle.style_default, ": ") | |||
|
831 | for part in xrepr(value, "default"): | |||
|
832 | yield part | |||
|
833 | yield (astyle.style_default, end) | |||
|
834 | xrepr.when_type(dict)(xrepr_dict) | |||
|
835 | xrepr.when_type(types.DictProxyType)(xrepr_dict) | |||
|
836 | ||||
|
837 | ||||
|
838 | def upgradexattr(attr): | |||
|
839 | """ | |||
|
840 | Convert an attribute descriptor string to a real descriptor object. | |||
|
841 | ||||
|
842 | If attr already is a descriptor object return if unmodified. A | |||
|
843 | ``SelfDescriptor`` will be returned if ``attr`` is ``None``. ``"foo"`` | |||
|
844 | returns an ``AttributeDescriptor`` for the attribute named ``"foo"``. | |||
|
845 | ``"foo()"`` returns a ``MethodDescriptor`` for the method named ``"foo"``. | |||
|
846 | ``"-foo"`` will return an ``IterAttributeDescriptor`` for the attribute | |||
|
847 | named ``"foo"`` and ``"-foo()"`` will return an ``IterMethodDescriptor`` | |||
|
848 | for the method named ``"foo"``. Furthermore integer will return the appropriate | |||
|
849 | ``IndexDescriptor`` and callables will return a ``FunctionDescriptor``. | |||
|
850 | """ | |||
|
851 | if attr is None: | |||
|
852 | return selfdescriptor | |||
|
853 | elif isinstance(attr, Descriptor): | |||
|
854 | return attr | |||
|
855 | elif isinstance(attr, str): | |||
|
856 | if attr.endswith("()"): | |||
|
857 | if attr.startswith("-"): | |||
|
858 | return IterMethodDescriptor(attr[1:-2]) | |||
|
859 | else: | |||
|
860 | return MethodDescriptor(attr[:-2]) | |||
|
861 | else: | |||
|
862 | if attr.startswith("-"): | |||
|
863 | return IterAttributeDescriptor(attr[1:]) | |||
|
864 | else: | |||
|
865 | return AttributeDescriptor(attr) | |||
|
866 | elif isinstance(attr, (int, long)): | |||
|
867 | return IndexDescriptor(attr) | |||
|
868 | elif callable(attr): | |||
|
869 | return FunctionDescriptor(attr) | |||
|
870 | else: | |||
|
871 | raise TypeError("can't handle descriptor %r" % attr) | |||
|
872 | ||||
|
873 | ||||
|
874 | def xattrs(item, mode="default"): | |||
|
875 | """ | |||
|
876 | Generic function that returns an iterable of attribute descriptors | |||
|
877 | to be used for displaying the attributes ob the object ``item`` in display | |||
|
878 | mode ``mode``. | |||
|
879 | ||||
|
880 | There are two possible modes: | |||
|
881 | ||||
|
882 | ``"detail"`` | |||
|
883 | The ``Display`` object wants to display a detailed list of the object | |||
|
884 | attributes. | |||
|
885 | ||||
|
886 | ``"default"`` | |||
|
887 | The ``Display`` object wants to display the object in a list view. | |||
|
888 | ||||
|
889 | If no implementation is registered for the object ``item`` ``xattrs`` falls | |||
|
890 | back to trying the ``__xattrs__`` method of the object. If this doesn't | |||
|
891 | exist either, ``dir(item)`` is used for ``"detail"`` mode and ``(None,)`` | |||
|
892 | for ``"default"`` mode. | |||
|
893 | ||||
|
894 | The implementation must yield attribute descriptors (see the class | |||
|
895 | ``Descriptor`` for more info). The ``__xattrs__`` method may also return | |||
|
896 | attribute descriptor strings (and ``None``) which will be converted to real | |||
|
897 | descriptors by ``upgradexattr()``. | |||
|
898 | """ | |||
|
899 | try: | |||
|
900 | func = item.__xattrs__ | |||
|
901 | except AttributeError: | |||
|
902 | if mode == "detail": | |||
|
903 | for attrname in dir(item): | |||
|
904 | yield AttributeDescriptor(attrname) | |||
|
905 | else: | |||
|
906 | yield selfdescriptor | |||
|
907 | else: | |||
|
908 | for attr in func(mode): | |||
|
909 | yield upgradexattr(attr) | |||
|
910 | xattrs = simplegeneric.generic(xattrs) | |||
|
911 | ||||
|
912 | ||||
|
913 | def xattrs_complex(self, mode="default"): | |||
|
914 | if mode == "detail": | |||
|
915 | return (AttributeDescriptor("real"), AttributeDescriptor("imag")) | |||
|
916 | return (selfdescriptor,) | |||
|
917 | xattrs.when_type(complex)(xattrs_complex) | |||
|
918 | ||||
|
919 | ||||
|
920 | def _isdict(item): | |||
|
921 | try: | |||
|
922 | itermeth = item.__class__.__iter__ | |||
|
923 | except (AttributeError, TypeError): | |||
|
924 | return False | |||
|
925 | return itermeth is dict.__iter__ or itermeth is types.DictProxyType.__iter__ | |||
|
926 | ||||
|
927 | ||||
|
928 | def _isstr(item): | |||
|
929 | if not isinstance(item, basestring): | |||
|
930 | return False | |||
|
931 | try: | |||
|
932 | itermeth = item.__class__.__iter__ | |||
|
933 | except AttributeError: | |||
|
934 | return True | |||
|
935 | return False # ``__iter__`` has been redefined | |||
|
936 | ||||
|
937 | ||||
|
938 | def xiter(item): | |||
|
939 | """ | |||
|
940 | Generic function that implements iteration for pipeline expression. If no | |||
|
941 | implementation is registered for ``item`` ``xiter`` falls back to ``iter``. | |||
|
942 | """ | |||
|
943 | try: | |||
|
944 | func = item.__xiter__ | |||
|
945 | except AttributeError: | |||
|
946 | if _isdict(item): | |||
|
947 | def items(item): | |||
|
948 | fields = ("key", "value") | |||
|
949 | for (key, value) in item.iteritems(): | |||
|
950 | yield Fields(fields, key=key, value=value) | |||
|
951 | return items(item) | |||
|
952 | elif isinstance(item, new.module): | |||
|
953 | def items(item): | |||
|
954 | fields = ("key", "value") | |||
|
955 | for key in sorted(item.__dict__): | |||
|
956 | yield Fields(fields, key=key, value=getattr(item, key)) | |||
|
957 | return items(item) | |||
|
958 | elif _isstr(item): | |||
|
959 | if not item: | |||
|
960 | raise ValueError("can't enter empty string") | |||
|
961 | lines = item.splitlines() | |||
|
962 | if len(lines) == 1: | |||
|
963 | def iterone(item): | |||
|
964 | yield item | |||
|
965 | return iterone(item) | |||
|
966 | else: | |||
|
967 | return iter(lines) | |||
|
968 | return iter(item) | |||
|
969 | else: | |||
|
970 | return iter(func()) # iter() just to be safe | |||
|
971 | xiter = simplegeneric.generic(xiter) | |||
|
972 | ||||
|
973 | ||||
|
974 | class ichain(Pipe): | |||
|
975 | """ | |||
|
976 | Chains multiple ``Table``s into one. | |||
|
977 | """ | |||
|
978 | ||||
|
979 | def __init__(self, *iters): | |||
|
980 | self.iters = iters | |||
|
981 | ||||
|
982 | def __iter__(self): | |||
|
983 | return itertools.chain(*self.iters) | |||
|
984 | ||||
|
985 | def __xrepr__(self, mode="default"): | |||
|
986 | if mode == "header" or mode == "footer": | |||
|
987 | for (i, item) in enumerate(self.iters): | |||
|
988 | if i: | |||
|
989 | yield (astyle.style_default, "+") | |||
|
990 | if isinstance(item, Pipe): | |||
|
991 | yield (astyle.style_default, "(") | |||
|
992 | for part in xrepr(item, mode): | |||
|
993 | yield part | |||
|
994 | if isinstance(item, Pipe): | |||
|
995 | yield (astyle.style_default, ")") | |||
|
996 | else: | |||
|
997 | yield (astyle.style_default, repr(self)) | |||
|
998 | ||||
|
999 | def __repr__(self): | |||
|
1000 | args = ", ".join([repr(it) for it in self.iters]) | |||
|
1001 | return "%s.%s(%s)" % \ | |||
|
1002 | (self.__class__.__module__, self.__class__.__name__, args) | |||
|
1003 | ||||
|
1004 | ||||
|
1005 | class ifile(path.path): | |||
|
1006 | """ | |||
|
1007 | file (or directory) object. | |||
|
1008 | """ | |||
|
1009 | ||||
|
1010 | def getmode(self): | |||
|
1011 | return self.stat().st_mode | |||
|
1012 | mode = property(getmode, None, None, "Access mode") | |||
|
1013 | ||||
|
1014 | def gettype(self): | |||
|
1015 | data = [ | |||
|
1016 | (stat.S_ISREG, "file"), | |||
|
1017 | (stat.S_ISDIR, "dir"), | |||
|
1018 | (stat.S_ISCHR, "chardev"), | |||
|
1019 | (stat.S_ISBLK, "blockdev"), | |||
|
1020 | (stat.S_ISFIFO, "fifo"), | |||
|
1021 | (stat.S_ISLNK, "symlink"), | |||
|
1022 | (stat.S_ISSOCK,"socket"), | |||
|
1023 | ] | |||
|
1024 | lstat = self.lstat() | |||
|
1025 | if lstat is not None: | |||
|
1026 | types = set([text for (func, text) in data if func(lstat.st_mode)]) | |||
|
1027 | else: | |||
|
1028 | types = set() | |||
|
1029 | m = self.mode | |||
|
1030 | types.update([text for (func, text) in data if func(m)]) | |||
|
1031 | return ", ".join(types) | |||
|
1032 | type = property(gettype, None, None, "file type (file, directory, link, etc.)") | |||
|
1033 | ||||
|
1034 | def getmodestr(self): | |||
|
1035 | m = self.mode | |||
|
1036 | data = [ | |||
|
1037 | (stat.S_IRUSR, "-r"), | |||
|
1038 | (stat.S_IWUSR, "-w"), | |||
|
1039 | (stat.S_IXUSR, "-x"), | |||
|
1040 | (stat.S_IRGRP, "-r"), | |||
|
1041 | (stat.S_IWGRP, "-w"), | |||
|
1042 | (stat.S_IXGRP, "-x"), | |||
|
1043 | (stat.S_IROTH, "-r"), | |||
|
1044 | (stat.S_IWOTH, "-w"), | |||
|
1045 | (stat.S_IXOTH, "-x"), | |||
|
1046 | ] | |||
|
1047 | return "".join([text[bool(m&bit)] for (bit, text) in data]) | |||
|
1048 | ||||
|
1049 | modestr = property(getmodestr, None, None, "Access mode as string") | |||
|
1050 | ||||
|
1051 | def getblocks(self): | |||
|
1052 | return self.stat().st_blocks | |||
|
1053 | blocks = property(getblocks, None, None, "File size in blocks") | |||
|
1054 | ||||
|
1055 | def getblksize(self): | |||
|
1056 | return self.stat().st_blksize | |||
|
1057 | blksize = property(getblksize, None, None, "Filesystem block size") | |||
|
1058 | ||||
|
1059 | def getdev(self): | |||
|
1060 | return self.stat().st_dev | |||
|
1061 | dev = property(getdev) | |||
|
1062 | ||||
|
1063 | def getnlink(self): | |||
|
1064 | return self.stat().st_nlink | |||
|
1065 | nlink = property(getnlink, None, None, "Number of links") | |||
|
1066 | ||||
|
1067 | def getuid(self): | |||
|
1068 | return self.stat().st_uid | |||
|
1069 | uid = property(getuid, None, None, "User id of file owner") | |||
|
1070 | ||||
|
1071 | def getgid(self): | |||
|
1072 | return self.stat().st_gid | |||
|
1073 | gid = property(getgid, None, None, "Group id of file owner") | |||
|
1074 | ||||
|
1075 | def getowner(self): | |||
|
1076 | stat = self.stat() | |||
|
1077 | try: | |||
|
1078 | return pwd.getpwuid(stat.st_uid).pw_name | |||
|
1079 | except KeyError: | |||
|
1080 | return stat.st_uid | |||
|
1081 | owner = property(getowner, None, None, "Owner name (or id)") | |||
|
1082 | ||||
|
1083 | def getgroup(self): | |||
|
1084 | stat = self.stat() | |||
|
1085 | try: | |||
|
1086 | return grp.getgrgid(stat.st_gid).gr_name | |||
|
1087 | except KeyError: | |||
|
1088 | return stat.st_gid | |||
|
1089 | group = property(getgroup, None, None, "Group name (or id)") | |||
|
1090 | ||||
|
1091 | def getadate(self): | |||
|
1092 | return datetime.datetime.utcfromtimestamp(self.atime) | |||
|
1093 | adate = property(getadate, None, None, "Access date") | |||
|
1094 | ||||
|
1095 | def getcdate(self): | |||
|
1096 | return datetime.datetime.utcfromtimestamp(self.ctime) | |||
|
1097 | cdate = property(getcdate, None, None, "Creation date") | |||
|
1098 | ||||
|
1099 | def getmdate(self): | |||
|
1100 | return datetime.datetime.utcfromtimestamp(self.mtime) | |||
|
1101 | mdate = property(getmdate, None, None, "Modification date") | |||
|
1102 | ||||
|
1103 | def mimetype(self): | |||
|
1104 | """ | |||
|
1105 | Return MIME type guessed from the extension. | |||
|
1106 | """ | |||
|
1107 | return mimetypes.guess_type(self.basename())[0] | |||
|
1108 | ||||
|
1109 | def encoding(self): | |||
|
1110 | """ | |||
|
1111 | Return guessed compression (like "compress" or "gzip"). | |||
|
1112 | """ | |||
|
1113 | return mimetypes.guess_type(self.basename())[1] | |||
|
1114 | ||||
|
1115 | def __repr__(self): | |||
|
1116 | return "ifile(%s)" % path._base.__repr__(self) | |||
|
1117 | ||||
|
1118 | if sys.platform == "win32": | |||
|
1119 | defaultattrs = (None, "type", "size", "modestr", "mdate") | |||
|
1120 | else: | |||
|
1121 | defaultattrs = (None, "type", "size", "modestr", "owner", "group", "mdate") | |||
|
1122 | ||||
|
1123 | def __xattrs__(self, mode="default"): | |||
|
1124 | if mode == "detail": | |||
|
1125 | return ( | |||
|
1126 | "name", | |||
|
1127 | "basename()", | |||
|
1128 | "abspath()", | |||
|
1129 | "realpath()", | |||
|
1130 | "type", | |||
|
1131 | "mode", | |||
|
1132 | "modestr", | |||
|
1133 | "stat()", | |||
|
1134 | "lstat()", | |||
|
1135 | "uid", | |||
|
1136 | "gid", | |||
|
1137 | "owner", | |||
|
1138 | "group", | |||
|
1139 | "dev", | |||
|
1140 | "nlink", | |||
|
1141 | "ctime", | |||
|
1142 | "mtime", | |||
|
1143 | "atime", | |||
|
1144 | "cdate", | |||
|
1145 | "mdate", | |||
|
1146 | "adate", | |||
|
1147 | "size", | |||
|
1148 | "blocks", | |||
|
1149 | "blksize", | |||
|
1150 | "isdir()", | |||
|
1151 | "islink()", | |||
|
1152 | "mimetype()", | |||
|
1153 | "encoding()", | |||
|
1154 | "-listdir()", | |||
|
1155 | "-dirs()", | |||
|
1156 | "-files()", | |||
|
1157 | "-walk()", | |||
|
1158 | "-walkdirs()", | |||
|
1159 | "-walkfiles()", | |||
|
1160 | ) | |||
|
1161 | else: | |||
|
1162 | return self.defaultattrs | |||
|
1163 | ||||
|
1164 | ||||
|
1165 | def xiter_ifile(self): | |||
|
1166 | if self.isdir(): | |||
|
1167 | yield (self / os.pardir).abspath() | |||
|
1168 | for child in sorted(self.listdir()): | |||
|
1169 | yield child | |||
|
1170 | else: | |||
|
1171 | f = self.open("rb") | |||
|
1172 | for line in f: | |||
|
1173 | yield line | |||
|
1174 | f.close() | |||
|
1175 | xiter.when_type(ifile)(xiter_ifile) | |||
|
1176 | ||||
|
1177 | ||||
|
1178 | # We need to implement ``xrepr`` for ``ifile`` as a generic function, because | |||
|
1179 | # otherwise ``xrepr_str`` would kick in. | |||
|
1180 | def xrepr_ifile(self, mode="default"): | |||
|
1181 | try: | |||
|
1182 | if self.isdir(): | |||
|
1183 | name = "idir" | |||
|
1184 | style = astyle.style_dir | |||
|
1185 | else: | |||
|
1186 | name = "ifile" | |||
|
1187 | style = astyle.style_file | |||
|
1188 | except IOError: | |||
|
1189 | name = "ifile" | |||
|
1190 | style = astyle.style_default | |||
|
1191 | if mode in ("cell", "header", "footer"): | |||
|
1192 | abspath = repr(path._base(self.normpath())) | |||
|
1193 | if abspath.startswith("u"): | |||
|
1194 | abspath = abspath[2:-1] | |||
|
1195 | else: | |||
|
1196 | abspath = abspath[1:-1] | |||
|
1197 | if mode == "cell": | |||
|
1198 | yield (style, abspath) | |||
|
1199 | else: | |||
|
1200 | yield (style, "%s(%s)" % (name, abspath)) | |||
|
1201 | else: | |||
|
1202 | yield (style, repr(self)) | |||
|
1203 | xrepr.when_type(ifile)(xrepr_ifile) | |||
|
1204 | ||||
|
1205 | ||||
|
1206 | class ils(Table): | |||
|
1207 | """ | |||
|
1208 | List the current (or a specified) directory. | |||
|
1209 | ||||
|
1210 | Examples:: | |||
|
1211 | ||||
|
1212 | >>> ils | |||
|
1213 | >>> ils("/usr/local/lib/python2.4") | |||
|
1214 | >>> ils("~") | |||
|
1215 | """ | |||
|
1216 | def __init__(self, base=os.curdir, dirs=True, files=True): | |||
|
1217 | self.base = os.path.expanduser(base) | |||
|
1218 | self.dirs = dirs | |||
|
1219 | self.files = files | |||
|
1220 | ||||
|
1221 | def __iter__(self): | |||
|
1222 | base = ifile(self.base) | |||
|
1223 | yield (base / os.pardir).abspath() | |||
|
1224 | for child in sorted(base.listdir()): | |||
|
1225 | if self.dirs: | |||
|
1226 | if self.files: | |||
|
1227 | yield child | |||
|
1228 | else: | |||
|
1229 | if child.isdir(): | |||
|
1230 | yield child | |||
|
1231 | elif self.files: | |||
|
1232 | if not child.isdir(): | |||
|
1233 | yield child | |||
|
1234 | ||||
|
1235 | def __xrepr__(self, mode="default"): | |||
|
1236 | return xrepr(ifile(self.base), mode) | |||
|
1237 | ||||
|
1238 | def __repr__(self): | |||
|
1239 | return "%s.%s(%r)" % \ | |||
|
1240 | (self.__class__.__module__, self.__class__.__name__, self.base) | |||
|
1241 | ||||
|
1242 | ||||
|
1243 | class iglob(Table): | |||
|
1244 | """ | |||
|
1245 | List all files and directories matching a specified pattern. | |||
|
1246 | (See ``glob.glob()`` for more info.). | |||
|
1247 | ||||
|
1248 | Examples:: | |||
|
1249 | ||||
|
1250 | >>> iglob("*.py") | |||
|
1251 | """ | |||
|
1252 | def __init__(self, glob): | |||
|
1253 | self.glob = glob | |||
|
1254 | ||||
|
1255 | def __iter__(self): | |||
|
1256 | for name in glob.glob(self.glob): | |||
|
1257 | yield ifile(name) | |||
|
1258 | ||||
|
1259 | def __xrepr__(self, mode="default"): | |||
|
1260 | if mode == "header" or mode == "footer" or mode == "cell": | |||
|
1261 | yield (astyle.style_default, | |||
|
1262 | "%s(%r)" % (self.__class__.__name__, self.glob)) | |||
|
1263 | else: | |||
|
1264 | yield (astyle.style_default, repr(self)) | |||
|
1265 | ||||
|
1266 | def __repr__(self): | |||
|
1267 | return "%s.%s(%r)" % \ | |||
|
1268 | (self.__class__.__module__, self.__class__.__name__, self.glob) | |||
|
1269 | ||||
|
1270 | ||||
|
1271 | class iwalk(Table): | |||
|
1272 | """ | |||
|
1273 | List all files and directories in a directory and it's subdirectory:: | |||
|
1274 | ||||
|
1275 | >>> iwalk | |||
|
1276 | >>> iwalk("/usr/local/lib/python2.4") | |||
|
1277 | >>> iwalk("~") | |||
|
1278 | """ | |||
|
1279 | def __init__(self, base=os.curdir, dirs=True, files=True): | |||
|
1280 | self.base = os.path.expanduser(base) | |||
|
1281 | self.dirs = dirs | |||
|
1282 | self.files = files | |||
|
1283 | ||||
|
1284 | def __iter__(self): | |||
|
1285 | for (dirpath, dirnames, filenames) in os.walk(self.base): | |||
|
1286 | if self.dirs: | |||
|
1287 | for name in sorted(dirnames): | |||
|
1288 | yield ifile(os.path.join(dirpath, name)) | |||
|
1289 | if self.files: | |||
|
1290 | for name in sorted(filenames): | |||
|
1291 | yield ifile(os.path.join(dirpath, name)) | |||
|
1292 | ||||
|
1293 | def __xrepr__(self, mode="default"): | |||
|
1294 | if mode == "header" or mode == "footer" or mode == "cell": | |||
|
1295 | yield (astyle.style_default, | |||
|
1296 | "%s(%r)" % (self.__class__.__name__, self.base)) | |||
|
1297 | else: | |||
|
1298 | yield (astyle.style_default, repr(self)) | |||
|
1299 | ||||
|
1300 | def __repr__(self): | |||
|
1301 | return "%s.%s(%r)" % \ | |||
|
1302 | (self.__class__.__module__, self.__class__.__name__, self.base) | |||
|
1303 | ||||
|
1304 | ||||
|
1305 | class ipwdentry(object): | |||
|
1306 | """ | |||
|
1307 | ``ipwdentry`` objects encapsulate entries in the Unix user account and | |||
|
1308 | password database. | |||
|
1309 | """ | |||
|
1310 | def __init__(self, id): | |||
|
1311 | self._id = id | |||
|
1312 | self._entry = None | |||
|
1313 | ||||
|
1314 | def __eq__(self, other): | |||
|
1315 | return self.__class__ is other.__class__ and self._id == other._id | |||
|
1316 | ||||
|
1317 | def __ne__(self, other): | |||
|
1318 | return self.__class__ is not other.__class__ or self._id != other._id | |||
|
1319 | ||||
|
1320 | def _getentry(self): | |||
|
1321 | if self._entry is None: | |||
|
1322 | if isinstance(self._id, basestring): | |||
|
1323 | self._entry = pwd.getpwnam(self._id) | |||
|
1324 | else: | |||
|
1325 | self._entry = pwd.getpwuid(self._id) | |||
|
1326 | return self._entry | |||
|
1327 | ||||
|
1328 | def getname(self): | |||
|
1329 | if isinstance(self._id, basestring): | |||
|
1330 | return self._id | |||
|
1331 | else: | |||
|
1332 | return self._getentry().pw_name | |||
|
1333 | name = property(getname, None, None, "User name") | |||
|
1334 | ||||
|
1335 | def getpasswd(self): | |||
|
1336 | return self._getentry().pw_passwd | |||
|
1337 | passwd = property(getpasswd, None, None, "Password") | |||
|
1338 | ||||
|
1339 | def getuid(self): | |||
|
1340 | if isinstance(self._id, basestring): | |||
|
1341 | return self._getentry().pw_uid | |||
|
1342 | else: | |||
|
1343 | return self._id | |||
|
1344 | uid = property(getuid, None, None, "User id") | |||
|
1345 | ||||
|
1346 | def getgid(self): | |||
|
1347 | return self._getentry().pw_gid | |||
|
1348 | gid = property(getgid, None, None, "Primary group id") | |||
|
1349 | ||||
|
1350 | def getgroup(self): | |||
|
1351 | return igrpentry(self.gid) | |||
|
1352 | group = property(getgroup, None, None, "Group") | |||
|
1353 | ||||
|
1354 | def getgecos(self): | |||
|
1355 | return self._getentry().pw_gecos | |||
|
1356 | gecos = property(getgecos, None, None, "Information (e.g. full user name)") | |||
|
1357 | ||||
|
1358 | def getdir(self): | |||
|
1359 | return self._getentry().pw_dir | |||
|
1360 | dir = property(getdir, None, None, "$HOME directory") | |||
|
1361 | ||||
|
1362 | def getshell(self): | |||
|
1363 | return self._getentry().pw_shell | |||
|
1364 | shell = property(getshell, None, None, "Login shell") | |||
|
1365 | ||||
|
1366 | def __xattrs__(self, mode="default"): | |||
|
1367 | return ("name", "passwd", "uid", "gid", "gecos", "dir", "shell") | |||
|
1368 | ||||
|
1369 | def __repr__(self): | |||
|
1370 | return "%s.%s(%r)" % \ | |||
|
1371 | (self.__class__.__module__, self.__class__.__name__, self._id) | |||
|
1372 | ||||
|
1373 | ||||
|
1374 | class ipwd(Table): | |||
|
1375 | """ | |||
|
1376 | List all entries in the Unix user account and password database. | |||
|
1377 | ||||
|
1378 | Example:: | |||
|
1379 | ||||
|
1380 | >>> ipwd | isort("uid") | |||
|
1381 | """ | |||
|
1382 | def __iter__(self): | |||
|
1383 | for entry in pwd.getpwall(): | |||
|
1384 | yield ipwdentry(entry.pw_name) | |||
|
1385 | ||||
|
1386 | def __xrepr__(self, mode="default"): | |||
|
1387 | if mode == "header" or mode == "footer" or mode == "cell": | |||
|
1388 | yield (astyle.style_default, "%s()" % self.__class__.__name__) | |||
|
1389 | else: | |||
|
1390 | yield (astyle.style_default, repr(self)) | |||
|
1391 | ||||
|
1392 | ||||
|
1393 | class igrpentry(object): | |||
|
1394 | """ | |||
|
1395 | ``igrpentry`` objects encapsulate entries in the Unix group database. | |||
|
1396 | """ | |||
|
1397 | def __init__(self, id): | |||
|
1398 | self._id = id | |||
|
1399 | self._entry = None | |||
|
1400 | ||||
|
1401 | def __eq__(self, other): | |||
|
1402 | return self.__class__ is other.__class__ and self._id == other._id | |||
|
1403 | ||||
|
1404 | def __ne__(self, other): | |||
|
1405 | return self.__class__ is not other.__class__ or self._id != other._id | |||
|
1406 | ||||
|
1407 | def _getentry(self): | |||
|
1408 | if self._entry is None: | |||
|
1409 | if isinstance(self._id, basestring): | |||
|
1410 | self._entry = grp.getgrnam(self._id) | |||
|
1411 | else: | |||
|
1412 | self._entry = grp.getgrgid(self._id) | |||
|
1413 | return self._entry | |||
|
1414 | ||||
|
1415 | def getname(self): | |||
|
1416 | if isinstance(self._id, basestring): | |||
|
1417 | return self._id | |||
|
1418 | else: | |||
|
1419 | return self._getentry().gr_name | |||
|
1420 | name = property(getname, None, None, "Group name") | |||
|
1421 | ||||
|
1422 | def getpasswd(self): | |||
|
1423 | return self._getentry().gr_passwd | |||
|
1424 | passwd = property(getpasswd, None, None, "Password") | |||
|
1425 | ||||
|
1426 | def getgid(self): | |||
|
1427 | if isinstance(self._id, basestring): | |||
|
1428 | return self._getentry().gr_gid | |||
|
1429 | else: | |||
|
1430 | return self._id | |||
|
1431 | gid = property(getgid, None, None, "Group id") | |||
|
1432 | ||||
|
1433 | def getmem(self): | |||
|
1434 | return self._getentry().gr_mem | |||
|
1435 | mem = property(getmem, None, None, "Members") | |||
|
1436 | ||||
|
1437 | def __xattrs__(self, mode="default"): | |||
|
1438 | return ("name", "passwd", "gid", "mem") | |||
|
1439 | ||||
|
1440 | def __xrepr__(self, mode="default"): | |||
|
1441 | if mode == "header" or mode == "footer" or mode == "cell": | |||
|
1442 | yield (astyle.style_default, "group ") | |||
|
1443 | try: | |||
|
1444 | yield (astyle.style_default, self.name) | |||
|
1445 | except KeyError: | |||
|
1446 | if isinstance(self._id, basestring): | |||
|
1447 | yield (astyle.style_default, self.name_id) | |||
|
1448 | else: | |||
|
1449 | yield (astyle.style_type_number, str(self._id)) | |||
|
1450 | else: | |||
|
1451 | yield (astyle.style_default, repr(self)) | |||
|
1452 | ||||
|
1453 | def __iter__(self): | |||
|
1454 | for member in self.mem: | |||
|
1455 | yield ipwdentry(member) | |||
|
1456 | ||||
|
1457 | def __repr__(self): | |||
|
1458 | return "%s.%s(%r)" % \ | |||
|
1459 | (self.__class__.__module__, self.__class__.__name__, self._id) | |||
|
1460 | ||||
|
1461 | ||||
|
1462 | class igrp(Table): | |||
|
1463 | """ | |||
|
1464 | This ``Table`` lists all entries in the Unix group database. | |||
|
1465 | """ | |||
|
1466 | def __iter__(self): | |||
|
1467 | for entry in grp.getgrall(): | |||
|
1468 | yield igrpentry(entry.gr_name) | |||
|
1469 | ||||
|
1470 | def __xrepr__(self, mode="default"): | |||
|
1471 | if mode == "header" or mode == "footer": | |||
|
1472 | yield (astyle.style_default, "%s()" % self.__class__.__name__) | |||
|
1473 | else: | |||
|
1474 | yield (astyle.style_default, repr(self)) | |||
|
1475 | ||||
|
1476 | ||||
|
1477 | class Fields(object): | |||
|
1478 | def __init__(self, fieldnames, **fields): | |||
|
1479 | self.__fieldnames = [upgradexattr(fieldname) for fieldname in fieldnames] | |||
|
1480 | for (key, value) in fields.iteritems(): | |||
|
1481 | setattr(self, key, value) | |||
|
1482 | ||||
|
1483 | def __xattrs__(self, mode="default"): | |||
|
1484 | return self.__fieldnames | |||
|
1485 | ||||
|
1486 | def __xrepr__(self, mode="default"): | |||
|
1487 | yield (-1, False) | |||
|
1488 | if mode == "header" or mode == "cell": | |||
|
1489 | yield (astyle.style_default, self.__class__.__name__) | |||
|
1490 | yield (astyle.style_default, "(") | |||
|
1491 | for (i, f) in enumerate(self.__fieldnames): | |||
|
1492 | if i: | |||
|
1493 | yield (astyle.style_default, ", ") | |||
|
1494 | yield (astyle.style_default, f.name()) | |||
|
1495 | yield (astyle.style_default, "=") | |||
|
1496 | for part in xrepr(getattr(self, f), "default"): | |||
|
1497 | yield part | |||
|
1498 | yield (astyle.style_default, ")") | |||
|
1499 | elif mode == "footer": | |||
|
1500 | yield (astyle.style_default, self.__class__.__name__) | |||
|
1501 | yield (astyle.style_default, "(") | |||
|
1502 | for (i, f) in enumerate(self.__fieldnames): | |||
|
1503 | if i: | |||
|
1504 | yield (astyle.style_default, ", ") | |||
|
1505 | yield (astyle.style_default, f.name()) | |||
|
1506 | yield (astyle.style_default, ")") | |||
|
1507 | else: | |||
|
1508 | yield (astyle.style_default, repr(self)) | |||
|
1509 | ||||
|
1510 | ||||
|
1511 | class FieldTable(Table, list): | |||
|
1512 | def __init__(self, *fields): | |||
|
1513 | Table.__init__(self) | |||
|
1514 | list.__init__(self) | |||
|
1515 | self.fields = fields | |||
|
1516 | ||||
|
1517 | def add(self, **fields): | |||
|
1518 | self.append(Fields(self.fields, **fields)) | |||
|
1519 | ||||
|
1520 | def __xrepr__(self, mode="default"): | |||
|
1521 | yield (-1, False) | |||
|
1522 | if mode == "header" or mode == "footer": | |||
|
1523 | yield (astyle.style_default, self.__class__.__name__) | |||
|
1524 | yield (astyle.style_default, "(") | |||
|
1525 | for (i, f) in enumerate(self.__fieldnames): | |||
|
1526 | if i: | |||
|
1527 | yield (astyle.style_default, ", ") | |||
|
1528 | yield (astyle.style_default, f) | |||
|
1529 | yield (astyle.style_default, ")") | |||
|
1530 | else: | |||
|
1531 | yield (astyle.style_default, repr(self)) | |||
|
1532 | ||||
|
1533 | def __repr__(self): | |||
|
1534 | return "<%s.%s object with fields=%r at 0x%x>" % \ | |||
|
1535 | (self.__class__.__module__, self.__class__.__name__, | |||
|
1536 | ", ".join(map(repr, self.fields)), id(self)) | |||
|
1537 | ||||
|
1538 | ||||
|
1539 | class List(list): | |||
|
1540 | def __xattrs__(self, mode="default"): | |||
|
1541 | return xrange(len(self)) | |||
|
1542 | ||||
|
1543 | def __xrepr__(self, mode="default"): | |||
|
1544 | yield (-1, False) | |||
|
1545 | if mode == "header" or mode == "cell" or mode == "footer" or mode == "default": | |||
|
1546 | yield (astyle.style_default, self.__class__.__name__) | |||
|
1547 | yield (astyle.style_default, "(") | |||
|
1548 | for (i, item) in enumerate(self): | |||
|
1549 | if i: | |||
|
1550 | yield (astyle.style_default, ", ") | |||
|
1551 | for part in xrepr(item, "default"): | |||
|
1552 | yield part | |||
|
1553 | yield (astyle.style_default, ")") | |||
|
1554 | else: | |||
|
1555 | yield (astyle.style_default, repr(self)) | |||
|
1556 | ||||
|
1557 | ||||
|
1558 | class ienv(Table): | |||
|
1559 | """ | |||
|
1560 | List environment variables. | |||
|
1561 | ||||
|
1562 | Example:: | |||
|
1563 | ||||
|
1564 | >>> ienv | |||
|
1565 | """ | |||
|
1566 | ||||
|
1567 | def __iter__(self): | |||
|
1568 | fields = ("key", "value") | |||
|
1569 | for (key, value) in os.environ.iteritems(): | |||
|
1570 | yield Fields(fields, key=key, value=value) | |||
|
1571 | ||||
|
1572 | def __xrepr__(self, mode="default"): | |||
|
1573 | if mode == "header" or mode == "cell": | |||
|
1574 | yield (astyle.style_default, "%s()" % self.__class__.__name__) | |||
|
1575 | else: | |||
|
1576 | yield (astyle.style_default, repr(self)) | |||
|
1577 | ||||
|
1578 | ||||
|
1579 | class ihist(Table): | |||
|
1580 | """ | |||
|
1581 | IPython input history | |||
|
1582 | ||||
|
1583 | Example:: | |||
|
1584 | ||||
|
1585 | >>> ihist | |||
|
1586 | >>> ihist(True) (raw mode) | |||
|
1587 | """ | |||
|
1588 | def __init__(self, raw=True): | |||
|
1589 | self.raw = raw | |||
|
1590 | ||||
|
1591 | def __iter__(self): | |||
|
1592 | api = ipapi.get() | |||
|
1593 | if self.raw: | |||
|
1594 | for line in api.IP.input_hist_raw: | |||
|
1595 | yield line.rstrip("\n") | |||
|
1596 | else: | |||
|
1597 | for line in api.IP.input_hist: | |||
|
1598 | yield line.rstrip("\n") | |||
|
1599 | ||||
|
1600 | ||||
|
1601 | class Alias(object): | |||
|
1602 | """ | |||
|
1603 | Entry in the alias table | |||
|
1604 | """ | |||
|
1605 | def __init__(self, name, args, command): | |||
|
1606 | self.name = name | |||
|
1607 | self.args = args | |||
|
1608 | self.command = command | |||
|
1609 | ||||
|
1610 | def __xattrs__(self, mode="default"): | |||
|
1611 | return ("name", "args", "command") | |||
|
1612 | ||||
|
1613 | ||||
|
1614 | class ialias(Table): | |||
|
1615 | """ | |||
|
1616 | IPython alias list | |||
|
1617 | ||||
|
1618 | Example:: | |||
|
1619 | ||||
|
1620 | >>> ialias | |||
|
1621 | """ | |||
|
1622 | def __iter__(self): | |||
|
1623 | api = ipapi.get() | |||
|
1624 | ||||
|
1625 | for (name, (args, command)) in api.IP.alias_table.iteritems(): | |||
|
1626 | yield Alias(name, args, command) | |||
|
1627 | ||||
|
1628 | ||||
|
1629 | class icsv(Pipe): | |||
|
1630 | """ | |||
|
1631 | This ``Pipe`` turns the input (with must be a pipe outputting lines | |||
|
1632 | or an ``ifile``) into lines of CVS columns. | |||
|
1633 | """ | |||
|
1634 | def __init__(self, **csvargs): | |||
|
1635 | """ | |||
|
1636 | Create an ``icsv`` object. ``cvsargs`` will be passed through as | |||
|
1637 | keyword arguments to ``cvs.reader()``. | |||
|
1638 | """ | |||
|
1639 | self.csvargs = csvargs | |||
|
1640 | ||||
|
1641 | def __iter__(self): | |||
|
1642 | input = self.input | |||
|
1643 | if isinstance(input, ifile): | |||
|
1644 | input = input.open("rb") | |||
|
1645 | reader = csv.reader(input, **self.csvargs) | |||
|
1646 | for line in reader: | |||
|
1647 | yield List(line) | |||
|
1648 | ||||
|
1649 | def __xrepr__(self, mode="default"): | |||
|
1650 | yield (-1, False) | |||
|
1651 | if mode == "header" or mode == "footer": | |||
|
1652 | input = getattr(self, "input", None) | |||
|
1653 | if input is not None: | |||
|
1654 | for part in xrepr(input, mode): | |||
|
1655 | yield part | |||
|
1656 | yield (astyle.style_default, " | ") | |||
|
1657 | yield (astyle.style_default, "%s(" % self.__class__.__name__) | |||
|
1658 | for (i, (name, value)) in enumerate(self.csvargs.iteritems()): | |||
|
1659 | if i: | |||
|
1660 | yield (astyle.style_default, ", ") | |||
|
1661 | yield (astyle.style_default, name) | |||
|
1662 | yield (astyle.style_default, "=") | |||
|
1663 | for part in xrepr(value, "default"): | |||
|
1664 | yield part | |||
|
1665 | yield (astyle.style_default, ")") | |||
|
1666 | else: | |||
|
1667 | yield (astyle.style_default, repr(self)) | |||
|
1668 | ||||
|
1669 | def __repr__(self): | |||
|
1670 | args = ", ".join(["%s=%r" % item for item in self.csvargs.iteritems()]) | |||
|
1671 | return "<%s.%s %s at 0x%x>" % \ | |||
|
1672 | (self.__class__.__module__, self.__class__.__name__, args, id(self)) | |||
|
1673 | ||||
|
1674 | ||||
|
1675 | class ix(Table): | |||
|
1676 | """ | |||
|
1677 | Execute a system command and list its output as lines | |||
|
1678 | (similar to ``os.popen()``). | |||
|
1679 | ||||
|
1680 | Examples:: | |||
|
1681 | ||||
|
1682 | >>> ix("ps x") | |||
|
1683 | >>> ix("find .") | ifile | |||
|
1684 | """ | |||
|
1685 | def __init__(self, cmd): | |||
|
1686 | self.cmd = cmd | |||
|
1687 | self._pipeout = None | |||
|
1688 | ||||
|
1689 | def __iter__(self): | |||
|
1690 | (_pipein, self._pipeout) = os.popen4(self.cmd) | |||
|
1691 | _pipein.close() | |||
|
1692 | for l in self._pipeout: | |||
|
1693 | yield l.rstrip("\r\n") | |||
|
1694 | self._pipeout.close() | |||
|
1695 | self._pipeout = None | |||
|
1696 | ||||
|
1697 | def __del__(self): | |||
|
1698 | if self._pipeout is not None and not self._pipeout.closed: | |||
|
1699 | self._pipeout.close() | |||
|
1700 | self._pipeout = None | |||
|
1701 | ||||
|
1702 | def __xrepr__(self, mode="default"): | |||
|
1703 | if mode == "header" or mode == "footer": | |||
|
1704 | yield (astyle.style_default, | |||
|
1705 | "%s(%r)" % (self.__class__.__name__, self.cmd)) | |||
|
1706 | else: | |||
|
1707 | yield (astyle.style_default, repr(self)) | |||
|
1708 | ||||
|
1709 | def __repr__(self): | |||
|
1710 | return "%s.%s(%r)" % \ | |||
|
1711 | (self.__class__.__module__, self.__class__.__name__, self.cmd) | |||
|
1712 | ||||
|
1713 | ||||
|
1714 | class ifilter(Pipe): | |||
|
1715 | """ | |||
|
1716 | Filter an input pipe. Only objects where an expression evaluates to true | |||
|
1717 | (and doesn't raise an exception) are listed. | |||
|
1718 | ||||
|
1719 | Examples:: | |||
|
1720 | ||||
|
1721 | >>> ils | ifilter("_.isfile() and size>1000") | |||
|
1722 | >>> igrp | ifilter("len(mem)") | |||
|
1723 | >>> sys.modules | ifilter(lambda _:_.value is not None) | |||
|
1724 | """ | |||
|
1725 | ||||
|
1726 | def __init__(self, expr, globals=None, errors="raiseifallfail"): | |||
|
1727 | """ | |||
|
1728 | Create an ``ifilter`` object. ``expr`` can be a callable or a string | |||
|
1729 | containing an expression. ``globals`` will be used as the global | |||
|
1730 | namespace for calling string expressions (defaulting to IPython's | |||
|
1731 | user namespace). ``errors`` specifies how exception during evaluation | |||
|
1732 | of ``expr`` are handled: | |||
|
1733 | ||||
|
1734 | ``"drop"`` | |||
|
1735 | drop all items that have errors; | |||
|
1736 | ||||
|
1737 | ``"keep"`` | |||
|
1738 | keep all items that have errors; | |||
|
1739 | ||||
|
1740 | ``"keeperror"`` | |||
|
1741 | keep the exception of all items that have errors; | |||
|
1742 | ||||
|
1743 | ``"raise"`` | |||
|
1744 | raise the exception; | |||
|
1745 | ||||
|
1746 | ``"raiseifallfail"`` | |||
|
1747 | raise the first exception if all items have errors; otherwise drop | |||
|
1748 | those with errors (this is the default). | |||
|
1749 | """ | |||
|
1750 | self.expr = expr | |||
|
1751 | self.globals = globals | |||
|
1752 | self.errors = errors | |||
|
1753 | ||||
|
1754 | def __iter__(self): | |||
|
1755 | if callable(self.expr): | |||
|
1756 | test = self.expr | |||
|
1757 | else: | |||
|
1758 | g = getglobals(self.globals) | |||
|
1759 | expr = compile(self.expr, "ipipe-expression", "eval") | |||
|
1760 | def test(item): | |||
|
1761 | return eval(expr, g, AttrNamespace(item)) | |||
|
1762 | ||||
|
1763 | ok = 0 | |||
|
1764 | exc_info = None | |||
|
1765 | for item in xiter(self.input): | |||
|
1766 | try: | |||
|
1767 | if test(item): | |||
|
1768 | yield item | |||
|
1769 | ok += 1 | |||
|
1770 | except (KeyboardInterrupt, SystemExit): | |||
|
1771 | raise | |||
|
1772 | except Exception, exc: | |||
|
1773 | if self.errors == "drop": | |||
|
1774 | pass # Ignore errors | |||
|
1775 | elif self.errors == "keep": | |||
|
1776 | yield item | |||
|
1777 | elif self.errors == "keeperror": | |||
|
1778 | yield exc | |||
|
1779 | elif self.errors == "raise": | |||
|
1780 | raise | |||
|
1781 | elif self.errors == "raiseifallfail": | |||
|
1782 | if exc_info is None: | |||
|
1783 | exc_info = sys.exc_info() | |||
|
1784 | if not ok and exc_info is not None: | |||
|
1785 | raise exc_info[0], exc_info[1], exc_info[2] | |||
|
1786 | ||||
|
1787 | def __xrepr__(self, mode="default"): | |||
|
1788 | if mode == "header" or mode == "footer": | |||
|
1789 | input = getattr(self, "input", None) | |||
|
1790 | if input is not None: | |||
|
1791 | for part in xrepr(input, mode): | |||
|
1792 | yield part | |||
|
1793 | yield (astyle.style_default, " | ") | |||
|
1794 | yield (astyle.style_default, "%s(" % self.__class__.__name__) | |||
|
1795 | for part in xrepr(self.expr, "default"): | |||
|
1796 | yield part | |||
|
1797 | yield (astyle.style_default, ")") | |||
|
1798 | else: | |||
|
1799 | yield (astyle.style_default, repr(self)) | |||
|
1800 | ||||
|
1801 | def __repr__(self): | |||
|
1802 | return "<%s.%s expr=%r at 0x%x>" % \ | |||
|
1803 | (self.__class__.__module__, self.__class__.__name__, | |||
|
1804 | self.expr, id(self)) | |||
|
1805 | ||||
|
1806 | ||||
|
1807 | class ieval(Pipe): | |||
|
1808 | """ | |||
|
1809 | Evaluate an expression for each object in the input pipe. | |||
|
1810 | ||||
|
1811 | Examples:: | |||
|
1812 | ||||
|
1813 | >>> ils | ieval("_.abspath()") | |||
|
1814 | >>> sys.path | ieval(ifile) | |||
|
1815 | """ | |||
|
1816 | ||||
|
1817 | def __init__(self, expr, globals=None, errors="raiseifallfail"): | |||
|
1818 | """ | |||
|
1819 | Create an ``ieval`` object. ``expr`` can be a callable or a string | |||
|
1820 | containing an expression. For the meaning of ``globals`` and | |||
|
1821 | ``errors`` see ``ifilter``. | |||
|
1822 | """ | |||
|
1823 | self.expr = expr | |||
|
1824 | self.globals = globals | |||
|
1825 | self.errors = errors | |||
|
1826 | ||||
|
1827 | def __iter__(self): | |||
|
1828 | if callable(self.expr): | |||
|
1829 | do = self.expr | |||
|
1830 | else: | |||
|
1831 | g = getglobals(self.globals) | |||
|
1832 | expr = compile(self.expr, "ipipe-expression", "eval") | |||
|
1833 | def do(item): | |||
|
1834 | return eval(expr, g, AttrNamespace(item)) | |||
|
1835 | ||||
|
1836 | ok = 0 | |||
|
1837 | exc_info = None | |||
|
1838 | for item in xiter(self.input): | |||
|
1839 | try: | |||
|
1840 | yield do(item) | |||
|
1841 | except (KeyboardInterrupt, SystemExit): | |||
|
1842 | raise | |||
|
1843 | except Exception, exc: | |||
|
1844 | if self.errors == "drop": | |||
|
1845 | pass # Ignore errors | |||
|
1846 | elif self.errors == "keep": | |||
|
1847 | yield item | |||
|
1848 | elif self.errors == "keeperror": | |||
|
1849 | yield exc | |||
|
1850 | elif self.errors == "raise": | |||
|
1851 | raise | |||
|
1852 | elif self.errors == "raiseifallfail": | |||
|
1853 | if exc_info is None: | |||
|
1854 | exc_info = sys.exc_info() | |||
|
1855 | if not ok and exc_info is not None: | |||
|
1856 | raise exc_info[0], exc_info[1], exc_info[2] | |||
|
1857 | ||||
|
1858 | def __xrepr__(self, mode="default"): | |||
|
1859 | if mode == "header" or mode == "footer": | |||
|
1860 | input = getattr(self, "input", None) | |||
|
1861 | if input is not None: | |||
|
1862 | for part in xrepr(input, mode): | |||
|
1863 | yield part | |||
|
1864 | yield (astyle.style_default, " | ") | |||
|
1865 | yield (astyle.style_default, "%s(" % self.__class__.__name__) | |||
|
1866 | for part in xrepr(self.expr, "default"): | |||
|
1867 | yield part | |||
|
1868 | yield (astyle.style_default, ")") | |||
|
1869 | else: | |||
|
1870 | yield (astyle.style_default, repr(self)) | |||
|
1871 | ||||
|
1872 | def __repr__(self): | |||
|
1873 | return "<%s.%s expr=%r at 0x%x>" % \ | |||
|
1874 | (self.__class__.__module__, self.__class__.__name__, | |||
|
1875 | self.expr, id(self)) | |||
|
1876 | ||||
|
1877 | ||||
|
1878 | class ienum(Pipe): | |||
|
1879 | """ | |||
|
1880 | Enumerate the input pipe (i.e. wrap each input object in an object | |||
|
1881 | with ``index`` and ``object`` attributes). | |||
|
1882 | ||||
|
1883 | Examples:: | |||
|
1884 | ||||
|
1885 | >>> xrange(20) | ieval("_,_*_") | ienum | ifilter("index % 2 == 0") | ieval("object") | |||
|
1886 | """ | |||
|
1887 | def __iter__(self): | |||
|
1888 | fields = ("index", "object") | |||
|
1889 | for (index, object) in enumerate(xiter(self.input)): | |||
|
1890 | yield Fields(fields, index=index, object=object) | |||
|
1891 | ||||
|
1892 | ||||
|
1893 | class isort(Pipe): | |||
|
1894 | """ | |||
|
1895 | Sorts the input pipe. | |||
|
1896 | ||||
|
1897 | Examples:: | |||
|
1898 | ||||
|
1899 | >>> ils | isort("size") | |||
|
1900 | >>> ils | isort("_.isdir(), _.lower()", reverse=True) | |||
|
1901 | """ | |||
|
1902 | ||||
|
1903 | def __init__(self, key=None, globals=None, reverse=False): | |||
|
1904 | """ | |||
|
1905 | Create an ``isort`` object. ``key`` can be a callable or a string | |||
|
1906 | containing an expression (or ``None`` in which case the items | |||
|
1907 | themselves will be sorted). If ``reverse`` is true the sort order | |||
|
1908 | will be reversed. For the meaning of ``globals`` see ``ifilter``. | |||
|
1909 | """ | |||
|
1910 | self.key = key | |||
|
1911 | self.globals = globals | |||
|
1912 | self.reverse = reverse | |||
|
1913 | ||||
|
1914 | def __iter__(self): | |||
|
1915 | if self.key is None: | |||
|
1916 | items = sorted(xiter(self.input), reverse=self.reverse) | |||
|
1917 | elif callable(self.key): | |||
|
1918 | items = sorted(xiter(self.input), key=self.key, reverse=self.reverse) | |||
|
1919 | else: | |||
|
1920 | g = getglobals(self.globals) | |||
|
1921 | key = compile(self.key, "ipipe-expression", "eval") | |||
|
1922 | def realkey(item): | |||
|
1923 | return eval(key, g, AttrNamespace(item)) | |||
|
1924 | items = sorted(xiter(self.input), key=realkey, reverse=self.reverse) | |||
|
1925 | for item in items: | |||
|
1926 | yield item | |||
|
1927 | ||||
|
1928 | def __xrepr__(self, mode="default"): | |||
|
1929 | if mode == "header" or mode == "footer": | |||
|
1930 | input = getattr(self, "input", None) | |||
|
1931 | if input is not None: | |||
|
1932 | for part in xrepr(input, mode): | |||
|
1933 | yield part | |||
|
1934 | yield (astyle.style_default, " | ") | |||
|
1935 | yield (astyle.style_default, "%s(" % self.__class__.__name__) | |||
|
1936 | for part in xrepr(self.key, "default"): | |||
|
1937 | yield part | |||
|
1938 | if self.reverse: | |||
|
1939 | yield (astyle.style_default, ", ") | |||
|
1940 | for part in xrepr(True, "default"): | |||
|
1941 | yield part | |||
|
1942 | yield (astyle.style_default, ")") | |||
|
1943 | else: | |||
|
1944 | yield (astyle.style_default, repr(self)) | |||
|
1945 | ||||
|
1946 | def __repr__(self): | |||
|
1947 | return "<%s.%s key=%r reverse=%r at 0x%x>" % \ | |||
|
1948 | (self.__class__.__module__, self.__class__.__name__, | |||
|
1949 | self.key, self.reverse, id(self)) | |||
|
1950 | ||||
|
1951 | ||||
|
1952 | tab = 3 # for expandtabs() | |||
|
1953 | ||||
|
1954 | def _format(field): | |||
|
1955 | if isinstance(field, str): | |||
|
1956 | text = repr(field.expandtabs(tab))[1:-1] | |||
|
1957 | elif isinstance(field, unicode): | |||
|
1958 | text = repr(field.expandtabs(tab))[2:-1] | |||
|
1959 | elif isinstance(field, datetime.datetime): | |||
|
1960 | # Don't use strftime() here, as this requires year >= 1900 | |||
|
1961 | text = "%04d-%02d-%02d %02d:%02d:%02d.%06d" % \ | |||
|
1962 | (field.year, field.month, field.day, | |||
|
1963 | field.hour, field.minute, field.second, field.microsecond) | |||
|
1964 | elif isinstance(field, datetime.date): | |||
|
1965 | text = "%04d-%02d-%02d" % (field.year, field.month, field.day) | |||
|
1966 | else: | |||
|
1967 | text = repr(field) | |||
|
1968 | return text | |||
|
1969 | ||||
|
1970 | ||||
|
1971 | class Display(object): | |||
|
1972 | class __metaclass__(type): | |||
|
1973 | def __ror__(self, input): | |||
|
1974 | return input | self() | |||
|
1975 | ||||
|
1976 | def __init__(self, input=None): | |||
|
1977 | self.input = input | |||
|
1978 | ||||
|
1979 | def __ror__(self, input): | |||
|
1980 | self.input = input | |||
|
1981 | return self | |||
|
1982 | ||||
|
1983 | def display(self): | |||
|
1984 | pass | |||
|
1985 | ||||
|
1986 | ||||
|
1987 | class iless(Display): | |||
|
1988 | cmd = "less --quit-if-one-screen --LONG-PROMPT --LINE-NUMBERS --chop-long-lines --shift=8 --RAW-CONTROL-CHARS" | |||
|
1989 | ||||
|
1990 | def display(self): | |||
|
1991 | try: | |||
|
1992 | pager = os.popen(self.cmd, "w") | |||
|
1993 | try: | |||
|
1994 | for item in xiter(self.input): | |||
|
1995 | first = False | |||
|
1996 | for attr in xattrs(item, "default"): | |||
|
1997 | if first: | |||
|
1998 | first = False | |||
|
1999 | else: | |||
|
2000 | pager.write(" ") | |||
|
2001 | attr = upgradexattr(attr) | |||
|
2002 | if not isinstance(attr, SelfDescriptor): | |||
|
2003 | pager.write(attr.name()) | |||
|
2004 | pager.write("=") | |||
|
2005 | pager.write(str(attr.value(item))) | |||
|
2006 | pager.write("\n") | |||
|
2007 | finally: | |||
|
2008 | pager.close() | |||
|
2009 | except Exception, exc: | |||
|
2010 | print "%s: %s" % (exc.__class__.__name__, str(exc)) | |||
|
2011 | ||||
|
2012 | ||||
|
2013 | class _RedirectIO(object): | |||
|
2014 | def __init__(self,*args,**kwargs): | |||
|
2015 | """ | |||
|
2016 | Map the system output streams to self. | |||
|
2017 | """ | |||
|
2018 | self.stream = StringIO.StringIO() | |||
|
2019 | self.stdout = sys.stdout | |||
|
2020 | sys.stdout = self | |||
|
2021 | self.stderr = sys.stderr | |||
|
2022 | sys.stderr = self | |||
|
2023 | ||||
|
2024 | def write(self, text): | |||
|
2025 | """ | |||
|
2026 | Write both to screen and to self. | |||
|
2027 | """ | |||
|
2028 | self.stream.write(text) | |||
|
2029 | self.stdout.write(text) | |||
|
2030 | if "\n" in text: | |||
|
2031 | self.stdout.flush() | |||
|
2032 | ||||
|
2033 | def writelines(self, lines): | |||
|
2034 | """ | |||
|
2035 | Write lines both to screen and to self. | |||
|
2036 | """ | |||
|
2037 | self.stream.writelines(lines) | |||
|
2038 | self.stdout.writelines(lines) | |||
|
2039 | self.stdout.flush() | |||
|
2040 | ||||
|
2041 | def restore(self): | |||
|
2042 | """ | |||
|
2043 | Restore the default system streams. | |||
|
2044 | """ | |||
|
2045 | self.stdout.flush() | |||
|
2046 | self.stderr.flush() | |||
|
2047 | sys.stdout = self.stdout | |||
|
2048 | sys.stderr = self.stderr | |||
|
2049 | ||||
|
2050 | ||||
|
2051 | class icap(Table): | |||
|
2052 | """ | |||
|
2053 | Execute a python string and capture any output to stderr/stdout. | |||
|
2054 | ||||
|
2055 | Examples:: | |||
|
2056 | ||||
|
2057 | >>> import time | |||
|
2058 | >>> icap("for i in range(10): print i, time.sleep(0.1)") | |||
|
2059 | ||||
|
2060 | """ | |||
|
2061 | def __init__(self, expr, globals=None): | |||
|
2062 | self.expr = expr | |||
|
2063 | self.globals = globals | |||
|
2064 | log = _RedirectIO() | |||
|
2065 | try: | |||
|
2066 | exec(expr, getglobals(globals)) | |||
|
2067 | finally: | |||
|
2068 | log.restore() | |||
|
2069 | self.stream = log.stream | |||
|
2070 | ||||
|
2071 | def __iter__(self): | |||
|
2072 | self.stream.seek(0) | |||
|
2073 | for line in self.stream: | |||
|
2074 | yield line.rstrip("\r\n") | |||
|
2075 | ||||
|
2076 | def __xrepr__(self, mode="default"): | |||
|
2077 | if mode == "header" or mode == "footer": | |||
|
2078 | yield (astyle.style_default, | |||
|
2079 | "%s(%r)" % (self.__class__.__name__, self.expr)) | |||
|
2080 | else: | |||
|
2081 | yield (astyle.style_default, repr(self)) | |||
|
2082 | ||||
|
2083 | def __repr__(self): | |||
|
2084 | return "%s.%s(%r)" % \ | |||
|
2085 | (self.__class__.__module__, self.__class__.__name__, self.expr) | |||
|
2086 | ||||
|
2087 | ||||
|
2088 | def xformat(value, mode, maxlength): | |||
|
2089 | align = None | |||
|
2090 | full = True | |||
|
2091 | width = 0 | |||
|
2092 | text = astyle.Text() | |||
|
2093 | for (style, part) in xrepr(value, mode): | |||
|
2094 | # only consider the first result | |||
|
2095 | if align is None: | |||
|
2096 | if isinstance(style, int): | |||
|
2097 | # (style, text) really is (alignment, stop) | |||
|
2098 | align = style | |||
|
2099 | full = part | |||
|
2100 | continue | |||
|
2101 | else: | |||
|
2102 | align = -1 | |||
|
2103 | full = True | |||
|
2104 | if not isinstance(style, int): | |||
|
2105 | text.append((style, part)) | |||
|
2106 | width += len(part) | |||
|
2107 | if width >= maxlength and not full: | |||
|
2108 | text.append((astyle.style_ellisis, "...")) | |||
|
2109 | width += 3 | |||
|
2110 | break | |||
|
2111 | if align is None: # default to left alignment | |||
|
2112 | align = -1 | |||
|
2113 | return (align, width, text) | |||
|
2114 | ||||
|
2115 | ||||
|
2116 | ||||
|
2117 | import astyle | |||
|
2118 | ||||
|
2119 | class idump(Display): | |||
|
2120 | # The approximate maximum length of a column entry | |||
|
2121 | maxattrlength = 200 | |||
|
2122 | ||||
|
2123 | # Style for column names | |||
|
2124 | style_header = astyle.Style.fromstr("white:black:bold") | |||
|
2125 | ||||
|
2126 | def __init__(self, input=None, *attrs): | |||
|
2127 | Display.__init__(self, input) | |||
|
2128 | self.attrs = [upgradexattr(attr) for attr in attrs] | |||
|
2129 | self.headerpadchar = " " | |||
|
2130 | self.headersepchar = "|" | |||
|
2131 | self.datapadchar = " " | |||
|
2132 | self.datasepchar = "|" | |||
|
2133 | ||||
|
2134 | def display(self): | |||
|
2135 | stream = genutils.Term.cout | |||
|
2136 | allattrs = [] | |||
|
2137 | attrset = set() | |||
|
2138 | colwidths = {} | |||
|
2139 | rows = [] | |||
|
2140 | for item in xiter(self.input): | |||
|
2141 | row = {} | |||
|
2142 | attrs = self.attrs | |||
|
2143 | if not attrs: | |||
|
2144 | attrs = xattrs(item, "default") | |||
|
2145 | for attr in attrs: | |||
|
2146 | if attr not in attrset: | |||
|
2147 | allattrs.append(attr) | |||
|
2148 | attrset.add(attr) | |||
|
2149 | colwidths[attr] = len(attr.name()) | |||
|
2150 | try: | |||
|
2151 | value = attr.value(item) | |||
|
2152 | except (KeyboardInterrupt, SystemExit): | |||
|
2153 | raise | |||
|
2154 | except Exception, exc: | |||
|
2155 | value = exc | |||
|
2156 | (align, width, text) = xformat(value, "cell", self.maxattrlength) | |||
|
2157 | colwidths[attr] = max(colwidths[attr], width) | |||
|
2158 | # remember alignment, length and colored parts | |||
|
2159 | row[attr] = (align, width, text) | |||
|
2160 | rows.append(row) | |||
|
2161 | ||||
|
2162 | stream.write("\n") | |||
|
2163 | for (i, attr) in enumerate(allattrs): | |||
|
2164 | attrname = attr.name() | |||
|
2165 | self.style_header(attrname).write(stream) | |||
|
2166 | spc = colwidths[attr] - len(attrname) | |||
|
2167 | if i < len(colwidths)-1: | |||
|
2168 | stream.write(self.headerpadchar*spc) | |||
|
2169 | stream.write(self.headersepchar) | |||
|
2170 | stream.write("\n") | |||
|
2171 | ||||
|
2172 | for row in rows: | |||
|
2173 | for (i, attr) in enumerate(allattrs): | |||
|
2174 | (align, width, text) = row[attr] | |||
|
2175 | spc = colwidths[attr] - width | |||
|
2176 | if align == -1: | |||
|
2177 | text.write(stream) | |||
|
2178 | if i < len(colwidths)-1: | |||
|
2179 | stream.write(self.datapadchar*spc) | |||
|
2180 | elif align == 0: | |||
|
2181 | spc = colwidths[attr] - width | |||
|
2182 | spc1 = spc//2 | |||
|
2183 | spc2 = spc-spc1 | |||
|
2184 | stream.write(self.datapadchar*spc1) | |||
|
2185 | text.write(stream) | |||
|
2186 | if i < len(colwidths)-1: | |||
|
2187 | stream.write(self.datapadchar*spc2) | |||
|
2188 | else: | |||
|
2189 | stream.write(self.datapadchar*spc) | |||
|
2190 | text.write(stream) | |||
|
2191 | if i < len(colwidths)-1: | |||
|
2192 | stream.write(self.datasepchar) | |||
|
2193 | stream.write("\n") | |||
|
2194 | ||||
|
2195 | ||||
|
2196 | class AttributeDetail(Table): | |||
|
2197 | """ | |||
|
2198 | ``AttributeDetail`` objects are use for displaying a detailed list of object | |||
|
2199 | attributes. | |||
|
2200 | """ | |||
|
2201 | def __init__(self, object, descriptor): | |||
|
2202 | self.object = object | |||
|
2203 | self.descriptor = descriptor | |||
|
2204 | ||||
|
2205 | def __iter__(self): | |||
|
2206 | return self.descriptor.iter(self.object) | |||
|
2207 | ||||
|
2208 | def name(self): | |||
|
2209 | return self.descriptor.name() | |||
|
2210 | ||||
|
2211 | def attrtype(self): | |||
|
2212 | return self.descriptor.attrtype(self.object) | |||
|
2213 | ||||
|
2214 | def valuetype(self): | |||
|
2215 | return self.descriptor.valuetype(self.object) | |||
|
2216 | ||||
|
2217 | def doc(self): | |||
|
2218 | return self.descriptor.doc(self.object) | |||
|
2219 | ||||
|
2220 | def shortdoc(self): | |||
|
2221 | return self.descriptor.shortdoc(self.object) | |||
|
2222 | ||||
|
2223 | def value(self): | |||
|
2224 | return self.descriptor.value(self.object) | |||
|
2225 | ||||
|
2226 | def __xattrs__(self, mode="default"): | |||
|
2227 | attrs = ("name()", "attrtype()", "valuetype()", "value()", "shortdoc()") | |||
|
2228 | if mode == "detail": | |||
|
2229 | attrs += ("doc()",) | |||
|
2230 | return attrs | |||
|
2231 | ||||
|
2232 | def __xrepr__(self, mode="default"): | |||
|
2233 | yield (-1, True) | |||
|
2234 | valuetype = self.valuetype() | |||
|
2235 | if valuetype is not noitem: | |||
|
2236 | for part in xrepr(valuetype): | |||
|
2237 | yield part | |||
|
2238 | yield (astyle.style_default, " ") | |||
|
2239 | yield (astyle.style_default, self.attrtype()) | |||
|
2240 | yield (astyle.style_default, " ") | |||
|
2241 | yield (astyle.style_default, self.name()) | |||
|
2242 | yield (astyle.style_default, " of ") | |||
|
2243 | for part in xrepr(self.object): | |||
|
2244 | yield part | |||
|
2245 | ||||
|
2246 | ||||
|
2247 | try: | |||
|
2248 | from ibrowse import ibrowse | |||
|
2249 | except ImportError: | |||
|
2250 | # No curses (probably Windows) => try igrid | |||
|
2251 | try: | |||
|
2252 | from igrid import igrid | |||
|
2253 | except ImportError: | |||
|
2254 | # no wx either => use ``idump`` as the default display. | |||
|
2255 | defaultdisplay = idump | |||
|
2256 | else: | |||
|
2257 | defaultdisplay = igrid | |||
|
2258 | __all__.append("igrid") | |||
|
2259 | else: | |||
|
2260 | defaultdisplay = ibrowse | |||
|
2261 | __all__.append("ibrowse") | |||
|
2262 | ||||
|
2263 | ||||
|
2264 | # If we're running under IPython, register our objects with IPython's | |||
|
2265 | # generic function ``result_display``, else install a displayhook | |||
|
2266 | # directly as sys.displayhook | |||
|
2267 | if generics is not None: | |||
|
2268 | def display_display(obj): | |||
|
2269 | return obj.display() | |||
|
2270 | generics.result_display.when_type(Display)(display_display) | |||
|
2271 | ||||
|
2272 | def display_tableobject(obj): | |||
|
2273 | return display_display(defaultdisplay(obj)) | |||
|
2274 | generics.result_display.when_type(Table)(display_tableobject) | |||
|
2275 | ||||
|
2276 | def display_tableclass(obj): | |||
|
2277 | return display_tableobject(obj()) | |||
|
2278 | generics.result_display.when_type(Table.__metaclass__)(display_tableclass) | |||
|
2279 | else: | |||
|
2280 | def installdisplayhook(): | |||
|
2281 | _originalhook = sys.displayhook | |||
|
2282 | def displayhook(obj): | |||
|
2283 | if isinstance(obj, type) and issubclass(obj, Table): | |||
|
2284 | obj = obj() | |||
|
2285 | if isinstance(obj, Table): | |||
|
2286 | obj = defaultdisplay(obj) | |||
|
2287 | if isinstance(obj, Display): | |||
|
2288 | return obj.display() | |||
|
2289 | else: | |||
|
2290 | _originalhook(obj) | |||
|
2291 | sys.displayhook = displayhook | |||
|
2292 | installdisplayhook() |
@@ -0,0 +1,17 | |||||
|
1 | """ Install various IPython completers | |||
|
2 | ||||
|
3 | IPython extension that installs the completers related to external apps. | |||
|
4 | ||||
|
5 | The actual implementations are in Extensions/ipy_completers.py | |||
|
6 | ||||
|
7 | """ | |||
|
8 | import IPython.ipapi | |||
|
9 | ||||
|
10 | ip = IPython.ipapi.get() | |||
|
11 | ||||
|
12 | from ipy_completers import * | |||
|
13 | ||||
|
14 | ip.set_hook('complete_command', apt_completer, re_key = '.*apt-get') | |||
|
15 | ip.set_hook('complete_command', svn_completer, str_key = 'svn') | |||
|
16 | ip.set_hook('complete_command', hg_completer, str_key = 'hg') | |||
|
17 | ip.set_hook('complete_command', bzr_completer, str_key = 'bzr') |
@@ -0,0 +1,359 | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | ||||
|
3 | """ Implementations for various useful completers | |||
|
4 | ||||
|
5 | See Extensions/ipy_stock_completers.py on examples of how to enable a completer, | |||
|
6 | but the basic idea is to do: | |||
|
7 | ||||
|
8 | ip.set_hook('complete_command', svn_completer, str_key = 'svn') | |||
|
9 | ||||
|
10 | """ | |||
|
11 | import IPython.ipapi | |||
|
12 | import glob,os,shlex,sys | |||
|
13 | import inspect | |||
|
14 | from time import time | |||
|
15 | ip = IPython.ipapi.get() | |||
|
16 | ||||
|
17 | try: | |||
|
18 | set | |||
|
19 | except: | |||
|
20 | from sets import Set as set | |||
|
21 | ||||
|
22 | TIMEOUT_STORAGE = 3 #Time in seconds after which the rootmodules will be stored | |||
|
23 | TIMEOUT_GIVEUP = 20 #Time in seconds after which we give up | |||
|
24 | ||||
|
25 | def quick_completer(cmd, completions): | |||
|
26 | """ Easily create a trivial completer for a command. | |||
|
27 | ||||
|
28 | Takes either a list of completions, or all completions in string | |||
|
29 | (that will be split on whitespace) | |||
|
30 | ||||
|
31 | Example:: | |||
|
32 | ||||
|
33 | [d:\ipython]|1> import ipy_completers | |||
|
34 | [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz']) | |||
|
35 | [d:\ipython]|3> foo b<TAB> | |||
|
36 | bar baz | |||
|
37 | [d:\ipython]|3> foo ba | |||
|
38 | """ | |||
|
39 | if isinstance(completions, basestring): | |||
|
40 | ||||
|
41 | completions = completions.split() | |||
|
42 | def do_complete(self,event): | |||
|
43 | return completions | |||
|
44 | ||||
|
45 | ip.set_hook('complete_command',do_complete, str_key = cmd) | |||
|
46 | ||||
|
47 | def getRootModules(): | |||
|
48 | """ | |||
|
49 | Returns a list containing the names of all the modules available in the | |||
|
50 | folders of the pythonpath. | |||
|
51 | """ | |||
|
52 | modules = [] | |||
|
53 | if ip.db.has_key('rootmodules'): | |||
|
54 | return ip.db['rootmodules'] | |||
|
55 | t = time() | |||
|
56 | store = False | |||
|
57 | for path in sys.path: | |||
|
58 | modules += moduleList(path) | |||
|
59 | if time() - t >= TIMEOUT_STORAGE and not store: | |||
|
60 | store = True | |||
|
61 | print "\nCaching the list of root modules, please wait!" | |||
|
62 | print "(This will only be done once - type '%rehashx' to " + \ | |||
|
63 | "reset cache!)" | |||
|
64 | ||||
|
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 | |||||
|
1 | """ Module with physical constants for use with ipython, profile | |||
|
2 | "physics". | |||
|
3 | ||||
|
4 | Definition of Fundamental Physical Constants, CODATA Recommended Values | |||
|
5 | ||||
|
6 | Source, Peter J. Mohr and Barry N. Taylor, | |||
|
7 | CODATA Recommended Values of the Fundamental | |||
|
8 | Physical Constants, 1998 | |||
|
9 | ||||
|
10 | Website: physics.nist.gov/constants | |||
|
11 | """ | |||
|
12 | # License: BSD-like | |||
|
13 | # Copyright: Gael Varoquaux (gael.varoquaux@normalesup.org) | |||
|
14 | ||||
|
15 | # inspired by maxima's physconst.mac by Cliff Yapp | |||
|
16 | ||||
|
17 | #from math import * # math MUST be imported BEFORE PhysicalQInteractive | |||
|
18 | from IPython.Extensions.PhysicalQInteractive import PhysicalQuantityInteractive | |||
|
19 | ||||
|
20 | # Math constants: | |||
|
21 | ||||
|
22 | # Pi mathematical constants | |||
|
23 | pi = 3.141592653589793238462643383279502884197169399375105820974944592 | |||
|
24 | ||||
|
25 | # Universal Constants | |||
|
26 | #------------------------------------------------------------------------- | |||
|
27 | ||||
|
28 | c = PhysicalQuantityInteractive(299792458 , 'm/s') | |||
|
29 | c.__doc__ = """speed of light in vacuum""" | |||
|
30 | c.__doc__ = "speed of light in vacuum" | |||
|
31 | ||||
|
32 | u_0 = PhysicalQuantityInteractive(4*pi*1E-7 , 'N/(A**2)') | |||
|
33 | u_0.__doc__ = """magnetic constant""" | |||
|
34 | mu_0 = PhysicalQuantityInteractive(4*pi*1E-7 , 'N/(A**2)') | |||
|
35 | ||||
|
36 | epsilon_0 = PhysicalQuantityInteractive(8.854187817E-12 , 'F/m') | |||
|
37 | epsilon_0.__doc__ = """electric constant """ | |||
|
38 | ||||
|
39 | Z_0 = PhysicalQuantityInteractive(376.730313461 , 'ohm') | |||
|
40 | Z_0.__doc__ = """characteristic impedance of vacuum """ | |||
|
41 | ||||
|
42 | G = PhysicalQuantityInteractive(6.673E-11 , 'm**3/(kg*s**2)') | |||
|
43 | G.__doc__ = """Newtonian constant of gravitation """ | |||
|
44 | ||||
|
45 | ||||
|
46 | h = PhysicalQuantityInteractive(6.62606876E-34 , 'J*s') | |||
|
47 | h.__doc__ = """Planck constant """ | |||
|
48 | ||||
|
49 | ||||
|
50 | h_eV = PhysicalQuantityInteractive(4.13566727E-15 , 'eV*s') | |||
|
51 | h_eV.__doc__ = """Planck constant in eVs """ | |||
|
52 | ||||
|
53 | ||||
|
54 | h_bar = PhysicalQuantityInteractive(1.054571596E-34 , 'J*s') | |||
|
55 | h_bar.__doc__ = """Hbar""" | |||
|
56 | ||||
|
57 | ||||
|
58 | h_bar_eV = PhysicalQuantityInteractive(6.58211889E-16 , 'eV*s') | |||
|
59 | h_bar_eV.__doc__ = """Hbar in eV""" | |||
|
60 | ||||
|
61 | ||||
|
62 | P_m = PhysicalQuantityInteractive(2.1767E-8 , 'kg') | |||
|
63 | P_m.__doc__ = """Planck mass""" | |||
|
64 | ||||
|
65 | ||||
|
66 | P_l = PhysicalQuantityInteractive(1.6160E-35 , 'm') | |||
|
67 | P_l.__doc__ = """Planck length """ | |||
|
68 | ||||
|
69 | ||||
|
70 | P_t = PhysicalQuantityInteractive(5.3906E-44 , 's') | |||
|
71 | P_t.__doc__ = """Planck time """ | |||
|
72 | ||||
|
73 | # Electromagnetic Constants | |||
|
74 | #------------------------------------------------------------------------ | |||
|
75 | ||||
|
76 | _e = PhysicalQuantityInteractive(1.602176462E-19 , 'C') | |||
|
77 | _e.__doc__ = """elementary charge""" | |||
|
78 | q = _e | |||
|
79 | ||||
|
80 | ||||
|
81 | capitalphi_0 = PhysicalQuantityInteractive(2.067833636E-15 , 'Wb') | |||
|
82 | capitalphi_0.__doc__ = """magnetic flux quantum """ | |||
|
83 | mfq_0 = PhysicalQuantityInteractive(2.067833636E-15 , 'Wb') | |||
|
84 | ||||
|
85 | ||||
|
86 | G_0 = PhysicalQuantityInteractive(7.748091696E-5 , 'S') | |||
|
87 | G_0.__doc__ = """conductance quantum """ | |||
|
88 | ||||
|
89 | ||||
|
90 | K_J = PhysicalQuantityInteractive(483597.898E9 , 'Hz/V') | |||
|
91 | K_J.__doc__ = """Josephson constant""" | |||
|
92 | ||||
|
93 | ||||
|
94 | R_K = PhysicalQuantityInteractive(25812.807572 , 'ohm') | |||
|
95 | R_K.__doc__ = """von Klitzing constant""" | |||
|
96 | ||||
|
97 | ||||
|
98 | u_B = PhysicalQuantityInteractive(927.400899E-26 , 'J/T') | |||
|
99 | u_B.__doc__ = """Bohr magneton""" | |||
|
100 | ||||
|
101 | ueVT_B = PhysicalQuantityInteractive(5.788381749E-5 , 'eV/T') | |||
|
102 | ueVT_B.__doc__ = """Bohr magneton in eV T-1""" | |||
|
103 | ||||
|
104 | ||||
|
105 | u_N = PhysicalQuantityInteractive(5.05078317E-27 , 'J/T') | |||
|
106 | u_N.__doc__ = """nuclear magneton """ | |||
|
107 | ||||
|
108 | ueVT_N = PhysicalQuantityInteractive(3.152451238E-8 , 'eV/T') | |||
|
109 | ueVT_N.__doc__ = """nuclear magneton in eV T-1 """ | |||
|
110 | ||||
|
111 | # Atomic and Nuclear Constants | |||
|
112 | # General | |||
|
113 | #------------------------------------------------------------------------- | |||
|
114 | # fine-structure constant | |||
|
115 | alpha = 7.297352533E-3 | |||
|
116 | ||||
|
117 | ||||
|
118 | Ry = PhysicalQuantityInteractive(10973731.568549 , '1/m') | |||
|
119 | Ry.__doc__ = """Rydberg constant """ | |||
|
120 | Ry_INF = PhysicalQuantityInteractive(10973731.568549 , '1/m') | |||
|
121 | ||||
|
122 | ||||
|
123 | a_0 = PhysicalQuantityInteractive(0.5291772083E-10 , 'm') | |||
|
124 | a_0.__doc__ = """Bohr radius """ | |||
|
125 | ||||
|
126 | ||||
|
127 | E_h = PhysicalQuantityInteractive(4.35974381E-18 , 'J') | |||
|
128 | E_h.__doc__ = """Hartree energy """ | |||
|
129 | ||||
|
130 | Eev_h = PhysicalQuantityInteractive(27.2113834 , 'eV') | |||
|
131 | Eev_h.__doc__ = """Hartree energy in eV """ | |||
|
132 | ||||
|
133 | ||||
|
134 | qcir2 = PhysicalQuantityInteractive(3.636947516E-4 , 'm**2/s') | |||
|
135 | qcir2.__doc__ = """quantum of circulation h/(2me) """ | |||
|
136 | ||||
|
137 | qcir = PhysicalQuantityInteractive(7.273895032E-4 , 'm**2/s') | |||
|
138 | qcir.__doc__ = """quantum of circulation h/(me) """ | |||
|
139 | ||||
|
140 | # Electroweak | |||
|
141 | #------------------------------------------------------------------------- | |||
|
142 | ||||
|
143 | Fcc = PhysicalQuantityInteractive(1.16639E-5 , '1/GeV**2') | |||
|
144 | Fcc.__doc__ = """Fermi coupling constant """ | |||
|
145 | # weak mixing angled W (on-shell scheme) | |||
|
146 | wma_W = 0.2224 | |||
|
147 | ||||
|
148 | # Electron, e- | |||
|
149 | #------------------------------------------------------------------------- | |||
|
150 | ||||
|
151 | m_e = PhysicalQuantityInteractive(9.10938188E-31 , 'kg') | |||
|
152 | m_e.__doc__ = """electron mass """ | |||
|
153 | ||||
|
154 | m_e_u = PhysicalQuantityInteractive(5.485799110E-4 , 'amu') | |||
|
155 | m_e_u.__doc__ = """electron mass (electron relative atomic mass times amu)""" | |||
|
156 | ||||
|
157 | me_J = PhysicalQuantityInteractive(8.18710414E-14 , 'J') | |||
|
158 | me_J.__doc__ = """electron mass - energy equivalent """ | |||
|
159 | ||||
|
160 | me_MeV = PhysicalQuantityInteractive(0.510998902 , 'MeV') | |||
|
161 | me_MeV.__doc__ = """electron mass - energy equivalent in MeV""" | |||
|
162 | ||||
|
163 | # electron-muon mass ratio | |||
|
164 | memu = 4.83633210E-3 | |||
|
165 | ||||
|
166 | # electron-tau mass ratio | |||
|
167 | metau = 2.87555E-4 | |||
|
168 | ||||
|
169 | # electron-proton mass ratio | |||
|
170 | memp = 5.446170232E-4 | |||
|
171 | ||||
|
172 | # electron-neutron mass ratio | |||
|
173 | memn = 5.438673462E-4 | |||
|
174 | ||||
|
175 | # electron-deuteron mass ratio | |||
|
176 | memd = 2.7244371170E-4 | |||
|
177 | ||||
|
178 | # electron to alpha particle mass ratio | |||
|
179 | memalpha = 1.3709335611E-4 | |||
|
180 | ||||
|
181 | ||||
|
182 | echargeemass = PhysicalQuantityInteractive(-1.758820174E11 , 'C/kg') | |||
|
183 | echargeemass.__doc__ = """electron charge to mass quotient """ | |||
|
184 | ||||
|
185 | ||||
|
186 | Molar_e = PhysicalQuantityInteractive(5.485799110E-7 , 'kg/mol') | |||
|
187 | Molar_e.__doc__ = """electron molar mass """ | |||
|
188 | ||||
|
189 | ||||
|
190 | lambdaC = PhysicalQuantityInteractive(2.426310215E-12 , 'm') | |||
|
191 | lambdaC.__doc__ = """Compton wavelength """ | |||
|
192 | ||||
|
193 | ||||
|
194 | r_e = PhysicalQuantityInteractive(2.817940285E-15 , 'm') | |||
|
195 | r_e.__doc__ = """classical electron radius """ | |||
|
196 | ||||
|
197 | ||||
|
198 | sigma_e = PhysicalQuantityInteractive(0.665245854E-28 , 'm**2') | |||
|
199 | sigma_e.__doc__ = """Thomson cross section """ | |||
|
200 | ||||
|
201 | ||||
|
202 | u_e = PhysicalQuantityInteractive(-928.476362E-26 , 'J/T') | |||
|
203 | u_e.__doc__ = """electron magnetic moment """ | |||
|
204 | ||||
|
205 | # electron magnetic moment to Bohr magneton ratio | |||
|
206 | ueuB = -1.0011596521869 | |||
|
207 | ||||
|
208 | # electron magnetic moment to nuclear magneton ratio | |||
|
209 | ueuN = -1838.2819660 | |||
|
210 | ||||
|
211 | # electron magnetic moment anomaly |ue|/uB - 1 | |||
|
212 | a_e = 1.1596521869E-3 | |||
|
213 | ||||
|
214 | # electron g-factor | |||
|
215 | g_e = -2.0023193043737 | |||
|
216 | ||||
|
217 | # electron-muon magnetic moment ratio | |||
|
218 | ueuu = 206.7669720 | |||
|
219 | ||||
|
220 | # electron-proton magnetic moment ratio | |||
|
221 | ueup = -658.2106875 | |||
|
222 | ||||
|
223 | # electron to shielded proton magnetic moment ratio (H2O, sphere, 25 C) | |||
|
224 | ueusp = -658.2275954 | |||
|
225 | ||||
|
226 | # electron-neutron magnetic moment ratio | |||
|
227 | ueun = 960.92050 | |||
|
228 | ||||
|
229 | # electron-deuteron magnetic moment ratio | |||
|
230 | ueud = -2143.923498 | |||
|
231 | ||||
|
232 | # electron to shielded helione magnetic moment ratio (gas, sphere, 25 C) | |||
|
233 | ueush = 864.058255 | |||
|
234 | ||||
|
235 | ||||
|
236 | gamma_e = PhysicalQuantityInteractive(1.760859794E11 , '1/(s*T)') | |||
|
237 | gamma_e.__doc__ = """electron gyromagnetic ratio """ | |||
|
238 | ||||
|
239 | # Muon, u- | |||
|
240 | #------------------------------------------------------------------------- | |||
|
241 | ||||
|
242 | m_u = PhysicalQuantityInteractive(1.88353109E-28 , 'kg') | |||
|
243 | m_u.__doc__ = """muon mass """ | |||
|
244 | ||||
|
245 | mu_u = PhysicalQuantityInteractive(0.1134289168 , 'amu') | |||
|
246 | mu_u.__doc__ = """muon mass in muon relative atomic mass times amu """ | |||
|
247 | ||||
|
248 | ||||
|
249 | muc2_J = PhysicalQuantityInteractive(1.69283332E-11 , 'J') | |||
|
250 | muc2_J.__doc__ = """energy equivalent """ | |||
|
251 | ||||
|
252 | muc2_MeV = PhysicalQuantityInteractive(105.6583568 , 'MeV') | |||
|
253 | muc2_MeV.__doc__ = """energy equivalent in MeV """ | |||
|
254 | ||||
|
255 | # muon-electron mass ratio | |||
|
256 | mume = 206.7682657 | |||
|
257 | ||||
|
258 | # muon-tau mass ratio | |||
|
259 | mum = 5.94572E-2 | |||
|
260 | ||||
|
261 | # muon-proton mass ratio | |||
|
262 | mump = 0.1126095173 | |||
|
263 | ||||
|
264 | # muon-neutron mass ratio | |||
|
265 | mumn = 0.1124545079 | |||
|
266 | ||||
|
267 | ||||
|
268 | Molar_u = PhysicalQuantityInteractive(0.1134289168E-3 , 'kg/mol') | |||
|
269 | Molar_u.__doc__ = """muon molar mass """ | |||
|
270 | ||||
|
271 | ||||
|
272 | lambda_C_u = PhysicalQuantityInteractive(11.73444197E-15 , 'm') | |||
|
273 | lambda_C_u.__doc__ = """muon Compton wavelength """ | |||
|
274 | ||||
|
275 | ||||
|
276 | uu = PhysicalQuantityInteractive(-4.49044813E-26 , 'J/T') | |||
|
277 | uu.__doc__ = """muon magnetic moment """ | |||
|
278 | ||||
|
279 | # ratio of muon magnetic moment to Bohr magneton ratio | |||
|
280 | uuuB = -4.84197085E-3 | |||
|
281 | ||||
|
282 | # ratio of muon magnetic moment to nuclear magneton ratio | |||
|
283 | uuuN = -8.89059770 | |||
|
284 | ||||
|
285 | # muon magnetic moment anomaly |uu|/(e /2mu) - 1 | |||
|
286 | a_u = 1.16591602E-3 | |||
|
287 | ||||
|
288 | # muon g-factor -2(1 + au) | |||
|
289 | g_u = -2.0023318320 | |||
|
290 | ||||
|
291 | # muon-proton magnetic moment ratio | |||
|
292 | uuup = -3.18334539 | |||
|
293 | ||||
|
294 | # Tau, tau- | |||
|
295 | #------------------------------------------------------------------------- | |||
|
296 | ||||
|
297 | m_tau = PhysicalQuantityInteractive(3.16788E-27 , 'kg') | |||
|
298 | m_tau.__doc__ = """tau mass """ | |||
|
299 | ||||
|
300 | mu_tau = PhysicalQuantityInteractive(1.90774 , 'amu') | |||
|
301 | mu_tau.__doc__ = """tau mass (tau relative atomic mass times amu) """ | |||
|
302 | ||||
|
303 | ||||
|
304 | mtauc2_J = PhysicalQuantityInteractive(2.84715E-10 , 'J') | |||
|
305 | mtauc2_J.__doc__ = """tau mass energy equivalent """ | |||
|
306 | ||||
|
307 | ||||
|
308 | mtauc2_MeV = PhysicalQuantityInteractive(1777.05 , 'MeV') | |||
|
309 | mtauc2_MeV.__doc__ = """tau mass energy equivalent in MeV """ | |||
|
310 | ||||
|
311 | # tau-electron mass ratio | |||
|
312 | mtaume = 3477.60 | |||
|
313 | ||||
|
314 | # tau-muon mass ratio | |||
|
315 | mtaumu = 16.8188 | |||
|
316 | ||||
|
317 | # tau-proton mass ratio | |||
|
318 | mtaump = 1.89396 | |||
|
319 | ||||
|
320 | # tau-neutron mass ratio | |||
|
321 | mtaumn = 1.89135 | |||
|
322 | ||||
|
323 | ||||
|
324 | Molar_tau = PhysicalQuantityInteractive(1.90774E-3 , 'kg/mol') | |||
|
325 | Molar_tau.__doc__ = """tau molar mass """ | |||
|
326 | ||||
|
327 | ||||
|
328 | lambda_C_tau = PhysicalQuantityInteractive(0.69770E-15 , 'm') | |||
|
329 | lambda_C_tau.__doc__ = """tau Compton wavelength """ | |||
|
330 | ||||
|
331 | # Proton, p | |||
|
332 | #------------------------------------------------------------------------- | |||
|
333 | ||||
|
334 | m_p = PhysicalQuantityInteractive(1.67262158E-27 , 'kg') | |||
|
335 | m_p.__doc__ = """proton mass """ | |||
|
336 | ||||
|
337 | mu_p = PhysicalQuantityInteractive(1.00727646688 , 'amu') | |||
|
338 | mu_p.__doc__ = """proton mass (proton relative atomic mass times amu) """ | |||
|
339 | ||||
|
340 | ||||
|
341 | mpc2_J = PhysicalQuantityInteractive(1.50327731E-10 , 'J') | |||
|
342 | mpc2_J.__doc__ = """energy equivalent """ | |||
|
343 | ||||
|
344 | mpc2_MeV = PhysicalQuantityInteractive(938.271998 , 'MeV') | |||
|
345 | mpc2_MeV.__doc__ = """energy equivalent in MeV """ | |||
|
346 | ||||
|
347 | # proton-electron mass ratio | |||
|
348 | mpme = 1836.1526675 | |||
|
349 | ||||
|
350 | # proton-muon mass ratio | |||
|
351 | mpmu = 8.88024408 | |||
|
352 | ||||
|
353 | # proton-tau mass ratio | |||
|
354 | mpmtau = 0.527994 | |||
|
355 | ||||
|
356 | # proton-neutron mass ratio | |||
|
357 | mpmn = 0.99862347855 | |||
|
358 | ||||
|
359 | ||||
|
360 | emp = PhysicalQuantityInteractive(9.57883408E7 , 'C/kg') | |||
|
361 | emp.__doc__ = """proton charge to mass quotient """ | |||
|
362 | ||||
|
363 | ||||
|
364 | Molar_p = PhysicalQuantityInteractive(1.00727646688E-3 , 'kg/mol') | |||
|
365 | Molar_p.__doc__ = """proton molar mass """ | |||
|
366 | ||||
|
367 | ||||
|
368 | lambda_C_p = PhysicalQuantityInteractive(1.321409847E-15 , 'm') | |||
|
369 | lambda_C_p.__doc__ = """proton Compton wavelength h/mpc """ | |||
|
370 | ||||
|
371 | ||||
|
372 | up = PhysicalQuantityInteractive(1.410606633E-26 , 'J/T') | |||
|
373 | up.__doc__ = """proton magnetic moment """ | |||
|
374 | ||||
|
375 | # proton magnetic moment to Bohr magneton ratio | |||
|
376 | upuB = 1.521032203E-3 | |||
|
377 | ||||
|
378 | # proton magnetic moment to nuclear magneton ratio | |||
|
379 | upuN = 2.792847337 | |||
|
380 | ||||
|
381 | # proton g-factor 2up/uN | |||
|
382 | g_p = 5.585694675 | |||
|
383 | ||||
|
384 | # proton-neutron magnetic moment ratio | |||
|
385 | upun = -1.45989805 | |||
|
386 | ||||
|
387 | ||||
|
388 | usp = PhysicalQuantityInteractive(1.410570399E-26 , 'J/T') | |||
|
389 | usp.__doc__ = """shielded proton magnetic moment (H2O, sphere, 25 C)""" | |||
|
390 | ||||
|
391 | # shielded proton magnetic moment to Bohr magneton ratio | |||
|
392 | uspuB = 1.520993132E-3 | |||
|
393 | ||||
|
394 | # shielded proton magnetic moment to nuclear magneton ratio | |||
|
395 | uspuN = 2.792775597 | |||
|
396 | ||||
|
397 | # proton magnetic shielding correction 1 - u p/up (H2O, sphere, 25 C) | |||
|
398 | spc = 25.687E-6 | |||
|
399 | ||||
|
400 | ||||
|
401 | gamma_p = PhysicalQuantityInteractive(2.67522212E8 , '1/(s*T)') | |||
|
402 | gamma_p.__doc__ = """proton gyromagnetic ratio """ | |||
|
403 | ||||
|
404 | ||||
|
405 | gamma_sp = PhysicalQuantityInteractive(2.67515341E8 , '1/(s*T)') | |||
|
406 | gamma_sp.__doc__ = """shielded proton gyromagnetic ratio (H2O, sphere, 25 C)""" | |||
|
407 | ||||
|
408 | # Neutron, n | |||
|
409 | #------------------------------------------------------------------------- | |||
|
410 | ||||
|
411 | m_n = PhysicalQuantityInteractive(1.67492716E-27 , 'kg') | |||
|
412 | m_n.__doc__ = """neutron mass """ | |||
|
413 | ||||
|
414 | mu_n = PhysicalQuantityInteractive(1.00866491578 , 'amu') | |||
|
415 | mu_n.__doc__ = """neutron mass (neutron relative atomic mass times amu) """ | |||
|
416 | ||||
|
417 | ||||
|
418 | mnc2_J = PhysicalQuantityInteractive(1.50534946E-10 , 'J') | |||
|
419 | mnc2_J.__doc__ = """neutron mass energy equivalent """ | |||
|
420 | ||||
|
421 | ||||
|
422 | mnc2_MeV = PhysicalQuantityInteractive(939.565330 , 'MeV') | |||
|
423 | mnc2_MeV.__doc__ = """neutron mass energy equivalent in MeV """ | |||
|
424 | ||||
|
425 | # neutron-electron mass ratio | |||
|
426 | mnme = 1838.6836550 | |||
|
427 | ||||
|
428 | # neutron-muon mass ratio | |||
|
429 | mnmu = 8.89248478 | |||
|
430 | ||||
|
431 | # neutron-tau mass ratio | |||
|
432 | mnm = 0.528722 | |||
|
433 | ||||
|
434 | # neutron-proton mass ratio | |||
|
435 | mnmp = 1.00137841887 | |||
|
436 | ||||
|
437 | ||||
|
438 | Molar_n = PhysicalQuantityInteractive(1.00866491578E-3 , 'kg/mol') | |||
|
439 | Molar_n.__doc__ = """neutron molar mass """ | |||
|
440 | ||||
|
441 | ||||
|
442 | lambda_C_n = PhysicalQuantityInteractive(1.319590898E-15 , 'm') | |||
|
443 | lambda_C_n.__doc__ = """neutron Compton wavelength""" | |||
|
444 | ||||
|
445 | ||||
|
446 | un = PhysicalQuantityInteractive(-0.96623640E-26 , 'J/T') | |||
|
447 | un.__doc__ = """neutron magnetic moment """ | |||
|
448 | ||||
|
449 | # neutron magnetic moment to Bohr magneton ratio | |||
|
450 | unuB = -1.04187563E-3 | |||
|
451 | ||||
|
452 | # neutron magnetic moment to nuclear magneton ratio | |||
|
453 | unuN = -1.91304272 | |||
|
454 | ||||
|
455 | # neutron g-factor | |||
|
456 | g_n = -3.82608545 | |||
|
457 | ||||
|
458 | # neutron-electron magnetic moment ratio | |||
|
459 | unue = 1.04066882E-3 | |||
|
460 | ||||
|
461 | # neutron-proton magnetic moment ratio | |||
|
462 | unup = -0.68497934 | |||
|
463 | ||||
|
464 | # neutron to shielded proton magnetic moment ratio (H2O, sphere, 25 C) | |||
|
465 | unusp = -0.68499694 | |||
|
466 | ||||
|
467 | ||||
|
468 | gamma_n = PhysicalQuantityInteractive(1.83247188E8 , '1/(s*T)') | |||
|
469 | gamma_n.__doc__ = """neutron gyromagnetic ratio """ | |||
|
470 | ||||
|
471 | # Deuteron, d | |||
|
472 | #------------------------------------------------------------------------- | |||
|
473 | ||||
|
474 | m_d = PhysicalQuantityInteractive(3.34358309E-27 , 'kg') | |||
|
475 | m_d.__doc__ = """deuteron mass """ | |||
|
476 | ||||
|
477 | ||||
|
478 | mu_d = PhysicalQuantityInteractive(2.01355321271 , 'amu') | |||
|
479 | mu_d.__doc__ = """deuteron mass (deuteron relative atomic mass times amu) """ | |||
|
480 | ||||
|
481 | ||||
|
482 | mdc2_J = PhysicalQuantityInteractive(3.00506262E-10 , 'J') | |||
|
483 | mdc2_J.__doc__ = """deuteron mass energy equivalent """ | |||
|
484 | ||||
|
485 | ||||
|
486 | mdc2_eV = PhysicalQuantityInteractive(1875.612762 , 'MeV') | |||
|
487 | mdc2_eV.__doc__ = """deuteron mass energy equivalent in MeV """ | |||
|
488 | ||||
|
489 | # deuteron-electron mass ratio | |||
|
490 | mdme = 3670.4829550 | |||
|
491 | ||||
|
492 | # deuteron-proton mass ratio | |||
|
493 | mdmp = 1.99900750083 | |||
|
494 | ||||
|
495 | ||||
|
496 | Molar_d = PhysicalQuantityInteractive(2.01355321271E-3 , 'kg/mol') | |||
|
497 | Molar_d.__doc__ = """deuteron molar mass """ | |||
|
498 | ||||
|
499 | ||||
|
500 | ud = PhysicalQuantityInteractive(0.433073457E-26 , 'J/T') | |||
|
501 | ud.__doc__ = """deuteron magnetic moment """ | |||
|
502 | ||||
|
503 | # deuteron magnetic moment to Bohr magneton ratio | |||
|
504 | uduB = 0.4669754556E-3 | |||
|
505 | ||||
|
506 | # deuteron magnetic moment to nuclear magneton ratio | |||
|
507 | uduN = 0.8574382284 | |||
|
508 | ||||
|
509 | # deuteron-electron magnetic moment ratio | |||
|
510 | udue = -4.664345537E-4 | |||
|
511 | ||||
|
512 | # deuteron-proton magnetic moment ratio | |||
|
513 | udup = 0.3070122083 | |||
|
514 | ||||
|
515 | # deuteron-neutron magnetic moment ratio | |||
|
516 | udun = -0.44820652 | |||
|
517 | ||||
|
518 | # Helion, h | |||
|
519 | #------------------------------------------------------------------------- | |||
|
520 | ||||
|
521 | m_h = PhysicalQuantityInteractive(5.00641174E-27 , 'kg') | |||
|
522 | m_h.__doc__ = """helion mass """ | |||
|
523 | ||||
|
524 | ||||
|
525 | mu_h = PhysicalQuantityInteractive(3.01493223469 , 'amu') | |||
|
526 | mu_h.__doc__ = """helion mass (helion relative atomic mass times amu) """ | |||
|
527 | ||||
|
528 | ||||
|
529 | mhc2_J = PhysicalQuantityInteractive(4.49953848E-10 , 'J') | |||
|
530 | mhc2_J.__doc__ = """helion mass energy equivalent """ | |||
|
531 | ||||
|
532 | mhc2_MeV = PhysicalQuantityInteractive(2808.39132 , 'MeV') | |||
|
533 | mhc2_MeV.__doc__ = """helion mass energy equivalent in MeV """ | |||
|
534 | ||||
|
535 | # helion-electron mass ratio | |||
|
536 | mhme = 5495.885238 | |||
|
537 | ||||
|
538 | # helion-proton mass ratio | |||
|
539 | mhmp = 2.99315265850 | |||
|
540 | ||||
|
541 | ||||
|
542 | Molar_h = PhysicalQuantityInteractive(3.01493223469E-3 , 'kg/mol') | |||
|
543 | Molar_h.__doc__ = """helion molar mass """ | |||
|
544 | ||||
|
545 | ||||
|
546 | ush = PhysicalQuantityInteractive(-1.074552967E-26 , 'J/T') | |||
|
547 | ush.__doc__ = """shielded helion magnetic moment (gas, sphere, 25 C)""" | |||
|
548 | ||||
|
549 | # shielded helion magnetic moment to Bohr magneton ratio | |||
|
550 | ushuB = -1.158671474E-3 | |||
|
551 | ||||
|
552 | # shielded helion magnetic moment to nuclear magneton ratio | |||
|
553 | ushuN = -2.127497718 | |||
|
554 | ||||
|
555 | # shielded helion to proton magnetic moment ratio (gas, sphere, 25 C) | |||
|
556 | ushup = -0.761766563 | |||
|
557 | ||||
|
558 | # shielded helion to shielded proton magnetic moment ratio (gas/H2O, spheres, 25 C) | |||
|
559 | ushusp = -0.7617861313 | |||
|
560 | ||||
|
561 | ||||
|
562 | gamma_h = PhysicalQuantityInteractive(2.037894764E8 , '1/(s*T)') | |||
|
563 | gamma_h.__doc__ = """shielded helion gyromagnetic (gas, sphere, 25 C) """ | |||
|
564 | ||||
|
565 | # Alpha particle, | |||
|
566 | #------------------------------------------------------------------------- | |||
|
567 | ||||
|
568 | m_alpha = PhysicalQuantityInteractive(6.64465598E-27 , 'kg') | |||
|
569 | m_alpha.__doc__ = """alpha particle mass """ | |||
|
570 | ||||
|
571 | mu_alpha = PhysicalQuantityInteractive(4.0015061747 , 'amu') | |||
|
572 | mu_alpha.__doc__ = """alpha particle mass (alpha particle relative atomic mass times amu) """ | |||
|
573 | ||||
|
574 | ||||
|
575 | malphac2_J = PhysicalQuantityInteractive(5.97191897E-10 , 'J') | |||
|
576 | malphac2_J.__doc__ = """alpha particle mass energy equivalent """ | |||
|
577 | ||||
|
578 | ||||
|
579 | malphac2_MeV = PhysicalQuantityInteractive(3727.37904 , 'MeV') | |||
|
580 | malphac2_MeV.__doc__ = """alpha particle mass energy equivalent in MeV """ | |||
|
581 | ||||
|
582 | # alpha particle to electron mass ratio | |||
|
583 | malphame = 7294.299508 | |||
|
584 | ||||
|
585 | # alpha particle to proton mass ratio | |||
|
586 | malphamp = 3.9725996846 | |||
|
587 | ||||
|
588 | ||||
|
589 | Molar_alpha = PhysicalQuantityInteractive(4.0015061747E-3 , 'kg/mol') | |||
|
590 | Molar_alpha.__doc__ = """alpha particle molar mass""" | |||
|
591 | ||||
|
592 | # PHYSICO-CHEMICAL | |||
|
593 | #------------------------------------------------------------------------- | |||
|
594 | ||||
|
595 | N_A = PhysicalQuantityInteractive(6.02214199E23 , '1/mol') | |||
|
596 | N_A.__doc__ = """Avogadro constant """ | |||
|
597 | L = PhysicalQuantityInteractive(6.02214199E23 , '1/mol') | |||
|
598 | ||||
|
599 | ||||
|
600 | m_u = PhysicalQuantityInteractive(1.66053873E-27 , 'kg') | |||
|
601 | m_u.__doc__ = """atomic mass constant mu = 112m(12C) = 1 u = 10E-3 kg mol-1/NA""" | |||
|
602 | # atomic mass constant mu = 112m(12C) = 1 u = 10E-3 kg mol-1/NA | |||
|
603 | amu = m_u | |||
|
604 | ||||
|
605 | ||||
|
606 | muc2_J = PhysicalQuantityInteractive(1.49241778E-10 , 'J') | |||
|
607 | muc2_J.__doc__ = """energy equivalent of the atomic mass constant""" | |||
|
608 | ||||
|
609 | ||||
|
610 | muc2_MeV = PhysicalQuantityInteractive(931.494013 , 'MeV') | |||
|
611 | muc2_MeV.__doc__ = """energy equivalent of the atomic mass constant in MeV """ | |||
|
612 | ||||
|
613 | ||||
|
614 | F = PhysicalQuantityInteractive(96485.3415 , 'C/mol') | |||
|
615 | F.__doc__ = """Faraday constant""" | |||
|
616 | ||||
|
617 | ||||
|
618 | N_Ah = PhysicalQuantityInteractive(3.990312689E-10 , 'J*s/mol') | |||
|
619 | N_Ah.__doc__ = """molar Planck constant """ | |||
|
620 | ||||
|
621 | ||||
|
622 | R = PhysicalQuantityInteractive(8.314472 , 'J/(mol*K)') | |||
|
623 | R.__doc__ = """molar gas constant """ | |||
|
624 | ||||
|
625 | ||||
|
626 | k_J = PhysicalQuantityInteractive(1.3806503E-23 , 'J/K') | |||
|
627 | k_J.__doc__ = """Boltzmann constant """ | |||
|
628 | ||||
|
629 | ||||
|
630 | k_eV = PhysicalQuantityInteractive(8.617342E-5 , 'eV/K') | |||
|
631 | k_eV.__doc__ = """Boltzmann constant in eV """ | |||
|
632 | ||||
|
633 | ||||
|
634 | n_0 = PhysicalQuantityInteractive(2.6867775E25 , '1/m**3') | |||
|
635 | n_0.__doc__ = """Loschmidt constant NA/Vm """ | |||
|
636 | ||||
|
637 | ||||
|
638 | Vm_1 = PhysicalQuantityInteractive(22.413996E-3 , 'm**3/mol') | |||
|
639 | Vm_1.__doc__ = """molar volume of ideal gas RT/p T = 273.15 K, p = 101.325 kPa """ | |||
|
640 | ||||
|
641 | Vm_2 = PhysicalQuantityInteractive(22.710981E-3 , 'm**3/mol') | |||
|
642 | Vm_2.__doc__ = """molar volume of ideal gas RT/p T = 273.15 K, p = 100 kPa """ | |||
|
643 | ||||
|
644 | # Sackur-Tetrode constant (absolute entropy constant) 52 + ln_(2 mukT1/h2)3/2kT1/p0 | |||
|
645 | # T1 = 1 K, p0 = 100 kPa | |||
|
646 | S_0R_1 = -1.1517048 | |||
|
647 | # T1 = 1 K, p0 = 101.325 kPa | |||
|
648 | S_0R_2 = -1.1648678 | |||
|
649 | ||||
|
650 | ||||
|
651 | sigma = PhysicalQuantityInteractive(5.670400E-8 , 'W/(m**2*K**4)') | |||
|
652 | sigma.__doc__ = """Stefan-Boltzmann constant """ | |||
|
653 | ||||
|
654 | ||||
|
655 | c_1 = PhysicalQuantityInteractive(3.74177107E-16 , 'W*m**2') | |||
|
656 | c_1.__doc__ = """first radiation constant""" | |||
|
657 | ||||
|
658 | ||||
|
659 | c_1L = PhysicalQuantityInteractive(1.191042722E-16 , 'W*m**2/sr') | |||
|
660 | c_1L.__doc__ = """first radiation constant for spectral radiance""" | |||
|
661 | ||||
|
662 | ||||
|
663 | c_2 = PhysicalQuantityInteractive(1.4387752E-2 , 'm*K') | |||
|
664 | c_2.__doc__ = """second radiation constant""" | |||
|
665 | ||||
|
666 | ||||
|
667 | b = PhysicalQuantityInteractive(2.8977686E-3 , 'm*K') | |||
|
668 | b.__doc__ = """Wien displacement law constant b = maxT = c2/4.965 114231... """ | |||
|
669 |
@@ -0,0 +1,62 | |||||
|
1 | """ Set default options for IPython. | |||
|
2 | ||||
|
3 | Just import this module to get reasonable defaults for everything. | |||
|
4 | ||||
|
5 | These configurations used to be performed in ipythonrc (or ipythonrc.ini). | |||
|
6 | Therefore importing this in your config files makes ipython basically | |||
|
7 | ignore your ipythonrc. This is *not* imported by default, you need to import | |||
|
8 | this manually in one of your config files. | |||
|
9 | ||||
|
10 | You can further override these defaults in e.g. your ipy_user_config.py, | |||
|
11 | ipy_profile_PROFILENAME etc. | |||
|
12 | ||||
|
13 | """ | |||
|
14 | ||||
|
15 | import IPython.rlineimpl as readline | |||
|
16 | import IPython.ipapi | |||
|
17 | ip = IPython.ipapi.get() | |||
|
18 | ||||
|
19 | o = ip.options | |||
|
20 | ||||
|
21 | o.colors = "Linux" | |||
|
22 | o.color_info=1 | |||
|
23 | o.confirm_exit=1 | |||
|
24 | o.pprint=1 | |||
|
25 | o.multi_line_specials=1 | |||
|
26 | o.xmode="Context" | |||
|
27 | ||||
|
28 | ||||
|
29 | o.prompt_in1='In [\#]: ' | |||
|
30 | o.prompt_in2 =' .\D.: ' | |||
|
31 | o.prompt_out = 'Out[\#]: ' | |||
|
32 | o.prompts_pad_left=1 | |||
|
33 | ||||
|
34 | o.autoindent = 1 | |||
|
35 | ||||
|
36 | o.readline_remove_delims="-/~" | |||
|
37 | o.readline_merge_completions=1 | |||
|
38 | ||||
|
39 | o.readline = 1 | |||
|
40 | ||||
|
41 | rlopts = """\ | |||
|
42 | tab: complete | |||
|
43 | "\C-l": possible-completions | |||
|
44 | set show-all-if-ambiguous on | |||
|
45 | "\C-o": tab-insert | |||
|
46 | "\M-i": " " | |||
|
47 | "\M-o": "\d\d\d\d" | |||
|
48 | "\M-I": "\d\d\d\d" | |||
|
49 | "\C-r": reverse-search-history | |||
|
50 | "\C-s": forward-search-history | |||
|
51 | "\C-p": history-search-backward | |||
|
52 | "\C-n": history-search-forward | |||
|
53 | "\e[A": history-search-backward | |||
|
54 | "\e[B": history-search-forward | |||
|
55 | "\C-k": kill-line | |||
|
56 | "\C-u": unix-line-discard""" | |||
|
57 | ||||
|
58 | if readline.have_readline: | |||
|
59 | for cmd in rlopts.split('\n'): | |||
|
60 | readline.parse_and_bind(cmd) | |||
|
61 | ||||
|
62 |
@@ -0,0 +1,84 | |||||
|
1 | """ 'editor' hooks for common editors that work well with ipython | |||
|
2 | ||||
|
3 | They should honor the line number argument, at least. | |||
|
4 | ||||
|
5 | Contributions are *very* welcome. | |||
|
6 | """ | |||
|
7 | ||||
|
8 | import IPython.ipapi | |||
|
9 | ip = IPython.ipapi.get() | |||
|
10 | ||||
|
11 | from IPython.Itpl import itplns | |||
|
12 | import os | |||
|
13 | ||||
|
14 | def install_editor(run_template, wait = False): | |||
|
15 | """ Gets a template in format "myeditor bah bah $file bah bah $line" | |||
|
16 | ||||
|
17 | $file will be replaced by file name, $line by line number (or 0). | |||
|
18 | Installs the editor that is called by IPython, instead of the default | |||
|
19 | notepad or vi. | |||
|
20 | ||||
|
21 | If wait is true, wait until the user presses enter before returning, | |||
|
22 | to facilitate non-blocking editors that exit immediately after | |||
|
23 | the call. | |||
|
24 | """ | |||
|
25 | ||||
|
26 | def call_editor(self, file, line=0): | |||
|
27 | if line is None: | |||
|
28 | line = 0 | |||
|
29 | cmd = itplns(run_template, locals()) | |||
|
30 | print ">",cmd | |||
|
31 | os.system(cmd) | |||
|
32 | if wait: | |||
|
33 | raw_input("Press Enter when done editing:") | |||
|
34 | ||||
|
35 | ip.set_hook('editor',call_editor) | |||
|
36 | ||||
|
37 | ||||
|
38 | # in these, exe is always the path/name of the executable. Useful | |||
|
39 | # if you don't have the editor directory in your path | |||
|
40 | ||||
|
41 | def komodo(exe = 'komodo'): | |||
|
42 | """ Activestate Komodo [Edit] """ | |||
|
43 | install_editor(exe + ' -l $line "$file"', wait = True) | |||
|
44 | ||||
|
45 | def scite(exe = "scite"): | |||
|
46 | """ SciTE or Sc1 """ | |||
|
47 | install_editor(exe + ' "$file" -goto:$line') | |||
|
48 | ||||
|
49 | def notepadplusplus(exe = 'notepad++'): | |||
|
50 | """ Notepad++ http://notepad-plus.sourceforge.net """ | |||
|
51 | install_editor(exe + ' -n$line "$file"') | |||
|
52 | ||||
|
53 | def jed(exe = 'jed'): | |||
|
54 | """ JED, the lightweight emacsish editor """ | |||
|
55 | install_editor(exe + ' +$line "$file"') | |||
|
56 | ||||
|
57 | def idle(exe = None): | |||
|
58 | """ Idle, the editor bundled with python | |||
|
59 | ||||
|
60 | Should be pretty smart about finding the executable. | |||
|
61 | """ | |||
|
62 | if exe is None: | |||
|
63 | import idlelib | |||
|
64 | p = os.path.dirname(idlelib.__file__) | |||
|
65 | exe = p + '/idle.py' | |||
|
66 | install_editor(exe + ' "$file"') | |||
|
67 | ||||
|
68 | ||||
|
69 | # these are untested, report any problems | |||
|
70 | ||||
|
71 | def emacs(exe = 'emacs'): | |||
|
72 | install_editor(exe + ' +$line "$file"') | |||
|
73 | ||||
|
74 | def gnuclient(exe= 'gnuclient'): | |||
|
75 | install_editor(exe + ' -nw +$line "$file"') | |||
|
76 | ||||
|
77 | def crimson_editor(exe = 'cedt.exe'): | |||
|
78 | install_editor(exe + ' /L:$line "$file"') | |||
|
79 | ||||
|
80 | def kate(exe = 'kate'): | |||
|
81 | install_editor(exe + ' -u -l $line "$file"') | |||
|
82 | ||||
|
83 | ||||
|
84 | No newline at end of file |
@@ -0,0 +1,76 | |||||
|
1 | import IPython.ipapi | |||
|
2 | ip = IPython.ipapi.get() | |||
|
3 | ||||
|
4 | import os,pprint | |||
|
5 | ||||
|
6 | def export(filename = None): | |||
|
7 | ||||
|
8 | lines = ['import IPython.ipapi', 'ip = IPython.ipapi.get()',''] | |||
|
9 | ||||
|
10 | vars = ip.db.keys('autorestore/*') | |||
|
11 | vars.sort() | |||
|
12 | varstomove = [] | |||
|
13 | get = ip.db.get | |||
|
14 | ||||
|
15 | macros = [] | |||
|
16 | variables = [] | |||
|
17 | ||||
|
18 | for var in vars: | |||
|
19 | k = os.path.basename(var) | |||
|
20 | v = get(var) | |||
|
21 | ||||
|
22 | if k.startswith('_'): | |||
|
23 | continue | |||
|
24 | if isinstance(v, IPython.macro.Macro): | |||
|
25 | macros.append((k,v)) | |||
|
26 | if type(v) in [int, str, float]: | |||
|
27 | variables.append((k,v)) | |||
|
28 | ||||
|
29 | ||||
|
30 | ||||
|
31 | if macros: | |||
|
32 | lines.extend(['# === Macros ===' ,'']) | |||
|
33 | for k,v in macros: | |||
|
34 | lines.append("ip.defmacro('%s'," % k) | |||
|
35 | for line in v.value.splitlines(): | |||
|
36 | lines.append(' ' + repr(line+'\n')) | |||
|
37 | lines.extend([')', '']) | |||
|
38 | ||||
|
39 | if variables: | |||
|
40 | lines.extend(['','# === Variables ===','']) | |||
|
41 | for k,v in variables: | |||
|
42 | varstomove.append(k) | |||
|
43 | lines.append('%s = %s' % (k,repr(v))) | |||
|
44 | ||||
|
45 | lines.append('ip.to_user_ns("%s")' % (' '.join(varstomove))) | |||
|
46 | ||||
|
47 | bkms = ip.db.get('bookmarks',{}) | |||
|
48 | ||||
|
49 | if bkms: | |||
|
50 | lines.extend(['','# === Bookmarks ===','']) | |||
|
51 | lines.append("ip.db['bookmarks'] = %s " % pprint.pformat(bkms, indent = 2) ) | |||
|
52 | ||||
|
53 | aliases = ip.db.get('stored_aliases', {} ) | |||
|
54 | ||||
|
55 | if aliases: | |||
|
56 | lines.extend(['','# === Alias definitions ===','']) | |||
|
57 | for k,v in aliases.items(): | |||
|
58 | try: | |||
|
59 | lines.append("ip.defalias('%s', %s)" % (k, repr(v[1]))) | |||
|
60 | except (AttributeError, TypeError): | |||
|
61 | pass | |||
|
62 | ||||
|
63 | env = ip.db.get('stored_env') | |||
|
64 | if env: | |||
|
65 | lines.extend(['','# === Stored env vars ===','']) | |||
|
66 | lines.append("ip.db['stored_env'] = %s " % pprint.pformat(env, indent = 2) ) | |||
|
67 | ||||
|
68 | ||||
|
69 | ||||
|
70 | out = '\n'.join(lines) | |||
|
71 | ||||
|
72 | if filename: | |||
|
73 | open(filename,'w').write(out) | |||
|
74 | else: | |||
|
75 | print out | |||
|
76 |
@@ -0,0 +1,43 | |||||
|
1 | """ IPython extension management tools. | |||
|
2 | ||||
|
3 | After installation, you'll have the 'extutil' object in your namespace. | |||
|
4 | to. | |||
|
5 | """ | |||
|
6 | ||||
|
7 | # for the purposes of this module, every module that has the name 'ip' globally | |||
|
8 | # installed as below is an IPython extension | |||
|
9 | ||||
|
10 | import IPython.ipapi | |||
|
11 | ip = IPython.ipapi.get() | |||
|
12 | ||||
|
13 | import sys,textwrap,inspect | |||
|
14 | ||||
|
15 | def indent(s, ind= ' '): | |||
|
16 | return '\n'.join([ind +l for l in s.splitlines()]) | |||
|
17 | ||||
|
18 | class ExtUtil: | |||
|
19 | """ IPython extensios (ipy_* etc.) management utilities """ | |||
|
20 | ||||
|
21 | def describe(self): | |||
|
22 | for n,mod in self._active(): | |||
|
23 | doc = inspect.getdoc(mod) | |||
|
24 | if doc: | |||
|
25 | print '== %s ==' % n | |||
|
26 | print indent(doc) | |||
|
27 | ||||
|
28 | ||||
|
29 | def ls(self): | |||
|
30 | """ Show list of installed extensions. """ | |||
|
31 | for n,m in self._active(): | |||
|
32 | print '%-20s %s' % (n,m.__file__.replace('\\','/')) | |||
|
33 | def _active(self): | |||
|
34 | act = [] | |||
|
35 | for mname,m in sys.modules.items(): | |||
|
36 | o = getattr(m, 'ip', None) | |||
|
37 | if isinstance(o, IPython.ipapi.IPApi): | |||
|
38 | act.append((mname,m)) | |||
|
39 | act.sort() | |||
|
40 | return act | |||
|
41 | ||||
|
42 | extutil = ExtUtil() | |||
|
43 | ip.to_user_ns('extutil') |
@@ -0,0 +1,244 | |||||
|
1 | """ File system operations | |||
|
2 | ||||
|
3 | Contains: Simple variants of normal unix shell commands (icp, imv, irm, | |||
|
4 | imkdir, igrep). | |||
|
5 | ||||
|
6 | Some "otherwise handy" utils ('collect' for gathering files to | |||
|
7 | ~/_ipython/collect, 'inote' for collecting single note lines to | |||
|
8 | ~/_ipython/note.txt) | |||
|
9 | ||||
|
10 | Mostly of use for bare windows installations where cygwin/equivalent is not | |||
|
11 | installed and you would otherwise need to deal with dos versions of the | |||
|
12 | commands (that e.g. don't understand / as path separator). These can | |||
|
13 | do some useful tricks on their own, though (like use 'mglob' patterns). | |||
|
14 | ||||
|
15 | Not to be confused with ipipe commands (ils etc.) that also start with i. | |||
|
16 | """ | |||
|
17 | ||||
|
18 | import IPython.ipapi | |||
|
19 | ip = IPython.ipapi.get() | |||
|
20 | ||||
|
21 | import shutil,os,shlex | |||
|
22 | from IPython.external import mglob | |||
|
23 | from IPython.external.path import path | |||
|
24 | from IPython.ipapi import UsageError | |||
|
25 | ||||
|
26 | def parse_args(args): | |||
|
27 | """ Given arg string 'CMD files... target', return ([files], target) """ | |||
|
28 | ||||
|
29 | tup = args.split(None, 1) | |||
|
30 | if len(tup) == 1: | |||
|
31 | raise UsageError("Expected arguments for " + tup[0]) | |||
|
32 | ||||
|
33 | tup2 = shlex.split(tup[1]) | |||
|
34 | ||||
|
35 | flist, trg = mglob.expand(tup2[0:-1]), tup2[-1] | |||
|
36 | if not flist: | |||
|
37 | raise UsageError("No files found:" + str(tup2[0:-1])) | |||
|
38 | return flist, trg | |||
|
39 | ||||
|
40 | def icp(ip,arg): | |||
|
41 | """ icp files... targetdir | |||
|
42 | ||||
|
43 | Copy all files to target, creating dirs for target if necessary | |||
|
44 | ||||
|
45 | icp srcdir dstdir | |||
|
46 | ||||
|
47 | Copy srcdir to distdir | |||
|
48 | ||||
|
49 | """ | |||
|
50 | import distutils.dir_util | |||
|
51 | ||||
|
52 | fs, targetdir = parse_args(arg) | |||
|
53 | if not os.path.isdir(targetdir) and len(fs) > 1: | |||
|
54 | distutils.dir_util.mkpath(targetdir,verbose =1) | |||
|
55 | for f in fs: | |||
|
56 | if os.path.isdir(f): | |||
|
57 | shutil.copytree(f, targetdir) | |||
|
58 | else: | |||
|
59 | shutil.copy2(f,targetdir) | |||
|
60 | return fs | |||
|
61 | ip.defalias("icp",icp) | |||
|
62 | ||||
|
63 | def imv(ip,arg): | |||
|
64 | """ imv src tgt | |||
|
65 | ||||
|
66 | Move source to target. | |||
|
67 | """ | |||
|
68 | ||||
|
69 | fs, target = parse_args(arg) | |||
|
70 | if len(fs) > 1: | |||
|
71 | assert os.path.isdir(target) | |||
|
72 | for f in fs: | |||
|
73 | shutil.move(f, target) | |||
|
74 | return fs | |||
|
75 | ip.defalias("imv",imv) | |||
|
76 | ||||
|
77 | def irm(ip,arg): | |||
|
78 | """ irm path[s]... | |||
|
79 | ||||
|
80 | Remove file[s] or dir[s] path. Dirs are deleted recursively. | |||
|
81 | """ | |||
|
82 | try: | |||
|
83 | paths = mglob.expand(arg.split(None,1)[1]) | |||
|
84 | except IndexError: | |||
|
85 | raise UsageError("%irm paths...") | |||
|
86 | import distutils.dir_util | |||
|
87 | for p in paths: | |||
|
88 | print "rm",p | |||
|
89 | if os.path.isdir(p): | |||
|
90 | distutils.dir_util.remove_tree(p, verbose = 1) | |||
|
91 | else: | |||
|
92 | os.remove(p) | |||
|
93 | ||||
|
94 | ip.defalias("irm",irm) | |||
|
95 | ||||
|
96 | def imkdir(ip,arg): | |||
|
97 | """ imkdir path | |||
|
98 | ||||
|
99 | Creates dir path, and all dirs on the road | |||
|
100 | """ | |||
|
101 | import distutils.dir_util | |||
|
102 | targetdir = arg.split(None,1)[1] | |||
|
103 | distutils.dir_util.mkpath(targetdir,verbose =1) | |||
|
104 | ||||
|
105 | ip.defalias("imkdir",imkdir) | |||
|
106 | ||||
|
107 | def igrep(ip,arg): | |||
|
108 | """ igrep PAT files... | |||
|
109 | ||||
|
110 | Very dumb file scan, case-insensitive. | |||
|
111 | ||||
|
112 | e.g. | |||
|
113 | ||||
|
114 | igrep "test this" rec:*.py | |||
|
115 | ||||
|
116 | """ | |||
|
117 | elems = shlex.split(arg) | |||
|
118 | dummy, pat, fs = elems[0], elems[1], mglob.expand(elems[2:]) | |||
|
119 | res = [] | |||
|
120 | for f in fs: | |||
|
121 | found = False | |||
|
122 | for l in open(f): | |||
|
123 | if pat.lower() in l.lower(): | |||
|
124 | if not found: | |||
|
125 | print "[[",f,"]]" | |||
|
126 | found = True | |||
|
127 | res.append(f) | |||
|
128 | print l.rstrip() | |||
|
129 | return res | |||
|
130 | ||||
|
131 | ip.defalias("igrep",igrep) | |||
|
132 | ||||
|
133 | def collect(ip,arg): | |||
|
134 | """ collect foo/a.txt rec:bar=*.py | |||
|
135 | ||||
|
136 | Copies foo/a.txt to ~/_ipython/collect/foo/a.txt and *.py from bar, | |||
|
137 | likewise | |||
|
138 | ||||
|
139 | Without args, try to open ~/_ipython/collect dir (in win32 at least). | |||
|
140 | """ | |||
|
141 | from IPython.external.path import path | |||
|
142 | basedir = path(ip.options.ipythondir + '/collect') | |||
|
143 | try: | |||
|
144 | fs = mglob.expand(arg.split(None,1)[1]) | |||
|
145 | except IndexError: | |||
|
146 | os.startfile(basedir) | |||
|
147 | return | |||
|
148 | for f in fs: | |||
|
149 | f = path(f) | |||
|
150 | trg = basedir / f.splitdrive()[1].lstrip('/\\') | |||
|
151 | if f.isdir(): | |||
|
152 | print "mkdir",trg | |||
|
153 | trg.makedirs() | |||
|
154 | continue | |||
|
155 | dname = trg.dirname() | |||
|
156 | if not dname.isdir(): | |||
|
157 | dname.makedirs() | |||
|
158 | print f,"=>",trg | |||
|
159 | shutil.copy2(f,trg) | |||
|
160 | ||||
|
161 | ip.defalias("collect",collect) | |||
|
162 | ||||
|
163 | def inote(ip,arg): | |||
|
164 | """ inote Hello world | |||
|
165 | ||||
|
166 | Adds timestamp and Hello world to ~/_ipython/notes.txt | |||
|
167 | ||||
|
168 | Without args, opens notes.txt for editing. | |||
|
169 | """ | |||
|
170 | import time | |||
|
171 | fname = ip.options.ipythondir + '/notes.txt' | |||
|
172 | ||||
|
173 | try: | |||
|
174 | entry = " === " + time.asctime() + ': ===\n' + arg.split(None,1)[1] + '\n' | |||
|
175 | f= open(fname, 'a').write(entry) | |||
|
176 | except IndexError: | |||
|
177 | ip.IP.hooks.editor(fname) | |||
|
178 | ||||
|
179 | ip.defalias("inote",inote) | |||
|
180 | ||||
|
181 | def pathobj_mangle(p): | |||
|
182 | return p.replace(' ', '__').replace('.','DOT') | |||
|
183 | def pathobj_unmangle(s): | |||
|
184 | return s.replace('__',' ').replace('DOT','.') | |||
|
185 | ||||
|
186 | ||||
|
187 | ||||
|
188 | class PathObj(path): | |||
|
189 | def __init__(self,p): | |||
|
190 | self.path = p | |||
|
191 | if p != '.': | |||
|
192 | self.ents = [pathobj_mangle(ent) for ent in os.listdir(p)] | |||
|
193 | else: | |||
|
194 | self.ents = None | |||
|
195 | def __complete__(self): | |||
|
196 | if self.path != '.': | |||
|
197 | return self.ents | |||
|
198 | self.ents = [pathobj_mangle(ent) for ent in os.listdir('.')] | |||
|
199 | return self.ents | |||
|
200 | def __getattr__(self,name): | |||
|
201 | if name in self.ents: | |||
|
202 | if self.path.endswith('/'): | |||
|
203 | sep = '' | |||
|
204 | else: | |||
|
205 | sep = '/' | |||
|
206 | ||||
|
207 | tgt = self.path + sep + pathobj_unmangle(name) | |||
|
208 | #print "tgt",tgt | |||
|
209 | if os.path.isdir(tgt): | |||
|
210 | return PathObj(tgt) | |||
|
211 | if os.path.isfile(tgt): | |||
|
212 | return path(tgt) | |||
|
213 | ||||
|
214 | raise AttributeError, name # <<< DON'T FORGET THIS LINE !! | |||
|
215 | def __str__(self): | |||
|
216 | return self.path | |||
|
217 | ||||
|
218 | def __repr__(self): | |||
|
219 | return "<PathObj to %s>" % self.path | |||
|
220 | ||||
|
221 | def __call__(self): | |||
|
222 | print "cd:",self.path | |||
|
223 | os.chdir(self.path) | |||
|
224 | ||||
|
225 | def complete_pathobj(obj, prev_completions): | |||
|
226 | if hasattr(obj,'__complete__'): | |||
|
227 | res = obj.__complete__() | |||
|
228 | if res: | |||
|
229 | return res | |||
|
230 | # just return normal attributes of 'path' object if the dir is empty | |||
|
231 | raise IPython.ipapi.TryNext | |||
|
232 | ||||
|
233 | complete_pathobj = IPython.generics.complete_object.when_type(PathObj)(complete_pathobj) | |||
|
234 | ||||
|
235 | def test_pathobj(): | |||
|
236 | #p = PathObj('c:/prj') | |||
|
237 | #p2 = p.cgi | |||
|
238 | #print p,p2 | |||
|
239 | rootdir = PathObj("/") | |||
|
240 | startmenu = PathObj("d:/Documents and Settings/All Users/Start Menu/Programs") | |||
|
241 | cwd = PathObj('.') | |||
|
242 | ip.to_user_ns("rootdir startmenu cwd") | |||
|
243 | ||||
|
244 | #test_pathobj() No newline at end of file |
@@ -0,0 +1,38 | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | ||||
|
3 | ||||
|
4 | """ | |||
|
5 | Add %global magic for GNU Global usage. | |||
|
6 | ||||
|
7 | http://www.gnu.org/software/global/ | |||
|
8 | ||||
|
9 | """ | |||
|
10 | ||||
|
11 | import IPython.ipapi | |||
|
12 | ip = IPython.ipapi.get() | |||
|
13 | import os | |||
|
14 | ||||
|
15 | # alter to your liking | |||
|
16 | global_bin = 'd:/opt/global/bin/global' | |||
|
17 | ||||
|
18 | def global_f(self,cmdline): | |||
|
19 | simple = 0 | |||
|
20 | if '-' not in cmdline: | |||
|
21 | cmdline = '-rx ' + cmdline | |||
|
22 | simple = 1 | |||
|
23 | ||||
|
24 | lines = [l.rstrip() for l in os.popen( global_bin + ' ' + cmdline ).readlines()] | |||
|
25 | ||||
|
26 | if simple: | |||
|
27 | parts = [l.split(None,3) for l in lines] | |||
|
28 | lines = ['%s [%s]\n%s' % (p[2].rjust(70),p[1],p[3].rstrip()) for p in parts] | |||
|
29 | print "\n".join(lines) | |||
|
30 | ||||
|
31 | ip.expose_magic('global', global_f) | |||
|
32 | ||||
|
33 | def global_completer(self,event): | |||
|
34 | compl = [l.rstrip() for l in os.popen(global_bin + ' -c ' + event.symbol).readlines()] | |||
|
35 | return compl | |||
|
36 | ||||
|
37 | ip.set_hook('complete_command', global_completer, str_key = '%global') | |||
|
38 |
@@ -0,0 +1,80 | |||||
|
1 | import os,sys | |||
|
2 | ||||
|
3 | import ipy_rehashdir,glob | |||
|
4 | from ipy_rehashdir import selflaunch, PyLauncher | |||
|
5 | ||||
|
6 | def pylaunchers(): | |||
|
7 | """Create launchers for python scripts in cwd and store them in alias table | |||
|
8 | ||||
|
9 | This is useful if you want to invoke .py scripts from ipykit session, | |||
|
10 | just adding .py files in PATH does not work without file association. | |||
|
11 | ||||
|
12 | .ipy files will be run like macros. | |||
|
13 | ||||
|
14 | """ | |||
|
15 | fs = glob.glob('*.py') + glob.glob('*.ipy') | |||
|
16 | for f in fs: | |||
|
17 | l = PyLauncher(f) | |||
|
18 | n = os.path.splitext(f)[0] | |||
|
19 | ip.defalias(n, l) | |||
|
20 | ip.magic('store '+n) | |||
|
21 | ||||
|
22 | ||||
|
23 | def exta_imports(): | |||
|
24 | # add some modules that you'd want to be bundled in the ipykit | |||
|
25 | # library zip file here. Do this if you get ImportErrors from scripts you | |||
|
26 | # try to launch with 'py' or pylaunchers. In theory you could include | |||
|
27 | # the whole stdlib here for full script coverage | |||
|
28 | ||||
|
29 | # note that this is never run, it's just here for py2exe | |||
|
30 | import distutils.dir_util | |||
|
31 | ||||
|
32 | def kitroot(): | |||
|
33 | return os.environ.get('IPYKITROOT', None) | |||
|
34 | ||||
|
35 | def main(): | |||
|
36 | ||||
|
37 | if not kitroot(): | |||
|
38 | print "Can't configure ipykit, IPYKITROOT should be set." | |||
|
39 | return | |||
|
40 | ||||
|
41 | os.environ["PATH"] = os.environ["PATH"] + ";" + kitroot() + "\\bin;" | |||
|
42 | ip.to_user_ns("pylaunchers") | |||
|
43 | cmds = ip.db.get('syscmdlist', None) | |||
|
44 | if cmds is None: | |||
|
45 | ip.magic('rehashx') | |||
|
46 | cmds = ip.db.get('syscmdlist', []) | |||
|
47 | #print cmds | |||
|
48 | if 'sc1' in cmds: | |||
|
49 | print "Default editor: Sc1" | |||
|
50 | import ipy_editors | |||
|
51 | ipy_editors.scite('sc1') | |||
|
52 | ||||
|
53 | # for icp, imv, imkdir, etc. | |||
|
54 | import ipy_fsops | |||
|
55 | ||||
|
56 | greeting = """\n\n === Welcome to ipykit === | |||
|
57 | ||||
|
58 | %quickref - learn quickly about IPython. | |||
|
59 | ||||
|
60 | """ | |||
|
61 | ||||
|
62 | def ipython_firstrun(ip): | |||
|
63 | ||||
|
64 | print "First run of ipykit - configuring" | |||
|
65 | ||||
|
66 | ip.defalias('py',selflaunch) | |||
|
67 | ip.defalias('d','dir /w /og /on') | |||
|
68 | ip.magic('store py') | |||
|
69 | ip.magic('store d') | |||
|
70 | ||||
|
71 | bins = kitroot() +'/bin' | |||
|
72 | ||||
|
73 | print greeting | |||
|
74 | ||||
|
75 | def init_ipython(ipy): | |||
|
76 | global ip | |||
|
77 | ip = ipy | |||
|
78 | main() | |||
|
79 | ||||
|
80 |
@@ -0,0 +1,62 | |||||
|
1 | """ Legacy stuff | |||
|
2 | ||||
|
3 | Various stuff that are there for historical / familiarity reasons. | |||
|
4 | ||||
|
5 | This is automatically imported by default profile, though not other profiles | |||
|
6 | (e.g. 'sh' profile). | |||
|
7 | ||||
|
8 | Stuff that is considered obsolete / redundant is gradually moved here. | |||
|
9 | ||||
|
10 | """ | |||
|
11 | ||||
|
12 | import IPython.ipapi | |||
|
13 | ip = IPython.ipapi.get() | |||
|
14 | ||||
|
15 | import os,sys | |||
|
16 | ||||
|
17 | from IPython.genutils import * | |||
|
18 | ||||
|
19 | # use rehashx | |||
|
20 | ||||
|
21 | def magic_rehash(self, parameter_s = ''): | |||
|
22 | """Update the alias table with all entries in $PATH. | |||
|
23 | ||||
|
24 | This version does no checks on execute permissions or whether the | |||
|
25 | contents of $PATH are truly files (instead of directories or something | |||
|
26 | else). For such a safer (but slower) version, use %rehashx.""" | |||
|
27 | ||||
|
28 | # This function (and rehashx) manipulate the alias_table directly | |||
|
29 | # rather than calling magic_alias, for speed reasons. A rehash on a | |||
|
30 | # typical Linux box involves several thousand entries, so efficiency | |||
|
31 | # here is a top concern. | |||
|
32 | ||||
|
33 | path = filter(os.path.isdir,os.environ.get('PATH','').split(os.pathsep)) | |||
|
34 | alias_table = self.shell.alias_table | |||
|
35 | for pdir in path: | |||
|
36 | for ff in os.listdir(pdir): | |||
|
37 | # each entry in the alias table must be (N,name), where | |||
|
38 | # N is the number of positional arguments of the alias. | |||
|
39 | alias_table[ff] = (0,ff) | |||
|
40 | # Make sure the alias table doesn't contain keywords or builtins | |||
|
41 | self.shell.alias_table_validate() | |||
|
42 | # Call again init_auto_alias() so we get 'rm -i' and other modified | |||
|
43 | # aliases since %rehash will probably clobber them | |||
|
44 | self.shell.init_auto_alias() | |||
|
45 | ||||
|
46 | ip.expose_magic("rehash", magic_rehash) | |||
|
47 | ||||
|
48 | # Exit | |||
|
49 | def magic_Quit(self, parameter_s=''): | |||
|
50 | """Exit IPython without confirmation (like %Exit).""" | |||
|
51 | ||||
|
52 | self.shell.exit_now = True | |||
|
53 | ||||
|
54 | ip.expose_magic("Quit", magic_Quit) | |||
|
55 | ||||
|
56 | ||||
|
57 | # make it autocallable fn if you really need it | |||
|
58 | def magic_p(self, parameter_s=''): | |||
|
59 | """Just a short alias for Python's 'print'.""" | |||
|
60 | exec 'print ' + parameter_s in self.shell.user_ns | |||
|
61 | ||||
|
62 | ip.expose_magic("p", magic_p) |
@@ -0,0 +1,215 | |||||
|
1 | """ Leo plugin for IPython | |||
|
2 | ||||
|
3 | Example use: | |||
|
4 | ||||
|
5 | nodes.foo = "hello world" | |||
|
6 | ||||
|
7 | -> create '@ipy foo' node with text "hello world" | |||
|
8 | ||||
|
9 | Access works also, and so does tab completion. | |||
|
10 | ||||
|
11 | """ | |||
|
12 | import IPython.ipapi | |||
|
13 | import IPython.genutils | |||
|
14 | import IPython.generics | |||
|
15 | import re | |||
|
16 | ||||
|
17 | ||||
|
18 | ||||
|
19 | ip = IPython.ipapi.get() | |||
|
20 | leo = ip.user_ns['leox'] | |||
|
21 | c,g = leo.c, leo.g | |||
|
22 | ||||
|
23 | # will probably be overwritten by user, but handy for experimentation early on | |||
|
24 | ip.user_ns['c'] = c | |||
|
25 | ip.user_ns['g'] = g | |||
|
26 | ||||
|
27 | ||||
|
28 | from IPython.external.simplegeneric import generic | |||
|
29 | import pprint | |||
|
30 | ||||
|
31 | @generic | |||
|
32 | def format_for_leo(obj): | |||
|
33 | """ Convert obj to string representiation (for editing in Leo)""" | |||
|
34 | return pprint.pformat(obj) | |||
|
35 | ||||
|
36 | @format_for_leo.when_type(list) | |||
|
37 | def format_list(obj): | |||
|
38 | return "\n".join(str(s) for s in obj) | |||
|
39 | ||||
|
40 | nodename_re = r'(@ipy?[\w-]+)?\s?(\w+)' | |||
|
41 | ||||
|
42 | def all_cells(): | |||
|
43 | d = {} | |||
|
44 | for p in c.allNodes_iter(): | |||
|
45 | h = p.headString() | |||
|
46 | if h.startswith('@') and len(h.split()) == 1: | |||
|
47 | continue | |||
|
48 | mo = re.match(nodename_re, h) | |||
|
49 | if not mo: | |||
|
50 | continue | |||
|
51 | d[mo.group(2)] = p.copy() | |||
|
52 | return d | |||
|
53 | ||||
|
54 | ||||
|
55 | class TrivialLeoWorkbook: | |||
|
56 | """ class to find cells """ | |||
|
57 | def __getattr__(self, key): | |||
|
58 | cells = all_cells() | |||
|
59 | p = cells[key] | |||
|
60 | body = p.bodyString() | |||
|
61 | return eval_body(body) | |||
|
62 | def __setattr__(self,key,val): | |||
|
63 | cells = all_cells() | |||
|
64 | p = cells.get(key,None) | |||
|
65 | if p is None: | |||
|
66 | add_var(key,val) | |||
|
67 | else: | |||
|
68 | c.setBodyString(p,format_for_leo(val)) | |||
|
69 | def __str__(self): | |||
|
70 | return "<TrivialLeoWorkbook>" | |||
|
71 | __repr__ = __str__ | |||
|
72 | ||||
|
73 | ip.user_ns['nodes'] = TrivialLeoWorkbook() | |||
|
74 | ||||
|
75 | ||||
|
76 | class LeoNode(object): | |||
|
77 | def __init__(self,p): | |||
|
78 | self.p = p.copy() | |||
|
79 | ||||
|
80 | def get_h(self): return self.p.headString() | |||
|
81 | def set_h(self,val): | |||
|
82 | print "set head",val | |||
|
83 | c.setHeadString(self.p,val) | |||
|
84 | ||||
|
85 | h = property( get_h, set_h) | |||
|
86 | ||||
|
87 | def get_b(self): return self.p.bodyString() | |||
|
88 | def set_b(self,val): | |||
|
89 | print "set body",val | |||
|
90 | c.setBodyString(self.p, val) | |||
|
91 | ||||
|
92 | b = property(get_b, set_b) | |||
|
93 | ||||
|
94 | def set_val(self, val): | |||
|
95 | self.b = pprint.pformat(val) | |||
|
96 | ||||
|
97 | val = property(lambda self: ip.ev(self.b.strip()), set_val) | |||
|
98 | ||||
|
99 | def set_l(self,val): | |||
|
100 | self.b = '\n'.join(val ) | |||
|
101 | l = property(lambda self : IPython.genutils.SList(self.b.splitlines()), | |||
|
102 | set_l) | |||
|
103 | ||||
|
104 | def __iter__(self): | |||
|
105 | return (LeoNode(p) for p in self.p.children_iter()) | |||
|
106 | ||||
|
107 | ||||
|
108 | class LeoWorkbook: | |||
|
109 | """ class for 'advanced' node access """ | |||
|
110 | def __getattr__(self, key): | |||
|
111 | if key.startswith('_') or key == 'trait_names': | |||
|
112 | raise AttributeError | |||
|
113 | cells = all_cells() | |||
|
114 | p = cells.get(key, None) | |||
|
115 | if p is None: | |||
|
116 | p = add_var(key,None) | |||
|
117 | ||||
|
118 | return LeoNode(p) | |||
|
119 | ||||
|
120 | def __str__(self): | |||
|
121 | return "<LeoWorkbook>" | |||
|
122 | __repr__ = __str__ | |||
|
123 | ip.user_ns['wb'] = LeoWorkbook() | |||
|
124 | ||||
|
125 | ||||
|
126 | _dummyval = object() | |||
|
127 | @IPython.generics.complete_object.when_type(LeoWorkbook) | |||
|
128 | def workbook_complete(obj, prev): | |||
|
129 | return all_cells().keys() | |||
|
130 | ||||
|
131 | ||||
|
132 | def add_var(varname, value = _dummyval): | |||
|
133 | nodename = '@ipy-var ' + varname | |||
|
134 | p2 = g.findNodeAnywhere(c,nodename) | |||
|
135 | if not c.positionExists(p2): | |||
|
136 | p2 = c.currentPosition().insertAfter() | |||
|
137 | c.setHeadString(p2,'@ipy ' + varname) | |||
|
138 | ||||
|
139 | c.setCurrentPosition(p2) | |||
|
140 | if value is _dummyval: | |||
|
141 | val = ip.user_ns[varname] | |||
|
142 | else: | |||
|
143 | val = value | |||
|
144 | if val is not None: | |||
|
145 | formatted = format_for_leo(val) | |||
|
146 | c.setBodyString(p2,formatted) | |||
|
147 | return p2 | |||
|
148 | ||||
|
149 | def add_file(self,fname): | |||
|
150 | p2 = c.currentPosition().insertAfter() | |||
|
151 | ||||
|
152 | def push_script(p): | |||
|
153 | ohist = ip.IP.output_hist | |||
|
154 | hstart = len(ip.IP.input_hist) | |||
|
155 | script = g.getScript(c,p,useSelectedText=False,forcePythonSentinels=False,useSentinels=False) | |||
|
156 | ||||
|
157 | script = g.splitLines(script + '\n') | |||
|
158 | script = ''.join(z for z in script if z.strip()) | |||
|
159 | ||||
|
160 | ip.runlines(script) | |||
|
161 | ||||
|
162 | has_output = False | |||
|
163 | for idx in range(hstart,len(ip.IP.input_hist)): | |||
|
164 | val = ohist.get(idx,None) | |||
|
165 | if val is None: | |||
|
166 | continue | |||
|
167 | has_output = True | |||
|
168 | inp = ip.IP.input_hist[idx] | |||
|
169 | if inp.strip(): | |||
|
170 | g.es('In: %s' % (inp[:40], ), tabName = 'IPython') | |||
|
171 | ||||
|
172 | g.es('<%d> %s' % (idx, pprint.pformat(ohist[idx],width = 40)), tabName = 'IPython') | |||
|
173 | ||||
|
174 | if not has_output: | |||
|
175 | g.es('ipy run: %s' %( p.headString(),), tabName = 'IPython') | |||
|
176 | ||||
|
177 | ||||
|
178 | def eval_body(body): | |||
|
179 | try: | |||
|
180 | val = ip.ev(body) | |||
|
181 | except: | |||
|
182 | # just use stringlist if it's not completely legal python expression | |||
|
183 | val = IPython.genutils.SList(body.splitlines()) | |||
|
184 | return val | |||
|
185 | ||||
|
186 | def push_variable(p,varname): | |||
|
187 | body = p.bodyString() | |||
|
188 | val = eval_body(body.strip()) | |||
|
189 | ip.user_ns[varname] = val | |||
|
190 | g.es('ipy var: %s' % (varname,), tabName = "IPython") | |||
|
191 | ||||
|
192 | def push_from_leo(p): | |||
|
193 | # headstring without @ are just scripts | |||
|
194 | if not p.headString().startswith('@'): | |||
|
195 | push_script(p) | |||
|
196 | return | |||
|
197 | tup = p.headString().split(None,1) | |||
|
198 | # @ipy foo is variable foo | |||
|
199 | if len(tup) == 2 and tup[0] == '@ipy': | |||
|
200 | varname = tup[1] | |||
|
201 | push_variable(p,varname) | |||
|
202 | return | |||
|
203 | ||||
|
204 | ip.user_ns['leox'].push = push_from_leo | |||
|
205 | ||||
|
206 | def leo_f(self,s): | |||
|
207 | ip = self.getapi() | |||
|
208 | s = s.strip() | |||
|
209 | if s in ip.user_ns: | |||
|
210 | add_var(s) | |||
|
211 | elif os.path.isfile(s): | |||
|
212 | # todo open file | |||
|
213 | pass | |||
|
214 | ||||
|
215 | ip.expose_magic('leo',leo_f) |
@@ -0,0 +1,47 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ | |||
|
3 | Add %p4 magic for pythonic p4 (Perforce) usage. | |||
|
4 | """ | |||
|
5 | ||||
|
6 | import IPython.ipapi | |||
|
7 | ip = IPython.ipapi.get() | |||
|
8 | ||||
|
9 | import os,sys,marshal | |||
|
10 | ||||
|
11 | import ipy_stock_completers | |||
|
12 | ||||
|
13 | def p4_f(self, parameter_s=''): | |||
|
14 | cmd = 'p4 -G ' + parameter_s | |||
|
15 | fobj = os.popen(cmd) | |||
|
16 | out = [] | |||
|
17 | while 1: | |||
|
18 | try: | |||
|
19 | out.append(marshal.load(fobj)) | |||
|
20 | except EOFError: | |||
|
21 | break | |||
|
22 | ||||
|
23 | return out | |||
|
24 | ||||
|
25 | def p4d(fname): | |||
|
26 | return os.popen('p4 where ' + fname).read().split()[0] | |||
|
27 | ||||
|
28 | ip.to_user_ns("p4d") | |||
|
29 | ||||
|
30 | ip.expose_magic('p4', p4_f) | |||
|
31 | ||||
|
32 | p4_commands = """\ | |||
|
33 | add admin annotate branch branches change changes changelist | |||
|
34 | changelists client clients counter counters delete depot depots | |||
|
35 | describe diff diff2 dirs edit filelog files fix fixes flush fstat | |||
|
36 | group groups have help info integrate integrated job jobs jobspec | |||
|
37 | label labels labelsync lock logger login logout monitor obliterate | |||
|
38 | opened passwd print protect rename reopen resolve resolved revert | |||
|
39 | review reviews set submit sync tag tickets triggers typemap unlock | |||
|
40 | user users verify workspace workspaces where""" | |||
|
41 | ||||
|
42 | def p4_completer(self,event): | |||
|
43 | return ipy_stock_completers.vcs_completer(p4_commands, event) | |||
|
44 | ||||
|
45 | ip.set_hook('complete_command', p4_completer, str_key = '%p4') | |||
|
46 | ip.set_hook('complete_command', p4_completer, str_key = 'p4') | |||
|
47 |
@@ -0,0 +1,46 | |||||
|
1 | """Config file for 'doctest' profile. | |||
|
2 | ||||
|
3 | This profile modifies the prompts to be the standard Python ones, so that you | |||
|
4 | can generate easily doctests from an IPython session. | |||
|
5 | ||||
|
6 | But more importantly, it enables pasting of code with '>>>' prompts and | |||
|
7 | arbitrary initial whitespace, as is typical of doctests in reST files and | |||
|
8 | docstrings. This allows you to easily re-run existing doctests and iteratively | |||
|
9 | work on them as part of your development workflow. | |||
|
10 | ||||
|
11 | The exception mode is also set to 'plain' so the generated exceptions are as | |||
|
12 | similar as possible to the default Python ones, for inclusion in doctests.""" | |||
|
13 | ||||
|
14 | # get various stuff that are there for historical / familiarity reasons | |||
|
15 | import ipy_legacy | |||
|
16 | ||||
|
17 | from IPython import ipapi | |||
|
18 | ||||
|
19 | from IPython.Extensions import InterpreterPasteInput | |||
|
20 | ||||
|
21 | def main(): | |||
|
22 | ip = ipapi.get() | |||
|
23 | o = ip.options | |||
|
24 | ||||
|
25 | # Set the prompts similar to the defaults | |||
|
26 | o.prompt_in1 = '>>> ' | |||
|
27 | o.prompt_in2 = '... ' | |||
|
28 | o.prompt_out = '' | |||
|
29 | ||||
|
30 | # Add a blank line before each new set of inputs. This is needed by | |||
|
31 | # doctest to distinguish each test from the next. | |||
|
32 | o.separate_in = '\n' | |||
|
33 | o.separate_out = '' | |||
|
34 | o.separate_out2 = '' | |||
|
35 | ||||
|
36 | # Disable pprint, so that outputs are printed as similarly to standard | |||
|
37 | # python as possible | |||
|
38 | o.pprint = False | |||
|
39 | ||||
|
40 | # Use plain exceptions, to also resemble normal pyhton. | |||
|
41 | o.xmode = 'plain' | |||
|
42 | ||||
|
43 | # Store the activity flag in the metadata bag from the running shell | |||
|
44 | ip.IP.meta.doctest_mode = True | |||
|
45 | ||||
|
46 | main() |
@@ -0,0 +1,4 | |||||
|
1 | """ Config file for 'default' profile """ | |||
|
2 | ||||
|
3 | # get various stuff that are there for historical / familiarity reasons | |||
|
4 | import ipy_legacy No newline at end of file |
@@ -0,0 +1,25 | |||||
|
1 | """ IPython 'sci' profile | |||
|
2 | ||||
|
3 | Replaces the old scipy profile. | |||
|
4 | ||||
|
5 | """ | |||
|
6 | ||||
|
7 | ||||
|
8 | import IPython.ipapi | |||
|
9 | import ipy_defaults | |||
|
10 | ||||
|
11 | def main(): | |||
|
12 | ip = IPython.ipapi.get() | |||
|
13 | ||||
|
14 | try: | |||
|
15 | ip.ex("import numpy") | |||
|
16 | ip.ex("import scipy") | |||
|
17 | ||||
|
18 | ip.ex("from numpy import *") | |||
|
19 | ip.ex("from scipy import *") | |||
|
20 | print "SciPy profile successfully loaded." | |||
|
21 | except ImportError: | |||
|
22 | print "Unable to start scipy profile, are scipy and numpy installed?" | |||
|
23 | ||||
|
24 | ||||
|
25 | main() |
@@ -0,0 +1,226 | |||||
|
1 | """Shell mode for IPython. | |||
|
2 | ||||
|
3 | Start ipython in shell mode by invoking "ipython -p sh" | |||
|
4 | ||||
|
5 | (the old version, "ipython -p pysh" still works but this is the more "modern" | |||
|
6 | shell mode and is recommended for users who don't care about pysh-mode | |||
|
7 | compatibility) | |||
|
8 | """ | |||
|
9 | ||||
|
10 | from IPython import ipapi | |||
|
11 | import os,textwrap | |||
|
12 | ||||
|
13 | # The import below effectively obsoletes your old-style ipythonrc[.ini], | |||
|
14 | # so consider yourself warned! | |||
|
15 | ||||
|
16 | import ipy_defaults | |||
|
17 | ||||
|
18 | def main(): | |||
|
19 | ip = ipapi.get() | |||
|
20 | o = ip.options | |||
|
21 | # autocall to "full" mode (smart mode is default, I like full mode) | |||
|
22 | ||||
|
23 | o.autocall = 2 | |||
|
24 | ||||
|
25 | # Jason Orendorff's path class is handy to have in user namespace | |||
|
26 | # if you are doing shell-like stuff | |||
|
27 | try: | |||
|
28 | ip.ex("from IPython.external.path import path" ) | |||
|
29 | except ImportError: | |||
|
30 | pass | |||
|
31 | ||||
|
32 | # beefed up %env is handy in shell mode | |||
|
33 | import envpersist | |||
|
34 | ||||
|
35 | # To see where mycmd resides (in path/aliases), do %which mycmd | |||
|
36 | import ipy_which | |||
|
37 | ||||
|
38 | # tab completers for hg, svn, ... | |||
|
39 | import ipy_app_completers | |||
|
40 | ||||
|
41 | # To make executables foo and bar in mybin usable without PATH change, do: | |||
|
42 | # %rehashdir c:/mybin | |||
|
43 | # %store foo | |||
|
44 | # %store bar | |||
|
45 | import ipy_rehashdir | |||
|
46 | import ipy_signals | |||
|
47 | ||||
|
48 | ip.ex('import os') | |||
|
49 | ip.ex("def up(): os.chdir('..')") | |||
|
50 | ||||
|
51 | # Nice prompt | |||
|
52 | ||||
|
53 | o.prompt_in1= r'\C_LightBlue[\C_LightCyan\Y2\C_LightBlue]\C_Green|\#> ' | |||
|
54 | o.prompt_in2= r'\C_Green|\C_LightGreen\D\C_Green> ' | |||
|
55 | o.prompt_out= '<\#> ' | |||
|
56 | ||||
|
57 | from IPython import Release | |||
|
58 | ||||
|
59 | import sys | |||
|
60 | # Non-chatty banner | |||
|
61 | o.banner = "IPython %s [on Py %s]\n" % (Release.version,sys.version.split(None,1)[0]) | |||
|
62 | ||||
|
63 | ||||
|
64 | ip.IP.default_option('cd','-q') | |||
|
65 | ip.IP.default_option('macro', '-r') | |||
|
66 | # If you only rarely want to execute the things you %edit... | |||
|
67 | #ip.IP.default_option('edit','-x') | |||
|
68 | ||||
|
69 | ||||
|
70 | o.prompts_pad_left="1" | |||
|
71 | # Remove all blank lines in between prompts, like a normal shell. | |||
|
72 | o.separate_in="0" | |||
|
73 | o.separate_out="0" | |||
|
74 | o.separate_out2="0" | |||
|
75 | ||||
|
76 | # now alias all syscommands | |||
|
77 | ||||
|
78 | db = ip.db | |||
|
79 | ||||
|
80 | syscmds = db.get("syscmdlist",[] ) | |||
|
81 | if not syscmds: | |||
|
82 | print textwrap.dedent(""" | |||
|
83 | System command list not initialized, probably the first run... | |||
|
84 | running %rehashx to refresh the command list. Run %rehashx | |||
|
85 | again to refresh command list (after installing new software etc.) | |||
|
86 | """) | |||
|
87 | ip.magic('rehashx') | |||
|
88 | syscmds = db.get("syscmdlist") | |||
|
89 | ||||
|
90 | # lowcase aliases on win32 only | |||
|
91 | if os.name == 'posix': | |||
|
92 | mapper = lambda s:s | |||
|
93 | else: | |||
|
94 | def mapper(s): return s.lower() | |||
|
95 | ||||
|
96 | for cmd in syscmds: | |||
|
97 | # print "sys",cmd #dbg | |||
|
98 | noext, ext = os.path.splitext(cmd) | |||
|
99 | key = mapper(noext) | |||
|
100 | if key not in ip.IP.alias_table: | |||
|
101 | ip.defalias(key, cmd) | |||
|
102 | ||||
|
103 | # mglob combines 'find', recursion, exclusion... '%mglob?' to learn more | |||
|
104 | ip.load("IPython.external.mglob") | |||
|
105 | ||||
|
106 | # win32 is crippled w/o cygwin, try to help it a little bit | |||
|
107 | if sys.platform == 'win32': | |||
|
108 | if 'cygwin' in os.environ['PATH'].lower(): | |||
|
109 | # use the colors of cygwin ls (recommended) | |||
|
110 | ip.defalias('d', 'ls -F --color=auto') | |||
|
111 | else: | |||
|
112 | # get icp, imv, imkdir, igrep, irm,... | |||
|
113 | ip.load('ipy_fsops') | |||
|
114 | ||||
|
115 | # and the next best thing to real 'ls -F' | |||
|
116 | ip.defalias('d','dir /w /og /on') | |||
|
117 | ||||
|
118 | extend_shell_behavior(ip) | |||
|
119 | ||||
|
120 | # XXX You do not need to understand the next function! | |||
|
121 | # This should probably be moved out of profile | |||
|
122 | ||||
|
123 | def extend_shell_behavior(ip): | |||
|
124 | ||||
|
125 | # Instead of making signature a global variable tie it to IPSHELL. | |||
|
126 | # In future if it is required to distinguish between different | |||
|
127 | # shells we can assign a signature per shell basis | |||
|
128 | ip.IP.__sig__ = 0xa005 | |||
|
129 | # mark the IPSHELL with this signature | |||
|
130 | ip.IP.user_ns['__builtins__'].__dict__['__sig__'] = ip.IP.__sig__ | |||
|
131 | ||||
|
132 | from IPython.Itpl import ItplNS | |||
|
133 | from IPython.genutils import shell | |||
|
134 | # utility to expand user variables via Itpl | |||
|
135 | # xxx do something sensible with depth? | |||
|
136 | ip.IP.var_expand = lambda cmd, lvars=None, depth=2: \ | |||
|
137 | str(ItplNS(cmd, ip.IP.user_ns, get_locals())) | |||
|
138 | ||||
|
139 | def get_locals(): | |||
|
140 | """ Substituting a variable through Itpl deep inside the IPSHELL stack | |||
|
141 | requires the knowledge of all the variables in scope upto the last | |||
|
142 | IPSHELL frame. This routine simply merges all the local variables | |||
|
143 | on the IPSHELL stack without worrying about their scope rules | |||
|
144 | """ | |||
|
145 | import sys | |||
|
146 | # note lambda expression constitues a function call | |||
|
147 | # hence fno should be incremented by one | |||
|
148 | getsig = lambda fno: sys._getframe(fno+1).f_globals \ | |||
|
149 | ['__builtins__'].__dict__['__sig__'] | |||
|
150 | getlvars = lambda fno: sys._getframe(fno+1).f_locals | |||
|
151 | # trackback until we enter the IPSHELL | |||
|
152 | frame_no = 1 | |||
|
153 | sig = ip.IP.__sig__ | |||
|
154 | fsig = ~sig | |||
|
155 | while fsig != sig : | |||
|
156 | try: | |||
|
157 | fsig = getsig(frame_no) | |||
|
158 | except (AttributeError, KeyError): | |||
|
159 | frame_no += 1 | |||
|
160 | except ValueError: | |||
|
161 | # stack is depleted | |||
|
162 | # call did not originate from IPSHELL | |||
|
163 | return {} | |||
|
164 | first_frame = frame_no | |||
|
165 | # walk further back until we exit from IPSHELL or deplete stack | |||
|
166 | try: | |||
|
167 | while(sig == getsig(frame_no+1)): | |||
|
168 | frame_no += 1 | |||
|
169 | except (AttributeError, KeyError, ValueError): | |||
|
170 | pass | |||
|
171 | # merge the locals from top down hence overriding | |||
|
172 | # any re-definitions of variables, functions etc. | |||
|
173 | lvars = {} | |||
|
174 | for fno in range(frame_no, first_frame-1, -1): | |||
|
175 | lvars.update(getlvars(fno)) | |||
|
176 | #print '\n'*5, first_frame, frame_no, '\n', lvars, '\n'*5 #dbg | |||
|
177 | return lvars | |||
|
178 | ||||
|
179 | def _runlines(lines): | |||
|
180 | """Run a string of one or more lines of source. | |||
|
181 | ||||
|
182 | This method is capable of running a string containing multiple source | |||
|
183 | lines, as if they had been entered at the IPython prompt. Since it | |||
|
184 | exposes IPython's processing machinery, the given strings can contain | |||
|
185 | magic calls (%magic), special shell access (!cmd), etc.""" | |||
|
186 | ||||
|
187 | # We must start with a clean buffer, in case this is run from an | |||
|
188 | # interactive IPython session (via a magic, for example). | |||
|
189 | ip.IP.resetbuffer() | |||
|
190 | lines = lines.split('\n') | |||
|
191 | more = 0 | |||
|
192 | command = '' | |||
|
193 | for line in lines: | |||
|
194 | # skip blank lines so we don't mess up the prompt counter, but do | |||
|
195 | # NOT skip even a blank line if we are in a code block (more is | |||
|
196 | # true) | |||
|
197 | # if command is not empty trim the line | |||
|
198 | if command != '' : | |||
|
199 | line = line.strip() | |||
|
200 | # add the broken line to the command | |||
|
201 | if line and line[-1] == '\\' : | |||
|
202 | command += line[0:-1] + ' ' | |||
|
203 | more = True | |||
|
204 | continue | |||
|
205 | else : | |||
|
206 | # add the last (current) line to the command | |||
|
207 | command += line | |||
|
208 | if command or more: | |||
|
209 | # push to raw history, so hist line numbers stay in sync | |||
|
210 | ip.IP.input_hist_raw.append("# " + command + "\n") | |||
|
211 | ||||
|
212 | more = ip.IP.push(ip.IP.prefilter(command,more)) | |||
|
213 | command = '' | |||
|
214 | # IPython's runsource returns None if there was an error | |||
|
215 | # compiling the code. This allows us to stop processing right | |||
|
216 | # away, so the user gets the error message at the right place. | |||
|
217 | if more is None: | |||
|
218 | break | |||
|
219 | # final newline in case the input didn't have it, so that the code | |||
|
220 | # actually does get executed | |||
|
221 | if more: | |||
|
222 | ip.IP.push('\n') | |||
|
223 | ||||
|
224 | ip.IP.runlines = _runlines | |||
|
225 | ||||
|
226 | main() |
@@ -0,0 +1,317 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | # | |||
|
3 | # File: ipy_profile_zope.py | |||
|
4 | # | |||
|
5 | # Copyright (c) InQuant GmbH | |||
|
6 | # | |||
|
7 | # An ipython profile for zope and plone. Some ideas | |||
|
8 | # stolen from http://www.tomster.org. | |||
|
9 | # | |||
|
10 | # Distributed under the terms of the BSD License. The full license is in | |||
|
11 | # the file COPYING, distributed as part of this software. | |||
|
12 | ||||
|
13 | __author__ = """Stefan Eletzhofer <stefan.eletzhofer@inquant.de>""" | |||
|
14 | __docformat__ = 'plaintext' | |||
|
15 | __revision__ = "$Revision$" | |||
|
16 | ||||
|
17 | from IPython import ipapi | |||
|
18 | from IPython import Release | |||
|
19 | from types import StringType | |||
|
20 | import sys | |||
|
21 | import os | |||
|
22 | import textwrap | |||
|
23 | ||||
|
24 | # The import below effectively obsoletes your old-style ipythonrc[.ini], | |||
|
25 | # so consider yourself warned! | |||
|
26 | # import ipy_defaults | |||
|
27 | ||||
|
28 | _marker = [] | |||
|
29 | def shasattr(obj, attr, acquire=False): | |||
|
30 | """ See Archetypes/utils.py | |||
|
31 | """ | |||
|
32 | if not acquire: | |||
|
33 | obj = obj.aq_base | |||
|
34 | return getattr(obj, attr, _marker) is not _marker | |||
|
35 | ||||
|
36 | class ZopeDebug(object): | |||
|
37 | def __init__(self): | |||
|
38 | ||||
|
39 | self.instancehome = os.environ.get( "INSTANCE_HOME" ) | |||
|
40 | ||||
|
41 | configfile = os.environ.get( "CONFIG_FILE" ) | |||
|
42 | if configfile is None and self.instancehome is not None: | |||
|
43 | configfile = os.path.join( self.instancehome, "etc", "zope.conf" ) | |||
|
44 | ||||
|
45 | if configfile is None: | |||
|
46 | raise RuntimeError( "CONFIG_FILE env not set" ) | |||
|
47 | ||||
|
48 | print "CONFIG_FILE=", configfile | |||
|
49 | print "INSTANCE_HOME=", self.instancehome | |||
|
50 | ||||
|
51 | self.configfile = configfile | |||
|
52 | ||||
|
53 | try: | |||
|
54 | from Zope2 import configure | |||
|
55 | except ImportError: | |||
|
56 | from Zope import configure | |||
|
57 | ||||
|
58 | configure( configfile ) | |||
|
59 | ||||
|
60 | try: | |||
|
61 | import Zope2 | |||
|
62 | app = Zope2.app() | |||
|
63 | except ImportError: | |||
|
64 | import Zope | |||
|
65 | app = Zope.app() | |||
|
66 | ||||
|
67 | from Testing.makerequest import makerequest | |||
|
68 | self.app = makerequest( app ) | |||
|
69 | ||||
|
70 | try: | |||
|
71 | self._make_permissive() | |||
|
72 | print "Permissive security installed" | |||
|
73 | except: | |||
|
74 | print "Permissive security NOT installed" | |||
|
75 | ||||
|
76 | self._pwd = self.portal or self.app | |||
|
77 | ||||
|
78 | try: | |||
|
79 | from zope.component import getSiteManager | |||
|
80 | from zope.component import getGlobalSiteManager | |||
|
81 | from zope.app.component.hooks import setSite | |||
|
82 | ||||
|
83 | if self.portal is not None: | |||
|
84 | setSite( self.portal ) | |||
|
85 | ||||
|
86 | gsm = getGlobalSiteManager() | |||
|
87 | sm = getSiteManager() | |||
|
88 | ||||
|
89 | if sm is gsm: | |||
|
90 | print "ERROR SETTING SITE!" | |||
|
91 | except: | |||
|
92 | pass | |||
|
93 | ||||
|
94 | ||||
|
95 | @property | |||
|
96 | def utils(self): | |||
|
97 | class Utils(object): | |||
|
98 | commit = self.commit | |||
|
99 | sync = self.sync | |||
|
100 | objectInfo = self.objectInfo | |||
|
101 | ls = self.ls | |||
|
102 | pwd = self.pwd | |||
|
103 | cd = self.cd | |||
|
104 | su = self.su | |||
|
105 | getCatalogInfo = self.getCatalogInfo | |||
|
106 | ||||
|
107 | @property | |||
|
108 | def cwd(self): | |||
|
109 | return self.pwd() | |||
|
110 | ||||
|
111 | return Utils() | |||
|
112 | ||||
|
113 | @property | |||
|
114 | def namespace(self): | |||
|
115 | return dict( utils=self.utils, app=self.app, portal=self.portal ) | |||
|
116 | ||||
|
117 | @property | |||
|
118 | def portal(self): | |||
|
119 | portals = self.app.objectValues( "Plone Site" ) | |||
|
120 | if len(portals): | |||
|
121 | return portals[0] | |||
|
122 | else: | |||
|
123 | raise KeyError( "No Plone Site found.") | |||
|
124 | ||||
|
125 | def pwd(self): | |||
|
126 | return self._pwd | |||
|
127 | ||||
|
128 | def _make_permissive(self): | |||
|
129 | """ | |||
|
130 | Make a permissive security manager with all rights. Hell, | |||
|
131 | we're developers, aren't we? Security is for whimps. :) | |||
|
132 | """ | |||
|
133 | from Products.CMFCore.tests.base.security import PermissiveSecurityPolicy | |||
|
134 | import AccessControl | |||
|
135 | from AccessControl.SecurityManagement import newSecurityManager | |||
|
136 | from AccessControl.SecurityManager import setSecurityPolicy | |||
|
137 | ||||
|
138 | _policy = PermissiveSecurityPolicy() | |||
|
139 | self.oldpolicy = setSecurityPolicy(_policy) | |||
|
140 | newSecurityManager(None, AccessControl.User.system) | |||
|
141 | ||||
|
142 | def su(self, username): | |||
|
143 | """ Change to named user. | |||
|
144 | """ | |||
|
145 | # TODO Make it easy to change back to permissive security. | |||
|
146 | user = self.portal.acl_users.getUser(username) | |||
|
147 | if not user: | |||
|
148 | print "Can't find %s in %s" % (username, self.portal.acl_users) | |||
|
149 | return | |||
|
150 | ||||
|
151 | from AccessControl import ZopeSecurityPolicy | |||
|
152 | import AccessControl | |||
|
153 | from AccessControl.SecurityManagement import newSecurityManager, getSecurityManager | |||
|
154 | from AccessControl.SecurityManager import setSecurityPolicy | |||
|
155 | ||||
|
156 | _policy = ZopeSecurityPolicy | |||
|
157 | self.oldpolicy = setSecurityPolicy(_policy) | |||
|
158 | wrapped_user = user.__of__(self.portal.acl_users) | |||
|
159 | newSecurityManager(None, user) | |||
|
160 | print 'User changed.' | |||
|
161 | return getSecurityManager().getUser() | |||
|
162 | ||||
|
163 | def getCatalogInfo(self, obj=None, catalog='portal_catalog', query=None, sort_on='created', sort_order='reverse' ): | |||
|
164 | """ Inspect portal_catalog. Pass an object or object id for a | |||
|
165 | default query on that object, or pass an explicit query. | |||
|
166 | """ | |||
|
167 | if obj and query: | |||
|
168 | print "Ignoring %s, using query." % obj | |||
|
169 | ||||
|
170 | catalog = self.portal.get(catalog) | |||
|
171 | if not catalog: | |||
|
172 | return 'No catalog' | |||
|
173 | ||||
|
174 | indexes = catalog._catalog.indexes | |||
|
175 | if not query: | |||
|
176 | if type(obj) is StringType: | |||
|
177 | cwd = self.pwd() | |||
|
178 | obj = cwd.unrestrictedTraverse( obj ) | |||
|
179 | # If the default in the signature is mutable, its value will | |||
|
180 | # persist across invocations. | |||
|
181 | query = {} | |||
|
182 | if indexes.get('path'): | |||
|
183 | from string import join | |||
|
184 | path = join(obj.getPhysicalPath(), '/') | |||
|
185 | query.update({'path': path}) | |||
|
186 | if indexes.get('getID'): | |||
|
187 | query.update({'getID': obj.id, }) | |||
|
188 | if indexes.get('UID') and shasattr(obj, 'UID'): | |||
|
189 | query.update({'UID': obj.UID(), }) | |||
|
190 | if indexes.get(sort_on): | |||
|
191 | query.update({'sort_on': sort_on, 'sort_order': sort_order}) | |||
|
192 | if not query: | |||
|
193 | return 'Empty query' | |||
|
194 | results = catalog(**query) | |||
|
195 | ||||
|
196 | result_info = [] | |||
|
197 | for r in results: | |||
|
198 | rid = r.getRID() | |||
|
199 | if rid: | |||
|
200 | result_info.append( | |||
|
201 | {'path': catalog.getpath(rid), | |||
|
202 | 'metadata': catalog.getMetadataForRID(rid), | |||
|
203 | 'indexes': catalog.getIndexDataForRID(rid), } | |||
|
204 | ) | |||
|
205 | else: | |||
|
206 | result_info.append({'missing': rid}) | |||
|
207 | ||||
|
208 | if len(result_info) == 1: | |||
|
209 | return result_info[0] | |||
|
210 | return result_info | |||
|
211 | ||||
|
212 | def commit(self): | |||
|
213 | """ | |||
|
214 | Commit the transaction. | |||
|
215 | """ | |||
|
216 | try: | |||
|
217 | import transaction | |||
|
218 | transaction.get().commit() | |||
|
219 | except ImportError: | |||
|
220 | get_transaction().commit() | |||
|
221 | ||||
|
222 | def sync(self): | |||
|
223 | """ | |||
|
224 | Sync the app's view of the zodb. | |||
|
225 | """ | |||
|
226 | self.app._p_jar.sync() | |||
|
227 | ||||
|
228 | def objectInfo( self, o ): | |||
|
229 | """ | |||
|
230 | Return a descriptive string of an object | |||
|
231 | """ | |||
|
232 | Title = "" | |||
|
233 | t = getattr( o, 'Title', None ) | |||
|
234 | if t: | |||
|
235 | Title = t() | |||
|
236 | return {'id': o.getId(), | |||
|
237 | 'Title': Title, | |||
|
238 | 'portal_type': getattr( o, 'portal_type', o.meta_type), | |||
|
239 | 'folderish': o.isPrincipiaFolderish | |||
|
240 | } | |||
|
241 | ||||
|
242 | def cd( self, path ): | |||
|
243 | """ | |||
|
244 | Change current dir to a specific folder. | |||
|
245 | ||||
|
246 | cd( ".." ) | |||
|
247 | cd( "/plone/Members/admin" ) | |||
|
248 | cd( portal.Members.admin ) | |||
|
249 | etc. | |||
|
250 | """ | |||
|
251 | if type(path) is not StringType: | |||
|
252 | path = '/'.join(path.getPhysicalPath()) | |||
|
253 | cwd = self.pwd() | |||
|
254 | x = cwd.unrestrictedTraverse( path ) | |||
|
255 | if x is None: | |||
|
256 | raise KeyError( "Can't cd to %s" % path ) | |||
|
257 | ||||
|
258 | print "%s -> %s" % ( self.pwd().getId(), x.getId() ) | |||
|
259 | self._pwd = x | |||
|
260 | ||||
|
261 | def ls( self, x=None ): | |||
|
262 | """ | |||
|
263 | List object(s) | |||
|
264 | """ | |||
|
265 | if type(x) is StringType: | |||
|
266 | cwd = self.pwd() | |||
|
267 | x = cwd.unrestrictedTraverse( x ) | |||
|
268 | if x is None: | |||
|
269 | x = self.pwd() | |||
|
270 | if x.isPrincipiaFolderish: | |||
|
271 | return [self.objectInfo(o) for id, o in x.objectItems()] | |||
|
272 | else: | |||
|
273 | return self.objectInfo( x ) | |||
|
274 | ||||
|
275 | zope_debug = None | |||
|
276 | ||||
|
277 | def ipy_set_trace(): | |||
|
278 | import IPython; IPython.Debugger.Pdb().set_trace() | |||
|
279 | ||||
|
280 | def main(): | |||
|
281 | global zope_debug | |||
|
282 | ip = ipapi.get() | |||
|
283 | o = ip.options | |||
|
284 | # autocall to "full" mode (smart mode is default, I like full mode) | |||
|
285 | ||||
|
286 | SOFTWARE_HOME = os.environ.get( "SOFTWARE_HOME" ) | |||
|
287 | sys.path.append( SOFTWARE_HOME ) | |||
|
288 | print "SOFTWARE_HOME=%s\n" % SOFTWARE_HOME | |||
|
289 | ||||
|
290 | zope_debug = ZopeDebug() | |||
|
291 | ||||
|
292 | # <HACK ALERT> | |||
|
293 | import pdb; | |||
|
294 | pdb.set_trace = ipy_set_trace | |||
|
295 | # </HACK ALERT> | |||
|
296 | ||||
|
297 | # I like my banner minimal. | |||
|
298 | o.banner = "ZOPE Py %s IPy %s\n" % (sys.version.split('\n')[0],Release.version) | |||
|
299 | ||||
|
300 | print textwrap.dedent("""\ | |||
|
301 | ZOPE mode iPython shell. | |||
|
302 | ||||
|
303 | Bound names: | |||
|
304 | app | |||
|
305 | portal | |||
|
306 | utils.{ %s } | |||
|
307 | ||||
|
308 | Uses the $SOFTWARE_HOME and $CONFIG_FILE environment | |||
|
309 | variables. | |||
|
310 | """ % ( ",".join([ x for x in dir(zope_debug.utils) if not x.startswith("_") ] ) ) ) | |||
|
311 | ||||
|
312 | ||||
|
313 | ip.user_ns.update( zope_debug.namespace ) | |||
|
314 | ||||
|
315 | ||||
|
316 | main() | |||
|
317 | # vim: set ft=python ts=4 sw=4 expandtab : |
@@ -0,0 +1,31 | |||||
|
1 | import inspect | |||
|
2 | import IPython.ipapi | |||
|
3 | from IPython.genutils import arg_split | |||
|
4 | ip = IPython.ipapi.get() | |||
|
5 | ||||
|
6 | from IPython import Debugger | |||
|
7 | ||||
|
8 | def call_pydb(self, args): | |||
|
9 | """Invoke pydb with the supplied parameters.""" | |||
|
10 | try: | |||
|
11 | import pydb | |||
|
12 | except ImportError: | |||
|
13 | raise ImportError("pydb doesn't seem to be installed.") | |||
|
14 | ||||
|
15 | if not hasattr(pydb.pydb, "runv"): | |||
|
16 | raise ImportError("You need pydb version 1.19 or later installed.") | |||
|
17 | ||||
|
18 | argl = arg_split(args) | |||
|
19 | # print argl # dbg | |||
|
20 | if len(inspect.getargspec(pydb.runv)[0]) == 2: | |||
|
21 | pdb = Debugger.Pdb(color_scheme=self.rc.colors) | |||
|
22 | ip.IP.history_saving_wrapper( lambda : pydb.runv(argl, pdb) )() | |||
|
23 | else: | |||
|
24 | ip.IP.history_saving_wrapper( lambda : pydb.runv(argl) )() | |||
|
25 | ||||
|
26 | ||||
|
27 | ip.expose_magic("pydb",call_pydb) | |||
|
28 | ||||
|
29 | ||||
|
30 | ||||
|
31 |
@@ -0,0 +1,140 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ IPython extension: add %rehashdir magic | |||
|
3 | ||||
|
4 | Usage: | |||
|
5 | ||||
|
6 | %rehashdir c:/bin c:/tools | |||
|
7 | - Add all executables under c:/bin and c:/tools to alias table, in | |||
|
8 | order to make them directly executable from any directory. | |||
|
9 | ||||
|
10 | This also serves as an example on how to extend ipython | |||
|
11 | with new magic functions. | |||
|
12 | ||||
|
13 | Unlike rest of ipython, this requires Python 2.4 (optional | |||
|
14 | extensions are allowed to do that). | |||
|
15 | ||||
|
16 | """ | |||
|
17 | ||||
|
18 | import IPython.ipapi | |||
|
19 | ip = IPython.ipapi.get() | |||
|
20 | ||||
|
21 | ||||
|
22 | import os,re,fnmatch,sys | |||
|
23 | ||||
|
24 | def selflaunch(ip,line): | |||
|
25 | """ Launch python script with 'this' interpreter | |||
|
26 | ||||
|
27 | e.g. d:\foo\ipykit.exe a.py | |||
|
28 | ||||
|
29 | """ | |||
|
30 | ||||
|
31 | tup = line.split(None,1) | |||
|
32 | if len(tup) == 1: | |||
|
33 | print "Launching nested ipython session" | |||
|
34 | os.system(sys.executable) | |||
|
35 | return | |||
|
36 | ||||
|
37 | cmd = sys.executable + ' ' + tup[1] | |||
|
38 | print ">",cmd | |||
|
39 | os.system(cmd) | |||
|
40 | ||||
|
41 | class PyLauncher: | |||
|
42 | """ Invoke selflanucher on the specified script | |||
|
43 | ||||
|
44 | This is mostly useful for associating with scripts using:: | |||
|
45 | _ip.defalias('foo',PyLauncher('foo_script.py')) | |||
|
46 | ||||
|
47 | """ | |||
|
48 | def __init__(self,script): | |||
|
49 | self.script = os.path.abspath(script) | |||
|
50 | def __call__(self, ip, line): | |||
|
51 | if self.script.endswith('.ipy'): | |||
|
52 | ip.runlines(open(self.script).read()) | |||
|
53 | else: | |||
|
54 | # first word is the script/alias name itself, strip it | |||
|
55 | tup = line.split(None,1) | |||
|
56 | if len(tup) == 2: | |||
|
57 | tail = ' ' + tup[1] | |||
|
58 | else: | |||
|
59 | tail = '' | |||
|
60 | ||||
|
61 | selflaunch(ip,"py " + self.script + tail) | |||
|
62 | def __repr__(self): | |||
|
63 | return 'PyLauncher("%s")' % self.script | |||
|
64 | ||||
|
65 | def rehashdir_f(self,arg): | |||
|
66 | """ Add executables in all specified dirs to alias table | |||
|
67 | ||||
|
68 | Usage: | |||
|
69 | ||||
|
70 | %rehashdir c:/bin;c:/tools | |||
|
71 | - Add all executables under c:/bin and c:/tools to alias table, in | |||
|
72 | order to make them directly executable from any directory. | |||
|
73 | ||||
|
74 | Without arguments, add all executables in current directory. | |||
|
75 | ||||
|
76 | """ | |||
|
77 | ||||
|
78 | # most of the code copied from Magic.magic_rehashx | |||
|
79 | ||||
|
80 | def isjunk(fname): | |||
|
81 | junk = ['*~'] | |||
|
82 | for j in junk: | |||
|
83 | if fnmatch.fnmatch(fname, j): | |||
|
84 | return True | |||
|
85 | return False | |||
|
86 | ||||
|
87 | created = [] | |||
|
88 | if not arg: | |||
|
89 | arg = '.' | |||
|
90 | path = map(os.path.abspath,arg.split(';')) | |||
|
91 | alias_table = self.shell.alias_table | |||
|
92 | ||||
|
93 | if os.name == 'posix': | |||
|
94 | isexec = lambda fname:os.path.isfile(fname) and \ | |||
|
95 | os.access(fname,os.X_OK) | |||
|
96 | else: | |||
|
97 | ||||
|
98 | try: | |||
|
99 | winext = os.environ['pathext'].replace(';','|').replace('.','') | |||
|
100 | except KeyError: | |||
|
101 | winext = 'exe|com|bat|py' | |||
|
102 | if 'py' not in winext: | |||
|
103 | winext += '|py' | |||
|
104 | ||||
|
105 | execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) | |||
|
106 | isexec = lambda fname:os.path.isfile(fname) and execre.match(fname) | |||
|
107 | savedir = os.getcwd() | |||
|
108 | try: | |||
|
109 | # write the whole loop for posix/Windows so we don't have an if in | |||
|
110 | # the innermost part | |||
|
111 | if os.name == 'posix': | |||
|
112 | for pdir in path: | |||
|
113 | os.chdir(pdir) | |||
|
114 | for ff in os.listdir(pdir): | |||
|
115 | if isexec(ff) and not isjunk(ff): | |||
|
116 | # each entry in the alias table must be (N,name), | |||
|
117 | # where N is the number of positional arguments of the | |||
|
118 | # alias. | |||
|
119 | src,tgt = os.path.splitext(ff)[0], os.path.abspath(ff) | |||
|
120 | created.append(src) | |||
|
121 | alias_table[src] = (0,tgt) | |||
|
122 | else: | |||
|
123 | for pdir in path: | |||
|
124 | os.chdir(pdir) | |||
|
125 | for ff in os.listdir(pdir): | |||
|
126 | if isexec(ff) and not isjunk(ff): | |||
|
127 | src, tgt = execre.sub(r'\1',ff), os.path.abspath(ff) | |||
|
128 | src = src.lower() | |||
|
129 | created.append(src) | |||
|
130 | alias_table[src] = (0,tgt) | |||
|
131 | # Make sure the alias table doesn't contain keywords or builtins | |||
|
132 | self.shell.alias_table_validate() | |||
|
133 | # Call again init_auto_alias() so we get 'rm -i' and other | |||
|
134 | # modified aliases since %rehashx will probably clobber them | |||
|
135 | # self.shell.init_auto_alias() | |||
|
136 | finally: | |||
|
137 | os.chdir(savedir) | |||
|
138 | return created | |||
|
139 | ||||
|
140 | ip.expose_magic("rehashdir",rehashdir_f) |
@@ -0,0 +1,68 | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | ||||
|
3 | """ IPython extension: Render templates from variables and paste to clipbard """ | |||
|
4 | ||||
|
5 | import IPython.ipapi | |||
|
6 | ||||
|
7 | ip = IPython.ipapi.get() | |||
|
8 | ||||
|
9 | from string import Template | |||
|
10 | import sys,os | |||
|
11 | ||||
|
12 | from IPython.Itpl import itplns | |||
|
13 | ||||
|
14 | def toclip_w32(s): | |||
|
15 | """ Places contents of s to clipboard | |||
|
16 | ||||
|
17 | Needs pyvin32 to work: | |||
|
18 | http://sourceforge.net/projects/pywin32/ | |||
|
19 | """ | |||
|
20 | import win32clipboard as cl | |||
|
21 | import win32con | |||
|
22 | cl.OpenClipboard() | |||
|
23 | cl.EmptyClipboard() | |||
|
24 | cl.SetClipboardText( s.replace('\n','\r\n' )) | |||
|
25 | cl.CloseClipboard() | |||
|
26 | ||||
|
27 | try: | |||
|
28 | import win32clipboard | |||
|
29 | toclip = toclip_w32 | |||
|
30 | except ImportError: | |||
|
31 | def toclip(s): pass | |||
|
32 | ||||
|
33 | ||||
|
34 | def render(tmpl): | |||
|
35 | """ Render a template (Itpl format) from ipython variables | |||
|
36 | ||||
|
37 | Example: | |||
|
38 | ||||
|
39 | $ import ipy_render | |||
|
40 | $ my_name = 'Bob' # %store this for convenience | |||
|
41 | $ t_submission_form = "Submission report, author: $my_name" # %store also | |||
|
42 | $ render t_submission_form | |||
|
43 | ||||
|
44 | => returns "Submission report, author: Bob" and copies to clipboard on win32 | |||
|
45 | ||||
|
46 | # if template exist as a file, read it. Note: ;f hei vaan => f("hei vaan") | |||
|
47 | $ ;render c:/templates/greeting.txt | |||
|
48 | ||||
|
49 | Template examples (Ka-Ping Yee's Itpl library): | |||
|
50 | ||||
|
51 | Here is a $string. | |||
|
52 | Here is a $module.member. | |||
|
53 | Here is an $object.member. | |||
|
54 | Here is a $functioncall(with, arguments). | |||
|
55 | Here is an ${arbitrary + expression}. | |||
|
56 | Here is an $array[3] member. | |||
|
57 | Here is a $dictionary['member']. | |||
|
58 | """ | |||
|
59 | ||||
|
60 | if os.path.isfile(tmpl): | |||
|
61 | tmpl = open(tmpl).read() | |||
|
62 | ||||
|
63 | res = itplns(tmpl, ip.user_ns) | |||
|
64 | toclip(res) | |||
|
65 | return res | |||
|
66 | ||||
|
67 | ip.to_user_ns('render') | |||
|
68 | No newline at end of file |
@@ -0,0 +1,38 | |||||
|
1 | """ Simple TCP socket server that executes statements in IPython instance. | |||
|
2 | ||||
|
3 | Usage: | |||
|
4 | ||||
|
5 | import ipy_server | |||
|
6 | ipy_server.serve_thread(16455) | |||
|
7 | ||||
|
8 | Now, to execute the statements in this ipython instance, open a TCP socket | |||
|
9 | (port 16455), write out the statements, and close the socket. | |||
|
10 | You can use e.g. "telnet localhost 16455" or a script to do this. | |||
|
11 | ||||
|
12 | This is a bit like 'M-x server-start" or gnuserv in the emacs world. | |||
|
13 | ||||
|
14 | """ | |||
|
15 | ||||
|
16 | import IPython.ipapi | |||
|
17 | ip = IPython.ipapi.get() | |||
|
18 | ||||
|
19 | import SocketServer | |||
|
20 | ||||
|
21 | # user-accessible port | |||
|
22 | PORT = 8099 | |||
|
23 | ||||
|
24 | class IPythonRequestHandler(SocketServer.StreamRequestHandler): | |||
|
25 | def handle(self): | |||
|
26 | #print "connection from", self.client_address | |||
|
27 | inp = self.rfile.read().replace('\r\n','\n') | |||
|
28 | #print "Execute",inp | |||
|
29 | ip.runlines(inp) | |||
|
30 | ||||
|
31 | def serve(port = PORT): | |||
|
32 | server = SocketServer.TCPServer(("", port), IPythonRequestHandler) | |||
|
33 | print "ipy_server on TCP port", port | |||
|
34 | server.serve_forever() | |||
|
35 | ||||
|
36 | def serve_thread(port = PORT): | |||
|
37 | import thread | |||
|
38 | thread.start_new_thread(serve, (port,)) No newline at end of file |
@@ -0,0 +1,62 | |||||
|
1 | """ Advanced signal (e.g. ctrl+C) handling for IPython | |||
|
2 | ||||
|
3 | So far, this only ignores ctrl + C in IPython file a subprocess | |||
|
4 | is executing, to get closer to how a "proper" shell behaves. | |||
|
5 | ||||
|
6 | Other signal processing may be implemented later on. | |||
|
7 | ||||
|
8 | If _ip.options.verbose is true, show exit status if nonzero | |||
|
9 | ||||
|
10 | """ | |||
|
11 | ||||
|
12 | import signal,os,sys | |||
|
13 | import IPython.ipapi | |||
|
14 | import subprocess | |||
|
15 | ||||
|
16 | ip = IPython.ipapi.get() | |||
|
17 | ||||
|
18 | def new_ipsystem_posix(cmd): | |||
|
19 | """ ctrl+c ignoring replacement for system() command in iplib. | |||
|
20 | ||||
|
21 | Ignore ctrl + c in IPython process during the command execution. | |||
|
22 | The subprocess will still get the ctrl + c signal. | |||
|
23 | ||||
|
24 | posix implementation | |||
|
25 | """ | |||
|
26 | ||||
|
27 | p = subprocess.Popen(cmd, shell = True) | |||
|
28 | ||||
|
29 | old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN) | |||
|
30 | pid,status = os.waitpid(p.pid,0) | |||
|
31 | signal.signal(signal.SIGINT, old_handler) | |||
|
32 | if status and ip.options.verbose: | |||
|
33 | print "[exit status: %d]" % status | |||
|
34 | ||||
|
35 | def new_ipsystem_win32(cmd): | |||
|
36 | """ ctrl+c ignoring replacement for system() command in iplib. | |||
|
37 | ||||
|
38 | Ignore ctrl + c in IPython process during the command execution. | |||
|
39 | The subprocess will still get the ctrl + c signal. | |||
|
40 | ||||
|
41 | win32 implementation | |||
|
42 | """ | |||
|
43 | old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN) | |||
|
44 | status = os.system(cmd) | |||
|
45 | signal.signal(signal.SIGINT, old_handler) | |||
|
46 | if status and ip.options.verbose: | |||
|
47 | print "[exit status: %d]" % status | |||
|
48 | ||||
|
49 | ||||
|
50 | def init(): | |||
|
51 | o = ip.options | |||
|
52 | try: | |||
|
53 | o.verbose | |||
|
54 | except AttributeError: | |||
|
55 | o.allow_new_attr (True ) | |||
|
56 | o.verbose = 0 | |||
|
57 | ||||
|
58 | ip.IP.system = (sys.platform == 'win32' and new_ipsystem_win32 or | |||
|
59 | new_ipsystem_posix) | |||
|
60 | ||||
|
61 | init() | |||
|
62 | No newline at end of file |
@@ -0,0 +1,17 | |||||
|
1 | """ Install various IPython completers | |||
|
2 | ||||
|
3 | IPython extension that installs completers related to core ipython behaviour. | |||
|
4 | ||||
|
5 | The actual implementations are in Extensions/ipy_completers.py | |||
|
6 | ||||
|
7 | """ | |||
|
8 | import IPython.ipapi | |||
|
9 | ||||
|
10 | ip = IPython.ipapi.get() | |||
|
11 | ||||
|
12 | from ipy_completers import * | |||
|
13 | ||||
|
14 | ip.set_hook('complete_command', module_completer, str_key = 'import') | |||
|
15 | ip.set_hook('complete_command', module_completer, str_key = 'from') | |||
|
16 | ip.set_hook('complete_command', runlistpy, str_key = '%run') | |||
|
17 | ip.set_hook('complete_command', cd_completer, str_key = '%cd') |
@@ -0,0 +1,24 | |||||
|
1 | """ System wide configuration file for IPython. | |||
|
2 | ||||
|
3 | This will be imported by ipython for all users. | |||
|
4 | ||||
|
5 | After this ipy_user_conf.py is imported, user specific configuration | |||
|
6 | should reside there. | |||
|
7 | ||||
|
8 | """ | |||
|
9 | ||||
|
10 | import IPython.ipapi | |||
|
11 | ip = IPython.ipapi.get() | |||
|
12 | ||||
|
13 | # add system wide configuration information, import extensions etc. here. | |||
|
14 | # nothing here is essential | |||
|
15 | ||||
|
16 | import sys | |||
|
17 | ||||
|
18 | import ext_rescapture # var = !ls and var = %magic | |||
|
19 | import pspersistence # %store magic | |||
|
20 | import clearcmd # %clear | |||
|
21 | ||||
|
22 | import ipy_stock_completers | |||
|
23 | ||||
|
24 | ip.load('IPython.history') |
@@ -0,0 +1,184 | |||||
|
1 | """Traits-aware tab completion. | |||
|
2 | ||||
|
3 | This module provides a custom tab-completer that intelligently hides the names | |||
|
4 | that the enthought.traits library (http://code.enthought.com/traits) | |||
|
5 | automatically adds to all objects that inherit from its base HasTraits class. | |||
|
6 | ||||
|
7 | ||||
|
8 | Activation | |||
|
9 | ========== | |||
|
10 | ||||
|
11 | To use this, put in your ~/.ipython/ipy_user_conf.py file: | |||
|
12 | ||||
|
13 | from ipy_traits_completer import activate | |||
|
14 | activate([complete_threshold]) | |||
|
15 | ||||
|
16 | The optional complete_threshold argument is the minimal length of text you need | |||
|
17 | to type for tab-completion to list names that are automatically generated by | |||
|
18 | traits. The default value is 3. Note that at runtime, you can change this | |||
|
19 | value simply by doing: | |||
|
20 | ||||
|
21 | import ipy_traits_completer | |||
|
22 | ipy_traits_completer.COMPLETE_THRESHOLD = 4 | |||
|
23 | ||||
|
24 | ||||
|
25 | Usage | |||
|
26 | ===== | |||
|
27 | ||||
|
28 | The system works as follows. If t is an empty object that HasTraits, then | |||
|
29 | (assuming the threshold is at the default value of 3): | |||
|
30 | ||||
|
31 | In [7]: t.ed<TAB> | |||
|
32 | ||||
|
33 | doesn't show anything at all, but: | |||
|
34 | ||||
|
35 | In [7]: t.edi<TAB> | |||
|
36 | t.edit_traits t.editable_traits | |||
|
37 | ||||
|
38 | shows these two names that come from traits. This allows you to complete on | |||
|
39 | the traits-specific names by typing at least 3 letters from them (or whatever | |||
|
40 | you set your threshold to), but to otherwise not see them in normal completion. | |||
|
41 | ||||
|
42 | ||||
|
43 | Notes | |||
|
44 | ===== | |||
|
45 | ||||
|
46 | - This requires Python 2.4 to work (I use sets). I don't think anyone is | |||
|
47 | using traits with 2.3 anyway, so that's OK. | |||
|
48 | """ | |||
|
49 | ||||
|
50 | ############################################################################# | |||
|
51 | # External imports | |||
|
52 | from enthought.traits import api as T | |||
|
53 | ||||
|
54 | # IPython imports | |||
|
55 | from IPython.ipapi import TryNext, get as ipget | |||
|
56 | from IPython.genutils import dir2 | |||
|
57 | try: | |||
|
58 | set | |||
|
59 | except: | |||
|
60 | from sets import Set as set | |||
|
61 | ||||
|
62 | ############################################################################# | |||
|
63 | # Module constants | |||
|
64 | ||||
|
65 | # The completion threshold | |||
|
66 | # This is currently implemented as a module global, since this sytem isn't | |||
|
67 | # likely to be modified at runtime by multiple instances. If needed in the | |||
|
68 | # future, we can always make it local to the completer as a function attribute. | |||
|
69 | COMPLETE_THRESHOLD = 3 | |||
|
70 | ||||
|
71 | # Set of names that Traits automatically adds to ANY traits-inheriting object. | |||
|
72 | # These are the names we'll filter out. | |||
|
73 | TRAIT_NAMES = set( dir2(T.HasTraits()) ) - set( dir2(object()) ) | |||
|
74 | ||||
|
75 | ############################################################################# | |||
|
76 | # Code begins | |||
|
77 | ||||
|
78 | def trait_completer(self,event): | |||
|
79 | """A custom IPython tab-completer that is traits-aware. | |||
|
80 | ||||
|
81 | It tries to hide the internal traits attributes, and reveal them only when | |||
|
82 | it can reasonably guess that the user really is after one of them. | |||
|
83 | """ | |||
|
84 | ||||
|
85 | #print '\nevent is:',event # dbg | |||
|
86 | symbol_parts = event.symbol.split('.') | |||
|
87 | base = '.'.join(symbol_parts[:-1]) | |||
|
88 | #print 'base:',base # dbg | |||
|
89 | ||||
|
90 | oinfo = self._ofind(base) | |||
|
91 | if not oinfo['found']: | |||
|
92 | raise TryNext | |||
|
93 | ||||
|
94 | obj = oinfo['obj'] | |||
|
95 | # OK, we got the object. See if it's traits, else punt | |||
|
96 | if not isinstance(obj,T.HasTraits): | |||
|
97 | raise TryNext | |||
|
98 | ||||
|
99 | # it's a traits object, don't show the tr* attributes unless the completion | |||
|
100 | # begins with 'tr' | |||
|
101 | attrs = dir2(obj) | |||
|
102 | # Now, filter out the attributes that start with the user's request | |||
|
103 | attr_start = symbol_parts[-1] | |||
|
104 | if attr_start: | |||
|
105 | attrs = [a for a in attrs if a.startswith(attr_start)] | |||
|
106 | ||||
|
107 | # Let's also respect the user's readline_omit__names setting: | |||
|
108 | omit__names = ipget().options.readline_omit__names | |||
|
109 | if omit__names == 1: | |||
|
110 | attrs = [a for a in attrs if not a.startswith('__')] | |||
|
111 | elif omit__names == 2: | |||
|
112 | attrs = [a for a in attrs if not a.startswith('_')] | |||
|
113 | ||||
|
114 | #print '\nastart:<%r>' % attr_start # dbg | |||
|
115 | ||||
|
116 | if len(attr_start)<COMPLETE_THRESHOLD: | |||
|
117 | attrs = list(set(attrs) - TRAIT_NAMES) | |||
|
118 | ||||
|
119 | # The base of the completion, so we can form the final results list | |||
|
120 | bdot = base+'.' | |||
|
121 | ||||
|
122 | tcomp = [bdot+a for a in attrs] | |||
|
123 | #print 'tcomp:',tcomp | |||
|
124 | return tcomp | |||
|
125 | ||||
|
126 | def activate(complete_threshold = COMPLETE_THRESHOLD): | |||
|
127 | """Activate the Traits completer. | |||
|
128 | ||||
|
129 | :Keywords: | |||
|
130 | complete_threshold : int | |||
|
131 | The minimum number of letters that a user must type in order to | |||
|
132 | activate completion of traits-private names.""" | |||
|
133 | ||||
|
134 | if not (isinstance(complete_threshold,int) and | |||
|
135 | complete_threshold>0): | |||
|
136 | e='complete_threshold must be a positive integer, not %r' % \ | |||
|
137 | complete_threshold | |||
|
138 | raise ValueError(e) | |||
|
139 | ||||
|
140 | # Set the module global | |||
|
141 | global COMPLETE_THRESHOLD | |||
|
142 | COMPLETE_THRESHOLD = complete_threshold | |||
|
143 | ||||
|
144 | # Activate the traits aware completer | |||
|
145 | ip = ipget() | |||
|
146 | ip.set_hook('complete_command', trait_completer, re_key = '.*') | |||
|
147 | ||||
|
148 | ||||
|
149 | ############################################################################# | |||
|
150 | if __name__ == '__main__': | |||
|
151 | # Testing/debugging | |||
|
152 | ||||
|
153 | # A sorted list of the names we'll filter out | |||
|
154 | TNL = list(TRAIT_NAMES) | |||
|
155 | TNL.sort() | |||
|
156 | ||||
|
157 | # Make a few objects for testing | |||
|
158 | class TClean(T.HasTraits): pass | |||
|
159 | class Bunch(object): pass | |||
|
160 | # A clean traits object | |||
|
161 | t = TClean() | |||
|
162 | # A nested object containing t | |||
|
163 | f = Bunch() | |||
|
164 | f.t = t | |||
|
165 | # And a naked new-style object | |||
|
166 | o = object() | |||
|
167 | ||||
|
168 | ip = ipget().IP | |||
|
169 | ||||
|
170 | # A few simplistic tests | |||
|
171 | ||||
|
172 | # Reset the threshold to the default, in case the test is running inside an | |||
|
173 | # instance of ipython that changed it | |||
|
174 | import ipy_traits_completer | |||
|
175 | ipy_traits_completer.COMPLETE_THRESHOLD = 3 | |||
|
176 | ||||
|
177 | assert ip.complete('t.ed') ==[] | |||
|
178 | ||||
|
179 | # For some bizarre reason, these fail on the first time I run them, but not | |||
|
180 | # afterwards. Traits does some really weird stuff at object instantiation | |||
|
181 | # time... | |||
|
182 | ta = ip.complete('t.edi') | |||
|
183 | assert ta == ['t.edit_traits', 't.editable_traits'] | |||
|
184 | print 'Tests OK' |
@@ -0,0 +1,76 | |||||
|
1 | r""" %which magic command | |||
|
2 | ||||
|
3 | %which <cmd> => search PATH for files matching PATH. Also scans aliases | |||
|
4 | ||||
|
5 | """ | |||
|
6 | ||||
|
7 | import IPython.ipapi | |||
|
8 | ip = IPython.ipapi.get() | |||
|
9 | ||||
|
10 | import os,sys | |||
|
11 | from fnmatch import fnmatch | |||
|
12 | def which(fname): | |||
|
13 | fullpath = filter(os.path.isdir,os.environ['PATH'].split(os.pathsep)) | |||
|
14 | ||||
|
15 | if '.' not in fullpath: | |||
|
16 | fullpath = ['.'] + fullpath | |||
|
17 | fn = fname | |||
|
18 | for p in fullpath: | |||
|
19 | for f in os.listdir(p): | |||
|
20 | head, ext = os.path.splitext(f) | |||
|
21 | if f == fn or fnmatch(head, fn): | |||
|
22 | yield os.path.join(p,f) | |||
|
23 | return | |||
|
24 | ||||
|
25 | def which_alias(fname): | |||
|
26 | for al, tgt in ip.IP.alias_table.items(): | |||
|
27 | if not (al == fname or fnmatch(al, fname)): | |||
|
28 | continue | |||
|
29 | if callable(tgt): | |||
|
30 | print "Callable alias",tgt | |||
|
31 | d = tgt.__doc__ | |||
|
32 | if d: | |||
|
33 | print "Docstring:\n",d | |||
|
34 | continue | |||
|
35 | trg = tgt[1] | |||
|
36 | ||||
|
37 | trans = ip.expand_alias(trg) | |||
|
38 | cmd = trans.split(None,1)[0] | |||
|
39 | print al,"->",trans | |||
|
40 | for realcmd in which(cmd): | |||
|
41 | print " ==",realcmd | |||
|
42 | ||||
|
43 | def which_f(self, arg): | |||
|
44 | r""" %which <cmd> => search PATH for files matching cmd. Also scans aliases. | |||
|
45 | ||||
|
46 | Traverses PATH and prints all files (not just executables!) that match the | |||
|
47 | pattern on command line. Probably more useful in finding stuff | |||
|
48 | interactively than 'which', which only prints the first matching item. | |||
|
49 | ||||
|
50 | Also discovers and expands aliases, so you'll see what will be executed | |||
|
51 | when you call an alias. | |||
|
52 | ||||
|
53 | Example: | |||
|
54 | ||||
|
55 | [~]|62> %which d | |||
|
56 | d -> ls -F --color=auto | |||
|
57 | == c:\cygwin\bin\ls.exe | |||
|
58 | c:\cygwin\bin\d.exe | |||
|
59 | ||||
|
60 | [~]|64> %which diff* | |||
|
61 | diff3 -> diff3 | |||
|
62 | == c:\cygwin\bin\diff3.exe | |||
|
63 | diff -> diff | |||
|
64 | == c:\cygwin\bin\diff.exe | |||
|
65 | c:\cygwin\bin\diff.exe | |||
|
66 | c:\cygwin\bin\diff3.exe | |||
|
67 | ||||
|
68 | """ | |||
|
69 | ||||
|
70 | which_alias(arg) | |||
|
71 | ||||
|
72 | for e in which(arg): | |||
|
73 | print e | |||
|
74 | ||||
|
75 | ip.expose_magic("which",which_f) | |||
|
76 |
@@ -0,0 +1,43 | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | ||||
|
3 | import IPython.ipapi | |||
|
4 | ip = IPython.ipapi.get() | |||
|
5 | ||||
|
6 | import os, subprocess | |||
|
7 | ||||
|
8 | workdir = None | |||
|
9 | def workdir_f(ip,line): | |||
|
10 | """ Exceute commands residing in cwd elsewhere | |||
|
11 | ||||
|
12 | Example:: | |||
|
13 | ||||
|
14 | workdir /myfiles | |||
|
15 | cd bin | |||
|
16 | workdir myscript.py | |||
|
17 | ||||
|
18 | executes myscript.py (stored in bin, but not in path) in /myfiles | |||
|
19 | """ | |||
|
20 | global workdir | |||
|
21 | dummy,cmd = line.split(None,1) | |||
|
22 | if os.path.isdir(cmd): | |||
|
23 | workdir = os.path.abspath(cmd) | |||
|
24 | print "Set workdir",workdir | |||
|
25 | elif workdir is None: | |||
|
26 | print "Please set workdir first by doing e.g. 'workdir q:/'" | |||
|
27 | else: | |||
|
28 | sp = cmd.split(None,1) | |||
|
29 | if len(sp) == 1: | |||
|
30 | head, tail = cmd, '' | |||
|
31 | else: | |||
|
32 | head, tail = sp | |||
|
33 | if os.path.isfile(head): | |||
|
34 | cmd = os.path.abspath(head) + ' ' + tail | |||
|
35 | print "Execute command '" + cmd+ "' in",workdir | |||
|
36 | olddir = os.getcwd() | |||
|
37 | os.chdir(workdir) | |||
|
38 | try: | |||
|
39 | os.system(cmd) | |||
|
40 | finally: | |||
|
41 | os.chdir(olddir) | |||
|
42 | ||||
|
43 | ip.defalias("workdir",workdir_f) |
@@ -0,0 +1,162 | |||||
|
1 | """ Preliminary "job control" extensions for IPython | |||
|
2 | ||||
|
3 | requires python 2.4 (or separate 'subprocess' module | |||
|
4 | ||||
|
5 | This provides 2 features, launching background jobs and killing foreground jobs from another IPython instance. | |||
|
6 | ||||
|
7 | Launching background jobs: | |||
|
8 | ||||
|
9 | Usage: | |||
|
10 | ||||
|
11 | [ipython]|2> import jobctrl | |||
|
12 | [ipython]|3> &ls | |||
|
13 | <3> <jobctrl.IpyPopen object at 0x00D87FD0> | |||
|
14 | [ipython]|4> _3.go | |||
|
15 | -----------> _3.go() | |||
|
16 | ChangeLog | |||
|
17 | IPython | |||
|
18 | MANIFEST.in | |||
|
19 | README | |||
|
20 | README_Windows.txt | |||
|
21 | ||||
|
22 | ... | |||
|
23 | ||||
|
24 | Killing foreground tasks: | |||
|
25 | ||||
|
26 | Launch IPython instance, run a blocking command: | |||
|
27 | ||||
|
28 | [Q:/ipython]|1> import jobctrl | |||
|
29 | [Q:/ipython]|2> cat | |||
|
30 | ||||
|
31 | Now launch a new IPython prompt and kill the process: | |||
|
32 | ||||
|
33 | IPython 0.8.3.svn.r2919 [on Py 2.5] | |||
|
34 | [Q:/ipython]|1> import jobctrl | |||
|
35 | [Q:/ipython]|2> %tasks | |||
|
36 | 6020: 'cat ' (Q:\ipython) | |||
|
37 | [Q:/ipython]|3> %kill | |||
|
38 | SUCCESS: The process with PID 6020 has been terminated. | |||
|
39 | [Q:/ipython]|4> | |||
|
40 | ||||
|
41 | (you don't need to specify PID for %kill if only one task is running) | |||
|
42 | """ | |||
|
43 | ||||
|
44 | from subprocess import Popen,PIPE | |||
|
45 | import os,shlex,sys,time | |||
|
46 | ||||
|
47 | from IPython import genutils | |||
|
48 | ||||
|
49 | import IPython.ipapi | |||
|
50 | ||||
|
51 | if os.name == 'nt': | |||
|
52 | def kill_process(pid): | |||
|
53 | os.system('taskkill /F /PID %d' % pid) | |||
|
54 | else: | |||
|
55 | def kill_process(pid): | |||
|
56 | os.system('kill -9 %d' % pid) | |||
|
57 | ||||
|
58 | ||||
|
59 | ||||
|
60 | class IpyPopen(Popen): | |||
|
61 | def go(self): | |||
|
62 | print self.communicate()[0] | |||
|
63 | def __repr__(self): | |||
|
64 | return '<IPython job "%s" PID=%d>' % (self.line, self.pid) | |||
|
65 | ||||
|
66 | def kill(self): | |||
|
67 | kill_process(self.pid) | |||
|
68 | ||||
|
69 | def startjob(job): | |||
|
70 | p = IpyPopen(shlex.split(job), stdout=PIPE, shell = False) | |||
|
71 | p.line = job | |||
|
72 | return p | |||
|
73 | ||||
|
74 | def jobctrl_prefilter_f(self,line): | |||
|
75 | if line.startswith('&'): | |||
|
76 | pre,fn,rest = self.split_user_input(line[1:]) | |||
|
77 | ||||
|
78 | line = ip.IP.expand_aliases(fn,rest) | |||
|
79 | return '_ip.startjob(%s)' % genutils.make_quoted_expr(line) | |||
|
80 | ||||
|
81 | raise IPython.ipapi.TryNext | |||
|
82 | ||||
|
83 | ||||
|
84 | def job_list(ip): | |||
|
85 | keys = ip.db.keys('tasks/*') | |||
|
86 | ents = [ip.db[k] for k in keys] | |||
|
87 | return ents | |||
|
88 | ||||
|
89 | def magic_tasks(self,line): | |||
|
90 | """ Show a list of tasks. | |||
|
91 | ||||
|
92 | A 'task' is a process that has been started in IPython when 'jobctrl' extension is enabled. | |||
|
93 | Tasks can be killed with %kill. | |||
|
94 | """ | |||
|
95 | ip = self.getapi() | |||
|
96 | ents = job_list(ip) | |||
|
97 | if not ents: | |||
|
98 | print "No tasks running" | |||
|
99 | for pid,cmd,cwd,t in ents: | |||
|
100 | dur = int(time.time()-t) | |||
|
101 | print "%d: '%s' (%s) %d:%02d" % (pid,cmd,cwd, dur / 60,dur%60) | |||
|
102 | ||||
|
103 | def magic_kill(self,line): | |||
|
104 | """ Kill a task | |||
|
105 | ||||
|
106 | Without args, either kill one task (if only one running) or show list (if many) | |||
|
107 | With arg, assume it's the process id. | |||
|
108 | ||||
|
109 | %kill is typically (much) more powerful than trying to terminate a process with ctrl+C. | |||
|
110 | """ | |||
|
111 | ip = self.getapi() | |||
|
112 | jobs = job_list(ip) | |||
|
113 | ||||
|
114 | if not line.strip(): | |||
|
115 | if len(jobs) == 1: | |||
|
116 | kill_process(jobs[0][0]) | |||
|
117 | else: | |||
|
118 | magic_tasks(self,line) | |||
|
119 | return | |||
|
120 | ||||
|
121 | try: | |||
|
122 | pid = int(line) | |||
|
123 | kill_process(pid) | |||
|
124 | except ValueError: | |||
|
125 | magic_tasks(self,line) | |||
|
126 | ||||
|
127 | if sys.platform == 'win32': | |||
|
128 | shell_internal_commands = 'break chcp cls copy ctty date del erase dir md mkdir path prompt rd rmdir time type ver vol'.split() | |||
|
129 | else: | |||
|
130 | # todo linux commands | |||
|
131 | shell_internal_commands = [] | |||
|
132 | ||||
|
133 | ||||
|
134 | def jobctrl_shellcmd(ip,cmd): | |||
|
135 | """ os.system replacement that stores process info to db['tasks/t1234'] """ | |||
|
136 | cmdname = cmd.split(None,1)[0] | |||
|
137 | if cmdname in shell_internal_commands: | |||
|
138 | use_shell = True | |||
|
139 | else: | |||
|
140 | use_shell = False | |||
|
141 | ||||
|
142 | p = Popen(cmd,shell = use_shell) | |||
|
143 | jobentry = 'tasks/t' + str(p.pid) | |||
|
144 | ||||
|
145 | try: | |||
|
146 | ip.db[jobentry] = (p.pid,cmd,os.getcwd(),time.time()) | |||
|
147 | p.communicate() | |||
|
148 | finally: | |||
|
149 | del ip.db[jobentry] | |||
|
150 | ||||
|
151 | ||||
|
152 | def install(): | |||
|
153 | global ip | |||
|
154 | ip = IPython.ipapi.get() | |||
|
155 | # needed to make startjob visible as _ip.startjob('blah') | |||
|
156 | ip.startjob = startjob | |||
|
157 | ip.set_hook('input_prefilter', jobctrl_prefilter_f) | |||
|
158 | ip.set_hook('shell_hook', jobctrl_shellcmd) | |||
|
159 | ip.expose_magic('kill',magic_kill) | |||
|
160 | ip.expose_magic('tasks',magic_tasks) | |||
|
161 | ||||
|
162 | install() |
@@ -0,0 +1,98 | |||||
|
1 | """ Fun magic line editor for ipython | |||
|
2 | ||||
|
3 | Use this to easily edit lists of strings gradually without crafting long | |||
|
4 | list comprehensions. | |||
|
5 | ||||
|
6 | 'l' is the magic variable name for every line (array element). Save the current | |||
|
7 | result (or more exactly, retrieve the last ipython computation result into | |||
|
8 | %led work area) by running '%led s'. Just run '%led' to show the current work | |||
|
9 | area data. | |||
|
10 | ||||
|
11 | Example use: | |||
|
12 | ||||
|
13 | [ipython]|25> setups = !ls *setup*.py | |||
|
14 | == | |||
|
15 | ['eggsetup.py', 'setup.py', 'setup_bdist_egg.py'] | |||
|
16 | [ipython]|26> setups | |||
|
17 | <26> ['eggsetup.py', 'setup.py', 'setup_bdist_egg.py'] | |||
|
18 | [ipython]|27> %led s | |||
|
19 | Data set from last result (_) | |||
|
20 | <27> ['eggsetup.py', 'setup.py', 'setup_bdist_egg.py'] | |||
|
21 | [ipython]|28> %led upper | |||
|
22 | cmd translated => l.upper() | |||
|
23 | <28> ['EGGSETUP.PY', 'SETUP.PY', 'SETUP_BDIST_EGG.PY'] | |||
|
24 | [ipython]|29> %led | |||
|
25 | Magic line editor (for lists of strings) | |||
|
26 | current data is: | |||
|
27 | ['eggsetup.py', 'setup.py', 'setup_bdist_egg.py'] | |||
|
28 | [ipython]|30> %led upper | |||
|
29 | cmd translated => l.upper() | |||
|
30 | <30> ['EGGSETUP.PY', 'SETUP.PY', 'SETUP_BDIST_EGG.PY'] | |||
|
31 | [ipython]|31> %led s | |||
|
32 | Data set from last result (_) | |||
|
33 | <31> ['EGGSETUP.PY', 'SETUP.PY', 'SETUP_BDIST_EGG.PY'] | |||
|
34 | [ipython]|32> %led "n:" + l | |||
|
35 | <32> ['n:EGGSETUP.PY', 'n:SETUP.PY', 'n:SETUP_BDIST_EGG.PY'] | |||
|
36 | [ipython]|33> %led s | |||
|
37 | Data set from last result (_) | |||
|
38 | <33> ['n:EGGSETUP.PY', 'n:SETUP.PY', 'n:SETUP_BDIST_EGG.PY'] | |||
|
39 | [ipython]|34> %led l. | |||
|
40 | l.__add__ l.__gt__ l.__reduce_ex__ l.endswith l.join l.rstrip | |||
|
41 | l.__class__ l.__hash__ l.__repr__ l.expandtabs l.ljust l.split | |||
|
42 | ||||
|
43 | ... (completions for string variable shown ) ... | |||
|
44 | ||||
|
45 | """ | |||
|
46 | import IPython.ipapi | |||
|
47 | import pprint | |||
|
48 | ip = IPython.ipapi.get() | |||
|
49 | ||||
|
50 | curdata = [] | |||
|
51 | ||||
|
52 | def line_edit_f(self, cmd ): | |||
|
53 | global curdata | |||
|
54 | ||||
|
55 | if not cmd: | |||
|
56 | ||||
|
57 | print "Magic line editor (for lists of strings)" | |||
|
58 | if curdata: | |||
|
59 | print "current data is:" | |||
|
60 | pprint.pprint(curdata) | |||
|
61 | else: | |||
|
62 | print "No current data, you should set it by running '%led s'" | |||
|
63 | print "When you have your data in _ (result of last computation)." | |||
|
64 | return | |||
|
65 | ||||
|
66 | if cmd == 's': | |||
|
67 | curdata = ip.ev('_') | |||
|
68 | print "Data set from last result (_)" | |||
|
69 | newlines = curdata | |||
|
70 | ||||
|
71 | else: | |||
|
72 | # simple method call, e.g. upper | |||
|
73 | if cmd.isalpha(): | |||
|
74 | cmd = 'l.' + cmd + '()' | |||
|
75 | print "cmd translated =>",cmd | |||
|
76 | ||||
|
77 | newlines = [] | |||
|
78 | for l in curdata: | |||
|
79 | try: | |||
|
80 | l2 = eval(cmd) | |||
|
81 | except Exception,e: | |||
|
82 | print "Dropping exception",e,"on line:",l | |||
|
83 | continue | |||
|
84 | newlines.append(l2) | |||
|
85 | ||||
|
86 | ||||
|
87 | return newlines | |||
|
88 | ||||
|
89 | def line_edit_complete_f(self,event): | |||
|
90 | """ Show all string methods in completions """ | |||
|
91 | if event.symbol.startswith('l.'): | |||
|
92 | return ['l.' + func for func in dir('')] | |||
|
93 | ||||
|
94 | return dir('') + ['l.' + func for func in dir('')] | |||
|
95 | ||||
|
96 | ip.set_hook('complete_command', line_edit_complete_f , str_key = '%led') | |||
|
97 | ||||
|
98 | ip.expose_magic('led', line_edit_f) No newline at end of file |
@@ -0,0 +1,43 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ | |||
|
3 | Extension for printing Numeric Arrays in flexible ways. | |||
|
4 | """ | |||
|
5 | ||||
|
6 | from Numeric import ArrayType | |||
|
7 | ||||
|
8 | def num_display(self,arg): | |||
|
9 | """Display method for printing which treats Numeric arrays specially. | |||
|
10 | """ | |||
|
11 | ||||
|
12 | # Non-numpy variables are printed using the system default | |||
|
13 | if type(arg) != ArrayType: | |||
|
14 | self._display(arg) | |||
|
15 | return | |||
|
16 | # Otherwise, we do work. | |||
|
17 | format = __IPYTHON__.runtime_rc.numarray_print_format | |||
|
18 | print 'NumPy array, format:',format | |||
|
19 | # Here is where all the printing logic needs to be implemented | |||
|
20 | print arg # nothing yet :) | |||
|
21 | ||||
|
22 | ||||
|
23 | def magic_format(self,parameter_s=''): | |||
|
24 | """Specifies format of numerical output. | |||
|
25 | ||||
|
26 | This command is similar to Ocave's format command. | |||
|
27 | """ | |||
|
28 | ||||
|
29 | valid_formats = ['long','short'] | |||
|
30 | ||||
|
31 | if parameter_s in valid_formats: | |||
|
32 | self.runtime_rc.numarray_print_format = parameter_s | |||
|
33 | print 'Numeric output format is now:',parameter_s | |||
|
34 | else: | |||
|
35 | print 'Invalid format:',parameter_s | |||
|
36 | print 'Valid formats:',valid_formats | |||
|
37 | ||||
|
38 | # setup default format | |||
|
39 | __IPYTHON__.runtime_rc.numarray_print_format = 'long' | |||
|
40 | ||||
|
41 | # Bind our new functions to the interpreter | |||
|
42 | __IPYTHON__.__class__.magic_format = magic_format | |||
|
43 | __IPYTHON__.hooks.display = num_display |
@@ -0,0 +1,359 | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | ||||
|
3 | """ PickleShare - a small 'shelve' like datastore with concurrency support | |||
|
4 | ||||
|
5 | Like shelve, a PickleShareDB object acts like a normal dictionary. Unlike | |||
|
6 | shelve, many processes can access the database simultaneously. Changing a | |||
|
7 | value in database is immediately visible to other processes accessing the | |||
|
8 | same database. | |||
|
9 | ||||
|
10 | Concurrency is possible because the values are stored in separate files. Hence | |||
|
11 | the "database" is a directory where *all* files are governed by PickleShare. | |||
|
12 | ||||
|
13 | Example usage:: | |||
|
14 | ||||
|
15 | from pickleshare import * | |||
|
16 | db = PickleShareDB('~/testpickleshare') | |||
|
17 | db.clear() | |||
|
18 | print "Should be empty:",db.items() | |||
|
19 | db['hello'] = 15 | |||
|
20 | db['aku ankka'] = [1,2,313] | |||
|
21 | db['paths/are/ok/key'] = [1,(5,46)] | |||
|
22 | print db.keys() | |||
|
23 | del db['aku ankka'] | |||
|
24 | ||||
|
25 | This module is certainly not ZODB, but can be used for low-load | |||
|
26 | (non-mission-critical) situations where tiny code size trumps the | |||
|
27 | advanced features of a "real" object database. | |||
|
28 | ||||
|
29 | Installation guide: easy_install pickleshare | |||
|
30 | ||||
|
31 | Author: Ville Vainio <vivainio@gmail.com> | |||
|
32 | License: MIT open source license. | |||
|
33 | ||||
|
34 | """ | |||
|
35 | ||||
|
36 | from IPython.external.path import path as Path | |||
|
37 | import os,stat,time | |||
|
38 | import cPickle as pickle | |||
|
39 | import UserDict | |||
|
40 | import warnings | |||
|
41 | import glob | |||
|
42 | ||||
|
43 | from sets import Set as set | |||
|
44 | ||||
|
45 | def gethashfile(key): | |||
|
46 | return ("%02x" % abs(hash(key) % 256))[-2:] | |||
|
47 | ||||
|
48 | _sentinel = object() | |||
|
49 | ||||
|
50 | class PickleShareDB(UserDict.DictMixin): | |||
|
51 | """ The main 'connection' object for PickleShare database """ | |||
|
52 | def __init__(self,root): | |||
|
53 | """ Return a db object that will manage the specied directory""" | |||
|
54 | self.root = Path(root).expanduser().abspath() | |||
|
55 | if not self.root.isdir(): | |||
|
56 | self.root.makedirs() | |||
|
57 | # cache has { 'key' : (obj, orig_mod_time) } | |||
|
58 | self.cache = {} | |||
|
59 | ||||
|
60 | ||||
|
61 | def __getitem__(self,key): | |||
|
62 | """ db['key'] reading """ | |||
|
63 | fil = self.root / key | |||
|
64 | try: | |||
|
65 | mtime = (fil.stat()[stat.ST_MTIME]) | |||
|
66 | except OSError: | |||
|
67 | raise KeyError(key) | |||
|
68 | ||||
|
69 | if fil in self.cache and mtime == self.cache[fil][1]: | |||
|
70 | return self.cache[fil][0] | |||
|
71 | try: | |||
|
72 | # The cached item has expired, need to read | |||
|
73 | obj = pickle.load(fil.open()) | |||
|
74 | except: | |||
|
75 | raise KeyError(key) | |||
|
76 | ||||
|
77 | self.cache[fil] = (obj,mtime) | |||
|
78 | return obj | |||
|
79 | ||||
|
80 | def __setitem__(self,key,value): | |||
|
81 | """ db['key'] = 5 """ | |||
|
82 | fil = self.root / key | |||
|
83 | parent = fil.parent | |||
|
84 | if parent and not parent.isdir(): | |||
|
85 | parent.makedirs() | |||
|
86 | pickled = pickle.dump(value,fil.open('w')) | |||
|
87 | try: | |||
|
88 | self.cache[fil] = (value,fil.mtime) | |||
|
89 | except OSError,e: | |||
|
90 | if e.errno != 2: | |||
|
91 | raise | |||
|
92 | ||||
|
93 | def hset(self, hashroot, key, value): | |||
|
94 | """ hashed set """ | |||
|
95 | hroot = self.root / hashroot | |||
|
96 | if not hroot.isdir(): | |||
|
97 | hroot.makedirs() | |||
|
98 | hfile = hroot / gethashfile(key) | |||
|
99 | d = self.get(hfile, {}) | |||
|
100 | d.update( {key : value}) | |||
|
101 | self[hfile] = d | |||
|
102 | ||||
|
103 | ||||
|
104 | ||||
|
105 | def hget(self, hashroot, key, default = _sentinel, fast_only = True): | |||
|
106 | """ hashed get """ | |||
|
107 | hroot = self.root / hashroot | |||
|
108 | hfile = hroot / gethashfile(key) | |||
|
109 | ||||
|
110 | d = self.get(hfile, _sentinel ) | |||
|
111 | #print "got dict",d,"from",hfile | |||
|
112 | if d is _sentinel: | |||
|
113 | if fast_only: | |||
|
114 | if default is _sentinel: | |||
|
115 | raise KeyError(key) | |||
|
116 | ||||
|
117 | return default | |||
|
118 | ||||
|
119 | # slow mode ok, works even after hcompress() | |||
|
120 | d = self.hdict(hashroot) | |||
|
121 | ||||
|
122 | return d.get(key, default) | |||
|
123 | ||||
|
124 | def hdict(self, hashroot): | |||
|
125 | """ Get all data contained in hashed category 'hashroot' as dict """ | |||
|
126 | hfiles = self.keys(hashroot + "/*") | |||
|
127 | hfiles.sort() | |||
|
128 | last = len(hfiles) and hfiles[-1] or '' | |||
|
129 | if last.endswith('xx'): | |||
|
130 | # print "using xx" | |||
|
131 | hfiles = [last] + hfiles[:-1] | |||
|
132 | ||||
|
133 | all = {} | |||
|
134 | ||||
|
135 | for f in hfiles: | |||
|
136 | # print "using",f | |||
|
137 | try: | |||
|
138 | all.update(self[f]) | |||
|
139 | except KeyError: | |||
|
140 | print "Corrupt",f,"deleted - hset is not threadsafe!" | |||
|
141 | del self[f] | |||
|
142 | ||||
|
143 | self.uncache(f) | |||
|
144 | ||||
|
145 | return all | |||
|
146 | ||||
|
147 | def hcompress(self, hashroot): | |||
|
148 | """ Compress category 'hashroot', so hset is fast again | |||
|
149 | ||||
|
150 | hget will fail if fast_only is True for compressed items (that were | |||
|
151 | hset before hcompress). | |||
|
152 | ||||
|
153 | """ | |||
|
154 | hfiles = self.keys(hashroot + "/*") | |||
|
155 | all = {} | |||
|
156 | for f in hfiles: | |||
|
157 | # print "using",f | |||
|
158 | all.update(self[f]) | |||
|
159 | self.uncache(f) | |||
|
160 | ||||
|
161 | self[hashroot + '/xx'] = all | |||
|
162 | for f in hfiles: | |||
|
163 | p = self.root / f | |||
|
164 | if p.basename() == 'xx': | |||
|
165 | continue | |||
|
166 | p.remove() | |||
|
167 | ||||
|
168 | ||||
|
169 | ||||
|
170 | def __delitem__(self,key): | |||
|
171 | """ del db["key"] """ | |||
|
172 | fil = self.root / key | |||
|
173 | self.cache.pop(fil,None) | |||
|
174 | try: | |||
|
175 | fil.remove() | |||
|
176 | except OSError: | |||
|
177 | # notfound and permission denied are ok - we | |||
|
178 | # lost, the other process wins the conflict | |||
|
179 | pass | |||
|
180 | ||||
|
181 | def _normalized(self, p): | |||
|
182 | """ Make a key suitable for user's eyes """ | |||
|
183 | return str(self.root.relpathto(p)).replace('\\','/') | |||
|
184 | ||||
|
185 | def keys(self, globpat = None): | |||
|
186 | """ All keys in DB, or all keys matching a glob""" | |||
|
187 | ||||
|
188 | if globpat is None: | |||
|
189 | files = self.root.walkfiles() | |||
|
190 | else: | |||
|
191 | files = [Path(p) for p in glob.glob(self.root/globpat)] | |||
|
192 | return [self._normalized(p) for p in files if p.isfile()] | |||
|
193 | ||||
|
194 | def uncache(self,*items): | |||
|
195 | """ Removes all, or specified items from cache | |||
|
196 | ||||
|
197 | Use this after reading a large amount of large objects | |||
|
198 | to free up memory, when you won't be needing the objects | |||
|
199 | for a while. | |||
|
200 | ||||
|
201 | """ | |||
|
202 | if not items: | |||
|
203 | self.cache = {} | |||
|
204 | for it in items: | |||
|
205 | self.cache.pop(it,None) | |||
|
206 | ||||
|
207 | def waitget(self,key, maxwaittime = 60 ): | |||
|
208 | """ Wait (poll) for a key to get a value | |||
|
209 | ||||
|
210 | Will wait for `maxwaittime` seconds before raising a KeyError. | |||
|
211 | The call exits normally if the `key` field in db gets a value | |||
|
212 | within the timeout period. | |||
|
213 | ||||
|
214 | Use this for synchronizing different processes or for ensuring | |||
|
215 | that an unfortunately timed "db['key'] = newvalue" operation | |||
|
216 | in another process (which causes all 'get' operation to cause a | |||
|
217 | KeyError for the duration of pickling) won't screw up your program | |||
|
218 | logic. | |||
|
219 | """ | |||
|
220 | ||||
|
221 | wtimes = [0.2] * 3 + [0.5] * 2 + [1] | |||
|
222 | tries = 0 | |||
|
223 | waited = 0 | |||
|
224 | while 1: | |||
|
225 | try: | |||
|
226 | val = self[key] | |||
|
227 | return val | |||
|
228 | except KeyError: | |||
|
229 | pass | |||
|
230 | ||||
|
231 | if waited > maxwaittime: | |||
|
232 | raise KeyError(key) | |||
|
233 | ||||
|
234 | time.sleep(wtimes[tries]) | |||
|
235 | waited+=wtimes[tries] | |||
|
236 | if tries < len(wtimes) -1: | |||
|
237 | tries+=1 | |||
|
238 | ||||
|
239 | def getlink(self,folder): | |||
|
240 | """ Get a convenient link for accessing items """ | |||
|
241 | return PickleShareLink(self, folder) | |||
|
242 | ||||
|
243 | def __repr__(self): | |||
|
244 | return "PickleShareDB('%s')" % self.root | |||
|
245 | ||||
|
246 | ||||
|
247 | ||||
|
248 | class PickleShareLink: | |||
|
249 | """ A shortdand for accessing nested PickleShare data conveniently. | |||
|
250 | ||||
|
251 | Created through PickleShareDB.getlink(), example:: | |||
|
252 | ||||
|
253 | lnk = db.getlink('myobjects/test') | |||
|
254 | lnk.foo = 2 | |||
|
255 | lnk.bar = lnk.foo + 5 | |||
|
256 | ||||
|
257 | """ | |||
|
258 | def __init__(self, db, keydir ): | |||
|
259 | self.__dict__.update(locals()) | |||
|
260 | ||||
|
261 | def __getattr__(self,key): | |||
|
262 | return self.__dict__['db'][self.__dict__['keydir']+'/' + key] | |||
|
263 | def __setattr__(self,key,val): | |||
|
264 | self.db[self.keydir+'/' + key] = val | |||
|
265 | def __repr__(self): | |||
|
266 | db = self.__dict__['db'] | |||
|
267 | keys = db.keys( self.__dict__['keydir'] +"/*") | |||
|
268 | return "<PickleShareLink '%s': %s>" % ( | |||
|
269 | self.__dict__['keydir'], | |||
|
270 | ";".join([Path(k).basename() for k in keys])) | |||
|
271 | ||||
|
272 | ||||
|
273 | def test(): | |||
|
274 | db = PickleShareDB('~/testpickleshare') | |||
|
275 | db.clear() | |||
|
276 | print "Should be empty:",db.items() | |||
|
277 | db['hello'] = 15 | |||
|
278 | db['aku ankka'] = [1,2,313] | |||
|
279 | db['paths/nest/ok/keyname'] = [1,(5,46)] | |||
|
280 | db.hset('hash', 'aku', 12) | |||
|
281 | db.hset('hash', 'ankka', 313) | |||
|
282 | print "12 =",db.hget('hash','aku') | |||
|
283 | print "313 =",db.hget('hash','ankka') | |||
|
284 | print "all hashed",db.hdict('hash') | |||
|
285 | print db.keys() | |||
|
286 | print db.keys('paths/nest/ok/k*') | |||
|
287 | print dict(db) # snapsot of whole db | |||
|
288 | db.uncache() # frees memory, causes re-reads later | |||
|
289 | ||||
|
290 | # shorthand for accessing deeply nested files | |||
|
291 | lnk = db.getlink('myobjects/test') | |||
|
292 | lnk.foo = 2 | |||
|
293 | lnk.bar = lnk.foo + 5 | |||
|
294 | print lnk.bar # 7 | |||
|
295 | ||||
|
296 | def stress(): | |||
|
297 | db = PickleShareDB('~/fsdbtest') | |||
|
298 | import time,sys | |||
|
299 | for i in range(1000): | |||
|
300 | for j in range(1000): | |||
|
301 | if i % 15 == 0 and i < 200: | |||
|
302 | if str(j) in db: | |||
|
303 | del db[str(j)] | |||
|
304 | continue | |||
|
305 | ||||
|
306 | if j%33 == 0: | |||
|
307 | time.sleep(0.02) | |||
|
308 | ||||
|
309 | db[str(j)] = db.get(str(j), []) + [(i,j,"proc %d" % os.getpid())] | |||
|
310 | db.hset('hash',j, db.hget('hash',j,15) + 1 ) | |||
|
311 | ||||
|
312 | print i, | |||
|
313 | sys.stdout.flush() | |||
|
314 | if i % 10 == 0: | |||
|
315 | db.uncache() | |||
|
316 | ||||
|
317 | def main(): | |||
|
318 | import textwrap | |||
|
319 | usage = textwrap.dedent("""\ | |||
|
320 | pickleshare - manage PickleShare databases | |||
|
321 | ||||
|
322 | Usage: | |||
|
323 | ||||
|
324 | pickleshare dump /path/to/db > dump.txt | |||
|
325 | pickleshare load /path/to/db < dump.txt | |||
|
326 | pickleshare test /path/to/db | |||
|
327 | """) | |||
|
328 | DB = PickleShareDB | |||
|
329 | import sys | |||
|
330 | if len(sys.argv) < 2: | |||
|
331 | print usage | |||
|
332 | return | |||
|
333 | ||||
|
334 | cmd = sys.argv[1] | |||
|
335 | args = sys.argv[2:] | |||
|
336 | if cmd == 'dump': | |||
|
337 | if not args: args= ['.'] | |||
|
338 | db = DB(args[0]) | |||
|
339 | import pprint | |||
|
340 | pprint.pprint(db.items()) | |||
|
341 | elif cmd == 'load': | |||
|
342 | cont = sys.stdin.read() | |||
|
343 | db = DB(args[0]) | |||
|
344 | data = eval(cont) | |||
|
345 | db.clear() | |||
|
346 | for k,v in db.items(): | |||
|
347 | db[k] = v | |||
|
348 | elif cmd == 'testwait': | |||
|
349 | db = DB(args[0]) | |||
|
350 | db.clear() | |||
|
351 | print db.waitget('250') | |||
|
352 | elif cmd == 'test': | |||
|
353 | test() | |||
|
354 | stress() | |||
|
355 | ||||
|
356 | if __name__== "__main__": | |||
|
357 | main() | |||
|
358 | ||||
|
359 |
@@ -0,0 +1,183 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ | |||
|
3 | %store magic for lightweight persistence. | |||
|
4 | ||||
|
5 | Stores variables, aliases etc. in PickleShare database. | |||
|
6 | ||||
|
7 | $Id: iplib.py 1107 2006-01-30 19:02:20Z vivainio $ | |||
|
8 | """ | |||
|
9 | ||||
|
10 | import IPython.ipapi | |||
|
11 | ip = IPython.ipapi.get() | |||
|
12 | ||||
|
13 | import pickleshare | |||
|
14 | ||||
|
15 | import inspect,pickle,os,sys,textwrap | |||
|
16 | from IPython.FakeModule import FakeModule | |||
|
17 | ||||
|
18 | def restore_aliases(self): | |||
|
19 | ip = self.getapi() | |||
|
20 | staliases = ip.db.get('stored_aliases', {}) | |||
|
21 | for k,v in staliases.items(): | |||
|
22 | #print "restore alias",k,v # dbg | |||
|
23 | #self.alias_table[k] = v | |||
|
24 | ip.defalias(k,v) | |||
|
25 | ||||
|
26 | ||||
|
27 | def refresh_variables(ip): | |||
|
28 | db = ip.db | |||
|
29 | for key in db.keys('autorestore/*'): | |||
|
30 | # strip autorestore | |||
|
31 | justkey = os.path.basename(key) | |||
|
32 | try: | |||
|
33 | obj = db[key] | |||
|
34 | except KeyError: | |||
|
35 | print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey | |||
|
36 | print "The error was:",sys.exc_info()[0] | |||
|
37 | else: | |||
|
38 | #print "restored",justkey,"=",obj #dbg | |||
|
39 | ip.user_ns[justkey] = obj | |||
|
40 | ||||
|
41 | ||||
|
42 | def restore_dhist(ip): | |||
|
43 | db = ip.db | |||
|
44 | ip.user_ns['_dh'] = db.get('dhist',[]) | |||
|
45 | ||||
|
46 | def restore_data(self): | |||
|
47 | ip = self.getapi() | |||
|
48 | refresh_variables(ip) | |||
|
49 | restore_aliases(self) | |||
|
50 | restore_dhist(self) | |||
|
51 | raise IPython.ipapi.TryNext | |||
|
52 | ||||
|
53 | ip.set_hook('late_startup_hook', restore_data) | |||
|
54 | ||||
|
55 | def magic_store(self, parameter_s=''): | |||
|
56 | """Lightweight persistence for python variables. | |||
|
57 | ||||
|
58 | Example: | |||
|
59 | ||||
|
60 | ville@badger[~]|1> A = ['hello',10,'world']\\ | |||
|
61 | ville@badger[~]|2> %store A\\ | |||
|
62 | ville@badger[~]|3> Exit | |||
|
63 | ||||
|
64 | (IPython session is closed and started again...) | |||
|
65 | ||||
|
66 | ville@badger:~$ ipython -p pysh\\ | |||
|
67 | ville@badger[~]|1> print A | |||
|
68 | ||||
|
69 | ['hello', 10, 'world'] | |||
|
70 | ||||
|
71 | Usage: | |||
|
72 | ||||
|
73 | %store - Show list of all variables and their current values\\ | |||
|
74 | %store <var> - Store the *current* value of the variable to disk\\ | |||
|
75 | %store -d <var> - Remove the variable and its value from storage\\ | |||
|
76 | %store -z - Remove all variables from storage\\ | |||
|
77 | %store -r - Refresh all variables from store (delete current vals)\\ | |||
|
78 | %store foo >a.txt - Store value of foo to new file a.txt\\ | |||
|
79 | %store foo >>a.txt - Append value of foo to file a.txt\\ | |||
|
80 | ||||
|
81 | It should be noted that if you change the value of a variable, you | |||
|
82 | need to %store it again if you want to persist the new value. | |||
|
83 | ||||
|
84 | Note also that the variables will need to be pickleable; most basic | |||
|
85 | python types can be safely %stored. | |||
|
86 | ||||
|
87 | Also aliases can be %store'd across sessions. | |||
|
88 | """ | |||
|
89 | ||||
|
90 | opts,argsl = self.parse_options(parameter_s,'drz',mode='string') | |||
|
91 | args = argsl.split(None,1) | |||
|
92 | ip = self.getapi() | |||
|
93 | db = ip.db | |||
|
94 | # delete | |||
|
95 | if opts.has_key('d'): | |||
|
96 | try: | |||
|
97 | todel = args[0] | |||
|
98 | except IndexError: | |||
|
99 | error('You must provide the variable to forget') | |||
|
100 | else: | |||
|
101 | try: | |||
|
102 | del db['autorestore/' + todel] | |||
|
103 | except: | |||
|
104 | error("Can't delete variable '%s'" % todel) | |||
|
105 | # reset | |||
|
106 | elif opts.has_key('z'): | |||
|
107 | for k in db.keys('autorestore/*'): | |||
|
108 | del db[k] | |||
|
109 | ||||
|
110 | elif opts.has_key('r'): | |||
|
111 | refresh_variables(ip) | |||
|
112 | ||||
|
113 | ||||
|
114 | # run without arguments -> list variables & values | |||
|
115 | elif not args: | |||
|
116 | vars = self.db.keys('autorestore/*') | |||
|
117 | vars.sort() | |||
|
118 | if vars: | |||
|
119 | size = max(map(len,vars)) | |||
|
120 | else: | |||
|
121 | size = 0 | |||
|
122 | ||||
|
123 | print 'Stored variables and their in-db values:' | |||
|
124 | fmt = '%-'+str(size)+'s -> %s' | |||
|
125 | get = db.get | |||
|
126 | for var in vars: | |||
|
127 | justkey = os.path.basename(var) | |||
|
128 | # print 30 first characters from every var | |||
|
129 | print fmt % (justkey,repr(get(var,'<unavailable>'))[:50]) | |||
|
130 | ||||
|
131 | # default action - store the variable | |||
|
132 | else: | |||
|
133 | # %store foo >file.txt or >>file.txt | |||
|
134 | if len(args) > 1 and args[1].startswith('>'): | |||
|
135 | fnam = os.path.expanduser(args[1].lstrip('>').lstrip()) | |||
|
136 | if args[1].startswith('>>'): | |||
|
137 | fil = open(fnam,'a') | |||
|
138 | else: | |||
|
139 | fil = open(fnam,'w') | |||
|
140 | obj = ip.ev(args[0]) | |||
|
141 | print "Writing '%s' (%s) to file '%s'." % (args[0], | |||
|
142 | obj.__class__.__name__, fnam) | |||
|
143 | ||||
|
144 | ||||
|
145 | if not isinstance (obj,basestring): | |||
|
146 | from pprint import pprint | |||
|
147 | pprint(obj,fil) | |||
|
148 | else: | |||
|
149 | fil.write(obj) | |||
|
150 | if not obj.endswith('\n'): | |||
|
151 | fil.write('\n') | |||
|
152 | ||||
|
153 | fil.close() | |||
|
154 | return | |||
|
155 | ||||
|
156 | # %store foo | |||
|
157 | try: | |||
|
158 | obj = ip.user_ns[args[0]] | |||
|
159 | except KeyError: | |||
|
160 | # it might be an alias | |||
|
161 | if args[0] in self.alias_table: | |||
|
162 | staliases = db.get('stored_aliases',{}) | |||
|
163 | staliases[ args[0] ] = self.alias_table[ args[0] ] | |||
|
164 | db['stored_aliases'] = staliases | |||
|
165 | print "Alias stored:", args[0], self.alias_table[ args[0] ] | |||
|
166 | return | |||
|
167 | else: | |||
|
168 | print "Error: unknown variable '%s'" % args[0] | |||
|
169 | ||||
|
170 | else: | |||
|
171 | if isinstance(inspect.getmodule(obj), FakeModule): | |||
|
172 | print textwrap.dedent("""\ | |||
|
173 | Warning:%s is %s | |||
|
174 | Proper storage of interactively declared classes (or instances | |||
|
175 | of those classes) is not possible! Only instances | |||
|
176 | of classes in real modules on file system can be %%store'd. | |||
|
177 | """ % (args[0], obj) ) | |||
|
178 | return | |||
|
179 | #pickled = pickle.dumps(obj) | |||
|
180 | self.db[ 'autorestore/' + args[0] ] = obj | |||
|
181 | print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__) | |||
|
182 | ||||
|
183 | ip.expose_magic('store',magic_store) |
@@ -0,0 +1,45 | |||||
|
1 | import IPython.ipapi | |||
|
2 | ||||
|
3 | ip = IPython.ipapi.get() | |||
|
4 | ||||
|
5 | def clip_f( self, parameter_s = '' ): | |||
|
6 | """Save a set of lines to the clipboard. | |||
|
7 | ||||
|
8 | Usage:\\ | |||
|
9 | %clip n1-n2 n3-n4 ... n5 .. n6 ... | |||
|
10 | ||||
|
11 | This function uses the same syntax as %macro for line extraction, but | |||
|
12 | instead of creating a macro it saves the resulting string to the | |||
|
13 | clipboard. | |||
|
14 | ||||
|
15 | When used without arguments, this returns the text contents of the clipboard. | |||
|
16 | E.g. | |||
|
17 | ||||
|
18 | mytext = %clip | |||
|
19 | ||||
|
20 | """ | |||
|
21 | ||||
|
22 | import win32clipboard as cl | |||
|
23 | import win32con | |||
|
24 | args = parameter_s.split() | |||
|
25 | cl.OpenClipboard() | |||
|
26 | if len( args ) == 0: | |||
|
27 | data = cl.GetClipboardData( win32con.CF_TEXT ) | |||
|
28 | cl.CloseClipboard() | |||
|
29 | return data | |||
|
30 | api = self.getapi() | |||
|
31 | ||||
|
32 | if parameter_s.lstrip().startswith('='): | |||
|
33 | rest = parameter_s[parameter_s.index('=')+1:].strip() | |||
|
34 | val = str(api.ev(rest)) | |||
|
35 | else: | |||
|
36 | ranges = args[0:] | |||
|
37 | val = ''.join( self.extract_input_slices( ranges ) ) | |||
|
38 | ||||
|
39 | cl.EmptyClipboard() | |||
|
40 | cl.SetClipboardText( val ) | |||
|
41 | cl.CloseClipboard() | |||
|
42 | print 'The following text was written to the clipboard' | |||
|
43 | print val | |||
|
44 | ||||
|
45 | ip.expose_magic( "clip", clip_f ) |
@@ -0,0 +1,43 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ | |||
|
3 | Class which mimics a module. | |||
|
4 | ||||
|
5 | Needed to allow pickle to correctly resolve namespaces during IPython | |||
|
6 | sessions. | |||
|
7 | ||||
|
8 | $Id: FakeModule.py 2754 2007-09-09 10:16:59Z fperez $""" | |||
|
9 | ||||
|
10 | #***************************************************************************** | |||
|
11 | # Copyright (C) 2002-2004 Fernando Perez. <fperez@colorado.edu> | |||
|
12 | # | |||
|
13 | # Distributed under the terms of the BSD License. The full license is in | |||
|
14 | # the file COPYING, distributed as part of this software. | |||
|
15 | #***************************************************************************** | |||
|
16 | ||||
|
17 | import types | |||
|
18 | ||||
|
19 | class FakeModule(types.ModuleType): | |||
|
20 | """Simple class with attribute access to fake a module. | |||
|
21 | ||||
|
22 | This is not meant to replace a module, but to allow inserting a fake | |||
|
23 | module in sys.modules so that systems which rely on run-time module | |||
|
24 | importing (like shelve and pickle) work correctly in interactive IPython | |||
|
25 | sessions. | |||
|
26 | ||||
|
27 | Do NOT use this code for anything other than this IPython private hack.""" | |||
|
28 | ||||
|
29 | def __init__(self,adict=None): | |||
|
30 | ||||
|
31 | # tmp to force __dict__ instance creation, else self.__dict__ fails | |||
|
32 | self.__iptmp = None | |||
|
33 | ||||
|
34 | # It seems pydoc (and perhaps others) needs any module instance to | |||
|
35 | # implement a __nonzero__ method, so we add it if missing: | |||
|
36 | self.__dict__.setdefault('__nonzero__',lambda : True) | |||
|
37 | self.__dict__.setdefault('__file__',__file__) | |||
|
38 | ||||
|
39 | # cleanup our temp trick | |||
|
40 | del self.__iptmp | |||
|
41 | ||||
|
42 | if adict is not None: | |||
|
43 | self.__dict__.update(adict) |
This diff has been collapsed as it changes many lines, (666 lines changed) Show them Hide them | |||||
@@ -0,0 +1,666 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """Improved replacement for the Gnuplot.Gnuplot class. | |||
|
3 | ||||
|
4 | This module imports Gnuplot and replaces some of its functionality with | |||
|
5 | improved versions. They add better handling of arrays for plotting and more | |||
|
6 | convenient PostScript generation, plus some fixes for hardcopy(). | |||
|
7 | ||||
|
8 | It also adds a convenient plot2 method for plotting dictionaries and | |||
|
9 | lists/tuples of arrays. | |||
|
10 | ||||
|
11 | This module is meant to be used as a drop-in replacement to the original | |||
|
12 | Gnuplot, so it should be safe to do: | |||
|
13 | ||||
|
14 | import IPython.Gnuplot2 as Gnuplot | |||
|
15 | ||||
|
16 | $Id: Gnuplot2.py 1210 2006-03-13 01:19:31Z fperez $""" | |||
|
17 | ||||
|
18 | import cStringIO | |||
|
19 | import os | |||
|
20 | import string | |||
|
21 | import sys | |||
|
22 | import tempfile | |||
|
23 | import time | |||
|
24 | import types | |||
|
25 | ||||
|
26 | import Gnuplot as Gnuplot_ori | |||
|
27 | import Numeric | |||
|
28 | ||||
|
29 | from IPython.genutils import popkey,xsys | |||
|
30 | ||||
|
31 | # needed by hardcopy(): | |||
|
32 | gp = Gnuplot_ori.gp | |||
|
33 | ||||
|
34 | # Patch for Gnuplot.py 1.6 compatibility. | |||
|
35 | # Thanks to Hayden Callow <h.callow@elec.canterbury.ac.nz> | |||
|
36 | try: | |||
|
37 | OptionException = Gnuplot_ori.PlotItems.OptionException | |||
|
38 | except AttributeError: | |||
|
39 | OptionException = Gnuplot_ori.Errors.OptionError | |||
|
40 | ||||
|
41 | # exhibit a similar interface to Gnuplot so it can be somewhat drop-in | |||
|
42 | Data = Gnuplot_ori.Data | |||
|
43 | Func = Gnuplot_ori.Func | |||
|
44 | GridData = Gnuplot_ori.GridData | |||
|
45 | PlotItem = Gnuplot_ori.PlotItem | |||
|
46 | PlotItems = Gnuplot_ori.PlotItems | |||
|
47 | ||||
|
48 | # Modify some of Gnuplot's functions with improved versions (or bugfixed, in | |||
|
49 | # hardcopy's case). In order to preserve the docstrings at runtime, I've | |||
|
50 | # copied them from the original code. | |||
|
51 | ||||
|
52 | # After some significant changes in v 1.7 of Gnuplot.py, we need to do a bit | |||
|
53 | # of version checking. | |||
|
54 | ||||
|
55 | if Gnuplot_ori.__version__ <= '1.6': | |||
|
56 | _BaseFileItem = PlotItems.File | |||
|
57 | _BaseTempFileItem = PlotItems.TempFile | |||
|
58 | ||||
|
59 | # Fix the File class to add the 'index' option for Gnuplot versions < 1.7 | |||
|
60 | class File(_BaseFileItem): | |||
|
61 | ||||
|
62 | _option_list = _BaseFileItem._option_list.copy() | |||
|
63 | _option_list.update({ | |||
|
64 | 'index' : lambda self, index: self.set_option_index(index), | |||
|
65 | }) | |||
|
66 | ||||
|
67 | # A new initializer is needed b/c we want to add a modified | |||
|
68 | # _option_sequence list which includes 'index' in the right place. | |||
|
69 | def __init__(self,*args,**kw): | |||
|
70 | self._option_sequence = ['binary', 'index', 'using', 'smooth', 'axes', | |||
|
71 | 'title', 'with'] | |||
|
72 | ||||
|
73 | _BaseFileItem.__init__(self,*args,**kw) | |||
|
74 | ||||
|
75 | # Let's fix the constructor docstring | |||
|
76 | __newdoc = \ | |||
|
77 | """Additional Keyword arguments added by IPython: | |||
|
78 | ||||
|
79 | 'index=<int>' -- similar to the `index` keyword in Gnuplot. | |||
|
80 | This allows only some of the datasets in a file to be | |||
|
81 | plotted. Datasets within a file are assumed to be separated | |||
|
82 | by _pairs_ of blank lines, and the first one is numbered as | |||
|
83 | 0 (similar to C/Python usage).""" | |||
|
84 | __init__.__doc__ = PlotItems.File.__init__.__doc__ + __newdoc | |||
|
85 | ||||
|
86 | def set_option_index(self, index): | |||
|
87 | if index is None: | |||
|
88 | self.clear_option('index') | |||
|
89 | elif type(index) in [type(''), type(1)]: | |||
|
90 | self._options['index'] = (index, 'index %s' % index) | |||
|
91 | elif type(index) is type(()): | |||
|
92 | self._options['index'] = (index,'index %s' % | |||
|
93 | string.join(map(repr, index), ':')) | |||
|
94 | else: | |||
|
95 | raise OptionException('index=%s' % (index,)) | |||
|
96 | ||||
|
97 | # We need a FileClass with a different name from 'File', which is a | |||
|
98 | # factory function in 1.7, so that our String class can subclass FileClass | |||
|
99 | # in any version. | |||
|
100 | _FileClass = File | |||
|
101 | ||||
|
102 | elif Gnuplot_ori.__version__ =='1.7': | |||
|
103 | _FileClass = _BaseFileItem = PlotItems._FileItem | |||
|
104 | _BaseTempFileItem = PlotItems._TempFileItem | |||
|
105 | File = PlotItems.File | |||
|
106 | ||||
|
107 | else: # changes in the newer version (svn as of March'06) | |||
|
108 | _FileClass = _BaseFileItem = PlotItems._FileItem | |||
|
109 | _BaseTempFileItem = PlotItems._NewFileItem | |||
|
110 | File = PlotItems.File | |||
|
111 | ||||
|
112 | ||||
|
113 | # Now, we can add our generic code which is version independent | |||
|
114 | ||||
|
115 | # First some useful utilities | |||
|
116 | def eps_fix_bbox(fname): | |||
|
117 | """Fix the bounding box of an eps file by running ps2eps on it. | |||
|
118 | ||||
|
119 | If its name ends in .eps, the original file is removed. | |||
|
120 | ||||
|
121 | This is particularly useful for plots made by Gnuplot with square aspect | |||
|
122 | ratio: there is a bug in Gnuplot which makes it generate a bounding box | |||
|
123 | which is far wider than the actual plot. | |||
|
124 | ||||
|
125 | This function assumes that ps2eps is installed in your system.""" | |||
|
126 | ||||
|
127 | # note: ps2ps and eps2eps do NOT work, ONLY ps2eps works correctly. The | |||
|
128 | # others make output with bitmapped fonts, which looks horrible. | |||
|
129 | print 'Fixing eps file: <%s>' % fname | |||
|
130 | xsys('ps2eps -f -q -l %s' % fname) | |||
|
131 | if fname.endswith('.eps'): | |||
|
132 | os.rename(fname+'.eps',fname) | |||
|
133 | ||||
|
134 | def is_list1d(x,containers = [types.ListType,types.TupleType]): | |||
|
135 | """Returns true if x appears to be a 1d list/tuple/array. | |||
|
136 | ||||
|
137 | The heuristics are: identify Numeric arrays, or lists/tuples whose first | |||
|
138 | element is not itself a list/tuple. This way zipped lists should work like | |||
|
139 | the original Gnuplot. There's no inexpensive way to know if a list doesn't | |||
|
140 | have a composite object after its first element, so that kind of input | |||
|
141 | will produce an error. But it should work well in most cases. | |||
|
142 | """ | |||
|
143 | x_type = type(x) | |||
|
144 | ||||
|
145 | return x_type == Numeric.ArrayType and len(x.shape)==1 or \ | |||
|
146 | (x_type in containers and | |||
|
147 | type(x[0]) not in containers + [Numeric.ArrayType]) | |||
|
148 | ||||
|
149 | def zip_items(items,titles=None): | |||
|
150 | """zip together neighboring 1-d arrays, and zip standalone ones | |||
|
151 | with their index. Leave other plot items alone.""" | |||
|
152 | ||||
|
153 | class StandaloneItem(Exception): pass | |||
|
154 | ||||
|
155 | def get_titles(titles): | |||
|
156 | """Return the next title and the input titles array. | |||
|
157 | ||||
|
158 | The input array may be changed to None when no titles are left to | |||
|
159 | prevent extra unnecessary calls to this function.""" | |||
|
160 | ||||
|
161 | try: | |||
|
162 | title = titles[tit_ct[0]] # tit_ct[0] is in zip_items'scope | |||
|
163 | except IndexError: | |||
|
164 | titles = None # so we don't enter again | |||
|
165 | title = None | |||
|
166 | else: | |||
|
167 | tit_ct[0] += 1 | |||
|
168 | return title,titles | |||
|
169 | ||||
|
170 | new_items = [] | |||
|
171 | ||||
|
172 | if titles: | |||
|
173 | # Initialize counter. It was put in a list as a hack to allow the | |||
|
174 | # nested get_titles to modify it without raising a NameError. | |||
|
175 | tit_ct = [0] | |||
|
176 | ||||
|
177 | n = 0 # this loop needs to be done by hand | |||
|
178 | while n < len(items): | |||
|
179 | item = items[n] | |||
|
180 | try: | |||
|
181 | if is_list1d(item): | |||
|
182 | if n==len(items)-1: # last in list | |||
|
183 | raise StandaloneItem | |||
|
184 | else: # check the next item and zip together if needed | |||
|
185 | next_item = items[n+1] | |||
|
186 | if next_item is None: | |||
|
187 | n += 1 | |||
|
188 | raise StandaloneItem | |||
|
189 | elif is_list1d(next_item): | |||
|
190 | # this would be best done with an iterator | |||
|
191 | if titles: | |||
|
192 | title,titles = get_titles(titles) | |||
|
193 | else: | |||
|
194 | title = None | |||
|
195 | new_items.append(Data(zip(item,next_item), | |||
|
196 | title=title)) | |||
|
197 | n += 1 # avoid double-inclusion of next item | |||
|
198 | else: # can't zip with next, zip with own index list | |||
|
199 | raise StandaloneItem | |||
|
200 | else: # not 1-d array | |||
|
201 | new_items.append(item) | |||
|
202 | except StandaloneItem: | |||
|
203 | if titles: | |||
|
204 | title,titles = get_titles(titles) | |||
|
205 | else: | |||
|
206 | title = None | |||
|
207 | new_items.append(Data(zip(range(len(item)),item),title=title)) | |||
|
208 | except AttributeError: | |||
|
209 | new_items.append(item) | |||
|
210 | n+=1 | |||
|
211 | ||||
|
212 | return new_items | |||
|
213 | ||||
|
214 | # And some classes with enhanced functionality. | |||
|
215 | class String(_FileClass): | |||
|
216 | """Make a PlotItem from data in a string with the same format as a File. | |||
|
217 | ||||
|
218 | This allows writing data directly inside python scripts using the exact | |||
|
219 | same format and manipulation options which would be used for external | |||
|
220 | files.""" | |||
|
221 | ||||
|
222 | def __init__(self, data_str, **keyw): | |||
|
223 | """Construct a String object. | |||
|
224 | ||||
|
225 | <data_str> is a string formatted exactly like a valid Gnuplot data | |||
|
226 | file would be. All options from the File constructor are valid here. | |||
|
227 | ||||
|
228 | Warning: when used for interactive plotting in scripts which exit | |||
|
229 | immediately, you may get an error because the temporary file used to | |||
|
230 | hold the string data was deleted before Gnuplot had a chance to see | |||
|
231 | it. You can work around this problem by putting a raw_input() call at | |||
|
232 | the end of the script. | |||
|
233 | ||||
|
234 | This problem does not appear when generating PostScript output, only | |||
|
235 | with Gnuplot windows.""" | |||
|
236 | ||||
|
237 | self.tmpfile = _BaseTempFileItem() | |||
|
238 | tmpfile = file(self.tmpfile.filename,'w') | |||
|
239 | tmpfile.write(data_str) | |||
|
240 | _BaseFileItem.__init__(self,self.tmpfile,**keyw) | |||
|
241 | ||||
|
242 | ||||
|
243 | class Gnuplot(Gnuplot_ori.Gnuplot): | |||
|
244 | """Improved Gnuplot class. | |||
|
245 | ||||
|
246 | Enhancements: better plot,replot and hardcopy methods. New methods for | |||
|
247 | quick range setting. | |||
|
248 | """ | |||
|
249 | ||||
|
250 | def xrange(self,min='*',max='*'): | |||
|
251 | """Set xrange. If min/max is omitted, it is set to '*' (auto). | |||
|
252 | ||||
|
253 | Note that this is different from the regular Gnuplot behavior, where | |||
|
254 | an unspecified limit means no change. Here any unspecified limit is | |||
|
255 | set to autoscaling, allowing these functions to be used for full | |||
|
256 | autoscaling when called with no arguments. | |||
|
257 | ||||
|
258 | To preserve one limit's current value while changing the other, an | |||
|
259 | explicit '' argument must be given as the limit to be kept. | |||
|
260 | ||||
|
261 | Similar functions exist for [y{2}z{2}rtuv]range.""" | |||
|
262 | ||||
|
263 | self('set xrange [%s:%s]' % (min,max)) | |||
|
264 | ||||
|
265 | def yrange(self,min='*',max='*'): | |||
|
266 | self('set yrange [%s:%s]' % (min,max)) | |||
|
267 | ||||
|
268 | def zrange(self,min='*',max='*'): | |||
|
269 | self('set zrange [%s:%s]' % (min,max)) | |||
|
270 | ||||
|
271 | def x2range(self,min='*',max='*'): | |||
|
272 | self('set xrange [%s:%s]' % (min,max)) | |||
|
273 | ||||
|
274 | def y2range(self,min='*',max='*'): | |||
|
275 | self('set yrange [%s:%s]' % (min,max)) | |||
|
276 | ||||
|
277 | def z2range(self,min='*',max='*'): | |||
|
278 | self('set zrange [%s:%s]' % (min,max)) | |||
|
279 | ||||
|
280 | def rrange(self,min='*',max='*'): | |||
|
281 | self('set rrange [%s:%s]' % (min,max)) | |||
|
282 | ||||
|
283 | def trange(self,min='*',max='*'): | |||
|
284 | self('set trange [%s:%s]' % (min,max)) | |||
|
285 | ||||
|
286 | def urange(self,min='*',max='*'): | |||
|
287 | self('set urange [%s:%s]' % (min,max)) | |||
|
288 | ||||
|
289 | def vrange(self,min='*',max='*'): | |||
|
290 | self('set vrange [%s:%s]' % (min,max)) | |||
|
291 | ||||
|
292 | def set_ps(self,option): | |||
|
293 | """Set an option for the PostScript terminal and reset default term.""" | |||
|
294 | ||||
|
295 | self('set terminal postscript %s ' % option) | |||
|
296 | self('set terminal %s' % gp.GnuplotOpts.default_term) | |||
|
297 | ||||
|
298 | def __plot_ps(self, plot_method,*items, **keyw): | |||
|
299 | """Wrapper for plot/splot/replot, with processing of hardcopy options. | |||
|
300 | ||||
|
301 | For internal use only.""" | |||
|
302 | ||||
|
303 | # Filter out PostScript options which will crash the normal plot/replot | |||
|
304 | psargs = {'filename':None, | |||
|
305 | 'mode':None, | |||
|
306 | 'eps':None, | |||
|
307 | 'enhanced':None, | |||
|
308 | 'color':None, | |||
|
309 | 'solid':None, | |||
|
310 | 'duplexing':None, | |||
|
311 | 'fontname':None, | |||
|
312 | 'fontsize':None, | |||
|
313 | 'debug':0 } | |||
|
314 | ||||
|
315 | for k in psargs.keys(): | |||
|
316 | if keyw.has_key(k): | |||
|
317 | psargs[k] = keyw[k] | |||
|
318 | del keyw[k] | |||
|
319 | ||||
|
320 | # Filter out other options the original plot doesn't know | |||
|
321 | hardcopy = popkey(keyw,'hardcopy',psargs['filename'] is not None) | |||
|
322 | titles = popkey(keyw,'titles',0) | |||
|
323 | ||||
|
324 | # the filename keyword should control hardcopy generation, this is an | |||
|
325 | # override switch only which needs to be explicitly set to zero | |||
|
326 | if hardcopy: | |||
|
327 | if psargs['filename'] is None: | |||
|
328 | raise ValueError, \ | |||
|
329 | 'If you request hardcopy, you must give a filename.' | |||
|
330 | ||||
|
331 | # set null output so nothing goes to screen. hardcopy() restores output | |||
|
332 | self('set term dumb') | |||
|
333 | # I don't know how to prevent screen output in Windows | |||
|
334 | if os.name == 'posix': | |||
|
335 | self('set output "/dev/null"') | |||
|
336 | ||||
|
337 | new_items = zip_items(items,titles) | |||
|
338 | # plot_method is either plot or replot from the original Gnuplot class: | |||
|
339 | plot_method(self,*new_items,**keyw) | |||
|
340 | ||||
|
341 | # Do hardcopy if requested | |||
|
342 | if hardcopy: | |||
|
343 | if psargs['filename'].endswith('.eps'): | |||
|
344 | psargs['eps'] = 1 | |||
|
345 | self.hardcopy(**psargs) | |||
|
346 | ||||
|
347 | def plot(self, *items, **keyw): | |||
|
348 | """Draw a new plot. | |||
|
349 | ||||
|
350 | Clear the current plot and create a new 2-d plot containing | |||
|
351 | the specified items. Each arguments should be of the | |||
|
352 | following types: | |||
|
353 | ||||
|
354 | 'PlotItem' (e.g., 'Data', 'File', 'Func') -- This is the most | |||
|
355 | flexible way to call plot because the PlotItems can | |||
|
356 | contain suboptions. Moreover, PlotItems can be saved to | |||
|
357 | variables so that their lifetime is longer than one plot | |||
|
358 | command; thus they can be replotted with minimal overhead. | |||
|
359 | ||||
|
360 | 'string' (e.g., 'sin(x)') -- The string is interpreted as | |||
|
361 | 'Func(string)' (a function that is computed by gnuplot). | |||
|
362 | ||||
|
363 | Anything else -- The object, which should be convertible to an | |||
|
364 | array, is passed to the 'Data' constructor, and thus | |||
|
365 | plotted as data. If the conversion fails, an exception is | |||
|
366 | raised. | |||
|
367 | ||||
|
368 | ||||
|
369 | This is a modified version of plot(). Compared to the original in | |||
|
370 | Gnuplot.py, this version has several enhancements, listed below. | |||
|
371 | ||||
|
372 | ||||
|
373 | Modifications to the input arguments | |||
|
374 | ------------------------------------ | |||
|
375 | ||||
|
376 | (1-d array means Numeric array, list or tuple): | |||
|
377 | ||||
|
378 | (i) Any 1-d array which is NOT followed by another 1-d array, is | |||
|
379 | automatically zipped with range(len(array_1d)). Typing g.plot(y) will | |||
|
380 | plot y against its indices. | |||
|
381 | ||||
|
382 | (ii) If two 1-d arrays are contiguous in the argument list, they are | |||
|
383 | automatically zipped together. So g.plot(x,y) plots y vs. x, and | |||
|
384 | g.plot(x1,y1,x2,y2) plots y1 vs. x1 and y2 vs. x2. | |||
|
385 | ||||
|
386 | (iii) Any 1-d array which is followed by None is automatically zipped | |||
|
387 | with range(len(array_1d)). In this form, typing g.plot(y1,None,y2) | |||
|
388 | will plot both y1 and y2 against their respective indices (and NOT | |||
|
389 | versus one another). The None prevents zipping y1 and y2 together, and | |||
|
390 | since y2 is unpaired it is automatically zipped to its indices by (i) | |||
|
391 | ||||
|
392 | (iv) Any other arguments which don't match these cases are left alone and | |||
|
393 | passed to the code below. | |||
|
394 | ||||
|
395 | For lists or tuples, the heuristics used to determine whether they are | |||
|
396 | in fact 1-d is fairly simplistic: their first element is checked, and | |||
|
397 | if it is not a list or tuple itself, it is assumed that the whole | |||
|
398 | object is one-dimensional. | |||
|
399 | ||||
|
400 | An additional optional keyword 'titles' has been added: it must be a | |||
|
401 | list of strings to be used as labels for the individual plots which | |||
|
402 | are NOT PlotItem objects (since those objects carry their own labels | |||
|
403 | within). | |||
|
404 | ||||
|
405 | ||||
|
406 | PostScript generation | |||
|
407 | --------------------- | |||
|
408 | ||||
|
409 | This version of plot() also handles automatically the production of | |||
|
410 | PostScript output. The main options are (given as keyword arguments): | |||
|
411 | ||||
|
412 | - filename: a string, typically ending in .eps. If given, the plot is | |||
|
413 | sent to this file in PostScript format. | |||
|
414 | ||||
|
415 | - hardcopy: this can be set to 0 to override 'filename'. It does not | |||
|
416 | need to be given to produce PostScript, its purpose is to allow | |||
|
417 | switching PostScript output off globally in scripts without having to | |||
|
418 | manually change 'filename' values in multiple calls. | |||
|
419 | ||||
|
420 | All other keywords accepted by Gnuplot.hardcopy() are transparently | |||
|
421 | passed, and safely ignored if output is sent to the screen instead of | |||
|
422 | PostScript. | |||
|
423 | ||||
|
424 | For example: | |||
|
425 | ||||
|
426 | In [1]: x=frange(0,2*pi,npts=100) | |||
|
427 | ||||
|
428 | Generate a plot in file 'sin.eps': | |||
|
429 | ||||
|
430 | In [2]: plot(x,sin(x),filename = 'sin.eps') | |||
|
431 | ||||
|
432 | Plot to screen instead, without having to change the filename: | |||
|
433 | ||||
|
434 | In [3]: plot(x,sin(x),filename = 'sin.eps',hardcopy=0) | |||
|
435 | ||||
|
436 | Pass the 'color=0' option to hardcopy for monochrome output: | |||
|
437 | ||||
|
438 | In [4]: plot(x,sin(x),filename = 'sin.eps',color=0) | |||
|
439 | ||||
|
440 | PostScript generation through plot() is useful mainly for scripting | |||
|
441 | uses where you are not interested in interactive plotting. For | |||
|
442 | interactive use, the hardcopy() function is typically more convenient: | |||
|
443 | ||||
|
444 | In [5]: plot(x,sin(x)) | |||
|
445 | ||||
|
446 | In [6]: hardcopy('sin.eps') """ | |||
|
447 | ||||
|
448 | self.__plot_ps(Gnuplot_ori.Gnuplot.plot,*items,**keyw) | |||
|
449 | ||||
|
450 | def plot2(self,arg,**kw): | |||
|
451 | """Plot the entries of a dictionary or a list/tuple of arrays. | |||
|
452 | ||||
|
453 | This simple utility calls plot() with a list of Gnuplot.Data objects | |||
|
454 | constructed either from the values of the input dictionary, or the entries | |||
|
455 | in it if it is a tuple or list. Each item gets labeled with the key/index | |||
|
456 | in the Gnuplot legend. | |||
|
457 | ||||
|
458 | Each item is plotted by zipping it with a list of its indices. | |||
|
459 | ||||
|
460 | Any keywords are passed directly to plot().""" | |||
|
461 | ||||
|
462 | if hasattr(arg,'keys'): | |||
|
463 | keys = arg.keys() | |||
|
464 | keys.sort() | |||
|
465 | else: | |||
|
466 | keys = range(len(arg)) | |||
|
467 | ||||
|
468 | pitems = [Data(zip(range(len(arg[k])),arg[k]),title=`k`) for k in keys] | |||
|
469 | self.plot(*pitems,**kw) | |||
|
470 | ||||
|
471 | def splot(self, *items, **keyw): | |||
|
472 | """Draw a new three-dimensional plot. | |||
|
473 | ||||
|
474 | Clear the current plot and create a new 3-d plot containing | |||
|
475 | the specified items. Arguments can be of the following types: | |||
|
476 | ||||
|
477 | 'PlotItem' (e.g., 'Data', 'File', 'Func', 'GridData' ) -- This | |||
|
478 | is the most flexible way to call plot because the | |||
|
479 | PlotItems can contain suboptions. Moreover, PlotItems can | |||
|
480 | be saved to variables so that their lifetime is longer | |||
|
481 | than one plot command--thus they can be replotted with | |||
|
482 | minimal overhead. | |||
|
483 | ||||
|
484 | 'string' (e.g., 'sin(x*y)') -- The string is interpreted as a | |||
|
485 | 'Func()' (a function that is computed by gnuplot). | |||
|
486 | ||||
|
487 | Anything else -- The object is converted to a Data() item, and | |||
|
488 | thus plotted as data. Note that each data point should | |||
|
489 | normally have at least three values associated with it | |||
|
490 | (i.e., x, y, and z). If the conversion fails, an | |||
|
491 | exception is raised. | |||
|
492 | ||||
|
493 | This is a modified version of splot(). Compared to the original in | |||
|
494 | Gnuplot.py, this version has several enhancements, listed in the | |||
|
495 | plot() documentation. | |||
|
496 | """ | |||
|
497 | ||||
|
498 | self.__plot_ps(Gnuplot_ori.Gnuplot.splot,*items,**keyw) | |||
|
499 | ||||
|
500 | def replot(self, *items, **keyw): | |||
|
501 | """Replot the data, possibly adding new 'PlotItem's. | |||
|
502 | ||||
|
503 | Replot the existing graph, using the items in the current | |||
|
504 | itemlist. If arguments are specified, they are interpreted as | |||
|
505 | additional items to be plotted alongside the existing items on | |||
|
506 | the same graph. See 'plot' for details. | |||
|
507 | ||||
|
508 | If you want to replot to a postscript file, you MUST give the | |||
|
509 | 'filename' keyword argument in each call to replot. The Gnuplot python | |||
|
510 | interface has no way of knowing that your previous call to | |||
|
511 | Gnuplot.plot() was meant for PostScript output.""" | |||
|
512 | ||||
|
513 | self.__plot_ps(Gnuplot_ori.Gnuplot.replot,*items,**keyw) | |||
|
514 | ||||
|
515 | # The original hardcopy has a bug. See fix at the end. The rest of the code | |||
|
516 | # was lifted verbatim from the original, so that people using IPython get the | |||
|
517 | # benefits without having to manually patch Gnuplot.py | |||
|
518 | def hardcopy(self, filename=None, | |||
|
519 | mode=None, | |||
|
520 | eps=None, | |||
|
521 | enhanced=None, | |||
|
522 | color=None, | |||
|
523 | solid=None, | |||
|
524 | duplexing=None, | |||
|
525 | fontname=None, | |||
|
526 | fontsize=None, | |||
|
527 | debug = 0, | |||
|
528 | ): | |||
|
529 | """Create a hardcopy of the current plot. | |||
|
530 | ||||
|
531 | Create a postscript hardcopy of the current plot to the | |||
|
532 | default printer (if configured) or to the specified filename. | |||
|
533 | ||||
|
534 | Note that gnuplot remembers the postscript suboptions across | |||
|
535 | terminal changes. Therefore if you set, for example, color=1 | |||
|
536 | for one hardcopy then the next hardcopy will also be color | |||
|
537 | unless you explicitly choose color=0. Alternately you can | |||
|
538 | force all of the options to their defaults by setting | |||
|
539 | mode='default'. I consider this to be a bug in gnuplot. | |||
|
540 | ||||
|
541 | Keyword arguments: | |||
|
542 | ||||
|
543 | 'filename=<string>' -- if a filename is specified, save the | |||
|
544 | output in that file; otherwise print it immediately | |||
|
545 | using the 'default_lpr' configuration option. If the | |||
|
546 | filename ends in '.eps', EPS mode is automatically | |||
|
547 | selected (like manually specifying eps=1 or mode='eps'). | |||
|
548 | ||||
|
549 | 'mode=<string>' -- set the postscript submode ('landscape', | |||
|
550 | 'portrait', 'eps', or 'default'). The default is | |||
|
551 | to leave this option unspecified. | |||
|
552 | ||||
|
553 | 'eps=<bool>' -- shorthand for 'mode="eps"'; asks gnuplot to | |||
|
554 | generate encapsulated postscript. | |||
|
555 | ||||
|
556 | 'enhanced=<bool>' -- if set (the default), then generate | |||
|
557 | enhanced postscript, which allows extra features like | |||
|
558 | font-switching, superscripts, and subscripts in axis | |||
|
559 | labels. (Some old gnuplot versions do not support | |||
|
560 | enhanced postscript; if this is the case set | |||
|
561 | gp.GnuplotOpts.prefer_enhanced_postscript=None.) | |||
|
562 | ||||
|
563 | 'color=<bool>' -- if set, create a plot with color. Default | |||
|
564 | is to leave this option unchanged. | |||
|
565 | ||||
|
566 | 'solid=<bool>' -- if set, force lines to be solid (i.e., not | |||
|
567 | dashed). | |||
|
568 | ||||
|
569 | 'duplexing=<string>' -- set duplexing option ('defaultplex', | |||
|
570 | 'simplex', or 'duplex'). Only request double-sided | |||
|
571 | printing if your printer can handle it. Actually this | |||
|
572 | option is probably meaningless since hardcopy() can only | |||
|
573 | print a single plot at a time. | |||
|
574 | ||||
|
575 | 'fontname=<string>' -- set the default font to <string>, | |||
|
576 | which must be a valid postscript font. The default is | |||
|
577 | to leave this option unspecified. | |||
|
578 | ||||
|
579 | 'fontsize=<double>' -- set the default font size, in | |||
|
580 | postscript points. | |||
|
581 | ||||
|
582 | 'debug=<bool>' -- print extra debugging information (useful if | |||
|
583 | your PostScript files are misteriously not being created). | |||
|
584 | """ | |||
|
585 | ||||
|
586 | if filename is None: | |||
|
587 | assert gp.GnuplotOpts.default_lpr is not None, \ | |||
|
588 | OptionException('default_lpr is not set, so you can only ' | |||
|
589 | 'print to a file.') | |||
|
590 | filename = gp.GnuplotOpts.default_lpr | |||
|
591 | lpr_output = 1 | |||
|
592 | else: | |||
|
593 | if filename.endswith('.eps'): | |||
|
594 | eps = 1 | |||
|
595 | lpr_output = 0 | |||
|
596 | ||||
|
597 | # Be careful processing the options. If the user didn't | |||
|
598 | # request an option explicitly, do not specify it on the 'set | |||
|
599 | # terminal' line (don't even specify the default value for the | |||
|
600 | # option). This is to avoid confusing older versions of | |||
|
601 | # gnuplot that do not support all of these options. The | |||
|
602 | # exception is 'enhanced', which is just too useful to have to | |||
|
603 | # specify each time! | |||
|
604 | ||||
|
605 | setterm = ['set', 'terminal', 'postscript'] | |||
|
606 | if eps: | |||
|
607 | assert mode is None or mode=='eps', \ | |||
|
608 | OptionException('eps option and mode are incompatible') | |||
|
609 | setterm.append('eps') | |||
|
610 | else: | |||
|
611 | if mode is not None: | |||
|
612 | assert mode in ['landscape', 'portrait', 'eps', 'default'], \ | |||
|
613 | OptionException('illegal mode "%s"' % mode) | |||
|
614 | setterm.append(mode) | |||
|
615 | if enhanced is None: | |||
|
616 | enhanced = gp.GnuplotOpts.prefer_enhanced_postscript | |||
|
617 | if enhanced is not None: | |||
|
618 | if enhanced: setterm.append('enhanced') | |||
|
619 | else: setterm.append('noenhanced') | |||
|
620 | if color is not None: | |||
|
621 | if color: setterm.append('color') | |||
|
622 | else: setterm.append('monochrome') | |||
|
623 | if solid is not None: | |||
|
624 | if solid: setterm.append('solid') | |||
|
625 | else: setterm.append('dashed') | |||
|
626 | if duplexing is not None: | |||
|
627 | assert duplexing in ['defaultplex', 'simplex', 'duplex'], \ | |||
|
628 | OptionException('illegal duplexing mode "%s"' % duplexing) | |||
|
629 | setterm.append(duplexing) | |||
|
630 | if fontname is not None: | |||
|
631 | setterm.append('"%s"' % fontname) | |||
|
632 | if fontsize is not None: | |||
|
633 | setterm.append('%s' % fontsize) | |||
|
634 | ||||
|
635 | self(string.join(setterm)) | |||
|
636 | self.set_string('output', filename) | |||
|
637 | # replot the current figure (to the printer): | |||
|
638 | self.refresh() | |||
|
639 | ||||
|
640 | # fperez. Ugly kludge: often for some reason the file is NOT created | |||
|
641 | # and we must reissue the creation commands. I have no idea why! | |||
|
642 | if not lpr_output: | |||
|
643 | #print 'Hardcopy <%s>' % filename # dbg | |||
|
644 | maxtries = 20 | |||
|
645 | delay = 0.1 # delay (in seconds) between print attempts | |||
|
646 | for i in range(maxtries): | |||
|
647 | time.sleep(0.05) # safety, very small delay | |||
|
648 | if os.path.isfile(filename): | |||
|
649 | if debug: | |||
|
650 | print 'Hardcopy to file <%s> success at attempt #%s.' \ | |||
|
651 | % (filename,i+1) | |||
|
652 | break | |||
|
653 | time.sleep(delay) | |||
|
654 | # try again, issue all commands just in case | |||
|
655 | self(string.join(setterm)) | |||
|
656 | self.set_string('output', filename) | |||
|
657 | self.refresh() | |||
|
658 | if not os.path.isfile(filename): | |||
|
659 | print >> sys.stderr,'ERROR: Tried %s times and failed to '\ | |||
|
660 | 'create hardcopy file `%s`' % (maxtries,filename) | |||
|
661 | ||||
|
662 | # reset the terminal to its `default' setting: | |||
|
663 | self('set terminal %s' % gp.GnuplotOpts.default_term) | |||
|
664 | self.set_string('output') | |||
|
665 | ||||
|
666 | #********************** End of file <Gnuplot2.py> ************************ |
@@ -0,0 +1,148 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """Interactive functions and magic functions for Gnuplot usage. | |||
|
3 | ||||
|
4 | This requires the Gnuplot.py module for interfacing python with Gnuplot, which | |||
|
5 | can be downloaded from: | |||
|
6 | ||||
|
7 | http://gnuplot-py.sourceforge.net/ | |||
|
8 | ||||
|
9 | See gphelp() below for details on the services offered by this module. | |||
|
10 | ||||
|
11 | Inspired by a suggestion/request from Arnd Baecker. | |||
|
12 | ||||
|
13 | $Id: GnuplotInteractive.py 389 2004-10-09 07:59:30Z fperez $""" | |||
|
14 | ||||
|
15 | __all__ = ['Gnuplot','gp','gp_new','plot','plot2','splot','replot', | |||
|
16 | 'hardcopy','gpdata','gpfile','gpstring','gpfunc','gpgrid', | |||
|
17 | 'gphelp'] | |||
|
18 | ||||
|
19 | import IPython.GnuplotRuntime as GRun | |||
|
20 | from IPython.genutils import page,warn | |||
|
21 | ||||
|
22 | # Set global names for interactive use | |||
|
23 | Gnuplot = GRun.Gnuplot | |||
|
24 | gp_new = GRun.gp_new | |||
|
25 | gp = GRun.gp | |||
|
26 | plot = gp.plot | |||
|
27 | plot2 = gp.plot2 | |||
|
28 | splot = gp.splot | |||
|
29 | replot = gp.replot | |||
|
30 | hardcopy = gp.hardcopy | |||
|
31 | ||||
|
32 | # Accessors for the main plot object constructors: | |||
|
33 | gpdata = Gnuplot.Data | |||
|
34 | gpfile = Gnuplot.File | |||
|
35 | gpstring = Gnuplot.String | |||
|
36 | gpfunc = Gnuplot.Func | |||
|
37 | gpgrid = Gnuplot.GridData | |||
|
38 | ||||
|
39 | def gphelp(): | |||
|
40 | """Print information about the Gnuplot facilities in IPython.""" | |||
|
41 | ||||
|
42 | page(""" | |||
|
43 | IPython provides an interface to access the Gnuplot scientific plotting | |||
|
44 | system, in an environment similar to that of Mathematica or Matlab. | |||
|
45 | ||||
|
46 | New top-level global objects | |||
|
47 | ---------------------------- | |||
|
48 | ||||
|
49 | Please see their respective docstrings for further details. | |||
|
50 | ||||
|
51 | - gp: a running Gnuplot instance. You can access its methods as | |||
|
52 | gp.<method>. gp(`a string`) will execute the given string as if it had been | |||
|
53 | typed in an interactive gnuplot window. | |||
|
54 | ||||
|
55 | - plot, splot, replot and hardcopy: aliases to the methods of the same name in | |||
|
56 | the global running Gnuplot instance gp. These allow you to simply type: | |||
|
57 | ||||
|
58 | In [1]: plot(x,sin(x),title='Sin(x)') # assuming x is a Numeric array | |||
|
59 | ||||
|
60 | and obtain a plot of sin(x) vs x with the title 'Sin(x)'. | |||
|
61 | ||||
|
62 | - gp_new: a function which returns a new Gnuplot instance. This can be used to | |||
|
63 | have multiple Gnuplot instances running in your session to compare different | |||
|
64 | plots, each in a separate window. | |||
|
65 | ||||
|
66 | - Gnuplot: alias to the Gnuplot2 module, an improved drop-in replacement for | |||
|
67 | the original Gnuplot.py. Gnuplot2 needs Gnuplot but redefines several of its | |||
|
68 | functions with improved versions (Gnuplot2 comes with IPython). | |||
|
69 | ||||
|
70 | - gpdata, gpfile, gpstring, gpfunc, gpgrid: aliases to Gnuplot.Data, | |||
|
71 | Gnuplot.File, Gnuplot.String, Gnuplot.Func and Gnuplot.GridData | |||
|
72 | respectively. These functions create objects which can then be passed to the | |||
|
73 | plotting commands. See the Gnuplot.py documentation for details. | |||
|
74 | ||||
|
75 | Keep in mind that all commands passed to a Gnuplot instance are executed in | |||
|
76 | the Gnuplot namespace, where no Python variables exist. For example, for | |||
|
77 | plotting sin(x) vs x as above, typing | |||
|
78 | ||||
|
79 | In [2]: gp('plot x,sin(x)') | |||
|
80 | ||||
|
81 | would not work. Instead, you would get the plot of BOTH the functions 'x' and | |||
|
82 | 'sin(x)', since Gnuplot doesn't know about the 'x' Python array. The plot() | |||
|
83 | method lives in python and does know about these variables. | |||
|
84 | ||||
|
85 | ||||
|
86 | New magic functions | |||
|
87 | ------------------- | |||
|
88 | ||||
|
89 | %gpc: pass one command to Gnuplot and execute it or open a Gnuplot shell where | |||
|
90 | each line of input is executed. | |||
|
91 | ||||
|
92 | %gp_set_default: reset the value of IPython's global Gnuplot instance.""") | |||
|
93 | ||||
|
94 | # Code below is all for IPython use | |||
|
95 | # Define the magic functions for communicating with the above gnuplot instance. | |||
|
96 | def magic_gpc(self,parameter_s=''): | |||
|
97 | """Execute a gnuplot command or open a gnuplot shell. | |||
|
98 | ||||
|
99 | Usage (omit the % if automagic is on). There are two ways to use it: | |||
|
100 | ||||
|
101 | 1) %gpc 'command' -> passes 'command' directly to the gnuplot instance. | |||
|
102 | ||||
|
103 | 2) %gpc -> will open up a prompt (gnuplot>>>) which takes input like the | |||
|
104 | standard gnuplot interactive prompt. If you need to type a multi-line | |||
|
105 | command, use \\ at the end of each intermediate line. | |||
|
106 | ||||
|
107 | Upon exiting of the gnuplot sub-shell, you return to your IPython | |||
|
108 | session (the gnuplot sub-shell can be invoked as many times as needed). | |||
|
109 | """ | |||
|
110 | ||||
|
111 | if parameter_s.strip(): | |||
|
112 | self.shell.gnuplot(parameter_s) | |||
|
113 | else: | |||
|
114 | self.shell.gnuplot.interact() | |||
|
115 | ||||
|
116 | def magic_gp_set_default(self,parameter_s=''): | |||
|
117 | """Set the default gnuplot instance accessed by the %gp magic function. | |||
|
118 | ||||
|
119 | %gp_set_default name | |||
|
120 | ||||
|
121 | Call with the name of the new instance at the command line. If you want to | |||
|
122 | set this instance in your own code (using an embedded IPython, for | |||
|
123 | example), simply set the variable __IPYTHON__.gnuplot to your own gnuplot | |||
|
124 | instance object.""" | |||
|
125 | ||||
|
126 | gname = parameter_s.strip() | |||
|
127 | G = eval(gname,self.shell.user_ns) | |||
|
128 | self.shell.gnuplot = G | |||
|
129 | self.shell.user_ns.update({'plot':G.plot,'splot':G.splot,'plot2':G.plot2, | |||
|
130 | 'replot':G.replot,'hardcopy':G.hardcopy}) | |||
|
131 | ||||
|
132 | try: | |||
|
133 | __IPYTHON__ | |||
|
134 | except NameError: | |||
|
135 | pass | |||
|
136 | else: | |||
|
137 | # make the global Gnuplot instance known to IPython | |||
|
138 | __IPYTHON__.gnuplot = GRun.gp | |||
|
139 | __IPYTHON__.gnuplot.shell_first_time = 1 | |||
|
140 | ||||
|
141 | print """*** Type `gphelp` for help on the Gnuplot integration features.""" | |||
|
142 | ||||
|
143 | # Add the new magic functions to the class dict | |||
|
144 | from IPython.iplib import InteractiveShell | |||
|
145 | InteractiveShell.magic_gpc = magic_gpc | |||
|
146 | InteractiveShell.magic_gp_set_default = magic_gp_set_default | |||
|
147 | ||||
|
148 | #********************** End of file <GnuplotInteractive.py> ******************* |
@@ -0,0 +1,147 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """Basic Gnuplot functionality for inclusion in other code. | |||
|
3 | ||||
|
4 | This module creates a running Gnuplot instance called 'gp' and builds other | |||
|
5 | convenient globals for quick use in running scripts. It is intended to allow | |||
|
6 | you to script plotting tasks in Python with a minimum of effort. A typical | |||
|
7 | usage would be: | |||
|
8 | ||||
|
9 | import IPython.GnuplotRuntime as GP # or some other short name | |||
|
10 | GP.gp.plot(GP.File('your_data.dat')) | |||
|
11 | ||||
|
12 | ||||
|
13 | This module exposes the following objects: | |||
|
14 | ||||
|
15 | - gp: a running Gnuplot instance. You can access its methods as | |||
|
16 | gp.<method>. gp(`a string`) will execute the given string as if it had been | |||
|
17 | typed in an interactive gnuplot window. | |||
|
18 | ||||
|
19 | - gp_new: a function which returns a new Gnuplot instance. This can be used to | |||
|
20 | have multiple Gnuplot instances running in your session to compare different | |||
|
21 | plots. | |||
|
22 | ||||
|
23 | - Gnuplot: alias to the Gnuplot2 module, an improved drop-in replacement for | |||
|
24 | the original Gnuplot.py. Gnuplot2 needs Gnuplot but redefines several of its | |||
|
25 | functions with improved versions (Gnuplot2 comes with IPython). | |||
|
26 | ||||
|
27 | - Data: alias to Gnuplot.Data, makes a PlotItem from array data. | |||
|
28 | ||||
|
29 | - File: alias to Gnuplot.File, makes a PlotItem from a file. | |||
|
30 | ||||
|
31 | - String: alias to Gnuplot.String, makes a PlotItem from a string formatted | |||
|
32 | exactly like a file for Gnuplot.File would be. | |||
|
33 | ||||
|
34 | - Func: alias to Gnuplot.Func, makes a PlotItem from a function string. | |||
|
35 | ||||
|
36 | - GridData: alias to Gnuplot.GridData, makes a PlotItem from grid data. | |||
|
37 | ||||
|
38 | - pm3d_config: a string with Gnuplot commands to set up the pm3d mode for | |||
|
39 | surface plotting. You can activate it simply by calling gp(pm3d_config). | |||
|
40 | ||||
|
41 | - eps_fix_bbox: A Unix-only function to fix eps files with bad bounding boxes | |||
|
42 | (which Gnuplot generates when the plot size is set to square). | |||
|
43 | ||||
|
44 | This requires the Gnuplot.py module for interfacing Python with Gnuplot, which | |||
|
45 | can be downloaded from: | |||
|
46 | ||||
|
47 | http://gnuplot-py.sourceforge.net/ | |||
|
48 | ||||
|
49 | Inspired by a suggestion/request from Arnd Baecker. | |||
|
50 | ||||
|
51 | $Id: GnuplotRuntime.py 389 2004-10-09 07:59:30Z fperez $""" | |||
|
52 | ||||
|
53 | __all__ = ['Gnuplot','gp','gp_new','Data','File','Func','GridData', | |||
|
54 | 'pm3d_config','eps_fix_bbox'] | |||
|
55 | ||||
|
56 | import os,tempfile,sys | |||
|
57 | from IPython.genutils import getoutput | |||
|
58 | ||||
|
59 | #--------------------------------------------------------------------------- | |||
|
60 | # Notes on mouse support for Gnuplot.py | |||
|
61 | ||||
|
62 | # If you do not have a mouse-enabled gnuplot, set gnuplot_mouse to 0. If you | |||
|
63 | # use gnuplot, you should really grab a recent, mouse enabled copy. It is an | |||
|
64 | # extremely useful feature. Mouse support is official as of gnuplot 4.0, | |||
|
65 | # released in April 2004. | |||
|
66 | ||||
|
67 | # For the mouse features to work correctly, you MUST set your Gnuplot.py | |||
|
68 | # module to use temporary files instead of 'inline data' for data | |||
|
69 | # communication. Note that this is the default, so unless you've manually | |||
|
70 | # fiddled with it you should be ok. If you need to make changes, in the | |||
|
71 | # Gnuplot module directory, loook for the gp_unix.py file and make sure the | |||
|
72 | # prefer_inline_data variable is set to 0. If you set it to 1 Gnuplot.py will | |||
|
73 | # try to pass the data to gnuplot via standard input, which completely | |||
|
74 | # confuses the mouse control system (even though it may be a bit faster than | |||
|
75 | # using temp files). | |||
|
76 | ||||
|
77 | # As of Gnuplot.py v1.7, a new option was added to use FIFOs (pipes). This | |||
|
78 | # mechanism, while fast, also breaks the mouse system. You must therefore set | |||
|
79 | # the variable prefer_fifo_data to 0 in gp_unix.py. | |||
|
80 | ||||
|
81 | tmpname = tempfile.mktemp() | |||
|
82 | open(tmpname,'w').write('set mouse') | |||
|
83 | gnu_out = getoutput('gnuplot '+ tmpname) | |||
|
84 | os.unlink(tmpname) | |||
|
85 | if gnu_out: # Gnuplot won't print anything if it has mouse support | |||
|
86 | print "*** Your version of Gnuplot appears not to have mouse support." | |||
|
87 | gnuplot_mouse = 0 | |||
|
88 | else: | |||
|
89 | gnuplot_mouse = 1 | |||
|
90 | del tmpname,gnu_out | |||
|
91 | ||||
|
92 | # Default state for persistence of new gnuplot instances | |||
|
93 | if os.name in ['nt','dos'] or sys.platform == 'cygwin': | |||
|
94 | gnuplot_persist = 0 | |||
|
95 | else: | |||
|
96 | gnuplot_persist = 1 | |||
|
97 | ||||
|
98 | import IPython.Gnuplot2 as Gnuplot | |||
|
99 | ||||
|
100 | class NotGiven: pass | |||
|
101 | ||||
|
102 | def gp_new(mouse=NotGiven,persist=NotGiven): | |||
|
103 | """Return a new Gnuplot instance. | |||
|
104 | ||||
|
105 | The instance returned uses the improved methods defined in Gnuplot2. | |||
|
106 | ||||
|
107 | Options (boolean): | |||
|
108 | ||||
|
109 | - mouse: if unspecified, the module global gnuplot_mouse is used. | |||
|
110 | ||||
|
111 | - persist: if unspecified, the module global gnuplot_persist is used.""" | |||
|
112 | ||||
|
113 | if mouse is NotGiven: | |||
|
114 | mouse = gnuplot_mouse | |||
|
115 | if persist is NotGiven: | |||
|
116 | persist = gnuplot_persist | |||
|
117 | g = Gnuplot.Gnuplot(persist=persist) | |||
|
118 | if mouse: | |||
|
119 | g('set mouse') | |||
|
120 | return g | |||
|
121 | ||||
|
122 | # Global-level names. | |||
|
123 | ||||
|
124 | # A global Gnuplot instance for interactive use: | |||
|
125 | gp = gp_new() | |||
|
126 | ||||
|
127 | # Accessors for the main plot object constructors: | |||
|
128 | Data = Gnuplot.Data | |||
|
129 | File = Gnuplot.File | |||
|
130 | Func = Gnuplot.Func | |||
|
131 | String = Gnuplot.String | |||
|
132 | GridData = Gnuplot.GridData | |||
|
133 | ||||
|
134 | # A Unix-only function to fix eps files with bad bounding boxes (which Gnuplot | |||
|
135 | # generates when the plot size is set to square): | |||
|
136 | eps_fix_bbox = Gnuplot.eps_fix_bbox | |||
|
137 | ||||
|
138 | # String for configuring pm3d. Simply call g(pm3d_config) to execute it. pm3d | |||
|
139 | # is a very nice mode for plotting colormaps on surfaces. Modify the defaults | |||
|
140 | # below to suit your taste. | |||
|
141 | pm3d_config = """ | |||
|
142 | set pm3d solid | |||
|
143 | set hidden3d | |||
|
144 | unset surface | |||
|
145 | set isosamples 50 | |||
|
146 | """ | |||
|
147 | #******************** End of file <GnuplotRuntime.py> ****************** |
@@ -0,0 +1,284 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """String interpolation for Python (by Ka-Ping Yee, 14 Feb 2000). | |||
|
3 | ||||
|
4 | This module lets you quickly and conveniently interpolate values into | |||
|
5 | strings (in the flavour of Perl or Tcl, but with less extraneous | |||
|
6 | punctuation). You get a bit more power than in the other languages, | |||
|
7 | because this module allows subscripting, slicing, function calls, | |||
|
8 | attribute lookup, or arbitrary expressions. Variables and expressions | |||
|
9 | are evaluated in the namespace of the caller. | |||
|
10 | ||||
|
11 | The itpl() function returns the result of interpolating a string, and | |||
|
12 | printpl() prints out an interpolated string. Here are some examples: | |||
|
13 | ||||
|
14 | from Itpl import printpl | |||
|
15 | printpl("Here is a $string.") | |||
|
16 | printpl("Here is a $module.member.") | |||
|
17 | printpl("Here is an $object.member.") | |||
|
18 | printpl("Here is a $functioncall(with, arguments).") | |||
|
19 | printpl("Here is an ${arbitrary + expression}.") | |||
|
20 | printpl("Here is an $array[3] member.") | |||
|
21 | printpl("Here is a $dictionary['member'].") | |||
|
22 | ||||
|
23 | The filter() function filters a file object so that output through it | |||
|
24 | is interpolated. This lets you produce the illusion that Python knows | |||
|
25 | how to do interpolation: | |||
|
26 | ||||
|
27 | import Itpl | |||
|
28 | sys.stdout = Itpl.filter() | |||
|
29 | f = "fancy" | |||
|
30 | print "Isn't this $f?" | |||
|
31 | print "Standard output has been replaced with a $sys.stdout object." | |||
|
32 | sys.stdout = Itpl.unfilter() | |||
|
33 | print "Okay, back $to $normal." | |||
|
34 | ||||
|
35 | Under the hood, the Itpl class represents a string that knows how to | |||
|
36 | interpolate values. An instance of the class parses the string once | |||
|
37 | upon initialization; the evaluation and substitution can then be done | |||
|
38 | each time the instance is evaluated with str(instance). For example: | |||
|
39 | ||||
|
40 | from Itpl import Itpl | |||
|
41 | s = Itpl("Here is $foo.") | |||
|
42 | foo = 5 | |||
|
43 | print str(s) | |||
|
44 | foo = "bar" | |||
|
45 | print str(s) | |||
|
46 | ||||
|
47 | $Id: Itpl.py 2918 2007-12-31 14:34:47Z vivainio $ | |||
|
48 | """ # ' -> close an open quote for stupid emacs | |||
|
49 | ||||
|
50 | #***************************************************************************** | |||
|
51 | # | |||
|
52 | # Copyright (c) 2001 Ka-Ping Yee <ping@lfw.org> | |||
|
53 | # | |||
|
54 | # | |||
|
55 | # Published under the terms of the MIT license, hereby reproduced: | |||
|
56 | # | |||
|
57 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |||
|
58 | # of this software and associated documentation files (the "Software"), to | |||
|
59 | # deal in the Software without restriction, including without limitation the | |||
|
60 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |||
|
61 | # sell copies of the Software, and to permit persons to whom the Software is | |||
|
62 | # furnished to do so, subject to the following conditions: | |||
|
63 | # | |||
|
64 | # The above copyright notice and this permission notice shall be included in | |||
|
65 | # all copies or substantial portions of the Software. | |||
|
66 | # | |||
|
67 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
|
68 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
|
69 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
|
70 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
|
71 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
|
72 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |||
|
73 | # IN THE SOFTWARE. | |||
|
74 | # | |||
|
75 | #***************************************************************************** | |||
|
76 | ||||
|
77 | __author__ = 'Ka-Ping Yee <ping@lfw.org>' | |||
|
78 | __license__ = 'MIT' | |||
|
79 | ||||
|
80 | import string | |||
|
81 | import sys | |||
|
82 | from tokenize import tokenprog | |||
|
83 | from types import StringType | |||
|
84 | ||||
|
85 | class ItplError(ValueError): | |||
|
86 | def __init__(self, text, pos): | |||
|
87 | self.text = text | |||
|
88 | self.pos = pos | |||
|
89 | def __str__(self): | |||
|
90 | return "unfinished expression in %s at char %d" % ( | |||
|
91 | repr(self.text), self.pos) | |||
|
92 | ||||
|
93 | def matchorfail(text, pos): | |||
|
94 | match = tokenprog.match(text, pos) | |||
|
95 | if match is None: | |||
|
96 | raise ItplError(text, pos) | |||
|
97 | return match, match.end() | |||
|
98 | ||||
|
99 | class Itpl: | |||
|
100 | """Class representing a string with interpolation abilities. | |||
|
101 | ||||
|
102 | Upon creation, an instance works out what parts of the format | |||
|
103 | string are literal and what parts need to be evaluated. The | |||
|
104 | evaluation and substitution happens in the namespace of the | |||
|
105 | caller when str(instance) is called.""" | |||
|
106 | ||||
|
107 | def __init__(self, format,codec=sys.stdin.encoding,encoding_errors='backslashreplace'): | |||
|
108 | """The single mandatory argument to this constructor is a format | |||
|
109 | string. | |||
|
110 | ||||
|
111 | The format string is parsed according to the following rules: | |||
|
112 | ||||
|
113 | 1. A dollar sign and a name, possibly followed by any of: | |||
|
114 | - an open-paren, and anything up to the matching paren | |||
|
115 | - an open-bracket, and anything up to the matching bracket | |||
|
116 | - a period and a name | |||
|
117 | any number of times, is evaluated as a Python expression. | |||
|
118 | ||||
|
119 | 2. A dollar sign immediately followed by an open-brace, and | |||
|
120 | anything up to the matching close-brace, is evaluated as | |||
|
121 | a Python expression. | |||
|
122 | ||||
|
123 | 3. Outside of the expressions described in the above two rules, | |||
|
124 | two dollar signs in a row give you one literal dollar sign. | |||
|
125 | ||||
|
126 | Optional arguments: | |||
|
127 | ||||
|
128 | - codec('utf_8'): a string containing the name of a valid Python | |||
|
129 | codec. | |||
|
130 | ||||
|
131 | - encoding_errors('backslashreplace'): a string with a valid error handling | |||
|
132 | policy. See the codecs module documentation for details. | |||
|
133 | ||||
|
134 | These are used to encode the format string if a call to str() fails on | |||
|
135 | the expanded result.""" | |||
|
136 | ||||
|
137 | if not isinstance(format,basestring): | |||
|
138 | raise TypeError, "needs string initializer" | |||
|
139 | self.format = format | |||
|
140 | self.codec = codec | |||
|
141 | self.encoding_errors = encoding_errors | |||
|
142 | ||||
|
143 | namechars = "abcdefghijklmnopqrstuvwxyz" \ | |||
|
144 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; | |||
|
145 | chunks = [] | |||
|
146 | pos = 0 | |||
|
147 | ||||
|
148 | while 1: | |||
|
149 | dollar = string.find(format, "$", pos) | |||
|
150 | if dollar < 0: break | |||
|
151 | nextchar = format[dollar+1] | |||
|
152 | ||||
|
153 | if nextchar == "{": | |||
|
154 | chunks.append((0, format[pos:dollar])) | |||
|
155 | pos, level = dollar+2, 1 | |||
|
156 | while level: | |||
|
157 | match, pos = matchorfail(format, pos) | |||
|
158 | tstart, tend = match.regs[3] | |||
|
159 | token = format[tstart:tend] | |||
|
160 | if token == "{": level = level+1 | |||
|
161 | elif token == "}": level = level-1 | |||
|
162 | chunks.append((1, format[dollar+2:pos-1])) | |||
|
163 | ||||
|
164 | elif nextchar in namechars: | |||
|
165 | chunks.append((0, format[pos:dollar])) | |||
|
166 | match, pos = matchorfail(format, dollar+1) | |||
|
167 | while pos < len(format): | |||
|
168 | if format[pos] == "." and \ | |||
|
169 | pos+1 < len(format) and format[pos+1] in namechars: | |||
|
170 | match, pos = matchorfail(format, pos+1) | |||
|
171 | elif format[pos] in "([": | |||
|
172 | pos, level = pos+1, 1 | |||
|
173 | while level: | |||
|
174 | match, pos = matchorfail(format, pos) | |||
|
175 | tstart, tend = match.regs[3] | |||
|
176 | token = format[tstart:tend] | |||
|
177 | if token[0] in "([": level = level+1 | |||
|
178 | elif token[0] in ")]": level = level-1 | |||
|
179 | else: break | |||
|
180 | chunks.append((1, format[dollar+1:pos])) | |||
|
181 | ||||
|
182 | else: | |||
|
183 | chunks.append((0, format[pos:dollar+1])) | |||
|
184 | pos = dollar + 1 + (nextchar == "$") | |||
|
185 | ||||
|
186 | if pos < len(format): chunks.append((0, format[pos:])) | |||
|
187 | self.chunks = chunks | |||
|
188 | ||||
|
189 | def __repr__(self): | |||
|
190 | return "<Itpl %s >" % repr(self.format) | |||
|
191 | ||||
|
192 | def _str(self,glob,loc): | |||
|
193 | """Evaluate to a string in the given globals/locals. | |||
|
194 | ||||
|
195 | The final output is built by calling str(), but if this fails, the | |||
|
196 | result is encoded with the instance's codec and error handling policy, | |||
|
197 | via a call to out.encode(self.codec,self.encoding_errors)""" | |||
|
198 | result = [] | |||
|
199 | app = result.append | |||
|
200 | for live, chunk in self.chunks: | |||
|
201 | if live: | |||
|
202 | val = eval(chunk,glob,loc) | |||
|
203 | try: | |||
|
204 | app(str(val)) | |||
|
205 | except UnicodeEncodeError: | |||
|
206 | app(unicode(val)) | |||
|
207 | ||||
|
208 | else: app(chunk) | |||
|
209 | out = ''.join(result) | |||
|
210 | try: | |||
|
211 | return str(out) | |||
|
212 | except UnicodeError: | |||
|
213 | return out.encode(self.codec,self.encoding_errors) | |||
|
214 | ||||
|
215 | def __str__(self): | |||
|
216 | """Evaluate and substitute the appropriate parts of the string.""" | |||
|
217 | ||||
|
218 | # We need to skip enough frames to get to the actual caller outside of | |||
|
219 | # Itpl. | |||
|
220 | frame = sys._getframe(1) | |||
|
221 | while frame.f_globals["__name__"] == __name__: frame = frame.f_back | |||
|
222 | loc, glob = frame.f_locals, frame.f_globals | |||
|
223 | ||||
|
224 | return self._str(glob,loc) | |||
|
225 | ||||
|
226 | class ItplNS(Itpl): | |||
|
227 | """Class representing a string with interpolation abilities. | |||
|
228 | ||||
|
229 | This inherits from Itpl, but at creation time a namespace is provided | |||
|
230 | where the evaluation will occur. The interpolation becomes a bit more | |||
|
231 | efficient, as no traceback needs to be extracte. It also allows the | |||
|
232 | caller to supply a different namespace for the interpolation to occur than | |||
|
233 | its own.""" | |||
|
234 | ||||
|
235 | def __init__(self, format,globals,locals=None, | |||
|
236 | codec='utf_8',encoding_errors='backslashreplace'): | |||
|
237 | """ItplNS(format,globals[,locals]) -> interpolating string instance. | |||
|
238 | ||||
|
239 | This constructor, besides a format string, takes a globals dictionary | |||
|
240 | and optionally a locals (which defaults to globals if not provided). | |||
|
241 | ||||
|
242 | For further details, see the Itpl constructor.""" | |||
|
243 | ||||
|
244 | if locals is None: | |||
|
245 | locals = globals | |||
|
246 | self.globals = globals | |||
|
247 | self.locals = locals | |||
|
248 | Itpl.__init__(self,format,codec,encoding_errors) | |||
|
249 | ||||
|
250 | def __str__(self): | |||
|
251 | """Evaluate and substitute the appropriate parts of the string.""" | |||
|
252 | return self._str(self.globals,self.locals) | |||
|
253 | ||||
|
254 | def __repr__(self): | |||
|
255 | return "<ItplNS %s >" % repr(self.format) | |||
|
256 | ||||
|
257 | # utilities for fast printing | |||
|
258 | def itpl(text): return str(Itpl(text)) | |||
|
259 | def printpl(text): print itpl(text) | |||
|
260 | # versions with namespace | |||
|
261 | def itplns(text,globals,locals=None): return str(ItplNS(text,globals,locals)) | |||
|
262 | def printplns(text,globals,locals=None): print itplns(text,globals,locals) | |||
|
263 | ||||
|
264 | class ItplFile: | |||
|
265 | """A file object that filters each write() through an interpolator.""" | |||
|
266 | def __init__(self, file): self.file = file | |||
|
267 | def __repr__(self): return "<interpolated " + repr(self.file) + ">" | |||
|
268 | def __getattr__(self, attr): return getattr(self.file, attr) | |||
|
269 | def write(self, text): self.file.write(str(Itpl(text))) | |||
|
270 | ||||
|
271 | def filter(file=sys.stdout): | |||
|
272 | """Return an ItplFile that filters writes to the given file object. | |||
|
273 | ||||
|
274 | 'file = filter(file)' replaces 'file' with a filtered object that | |||
|
275 | has a write() method. When called with no argument, this creates | |||
|
276 | a filter to sys.stdout.""" | |||
|
277 | return ItplFile(file) | |||
|
278 | ||||
|
279 | def unfilter(ifile=None): | |||
|
280 | """Return the original file that corresponds to the given ItplFile. | |||
|
281 | ||||
|
282 | 'file = unfilter(file)' undoes the effect of 'file = filter(file)'. | |||
|
283 | 'sys.stdout = unfilter()' undoes the effect of 'sys.stdout = filter()'.""" | |||
|
284 | return ifile and ifile.file or sys.stdout.file |
@@ -0,0 +1,270 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ | |||
|
3 | Logger class for IPython's logging facilities. | |||
|
4 | ||||
|
5 | $Id: Logger.py 2875 2007-11-26 08:37:39Z fperez $ | |||
|
6 | """ | |||
|
7 | ||||
|
8 | #***************************************************************************** | |||
|
9 | # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and | |||
|
10 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> | |||
|
11 | # | |||
|
12 | # Distributed under the terms of the BSD License. The full license is in | |||
|
13 | # the file COPYING, distributed as part of this software. | |||
|
14 | #***************************************************************************** | |||
|
15 | ||||
|
16 | #**************************************************************************** | |||
|
17 | # Modules and globals | |||
|
18 | ||||
|
19 | from IPython import Release | |||
|
20 | __author__ = '%s <%s>\n%s <%s>' % \ | |||
|
21 | ( Release.authors['Janko'] + Release.authors['Fernando'] ) | |||
|
22 | __license__ = Release.license | |||
|
23 | ||||
|
24 | # Python standard modules | |||
|
25 | import glob | |||
|
26 | import os | |||
|
27 | import time | |||
|
28 | ||||
|
29 | #**************************************************************************** | |||
|
30 | # FIXME: This class isn't a mixin anymore, but it still needs attributes from | |||
|
31 | # ipython and does input cache management. Finish cleanup later... | |||
|
32 | ||||
|
33 | class Logger(object): | |||
|
34 | """A Logfile class with different policies for file creation""" | |||
|
35 | ||||
|
36 | def __init__(self,shell,logfname='Logger.log',loghead='',logmode='over'): | |||
|
37 | ||||
|
38 | self._i00,self._i,self._ii,self._iii = '','','','' | |||
|
39 | ||||
|
40 | # this is the full ipython instance, we need some attributes from it | |||
|
41 | # which won't exist until later. What a mess, clean up later... | |||
|
42 | self.shell = shell | |||
|
43 | ||||
|
44 | self.logfname = logfname | |||
|
45 | self.loghead = loghead | |||
|
46 | self.logmode = logmode | |||
|
47 | self.logfile = None | |||
|
48 | ||||
|
49 | # Whether to log raw or processed input | |||
|
50 | self.log_raw_input = False | |||
|
51 | ||||
|
52 | # whether to also log output | |||
|
53 | self.log_output = False | |||
|
54 | ||||
|
55 | # whether to put timestamps before each log entry | |||
|
56 | self.timestamp = False | |||
|
57 | ||||
|
58 | # activity control flags | |||
|
59 | self.log_active = False | |||
|
60 | ||||
|
61 | # logmode is a validated property | |||
|
62 | def _set_mode(self,mode): | |||
|
63 | if mode not in ['append','backup','global','over','rotate']: | |||
|
64 | raise ValueError,'invalid log mode %s given' % mode | |||
|
65 | self._logmode = mode | |||
|
66 | ||||
|
67 | def _get_mode(self): | |||
|
68 | return self._logmode | |||
|
69 | ||||
|
70 | logmode = property(_get_mode,_set_mode) | |||
|
71 | ||||
|
72 | def logstart(self,logfname=None,loghead=None,logmode=None, | |||
|
73 | log_output=False,timestamp=False,log_raw_input=False): | |||
|
74 | """Generate a new log-file with a default header. | |||
|
75 | ||||
|
76 | Raises RuntimeError if the log has already been started""" | |||
|
77 | ||||
|
78 | if self.logfile is not None: | |||
|
79 | raise RuntimeError('Log file is already active: %s' % | |||
|
80 | self.logfname) | |||
|
81 | ||||
|
82 | self.log_active = True | |||
|
83 | ||||
|
84 | # The parameters can override constructor defaults | |||
|
85 | if logfname is not None: self.logfname = logfname | |||
|
86 | if loghead is not None: self.loghead = loghead | |||
|
87 | if logmode is not None: self.logmode = logmode | |||
|
88 | ||||
|
89 | # Parameters not part of the constructor | |||
|
90 | self.timestamp = timestamp | |||
|
91 | self.log_output = log_output | |||
|
92 | self.log_raw_input = log_raw_input | |||
|
93 | ||||
|
94 | # init depending on the log mode requested | |||
|
95 | isfile = os.path.isfile | |||
|
96 | logmode = self.logmode | |||
|
97 | ||||
|
98 | if logmode == 'append': | |||
|
99 | self.logfile = open(self.logfname,'a') | |||
|
100 | ||||
|
101 | elif logmode == 'backup': | |||
|
102 | if isfile(self.logfname): | |||
|
103 | backup_logname = self.logfname+'~' | |||
|
104 | # Manually remove any old backup, since os.rename may fail | |||
|
105 | # under Windows. | |||
|
106 | if isfile(backup_logname): | |||
|
107 | os.remove(backup_logname) | |||
|
108 | os.rename(self.logfname,backup_logname) | |||
|
109 | self.logfile = open(self.logfname,'w') | |||
|
110 | ||||
|
111 | elif logmode == 'global': | |||
|
112 | self.logfname = os.path.join(self.shell.home_dir,self.logfname) | |||
|
113 | self.logfile = open(self.logfname, 'a') | |||
|
114 | ||||
|
115 | elif logmode == 'over': | |||
|
116 | if isfile(self.logfname): | |||
|
117 | os.remove(self.logfname) | |||
|
118 | self.logfile = open(self.logfname,'w') | |||
|
119 | ||||
|
120 | elif logmode == 'rotate': | |||
|
121 | if isfile(self.logfname): | |||
|
122 | if isfile(self.logfname+'.001~'): | |||
|
123 | old = glob.glob(self.logfname+'.*~') | |||
|
124 | old.sort() | |||
|
125 | old.reverse() | |||
|
126 | for f in old: | |||
|
127 | root, ext = os.path.splitext(f) | |||
|
128 | num = int(ext[1:-1])+1 | |||
|
129 | os.rename(f, root+'.'+`num`.zfill(3)+'~') | |||
|
130 | os.rename(self.logfname, self.logfname+'.001~') | |||
|
131 | self.logfile = open(self.logfname,'w') | |||
|
132 | ||||
|
133 | if logmode != 'append': | |||
|
134 | self.logfile.write(self.loghead) | |||
|
135 | ||||
|
136 | self.logfile.flush() | |||
|
137 | ||||
|
138 | def switch_log(self,val): | |||
|
139 | """Switch logging on/off. val should be ONLY a boolean.""" | |||
|
140 | ||||
|
141 | if val not in [False,True,0,1]: | |||
|
142 | raise ValueError, \ | |||
|
143 | 'Call switch_log ONLY with a boolean argument, not with:',val | |||
|
144 | ||||
|
145 | label = {0:'OFF',1:'ON',False:'OFF',True:'ON'} | |||
|
146 | ||||
|
147 | if self.logfile is None: | |||
|
148 | print """ | |||
|
149 | Logging hasn't been started yet (use logstart for that). | |||
|
150 | ||||
|
151 | %logon/%logoff are for temporarily starting and stopping logging for a logfile | |||
|
152 | which already exists. But you must first start the logging process with | |||
|
153 | %logstart (optionally giving a logfile name).""" | |||
|
154 | ||||
|
155 | else: | |||
|
156 | if self.log_active == val: | |||
|
157 | print 'Logging is already',label[val] | |||
|
158 | else: | |||
|
159 | print 'Switching logging',label[val] | |||
|
160 | self.log_active = not self.log_active | |||
|
161 | self.log_active_out = self.log_active | |||
|
162 | ||||
|
163 | def logstate(self): | |||
|
164 | """Print a status message about the logger.""" | |||
|
165 | if self.logfile is None: | |||
|
166 | print 'Logging has not been activated.' | |||
|
167 | else: | |||
|
168 | state = self.log_active and 'active' or 'temporarily suspended' | |||
|
169 | print 'Filename :',self.logfname | |||
|
170 | print 'Mode :',self.logmode | |||
|
171 | print 'Output logging :',self.log_output | |||
|
172 | print 'Raw input log :',self.log_raw_input | |||
|
173 | print 'Timestamping :',self.timestamp | |||
|
174 | print 'State :',state | |||
|
175 | ||||
|
176 | def log(self,line_ori,line_mod,continuation=None): | |||
|
177 | """Write the line to a log and create input cache variables _i*. | |||
|
178 | ||||
|
179 | Inputs: | |||
|
180 | ||||
|
181 | - line_ori: unmodified input line from the user. This is not | |||
|
182 | necessarily valid Python. | |||
|
183 | ||||
|
184 | - line_mod: possibly modified input, such as the transformations made | |||
|
185 | by input prefilters or input handlers of various kinds. This should | |||
|
186 | always be valid Python. | |||
|
187 | ||||
|
188 | - continuation: if True, indicates this is part of multi-line input.""" | |||
|
189 | ||||
|
190 | # update the auto _i tables | |||
|
191 | #print '***logging line',line_mod # dbg | |||
|
192 | #print '***cache_count', self.shell.outputcache.prompt_count # dbg | |||
|
193 | try: | |||
|
194 | input_hist = self.shell.user_ns['_ih'] | |||
|
195 | except: | |||
|
196 | print 'userns:',self.shell.user_ns.keys() | |||
|
197 | return | |||
|
198 | ||||
|
199 | out_cache = self.shell.outputcache | |||
|
200 | ||||
|
201 | # add blank lines if the input cache fell out of sync. | |||
|
202 | if out_cache.do_full_cache and \ | |||
|
203 | out_cache.prompt_count +1 > len(input_hist): | |||
|
204 | input_hist.extend(['\n'] * (out_cache.prompt_count - len(input_hist))) | |||
|
205 | ||||
|
206 | if not continuation and line_mod: | |||
|
207 | self._iii = self._ii | |||
|
208 | self._ii = self._i | |||
|
209 | self._i = self._i00 | |||
|
210 | # put back the final \n of every input line | |||
|
211 | self._i00 = line_mod+'\n' | |||
|
212 | #print 'Logging input:<%s>' % line_mod # dbg | |||
|
213 | input_hist.append(self._i00) | |||
|
214 | #print '---[%s]' % (len(input_hist)-1,) # dbg | |||
|
215 | ||||
|
216 | # hackish access to top-level namespace to create _i1,_i2... dynamically | |||
|
217 | to_main = {'_i':self._i,'_ii':self._ii,'_iii':self._iii} | |||
|
218 | if self.shell.outputcache.do_full_cache: | |||
|
219 | in_num = self.shell.outputcache.prompt_count | |||
|
220 | ||||
|
221 | # but if the opposite is true (a macro can produce multiple inputs | |||
|
222 | # with no output display called), then bring the output counter in | |||
|
223 | # sync: | |||
|
224 | last_num = len(input_hist)-1 | |||
|
225 | if in_num != last_num: | |||
|
226 | in_num = self.shell.outputcache.prompt_count = last_num | |||
|
227 | new_i = '_i%s' % in_num | |||
|
228 | if continuation: | |||
|
229 | self._i00 = '%s%s\n' % (self.shell.user_ns[new_i],line_mod) | |||
|
230 | input_hist[in_num] = self._i00 | |||
|
231 | to_main[new_i] = self._i00 | |||
|
232 | self.shell.user_ns.update(to_main) | |||
|
233 | ||||
|
234 | # Write the log line, but decide which one according to the | |||
|
235 | # log_raw_input flag, set when the log is started. | |||
|
236 | if self.log_raw_input: | |||
|
237 | self.log_write(line_ori) | |||
|
238 | else: | |||
|
239 | self.log_write(line_mod) | |||
|
240 | ||||
|
241 | def log_write(self,data,kind='input'): | |||
|
242 | """Write data to the log file, if active""" | |||
|
243 | ||||
|
244 | #print 'data: %r' % data # dbg | |||
|
245 | if self.log_active and data: | |||
|
246 | write = self.logfile.write | |||
|
247 | if kind=='input': | |||
|
248 | if self.timestamp: | |||
|
249 | write(time.strftime('# %a, %d %b %Y %H:%M:%S\n', | |||
|
250 | time.localtime())) | |||
|
251 | write('%s\n' % data) | |||
|
252 | elif kind=='output' and self.log_output: | |||
|
253 | odata = '\n'.join(['#[Out]# %s' % s | |||
|
254 | for s in data.split('\n')]) | |||
|
255 | write('%s\n' % odata) | |||
|
256 | self.logfile.flush() | |||
|
257 | ||||
|
258 | def logstop(self): | |||
|
259 | """Fully stop logging and close log file. | |||
|
260 | ||||
|
261 | In order to start logging again, a new logstart() call needs to be | |||
|
262 | made, possibly (though not necessarily) with a new filename, mode and | |||
|
263 | other options.""" | |||
|
264 | ||||
|
265 | self.logfile.close() | |||
|
266 | self.logfile = None | |||
|
267 | self.log_active = False | |||
|
268 | ||||
|
269 | # For backwards compatibility, in case anyone was using this. | |||
|
270 | close_log = logstop |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644, binary diff hidden |
|
NO CONTENT: new file 100644, binary diff hidden |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
General Comments 0
You need to be logged in to leave comments.
Login now