| 69 | |
| 70 | |
| 71 | class PasteHelper: |
| 72 | def __init__(self, executable: str) -> None: |
| 73 | self.executable = executable |
| 74 | |
| 75 | def paste(self, s: str) -> tuple[str, None]: |
| 76 | """Call out to helper program for pastebin upload.""" |
| 77 | |
| 78 | try: |
| 79 | helper = subprocess.Popen( |
| 80 | "", |
| 81 | executable=self.executable, |
| 82 | stdin=subprocess.PIPE, |
| 83 | stdout=subprocess.PIPE, |
| 84 | ) |
| 85 | assert helper.stdin is not None |
| 86 | encoding = getpreferredencoding() |
| 87 | helper.stdin.write(s.encode(encoding)) |
| 88 | output = helper.communicate()[0].decode(encoding) |
| 89 | paste_url = output.split()[0] |
| 90 | except OSError as e: |
| 91 | if e.errno == errno.ENOENT: |
| 92 | raise PasteFailed(_("Helper program not found.")) |
| 93 | else: |
| 94 | raise PasteFailed(_("Helper program could not be run.")) |
| 95 | |
| 96 | if helper.returncode != 0: |
| 97 | raise PasteFailed( |
| 98 | _( |
| 99 | "Helper program returned non-zero exit status %d." |
| 100 | % (helper.returncode,) |
| 101 | ) |
| 102 | ) |
| 103 | |
| 104 | if not paste_url: |
| 105 | raise PasteFailed(_("No output from helper program.")) |
| 106 | |
| 107 | parsed_url = urlparse(paste_url) |
| 108 | if not parsed_url.scheme or any( |
| 109 | unicodedata.category(c) == "Cc" for c in paste_url |
| 110 | ): |
| 111 | raise PasteFailed( |
| 112 | _( |
| 113 | "Failed to recognize the helper " |
| 114 | "program's output as an URL." |
| 115 | ) |
| 116 | ) |
| 117 | |
| 118 | return paste_url, None |