diff --git a/lesscpy/lessc/lexer.py b/lesscpy/lessc/lexer.py index a333d94..97b574e 100644 --- a/lesscpy/lessc/lexer.py +++ b/lesscpy/lessc/lexer.py @@ -11,6 +11,7 @@ """ import re import ply.lex as lex +from six import string_types from lesscpy.lib import dom from lesscpy.lib import css @@ -460,12 +461,16 @@ class LessLexer: self.lexer.input(f.read()) return self - def input(self, filename): + def input(self, file): """ - Wrapper for file + Load lexer with content from `file` which can be a path or a file + like object. """ - with open(filename) as f: - self.lexer.input(f.read()) + if isinstance(file, string_types): + with open(file) as f: + self.lexer.input(f.read()) + else: + self.lexer.input(file.read()) def token(self): """ diff --git a/lesscpy/lessc/parser.py b/lesscpy/lessc/parser.py index 153feff..32014c3 100644 --- a/lesscpy/lessc/parser.py +++ b/lesscpy/lessc/parser.py @@ -77,18 +77,33 @@ class LessParser(object): self.result = None self.target = None - def parse(self, filename='', debuglevel=0): + def parse(self, filename=None, file=None, debuglevel=0): """ Parse file. kwargs: filename (str): File to parse debuglevel (int): Parser debuglevel """ + self.scope.push() + + if not file: + # We use a path. + file = filename + else: + # We use a stream and try to extract the name from the stream. + if hasattr(file, 'name'): + if filename is not None: + raise AssertionError( + 'names of file and filename are in conflict') + filename = file.name() + else: + filename = '(stream)' + + self.target = filename if self.verbose: print('Compiling target: %s' % filename, file=sys.stderr) - self.scope.push() - self.target = filename self.result = self.parser.parse( - filename, lexer=self.lex, debug=debuglevel) + file, lexer=self.lex, debug=debuglevel) + self.post_parse() def post_parse(self): diff --git a/requirements.txt b/requirements.txt index 90412f0..4efa1dc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ ply +six diff --git a/test/test_lexer.py b/test/test_lexer.py new file mode 100644 index 0000000..60b573b --- /dev/null +++ b/test/test_lexer.py @@ -0,0 +1,47 @@ +""" +Unit tests for the lexer. +""" +from tempfile import NamedTemporaryFile +import unittest + +from six import StringIO + +from lesscpy.lessc.lexer import LessLexer + + +class TestLessLexer(unittest.TestCase): + """ + Unit tests for LessLexer + """ + + def setUp(self): + self.lexer = LessLexer() + + + def test_input_stream(self): + """ + It can load content from a string. + """ + file = StringIO(""" + @simple-var: 1; + """) + + self.lexer.input(file) + + token = self.lexer.token() + self.assertEqual('@simple-var', token.value) + + def test_input_path(self): + """ + It can load content from a path. + """ + file = NamedTemporaryFile() + file.write(b""" + @simple-var: 1; + """) + file.seek(0) + + self.lexer.input(file.name) + + token = self.lexer.token() + self.assertEqual('@simple-var', token.value) diff --git a/test/test_parser.py b/test/test_parser.py new file mode 100644 index 0000000..7a23605 --- /dev/null +++ b/test/test_parser.py @@ -0,0 +1,35 @@ +""" +Unit test for the parser. +""" +import unittest + +from six import StringIO + +from lesscpy.lessc.parser import LessParser + + +class TestLessParser(unittest.TestCase): + """ + Unit tests for LessParser. + """ + + def setUp(self): + self.parser = LessParser() + + def test_parse_stream(self): + """ + It can parse input from a file stream. + """ + stream = StringIO(""" + @nice-blue: #5B83AD; + """) + + self.parser.parse(file=stream) + + # A single object is parser which is the expected variable. + self.assertEqual(1, len(self.parser.result)) + # This is a stream without a name so it sets default name. + self.assertEqual('(stream)', self.parser.target) + variable = self.parser.result[0] + self.assertEqual('@nice-blue', variable.name) + self.assertEqual(['#5b83ad'], variable.value)