Merge lp://qastaging/~robert-kern/ipython/clipboard into lp://qastaging/ipython/0.11

Proposed by Fernando Perez
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
Reviewer Review Type Date Requested Status
Fernando Perez Approve
Review via email: mp+4497@code.qastaging.launchpad.net
To post a comment you must log in.
Revision history for this message
Fernando Perez (fdo.perez) wrote :

You mentioned on list it was ready for merge, so here we go...

Revision history for this message
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!

review: Needs Fixing
Revision history for this message
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

Revision history for this message
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...

Revision history for this message
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

Revision history for this message
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://code.launchpad.net/~robert-kern/ipython/clipboard/+merge/4497
> 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>

Revision history for this message
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

Revision history for this message
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://code.launchpad.net/~robert-kern/ipython/clipboard/+merge/4497
> 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>

Revision history for this message
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://code.launchpad.net/~robert-kern/ipython/clipboard/+merge/4497
> 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>

Revision history for this message
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!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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

Subscribers

People subscribed via source and target branches