Skip to content

Commit ac630cc

Browse files
committed
Move pasting logic out of Repl
Signed-off-by: Sebastian Ramacher <sebastian+dev@ramacher.at>
1 parent ad8958f commit ac630cc

File tree

2 files changed

+127
-79
lines changed

2 files changed

+127
-79
lines changed

bpython/paste.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# encoding: utf-8
2+
3+
# The MIT License
4+
#
5+
# Copyright (c) 2014-2015 Sebastian Ramacher
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in
15+
# all copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
# THE SOFTWARE.
24+
25+
from locale import getpreferredencoding
26+
from six.moves.urllib_parse import quote as urlquote, urljoin, urlparse
27+
from string import Template
28+
import errno
29+
import requests
30+
import subprocess
31+
import unicodedata
32+
33+
from bpython.translations import _
34+
35+
36+
class PasteFailed(Exception):
37+
pass
38+
39+
40+
class PastePinnwand(object):
41+
def __init__(self, url, expiry, show_url, removal_url):
42+
self.url = url
43+
self.expiry = expiry
44+
self.show_url = show_url
45+
self.removal_url = removal_url
46+
47+
def paste(self, s):
48+
"""Upload to pastebin via json interface."""
49+
50+
url = urljoin(self.url, '/json/new')
51+
payload = {
52+
'code': s,
53+
'lexer': 'pycon',
54+
'expiry': self.expiry
55+
}
56+
57+
try:
58+
response = requests.post(url, data=payload, verify=True)
59+
response.raise_for_status()
60+
except requests.exceptions.RequestException as exc:
61+
raise PasteFailed(exc.message)
62+
63+
data = response.json()
64+
65+
paste_url_template = Template(self.show_url)
66+
paste_id = urlquote(data['paste_id'])
67+
paste_url = paste_url_template.safe_substitute(paste_id=paste_id)
68+
69+
removal_url_template = Template(self.removal_url)
70+
removal_id = urlquote(data['removal_id'])
71+
removal_url = removal_url_template.safe_substitute(
72+
removal_id=removal_id)
73+
74+
return (paste_url, removal_url)
75+
76+
77+
class PasteHelper(object):
78+
def __init__(self, executable):
79+
self.executable = executable
80+
81+
def paste(self, s):
82+
"""Call out to helper program for pastebin upload."""
83+
84+
try:
85+
helper = subprocess.Popen('',
86+
executable=self.executable,
87+
stdin=subprocess.PIPE,
88+
stdout=subprocess.PIPE)
89+
helper.stdin.write(s.encode(getpreferredencoding()))
90+
output = helper.communicate()[0].decode(getpreferredencoding())
91+
paste_url = output.split()[0]
92+
except OSError as e:
93+
if e.errno == errno.ENOENT:
94+
raise PasteFailed(_('Helper program not found.'))
95+
else:
96+
raise PasteFailed(_('Helper program could not be run.'))
97+
98+
if helper.returncode != 0:
99+
raise PasteFailed(_('Helper program returned non-zero exit '
100+
'status %d.' % (helper.returncode, )))
101+
102+
if not paste_url:
103+
raise PasteFailed(_('No output from helper program.'))
104+
else:
105+
parsed_url = urlparse(paste_url)
106+
if (not parsed_url.scheme
107+
or any(unicodedata.category(c) == 'Cc'
108+
for c in paste_url)):
109+
raise PasteFailed(_('Failed to recognize the helper '
110+
'program\'s output as an URL.'))
111+
112+
return paste_url,

bpython/repl.py

Lines changed: 15 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,21 @@
2424
# THE SOFTWARE.
2525

2626
import code
27-
import errno
2827
import inspect
2928
import io
3029
import os
3130
import pkgutil
3231
import pydoc
33-
import requests
3432
import shlex
3533
import subprocess
3634
import sys
3735
import tempfile
3836
import textwrap
3937
import time
4038
import traceback
41-
import unicodedata
4239
from itertools import takewhile
4340
from locale import getpreferredencoding
44-
from string import Template
4541
from six import itervalues
46-
from six.moves.urllib_parse import quote as urlquote, urljoin, urlparse
4742

4843
from pygments.token import Token
4944

@@ -53,6 +48,7 @@
5348
from bpython.clipboard import get_clipboard, CopyFailed
5449
from bpython.formatter import Parenthesis
5550
from bpython.history import History
51+
from bpython.paste import PasteHelper, PastePinnwand, PasteFailed
5652
from bpython.translations import _, ngettext
5753

5854

@@ -386,6 +382,13 @@ def __init__(self, interp, config):
386382

387383
self.completers = autocomplete.get_default_completer(
388384
config.autocomplete_mode)
385+
if self.config.pastebin_helper:
386+
self.paster = PasteHelper(self.config.pastebin_helper)
387+
else:
388+
self.paster = PastePinnwand(self.config.pastebin_url,
389+
self.config.pastebin_expiry,
390+
self.config.pastebin_show_url,
391+
self.config.pastebin_removal_url)
389392

390393
@property
391394
def ps1(self):
@@ -761,91 +764,24 @@ def do_pastebin(self, s):
761764
self.prev_removal_url), 10)
762765
return self.prev_pastebin_url
763766

764-
if self.config.pastebin_helper:
765-
return self.do_pastebin_helper(s)
766-
else:
767-
return self.do_pastebin_json(s)
768-
769-
def do_pastebin_json(self, s):
770-
"""Upload to pastebin via json interface."""
771-
772-
url = urljoin(self.config.pastebin_url, '/json/new')
773-
payload = {
774-
'code': s,
775-
'lexer': 'pycon',
776-
'expiry': self.config.pastebin_expiry
777-
}
778767

779768
self.interact.notify(_('Posting data to pastebin...'))
780769
try:
781-
response = requests.post(url, data=payload, verify=True)
782-
response.raise_for_status()
783-
except requests.exceptions.RequestException as exc:
784-
self.interact.notify(_('Upload failed: %s') % (exc, ))
770+
paste_url, removal_url = self.paster.paste(s)
771+
except PasteFailed as e:
772+
self.interact.notify(_('Upload failed: %s') % e)
785773
return
786774

787775
self.prev_pastebin_content = s
788-
data = response.json()
789-
790-
paste_url_template = Template(self.config.pastebin_show_url)
791-
paste_id = urlquote(data['paste_id'])
792-
paste_url = paste_url_template.safe_substitute(paste_id=paste_id)
793-
794-
removal_url_template = Template(self.config.pastebin_removal_url)
795-
removal_id = urlquote(data['removal_id'])
796-
removal_url = removal_url_template.safe_substitute(
797-
removal_id=removal_id)
798-
799776
self.prev_pastebin_url = paste_url
800777
self.prev_removal_url = removal_url
801-
self.interact.notify(_('Pastebin URL: %s - Removal URL: %s') %
802-
(paste_url, removal_url), 10)
803-
804-
return paste_url
805778

806-
def do_pastebin_helper(self, s):
807-
"""Call out to helper program for pastebin upload."""
808-
self.interact.notify(_('Posting data to pastebin...'))
809-
810-
try:
811-
helper = subprocess.Popen('',
812-
executable=self.config.pastebin_helper,
813-
stdin=subprocess.PIPE,
814-
stdout=subprocess.PIPE)
815-
helper.stdin.write(s.encode(getpreferredencoding()))
816-
output = helper.communicate()[0].decode(getpreferredencoding())
817-
paste_url = output.split()[0]
818-
except OSError as e:
819-
if e.errno == errno.ENOENT:
820-
self.interact.notify(_('Upload failed: '
821-
'Helper program not found.'))
822-
else:
823-
self.interact.notify(_('Upload failed: '
824-
'Helper program could not be run.'))
825-
return
826-
827-
if helper.returncode != 0:
828-
self.interact.notify(_('Upload failed: '
829-
'Helper program returned non-zero exit '
830-
'status %d.' % (helper.returncode, )))
831-
return
832-
833-
if not paste_url:
834-
self.interact.notify(_('Upload failed: '
835-
'No output from helper program.'))
836-
return
779+
if removal_url is not None:
780+
self.interact.notify(_('Pastebin URL: %s - Removal URL: %s') %
781+
(paste_url, removal_url), 10)
837782
else:
838-
parsed_url = urlparse(paste_url)
839-
if (not parsed_url.scheme
840-
or any(unicodedata.category(c) == 'Cc'
841-
for c in paste_url)):
842-
self.interact.notify(_("Upload failed: "
843-
"Failed to recognize the helper "
844-
"program's output as an URL."))
845-
return
783+
self.interact.notify(_('Pastebin URL: %s') % (paste_url, ), 10)
846784

847-
self.prev_pastebin_content = s
848-
self.interact.notify(_('Pastebin URL: %s') % (paste_url, ), 10)
849785
return paste_url
850786

851787
def push(self, s, insert_into_history=True):

0 commit comments

Comments
 (0)