Merge pull request #157 from chrisallenlane/flense

Performed a large refactoring
This commit is contained in:
Chris Lane committed 2014-04-26 23:46:04 -04:00
commit 2184b963a1
105 files changed
+346 -388

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
Changelog
=========
+10 -44
View File
@@ -7,7 +7,7 @@ remember.
![The obligatory xkcd](http://imgs.xkcd.com/comics/tar.png 'The obligatory xkcd')
`cheat` depends only on python.
`cheat` depends only on `python` and `pip`.
Examples
@@ -15,9 +15,7 @@ Examples
The next time you're forced to disarm a nuclear weapon without consulting
Google, you may run:
```sh
cheat tar
```
cheat tar
You will be presented with a cheatsheet resembling:
@@ -38,7 +36,7 @@ tar -xjvf /path/to/foo.tgz
tar -cjvf /path/to/foo.tgz /path/to/foo/
```
To see what cheatsheets are availble, run `cheat` with no arguments.
To see what cheatsheets are availble, run `cheat -l`.
Note that, while `cheat` was designed primarily for *nix system administrators,
it is agnostic as to what content it stores. If you would like to use `cheat`
@@ -47,38 +45,14 @@ to store notes on your favorite cookie recipes, feel free.
Installing
==========
First install the required python dependencies with:
### Installing for all users (requires root)
sudo pip install docopt pygments
Clone this repository and `cd` into it, then run
Then, clone this repository, `cd` into it, and run:
sudo python setup.py install
### Installing in your home directory
Clone this repository and `cd` into it, then run
mkdir -p ~/bin
cp cheat ~/bin
mkdir ~/.cheat
cp cheatsheets/* ~/.cheat
### Testing
After installing for all users or in your home directory, try `cheat tar` for instance.
### Troubleshooting
In case you got an error such as:
> ImportError: No module named argparse
You're probably using python < 2.7 and you need to manually install the argparse module.
You can do this easily with pip:
```bash
sudo apt-get install python-pip
sudo pip install argparse
```
Other methods: https://pypi.python.org/pypi/argparse
Modifying Cheatsheets
=====================
@@ -118,15 +92,11 @@ Setting a CHEATPATH
You can additionally instruct `cheat` to look for cheatsheets in other
directories by exporting a `CHEATPATH` environment variable:
```bash
export CHEATPATH=/path/to/my/cheats
```
export CHEATPATH=/path/to/my/cheats
You may, of course, append multiple directories to your `CHEATPATH`:
```bash
export CHEATPATH=$CHEATPATH:/path/to/more/cheats
```
export CHEATPATH=$CHEATPATH:/path/to/more/cheats
You may view which directories are on your `CHEATPATH` with `cheat -d`.
@@ -135,18 +105,14 @@ Enabling Syntax Highlighting
`cheat` can apply syntax highlighting to your cheatsheets if so desired. To
enable this feature, set a `CHEATCOLORS` environment variable:
```bash
export CHEATCOLORS=true
```
export CHEATCOLORS=true
Creating/Editing Cheatsheets
----------------------------
Provided that you have an `EDITOR` environment variable set, you may create new
cheatsheets via:
```bash
cheat -e foo
```
cheat -e foo
If the 'foo' cheatsheet already exists, it will be opened for editing.
Executable
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/env python
"""cheat
Usage:
cheat <cheatsheet>
cheat -e <cheatsheet>
cheat -s <keyword>
cheat -l
cheat -d
cheat -v
cheat allows you to create and view interactive cheatsheets on the
command-line. It was designed to help remind *nix system
administrators of options for commands that they use frequently,
but not frequently enough to remember.
Examples:
To look up 'tar':
cheat tar
To create or edit the cheatsheet for 'foo':
cheat -e foo
Options:
-d --directories List directories on CHEATPATH
-e --edit Edit cheatsheet
-l --list List cheatsheets
-s --search Search cheatsheets for <keyword>
-v --version Print the version number
"""
# require the dependencies
from cheat import *
from cheat.utils import *
from docopt import docopt
if __name__ == '__main__':
# parse the command-line options
options = docopt(__doc__, version='cheat 2.0.0')
# list directories
if options['--directories']:
print("\n".join(sheets.paths()))
# list cheatsheets
elif options['--list']:
print(sheets.list())
# create/edit cheatsheet
elif options['--edit']:
sheet.create_or_edit(options['<cheatsheet>'])
# search among the cheatsheets
elif options['--search']:
print(colorize(sheets.search(options['<keyword>'])))
# print the cheatsheet
else:
print(colorize(sheet.read(options['<cheatsheet>'])))
-320
View File
@@ -1,320 +0,0 @@
#!/usr/bin/env python
"""
cheat.py -- cheat allows you to create and view interactive cheatsheets on the
command-line. It was designed to help remind *nix system
administrators of options for commands that they use frequently,
but not frequently enough to remember.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import sys
import argparse
import subprocess
from textwrap import dedent
DEFAULT_CHEAT_DIR = (os.environ.get('DEFAULT_CHEAT_DIR') or
os.path.join(os.path.expanduser('~'), '.cheat'))
USE_PYGMENTS = False
# NOTE remove this check if it is confirmed to work on windows
if os.name == 'posix' and 'CHEATCOLORS' in os.environ:
try:
from pygments import highlight
from pygments.util import ClassNotFound
from pygments.lexers import get_lexer_for_filename, TextLexer
from pygments.formatters import TerminalFormatter
USE_PYGMENTS = True
except ImportError:
pass
def pretty_print(filename):
"""Applies syntax highlighting to a cheatsheet and writes it to stdout"""
try:
if os.path.splitext(filename)[1]:
lexer = get_lexer_for_filename(filename)
else:
# shell is a sensible default when there is no extension
lexer = get_lexer_for_filename(filename + '.sh')
except ClassNotFound:
lexer = TextLexer()
with open(filename) as istream:
code = istream.read()
fmt = TerminalFormatter()
highlight(code, lexer, fmt, sys.stdout)
class CheatSheets(object):
"""Cheatsheets database class."""
dirs = None
sheets = None
def __init__(self):
self.dirs = self.__cheat_directories()
# verify that we have at least one cheat directory
if not self.dirs:
error_msg = ('The {default} dir does not exist'
' or the CHEATPATH var is not set.')
print >> sys.stderr, error_msg.format(default=DEFAULT_CHEAT_DIR)
exit(1)
self.sheets = self.__cheat_files()
def __cheat_directories(self):
"""Assembles a list of directories containing cheatsheets."""
default_directories = [DEFAULT_CHEAT_DIR]
try:
import cheatsheets
default_directories.append(cheatsheets.cheat_dir)
except ImportError:
pass
default = [default_dir for default_dir in default_directories
if os.path.isdir(default_dir)]
if 'CHEATPATH' in os.environ and os.environ['CHEATPATH']:
return [path for path in os.environ['CHEATPATH'].split(os.pathsep)
if os.path.isdir(path)] + default
else:
return default
def __cheat_files(self):
"""
Assembles a dictionary of cheatsheets found in the above directories.
"""
cheats = {}
for cheat_dir in reversed(self.dirs):
cheats.update(dict([(cheat, cheat_dir)
for cheat in os.listdir(cheat_dir)
if not cheat.startswith('.')
and not cheat.startswith('__')]))
return cheats
def edit(self, cheat):
"""Creates or edits a cheatsheet"""
# Assert that the EDITOR environment variable is set and that at least
# 3 arguments have been given
if 'EDITOR' not in os.environ:
print >> sys.stderr, ('In order to create/edit a cheatsheet you '
'must set your EDITOR environment variable '
'to your favorite editor\'s path.')
exit(1)
elif os.environ['EDITOR'] == "":
print >> sys.stderr, ('Your EDITOR environment variable is set '
'to nothing, in order to create/edit a '
'cheatsheet your must set it to a valid '
'editor\'s path.')
exit(1)
else:
editor = os.environ['EDITOR'].split()
# if the cheatsheet already exists, open it for editing
try:
if cheat in self.sheets:
sheet_path = os.path.join(self.sheets[cheat], cheat)
if os.access(sheet_path, os.W_OK):
subprocess.call(editor + [sheet_path])
else:
print >> sys.stderr, ("Sheet '%s' [%s] is not editable."
% (cheat, sheet_path))
print ('Do you want to '
'copy it to your user cheatsheets directory [%s] '
'before editing?\nKeep in mind that your sheet '
'will always be used before system-wide one.'
% DEFAULT_CHEAT_DIR)
awn = raw_input('[y/n] ')
if awn != 'y':
print ('Ok, if you want to edit system-wide sheet, '
'please try `cheat -e <cheatsheet>` '
'again with sudo.')
exit(1)
import shutil
# attempt to copy the cheatsheet to DEFAULT_CHEAT_DIR
try:
new_sheet = os.path.join(DEFAULT_CHEAT_DIR, cheat)
shutil.copy(sheet_path, new_sheet)
subprocess.call(editor + [new_sheet])
# fail gracefully if the cheatsheet cannot be copied. This
# can happen if DEFAULT_CHEAT_DIR does not exist
except IOError:
print ('Could not copy cheatsheet for editing.')
exit(1)
# otherwise, create it
else:
import cheatsheets as cs
# Attempt to write the new cheatsheet to the user's ~/.cheat
# dir if it exists. If it does not exist, attempt to create it.
if os.access(DEFAULT_CHEAT_DIR, os.W_OK) or os.makedirs(DEFAULT_CHEAT_DIR):
subprocess.call(editor
+ [os.path.join(DEFAULT_CHEAT_DIR, cheat)])
# If the directory cannot be created, write to the python
# package directory, though that will likely require the use
# of sudo
else:
if os.access(sheet_path, os.W_OK):
subprocess.call(editor
+ [os.path.join(cs.cheat_dir, cheat)])
else:
error_msg = ("Couldn't create '%s' cheatsheet.\n"
"Please retry usig sudo." % cheat)
print >> sys.stderr, error_msg
exit(1)
except OSError as errno:
print >> sys.stderr, ("Could not launch `%s` as your editor : %s"
% (editor[0], errno.strerror))
exit(1)
def list(self):
"""Lists the cheatsheets that are currently available"""
max_command = max([len(x) for x in self.sheets.keys()]) + 3
return ('\n'.join(sorted(['%s [%s]' % (key.ljust(max_command), value)
for key, value in self.sheets.items()])))
def __parse_cheat_command_block(self, cheat):
"""Parse text blocks inside specified sheet file"""
block = ""
path = os.path.join(self.sheets[cheat], cheat)
with open(path) as cheat_fp:
for line in cheat_fp.readlines():
if line == '\n':
if block:
yield block
block = ""
else:
block += line
if block:
yield block
def search(self, term):
"""Search for a term in sheetcheats"""
for cheat in self.sheets.keys():
output = ''
for block in self.__parse_cheat_command_block(cheat):
if term in block:
if not output:
output = cheat + ":\n"
output += ''.join([" " + line + '\n' for line
in block.split('\n')])
if output:
sys.stdout.write(output);
# Custom action for argparse
class ListDirectories(argparse.Action):
"""List cheat directories and exit"""
def __call__(self, parser, namespace, values, option_string=None):
print("\n".join(sheets.dirs))
parser.exit()
class ListCheatsheets(argparse.Action):
"""List cheatsheets and exit"""
def __call__(self, parser, namespace, values, option_string=None):
print(sheets.list());
parser.exit()
class EditSheet(argparse.Action):
"""If the user wants to edit a cheatsheet"""
def __call__(self, parser, namespace, values, option_string=None):
sheets.edit(values[0])
parser.exit()
class SearchSheet(argparse.Action):
"""If the user wants to search a term inside all cheatsheets"""
def __call__(self, parser, namespace, values, option_string=None):
sheets.search(values[0])
parser.exit()
def main():
"""Main execution function"""
global sheets
sheets = CheatSheets()
desc = dedent('''
cheat allows you to create and view interactive cheatsheets on the
command-line. It was designed to help remind *nix system
administrators of options for commands that they use frequently,
but not frequently enough to remember.''').strip()
epi = dedent('''
Examples:
To look up 'tar':
cheat tar
To create or edit the cheatsheet for 'foo':
cheat -e foo
To list the directories on the CHEATPATH
cheat -d
To list the available cheatsheets:
cheat -l
''').strip()
parser = argparse.ArgumentParser(prog='cheat',
description=desc, epilog=epi,
formatter_class=argparse.
RawDescriptionHelpFormatter)
parser_group = parser.add_mutually_exclusive_group()
parser_group.add_argument('sheet', metavar='cheatsheet',
action='store', type=str, nargs='?',
help='Look at <cheatseet>')
parser_group.add_argument('-e', '--edit', metavar='cheatsheet',
action=EditSheet, type=str, nargs=1,
help='Edit <cheatsheet>')
parser_group.add_argument('-s', '--search', metavar='term',
action=SearchSheet, type=str, nargs=1,
help='Search <term> inside all cheatsheets')
parser_group.add_argument('-l', '--list',
action=ListCheatsheets, nargs=0,
help='List all available cheatsheets')
parser_group.add_argument('-d', '--cheat-directories',
action=ListDirectories, nargs=0,
help='List all current cheat dirs')
args = parser.parse_args()
sheet = args.sheet
# Print the cheatsheet if it exists
if not sheet or sheet in ['help', 'cheat']:
parser.print_help()
elif sheet in sheets.sheets:
filename = os.path.join(sheets.sheets[sheet], sheet)
if USE_PYGMENTS:
pretty_print(filename)
else:
with open(filename) as istream:
for line in istream:
sys.stdout.write(line)
# if it does not, say so
else:
print >> sys.stderr, ('No cheatsheet found for %s.' % sheet)
exit(1)
exit()
if __name__ == '__main__':
main()
+3
View File
@@ -0,0 +1,3 @@
import sheet
import sheets
import utils
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+4
View File
@@ -0,0 +1,4 @@
import os
def sheets_dir():
return os.path.split(__file__)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -17,6 +17,15 @@ git commit -m "Your commit message"
# To edit previous commit message
git commit --amend
# To removed staged and working directory changes
git reset --hard
# To remove untracked files
git clean -f -d
# To remove untracked and ignored files
git clean -f -d -x
# To push to the tracked master branch:
git push origin master
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+94
View File
@@ -0,0 +1,94 @@
from cheat import sheets
from cheat import utils
from cheat.utils import *
import os
import shutil
import subprocess
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.')
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 and is writeable...
elif exists(sheet) and is_writable(sheet):
edit(sheet)
# if the cheatsheet exists but is not writable...
elif exists(sheet) and not is_writable(sheet):
# ... ask the user if we should copy the cheatsheet to her home directory for editing
yes = prompt_yes_or_no(
'The ' + sheet + ' sheet is not editable. Do you want to copy it to '
'your user cheatsheets directory before editing? Keep in mind that '
'your sheet will always be used before system-wide one.'
)
# if yes, copy the cheatsheet to the home directory before editing
if yes:
copy(path(sheet), os.path.join(sheets.default_path(), sheet))
edit(sheet)
# if no, just abort
else:
die('Aborting.')
def create(sheet):
""" Creates a cheatsheet """
new_sheet_path = os.path.join(sheets.default_path(), sheet)
try:
subprocess.call([editor(), new_sheet_path])
except OSError:
die('Could not launch ' + editor())
def edit(sheet):
""" Opens a cheatsheet for editing """
try:
subprocess.call([editor(), path(sheet)])
except OSError:
die('Could not launch ' + editor())
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 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 path(sheet):
""" Returns a sheet's filesystem path """
return sheets.get()[sheet]
def read(sheet):
""" Returns the contents of the cheatsheet as a String """
if not exists(sheet):
die('No cheatsheet found for ' + sheet)
with open (path(sheet)) as cheatfile:
return cheatfile.read()
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff. Show more