Merge lp://qastaging/~robert-kern/ipython/clipboard into lp://qastaging/ipython/0.11
- clipboard
- Merge into trunk
Status: | Merged |
---|---|
Merge reported by: | Fernando Perez |
Merged at revision: | not available |
Proposed branch: | lp://qastaging/~robert-kern/ipython/clipboard |
Merge into: | lp://qastaging/ipython/0.11 |
Diff against target: | None lines |
To merge this branch: | bzr merge lp://qastaging/~robert-kern/ipython/clipboard |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Fernando Perez | Approve | ||
Review via email:
|
Commit message
Description of the change
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Fernando Perez (fdo.perez) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Fernando Perez (fdo.perez) wrote : | # |
How does the clipboard call work if Qt or Wx is already loaded? I see it uses Tk, so what happens if I'm using -wthread and I type %cpaste? Do we get a toolkit conflict?
I'm OK with this going in without implementations for all platforms, but I don't want to have something where a simple %cpaste becomes synonym with %crashme :)
Otherwise, looks good, almost ready. Only remaining issues:
- tests? could be doctests or standalone. I realize that testing the clipboard usage itself may be hard (though a mock object could help). But at least some smoke tests that cover each function's main entry point just for sanity...
- Some examples in the docstrings? These could be the tests as well. The docstrings must at least list the arguments for all functions. We'll be using the numpy standard from now on, which is about as easy to type as it gets, and which you're plenty familiar with :)
Thanks!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Fernando Perez (fdo.perez) wrote : | # |
Minor nit: PEP-8:
Blank Lines
Separate top-level function and class definitions with two blank lines.
Method definitions inside a class are separated by a single blank line.
Cheers,
f
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Fernando Perez (fdo.perez) wrote : | # |
Robert, any thoughts on this? I'd like to get it ready for merging before we cut 0.10 out, since it seems it only needs a little love...
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Robert Kern (robert-kern) wrote : | # |
No time, really.
--
Robert Kern
"I have come to believe that the whole world is an enigma, a harmless
enigma that is made terrible by our own mad attempt to interpret it as
though it had an underlying truth."
-- Umberto Eco
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Brian Granger (ellisonbg) wrote : | # |
Fernando, is it possible to target merge proposals to particular
milestones or releases? I see that you can link branches to tickets
and blueprints, but is that it. It would be great to see only the
merge proposals related to 0.10. I am just not sure if what I see on
LP is what you see.
Brian
On Wed, Apr 15, 2009 at 2:15 AM, Robert Kern
<email address hidden> wrote:
> No time, really.
>
> --
> Robert Kern
>
> "I have come to believe that the whole world is an enigma, a harmless
> enigma that is made terrible by our own mad attempt to interpret it as
> though it had an underlying truth."
> -- Umberto Eco
> https:/
> You are subscribed to branch lp:ipython.
>
--
Brian E. Granger, Ph.D.
Assistant Professor of Physics
Cal Poly State University, San Luis Obispo
<email address hidden>
<email address hidden>
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Fernando Perez (fdo.perez) wrote : | # |
On Wed, Apr 15, 2009 at 1:39 PM, Brian Granger <email address hidden> wrote:
> Fernando, is it possible to target merge proposals to particular
> milestones or releases? I see that you can link branches to tickets
> and blueprints, but is that it. It would be great to see only the
> merge proposals related to 0.10. I am just not sure if what I see on
> LP is what you see.
No, that doesn't seem to be available, as far as I can see. I get the
same thing you see.
A convoluted solution would be to make a 'meta bug' linked to a
milestone linked to a branch, but that's ugly as hell.
f
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Brian Granger (ellisonbg) wrote : | # |
I figure out how to associate branches with series and so on, but I
don't have the permissions to do so. I know it can be done because I
can do it on another project (pyxg). See my other email about how you
can give the rest of us permissions to do these things.
Cheers,
Brian
On Wed, Apr 15, 2009 at 2:27 PM, Fernando Perez <email address hidden> wrote:
> On Wed, Apr 15, 2009 at 1:39 PM, Brian Granger <email address hidden> wrote:
>> Fernando, is it possible to target merge proposals to particular
>> milestones or releases? I see that you can link branches to tickets
>> and blueprints, but is that it. It would be great to see only the
>> merge proposals related to 0.10. I am just not sure if what I see on
>> LP is what you see.
>
> No, that doesn't seem to be available, as far as I can see. I get the
> same thing you see.
>
> A convoluted solution would be to make a 'meta bug' linked to a
> milestone linked to a branch, but that's ugly as hell.
>
> f
> --
> https:/
> You are subscribed to branch lp:ipython.
>
--
Brian E. Granger, Ph.D.
Assistant Professor of Physics
Cal Poly State University, San Luis Obispo
<email address hidden>
<email address hidden>
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Brian Granger (ellisonbg) wrote : | # |
Oh, I should also say that I am pretty frustrated with how difficult
and non-intuitive all of these things are on LP.
Brian
On Wed, Apr 15, 2009 at 2:27 PM, Fernando Perez <email address hidden> wrote:
> On Wed, Apr 15, 2009 at 1:39 PM, Brian Granger <email address hidden> wrote:
>> Fernando, is it possible to target merge proposals to particular
>> milestones or releases? I see that you can link branches to tickets
>> and blueprints, but is that it. It would be great to see only the
>> merge proposals related to 0.10. I am just not sure if what I see on
>> LP is what you see.
>
> No, that doesn't seem to be available, as far as I can see. I get the
> same thing you see.
>
> A convoluted solution would be to make a 'meta bug' linked to a
> milestone linked to a branch, but that's ugly as hell.
>
> f
> --
> https:/
> You are subscribed to branch lp:ipython.
>
--
Brian E. Granger, Ph.D.
Assistant Professor of Physics
Cal Poly State University, San Luis Obispo
<email address hidden>
<email address hidden>
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Fernando Perez (fdo.perez) wrote : | # |
OK, I can't test on OSX, but at least I added some tests so that this code doesn't go under the radar. I'm approving the merge with these tests, since that's really all that was missing. The functionality is actually very useful. Thanks!
Preview Diff
1 | === modified file 'IPython/Magic.py' |
2 | --- IPython/Magic.py 2008-09-20 09:34:38 +0000 |
3 | +++ IPython/Magic.py 2009-03-05 08:04:43 +0000 |
4 | @@ -3210,44 +3210,31 @@ |
5 | page(self.shell.pycolorize(cont), |
6 | screen_lines=self.shell.rc.screen_length) |
7 | |
8 | - def magic_cpaste(self, parameter_s=''): |
9 | - """Allows you to paste & execute a pre-formatted code block from clipboard. |
10 | - |
11 | - You must terminate the block with '--' (two minus-signs) alone on the |
12 | - line. You can also provide your own sentinel with '%paste -s %%' ('%%' |
13 | - is the new sentinel for this operation) |
14 | - |
15 | - The block is dedented prior to execution to enable execution of method |
16 | - definitions. '>' and '+' characters at the beginning of a line are |
17 | - ignored, to allow pasting directly from e-mails, diff files and |
18 | - doctests (the '...' continuation prompt is also stripped). The |
19 | - executed block is also assigned to variable named 'pasted_block' for |
20 | - later editing with '%edit pasted_block'. |
21 | - |
22 | - You can also pass a variable name as an argument, e.g. '%cpaste foo'. |
23 | - This assigns the pasted block to variable 'foo' as string, without |
24 | - dedenting or executing it (preceding >>> and + is still stripped) |
25 | - |
26 | - '%cpaste -r' re-executes the block previously entered by cpaste. |
27 | - |
28 | - Do not be alarmed by garbled output on Windows (it's a readline bug). |
29 | - Just press enter and type -- (and press enter again) and the block |
30 | - will be what was just pasted. |
31 | - |
32 | - IPython statements (magics, shell escapes) are not supported (yet). |
33 | - """ |
34 | - opts,args = self.parse_options(parameter_s,'rs:',mode='string') |
35 | - par = args.strip() |
36 | - if opts.has_key('r'): |
37 | - b = self.user_ns.get('pasted_block', None) |
38 | - if b is None: |
39 | - raise UsageError('No previous pasted block available') |
40 | - print "Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)) |
41 | - exec b in self.user_ns |
42 | - return |
43 | - |
44 | - sentinel = opts.get('s','--') |
45 | - |
46 | + def _rerun_pasted(self): |
47 | + """ Rerun a previously pasted command. |
48 | + """ |
49 | + b = self.user_ns.get('pasted_block', None) |
50 | + if b is None: |
51 | + raise UsageError('No previous pasted block available') |
52 | + print "Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)) |
53 | + exec b in self.user_ns |
54 | + |
55 | + def _get_pasted_lines(self, sentinel): |
56 | + """ Yield pasted lines until the user enters the given sentinel value. |
57 | + """ |
58 | + from IPython import iplib |
59 | + print "Pasting code; enter '%s' alone on the line to stop." % sentinel |
60 | + while True: |
61 | + l = iplib.raw_input_original(':') |
62 | + if l == sentinel: |
63 | + return |
64 | + else: |
65 | + yield l |
66 | + |
67 | + def _strip_pasted_lines_for_code(self, raw_lines): |
68 | + """ Strip non-code parts of a sequence of lines to return a block of |
69 | + code. |
70 | + """ |
71 | # Regular expressions that declare text we strip from the input: |
72 | strip_re = [r'^\s*In \[\d+\]:', # IPython input prompt |
73 | r'^\s*(\s?>)+', # Python input prompt |
74 | @@ -3257,20 +3244,19 @@ |
75 | |
76 | strip_from_start = map(re.compile,strip_re) |
77 | |
78 | - from IPython import iplib |
79 | lines = [] |
80 | - print "Pasting code; enter '%s' alone on the line to stop." % sentinel |
81 | - while 1: |
82 | - l = iplib.raw_input_original(':') |
83 | - if l ==sentinel: |
84 | - break |
85 | - |
86 | + for l in raw_lines: |
87 | for pat in strip_from_start: |
88 | l = pat.sub('',l) |
89 | lines.append(l) |
90 | |
91 | block = "\n".join(lines) + '\n' |
92 | #print "block:\n",block |
93 | + return block |
94 | + |
95 | + def _execute_block(self, block, par): |
96 | + """ Execute a block, or store it in a variable, per the user's request. |
97 | + """ |
98 | if not par: |
99 | b = textwrap.dedent(block) |
100 | self.user_ns['pasted_block'] = b |
101 | @@ -3278,7 +3264,77 @@ |
102 | else: |
103 | self.user_ns[par] = SList(block.splitlines()) |
104 | print "Block assigned to '%s'" % par |
105 | - |
106 | + |
107 | + def magic_cpaste(self, parameter_s=''): |
108 | + """Allows you to paste & execute a pre-formatted code block from clipboard. |
109 | + |
110 | + You must terminate the block with '--' (two minus-signs) alone on the |
111 | + line. You can also provide your own sentinel with '%paste -s %%' ('%%' |
112 | + is the new sentinel for this operation) |
113 | + |
114 | + The block is dedented prior to execution to enable execution of method |
115 | + definitions. '>' and '+' characters at the beginning of a line are |
116 | + ignored, to allow pasting directly from e-mails, diff files and |
117 | + doctests (the '...' continuation prompt is also stripped). The |
118 | + executed block is also assigned to variable named 'pasted_block' for |
119 | + later editing with '%edit pasted_block'. |
120 | + |
121 | + You can also pass a variable name as an argument, e.g. '%cpaste foo'. |
122 | + This assigns the pasted block to variable 'foo' as string, without |
123 | + dedenting or executing it (preceding >>> and + is still stripped) |
124 | + |
125 | + '%cpaste -r' re-executes the block previously entered by cpaste. |
126 | + |
127 | + Do not be alarmed by garbled output on Windows (it's a readline bug). |
128 | + Just press enter and type -- (and press enter again) and the block |
129 | + will be what was just pasted. |
130 | + |
131 | + IPython statements (magics, shell escapes) are not supported (yet). |
132 | + """ |
133 | + opts,args = self.parse_options(parameter_s,'rs:',mode='string') |
134 | + par = args.strip() |
135 | + if opts.has_key('r'): |
136 | + self._rerun_pasted() |
137 | + return |
138 | + |
139 | + sentinel = opts.get('s','--') |
140 | + |
141 | + block = self._strip_pasted_lines_for_code( |
142 | + self._get_pasted_lines(sentinel)) |
143 | + |
144 | + self._execute_block(block, par) |
145 | + |
146 | + def magic_paste(self, parameter_s=''): |
147 | + """Allows you to paste & execute a pre-formatted code block from clipboard. |
148 | + |
149 | + The text is pulled directly from the clipboard without user |
150 | + intervention. |
151 | + |
152 | + The block is dedented prior to execution to enable execution of method |
153 | + definitions. '>' and '+' characters at the beginning of a line are |
154 | + ignored, to allow pasting directly from e-mails, diff files and |
155 | + doctests (the '...' continuation prompt is also stripped). The |
156 | + executed block is also assigned to variable named 'pasted_block' for |
157 | + later editing with '%edit pasted_block'. |
158 | + |
159 | + You can also pass a variable name as an argument, e.g. '%paste foo'. |
160 | + This assigns the pasted block to variable 'foo' as string, without |
161 | + dedenting or executing it (preceding >>> and + is still stripped) |
162 | + |
163 | + '%paste -r' re-executes the block previously entered by cpaste. |
164 | + |
165 | + IPython statements (magics, shell escapes) are not supported (yet). |
166 | + """ |
167 | + opts,args = self.parse_options(parameter_s,'r:',mode='string') |
168 | + par = args.strip() |
169 | + if opts.has_key('r'): |
170 | + self._rerun_pasted() |
171 | + return |
172 | + |
173 | + text = self.shell.hooks.clipboard_get() |
174 | + block = self._strip_pasted_lines_for_code(text.splitlines()) |
175 | + self._execute_block(block, par) |
176 | + |
177 | def magic_quickref(self,arg): |
178 | """ Show a quick reference sheet """ |
179 | import IPython.usage |
180 | |
181 | === added file 'IPython/clipboard.py' |
182 | --- IPython/clipboard.py 1970-01-01 00:00:00 +0000 |
183 | +++ IPython/clipboard.py 2009-03-05 08:04:43 +0000 |
184 | @@ -0,0 +1,56 @@ |
185 | +""" Utilities for accessing the platform's clipboard. |
186 | +""" |
187 | + |
188 | +import subprocess |
189 | +import sys |
190 | + |
191 | +from IPython.ipapi import TryNext |
192 | + |
193 | + |
194 | +def win32_clipboard_get(): |
195 | + """ Get the current clipboard's text on Windows. |
196 | + |
197 | + Requires Mark Hammond's pywin32 extensions. |
198 | + """ |
199 | + try: |
200 | + import win32clipboard |
201 | + except ImportError: |
202 | + message = ("Getting text from the clipboard requires the pywin32 " |
203 | + "extensions: http://sourceforge.net/projects/pywin32/") |
204 | + raise TryNext(message) |
205 | + win32clipboard.OpenClipboard() |
206 | + text = win32clipboard.GetClipboardData(win32clipboard.CF_TEXT) |
207 | + # FIXME: convert \r\n to \n? |
208 | + win32clipboard.CloseClipboard() |
209 | + return text |
210 | + |
211 | +def osx_clipboard_get(): |
212 | + """ Get the clipboard's text on OS X. |
213 | + """ |
214 | + p = subprocess.Popen(['pbpaste', '-Prefer', 'ascii'], |
215 | + stdout=subprocess.PIPE) |
216 | + text, stderr = p.communicate() |
217 | + # Text comes in with old Mac \r line endings. Change them to \n. |
218 | + text = text.replace('\r', '\n') |
219 | + return text |
220 | + |
221 | +def tkinter_clipboard_get(): |
222 | + """ Get the clipboard's text using Tkinter. |
223 | + |
224 | + This is the default on systems that are not Windows or OS X. It may |
225 | + interfere with other UI toolkits and should be replaced with an |
226 | + implementation that uses that toolkit. |
227 | + """ |
228 | + try: |
229 | + import Tkinter |
230 | + except ImportError: |
231 | + message = ("Getting text from the clipboard on this platform " |
232 | + "requires Tkinter.") |
233 | + raise TryNext(message) |
234 | + root = Tkinter.Tk() |
235 | + root.withdraw() |
236 | + text = root.clipboard_get() |
237 | + root.destroy() |
238 | + return text |
239 | + |
240 | + |
241 | |
242 | === modified file 'IPython/hooks.py' |
243 | --- IPython/hooks.py 2008-09-20 09:34:38 +0000 |
244 | +++ IPython/hooks.py 2009-03-05 08:04:43 +0000 |
245 | @@ -49,6 +49,7 @@ |
246 | __version__ = Release.version |
247 | |
248 | import os,bisect |
249 | +import sys |
250 | from genutils import Term,shell |
251 | from pprint import PrettyPrinter |
252 | |
253 | @@ -58,7 +59,8 @@ |
254 | __all__ = ['editor', 'fix_error_editor', 'synchronize_with_editor', 'result_display', |
255 | 'input_prefilter', 'shutdown_hook', 'late_startup_hook', |
256 | 'generate_prompt', 'generate_output_prompt','shell_hook', |
257 | - 'show_in_pager','pre_prompt_hook', 'pre_runcode_hook'] |
258 | + 'show_in_pager','pre_prompt_hook', 'pre_runcode_hook', |
259 | + 'clipboard_get'] |
260 | # vds: << |
261 | |
262 | pformat = PrettyPrinter().pformat |
263 | @@ -248,5 +250,20 @@ |
264 | def pre_runcode_hook(self): |
265 | """ Executed before running the (prefiltered) code in IPython """ |
266 | return None |
267 | - |
268 | |
269 | +def clipboard_get(self): |
270 | + """ Get text from the clipboard. |
271 | + """ |
272 | + from IPython.clipboard import (osx_clipboard_get, tkinter_clipboard_get, |
273 | + win32_clipboard_get) |
274 | + if sys.platform == 'win32': |
275 | + chain = [win32_clipboard_get, tkinter_clipboard_get] |
276 | + elif sys.platform == 'darwin': |
277 | + chain = [osx_clipboard_get, tkinter_clipboard_get] |
278 | + else: |
279 | + chain = [tkinter_clipboard_get] |
280 | + dispatcher = CommandChainDispatcher() |
281 | + for func in chain: |
282 | + dispatcher.add(func) |
283 | + text = dispatcher() |
284 | + return text |
You mentioned on list it was ready for merge, so here we go...