diff --git a/.gitignore b/.gitignore index 0e17dd1..3729791 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ -/sass.cpp -/dist/* -/sass.egg-info/* -/sass.so -/test.pyc -/sass.c -build +/sass/_sass.cpp +/dist/ +/build/ +*.egg-info/ +*.egg +*.egg/ +*.so +*.pyc +/.tox/ diff --git a/.travis.yml b/.travis.yml index d2f9874..1f60b62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,20 @@ language: python - -env: - - PIP_USE_MIRRORS=true python: + - "2.6" - "2.7" - "3.2" - "3.3" + - "3.4" before_install: - git submodule update --init --recursive install: - - sudo apt-get install cython python-dev - - pip install coverage coveralls + - pip install -e . + - pip install pyflakes + - pip install pep8 script: - - ./travis-ci.sh - -after_success: - - coveralls \ No newline at end of file + - python setup.py test + - pyflakes setup.py sass + - pep8 setup.py sass diff --git a/MANIFEST.in b/MANIFEST.in index 214c92e..1646218 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ -include *.cpp +include sass/_sass.cpp +include sass/tests/*.scss include README.rst recursive-include libsass *.cpp *.hpp *.h diff --git a/Makefile b/Makefile deleted file mode 100644 index a0a88b4..0000000 --- a/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -LDFLAGS="-Llibsass" - -all: libsass ext - -ext: sass.pyx setup.py - cython --cplus sass.pyx - python setup.py build - python setup.py sdist - -test: libsass ext - nosetests - -clean: - rm sass.cpp || true - rm -r build - python setup.py clean - -dist: all test - -upload: dist - python setup.py upload diff --git a/libsass b/libsass index 0388a62..fca1f75 160000 --- a/libsass +++ b/libsass @@ -1 +1 @@ -Subproject commit 0388a62b764c062bbcd45782ec09476c4673efcd +Subproject commit fca1f75a14fe5336c7b1a4b38c69bda7fece714f diff --git a/sass/__init__.py b/sass/__init__.py new file mode 100644 index 0000000..2819498 --- /dev/null +++ b/sass/__init__.py @@ -0,0 +1,41 @@ +import sass._sass as _sass +from sass._sass import CompileError + +OUTPUT_STYLES = { + 'nested': 0, + 'expanded': 1, + 'compact': 2, + 'compressed': 3, +} + + +def compile_string(string, include_paths=b'', image_path=b'', + output_style='nested'): + if not isinstance(string, bytes): + string = string.encode('utf-8') + if not isinstance(include_paths, bytes): + include_paths = include_paths.encode('utf-8') + if not isinstance(image_path, bytes): + image_path = image_path.encode('utf-8') + output_style = OUTPUT_STYLES[output_style] + + return _sass.compile_string( + string, include_paths, image_path, output_style + ) + + +def compile_file(path, include_paths=b'', image_path=b'', + output_style='nested'): + if not isinstance(path, bytes): + path = path.encode('utf-8') + if not isinstance(include_paths, bytes): + include_paths = include_paths.encode('utf-8') + if not isinstance(image_path, bytes): + image_path = image_path.encode('utf-8') + output_style = OUTPUT_STYLES[output_style] + + return _sass.compile_file( + path, include_paths, image_path, output_style + ) + +__all__ = ['compile_string', 'compile_file', 'CompileError'] diff --git a/sass.pyx b/sass/_sass.pyx similarity index 84% rename from sass.pyx rename to sass/_sass.pyx index dafe285..9380fd8 100644 --- a/sass.pyx +++ b/sass/_sass.pyx @@ -13,13 +13,6 @@ # limitations under the License. # -SASS_STYLE_NESTED = 0 -SASS_STYLE_EXPANDED = 1 -SASS_STYLE_COMPACT = 2 -SASS_STYLE_COMPRESSED = 3 - -__version__ = '2.2+libsass1.0.1' - cdef extern from "libsass/sass_interface.h": cdef struct sass_options: @@ -39,6 +32,8 @@ cdef extern from "libsass/sass_interface.h": cdef struct sass_file_context: char* input_path char* output_string + char* source_map_string + char* source_map_file sass_options options int error_status char* error_message @@ -55,11 +50,8 @@ cdef extern from "libsass/sass_interface.h": class CompileError(Exception): pass -def compile_string(bytes s, include_paths=None, image_path=None, int output_style=SASS_STYLE_NESTED): +def compile_string(bytes s, bytes include_paths, bytes image_path, int output_style): """Compiles SASS string to CSS""" - - include_paths = include_paths or b'' - image_path = image_path or b'' cdef sass_context* ctx = sass_new_context() try: ctx.source_string = s @@ -74,14 +66,13 @@ def compile_string(bytes s, include_paths=None, image_path=None, int output_styl sass_free_context(ctx) -def compile_file(bytes path, include_paths=None, int output_style=SASS_STYLE_NESTED): +def compile_file(bytes path, bytes include_paths, bytes image_path, int output_style): """Compiles SASS file to CSS string""" - - include_paths = include_paths or b'' cdef sass_file_context* ctx = sass_new_file_context() try: ctx.input_path = path ctx.options.include_paths = include_paths + ctx.options.image_path = image_path ctx.options.output_style = output_style sass_compile_file(ctx) if ctx.error_status: diff --git a/sass/tests/__init__.py b/sass/tests/__init__.py new file mode 100644 index 0000000..7ac9751 --- /dev/null +++ b/sass/tests/__init__.py @@ -0,0 +1,59 @@ +# Copyright 2012 Sergey Kirillov +# +# 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. +# +from __future__ import unicode_literals +from pkg_resources import resource_filename + +import unittest + +import sass + +scss_test_file = resource_filename(__name__, 'test.scss') + +compiled_result = b'''table.hl { + margin: 2em 0; } + table.hl td.ln { + text-align: right; } + +li { + font-family: serif; + font-weight: bold; + font-size: 1.2em; } +''' + + +class SASSTest(unittest.TestCase): + + def test_compile_string_with_bad_string(self): + try: + sass.compile_string(b'bad string') + except sass.CompileError: + pass + else: + self.fail() + + def test_compile_string(self): + with open(scss_test_file, 'rb') as scss_file: + result = sass.compile_string(scss_file.read()) + self.assertEqual(result, compiled_result) + + def test_compile_file(self): + result = sass.compile_file(scss_test_file) + self.assertEqual(result, compiled_result) + + +loader = unittest.TestLoader() +suite = unittest.TestSuite(( + loader.loadTestsFromTestCase(SASSTest), +)) diff --git a/sass/tests/test.scss b/sass/tests/test.scss new file mode 100644 index 0000000..d3300ea --- /dev/null +++ b/sass/tests/test.scss @@ -0,0 +1,14 @@ +table.hl { + margin: 2em 0; + td.ln { + text-align: right; + } +} + +li { + font: { + family: serif; + weight: bold; + size: 1.2em; + } +} diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index f121497..6701a0d --- a/setup.py +++ b/setup.py @@ -1,29 +1,25 @@ +#!/usr/bin/env python # Copyright 2012 Sergey Kirillov # -# Licensed under the Apache License, Version 2.0 (the "License"); +# 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, +# 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 os.path -from distutils.extension import Extension -from setuptools import setup, Extension - -try: - from Cython.Distutils import build_ext -except ImportError: - print("No Cython installed. Building from pregenerated C source.") - build_ext = None +from setuptools import setup, Extension, find_packages +from setuptools.command.sdist import sdist as _sdist +from setuptools.command.develop import develop as _develop -import os.path -here = os.path.dirname(os.path.abspath(__file__)) +cmdclass = {} libsass_sources = [ 'libsass/ast.cpp', @@ -33,6 +29,7 @@ 'libsass/context.cpp', 'libsass/contextualize.cpp', 'libsass/copy_c_str.cpp', + 'libsass/emscripten_wrapper.cpp', 'libsass/error_handling.cpp', 'libsass/eval.cpp', 'libsass/expand.cpp', @@ -46,42 +43,73 @@ 'libsass/prelexer.cpp', 'libsass/sass.cpp', 'libsass/sass_interface.cpp', + 'libsass/sass2scss/sass2scss.cpp', 'libsass/source_map.cpp', 'libsass/to_c.cpp', 'libsass/to_string.cpp', - 'libsass/units.cpp' + 'libsass/trim.cpp', + 'libsass/units.cpp', + 'libsass/utf8_string.cpp', + 'libsass/util.cpp', ] -if build_ext: - sources = libsass_sources + ["sass.pyx"] - cmdclass = {'build_ext': build_ext} +# If running from git repository, rebuild _sass.cpp from cython source, +# otherwise assume nothing has changed +if os.path.exists('.git/'): + # Force installation of Cython + from setuptools.dist import Distribution + Distribution(dict(setup_requires=['Cython'])) + + from Cython.Distutils import build_ext + cmdclass['build_ext'] = build_ext + + sources = libsass_sources + ['sass/_sass.pyx'] else: - sources = libsass_sources + ["sass.cpp"] - cmdclass = {} + sources = libsass_sources + ['sass/_sass.cpp'] + +ext_modules = [ + Extension( + 'sass._sass', sources, + libraries=['stdc++'], library_dirs=['./libsass'], + include_dirs=['.', 'libsass'], language='c++' + ), +] + + +class sdist(_sdist): + def run(self): + from Cython.Build import cythonize + print("pre-compiling cython") + cythonize(ext_modules) + + _sdist.run(self) + +cmdclass['sdist'] = sdist + + +class develop(_develop): + def run(self): + from subprocess import check_call + print("retrieving libsass submodule") + if os.path.exists('.git'): + check_call(['git', 'submodule', 'update', '--init', '--recursive']) + _develop.run(self) -ext_modules = [Extension("sass", - sources, - libraries=['stdc++'], - library_dirs=['./libsass'], - include_dirs=['.', 'libsass'], - language='c++' -)] +cmdclass['develop'] = develop setup( - name = 'sass', - cmdclass = cmdclass, - ext_modules = ext_modules, - version = '2.2', - author = 'Sergey Kirilov', - author_email = 'sergey.kirillov@gmail.com', - url='https://github.com/pistolero/python-scss', - install_requires=[], - extras_require = { -# 'develop': ['Cython'] - }, - tests_require = ['nose'], - license="Apache License 2.0", - keywords="sass scss libsass", - description='Python bindings for libsass', - long_description=open(os.path.join(here, 'README.rst'), 'rb').read().decode('utf-8') + name='sass', + cmdclass=cmdclass, + ext_modules=ext_modules, + version='2.2', + author='Sergey Kirilov', + author_email='sergey.kirillov@gmail.com', + url='https://github.com/pistolero/python-scss', + install_requires=[], + test_suite='sass.tests.suite', + packages=find_packages(), + license='Apache License 2.0', + keywords='sass scss libsass', + description='Python bindings for libsass', + zip_safe=False, ) diff --git a/test.py b/test.py deleted file mode 100644 index 965b2e7..0000000 --- a/test.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright 2012 Sergey Kirillov -# -# 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 sass -from nose.tools import eq_, assert_equal, raises - -@raises(sass.CompileError) -def test1(): - result = sass.compile_string(b'asd', '', sass.SASS_STYLE_NESTED) - eq_(result, b'asd') - - -def test2(): - result = sass.compile_string(b'''table.hl { - margin: 2em 0; - td.ln { - text-align: right; - } -} - -li { - font: { - family: serif; - weight: bold; - size: 1.2em; - } -}''', '', sass.SASS_STYLE_NESTED) - - expected = b'''table.hl { - margin: 2em 0; } - table.hl td.ln { - text-align: right; } - -li { - font-family: serif; - font-weight: bold; - font-size: 1.2em; } -''' - - assert_equal(result, expected) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..2725017 --- /dev/null +++ b/tox.ini @@ -0,0 +1,10 @@ +[tox] +envlist = py26,py27,py34 +[testenv] +deps = + pyflakes + pep8 +commands = + python setup.py test + pyflakes setup.py sass + pep8 setup.py sass diff --git a/travis-ci.sh b/travis-ci.sh deleted file mode 100755 index d60516d..0000000 --- a/travis-ci.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -set -e - -echo "Building" -make - -echo "Installing sdist" -pip install dist/sass-*.tar.gz - -echo "Running tests" -#nosetests -nosetests --with-coverage -