diff --git a/.gitignore b/.gitignore index a735f8b..6470873 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,8 @@ nosetests.xml # Translations *.mo + +# IDE Project Files +*.project +*.pydev* +*.idea diff --git a/requirements.txt b/requirements.txt index d6e1198..4db58a0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,3 @@ --e . +hacking>=0.10.0,<0.11 +ply +six>=1.5.2 diff --git a/setup.cfg b/setup.cfg index 8c28267..39bcf8b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,25 @@ [metadata] description-file = README.md +name = timex +version = 0.21 +author = Monsyne Dragon +author_email = mdragon@rackspace.com +summary = A time expressions library implementing a mini-language for manipulating datetimes +license = Apache-2 +keywords = + datetime + manipulation + transformation + DSL +classifiers = + Development Status :: 3 - Alpha + License :: OSI Approved :: Apache Software License + Operating System :: POSIX :: Linux + Programming Language :: Python :: 2.6 + Programming Language :: Python :: 2.7 +home-page = https://github.com/stackforge/stacktach-timex + +[files] +packages = + timex diff --git a/setup.py b/setup.py index f308139..aa2d8a0 100644 --- a/setup.py +++ b/setup.py @@ -1,49 +1,8 @@ -import os -from setuptools import setup, find_packages +#!/usr/bin/env python - -def read(fname): - return open(os.path.join(os.path.dirname(__file__), fname)).read() - -desc = """timex -===== - -A time expressions library implementing a mini-language for manipulating -datetimes. - -Much like regular expressions provide a mini-language for performing certain -operation on strings, Timex's time expressions provide a convenient way of -expressing datetime and date-range operations. These expressions are strings, -and can be safely read from a config file or user input. - -Read README.md for syntax and examples. -""" +from setuptools import setup setup( - name='timex', - version='0.20.0', - author='Monsyne Dragon', - author_email='mdragon@rackspace.com', - description=("A time expressions library implementing a mini-language " - "for manipulating datetimes"), - license='Apache License (2.0)', - keywords='datetime manipulation transformation DSL', - packages=find_packages(exclude=['tests']), - classifiers=[ - 'Development Status :: 3 - Alpha', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: POSIX :: Linux', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - ], - url='https://github.com/stackforge/stacktach-timex', - scripts=[], - long_description=desc, - install_requires=[ - "ply", - "six >= 1.5.2", - ], - - - zip_safe=False + setup_requires=['pbr'], + pbr=True, ) diff --git a/tests/test_expression.py b/tests/test_expression.py index 5728fcc..b6556bd 100644 --- a/tests/test_expression.py +++ b/tests/test_expression.py @@ -1,19 +1,33 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# Copyright © 2014 Rackspace Hosting. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + import datetime -#for Python2.6 compatability. +# for Python2.6 compatability. import unittest2 as unittest -import mock -import six from timex import expression class TestTimestamp(unittest.TestCase): - def setUp(self): super(TestTimestamp, self).setUp() - self.dt = datetime.datetime(2014, 8, 1, 2 ,10, 23, 550) - self.other_dt = datetime.datetime(2014, 8, 7, 2 ,0, 0, 0) + self.dt = datetime.datetime(2014, 8, 1, 2, 10, 23, 550) + self.other_dt = datetime.datetime(2014, 8, 7, 2, 0, 0, 0) self.timestamp = expression.Timestamp(self.dt) def test_timestamp_properties(self): @@ -101,14 +115,18 @@ class TestTimestamp(unittest.TestCase): self.assertEqual(res.timestamp, expected) expected = datetime.datetime(2014, 8, 1, 0, 0, 0, 0) - res = self.timestamp % expression.Duration(hour=0, minute=0, second=0, microsecond=0) + res = self.timestamp % expression.Duration(hour=0, minute=0, second=0, + microsecond=0) self.assertEqual(res.timestamp, expected) def test_handle_ambig_duration(self): d = expression.Duration(hour=10, unknown=2) - self.assertRaises(expression.TimexExpressionError, self.timestamp.__add__, d) - self.assertRaises(expression.TimexExpressionError, self.timestamp.__sub__, d) - self.assertRaises(expression.TimexExpressionError, self.timestamp.__mod__, d) + self.assertRaises(expression.TimexExpressionError, + self.timestamp.__add__, d) + self.assertRaises(expression.TimexExpressionError, + self.timestamp.__sub__, d) + self.assertRaises(expression.TimexExpressionError, + self.timestamp.__mod__, d) def test_total_seconds(self): self.assertFalse(self.timestamp.is_range) @@ -116,13 +134,12 @@ class TestTimestamp(unittest.TestCase): class TestTimeRange(unittest.TestCase): - def setUp(self): super(TestTimeRange, self).setUp() - self.begin_dt = datetime.datetime(2014, 8, 1, 2 ,10, 23, 550) - self.end_dt = datetime.datetime(2014, 8, 2, 2 ,10, 23, 550) - self.middle_dt = datetime.datetime(2014, 8, 1, 17 ,30, 10, 25) - self.other_dt = datetime.datetime(2014, 8, 7, 2 ,0, 0, 0) + self.begin_dt = datetime.datetime(2014, 8, 1, 2, 10, 23, 550) + self.end_dt = datetime.datetime(2014, 8, 2, 2, 10, 23, 550) + self.middle_dt = datetime.datetime(2014, 8, 1, 17, 30, 10, 25) + self.other_dt = datetime.datetime(2014, 8, 7, 2, 0, 0, 0) self.timerange = expression.TimeRange(self.begin_dt, self.end_dt) def test_timerange_properties(self): @@ -131,20 +148,20 @@ class TestTimeRange(unittest.TestCase): self.assertEqual(self.begin_dt, self.timerange.timestamp) def test_match(self): - #ranges include the beginning. + # ranges include the beginning. self.assertTrue(self.timerange.match(self.begin_dt)) self.assertTrue(self.timerange.match(self.middle_dt)) - #ranges *don`t* include the end. + # ranges *don`t* include the end. self.assertFalse(self.timerange.match(self.end_dt)) self.assertFalse(self.timerange.match(self.other_dt)) def test_in(self): - #ranges include the beginning. + # ranges include the beginning. self.assertTrue(self.begin_dt in self.timerange) self.assertTrue(self.middle_dt in self.timerange) - #ranges *don`t* include the end. + # ranges *don`t* include the end. self.assertFalse(self.end_dt in self.timerange) self.assertFalse(self.other_dt in self.timerange) @@ -234,7 +251,6 @@ class TestTimeRange(unittest.TestCase): self.assertEqual(res.begin, expected_begin) self.assertEqual(res.end, expected_end) - def test_replace(self): expected_begin = datetime.datetime(2014, 8, 1, 6, 10, 23, 550) expected_end = datetime.datetime(2014, 8, 2, 6, 10, 23, 550) @@ -256,7 +272,8 @@ class TestTimeRange(unittest.TestCase): expected_begin = datetime.datetime(2014, 8, 1, 0, 0, 0, 0) expected_end = datetime.datetime(2014, 8, 2, 0, 0, 0, 0) - res = self.timerange % expression.Duration(hour=0, minute=0, second=0, microsecond=0) + res = self.timerange % expression.Duration(hour=0, minute=0, second=0, + microsecond=0) self.assertEqual(res.begin, expected_begin) self.assertEqual(res.end, expected_end) @@ -283,17 +300,16 @@ class TestTimeRange(unittest.TestCase): def test_total_seconds(self): self.assertTrue(self.timerange.is_range) - self.assertEqual(self.timerange.total_seconds(), 24*60*60) + self.assertEqual(self.timerange.total_seconds(), 24 * 60 * 60) class TestPinnedTimeRange(unittest.TestCase): - def setUp(self): super(TestPinnedTimeRange, self).setUp() self.begin_dt = datetime.datetime(2014, 8, 1, 1, 0, 0, 0) self.end_dt = datetime.datetime(2014, 8, 2, 1, 0, 0, 0) - self.middle_dt = datetime.datetime(2014, 8, 1, 17 ,30, 10, 25) - self.other_dt = datetime.datetime(2014, 8, 7, 2 ,0, 0, 0) + self.middle_dt = datetime.datetime(2014, 8, 1, 17, 30, 10, 25) + self.other_dt = datetime.datetime(2014, 8, 7, 2, 0, 0, 0) self.timerange = expression.PinnedTimeRange(self.begin_dt, self.end_dt, self.middle_dt, @@ -340,7 +356,6 @@ class TestPinnedTimeRange(unittest.TestCase): class TestDuration(unittest.TestCase): - def setUp(self): super(TestDuration, self).setUp() self.second = expression.Duration(second=1) @@ -377,4 +392,3 @@ class TestDuration(unittest.TestCase): self.assertEqual(dd[unit], 1) for unit in ('microsecond', 'minute', 'month', 'year', 'unknown'): self.assertNotIn(unit, dd) - diff --git a/tests/test_parse.py b/tests/test_parse.py index eb62be8..27ad2f4 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -1,18 +1,32 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# Copyright © 2014 Rackspace Hosting. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + import datetime -#for Python2.6 compatability. +# for Python2.6 compatability. import unittest2 as unittest -import mock -import six import timex class TestParse(unittest.TestCase): - def setUp(self): super(TestParse, self).setUp() - self.dt = datetime.datetime(2014, 8, 1, 2 ,10, 23, 550) + self.dt = datetime.datetime(2014, 8, 1, 2, 10, 23, 550) self.other_dt = datetime.datetime(2014, 8, 7, 3, 20, 0, 0) def test_var(self): @@ -22,21 +36,21 @@ class TestParse(unittest.TestCase): self.assertEqual(t.timestamp, self.dt) def test_timestamp_add(self): - result = datetime.datetime(2014, 8, 2, 4 ,10, 23, 550) + result = datetime.datetime(2014, 8, 2, 4, 10, 23, 550) exp = timex.parse("$test_thingy + 1d 2h") t = exp(test_thingy=self.dt) self.assertFalse(t.is_range) self.assertEqual(t.timestamp, result) def test_timestamp_sub(self): - result = datetime.datetime(2014, 7, 31, 0 ,10, 23, 550) + result = datetime.datetime(2014, 7, 31, 0, 10, 23, 550) exp = timex.parse("$test_thingy - 1d 2h") t = exp(test_thingy=self.dt) self.assertFalse(t.is_range) self.assertEqual(t.timestamp, result) def test_timestamp_replace(self): - result = datetime.datetime(2014, 8, 7, 6 ,10, 23, 550) + result = datetime.datetime(2014, 8, 7, 6, 10, 23, 550) exp = timex.parse("$test_thingy @ 7d 6h") t = exp(test_thingy=self.dt) self.assertFalse(t.is_range) @@ -50,7 +64,7 @@ class TestParse(unittest.TestCase): self.assertEqual(t.end, self.other_dt) def test_timerange_add(self): - result_begin = datetime.datetime(2014, 8, 2, 4 ,10, 23, 550) + result_begin = datetime.datetime(2014, 8, 2, 4, 10, 23, 550) result_end = datetime.datetime(2014, 8, 8, 5, 20, 0, 0) exp = timex.parse("($test_thingy to $other) + 1d 2h") t = exp(test_thingy=self.dt, other=self.other_dt) @@ -59,7 +73,7 @@ class TestParse(unittest.TestCase): self.assertEqual(t.end, result_end) def test_timerange_sub(self): - result_begin = datetime.datetime(2014, 7, 31, 0 ,10, 23, 550) + result_begin = datetime.datetime(2014, 7, 31, 0, 10, 23, 550) result_end = datetime.datetime(2014, 8, 6, 1, 20, 0, 0) exp = timex.parse("($test_thingy to $other) - 1d 2h") t = exp(test_thingy=self.dt, other=self.other_dt) @@ -129,4 +143,3 @@ class TestParse(unittest.TestCase): self.assertTrue(t.is_range) self.assertEqual(t.begin, result_begin) self.assertEqual(t.end, result_end) - diff --git a/timex/__init__.py b/timex/__init__.py index 9d4d2fc..cc1bdb7 100644 --- a/timex/__init__.py +++ b/timex/__init__.py @@ -1,7 +1,10 @@ +from timex.expression import Duration +from timex.expression import PinnedTimeRange +from timex.expression import Timestamp +from timex.expression import TimeRange +from timex.expression import TimexExpressionError +from timex.expression import TimexParserError +from timex.expression import TimexLexerError +from timex.expression import TimexError from timex.parser import parse -from timex.expression import TimexExpressionError, TimexParserError -from timex.expression import TimexLexerError, TimexError -from timex.expression import Timestamp, TimeRange, PinnedTimeRange, Duration - __version__ = '0.10.0' - diff --git a/timex/expression.py b/timex/expression.py index a468ba2..ac5c9b0 100644 --- a/timex/expression.py +++ b/timex/expression.py @@ -1,6 +1,23 @@ -import logging -import datetime +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# Copyright © 2014 Rackspace Hosting. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + import abc +import datetime +import logging import six @@ -25,7 +42,6 @@ class TimexExpressionError(TimexError): @six.add_metaclass(abc.ABCMeta) class TimeMatcher(object): - _allow_ambig_duration = False @abc.abstractmethod @@ -60,11 +76,10 @@ class TimeMatcher(object): def _check_duration(self, duration): if isinstance(duration, Duration): if ((duration.ambiguous and self._allow_ambig_duration) - or not duration.ambiguous): + or not duration.ambiguous): return True raise TimexExpressionError("Invalid duration for time operation") - def _dt_replace(self, dt, duration): return dt.replace(**duration.as_dict) @@ -73,15 +88,16 @@ class TimeMatcher(object): months = d.pop('month', 0) years = d.pop('year', 0) if d: - delta = datetime.timedelta(**dict((k+"s",val) for k, val in d.items())) + delta = datetime.timedelta( + **dict((k + "s", val) for k, val in d.items())) dt = dt + delta if months: newmonth = dt.month + months years += (newmonth - 1) // 12 - newmonth = ((newmonth-1) % 12) + 1 + newmonth = ((newmonth - 1) % 12) + 1 dt = dt.replace(month=newmonth) if years: - dt = dt.replace(year=(dt.year+years)) + dt = dt.replace(year=(dt.year + years)) return dt def _dt_sub(self, dt, duration): @@ -89,21 +105,21 @@ class TimeMatcher(object): months = d.pop('month', 0) years = d.pop('year', 0) if d: - delta = datetime.timedelta(**dict((k+"s",val) for k, val in d.items())) + delta = datetime.timedelta( + **dict((k + "s", val) for k, val in d.items())) dt = dt - delta if months: newmonth = dt.month - months years -= (newmonth - 1) // 12 - newmonth = ((newmonth-1) % 12) + 1 + newmonth = ((newmonth - 1) % 12) + 1 dt = dt.replace(month=newmonth) if years: - dt = dt.replace(year=(dt.year-years)) + dt = dt.replace(year=(dt.year - years)) return dt class Timestamp(TimeMatcher): - """This is a wrapper on a datetime that has the same - interface as TimeRange""" + """Wrapper on a datetime with same interface as TimeRange""" def __init__(self, dt): self.timestamp = dt @@ -136,7 +152,6 @@ class Timestamp(TimeMatcher): class TimeRange(TimeMatcher): - _allow_ambig_duration = True def __init__(self, begin, end): @@ -149,7 +164,8 @@ class TimeRange(TimeMatcher): def total_seconds(self): delta = self.end - self.begin - return delta.seconds + (delta.days * 24 * 3600) + (delta.microseconds * 1e-6) + return (delta.seconds + (delta.days * 24 * 3600) + + (delta.microseconds * 1e-6)) def __nonzero__(self): return self.total_seconds() > 0 @@ -159,7 +175,10 @@ class TimeRange(TimeMatcher): return True def match(self, dt): - """TimeRanges match datetimes from begin (inclusive) to end (exclusive)""" + """Match datetimes + + TimeRanges match datetimes from begin (inclusive) to end (exclusive) + """ return dt >= self.begin and dt < self.end def __add__(self, other): @@ -236,11 +255,11 @@ class PinnedTimeRange(TimeRange): return self.time_range def __repr__(self): - return "PinnedTimeRange from %r to %r. Pinned to %s(%r)" % (self.begin, self.end, self.unit, self.pinned_to) + return ("PinnedTimeRange from %r to %r. Pinned to %s(%r)" + % (self.begin, self.end, self.unit, self.pinned_to)) class Environment(dict): - def time_func_hour(self, timestamp): dt = timestamp.timestamp begin = dt.replace(minute=0, second=0, microsecond=0) @@ -261,20 +280,20 @@ class Environment(dict): def time_func_year(self, timestamp): dt = timestamp.timestamp - begin = dt.replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0) + begin = dt.replace(month=1, day=1, hour=0, minute=0, second=0, + microsecond=0) end = Timestamp(begin) + Duration(year=1) return PinnedTimeRange(begin, end.timestamp, dt, 'year') @six.add_metaclass(abc.ABCMeta) class TimeExpression(object): - @abc.abstractmethod def apply(self, env): """Apply the expression to a given set of arguments. - :param env: a dictionary-like object. expression functions should be methods - on this object with names beginning with 'time_func_' + :param env: a dictionary-like object. expression functions should + be methods on this object with names beginning with 'time_func_' returns: TimeMatcher instance """ @@ -308,7 +327,8 @@ class TimeRangeFunction(TimeRangeExpression): self.expr = expr def __repr__(self): - return '%s %s(%r)' % (self.__class__.__name__, self.func_name, self.expr) + return ('%s %s(%r)' + % (self.__class__.__name__, self.func_name, self.expr)) def apply(self, env): arg = self.expr.apply(env) @@ -341,7 +361,8 @@ class Operation(TimeExpression): self.duration = duration def __repr__(self): - return '%s(%r, %r)' % (self.__class__.__name__, self.expr, self.duration) + return ('%s(%r, %r)' + % (self.__class__.__name__, self.expr, self.duration)) class Replace(Operation): @@ -366,11 +387,10 @@ class Minus(Operation): class Duration(object): - - UNIT_SIZES = {'year': 365*24*60*60, - 'month': 28*24*60*60, - 'day': 24*60*60, - 'hour': 60*60, + UNIT_SIZES = {'year': 365 * 24 * 60 * 60, + 'month': 28 * 24 * 60 * 60, + 'day': 24 * 60 * 60, + 'hour': 60 * 60, 'minute': 60, 'second': 1, 'microsecond': 1e-6} @@ -423,7 +443,7 @@ class Duration(object): elif d >= self.UNIT_SIZES['minute']: unit = 'second' else: - unit = microsecond + unit = 'microsecond' vals = self.as_dict del vals['unknown'] if unit in vals: @@ -476,4 +496,3 @@ class Duration(object): if our_val != other_val: return False return True - diff --git a/timex/lexer.py b/timex/lexer.py index 7cd0cde..9202f13 100644 --- a/timex/lexer.py +++ b/timex/lexer.py @@ -1,5 +1,22 @@ -import sys +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# Copyright © 2014 Rackspace Hosting. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + import logging +import sys import ply.lex @@ -16,8 +33,8 @@ class TimexLexer(object): self.debug = debug if not self.__doc__: raise TimexLexerError("Docstring information is missing. " - "Timex uses PLY which requires docstrings for " - "configuration.") + "Timex uses PLY which requires " + "docstrings for configuration.") self.lexer = ply.lex.lex(module=self, debug=self.debug, errorlog=logger) @@ -36,19 +53,20 @@ class TimexLexer(object): token.col = token.lexpos - self.latest_newline return token - reserved_words = { 'to' : 'TO', - 'us' : 'MICROSECOND', - 's' : 'SECOND', - 'sec' : 'SECOND', - 'm' : 'MINUTE', - 'min' : 'MINUTE', - 'h' : 'HOUR', - 'hr' : 'HOUR', - 'd' : 'DAY', - 'mo' : 'MONTH', - 'y' : 'YEAR', - 'yr' : 'YEAR', - } + reserved_words = { + 'to': 'TO', + 'us': 'MICROSECOND', + 's': 'SECOND', + 'sec': 'SECOND', + 'm': 'MINUTE', + 'min': 'MINUTE', + 'h': 'HOUR', + 'hr': 'HOUR', + 'd': 'DAY', + 'mo': 'MONTH', + 'y': 'YEAR', + 'yr': 'YEAR', + } tokens = ('NUMBER', 'PLUS', @@ -59,12 +77,12 @@ class TimexLexer(object): 'VAR', 'IDENTIFIER') + tuple(set(reserved_words.values())) - t_PLUS = r'\+' - t_MINUS = r'-' + t_PLUS = r'\+' + t_MINUS = r'-' t_REPLACE = r'@' - t_VAR = r'\$' - t_LPAREN = r'\(' - t_RPAREN = r'\)' + t_VAR = r'\$' + t_LPAREN = r'\(' + t_RPAREN = r'\)' def t_IDENTIFIER(self, t): r'[a-zA-Z_][a-zA-Z0-9_]*' @@ -76,13 +94,12 @@ class TimexLexer(object): t.value = int(t.value) return t - def t_newline(self, t): r'\n+' t.lexer.lineno += len(t.value) self.latest_newline = t.lexpos - t_ignore = ' \t' + t_ignore = ' \t' def t_error(self, t): raise TimexLexerError('Error on line %s, col %s: Unexpected character:' @@ -99,4 +116,3 @@ if __name__ == '__main__': while token: print('%-20s%s' % (token.value, token.type)) token = lexer.token() - diff --git a/timex/parser.py b/timex/parser.py index 6cce215..7189d48 100644 --- a/timex/parser.py +++ b/timex/parser.py @@ -1,14 +1,34 @@ -import sys, os +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# Copyright © 2014 Rackspace Hosting. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + import logging +import os import ply.yacc -from timex.lexer import TimexLexer +from timex.expression import Duration +from timex.expression import Minus +from timex.expression import Plus +from timex.expression import Replace +from timex.expression import TimeRangeExpression +from timex.expression import TimeRangeFunction from timex.expression import TimexParserError -from timex.expression import Replace, Plus, Minus -from timex.expression import Duration, Variable -from timex.expression import TimeRangeFunction, TimeRangeExpression - +from timex.expression import Variable +from timex.lexer import TimexLexer """ @@ -53,7 +73,6 @@ unit : SECOND """ - logger = logging.getLogger(__name__) @@ -62,7 +81,7 @@ def parse(string): class TimexParser(object): - """ LALR parser for time expression mini-language.""" + """LALR parser for time expression mini-language.""" tokens = TimexLexer.tokens def __init__(self, debug=False, lexer_class=None, start='time_expression'): @@ -70,15 +89,15 @@ class TimexParser(object): self.start = start if not self.__doc__: raise TimexParserError("Docstring information is missing. " - "Timex uses PLY which requires docstrings for " - "configuration.") + "Timex uses PLY which requires " + "docstrings for configuration.") self.lexer_class = lexer_class or TimexLexer def _parse_table(self): tabdir = os.path.dirname(__file__) try: module_name = os.path.splitext(os.path.split(__file__)[1])[0] - except: + except Exception: module_name = __name__ table_module = '_'.join([module_name, self.start, 'parsetab']) return (tabdir, table_module) @@ -89,11 +108,11 @@ class TimexParser(object): tabdir, table_module = self._parse_table() parser = ply.yacc.yacc(module=self, debug=self.debug, - tabmodule = table_module, - outputdir = tabdir, + tabmodule=table_module, + outputdir=tabdir, write_tables=0, - start = self.start, - errorlog = logger) + start=self.start, + errorlog=logger) return parser.parse(string, lexer=lexer) @@ -106,7 +125,7 @@ class TimexParser(object): def p_error(self, t): raise TimexParserError('Parse error at %s:%s near token %s (%s)' % - (t.lineno, t.col, t.value, t.type)) + (t.lineno, t.col, t.value, t.type)) def p_time_expression(self, p): """time_expression : timerange_expression @@ -189,7 +208,8 @@ class TimexParser(object): | HOUR | DAY | MONTH - | YEAR""" + | YEAR + """ unit = TimexLexer.reserved_words[p[1]] unit = unit.lower() p[0] = unit diff --git a/tox.ini b/tox.ini index 9afa955..862a7ea 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26,py27 +envlist = py26,py27,pep8 [testenv] deps = @@ -13,3 +13,11 @@ commands = sitepackages = False +[testenv:pep8] +commands = + flake8 + +[flake8] +ignore = H405 +exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,timex/__init__.py +show-source = True