Add classes for better readability

This commit is contained in:
Tomas Korbar
2018-05-30 08:49:52 +02:00
committed by Tomas Korbar
parent a651426075
commit 7814de96d2
5 changed files with 241 additions and 215 deletions

View File

@ -36,9 +36,10 @@ Examples:
# require the dependencies # require the dependencies
from __future__ import print_function from __future__ import print_function
from cheat import sheets, sheet from cheat.sheets import Sheets
from cheat.utils import colorize from cheat.sheet import Sheet
from cheat.utils import die from cheat.utils import Utils
from cheat.configuration import Configuration
from docopt import docopt from docopt import docopt
import os import os
@ -59,6 +60,11 @@ if __name__ == '__main__':
# parse the command-line options # parse the command-line options
options = docopt(__doc__, version='cheat 2.3.1') options = docopt(__doc__, version='cheat 2.3.1')
config = Configuration()
sheets = Sheets(config.get_default_cheat_dir(),config.get_cheatpath())
sheet = Sheet(config.get_default_cheat_dir(),config.get_cheatpath(),config.get_editor())
utils = Utils(config.get_cheatcolors(),config.get_editor())
# list directories # list directories
if options['--directories']: if options['--directories']:
print("\n".join(sheets.paths())) print("\n".join(sheets.paths()))
@ -73,8 +79,8 @@ if __name__ == '__main__':
# search among the cheatsheets # search among the cheatsheets
elif options['--search']: elif options['--search']:
print(colorize(sheets.search(options['<keyword>'])), end="") print(utils.colorize(sheets.search(options['<keyword>'])), end="")
# print the cheatsheet # print the cheatsheet
else: else:
print(colorize(sheet.read(options['<cheatsheet>'])), end="") print(utils.colorize(sheet.read(options['<cheatsheet>'])), end="")

View File

@ -1,5 +1,5 @@
import os import os
from cheat.utils import warn from cheat.utils import Utils
import json import json
class Configuration: class Configuration:
@ -15,12 +15,12 @@ class Configuration:
try: try:
merged_config.update(self._read_configuration_file('/etc/cheat')) merged_config.update(self._read_configuration_file('/etc/cheat'))
except Exception: except Exception:
warn('error while parsing global configuration') Utils.warn('error while parsing global configuration')
try: try:
merged_config.update(self._read_configuration_file(os.path.expanduser(os.path.join('~','.config','cheat','cheat')))) merged_config.update(self._read_configuration_file(os.path.expanduser(os.path.join('~','.config','cheat','cheat'))))
except Exception: except Exception:
warn('error while parsing user configuration') Utils.warn('error while parsing user configuration')
merged_config.update(self._read_env_vars_config()) merged_config.update(self._read_env_vars_config())

View File

@ -1,10 +1,19 @@
import os import os
import shutil import shutil
from cheat import sheets from cheat.sheets import Sheets
from cheat.utils import die, open_with_editor from cheat.utils import Utils
def copy(current_sheet_path, new_sheet_path):
class Sheet:
def __init__(self,default_cheat_dir,cheatpath,editor_exec):
self.sheets_instance = Sheets(default_cheat_dir,cheatpath)
self.utils_instance = Utils(None,editor_exec)
def copy(self,current_sheet_path, new_sheet_path):
""" Copies a sheet to a new path """ """ Copies a sheet to a new path """
# attempt to copy the sheet to DEFAULT_CHEAT_DIR # attempt to copy the sheet to DEFAULT_CHEAT_DIR
@ -14,63 +23,63 @@ def copy(current_sheet_path, new_sheet_path):
# fail gracefully if the cheatsheet cannot be copied. This can happen if # fail gracefully if the cheatsheet cannot be copied. This can happen if
# DEFAULT_CHEAT_DIR does not exist # DEFAULT_CHEAT_DIR does not exist
except IOError: except IOError:
die('Could not copy cheatsheet for editing.') Utils.die('Could not copy cheatsheet for editing.')
def create_or_edit(sheet): def create_or_edit(self,sheet):
""" Creates or edits a cheatsheet """ """ Creates or edits a cheatsheet """
# if the cheatsheet does not exist # if the cheatsheet does not exist
if not exists(sheet): if not self.exists(sheet):
create(sheet) self.create(sheet)
# if the cheatsheet exists but not in the default_path, copy it to the # if the cheatsheet exists but not in the default_path, copy it to the
# default path before editing # default path before editing
elif exists(sheet) and not exists_in_default_path(sheet): elif self.exists(sheet) and not self.exists_in_default_path(sheet):
copy(path(sheet), os.path.join(sheets.default_path(), sheet)) self.copy(self.path(sheet), os.path.join(self.sheets_instance.default_path(), sheet))
edit(sheet) self.edit(sheet)
# if it exists and is in the default path, then just open it # if it exists and is in the default path, then just open it
else: else:
edit(sheet) self.edit(sheet)
def create(sheet): def create(self,sheet):
""" Creates a cheatsheet """ """ Creates a cheatsheet """
new_sheet_path = os.path.join(sheets.default_path(), sheet) new_sheet_path = os.path.join(self.sheets_instance.default_path(), sheet)
open_with_editor(new_sheet_path) self.utils_instance.open_with_editor(new_sheet_path)
def edit(sheet): def edit(self,sheet):
""" Opens a cheatsheet for editing """ """ Opens a cheatsheet for editing """
open_with_editor(path(sheet)) self.utils_instance.open_with_editor(self.path(sheet))
def exists(sheet): def exists(self,sheet):
""" Predicate that returns true if the sheet exists """ """ Predicate that returns true if the sheet exists """
return sheet in sheets.get() and os.access(path(sheet), os.R_OK) return sheet in self.sheets_instance.get() and os.access(self.path(sheet), os.R_OK)
def exists_in_default_path(sheet): def exists_in_default_path(self,sheet):
""" Predicate that returns true if the sheet exists in default_path""" """ Predicate that returns true if the sheet exists in default_path"""
default_path_sheet = os.path.join(sheets.default_path(), sheet) default_path_sheet = os.path.join(self.sheets_instance.default_path(), sheet)
return sheet in sheets.get() and os.access(default_path_sheet, os.R_OK) return sheet in self.sheets_instance.get() and os.access(default_path_sheet, os.R_OK)
def is_writable(sheet): def is_writable(self,sheet):
""" Predicate that returns true if the sheet is writeable """ """ Predicate that returns true if the sheet is writeable """
return sheet in sheets.get() and os.access(path(sheet), os.W_OK) return sheet in self.sheets_instance.get() and os.access(self.path(sheet), os.W_OK)
def path(sheet): def path(self,sheet):
""" Returns a sheet's filesystem path """ """ Returns a sheet's filesystem path """
return sheets.get()[sheet] return self.sheets_instance.get()[sheet]
def read(sheet): def read(self,sheet):
""" Returns the contents of the cheatsheet as a String """ """ Returns the contents of the cheatsheet as a String """
if not exists(sheet): if not self.exists(sheet):
die('No cheatsheet found for ' + sheet) Utils.die('No cheatsheet found for ' + sheet)
with open(path(sheet)) as cheatfile: with open(self.path(sheet)) as cheatfile:
return cheatfile.read() return cheatfile.read()

View File

@ -1,14 +1,21 @@
import os import os
from cheat.utils import die
from cheat.utils import highlight
from cheat.configuration import Configuration from cheat.configuration import Configuration
from cheat.utils import Utils
def default_path(): class Sheets:
def __init__(self,default_cheat_dir,cheatpath):
self.default_cheat_dir = default_cheat_dir
self.cheatpath = cheatpath
def default_path(self):
""" Returns the default cheatsheet path """ """ Returns the default cheatsheet path """
# determine the default cheatsheet dir # determine the default cheatsheet dir
default_sheets_dir = Configuration().get_default_cheat_dir() or os.path.join('~', '.cheat') default_sheets_dir = self.default_cheat_dir or os.path.join('~', '.cheat')
default_sheets_dir = os.path.expanduser(os.path.expandvars(default_sheets_dir)) default_sheets_dir = os.path.expanduser(os.path.expandvars(default_sheets_dir))
# create the DEFAULT_CHEAT_DIR if it does not exist # create the DEFAULT_CHEAT_DIR if it does not exist
@ -19,24 +26,24 @@ def default_path():
os.mkdir(default_sheets_dir) os.mkdir(default_sheets_dir)
except OSError: except OSError:
die('Could not create DEFAULT_CHEAT_DIR') Utils.die('Could not create DEFAULT_CHEAT_DIR')
# assert that the DEFAULT_CHEAT_DIR is readable and writable # assert that the DEFAULT_CHEAT_DIR is readable and writable
if not os.access(default_sheets_dir, os.R_OK): if not os.access(default_sheets_dir, os.R_OK):
die('The DEFAULT_CHEAT_DIR (' + default_sheets_dir +') is not readable.') Utils.die('The DEFAULT_CHEAT_DIR (' + default_sheets_dir +') is not readable.')
if not os.access(default_sheets_dir, os.W_OK): if not os.access(default_sheets_dir, os.W_OK):
die('The DEFAULT_CHEAT_DIR (' + default_sheets_dir +') is not writable.') Utils.die('The DEFAULT_CHEAT_DIR (' + default_sheets_dir +') is not writable.')
# return the default dir # return the default dir
return default_sheets_dir return default_sheets_dir
def get(): def get(self):
""" Assembles a dictionary of cheatsheets as name => file-path """ """ Assembles a dictionary of cheatsheets as name => file-path """
cheats = {} cheats = {}
# otherwise, scan the filesystem # otherwise, scan the filesystem
for cheat_dir in reversed(paths()): for cheat_dir in reversed(self.paths()):
cheats.update( cheats.update(
dict([ dict([
(cheat, os.path.join(cheat_dir, cheat)) (cheat, os.path.join(cheat_dir, cheat))
@ -49,44 +56,43 @@ def get():
return cheats return cheats
def paths(): def paths(self):
""" Assembles a list of directories containing cheatsheets """ """ Assembles a list of directories containing cheatsheets """
sheet_paths = [ sheet_paths = [
default_path(), self.default_path(),
'/usr/share/cheat', '/usr/share/cheat',
] ]
# merge the CHEATPATH paths into the sheet_paths # merge the CHEATPATH paths into the sheet_paths
if Configuration().get_cheatpath(): if self.cheatpath:
for path in Configuration().get_cheatpath().split(os.pathsep): for path in self.cheatpath.split(os.pathsep):
if os.path.isdir(path): if os.path.isdir(path):
sheet_paths.append(path) sheet_paths.append(path)
if not sheet_paths: if not sheet_paths:
die('The DEFAULT_CHEAT_DIR dir does not exist or the CHEATPATH is not set.') Utils.die('The DEFAULT_CHEAT_DIR dir does not exist or the CHEATPATH is not set.')
return sheet_paths return sheet_paths
def list(): def list(self):
""" Lists the available cheatsheets """ """ Lists the available cheatsheets """
sheet_list = '' sheet_list = ''
pad_length = max([len(x) for x in get().keys()]) + 4 pad_length = max([len(x) for x in self.get().keys()]) + 4
for sheet in sorted(get().items()): for sheet in sorted(self.get().items()):
sheet_list += sheet[0].ljust(pad_length) + sheet[1] + "\n" sheet_list += sheet[0].ljust(pad_length) + sheet[1] + "\n"
return sheet_list return sheet_list
def search(term): def search(self,term):
""" Searches all cheatsheets for the specified term """ """ Searches all cheatsheets for the specified term """
result = '' result = ''
lowered_term = term.lower()
for cheatsheet in sorted(get().items()): for cheatsheet in sorted(self.get().items()):
match = '' match = ''
for line in open(cheatsheet[1]): for line in open(cheatsheet[1]):
if term in line: if term in line:
match += ' ' + highlight(term, line) match += ' ' + line
if match != '': if match != '':
result += cheatsheet[0] + ":\n" + match + "\n" result += cheatsheet[0] + ":\n" + match + "\n"

View File

@ -3,9 +3,15 @@ import os
import subprocess import subprocess
import sys import sys
from cheat.configuration import Configuration class Utils:
def highlight(needle, haystack):
def __init__(self,cheatcolors,editor_executable):
self.displaycolors = cheatcolors
self.editor_executable = editor_executable
def highlight(self, needle, haystack):
""" Highlights a search term matched within a line """ """ Highlights a search term matched within a line """
# if a highlight color is not configured, exit early # if a highlight color is not configured, exit early
@ -21,14 +27,14 @@ def highlight(needle, haystack):
return haystack return haystack
# if the import succeeds, colorize the needle in haystack # if the import succeeds, colorize the needle in haystack
return haystack.replace(needle, colored(needle, os.environ.get('CHEAT_HIGHLIGHT'))); return haystack.replace(needle, colored(needle, os.environ.get('CHEAT_HIGHLIGHT')))
def colorize(sheet_content): def colorize(self,sheet_content):
""" Colorizes cheatsheet content if so configured """ """ Colorizes cheatsheet content if so configured """
# only colorize if configured to do so, and if stdout is a tty # only colorize if configured to do so, and if stdout is a tty
if not Configuration().get_cheatcolors() or not sys.stdout.isatty(): if not self.displaycolors or not sys.stdout.isatty():
return sheet_content return sheet_content
# don't attempt to colorize an empty cheatsheet # don't attempt to colorize an empty cheatsheet
@ -60,37 +66,36 @@ def colorize(sheet_content):
return highlight(sheet_content, lexer, TerminalFormatter()) return highlight(sheet_content, lexer, TerminalFormatter())
@staticmethod
def die(message): def die(message):
""" Prints a message to stderr and then terminates """ """ Prints a message to stderr and then terminates """
warn(message) Utils.warn(message)
exit(1) exit(1)
def editor(): def editor(self):
""" Determines the user's preferred editor """ """ Determines the user's preferred editor """
# determine which editor to use
editor = Configuration().get_editor()
# assert that the editor is set # assert that the editor is set
if (not editor): if (not self.editor_executable):
die( Utils.die(
'You must set a CHEAT_EDITOR, VISUAL, or EDITOR environment ' 'You must set a CHEAT_EDITOR, VISUAL, or EDITOR environment '
'variable or setting in order to create/edit a cheatsheet.' 'variable or setting in order to create/edit a cheatsheet.'
) )
return editor return self.editor_executable
def open_with_editor(filepath): def open_with_editor(self,filepath):
""" Open `filepath` using the EDITOR specified by the environment variables """ """ Open `filepath` using the EDITOR specified by the environment variables """
editor_cmd = editor().split() editor_cmd = self.editor().split()
try: try:
subprocess.call(editor_cmd + [filepath]) subprocess.call(editor_cmd + [filepath])
except OSError: except OSError:
die('Could not launch ' + editor()) Utils.die('Could not launch ' + self.editor())
@staticmethod
def warn(message): def warn(message):
""" Prints a message to stderr """ """ Prints a message to stderr """
print((message), file=sys.stderr) print((message), file=sys.stderr)