Skip to content

Commit

Permalink
Prepared files for first release
Browse files Browse the repository at this point in the history
  • Loading branch information
shobrook committed Mar 31, 2018
1 parent 934f042 commit 61bb982
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.DS_Store
__pycache__/
*.pyc
*.egg
venv
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2018 Jonathan Shobrook

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
recursive-include rebound *
include LICENSE README.rst requirements.txt
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,30 @@
# Rebound
A command-line tool that automatically searches Stack Overflow when you get an error.
Rebound automatically displays Stack Overflow search results in your terminal when you get a compiler error. Just use the `rebound` command before the file you want to execute.

[![placeholder demo](https://asciinema.org/a/ADAlkK9BkqxQMkYY2wZFC3CCz)]

## Installation

You can install rebound with pip (homebrew coming soon):

`$ pip install rebound-cli`

Requires Python 2.0 or higher. OS X, Linux, and Windows are all supported.

## Usage

Compiling a file with rebound is as simple as doing it normally. Just run (without the brackets):

`$ rebound [file_name]`

This will execute the file, catch any compiler errors, and prompt you to browse related Stack Overflow questions/answers. Rebound currently supports Python and NodeJS files. Support for Ruby and Java is coming soon!

## Contributing

Rebound is written in Python and built on Urwid. Beautiful Soup is used to scrape Stack Overflow content and subprocess is used to catch compiler errors.

To make a contribution, clone the repo and create a new branch, make your changes and then submit a pull request. If you've discovered a bug or have a feature request, create an issue and tag it respectively.

## Acknowledgements

Special thanks to @alichtman for providing helpful feedback.
49 changes: 49 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
Rebound
=======

Rebound automatically displays Stack Overflow search results in your
terminal when you get a compiler error. Just use the ``rebound`` command
before the file you want to execute.

[|placeholder demo|]

Installation
------------

You can install rebound with pip (homebrew coming soon):

``$ pip install rebound-cli``

Requires Python 2.0 or higher. OS X, Linux, and Windows are all
supported.

Usage
-----

Compiling a file with rebound is as simple as doing it normally. Just
run (without the brackets):

``$ rebound [file_name]``

This will execute the file, catch any compiler errors, and prompt you to
browse related Stack Overflow questions/answers. Rebound currently
supports Python and NodeJS files. Support for Ruby and Java is coming
soon!

Contributing
------------

Rebound is written in Python and built on Urwid. Beautiful Soup is used
to scrape Stack Overflow content and subprocess is used to catch
compiler errors.

To make a contribution, clone the repo and create a new branch, make
your changes and then submit a pull request. If you’ve discovered a bug
or have a feature request, create an issue and tag it respectively.

Acknowledgements
----------------

Special thanks to @alichtman for providing helpful feedback.

.. |placeholder demo| image:: https://asciinema.org/a/ADAlkK9BkqxQMkYY2wZFC3CCz
5 changes: 5 additions & 0 deletions rebound/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import sys
from rebound import main

if __name__ == "__main__":
main(sys.argv[1:])
19 changes: 10 additions & 9 deletions app/rebound.py → rebound/rebound.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
"""
Name: Rebound
Version: 1.0
Version: 1.1
Description: Automatically displays Stack Overflow search results when you get an error, inside the terminal
Author: @shobrook
"""

import sys
import os
import utilities as util


def rebound(command):
def main(command):
language = util.get_language(command[0].lower()) # Gets the language name
if language == "": # Unknown language
sys.stdout.write("\n" + util.RED + "Sorry, Rebound doesn't support this file type." + util.END)
if language == '': # Unknown language
sys.stdout.write("\n%s%s%s" % (util.RED, "Sorry, Rebound doesn't support this file type.", util.END))
return

util.get_question_and_answers("https://stackoverflow.com/questions/39182074/urwid-colored-text-simplified")

output, error = util.execute([language] + command[0:]) # Executes the command and pipes stdout
error_msg = util.get_error_message(error, language) # Prepares error message for search

Expand All @@ -25,11 +26,11 @@ def rebound(command):

if search_results != []:
if captcha:
sys.stdout.write("\n" + util.RED + "Sorry, Stack Overflow blocked our request. Try again in a minute." + util.END)
sys.stdout.write("\n%s%s%s" % (util.RED, "Sorry, Stack Overflow blocked our request. Try again in a minute.", util.END))
return
elif util.confirm("\nDisplay Stack Overflow results?"):
return util.App(search_results)
else:
sys.stdout.write("\n" + util.RED + "No Stack Overflow results found." + util.END)

rebound(sys.argv[1:])
sys.stdout.write("\n%s%s%s" % (util.RED, "No Stack Overflow results found.", util.END))
else:
sys.stdout.write("\n%s%s%s" % (util.CYAN, "No error detected :)", util.END))
56 changes: 31 additions & 25 deletions app/utilities.py → rebound/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@


def read(pipe, funcs):
"""Reads piped output to a queue and list."""
"""Reads and pushes piped output to a queue and list."""
for line in iter(pipe.readline, b''):
for func in funcs:
func(line.decode("utf-8"))
Expand All @@ -49,6 +49,7 @@ def write(get):
def execute(command):
"""Executes a given command and clones stdout/err to both variables and the
terminal (in real-time)."""
# TODO: Handle nonexistent files
process = Popen(
command,
cwd=None,
Expand Down Expand Up @@ -100,20 +101,20 @@ def get_language(file_path):
elif ".java" in file_path:
return "java"
else:
return "" # Unknown language
return '' # Unknown language


def get_error_message(error, language):
"""Filters the traceback from stderr and returns only the error message."""
if error == "":
if error == '':
return None
elif language == "python":
if any(e in error for e in ["KeyboardInterrupt", "SystemExit", "GeneratorExit"]): # Non-compiler errors
return None
else:
return error.split("\n")[-2][1:]
return error.split('\n')[-2][1:]
elif language == "node":
return error.split("\n")[4][1:]
return error.split('\n')[4][1:]
elif language == "ruby":
return # TODO
elif language == "java":
Expand Down Expand Up @@ -169,7 +170,7 @@ def souper(url):

def search_stackoverflow(query):
"""Wrapper function for get_search_results."""
soup = souper(SO_URL + "/search?pagesize=50&q=%s" % query.replace(" ", "+"))
soup = souper(SO_URL + "/search?pagesize=50&q=%s" % query.replace(' ', '+'))

# TODO: Randomize the user agent

Expand All @@ -185,23 +186,26 @@ def get_question_and_answers(url):

soup = souper(url)

question_title = soup.find_all("a", class_="question-hyperlink")[0].get_text()
question_stats = soup.find_all("span", class_="vote-count-post")[0].get_text() # No. of votes
if soup == None:
return (None, None, None, None)
else:
question_title = soup.find_all('a', class_="question-hyperlink")[0].get_text()
question_stats = soup.find_all("span", class_="vote-count-post")[0].get_text() # No. of votes

try:
# Votes, submission date, view count, date of last activity
question_stats = question_stats + " Votes | " + (((soup.find_all("div", class_="module question-stats")[0].get_text()).replace("\n", " ")).replace(" "," | "))
except IndexError:
question_stats = "Could not load statistics."
try:
# Votes, submission date, view count
question_stats = question_stats + " Votes | " + "|".join((((soup.find_all("div", class_="module question-stats")[0].get_text()).replace("\n", " ")).replace(" "," | ")).split("|")[:2])
except IndexError:
question_stats = "Could not load statistics."

question_desc = (soup.find_all("div", class_="post-text")[0]).get_text()
question_stats = ' '.join(question_stats.split())
question_desc = (soup.find_all("div", class_="post-text")[0]).get_text()
question_stats = ' '.join(question_stats.split())

answers = [answer.get_text() for answer in soup.find_all("div", class_="post-text")][1:]
if len(answers) == 0:
answers.append(urwid.Text("No answers for this question."))
answers = [answer.get_text() for answer in soup.find_all("div", class_="post-text")][1:]
if len(answers) == 0:
answers.append(urwid.Text("No answers for this question."))

return question_title, question_desc, question_stats, answers
return question_title, question_desc, question_stats, answers


## INTERFACE ##
Expand All @@ -226,8 +230,10 @@ class App(object):
def __init__(self, search_results):
self.search_results = search_results
self.palette = [
('menu', 'black', 'light cyan', 'standout'),
('reveal focus', 'black', 'light cyan', 'standout')
("title", "light cyan,underline", "default", "standout"),
("stats", "light green", "default", "standout"),
("menu", "black", "light cyan", "standout"),
("reveal focus", "black", "light cyan", "standout")
]
self.menu = urwid.Text([
u'\n',
Expand Down Expand Up @@ -297,9 +303,9 @@ def __stylize_title(self, search_result):


def __stylize_question(self, title, desc, stats):
new_title = urwid.AttrMap(urwid.Text(("light cyan", u"%s\n" % title)), "underline")
new_title = urwid.Text(("title", u"%s\n" % title))
new_desc = urwid.Text(u"%s\n" % desc) # TODO: Highlight code blocks
new_stats = urwid.Text(("light green", u"%s\n" % stats))
new_stats = urwid.Text(("stats", u"%s\n" % stats))

return [new_title, new_desc, new_stats]

Expand All @@ -313,8 +319,8 @@ def __stylize_answer(self, answer):

def confirm(question):
"""Prompts a given question and handles user input."""
valid = {"yes": True, "y": True, "ye": True,
"no": False, "n": False, "": True}
valid = {"yes": True, 'y': True, "ye": True,
"no": False, 'n': False, '': True}
prompt = " [Y/n] "

while True:
Expand Down
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
BeautifulSoup4
requests
urllib3
urwid
43 changes: 43 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
try: from setuptools import setup
except ImportError: from distutils.core import setup
from codecs import open
import sys

if sys.version_info[:3] < (2, 0, 0):
sys.stdout.write("Python 1 is not supported.")
sys.exit(1)

with open("README.rst", encoding="utf-8") as file:
readme = file.read()

setup(
name="rebound-cli",
version="1.0a1",
description="Automatically displays Stack Overflow results when you get a compiler error",
long_description=readme,
url="https://github.com/shobrook/rebound",
author="shobrook",
author_email="shobrookj@gmail.com",
classifiers=[
"Development Status :: 3 - Alpha",
"Environment :: Console",
"Intended Audience :: Developers",
"Topic :: Software Development",
"Topic :: Software Development :: Debuggers",
"Natural Language :: English",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python"
],
keywords="stackoverflow stack overflow debug debugging error-handling compile errors error message cli",
include_package_data=True,
packages=["rebound"],
#data_files=[("rebound", ["rebound/user_agents.txt"])],
entry_points={"console_scripts": ["rebound = rebound.rebound:main"]},
install_requires=["BeautifulSoup4", "requests", "urllib3" "urwid"],
requires=["BeautifulSoup4", "requests", "urllib3" "urwid"],
python_requires=">=3", # NOTE: This will change
license="MIT",
project_urls={
"Bug Reports": "https://github.com/shobrook/rebound/issues",
}
)

0 comments on commit 61bb982

Please sign in to comment.