Commit 66955b6f authored by Kevin Lyda's avatar Kevin Lyda 💬
Browse files

More pylint placating.

Also added some dist_clean dirs to remove and info on using coverage.
parent cb233338
...@@ -32,6 +32,10 @@ Contributions are welcome! Please add unit tests for new features ...@@ -32,6 +32,10 @@ Contributions are welcome! Please add unit tests for new features
or bug fixes. To run all the unit tests run ``./setup test``. or bug fixes. To run all the unit tests run ``./setup test``.
If you have `tox`_ installed, just run ``tox``. If you have `tox`_ installed, just run ``tox``.
You can review `coverage`_ of added tests by running
``coverage run setup.py test`` and then running
``coverage report -m``.
Note that tests are run on `Travis`_ for all supported python Note that tests are run on `Travis`_ for all supported python
versions whenever the tree on github is pushed to. versions whenever the tree on github is pushed to.
...@@ -54,5 +58,6 @@ Credits ...@@ -54,5 +58,6 @@ Credits
- `Kevin Lyda`_: Who got burned one too many times by broken crontabs. - `Kevin Lyda`_: Who got burned one too many times by broken crontabs.
.. _`tox`: http://pypi.python.org/pypi/tox .. _`tox`: http://pypi.python.org/pypi/tox
.. _`coverage`: http://pypi.python.org/pypi/coverage
.. _`Travis`: http://travis-ci.org/#!/lyda/chkcrontab .. _`Travis`: http://travis-ci.org/#!/lyda/chkcrontab
.. _`Kevin Lyda`: https://github.com/lyda .. _`Kevin Lyda`: https://github.com/lyda
...@@ -58,7 +58,8 @@ A brief description of each class and function: ...@@ -58,7 +58,8 @@ A brief description of each class and function:
cron line. cron line.
Logging class to pretty-print output: Logging class to pretty-print output:
LogCounter: A logging class that provides a summary of warnings and errors. LogCounter: A logging class that provides a summary of warnings and
errors.
Putting it all together: Putting it all together:
CheckCrontab: Checks the a crontab file. CheckCrontab: Checks the a crontab file.
...@@ -118,7 +119,8 @@ class FSM(object): ...@@ -118,7 +119,8 @@ class FSM(object):
if state not in self.states: if state not in self.states:
self.states[state] = {} self.states[state] = {}
self.states[state].update([(char, (action, next_state)) for char in chars]) self.states[state].update([(char, (action, next_state))
for char in chars])
def AddEndState(self, state, action): def AddEndState(self, state, action):
"""Handle the end state of the FSM. """Handle the end state of the FSM.
...@@ -168,63 +170,75 @@ class FSM(object): ...@@ -168,63 +170,75 @@ class FSM(object):
return data_out return data_out
def ActionTime(data_out, char): def action_time(data_out, char):
"""Add a char to time."""
data_out['time'] += char data_out['time'] += char
def ActionStar(data_out, char): def action_star(data_out, char):
"""Add a char to time."""
data_out['time'] = char data_out['time'] = char
def ActionDash(data_out, unused_char): def action_dash(data_out, unused_char):
"""Move time to range, reset time."""
data_out['range'] = data_out['time'] data_out['range'] = data_out['time']
data_out['time'] = '' data_out['time'] = ''
def ActionStep(data_out, char): def action_step(data_out, char):
"""Add a char to step."""
data_out['step'] += char data_out['step'] += char
def ActionNoop(unused_data_out, unused_char): def action_noop(unused_data_out, unused_char):
"""Do nothing."""
pass pass
def ActionTimeComma(data_out, unused_char=''): def action_time_comma(data_out, unused_char=''):
"""Move time to cron_times, reset time."""
data_out['cron_times'].append(CTTime(int(data_out['time']))) data_out['cron_times'].append(CTTime(int(data_out['time'])))
data_out['time'] = '' data_out['time'] = ''
def ActionStarComma(data_out, unused_char=''): def action_star_comma(data_out, unused_char=''):
"""Set cron_times, reset time."""
data_out['cron_times'].append(CTStar()) data_out['cron_times'].append(CTStar())
data_out['time'] = '' data_out['time'] = ''
def ActionStarStepComma(data_out, unused_char=''): def action_star_step_comma(data_out, unused_char=''):
"""Set cron_times, reset time & step."""
data_out['cron_times'].append(CTStarStep(int(data_out['step']))) data_out['cron_times'].append(CTStarStep(int(data_out['step'])))
data_out['time'] = '' data_out['time'] = ''
data_out['step'] = '' data_out['step'] = ''
def ActionTextComma(data_out, unused_char=''): def action_text_comma(data_out, unused_char=''):
"""Set cron_times from time, reset time."""
data_out['cron_times'].append(CTText(data_out['time'])) data_out['cron_times'].append(CTText(data_out['time']))
data_out['time'] = '' data_out['time'] = ''
def ActionRangeComma(data_out, unused_char=''): def action_range_comma(data_out, unused_char=''):
"""Set cron_times from range & time, reset range & time."""
data_out['cron_times'].append(CTRange(int(data_out['range']), data_out['cron_times'].append(CTRange(int(data_out['range']),
int(data_out['time']))) int(data_out['time'])))
data_out['range'] = '' data_out['range'] = ''
data_out['time'] = '' data_out['time'] = ''
def ActionTextRangeComma(data_out, unused_char=''): def action_text_range_comma(data_out, unused_char=''):
"""Set cron_times from range & time, reset range & time."""
data_out['cron_times'].append(CTTextRange(data_out['range'], data_out['cron_times'].append(CTTextRange(data_out['range'],
data_out['time'])) data_out['time']))
data_out['range'] = '' data_out['range'] = ''
data_out['time'] = '' data_out['time'] = ''
def ActionRangeStepComma(data_out, unused_char=''): def action_range_step_comma(data_out, unused_char=''):
"""Set cron_times from range, time & step, reset range, time & step."""
data_out['cron_times'].append(CTRangeStep(int(data_out['range']), data_out['cron_times'].append(CTRangeStep(int(data_out['range']),
int(data_out['time']), int(data_out['time']),
int(data_out['step']))) int(data_out['step'])))
...@@ -233,7 +247,8 @@ def ActionRangeStepComma(data_out, unused_char=''): ...@@ -233,7 +247,8 @@ def ActionRangeStepComma(data_out, unused_char=''):
data_out['step'] = '' data_out['step'] = ''
def ActionTextRangeStepComma(data_out, unused_char=''): def action_text_range_step_comma(data_out, unused_char=''):
"""Set cron_times from range, time & step, reset range, time & step."""
data_out['cron_times'].append(CTTextRangeStep(data_out['range'], data_out['cron_times'].append(CTTextRangeStep(data_out['range'],
data_out['time'], data_out['time'],
int(data_out['step']))) int(data_out['step'])))
...@@ -254,59 +269,61 @@ def InitCronFSM(): ...@@ -254,59 +269,61 @@ def InitCronFSM():
'cron_times': []})) 'cron_times': []}))
# Case: * # Case: *
fsm.AddTransition('*', 'start', ActionStar, 'star') fsm.AddTransition('*', 'start', action_star, 'star')
fsm.AddTransition('*', 'next', ActionStar, 'star') fsm.AddTransition('*', 'next', action_star, 'star')
fsm.AddEndState('star', ActionStarComma) fsm.AddEndState('star', action_star_comma)
fsm.AddTransition(',', 'star', ActionStarComma, 'next') fsm.AddTransition(',', 'star', action_star_comma, 'next')
# Case: */<number> # Case: */<number>
fsm.AddTransition('/', 'star', ActionNoop, 'start_star_step') fsm.AddTransition('/', 'star', action_noop, 'start_star_step')
fsm.AddTransition(string.digits, 'start_star_step', ActionStep, 'star_step') fsm.AddTransition(string.digits, 'start_star_step', action_step,
fsm.AddTransition(string.digits, 'star_step', ActionStep, 'star_step') 'star_step')
fsm.AddEndState('star_step', ActionStarStepComma) fsm.AddTransition(string.digits, 'star_step', action_step, 'star_step')
fsm.AddTransition(',', 'star_step', ActionStarStepComma, 'next') fsm.AddEndState('star_step', action_star_step_comma)
fsm.AddTransition(',', 'star_step', action_star_step_comma, 'next')
# Case: <number> # Case: <number>
fsm.AddTransition(string.digits, 'start', ActionTime, 'time') fsm.AddTransition(string.digits, 'start', action_time, 'time')
fsm.AddTransition(string.digits, 'next', ActionTime, 'time') fsm.AddTransition(string.digits, 'next', action_time, 'time')
fsm.AddTransition(string.digits, 'time', ActionTime, 'time') fsm.AddTransition(string.digits, 'time', action_time, 'time')
fsm.AddEndState('time', ActionTimeComma) fsm.AddEndState('time', action_time_comma)
fsm.AddTransition(',', 'time', ActionTimeComma, 'next') fsm.AddTransition(',', 'time', action_time_comma, 'next')
# Case: <number>-<number> # Case: <number>-<number>
fsm.AddTransition('-', 'time', ActionDash, 'start_range') fsm.AddTransition('-', 'time', action_dash, 'start_range')
fsm.AddTransition(string.digits, 'start_range', ActionTime, 'range') fsm.AddTransition(string.digits, 'start_range', action_time, 'range')
fsm.AddTransition(string.digits, 'range', ActionTime, 'range') fsm.AddTransition(string.digits, 'range', action_time, 'range')
fsm.AddEndState('range', ActionRangeComma) fsm.AddEndState('range', action_range_comma)
fsm.AddTransition(',', 'range', ActionRangeComma, 'next') fsm.AddTransition(',', 'range', action_range_comma, 'next')
# Case: <number>-<number>/<number> # Case: <number>-<number>/<number>
fsm.AddTransition('/', 'range', ActionNoop, 'start_range_step') fsm.AddTransition('/', 'range', action_noop, 'start_range_step')
fsm.AddTransition(string.digits, 'start_range_step', fsm.AddTransition(string.digits, 'start_range_step',
ActionStep, 'range_step') action_step, 'range_step')
fsm.AddTransition(string.digits, 'range_step', ActionStep, 'range_step') fsm.AddTransition(string.digits, 'range_step', action_step, 'range_step')
fsm.AddEndState('range_step', ActionRangeStepComma) fsm.AddEndState('range_step', action_range_step_comma)
fsm.AddTransition(',', 'range_step', ActionRangeStepComma, 'next') fsm.AddTransition(',', 'range_step', action_range_step_comma, 'next')
# Case: <text> # Case: <text>
fsm.AddTransition(string.ascii_letters, 'start', ActionTime, 'text') fsm.AddTransition(string.ascii_letters, 'start', action_time, 'text')
fsm.AddTransition(string.ascii_letters, 'next', ActionTime, 'text') fsm.AddTransition(string.ascii_letters, 'next', action_time, 'text')
fsm.AddTransition(string.ascii_letters, 'text', ActionTime, 'text') fsm.AddTransition(string.ascii_letters, 'text', action_time, 'text')
fsm.AddEndState('text', ActionTextComma) fsm.AddEndState('text', action_text_comma)
fsm.AddTransition(',', 'text', ActionTextComma, 'next') fsm.AddTransition(',', 'text', action_text_comma, 'next')
# Case: <text>-<text> # Case: <text>-<text>
fsm.AddTransition('-', 'text', ActionDash, 'start_text_range') fsm.AddTransition('-', 'text', action_dash, 'start_text_range')
fsm.AddTransition(string.ascii_letters, 'start_text_range', ActionTime, fsm.AddTransition(string.ascii_letters, 'start_text_range', action_time,
'text_range') 'text_range')
fsm.AddTransition(string.ascii_letters, 'text_range', ActionTime, fsm.AddTransition(string.ascii_letters, 'text_range', action_time,
'text_range') 'text_range')
fsm.AddEndState('text_range', ActionTextRangeComma) fsm.AddEndState('text_range', action_text_range_comma)
fsm.AddTransition(',', 'text_range', ActionTextRangeComma, 'next') fsm.AddTransition(',', 'text_range', action_text_range_comma, 'next')
# Case: <text>-<text>/<text> # Case: <text>-<text>/<text>
fsm.AddTransition('/', 'text_range', ActionNoop, 'start_text_range_step') fsm.AddTransition('/', 'text_range', action_noop, 'start_text_range_step')
fsm.AddTransition(string.digits, 'start_text_range_step', ActionStep, fsm.AddTransition(string.digits, 'start_text_range_step', action_step,
'text_range_step') 'text_range_step')
fsm.AddTransition(string.digits, 'text_range_step', ActionStep, fsm.AddTransition(string.digits, 'text_range_step', action_step,
'text_range_step') 'text_range_step')
fsm.AddEndState('text_range_step', ActionTextRangeStepComma) fsm.AddEndState('text_range_step', action_text_range_step_comma)
fsm.AddTransition(',', 'text_range_step', ActionTextRangeStepComma, 'next') fsm.AddTransition(',', 'text_range_step', action_text_range_step_comma,
'next')
return fsm return fsm
...@@ -315,25 +332,33 @@ class CronTimeField(object): ...@@ -315,25 +332,33 @@ class CronTimeField(object):
"""CronTimeField superclass for various time specifiers in cron fields.""" """CronTimeField superclass for various time specifiers in cron fields."""
def __init__(self): def __init__(self):
pass self._text = None
self._kind = None
self._start = None
self._end = None
self._step = None
def __str__(self): def __str__(self):
return self._text return self._text
@property @property
def kind(self): def kind(self):
"""Kind field."""
return self._kind return self._kind
@property @property
def start(self): def start(self):
"""Start value of this field."""
return self._start return self._start
@property @property
def end(self): def end(self):
"""End value of this field."""
return self._end return self._end
@property @property
def step(self): def step(self):
"""Step for this field."""
return self._step return self._step
def CheckLowStep(self, diagnostics, cron_time_field): def CheckLowStep(self, diagnostics, cron_time_field):
...@@ -497,6 +522,7 @@ class CTText(CronTimeField): ...@@ -497,6 +522,7 @@ class CTText(CronTimeField):
self._text = '%s' % start_time self._text = '%s' % start_time
def GetDiagnostics(self, cron_time_field): def GetDiagnostics(self, cron_time_field):
"""Checks for issues with a text field."""
diagnostics = [] diagnostics = []
self.CheckValidText(diagnostics, self._start, cron_time_field) self.CheckValidText(diagnostics, self._start, cron_time_field)
return diagnostics return diagnostics
...@@ -551,11 +577,14 @@ class CronTimeFieldLimit(object): ...@@ -551,11 +577,14 @@ class CronTimeFieldLimit(object):
self.min_time = min_time self.min_time = min_time
self.max_time = max_time self.max_time = max_time
self.valid_text = valid_text self.valid_text = valid_text
self._name = None
def _GetName(self): def _GetName(self):
"""Return the name."""
return self._name return self._name
def _SetName(self, name): def _SetName(self, name):
"""Set the name."""
self._name = name self._name = name
name = property(_GetName, _SetName, name = property(_GetName, _SetName,
...@@ -663,6 +692,9 @@ class CronLineTimeAction(object): ...@@ -663,6 +692,9 @@ class CronLineTimeAction(object):
self.user = user self.user = user
self.command = command self.command = command
def _CheckTimeField(self, log):
pass
def ValidateAndLog(self, log): def ValidateAndLog(self, log):
"""Validates an @ time spec line and logs any errors and warnings. """Validates an @ time spec line and logs any errors and warnings.
...@@ -675,7 +707,8 @@ class CronLineTimeAction(object): ...@@ -675,7 +707,8 @@ class CronLineTimeAction(object):
if self.user in USER_WHITELIST: if self.user in USER_WHITELIST:
return return
elif len(self.user) > 31: elif len(self.user) > 31:
log.LineError(log.MSG_INVALID_USER, 'Username too long "%s"' % self.user) log.LineError(log.MSG_INVALID_USER,
'Username too long "%s"' % self.user)
elif self.user.startswith('-'): elif self.user.startswith('-'):
log.LineError(log.MSG_INVALID_USER, 'Invalid username "%s"' % self.user) log.LineError(log.MSG_INVALID_USER, 'Invalid username "%s"' % self.user)
elif re.search(r'[\s!"#$%&\'()*+,/:;<=>?@[\\\]^`{|}~]', self.user): elif re.search(r'[\s!"#$%&\'()*+,/:;<=>?@[\\\]^`{|}~]', self.user):
...@@ -811,6 +844,7 @@ class CronLineFactory(object): ...@@ -811,6 +844,7 @@ class CronLineFactory(object):
class LogMsgKindNotFound(Exception): class LogMsgKindNotFound(Exception):
"""Exception for broken log messages."""
pass pass
......
...@@ -64,9 +64,9 @@ class CleanCmd(Command): ...@@ -64,9 +64,9 @@ class CleanCmd(Command):
def run(self): def run(self):
# Configure for this project. # Configure for this project.
suffixes2del = [ 'MANIFEST', '.pyc', 'chkcrontabc' ] suffixes2del = ['MANIFEST', '.pyc', 'chkcrontabc']
dirs2del = [ './build', './dist' ] dirs2del = ['./build', './dist', './.tox', './.coverage']
dirs2ign = [ './.git' ] dirs2ign = ['./.git']
# End config. # End config.
doomed = set() doomed = set()
# Change to base dir. # Change to base dir.
...@@ -162,7 +162,7 @@ if 'setuptools' not in dir(): ...@@ -162,7 +162,7 @@ if 'setuptools' not in dir():
setup( setup(
cmdclass=cmdclass, cmdclass=cmdclass,
name='chkcrontab', name='chkcrontab',
version='1.3', version='1.4a',
url='http://code.google.com/p/chkcrontab', url='http://code.google.com/p/chkcrontab',
author='Kevin Lyda', author='Kevin Lyda',
author_email='lyda@google.com', author_email='lyda@google.com',
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment