From 54e2e20beda93926a4260730f79cb49d363ba1ed Mon Sep 17 00:00:00 2001 From: Kareem Zidane Date: Thu, 9 Jan 2020 09:46:27 -0500 Subject: [PATCH] removed pexpect, fixed windows --- .gitignore | 1 + help50/__main__.py | 53 ++++++++++++++++++++++++++++++++-------------- setup.py | 4 ++-- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index a155c6c..c7545c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .* !.gitignore !.travis.yml +help50.egg* __pycache__/ diff --git a/help50/__main__.py b/help50/__main__.py index 44c567d..a527480 100644 --- a/help50/__main__.py +++ b/help50/__main__.py @@ -4,7 +4,6 @@ import re import shlex import sys -import tempfile import traceback from argparse import ArgumentParser, REMAINDER @@ -14,6 +13,12 @@ from . import __version__, internal, Error +ON_WINDOWS = os.name == "nt" + +if ON_WINDOWS: + # Fix termcolor colors on Windows + import colorama + colorama.init() def excepthook(cls, exc, tb): if (issubclass(cls, lib50.Error) or issubclass(cls, Error)) and exc.args: @@ -60,7 +65,6 @@ def main(): "students understand error messages.") parser.add_argument("-s", "--slug", help="identifier indicating from where to download helpers", default="cs50/helpers/master") parser.add_argument("-d", "--dev", help="slug will be treated as a local path, useful for developing helpers (implies --verbose)", action="store_true") - parser.add_argument("-i", "--interactive", help="read command output from stdin instead of running a command", action="store_true") parser.add_argument("-v", "--verbose", help="display the full tracebacks of any errors", action="store_true") parser.add_argument("-V", "--version", action="version", version=f"%(prog)s {__version__}") parser.add_argument("command", nargs=REMAINDER, @@ -72,25 +76,42 @@ def main(): excepthook.verbose = args.verbose + if args.command: + command = " ".join(shlex.quote(word) for word in args.command) + script = b"" + + # pty isn't supported on Windows + if ON_WINDOWS: + import subprocess + proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + for b in iter(lambda: proc.stdout.read(1), b""): + script += b + sys.stdout.buffer.write(b) + sys.stdout.flush() + else: + import pty + def master_read(fd): + nonlocal script + data = os.read(fd, 1024) + script += data + return data + + old_env = os.environ.copy() - if args.interactive: - script = sys.stdin.read() - elif args.command: - # Capture stdout and stderr from process, and print it out - with tempfile.TemporaryFile(mode="r+b") as temp: - env = os.environ.copy() # Hack to prevent some programs from wrapping their error messages - env["COLUMNS"] = "5050" - proc = pexpect.spawn(f"bash -lc \"{' '.join(shlex.quote(word) for word in args.command)}\"", env=env) - proc.logfile_read = temp - proc.interact() - proc.close() - - temp.seek(0) - script = temp.read().decode().replace("\r\n", "\n") + os.environ.update({"COLUMNS": "5050"}) + try: + # Run the process in a pseudo-terminal e.g., to preserve colored output + pty.spawn(["bash", "-lc", command], master_read) + finally: + os.environ.clear() + os.environ.update(old_env) + + script = script.decode().replace("\r\n", "\n") else: raise Error("Careful, you forgot to tell me with which command you " "need help!") + termcolor.cprint("\nAsking for help...\n", "yellow") try: diff --git a/setup.py b/setup.py index 7dcfc73..b6e8e61 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ ], license="GPLv3", description="This is help50, a command-line tool that helps students understand error messages.", - install_requires=["pexpect", "termcolor", "lib50>=1.1.10"], + install_requires=["colorama", "termcolor", "lib50>=1.1.10"], keywords="help50", name="help50", packages=["help50"], @@ -21,5 +21,5 @@ }, py_requires="3.6", url="https://github.com/cs50/help50", - version="3.0.0" + version="3.0.1" )