Skip to content

Commit

Permalink
added config file with ability to blacklist plugins
Browse files Browse the repository at this point in the history
added new tests
added ability to set fencepy root, as required by new tests
some other related refactoring
  • Loading branch information
Adam Kaufman committed Jan 15, 2015
1 parent 7adc5c2 commit 1ea267c
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 20 deletions.
11 changes: 11 additions & 0 deletions fencepy/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,20 @@ def findpybin(name, start):

@contextmanager
def redirected(out=sys.stdout, err=sys.stderr):
"""Temporarily redirect stdout and/or stderr"""
saved = sys.stdout, sys.stderr
sys.stdout, sys.stderr = out, err
try:
yield
finally:
sys.stdout, sys.stderr = saved


def str2bool(value):
"""Convert various acceptable string values into a bool"""
if type(value) in (str, unicode):
if value.lower() in ('true', 't', 'yes', 'y', '1'):
return True
elif value.lower() in ('false', 'f', 'no', 'n', '0'):
return False
raise ValueError('{0} is not an acceptable boolean value'.format(value))
68 changes: 52 additions & 16 deletions fencepy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@
import shutil
import sys
from . import plugins
from .helpers import getoutputoserror, findpybin
from .helpers import getoutputoserror, findpybin, str2bool

FENCEPY_ROOT = os.path.join(os.path.expanduser('~'), '.fencepy')
if not os.path.exists(FENCEPY_ROOT):
os.mkdir(FENCEPY_ROOT)
if sys.version.startswith('2'):
from ConfigParser import SafeConfigParser
else:
from configparser import SafeConfigParser

from logging.handlers import RotatingFileHandler
import logging as l
l.basicConfig(filename=os.path.join(FENCEPY_ROOT, 'fencepy.log'))

VENV_ROOT = os.path.join(FENCEPY_ROOT, 'virtualenvs')


def _get_args():
Expand All @@ -45,20 +44,35 @@ def _get_args():
help='Use DIR as the root path for the fenced environment instead of the CWD')
paths.add_argument('-D', '--virtualenv-dir', metavar='DIR',
help='Use DIR as the root directory for the virtualenv instead of the default')
paths.add_argument('-F', '--fencepy-root', metavar='DIR',
help='Use DIR as the root directory for all fencepy ops instead of the default')

# miscellaneous functionality
misc = parser.add_argument_group('miscellaneous')
misc.add_argument('-P', '--plugins',
help='Comma-separated list of plugins to apply, defaults to all plugins')
misc.add_argument('-P', '--plugins', default='', dest='pluginstr',
help='Comma-separated list of plugins to apply, override any settings in config file')
misc.add_argument('-C', '--config-file',
help='Config file to use instead of ~/.fencepy/fencepy.conf')
misc.add_argument('-v', '--verbose', '--debug', action='store_true',
help='Print debug logging')
misc.add_argument('-q', '--quiet', action='store_true',
help='Silence all console output')
misc.add_argument("-h", "--help", action="help",
help="show this help message and exit")
help="Show this help message and exit")

args = vars(parser.parse_args())

# set up the root directory
if not args['fencepy_root']:
args['fencepy_root'] = os.path.join(os.path.expanduser('~'), '.fencepy')
if not os.path.exists(args['fencepy_root']):
os.mkdir(args['fencepy_root'])

# set up logging
f = l.Formatter('%(asctime)s [%(levelname)s] %(module)s: %(message)s')
h = RotatingFileHandler(os.path.join(args['fencepy_root'], 'fencepy.log'))
h.setFormatter(f)
l.getLogger('').addHandler(h)
if not args['quiet']:
f = l.Formatter('[%(levelname)s] %(message)s')
h = l.StreamHandler()
Expand Down Expand Up @@ -87,11 +101,12 @@ def _get_args():

# reset the virtualenv root, if necessary
if not args['virtualenv_dir']:
venv_root = os.path.join(args['fencepy_root'], 'virtualenvs')

# if we're one directory below the root, this logic needs to work differently
parent = os.path.dirname(args['dir'])
if parent in ('/', os.path.splitdrive(parent)[0]):
args['virtualenv_dir'] = os.path.join(VENV_ROOT, os.path.basename(args['dir']))
args['virtualenv_dir'] = os.path.join(venv_root, os.path.basename(args['dir']))

else:
# need the realpath here because in some circumstances windows paths get passed
Expand All @@ -101,17 +116,38 @@ def _get_args():
if tokens[-1] == '':
tokens = tokens[:-1]
prjpart = '.'.join([os.path.basename(args['dir']), '.'.join([d[0] for d in tokens])])
args['virtualenv_dir'] = os.path.join(VENV_ROOT, '-'.join((prjpart, args['pyversion'])))
args['virtualenv_dir'] = os.path.join(venv_root, '-'.join((prjpart, args['pyversion'])))

# set the mode properly
modecount = [args['activate'], args['create'], args['erase']].count(True)
if modecount > 1:
l.error("please specify only one of -a, -c, -e")
raise RuntimeError('please specify only one of -a, -c, -e')
elif modecount == 0:
args['activate'] = True

# plugins
args['plugins'] = args['plugins'].split(',') if args['plugins'] else ['sublime', 'requirements', 'ps1']
# only populate the parser if there's a valid file
config = SafeConfigParser()
readconf = True
if args['config_file']:
if not os.path.exists(args['config_file']):
raise IOError('specified config file {0} does not exist'.format(args['config_file']))
else:
args['config_file'] = os.path.join(args['fencepy_root'], 'fencepy.conf')
if not os.path.exists(args['config_file']):
readconf = False
if readconf:
config.read(args['config_file'])

# plugins -- all false means all true
if args['pluginstr']:
args['plugins'] = dict((key, key in args['pluginstr'].split(',')) for key in plugins.PLUGINS)
else:
args['plugins'] = dict((key, True) for key in plugins.PLUGINS)
if config.has_section('plugins'):
for key, value in config.items('plugins'):
if key not in plugins.PLUGINS:
raise KeyError('invalid configuration: {0} is not a valid plugin'.format(key))
args['plugins'][key] = str2bool(value)

return args

Expand Down Expand Up @@ -185,7 +221,7 @@ def _create(args):
return 1

# plugins
for plugin_name in args['plugins']:
for plugin_name in [key for key, value in args['plugins'].items() if value]:
if getattr(plugins, 'install_{0}'.format(plugin_name))(args) == 1:
return 1

Expand Down
2 changes: 2 additions & 0 deletions fencepy/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import logging
l = logging.getLogger('')

PLUGINS = ['requirements', 'sublime', 'ps1']


def install_requirements(args):
"""Install requirements out of requirements.txt, if it exists"""
Expand Down
31 changes: 27 additions & 4 deletions tests/test_fencepy.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,22 @@
class TestFencepy(TestCase):

def _fence(self, *args):
sys.argv = ['fencepy'] + list(args)
# always include the overridden fencepy directory
sys.argv = ['fencepy', '-F', self.fdir] + list(args)
ret = fencepy.fence()
sys.argv = ORIGINAL_ARGV
return ret

def _get_arg_dict(self, *args):
sys.argv = ['fencepy', '-q'] + list(args)
# always include the overridden fencepy directory
sys.argv = ['fencepy', '-F', self.fdir, '-q'] + list(args)
ret = fencepy.main._get_args()
sys.argv = ORIGINAL_ARGV
return ret

def setUp(self):
self.tempdir = tempfile.mkdtemp()
self.fdir = os.path.join(self.tempdir, 'fencepy')
self.pname = str(uuid.uuid4())
self.pdir = os.path.join(self.tempdir, self.pname)
os.mkdir(self.pdir)
Expand Down Expand Up @@ -154,5 +157,25 @@ def test_activate_bash(self):
self._test_activate('bash', 'activate')

def test_multiple_modes(self):
ret = self._fence('-a', '-c', '-e')
self.assertEqual(ret, 1, 'multiple modes are not allowed')
with raises(RuntimeError):
ret = self._fence('-a', '-c', '-e')

def test_specify_plugins(self):
self.assertTrue(self.default_args['plugins']['requirements'])
self.assertTrue(self.default_args['plugins']['ps1'])
self.assertTrue(self.default_args['plugins']['sublime'])
args = self._get_arg_dict('-P', 'requirements')
self.assertTrue(args['plugins']['requirements'])
self.assertFalse(args['plugins']['ps1'])
self.assertFalse(args['plugins']['sublime'])
args = self._get_arg_dict('-P', 'requirements,ps1')
self.assertTrue(args['plugins']['requirements'])
self.assertTrue(args['plugins']['ps1'])
self.assertFalse(args['plugins']['sublime'])

lines = ['[plugins]', 'requirements = false']
open(os.path.join(self.fdir, 'fencepy.conf'), 'w').write(os.linesep.join(lines))
args = self._get_arg_dict()
self.assertFalse(args['plugins']['requirements'])
self.assertTrue(args['plugins']['ps1'])
self.assertTrue(args['plugins']['sublime'])
9 changes: 9 additions & 0 deletions tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,12 @@ def test_pseudo_merge_dict_bad_dto(self):
}
with raises(ValueError):
helpers.pseudo_merge_dict(dto, dfrom)

def test_str2bool(self):
for true in ('True', 'true', 'TRUE', 'T', 't', 'Yes', 'yes', 'YES', 'Y', 'y', '1', 'truE', 'yEs'):
self.assertTrue(helpers.str2bool(true))
for false in ('False', 'false', 'FALSE', 'F', 'f', 'No', 'no', 'NO', 'N', 'n', '0', 'faLsE', 'nO'):
self.assertFalse(helpers.str2bool(false))
for error in ('bad', '10', None, 1, 'truee'):
with raises(ValueError):
helpers.str2bool(error)

0 comments on commit 1ea267c

Please sign in to comment.