Skip to content

Commit

Permalink
# This is a combination of 17 commits.
Browse files Browse the repository at this point in the history
# This is the 1st commit message:

move from print_err to logging

# This is the commit message #2:

change sanity check message

# This is the commit message #3:

flake8 linting

# This is the commit message #4:

flake8 linting

# This is the commit message #5:

pylint updates

# This is the commit message #6:

pylint updates

# This is the commit message #7:

pylint updates for errors.py

# This is the commit message #8:

update changes from #344

# This is the commit message #9:

update formatting

# This is the commit message #10:

update formatting

# This is the commit message #11:

add consuructor to class

# This is the commit message #12:

refactor logging levels and messages

# This is the commit message #13:

refactor logging levels and messages

# This is the commit message #14:

pylint recomendations

# This is the commit message #15:

flake8 formating

# This is the commit message #16:

flake8 formating

# This is the commit message #17:

change logging to print
  • Loading branch information
Kimaru thagana authored and gleitz committed Apr 14, 2021
1 parent c27461d commit d44f7d5
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 40 deletions.
2 changes: 2 additions & 0 deletions howdoi/errors.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@


class GoogleValidationError(Exception):
pass

Expand Down
89 changes: 49 additions & 40 deletions howdoi/howdoi.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
######################################################

import gc

gc.disable()
import argparse
import json
Expand All @@ -22,6 +23,7 @@

from multiprocessing import Pool

import logging
import appdirs
import requests

Expand All @@ -41,27 +43,14 @@
from howdoi import __version__
from howdoi.errors import GoogleValidationError, BingValidationError, DDGValidationError


# rudimentary standardized 3-level log output
def _print_err(err):
print(f'[ERROR] {err}')


_print_ok = print # noqa: E305


def _print_dbg(err):
print(f'[DEBUG] {err}')


logging.basicConfig(format='%(levelname)s: %(message)s')
if os.getenv('HOWDOI_DISABLE_SSL'): # Set http instead of https
SCHEME = 'http://'
VERIFY_SSL_CERTIFICATE = False
else:
SCHEME = 'https://'
VERIFY_SSL_CERTIFICATE = True


SUPPORTED_SEARCH_ENGINES = ('google', 'bing', 'duckduckgo')

URL = os.getenv('HOWDOI_URL') or 'stackoverflow.com'
Expand All @@ -72,7 +61,7 @@ def _print_dbg(err):
('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/536.5 (KHTML, like Gecko) '
'Chrome/19.0.1084.46 Safari/536.5'),
('Mozilla/5.0 (Windows; Windows NT 6.1) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.46'
'Safari/536.5'), )
'Safari/536.5'),)
SEARCH_URLS = {
'bing': SCHEME + 'www.bing.com/search?q=site:{0}%20{1}&hl=en',
'google': SCHEME + 'www.google.com/search?q=site:{0}%20{1}&hl=en',
Expand Down Expand Up @@ -115,11 +104,12 @@ def _print_dbg(err):
STASH_EMPTY = 'empty'

if os.getenv('HOWDOI_DISABLE_CACHE'):
cache = NullCache() # works like an always empty cache
# works like an always empty cache
cache = NullCache() # pylint: disable=C0103
else:
cache = FileSystemCache(CACHE_DIR, CACHE_ENTRY_MAX, default_timeout=0)
cache = FileSystemCache(CACHE_DIR, CACHE_ENTRY_MAX, default_timeout=0) # pylint: disable=C0103

howdoi_session = requests.session()
howdoi_session = requests.session() # pylint: disable=C0103


class BlockError(RuntimeError):
Expand Down Expand Up @@ -187,8 +177,8 @@ def _get_result(url):
proxies=get_proxies(),
verify=VERIFY_SSL_CERTIFICATE).text
except requests.exceptions.SSLError as error:
_print_err('Encountered an SSL Error. Try using HTTP instead of '
'HTTPS by setting the environment variable "HOWDOI_DISABLE_SSL".\n')
logging.error('Encountered an SSL Error. Try using HTTP instead of '
'HTTPS by setting the environment variable "HOWDOI_DISABLE_SSL".\n')
raise error


Expand Down Expand Up @@ -277,8 +267,8 @@ def _get_links(query):

result = _get_result(search_url.format(URL, url_quote(query)))
if _is_blocked(result):
_print_err('Unable to find an answer because the search engine temporarily blocked the request. '
'Please wait a few minutes or select a different search engine.')
logging.error('Unable to find an answer because the search engine temporarily blocked the request. '
'Please wait a few minutes or select a different search engine.')
raise BlockError("Temporary block by search engine")

html = pq(result)
Expand Down Expand Up @@ -334,12 +324,15 @@ def _get_questions(links):
return [link for link in links if _is_question(link)]


def _get_answer(args, link):
def _get_answer(args, link): # pylint: disable=too-many-branches
cache_key = link
page = cache.get(link) # pylint: disable=assignment-from-none
if not page:
print(f'Fetching page: {link}')
page = _get_result(link + '?answertab=votes')
cache.set(cache_key, page)
else:
print(f'Using cached page: {link}')

html = pq(page)

Expand All @@ -356,8 +349,10 @@ def _get_answer(args, link):
answer_body_cls = ".post-text"

if not instructions and not args['all']:
print('No code sample found, returning entire answer')
text = get_text(first_answer.find(answer_body_cls).eq(0))
elif args['all']:
print('Returning entire answer')
texts = []
for html_tag in first_answer.items(f'{answer_body_cls} > *'):
current_text = get_text(html_tag)
Expand All @@ -370,6 +365,7 @@ def _get_answer(args, link):
else:
text = _format_output(args, get_text(instructions.eq(0)))
if text is None:
print(f'{RED}Answer was empty')
text = NO_ANSWER_MSG
text = text.strip()
return text
Expand Down Expand Up @@ -411,6 +407,10 @@ def _get_answers(args):
initial_pos = args['pos'] - 1
final_pos = initial_pos + args['num_answers']
question_links = question_links[initial_pos:final_pos]
search_engine = os.getenv('HOWDOI_SEARCH_ENGINE', 'google')

print(f'{URL} links found on {search_engine}: {len(question_links)}')
print(f'Answers requested: {args["num_answers"]} starting at position: {initial_pos}')

with Pool() as pool:
answers = pool.starmap(
Expand All @@ -421,6 +421,7 @@ def _get_answers(args):
for idx, _ in enumerate(answers):
answers[idx]['position'] = idx + 1

print(f'Total answers returned: {len(answers)}')
return answers


Expand Down Expand Up @@ -512,7 +513,7 @@ def print_stash(stash_list=None):
stash_list = ['\nSTASH LIST:']
commands = keep_utils.read_commands()
if commands is None or len(commands.items()) == 0:
print(f'No commands found in stash. Add a command with "howdoi --{STASH_SAVE} <query>".')
logging.error('No commands found in stash. Add a command with "howdoi --%s <query>".', STASH_SAVE)
return
for _, fields in commands.items():
stash_list.append(format_stash_item(fields))
Expand All @@ -534,9 +535,9 @@ def _stash_remove(cmd_key, title):
commands = keep_utils.read_commands()
if commands is not None and cmd_key in commands:
keep_utils.remove_command(cmd_key)
print(f'\n{BOLD}{GREEN}"{title}" removed from stash.{END_FORMAT}\n')
print(f'\n{BOLD}{GREEN} {title} removed from stash.{END_FORMAT}\n')
else:
print(f'\n{BOLD}{RED}"{title}" not found in stash.{END_FORMAT}\n')
print(f'\n {BOLD}{GREEN} {title} not found in stash.{END_FORMAT}\n')


def _stash_save(cmd_key, title, answer):
Expand Down Expand Up @@ -578,12 +579,15 @@ def howdoi(raw_query):
res = cache.get(cache_key) # pylint: disable=assignment-from-none

if res:
logging.info('Using cached response (add -C to clear the cache)')
return _parse_cmd(args, res)

print(f'Fetching answers for query: {args["query"]}')

try:
res = _get_answers(args)
if not res:
res = {'error': 'Sorry, couldn\'t find any help with that topic\n'}
if not res and not args["explain"]: # only set message when they havent set the -x flag
res = {'error': 'Sorry, couldn\'t find any help with that topic\n(use --explain to learn why)'}
cache.set(cache_key, res)
except (RequestsConnectionError, SSLError):
res = {f'error: Unable to reach {args["search_engine"]}. Do you need to use a proxy?\n'}
Expand Down Expand Up @@ -611,6 +615,7 @@ def get_parser():
parser.add_argument('-a', '--all', help='display the full text of the answer', action='store_true')
parser.add_argument('-l', '--link', help='display only the answer link', action='store_true')
parser.add_argument('-c', '--color', help='enable colorized output', action='store_true')
parser.add_argument('-x', '--explain', help='explain how answer was chosen', action='store_true')
parser.add_argument('-C', '--clear-cache', help='clear the cache',
action='store_true')
parser.add_argument('-j', '--json', help='return answers in raw json format', dest='json_output',
Expand Down Expand Up @@ -640,9 +645,9 @@ def _sanity_check(test_query=None):
error_result = b"Sorry, couldn't find any help with that topic\n"

if _clear_cache():
_print_ok('Cache cleared successfully')
print(f'{GREEN}Cache cleared successfully')
else:
_print_err('Clearing cache failed')
logging.error('Clearing cache failed')

google_args = vars(parser.parse_args(test_query))
google_args['search_engine'] = 'google'
Expand Down Expand Up @@ -685,7 +690,7 @@ def prompt_stash_remove(args, stash_list, view_stash=True):
if user_input == 0:
return
if user_input < 1 or user_input > last_index:
print(f'\n{RED}Input index is invalid.{END_FORMAT}')
logging.error('\n%sInput index is invalid.%s', RED, END_FORMAT)
prompt_stash_remove(args, stash_list, False)
return
cmd = stash_list[user_input - 1]
Expand All @@ -694,7 +699,7 @@ def prompt_stash_remove(args, stash_list, view_stash=True):
_stash_remove(cmd_key, cmd_name)
return
except ValueError:
print(f'\n{RED}Invalid input. Must specify index of command.{END_FORMAT}')
logging.error('\n %s Invalid input. Must specify index of command. %s', RED, END_FORMAT)
prompt_stash_remove(args, stash_list, False)
return

Expand All @@ -706,15 +711,15 @@ def perform_sanity_check():
try:
_sanity_check()
except GoogleValidationError:
_print_err('Google query failed')
logging.error('Google query failed')
return -1
except BingValidationError:
_print_err('Bing query failed')
logging.error('Bing query failed')
return -1
except DDGValidationError:
_print_err('DuckDuckGo query failed')
logging.error('DuckDuckGo query failed')
return -1
print('Ok')
print(f'{GREEN}Sanity check passed')
return 0


Expand All @@ -723,19 +728,23 @@ def command_line_runner(): # pylint: disable=too-many-return-statements,too-man
args = vars(parser.parse_args())

if args['version']:
_print_ok(__version__)
print(__version__)
return

if args['explain']:
logging.getLogger().setLevel(logging.INFO)
print('Version: %s', __version__)

if args['sanity_check']:
sys.exit(
perform_sanity_check()
)

if args['clear_cache']:
if _clear_cache():
_print_ok('Cache cleared successfully')
print(f'{GREEN}Cache cleared successfully')
else:
_print_err('Clearing cache failed')
logging.error('Clearing cache failed')

if args[STASH_VIEW]:
print_stash()
Expand All @@ -762,7 +771,7 @@ def command_line_runner(): # pylint: disable=too-many-return-statements,too-man
args['color'] = True

if not args['search_engine'] in SUPPORTED_SEARCH_ENGINES:
_print_err('Unsupported engine.\nThe supported engines are: %s' % ', '.join(SUPPORTED_SEARCH_ENGINES))
logging.error('Unsupported engine.\nThe supported engines are: %s' ', '.join(SUPPORTED_SEARCH_ENGINES))
return
if args['search_engine'] != 'google':
os.environ['HOWDOI_SEARCH_ENGINE'] = args['search_engine']
Expand Down

0 comments on commit d44f7d5

Please sign in to comment.