Show More
@@ -0,0 +1,56 b'' | |||
|
1 | """ Utilities for accessing the platform's clipboard. | |
|
2 | """ | |
|
3 | ||
|
4 | import subprocess | |
|
5 | import sys | |
|
6 | ||
|
7 | from IPython.ipapi import TryNext | |
|
8 | ||
|
9 | ||
|
10 | def win32_clipboard_get(): | |
|
11 | """ Get the current clipboard's text on Windows. | |
|
12 | ||
|
13 | Requires Mark Hammond's pywin32 extensions. | |
|
14 | """ | |
|
15 | try: | |
|
16 | import win32clipboard | |
|
17 | except ImportError: | |
|
18 | message = ("Getting text from the clipboard requires the pywin32 " | |
|
19 | "extensions: http://sourceforge.net/projects/pywin32/") | |
|
20 | raise TryNext(message) | |
|
21 | win32clipboard.OpenClipboard() | |
|
22 | text = win32clipboard.GetClipboardData(win32clipboard.CF_TEXT) | |
|
23 | # FIXME: convert \r\n to \n? | |
|
24 | win32clipboard.CloseClipboard() | |
|
25 | return text | |
|
26 | ||
|
27 | def osx_clipboard_get(): | |
|
28 | """ Get the clipboard's text on OS X. | |
|
29 | """ | |
|
30 | p = subprocess.Popen(['pbpaste', '-Prefer', 'ascii'], | |
|
31 | stdout=subprocess.PIPE) | |
|
32 | text, stderr = p.communicate() | |
|
33 | # Text comes in with old Mac \r line endings. Change them to \n. | |
|
34 | text = text.replace('\r', '\n') | |
|
35 | return text | |
|
36 | ||
|
37 | def tkinter_clipboard_get(): | |
|
38 | """ Get the clipboard's text using Tkinter. | |
|
39 | ||
|
40 | This is the default on systems that are not Windows or OS X. It may | |
|
41 | interfere with other UI toolkits and should be replaced with an | |
|
42 | implementation that uses that toolkit. | |
|
43 | """ | |
|
44 | try: | |
|
45 | import Tkinter | |
|
46 | except ImportError: | |
|
47 | message = ("Getting text from the clipboard on this platform " | |
|
48 | "requires Tkinter.") | |
|
49 | raise TryNext(message) | |
|
50 | root = Tkinter.Tk() | |
|
51 | root.withdraw() | |
|
52 | text = root.clipboard_get() | |
|
53 | root.destroy() | |
|
54 | return text | |
|
55 | ||
|
56 |
@@ -44,6 +44,7 b' somewhere in your configuration files or ipython command line.' | |||
|
44 | 44 | from IPython.core import ipapi |
|
45 | 45 | |
|
46 | 46 | import os,bisect |
|
47 | import sys | |
|
47 | 48 | from IPython.utils.genutils import Term,shell |
|
48 | 49 | from pprint import PrettyPrinter |
|
49 | 50 | |
@@ -53,7 +54,8 b' from pprint import PrettyPrinter' | |||
|
53 | 54 | __all__ = ['editor', 'fix_error_editor', 'synchronize_with_editor', 'result_display', |
|
54 | 55 | 'input_prefilter', 'shutdown_hook', 'late_startup_hook', |
|
55 | 56 | 'generate_prompt', 'generate_output_prompt','shell_hook', |
|
56 |
'show_in_pager','pre_prompt_hook', 'pre_runcode_hook' |
|
|
57 | 'show_in_pager','pre_prompt_hook', 'pre_runcode_hook', | |
|
58 | 'clipboard_get'] | |
|
57 | 59 | # vds: << |
|
58 | 60 | |
|
59 | 61 | pformat = PrettyPrinter().pformat |
@@ -244,4 +246,21 b' def pre_runcode_hook(self):' | |||
|
244 | 246 | """ Executed before running the (prefiltered) code in IPython """ |
|
245 | 247 | return None |
|
246 | 248 | |
|
247 | ||
|
249 | def clipboard_get(self): | |
|
250 | """ Get text from the clipboard. | |
|
251 | """ | |
|
252 | from IPython.lib.clipboard import ( | |
|
253 | osx_clipboard_get, tkinter_clipboard_get, | |
|
254 | win32_clipboard_get | |
|
255 | ) | |
|
256 | if sys.platform == 'win32': | |
|
257 | chain = [win32_clipboard_get, tkinter_clipboard_get] | |
|
258 | elif sys.platform == 'darwin': | |
|
259 | chain = [osx_clipboard_get, tkinter_clipboard_get] | |
|
260 | else: | |
|
261 | chain = [tkinter_clipboard_get] | |
|
262 | dispatcher = CommandChainDispatcher() | |
|
263 | for func in chain: | |
|
264 | dispatcher.add(func) | |
|
265 | text = dispatcher() | |
|
266 | return text |
@@ -3262,6 +3262,61 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
3262 | 3262 | page(self.shell.pycolorize(cont), |
|
3263 | 3263 | screen_lines=self.shell.rc.screen_length) |
|
3264 | 3264 | |
|
3265 | def _rerun_pasted(self): | |
|
3266 | """ Rerun a previously pasted command. | |
|
3267 | """ | |
|
3268 | b = self.user_ns.get('pasted_block', None) | |
|
3269 | if b is None: | |
|
3270 | raise UsageError('No previous pasted block available') | |
|
3271 | print "Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)) | |
|
3272 | exec b in self.user_ns | |
|
3273 | ||
|
3274 | def _get_pasted_lines(self, sentinel): | |
|
3275 | """ Yield pasted lines until the user enters the given sentinel value. | |
|
3276 | """ | |
|
3277 | from IPython.core import iplib | |
|
3278 | print "Pasting code; enter '%s' alone on the line to stop." % sentinel | |
|
3279 | while True: | |
|
3280 | l = iplib.raw_input_original(':') | |
|
3281 | if l == sentinel: | |
|
3282 | return | |
|
3283 | else: | |
|
3284 | yield l | |
|
3285 | ||
|
3286 | def _strip_pasted_lines_for_code(self, raw_lines): | |
|
3287 | """ Strip non-code parts of a sequence of lines to return a block of | |
|
3288 | code. | |
|
3289 | """ | |
|
3290 | # Regular expressions that declare text we strip from the input: | |
|
3291 | strip_re = [r'^\s*In \[\d+\]:', # IPython input prompt | |
|
3292 | r'^\s*(\s?>)+', # Python input prompt | |
|
3293 | r'^\s*\.{3,}', # Continuation prompts | |
|
3294 | r'^\++', | |
|
3295 | ] | |
|
3296 | ||
|
3297 | strip_from_start = map(re.compile,strip_re) | |
|
3298 | ||
|
3299 | lines = [] | |
|
3300 | for l in raw_lines: | |
|
3301 | for pat in strip_from_start: | |
|
3302 | l = pat.sub('',l) | |
|
3303 | lines.append(l) | |
|
3304 | ||
|
3305 | block = "\n".join(lines) + '\n' | |
|
3306 | #print "block:\n",block | |
|
3307 | return block | |
|
3308 | ||
|
3309 | def _execute_block(self, block, par): | |
|
3310 | """ Execute a block, or store it in a variable, per the user's request. | |
|
3311 | """ | |
|
3312 | if not par: | |
|
3313 | b = textwrap.dedent(block) | |
|
3314 | self.user_ns['pasted_block'] = b | |
|
3315 | exec b in self.user_ns | |
|
3316 | else: | |
|
3317 | self.user_ns[par] = SList(block.splitlines()) | |
|
3318 | print "Block assigned to '%s'" % par | |
|
3319 | ||
|
3265 | 3320 | def magic_cpaste(self, parameter_s=''): |
|
3266 | 3321 | """Allows you to paste & execute a pre-formatted code block from clipboard. |
|
3267 | 3322 | |
@@ -3287,49 +3342,59 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
3287 | 3342 | will be what was just pasted. |
|
3288 | 3343 | |
|
3289 | 3344 | IPython statements (magics, shell escapes) are not supported (yet). |
|
3345 | ||
|
3346 | See also | |
|
3347 | -------- | |
|
3348 | %paste: automatically pull code from clipboard. | |
|
3290 | 3349 | """ |
|
3350 | ||
|
3291 | 3351 | opts,args = self.parse_options(parameter_s,'rs:',mode='string') |
|
3292 | 3352 | par = args.strip() |
|
3293 | 3353 | if opts.has_key('r'): |
|
3294 |
|
|
|
3295 | if b is None: | |
|
3296 | raise UsageError('No previous pasted block available') | |
|
3297 | print "Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)) | |
|
3298 | exec b in self.user_ns | |
|
3354 | self._rerun_pasted() | |
|
3299 | 3355 | return |
|
3300 | 3356 | |
|
3301 | 3357 | sentinel = opts.get('s','--') |
|
3302 | 3358 | |
|
3303 | # Regular expressions that declare text we strip from the input: | |
|
3304 | strip_re = [r'^\s*In \[\d+\]:', # IPython input prompt | |
|
3305 | r'^\s*(\s?>)+', # Python input prompt | |
|
3306 | r'^\s*\.{3,}', # Continuation prompts | |
|
3307 | r'^\++', | |
|
3308 | ] | |
|
3359 | block = self._strip_pasted_lines_for_code( | |
|
3360 | self._get_pasted_lines(sentinel)) | |
|
3309 | 3361 | |
|
3310 | strip_from_start = map(re.compile,strip_re) | |
|
3362 | self._execute_block(block, par) | |
|
3311 | 3363 | |
|
3312 | from IPython.core import iplib | |
|
3313 | lines = [] | |
|
3314 | print "Pasting code; enter '%s' alone on the line to stop." % sentinel | |
|
3315 | while 1: | |
|
3316 | l = iplib.raw_input_original(':') | |
|
3317 | if l ==sentinel: | |
|
3318 | break | |
|
3364 | def magic_paste(self, parameter_s=''): | |
|
3365 | """Allows you to paste & execute a pre-formatted code block from clipboard. | |
|
3319 | 3366 |
|
|
3320 | for pat in strip_from_start: | |
|
3321 | l = pat.sub('',l) | |
|
3322 | lines.append(l) | |
|
3367 | The text is pulled directly from the clipboard without user | |
|
3368 | intervention. | |
|
3323 | 3369 | |
|
3324 | block = "\n".join(lines) + '\n' | |
|
3325 | #print "block:\n",block | |
|
3326 | if not par: | |
|
3327 | b = textwrap.dedent(block) | |
|
3328 | self.user_ns['pasted_block'] = b | |
|
3329 | exec b in self.user_ns | |
|
3330 |
|
|
|
3331 | self.user_ns[par] = SList(block.splitlines()) | |
|
3332 | print "Block assigned to '%s'" % par | |
|
3370 | The block is dedented prior to execution to enable execution of method | |
|
3371 | definitions. '>' and '+' characters at the beginning of a line are | |
|
3372 | ignored, to allow pasting directly from e-mails, diff files and | |
|
3373 | doctests (the '...' continuation prompt is also stripped). The | |
|
3374 | executed block is also assigned to variable named 'pasted_block' for | |
|
3375 | later editing with '%edit pasted_block'. | |
|
3376 | ||
|
3377 | You can also pass a variable name as an argument, e.g. '%paste foo'. | |
|
3378 | This assigns the pasted block to variable 'foo' as string, without | |
|
3379 | dedenting or executing it (preceding >>> and + is still stripped) | |
|
3380 | ||
|
3381 | '%paste -r' re-executes the block previously entered by cpaste. | |
|
3382 | ||
|
3383 | IPython statements (magics, shell escapes) are not supported (yet). | |
|
3384 | ||
|
3385 | See also | |
|
3386 | -------- | |
|
3387 | %cpaste: manually paste code into terminal until you mark its end. | |
|
3388 | """ | |
|
3389 | opts,args = self.parse_options(parameter_s,'r:',mode='string') | |
|
3390 | par = args.strip() | |
|
3391 | if opts.has_key('r'): | |
|
3392 | self._rerun_pasted() | |
|
3393 | return | |
|
3394 | ||
|
3395 | text = self.shell.hooks.clipboard_get() | |
|
3396 | block = self._strip_pasted_lines_for_code(text.splitlines()) | |
|
3397 | self._execute_block(block, par) | |
|
3333 | 3398 | |
|
3334 | 3399 | def magic_quickref(self,arg): |
|
3335 | 3400 | """ Show a quick reference sheet """ |
@@ -252,3 +252,39 b' class TestMagicRun(object):' | |||
|
252 | 252 | |
|
253 | 253 | def teardown(self): |
|
254 | 254 | self.tmpfile.close() |
|
255 | ||
|
256 | # Multiple tests for clipboard pasting | |
|
257 | def test_paste(): | |
|
258 | ||
|
259 | def paste(txt): | |
|
260 | hooks.clipboard_get = lambda : txt | |
|
261 | _ip.magic('paste') | |
|
262 | ||
|
263 | # Inject fake clipboard hook but save original so we can restore it later | |
|
264 | hooks = _ip.IP.hooks | |
|
265 | user_ns = _ip.user_ns | |
|
266 | original_clip = hooks.clipboard_get | |
|
267 | ||
|
268 | try: | |
|
269 | # Run tests with fake clipboard function | |
|
270 | user_ns.pop('x', None) | |
|
271 | paste('x=1') | |
|
272 | yield (nt.assert_equal, user_ns['x'], 1) | |
|
273 | ||
|
274 | user_ns.pop('x', None) | |
|
275 | paste('>>> x=2') | |
|
276 | yield (nt.assert_equal, user_ns['x'], 2) | |
|
277 | ||
|
278 | paste(""" | |
|
279 | >>> x = [1,2,3] | |
|
280 | >>> y = [] | |
|
281 | >>> for i in x: | |
|
282 | ... y.append(i**2) | |
|
283 | ... | |
|
284 | """) | |
|
285 | yield (nt.assert_equal, user_ns['x'], [1,2,3]) | |
|
286 | yield (nt.assert_equal, user_ns['y'], [1,4,9]) | |
|
287 | ||
|
288 | finally: | |
|
289 | # Restore original hook | |
|
290 | hooks.clipboard_get = original_clip |
General Comments 0
You need to be logged in to leave comments.
Login now