Index: Bindings.py
===================================================================
--- Bindings.py	(revision 58196)
+++ Bindings.py	(working copy)
@@ -36,6 +36,7 @@
    ('Cu_t', '<<cut>>'),
    ('_Copy', '<<copy>>'),
    ('_Paste', '<<paste>>'),
+   ('Paste Code', '<<paste-code>>'),
    ('Select _All', '<<select-all>>'),
    None,
    ('_Find...', '<<find>>'),
Index: config-keys.def
===================================================================
--- config-keys.def	(revision 58196)
+++ config-keys.def	(working copy)
@@ -11,6 +11,7 @@
 copy=<Control-Key-c> <Control-Key-C>
 cut=<Control-Key-x> <Control-Key-X>
 paste=<Control-Key-v> <Control-Key-V>
+paste-code=<Control-Shift-Key-v> <Control-Shift-Key-V>
 beginning-of-line= <Key-Home>
 center-insert=<Control-Key-l> <Control-Key-L>
 close-all-windows=<Control-Key-q>
@@ -62,6 +63,7 @@
 copy=<Alt-Key-w> <Meta-Key-w>
 cut=<Control-Key-w>
 paste=<Control-Key-y>
+paste-code=<Control-Shift-Key-Y>
 beginning-of-line=<Control-Key-a> <Key-Home>
 center-insert=<Control-Key-l>
 close-all-windows=<Control-Key-x><Control-Key-c>
@@ -113,6 +115,7 @@
 copy=<Command-Key-c>
 cut=<Command-Key-x>
 paste=<Command-Key-v>
+paste-code=<Shift-Command-Key-V>
 beginning-of-line= <Key-Home>
 center-insert=<Control-Key-l>
 close-all-windows=<Command-Key-q>
@@ -212,3 +215,4 @@
 open-window-from-file = <Command-Key-o>
 python-docs = <Key-F1>
 
+
Index: configHandler.py
===================================================================
--- configHandler.py	(revision 58196)
+++ configHandler.py	(working copy)
@@ -547,6 +547,7 @@
             '<<copy>>': ['<Control-c>', '<Control-C>'],
             '<<cut>>': ['<Control-x>', '<Control-X>'],
             '<<paste>>': ['<Control-v>', '<Control-V>'],
+            '<<paste-code>>': ['Control-Shift-v', 'Control-Shift-V'],
             '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
             '<<center-insert>>': ['<Control-l>'],
             '<<close-all-windows>>': ['<Control-q>'],
Index: EditorWindow.py
===================================================================
--- EditorWindow.py	(revision 58196)
+++ EditorWindow.py	(working copy)
@@ -132,6 +132,7 @@
         text.bind("<<cut>>", self.cut)
         text.bind("<<copy>>", self.copy)
         text.bind("<<paste>>", self.paste)
+        text.bind("<<paste-code>>", self.paste_code)
         text.bind("<<center-insert>>", self.center_insert_event)
         text.bind("<<help>>", self.help_dialog)
         text.bind("<<python-docs>>", self.python_docs)
@@ -416,6 +417,58 @@
         self.text.event_generate("<<Paste>>")
         return "break"
 
+    def paste_code(self, event):
+        original_data = self.text.clipboard_get()
+        pasted_data = self._process_paste_code(data)
+        self.text.clipboard_clear()
+        self.text.clipboard_append(pasted_data)
+        return_value = self.paste(event)
+        self.text.clipboard_clear()
+        self.text.clipboard_append(original_data)
+        return return_value
+
+    def _process_paste_code(self, code):        
+        "Override this method to change how paste_code processes pasted text"
+        extra_newline = not (code and code[-1] == '\n')
+        if extra_newline:
+            code += '\n'
+        lines = code.splitlines(True)
+
+        py_parser = PyParse.Parser(self.indentwidth, self.tabwidth)
+        py_parser.set_str(code)
+        py_parser._study1()
+
+        # The list of lines is changed in-place because some lines are skipped,
+        # since only lines found in py_parser.goodlines are processed.
+        to_be_deleted = []
+        for line_num in py_parser.goodlines:
+            if line_num >= len(lines):
+                break
+            line = self._process_pasted_good_line(lines[line_num])
+            if line is None:
+                # None means remove the line
+                to_be_deleted.append(line_num)
+            else:
+                lines[line_num] = line
+
+        # Remove lines which need to be deleted.
+        # This is done from last to first to avoid changing line indices.
+        to_be_deleted.sort()
+        to_be_deleted.reverse()
+        for line_num in to_be_deleted:
+            del lines[line_num]
+            
+        new_code = ''.join(lines)
+        if extra_newline:
+            new_code = new_code[:-1]
+        return new_code
+
+    def _process_pasted_good_line(self, line):
+        "Override this method to change how paste_code processes single lines"
+        if line[:4] in ['>>> ', '... ']:
+            line = line[4:]
+        return line
+
     def select_all(self, event=None):
         self.text.tag_add("sel", "1.0", "end-1c")
         self.text.mark_set("insert", "1.0")
Index: PyShell.py
===================================================================
--- PyShell.py	(revision 58196)
+++ PyShell.py	(working copy)
@@ -1240,6 +1240,12 @@
             if not use_subprocess:
                 raise KeyboardInterrupt
 
+    def _process_pasted_good_line(self, line):
+        if not line.rstrip():
+            # remove whitespace lines
+            return None
+        return OutputWindow._process_pasted_good_line(self, line)
+
 class PseudoFile(object):
 
     def __init__(self, shell, tags, encoding=None):
