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

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

View File

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

View File

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

View File

@ -3,94 +3,99 @@ import os
import subprocess
import sys
from cheat.configuration import Configuration
def highlight(needle, haystack):
""" Highlights a search term matched within a line """
# if a highlight color is not configured, exit early
if not 'CHEAT_HIGHLIGHT' in os.environ:
return haystack
# otherwise, attempt to import the termcolor library
try:
from termcolor import colored
# if the import fails, return uncolored text
except ImportError:
return haystack
# if the import succeeds, colorize the needle in haystack
return haystack.replace(needle, colored(needle, os.environ.get('CHEAT_HIGHLIGHT')));
class Utils:
def colorize(sheet_content):
""" Colorizes cheatsheet content if so configured """
def __init__(self,cheatcolors,editor_executable):
self.displaycolors = cheatcolors
self.editor_executable = editor_executable
# only colorize if configured to do so, and if stdout is a tty
if not Configuration().get_cheatcolors() or not sys.stdout.isatty():
return sheet_content
# don't attempt to colorize an empty cheatsheet
if not sheet_content.strip():
return ""
def highlight(self, needle, haystack):
""" Highlights a search term matched within a line """
# otherwise, attempt to import the pygments library
try:
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import TerminalFormatter
# if a highlight color is not configured, exit early
if not 'CHEAT_HIGHLIGHT' in os.environ:
return haystack
# if the import fails, return uncolored text
except ImportError:
return sheet_content
# otherwise, attempt to colorize
first_line = sheet_content.splitlines()[0]
lexer = get_lexer_by_name('bash')
# apply syntax-highlighting if the first line is a code-fence
if first_line.startswith('```'):
sheet_content = '\n'.join(sheet_content.split('\n')[1:-2])
# otherwise, attempt to import the termcolor library
try:
lexer = get_lexer_by_name(first_line[3:])
except Exception:
pass
from termcolor import colored
return highlight(sheet_content, lexer, TerminalFormatter())
# if the import fails, return uncolored text
except ImportError:
return haystack
# if the import succeeds, colorize the needle in haystack
return haystack.replace(needle, colored(needle, os.environ.get('CHEAT_HIGHLIGHT')))
def die(message):
""" Prints a message to stderr and then terminates """
warn(message)
exit(1)
def colorize(self,sheet_content):
""" Colorizes cheatsheet content if so configured """
# only colorize if configured to do so, and if stdout is a tty
if not self.displaycolors or not sys.stdout.isatty():
return sheet_content
# don't attempt to colorize an empty cheatsheet
if not sheet_content.strip():
return ""
# otherwise, attempt to import the pygments library
try:
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import TerminalFormatter
# if the import fails, return uncolored text
except ImportError:
return sheet_content
# otherwise, attempt to colorize
first_line = sheet_content.splitlines()[0]
lexer = get_lexer_by_name('bash')
# apply syntax-highlighting if the first line is a code-fence
if first_line.startswith('```'):
sheet_content = '\n'.join(sheet_content.split('\n')[1:-2])
try:
lexer = get_lexer_by_name(first_line[3:])
except Exception:
pass
return highlight(sheet_content, lexer, TerminalFormatter())
def editor():
""" Determines the user's preferred editor """
# determine which editor to use
editor = Configuration().get_editor()
# assert that the editor is set
if (not editor):
die(
'You must set a CHEAT_EDITOR, VISUAL, or EDITOR environment '
'variable or setting in order to create/edit a cheatsheet.'
)
return editor
@staticmethod
def die(message):
""" Prints a message to stderr and then terminates """
Utils.warn(message)
exit(1)
def open_with_editor(filepath):
""" Open `filepath` using the EDITOR specified by the environment variables """
editor_cmd = editor().split()
try:
subprocess.call(editor_cmd + [filepath])
except OSError:
die('Could not launch ' + editor())
def editor(self):
""" Determines the user's preferred editor """
# assert that the editor is set
if (not self.editor_executable):
Utils.die(
'You must set a CHEAT_EDITOR, VISUAL, or EDITOR environment '
'variable or setting in order to create/edit a cheatsheet.'
)
return self.editor_executable
def warn(message):
""" Prints a message to stderr """
print((message), file=sys.stderr)
def open_with_editor(self,filepath):
""" Open `filepath` using the EDITOR specified by the environment variables """
editor_cmd = self.editor().split()
try:
subprocess.call(editor_cmd + [filepath])
except OSError:
Utils.die('Could not launch ' + self.editor())
@staticmethod
def warn(message):
""" Prints a message to stderr """
print((message), file=sys.stderr)