New Upstream Release - python-lesscpy
Ready changes
Summary
Merged new upstream version: 0.15.1+ds (was: 0.13.0+ds).
Resulting package
Built on 2022-11-13T19:23 (took 2m16s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-releases python3-lesscpy
Lintian Result
Diff
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..51f0419
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,5 @@
+James Page <james.page@ubuntu.com>
+Patrick <patrick@koumbit.org>
+Sascha Peilicke <sascha@peilicke.de>
+Simon de Haan <simon@praekeltfoundation.org>
+Jóhann T Maríusson <jtm@robot.is>
diff --git a/PKG-INFO b/PKG-INFO
index 39cd933..78c1d51 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,182 +1,11 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
Name: lesscpy
-Version: 0.13.0
+Version: 0.15.1
Summary: Python LESS compiler
Home-page: https://github.com/lesscpy/lesscpy
Author: Jóhann T Maríusson
Author-email: jtm@robot.is
License: MIT
-Description-Content-Type: UNKNOWN
-Description: LESSCPY
- =======
-
- .. image:: https://travis-ci.org/lesscpy/lesscpy.png?branch=master
- :target: https://travis-ci.org/lesscpy/lesscpy
-
- .. image:: https://coveralls.io/repos/lesscpy/lesscpy/badge.png
- :target: https://coveralls.io/r/lesscpy/lesscpy
-
- .. image:: https://pypip.in/d/lesscpy/badge.png
- :target: https://pypi.python.org/pypi/lesscpy
-
- .. image:: https://pypip.in/v/lesscpy/badge.png
- :target: https://pypi.python.org/pypi/lesscpy
-
- .. image:: https://pypip.in/wheel/lesscpy/badge.png
- :target: https://pypi.python.org/pypi/lesscpy
- :alt: Wheel Status
-
- .. image:: https://pypip.in/license/lesscpy/badge.png
- :target: https://pypi.python.org/pypi/lesscpy
- :alt: License
-
- Python LESS Compiler.
-
- A compiler written in Python for the LESS language. For those of us not willing
- or able to have node.js installed in our environment. Not all features of LESS
- are supported (yet). Some features wil probably never be supported (JavaScript
- evaluation). This program uses PLY (Python Lex-Yacc) to tokenize / parse the
- input and is considerably slower than the NodeJS compiler. The plan is to
- utilize this to build in proper syntax checking and perhaps YUI compressing.
-
- This is an early version, so you are likely to find bugs.
-
- For more information on LESS:
- http://lesscss.org/ or https://github.com/cloudhead/less.js
-
- Development files:
- https://github.com/lesscpy/lesscpy
-
-
- Supported features
- ------------------
-
- - Variables
- - String interpolation
- - Mixins (nested, calls, closures, recursive)
- - Guard expressions
- - Parametered mixins (class / id)
- - @arguments
- - Nesting
- - Escapes ~/e()
- - Expressions
- - Keyframe blocks
- - Color functions (lighten, darken, saturate, desaturate, spin, hue, mix,
- saturation, lightness)
- - Other functions (round, increment, decrement, format '%(', ...)
-
-
- Differences from less.js
- ------------------------
-
- - All colors are auto-formatted to #nnnnnn. eg, #f7e923
- - Does not preserve CSS comments
-
-
- Not supported
- -------------
-
- - JavaScript evaluation
-
-
- Requirements
- ------------
-
- - Python 2.6, 2.7, 3.3, 3.4, or 3.5
- - ply (Python Lex-Yacc) (check requirements.txt)
-
-
- Installation
- ------------
-
- To install lesscpy from the `Python Package Index`_, simply:
-
- .. code-block:: bash
-
- $ pip install lesscpy
-
- To do a local system-wide install:
-
- .. code-block:: bash
-
- python setup.py install
-
- Or simply place the package into your Python path. Or rather use packages
- provided by your distribution (openSUSE has them at least).
-
-
- Compiler script Usage
- ---------------------
-
- .. code-block:: text
-
- usage: lesscpy [-h] [-v] [-I INCLUDE] [-V] [-x] [-X] [-t] [-s SPACES] [-o OUT]
- [-r] [-f] [-m] [-D] [-g] [-S] [-L] [-N]
- target
-
- LessCss Compiler
-
- positional arguments:
- target less file or directory
-
- optional arguments:
- -h, --help show this help message and exit
- -v, --version show program's version number and exit
- -I INCLUDE, --include INCLUDE
- Included less-files (comma separated)
- -V, --verbose Verbose mode
-
- Formatting options:
- -x, --minify Minify output
- -X, --xminify Minify output, no end of block newlines
- -t, --tabs Use tabs
- -s SPACES, --spaces SPACES
- Number of startline spaces (default 2)
-
- Directory options:
- Compiles all \*.less files in directory that have a newer timestamp than
- it's css file.
-
- -o OUT, --out OUT Output directory
- -r, --recurse Recursive into subdirectorys
- -f, --force Force recompile on all files
- -m, --min-ending Add '.min' into output filename. eg, name.min.css
- -D, --dry-run Dry run, do not write files
-
- Debugging:
- -g, --debug Debugging information
- -S, --scopemap Scopemap
- -L, --lex-only Run lexer on target
- -N, --no-css No css output
-
-
- Python usage
- ------------
-
- If you want to use the compiler from within Python, you can do it like this:
-
- .. code-block:: python
-
- import lesscpy
- from six import StringIO
-
- print(lesscpy.compile(StringIO(u"a { border-width: 2px * 3; }"), minify=True))
-
- The output will be:
-
- .. code-block:: text
-
- a{border-width:6px;}
-
- License
- -------
-
- See the LICENSE file
-
-
- .. _`Python Package Index`: https://pypi.python.org/pypi/lesscpy
-
-Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
@@ -185,10 +14,188 @@ Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2.6
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Code Generators
Classifier: Topic :: Software Development :: Pre-processors
+License-File: LICENSE
+License-File: AUTHORS
+
+LESSCPY
+=======
+
+.. image:: https://travis-ci.org/lesscpy/lesscpy.png?branch=master
+ :target: https://travis-ci.org/lesscpy/lesscpy
+
+.. image:: https://coveralls.io/repos/lesscpy/lesscpy/badge.png
+ :target: https://coveralls.io/r/lesscpy/lesscpy
+
+.. image:: https://img.shields.io/pypi/dm/lesscpy.svg
+ :target: https://pypi.python.org/pypi/lesscpy
+
+.. image:: https://img.shields.io/pypi/v/lesscpy.svg
+ :target: https://pypi.python.org/pypi/lesscpy
+
+.. image:: https://img.shields.io/pypi/wheel/lesscpy.svg
+ :target: https://pypi.python.org/pypi/lesscpy
+ :alt: Wheel Status
+
+.. image:: https://img.shields.io/pypi/l/lesscpy.svg
+ :target: https://pypi.python.org/pypi/lesscpy
+ :alt: License
+
+Python LESS Compiler.
+
+A compiler written in Python for the LESS language. For those of us not willing
+or able to have node.js installed in our environment. Not all features of LESS
+are supported (yet). Some features wil probably never be supported (JavaScript
+evaluation). This program uses PLY (Python Lex-Yacc) to tokenize / parse the
+input and is considerably slower than the NodeJS compiler. The plan is to
+utilize this to build in proper syntax checking and perhaps YUI compressing.
+
+This is an early version, so you are likely to find bugs.
+
+For more information on LESS:
+ http://lesscss.org/ or https://github.com/cloudhead/less.js
+
+Development files:
+ https://github.com/lesscpy/lesscpy
+
+
+Supported features
+------------------
+
+- Variables
+- String interpolation
+- Mixins (nested, calls, closures, recursive)
+- Guard expressions
+- Parametered mixins (class / id)
+- @arguments
+- Nesting
+- Escapes ~/e()
+- Expressions
+- Keyframe blocks
+- Color functions (lighten, darken, saturate, desaturate, spin, hue, mix,
+ saturation, lightness)
+- Other functions (round, increment, decrement, format '%(', ...)
+
+
+Differences from less.js
+------------------------
+
+- All colors are auto-formatted to #nnnnnn. eg, #f7e923
+- Does not preserve CSS comments
+
+
+Not supported
+-------------
+
+- JavaScript evaluation
+
+
+Requirements
+------------
+
+- Python 3.7 - 3.11,
+- ply (Python Lex-Yacc) (check requirements.txt)
+
+Installation
+------------
+
+To install lesscpy from the `Python Package Index`_, simply:
+
+.. code-block:: bash
+
+ $ pip install lesscpy
+
+To do a local system-wide install:
+
+.. code-block:: bash
+
+ python setup.py install
+
+Or simply place the package into your Python path. Or rather use packages
+provided by your distribution (openSUSE has them at least).
+
+
+Compiler script Usage
+---------------------
+
+.. code-block:: text
+
+ usage: lesscpy [-h] [-v] [-I INCLUDE] [-V] [-C] [-x] [-X] [-t] [-s SPACES]
+ [-o OUT] [-r] [-f] [-m] [-D] [-g] [-S] [-L] [-N]
+ target [output]
+
+ LessCss Compiler
+
+ positional arguments:
+ target less file or directory
+ output output file path
+
+ optional arguments:
+ -h, --help show this help message and exit
+ -v, --version show program's version number and exit
+ -I INCLUDE, --include INCLUDE
+ Included less-files (comma separated)
+ -V, --verbose Verbose mode
+ -C, --dont_create_dirs
+ Creates directories when outputing files (lessc non-
+ compatible)
+
+ Formatting options:
+ -x, --minify Minify output
+ -X, --xminify Minify output, no end of block newlines
+ -t, --tabs Use tabs
+ -s SPACES, --spaces SPACES
+ Number of startline spaces (default 2)
+
+ Directory options:
+ Compiles all *.less files in directory that have a newer timestamp than
+ it's css file.
+
+ -o OUT, --out OUT Output directory
+ -r, --recurse Recursive into subdirectorys
+ -f, --force Force recompile on all files
+ -m, --min-ending Add '.min' into output filename. eg, name.min.css
+ -D, --dry-run Dry run, do not write files
+
+ Debugging:
+ -g, --debug Debugging information
+ -S, --scopemap Scopemap
+ -L, --lex-only Run lexer on target
+ -N, --no-css No css output
+
+ << jtm@robot.is @_o >>
+
+Python usage
+------------
+
+If you want to use the compiler from within Python, you can do it like this:
+
+.. code-block:: python
+
+ import lesscpy
+
+ print(lesscpy.compile(StringIO(u"a { border-width: 2px * 3; }"), minify=True))
+
+The output will be:
+
+.. code-block:: text
+
+ a{border-width:6px;}
+
+License
+-------
+
+See the LICENSE file
+
+
+.. _`Python Package Index`: https://pypi.python.org/pypi/lesscpy
diff --git a/README.rst b/README.rst
index 06c4ff9..4e40bf3 100644
--- a/README.rst
+++ b/README.rst
@@ -7,17 +7,17 @@ LESSCPY
.. image:: https://coveralls.io/repos/lesscpy/lesscpy/badge.png
:target: https://coveralls.io/r/lesscpy/lesscpy
-.. image:: https://pypip.in/d/lesscpy/badge.png
+.. image:: https://img.shields.io/pypi/dm/lesscpy.svg
:target: https://pypi.python.org/pypi/lesscpy
-.. image:: https://pypip.in/v/lesscpy/badge.png
+.. image:: https://img.shields.io/pypi/v/lesscpy.svg
:target: https://pypi.python.org/pypi/lesscpy
-.. image:: https://pypip.in/wheel/lesscpy/badge.png
+.. image:: https://img.shields.io/pypi/wheel/lesscpy.svg
:target: https://pypi.python.org/pypi/lesscpy
:alt: Wheel Status
-.. image:: https://pypip.in/license/lesscpy/badge.png
+.. image:: https://img.shields.io/pypi/l/lesscpy.svg
:target: https://pypi.python.org/pypi/lesscpy
:alt: License
@@ -73,9 +73,8 @@ Not supported
Requirements
------------
-- Python 2.6, 2.7, 3.3, 3.4, or 3.5
+- Python 3.7 - 3.11,
- ply (Python Lex-Yacc) (check requirements.txt)
-
Installation
------------
@@ -101,14 +100,15 @@ Compiler script Usage
.. code-block:: text
- usage: lesscpy [-h] [-v] [-I INCLUDE] [-V] [-x] [-X] [-t] [-s SPACES] [-o OUT]
- [-r] [-f] [-m] [-D] [-g] [-S] [-L] [-N]
- target
+ usage: lesscpy [-h] [-v] [-I INCLUDE] [-V] [-C] [-x] [-X] [-t] [-s SPACES]
+ [-o OUT] [-r] [-f] [-m] [-D] [-g] [-S] [-L] [-N]
+ target [output]
LessCss Compiler
positional arguments:
target less file or directory
+ output output file path
optional arguments:
-h, --help show this help message and exit
@@ -116,6 +116,9 @@ Compiler script Usage
-I INCLUDE, --include INCLUDE
Included less-files (comma separated)
-V, --verbose Verbose mode
+ -C, --dont_create_dirs
+ Creates directories when outputing files (lessc non-
+ compatible)
Formatting options:
-x, --minify Minify output
@@ -125,7 +128,7 @@ Compiler script Usage
Number of startline spaces (default 2)
Directory options:
- Compiles all \*.less files in directory that have a newer timestamp than
+ Compiles all *.less files in directory that have a newer timestamp than
it's css file.
-o OUT, --out OUT Output directory
@@ -140,6 +143,7 @@ Compiler script Usage
-L, --lex-only Run lexer on target
-N, --no-css No css output
+ << jtm@robot.is @_o >>
Python usage
------------
@@ -149,7 +153,6 @@ If you want to use the compiler from within Python, you can do it like this:
.. code-block:: python
import lesscpy
- from six import StringIO
print(lesscpy.compile(StringIO(u"a { border-width: 2px * 3; }"), minify=True))
diff --git a/debian/changelog b/debian/changelog
index 57b32e3..7b7ac5b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+python-lesscpy (0.15.1+ds-1) UNRELEASED; urgency=low
+
+ * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk> Sun, 13 Nov 2022 19:22:01 -0000
+
python-lesscpy (0.13.0+ds-2) unstable; urgency=medium
* Unnecessary postinst and prerm and add preinst for upgrade
diff --git a/lesscpy.egg-info/PKG-INFO b/lesscpy.egg-info/PKG-INFO
index 39cd933..78c1d51 100644
--- a/lesscpy.egg-info/PKG-INFO
+++ b/lesscpy.egg-info/PKG-INFO
@@ -1,182 +1,11 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
Name: lesscpy
-Version: 0.13.0
+Version: 0.15.1
Summary: Python LESS compiler
Home-page: https://github.com/lesscpy/lesscpy
Author: Jóhann T Maríusson
Author-email: jtm@robot.is
License: MIT
-Description-Content-Type: UNKNOWN
-Description: LESSCPY
- =======
-
- .. image:: https://travis-ci.org/lesscpy/lesscpy.png?branch=master
- :target: https://travis-ci.org/lesscpy/lesscpy
-
- .. image:: https://coveralls.io/repos/lesscpy/lesscpy/badge.png
- :target: https://coveralls.io/r/lesscpy/lesscpy
-
- .. image:: https://pypip.in/d/lesscpy/badge.png
- :target: https://pypi.python.org/pypi/lesscpy
-
- .. image:: https://pypip.in/v/lesscpy/badge.png
- :target: https://pypi.python.org/pypi/lesscpy
-
- .. image:: https://pypip.in/wheel/lesscpy/badge.png
- :target: https://pypi.python.org/pypi/lesscpy
- :alt: Wheel Status
-
- .. image:: https://pypip.in/license/lesscpy/badge.png
- :target: https://pypi.python.org/pypi/lesscpy
- :alt: License
-
- Python LESS Compiler.
-
- A compiler written in Python for the LESS language. For those of us not willing
- or able to have node.js installed in our environment. Not all features of LESS
- are supported (yet). Some features wil probably never be supported (JavaScript
- evaluation). This program uses PLY (Python Lex-Yacc) to tokenize / parse the
- input and is considerably slower than the NodeJS compiler. The plan is to
- utilize this to build in proper syntax checking and perhaps YUI compressing.
-
- This is an early version, so you are likely to find bugs.
-
- For more information on LESS:
- http://lesscss.org/ or https://github.com/cloudhead/less.js
-
- Development files:
- https://github.com/lesscpy/lesscpy
-
-
- Supported features
- ------------------
-
- - Variables
- - String interpolation
- - Mixins (nested, calls, closures, recursive)
- - Guard expressions
- - Parametered mixins (class / id)
- - @arguments
- - Nesting
- - Escapes ~/e()
- - Expressions
- - Keyframe blocks
- - Color functions (lighten, darken, saturate, desaturate, spin, hue, mix,
- saturation, lightness)
- - Other functions (round, increment, decrement, format '%(', ...)
-
-
- Differences from less.js
- ------------------------
-
- - All colors are auto-formatted to #nnnnnn. eg, #f7e923
- - Does not preserve CSS comments
-
-
- Not supported
- -------------
-
- - JavaScript evaluation
-
-
- Requirements
- ------------
-
- - Python 2.6, 2.7, 3.3, 3.4, or 3.5
- - ply (Python Lex-Yacc) (check requirements.txt)
-
-
- Installation
- ------------
-
- To install lesscpy from the `Python Package Index`_, simply:
-
- .. code-block:: bash
-
- $ pip install lesscpy
-
- To do a local system-wide install:
-
- .. code-block:: bash
-
- python setup.py install
-
- Or simply place the package into your Python path. Or rather use packages
- provided by your distribution (openSUSE has them at least).
-
-
- Compiler script Usage
- ---------------------
-
- .. code-block:: text
-
- usage: lesscpy [-h] [-v] [-I INCLUDE] [-V] [-x] [-X] [-t] [-s SPACES] [-o OUT]
- [-r] [-f] [-m] [-D] [-g] [-S] [-L] [-N]
- target
-
- LessCss Compiler
-
- positional arguments:
- target less file or directory
-
- optional arguments:
- -h, --help show this help message and exit
- -v, --version show program's version number and exit
- -I INCLUDE, --include INCLUDE
- Included less-files (comma separated)
- -V, --verbose Verbose mode
-
- Formatting options:
- -x, --minify Minify output
- -X, --xminify Minify output, no end of block newlines
- -t, --tabs Use tabs
- -s SPACES, --spaces SPACES
- Number of startline spaces (default 2)
-
- Directory options:
- Compiles all \*.less files in directory that have a newer timestamp than
- it's css file.
-
- -o OUT, --out OUT Output directory
- -r, --recurse Recursive into subdirectorys
- -f, --force Force recompile on all files
- -m, --min-ending Add '.min' into output filename. eg, name.min.css
- -D, --dry-run Dry run, do not write files
-
- Debugging:
- -g, --debug Debugging information
- -S, --scopemap Scopemap
- -L, --lex-only Run lexer on target
- -N, --no-css No css output
-
-
- Python usage
- ------------
-
- If you want to use the compiler from within Python, you can do it like this:
-
- .. code-block:: python
-
- import lesscpy
- from six import StringIO
-
- print(lesscpy.compile(StringIO(u"a { border-width: 2px * 3; }"), minify=True))
-
- The output will be:
-
- .. code-block:: text
-
- a{border-width:6px;}
-
- License
- -------
-
- See the LICENSE file
-
-
- .. _`Python Package Index`: https://pypi.python.org/pypi/lesscpy
-
-Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
@@ -185,10 +14,188 @@ Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2.6
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Code Generators
Classifier: Topic :: Software Development :: Pre-processors
+License-File: LICENSE
+License-File: AUTHORS
+
+LESSCPY
+=======
+
+.. image:: https://travis-ci.org/lesscpy/lesscpy.png?branch=master
+ :target: https://travis-ci.org/lesscpy/lesscpy
+
+.. image:: https://coveralls.io/repos/lesscpy/lesscpy/badge.png
+ :target: https://coveralls.io/r/lesscpy/lesscpy
+
+.. image:: https://img.shields.io/pypi/dm/lesscpy.svg
+ :target: https://pypi.python.org/pypi/lesscpy
+
+.. image:: https://img.shields.io/pypi/v/lesscpy.svg
+ :target: https://pypi.python.org/pypi/lesscpy
+
+.. image:: https://img.shields.io/pypi/wheel/lesscpy.svg
+ :target: https://pypi.python.org/pypi/lesscpy
+ :alt: Wheel Status
+
+.. image:: https://img.shields.io/pypi/l/lesscpy.svg
+ :target: https://pypi.python.org/pypi/lesscpy
+ :alt: License
+
+Python LESS Compiler.
+
+A compiler written in Python for the LESS language. For those of us not willing
+or able to have node.js installed in our environment. Not all features of LESS
+are supported (yet). Some features wil probably never be supported (JavaScript
+evaluation). This program uses PLY (Python Lex-Yacc) to tokenize / parse the
+input and is considerably slower than the NodeJS compiler. The plan is to
+utilize this to build in proper syntax checking and perhaps YUI compressing.
+
+This is an early version, so you are likely to find bugs.
+
+For more information on LESS:
+ http://lesscss.org/ or https://github.com/cloudhead/less.js
+
+Development files:
+ https://github.com/lesscpy/lesscpy
+
+
+Supported features
+------------------
+
+- Variables
+- String interpolation
+- Mixins (nested, calls, closures, recursive)
+- Guard expressions
+- Parametered mixins (class / id)
+- @arguments
+- Nesting
+- Escapes ~/e()
+- Expressions
+- Keyframe blocks
+- Color functions (lighten, darken, saturate, desaturate, spin, hue, mix,
+ saturation, lightness)
+- Other functions (round, increment, decrement, format '%(', ...)
+
+
+Differences from less.js
+------------------------
+
+- All colors are auto-formatted to #nnnnnn. eg, #f7e923
+- Does not preserve CSS comments
+
+
+Not supported
+-------------
+
+- JavaScript evaluation
+
+
+Requirements
+------------
+
+- Python 3.7 - 3.11,
+- ply (Python Lex-Yacc) (check requirements.txt)
+
+Installation
+------------
+
+To install lesscpy from the `Python Package Index`_, simply:
+
+.. code-block:: bash
+
+ $ pip install lesscpy
+
+To do a local system-wide install:
+
+.. code-block:: bash
+
+ python setup.py install
+
+Or simply place the package into your Python path. Or rather use packages
+provided by your distribution (openSUSE has them at least).
+
+
+Compiler script Usage
+---------------------
+
+.. code-block:: text
+
+ usage: lesscpy [-h] [-v] [-I INCLUDE] [-V] [-C] [-x] [-X] [-t] [-s SPACES]
+ [-o OUT] [-r] [-f] [-m] [-D] [-g] [-S] [-L] [-N]
+ target [output]
+
+ LessCss Compiler
+
+ positional arguments:
+ target less file or directory
+ output output file path
+
+ optional arguments:
+ -h, --help show this help message and exit
+ -v, --version show program's version number and exit
+ -I INCLUDE, --include INCLUDE
+ Included less-files (comma separated)
+ -V, --verbose Verbose mode
+ -C, --dont_create_dirs
+ Creates directories when outputing files (lessc non-
+ compatible)
+
+ Formatting options:
+ -x, --minify Minify output
+ -X, --xminify Minify output, no end of block newlines
+ -t, --tabs Use tabs
+ -s SPACES, --spaces SPACES
+ Number of startline spaces (default 2)
+
+ Directory options:
+ Compiles all *.less files in directory that have a newer timestamp than
+ it's css file.
+
+ -o OUT, --out OUT Output directory
+ -r, --recurse Recursive into subdirectorys
+ -f, --force Force recompile on all files
+ -m, --min-ending Add '.min' into output filename. eg, name.min.css
+ -D, --dry-run Dry run, do not write files
+
+ Debugging:
+ -g, --debug Debugging information
+ -S, --scopemap Scopemap
+ -L, --lex-only Run lexer on target
+ -N, --no-css No css output
+
+ << jtm@robot.is @_o >>
+
+Python usage
+------------
+
+If you want to use the compiler from within Python, you can do it like this:
+
+.. code-block:: python
+
+ import lesscpy
+
+ print(lesscpy.compile(StringIO(u"a { border-width: 2px * 3; }"), minify=True))
+
+The output will be:
+
+.. code-block:: text
+
+ a{border-width:6px;}
+
+License
+-------
+
+See the LICENSE file
+
+
+.. _`Python Package Index`: https://pypi.python.org/pypi/lesscpy
diff --git a/lesscpy.egg-info/SOURCES.txt b/lesscpy.egg-info/SOURCES.txt
index 6aaa820..99a34b2 100644
--- a/lesscpy.egg-info/SOURCES.txt
+++ b/lesscpy.egg-info/SOURCES.txt
@@ -1,3 +1,4 @@
+AUTHORS
LICENSE
MANIFEST.in
README.rst
@@ -53,7 +54,6 @@ test/test_color.py
test/test_color.pyc
test/test_expression.py
test/test_expression.pyc
-test/test_font_awesome.pyc
test/test_identifier.py
test/test_identifier.pyc
test/test_issues.py
@@ -68,6 +68,30 @@ test/test_pycompile.py
test/test_pycompile.pyc
test/test_utility.py
test/test_utility.pyc
+test/__pycache__/__init__.cpython-37.pyc
+test/__pycache__/__init__.cpython-39.pyc
+test/__pycache__/core.cpython-37.pyc
+test/__pycache__/core.cpython-39.pyc
+test/__pycache__/test_bootstrap3.cpython-37.pyc
+test/__pycache__/test_bootstrap3.cpython-39.pyc
+test/__pycache__/test_color.cpython-37.pyc
+test/__pycache__/test_color.cpython-39.pyc
+test/__pycache__/test_expression.cpython-37.pyc
+test/__pycache__/test_expression.cpython-39.pyc
+test/__pycache__/test_identifier.cpython-37.pyc
+test/__pycache__/test_identifier.cpython-39.pyc
+test/__pycache__/test_issues.cpython-37.pyc
+test/__pycache__/test_issues.cpython-39.pyc
+test/__pycache__/test_less.cpython-37.pyc
+test/__pycache__/test_less.cpython-39.pyc
+test/__pycache__/test_lexer.cpython-37.pyc
+test/__pycache__/test_lexer.cpython-39.pyc
+test/__pycache__/test_parser.cpython-37.pyc
+test/__pycache__/test_parser.cpython-39.pyc
+test/__pycache__/test_pycompile.cpython-37.pyc
+test/__pycache__/test_pycompile.cpython-39.pyc
+test/__pycache__/test_utility.cpython-37.pyc
+test/__pycache__/test_utility.cpython-39.pyc
test/bootstrap3/css/bootstrap.css
test/bootstrap3/css/bootstrap.min.css
test/bootstrap3/css/theme.css
@@ -120,6 +144,8 @@ test/css/colors.css
test/css/colors.min.css
test/css/comments.css
test/css/comments.min.css
+test/css/css-variables.css
+test/css/css-variables.min.css
test/css/elements.css
test/css/elements.min.css
test/css/escapes.css
@@ -138,6 +164,10 @@ test/css/imports.css
test/css/imports.min.css
test/css/keyframes.css
test/css/keyframes.min.css
+test/css/media-nested-2.css
+test/css/media-nested-2.min.css
+test/css/media-nested.css
+test/css/media-nested.min.css
test/css/media.css
test/css/media.min.css
test/css/mixin-args-guards.css
@@ -184,11 +214,14 @@ test/css/issues/issue31.css
test/css/issues/issue4.css
test/css/issues/issue5.css
test/css/issues/issue6.css
+test/css/issues/issue61.css
+test/css/issues/issue65.css
test/css/issues/issue67.css
test/less/argb.less
test/less/calls.less
test/less/colors.less
test/less/comments.less
+test/less/css-variables.less
test/less/elements.less
test/less/escapes.less
test/less/expressions.less
@@ -198,6 +231,8 @@ test/less/identifiers.less
test/less/ie.less
test/less/imports.less
test/less/keyframes.less
+test/less/media-nested-2.less
+test/less/media-nested.less
test/less/media.less
test/less/mixin-args-guards.less
test/less/mixin-args-local-calls.less
@@ -228,4 +263,6 @@ test/less/issues/issue31.less
test/less/issues/issue4.less
test/less/issues/issue5.less
test/less/issues/issue6.less
+test/less/issues/issue61.less
+test/less/issues/issue65.less
test/less/issues/issue67.less
\ No newline at end of file
diff --git a/lesscpy.egg-info/entry_points.txt b/lesscpy.egg-info/entry_points.txt
index 3db0fb1..ccb4df2 100644
--- a/lesscpy.egg-info/entry_points.txt
+++ b/lesscpy.egg-info/entry_points.txt
@@ -1,3 +1,2 @@
[console_scripts]
lesscpy = lesscpy.scripts.compiler:run
-
diff --git a/lesscpy.egg-info/requires.txt b/lesscpy.egg-info/requires.txt
index 4efa1dc..90412f0 100644
--- a/lesscpy.egg-info/requires.txt
+++ b/lesscpy.egg-info/requires.txt
@@ -1,2 +1 @@
ply
-six
diff --git a/lesscpy/__init__.py b/lesscpy/__init__.py
index 3ac4ae4..d22e489 100644
--- a/lesscpy/__init__.py
+++ b/lesscpy/__init__.py
@@ -1,4 +1,4 @@
-__version_info__ = ('0', '13', '0')
+__version_info__ = ('0', '15', '1')
__version__ = '.'.join(__version_info__)
@@ -18,4 +18,3 @@ def compile(file, minify=False, xminify=False, tabs=False, spaces=True):
p.parse(file=file)
f = formatter.Formatter(opt)
return f.format(p)
-
diff --git a/lesscpy/__main__.py b/lesscpy/__main__.py
index 92b5465..7c9e459 100644
--- a/lesscpy/__main__.py
+++ b/lesscpy/__main__.py
@@ -2,4 +2,3 @@ from lesscpy.scripts.compiler import run
if __name__ == '__main__':
run()
-
diff --git a/lesscpy/lessc/color.py b/lesscpy/lessc/color.py
index 84b679c..d734233 100644
--- a/lesscpy/lessc/color.py
+++ b/lesscpy/lessc/color.py
@@ -12,13 +12,12 @@ import operator
import colorsys
import re
-import six
+from six import string_types
from . import utility
from lesscpy.lib import colors
-class Color():
-
+class Color:
def process(self, expression):
""" Process color expression
args:
@@ -70,10 +69,9 @@ class Color():
return self._rgbatohex(list(map(int, args)))
except ValueError:
if all((a for a in args
- if a[-1] == '%'
- and 100 >= int(a[:-1]) >= 0)):
- return self._rgbatohex([int(a[:-1]) * 255 / 100.0
- for a in args])
+ if a[-1] == '%' and 100 >= int(a[:-1]) >= 0)):
+ return self._rgbatohex(
+ [int(a[:-1]) * 255 / 100.0 for a in args])
raise ValueError('Illegal color values')
def rgba(self, *args):
@@ -94,15 +92,14 @@ class Color():
return self._rgbatohex(list(map(int, args)))
except ValueError:
if all((a for a in args
- if a[-1] == '%'
- and 100 >= int(a[:-1]) >= 0)):
+ if a[-1] == '%' and 100 >= int(a[:-1]) >= 0)):
alpha = list(args)[3]
if alpha[-1] == '%' and float(alpha[:-1]) == 0:
- values = self._rgbatohex_raw([int(a[:-1]) * 255 / 100.0
- for a in args])
+ values = self._rgbatohex_raw(
+ [int(a[:-1]) * 255 / 100.0 for a in args])
return "rgba(%s)" % ','.join([str(a) for a in values])
- return self._rgbatohex([int(a[:-1]) * 255 / 100.0
- for a in args])
+ return self._rgbatohex(
+ [int(a[:-1]) * 255 / 100.0 for a in args])
raise ValueError('Illegal color values')
def argb(self, *args):
@@ -135,16 +132,17 @@ class Color():
if fval > 1:
rgb = [255] + rgb[1:] # Clip invalid integer/float values
elif 1 >= fval >= 0:
- rgb = [fval * 256] + rgb[1:] # Convert 0-1 to 0-255 range for _rgbatohex
+ rgb = [
+ fval * 256
+ ] + rgb[1:] # Convert 0-1 to 0-255 range for _rgbatohex
else:
rgb = [0] + rgb[1:] # Clip lower bound
return self._rgbatohex(list(map(int, rgb)))
except ValueError:
if all((a for a in rgb
- if a[-1] == '%'
- and 100 >= int(a[:-1]) >= 0)):
- return self._rgbatohex([int(a[:-1]) * 255 / 100.0
- for a in rgb])
+ if a[-1] == '%' and 100 >= int(a[:-1]) >= 0)):
+ return self._rgbatohex(
+ [int(a[:-1]) * 255 / 100.0 for a in rgb])
raise ValueError('Illegal color values')
def hsl(self, *args):
@@ -158,7 +156,8 @@ class Color():
return self.hsla(*args)
elif len(args) == 3:
h, s, l = args
- rgb = colorsys.hls_to_rgb(int(h) / 360.0, utility.pc_or_float(l), utility.pc_or_float(s))
+ rgb = colorsys.hls_to_rgb(
+ int(h) / 360.0, utility.pc_or_float(l), utility.pc_or_float(s))
color = (utility.convergent_round(c * 255) for c in rgb)
return self._rgbatohex(color)
raise ValueError('Illegal color values')
@@ -172,7 +171,8 @@ class Color():
"""
if len(args) == 4:
h, s, l, a = args
- rgb = colorsys.hls_to_rgb(int(h) / 360.0, utility.pc_or_float(l), utility.pc_or_float(s))
+ rgb = colorsys.hls_to_rgb(
+ int(h) / 360.0, utility.pc_or_float(l), utility.pc_or_float(s))
color = [float(utility.convergent_round(c * 255)) for c in rgb]
color.append(utility.pc_or_float(a))
return "rgba(%s,%s,%s,%s)" % tuple(color)
@@ -304,7 +304,7 @@ class Color():
str
"""
if color and degree:
- if isinstance(degree, six.string_types):
+ if isinstance(degree, string_types):
degree = float(degree.strip('%'))
h, l, s = self._hextohls(color)
h = ((h * 360.0) + degree) % 360.0
@@ -348,14 +348,14 @@ class Color():
str
"""
if color1 and color2:
- if isinstance(weight, six.string_types):
+ if isinstance(weight, string_types):
weight = float(weight.strip('%'))
weight = ((weight / 100.0) * 2) - 1
rgb1 = self._hextorgb(color1)
rgb2 = self._hextorgb(color2)
alpha = 0
- w1 = (((weight if weight * alpha == -1
- else weight + alpha) / (1 + weight * alpha)) + 1)
+ w1 = (((weight if weight * alpha == -1 else weight + alpha) /
+ (1 + weight * alpha)) + 1)
w1 = w1 / 2.0
w2 = 1 - w1
rgb = [
@@ -384,17 +384,17 @@ class Color():
raise ValueError('Cannot format non-color')
def _rgbatohex_raw(self, rgba):
- values = ["%x" % int(v) for v in
- [0xff if h > 0xff else 0 if h < 0 else h for h in rgba]]
+ values = [
+ "%x" % int(v)
+ for v in [0xff if h > 0xff else 0 if h < 0 else h for h in rgba]
+ ]
return values
def _rgbatohex(self, rgba):
- return '#%s' % ''.join(["%02x" % int(v) for v in
- [0xff
- if h > 0xff else
- 0 if h < 0 else h
- for h in rgba]
- ])
+ return '#%s' % ''.join([
+ "%02x" % int(v)
+ for v in [0xff if h > 0xff else 0 if h < 0 else h for h in rgba]
+ ])
def _hextorgb(self, hex):
if hex.lower() in colors.lessColors:
@@ -417,7 +417,7 @@ class Color():
return colorsys.rgb_to_hls(*[c / 255.0 for c in rgb])
def _ophsl(self, color, diff, idx, operation):
- if isinstance(diff, six.string_types):
+ if isinstance(diff, string_types):
diff = float(diff.strip('%'))
hls = list(self._hextohls(color))
hls[idx] = self._clamp(operation(hls[idx], diff / 100.0))
diff --git a/lesscpy/lessc/formatter.py b/lesscpy/lessc/formatter.py
index 796b79e..b8dd797 100644
--- a/lesscpy/lessc/formatter.py
+++ b/lesscpy/lessc/formatter.py
@@ -10,7 +10,6 @@
class Formatter(object):
-
def __init__(self, args):
self.args = args
@@ -25,21 +24,9 @@ class Formatter(object):
self.args.minify = True
self.items = {}
if self.args.minify:
- self.items.update({
- 'nl': '',
- 'tab': '',
- 'ws': '',
- 'eb': eb
- })
+ self.items.update({'nl': '', 'tab': '', 'ws': '', 'eb': eb})
else:
tab = '\t' if self.args.tabs else ' ' * int(self.args.spaces)
- self.items.update({
- 'nl': '\n',
- 'tab': tab,
- 'ws': ' ',
- 'eb': eb
- })
- self.out = [u.fmt(self.items)
- for u in parse.result
- if u]
+ self.items.update({'nl': '\n', 'tab': tab, 'ws': ' ', 'eb': eb})
+ self.out = [u.fmt(self.items) for u in parse.result if u]
return ''.join(self.out).strip()
diff --git a/lesscpy/lessc/lexer.py b/lesscpy/lessc/lexer.py
index c29f13e..38a964f 100644
--- a/lesscpy/lessc/lexer.py
+++ b/lesscpy/lessc/lexer.py
@@ -31,73 +31,21 @@ class LessLexer:
)
literals = '<>=%!/*-+&'
tokens = [
- 'css_ident',
- 'css_dom',
- 'css_class',
- 'css_id',
- 'css_property',
- 'css_vendor_property',
- 'css_comment',
- 'css_string',
- 'css_color',
- 'css_filter',
- 'css_number',
- 'css_important',
- 'css_vendor_hack',
- 'css_uri',
- 'css_ms_filter',
- 'css_keyframe_selector',
-
- 'css_media_type',
- 'css_media_feature',
-
- 't_and',
- 't_not',
- 't_only',
-
- 'less_variable',
- 'less_comment',
- 'less_open_format',
- 'less_when',
- 'less_and',
- 'less_not',
-
- 't_ws',
- 't_popen',
- 't_pclose',
- 't_semicolon',
- 't_tilde',
- 't_colon',
- 't_comma',
-
- 't_eopen',
- 't_eclose',
-
- 't_isopen',
- 't_isclose',
-
- 't_bopen',
- 't_bclose'
+ 'css_ident', 'css_dom', 'css_class', 'css_id', 'css_property',
+ 'css_vendor_property', 'css_user_property', 'css_comment',
+ 'css_string', 'css_color', 'css_filter', 'css_number', 'css_important',
+ 'css_vendor_hack', 'css_uri', 'css_ms_filter', 'css_keyframe_selector',
+ 'css_media_type', 'css_media_feature', 't_and', 't_not', 't_only',
+ 'less_variable', 'less_comment', 'less_open_format', 'less_when',
+ 'less_and', 'less_not', 't_ws', 't_popen', 't_pclose', 't_semicolon',
+ 't_tilde', 't_colon', 't_comma', 't_eopen', 't_eclose', 't_isopen',
+ 't_isclose', 't_bopen', 't_bclose'
]
tokens += list(set(reserved.tokens.values()))
# Tokens with significant following whitespace
- significant_ws = set([
- 'css_class',
- 'css_id',
- 'css_dom',
- 'css_property',
- 'css_vendor_property',
- 'css_ident',
- 'css_number',
- 'css_color',
- 'css_media_type',
- 'css_filter',
- 'less_variable',
- 't_and',
- 't_not',
- 't_only',
- '&',
- ])
+ significant_ws = {'css_class', 'css_id', 'css_dom', 'css_property', 'css_vendor_property', 'css_user_property', 'css_ident',
+ 'css_number', 'css_color', 'css_media_type', 'css_filter', 'less_variable', 't_and', 't_not',
+ 't_only', '&'}
significant_ws.update(reserved.tokens.values())
def __init__(self):
@@ -139,7 +87,7 @@ class LessLexer:
return t
def t_css_ident(self, t):
- (r'([\-\.\#]?'
+ (r'((\-|\.|\#|\-\-)?'
'([_a-z]'
'|[\200-\377]'
'|\\\[0-9a-f]{1,6}'
@@ -176,13 +124,17 @@ class LessLexer:
t.type = 'less_not'
elif v in ('from', 'to'):
t.type = 'css_keyframe_selector'
- elif v in css.propertys:
+ elif v in css.properties:
t.type = 'css_property'
t.lexer.in_property_decl = True
- elif (v in dom.elements or v.lower() in dom.elements) and not t.lexer.in_property_decl:
+ elif (v in dom.elements
+ or v.lower() in dom.elements) and not t.lexer.in_property_decl:
# DOM elements can't be part of property declarations, avoids ambiguity between 'rect' DOM
# element and rect() CSS function.
t.type = 'css_dom'
+ elif v.startswith("--"):
+ t.type = 'css_user_property'
+ t.lexer.in_property_decl = True
elif c == '-':
t.type = 'css_vendor_property'
t.lexer.in_property_decl = True
@@ -447,8 +399,8 @@ class LessLexer:
# Error handling rule
def t_error(self, t):
- raise SyntaxError("Illegal character '%s' line %d" %
- (t.value[0], t.lexer.lineno))
+ raise SyntaxError(
+ "Illegal character '%s' line %d" % (t.value[0], t.lexer.lineno))
t.lexer.skip(1)
# Build the lexer
@@ -493,8 +445,8 @@ class LessLexer:
if not t:
return t
if t.type == 't_ws' and (
- self.pretok or (self.last
- and self.last.type not in self.significant_ws)):
+ self.pretok or
+ (self.last and self.last.type not in self.significant_ws)):
continue
self.pretok = False
if t.type == 't_bclose' and self.last and self.last.type not in ['t_bopen', 't_bclose'] and self.last.type != 't_semicolon' \
diff --git a/lesscpy/lessc/parser.py b/lesscpy/lessc/parser.py
index f7d2650..a16a7d8 100644
--- a/lesscpy/lessc/parser.py
+++ b/lesscpy/lessc/parser.py
@@ -18,7 +18,7 @@ import os
import tempfile
import sys
import ply.yacc
-import six
+from six import string_types
from . import lexer
from . import utility
@@ -27,12 +27,14 @@ from .color import Color
from lesscpy.exceptions import CompilationError
from lesscpy.plib import Block, Call, Deferred, Expression, Identifier, Mixin, NegatedExpression, Property, Statement, Variable, Import, KeyframeSelector
+
class ErrorRegister(object):
"""
Raises CompilationError when an error occurs.
"""
+
def __init__(self):
self.errors = []
@@ -45,12 +47,14 @@ class ErrorRegister(object):
close = __close__
+
class PrintErrorRegister(object):
"""
Colored error output to stderr.
"""
+
def __init__(self):
self.has_errored = False
@@ -80,8 +84,7 @@ class LessParser(object):
outputdir=tempfile.gettempdir(),
importlvl=0,
verbose=False,
- fail_with_exc=False
- ):
+ fail_with_exc=False):
""" Parser object
Kwargs:
@@ -102,19 +105,16 @@ class LessParser(object):
if not tabfile:
tabfile = 'yacctab'
- self.ignored = ('css_comment', 'less_comment',
- 'css_vendor_hack')
+ self.ignored = ('css_comment', 'less_comment', 'css_vendor_hack')
- self.tokens = [t for t in self.lex.tokens
- if t not in self.ignored]
+ self.tokens = [t for t in self.lex.tokens if t not in self.ignored]
self.parser = ply.yacc.yacc(
module=self,
start='tunit',
debug=yacc_debug,
optimize=yacc_optimize,
tabmodule=tabfile,
- outputdir=outputdir
- )
+ outputdir=outputdir)
self.scope = scope if scope else Scope()
self.stash = {}
self.result = None
@@ -125,7 +125,6 @@ class LessParser(object):
else:
self.register = PrintErrorRegister()
-
def parse(self, filename=None, file=None, debuglevel=0):
""" Parse file.
kwargs:
@@ -150,8 +149,7 @@ class LessParser(object):
self.target = filename
if self.verbose and not self.fail_with_exc:
print('Compiling target: %s' % filename, file=sys.stderr)
- self.result = self.parser.parse(
- file, lexer=self.lex, debug=debuglevel)
+ self.result = self.parser.parse(file, lexer=self.lex, debug=debuglevel)
self.post_parse()
self.register.close()
@@ -236,7 +234,7 @@ class LessParser(object):
if self.importlvl > 8:
raise ImportError(
'Recrusive import level too deep > 8 (circular import ?)')
- if isinstance(p[3], six.string_types):
+ if isinstance(p[3], string_types):
ipath = utility.destring(p[3])
elif isinstance(p[3], list):
p[3] = Import(p[3], p.lineno(4)).parse(self.scope)
@@ -244,7 +242,8 @@ class LessParser(object):
elif isinstance(p[3], Call):
# NOTE(saschpe): Always in the form of 'url("...");', so parse it
# and retrieve the inner css_string. This whole func is messy.
- p[3] = p[3].parse(self.scope) # Store it as string, Statement.fmt expects it.
+ p[3] = p[3].parse(
+ self.scope) # Store it as string, Statement.fmt expects it.
ipath = utility.destring(p[3][4:-1])
fn, fe = os.path.splitext(ipath)
if not fe or fe.lower() == '.less':
@@ -254,8 +253,10 @@ class LessParser(object):
ipath += '.less'
filename = "%s%s%s" % (cpath, os.sep, ipath)
if os.path.exists(filename):
- recurse = LessParser(importlvl=self.importlvl + 1,
- verbose=self.verbose, scope=self.scope)
+ recurse = LessParser(
+ importlvl=self.importlvl + 1,
+ verbose=self.verbose,
+ scope=self.scope)
recurse.parse(filename=filename, debuglevel=0)
p[0] = recurse.result
else:
@@ -493,6 +494,7 @@ class LessParser(object):
def p_prop_open(self, p):
""" prop_open : property t_colon
| vendor_property t_colon
+ | user_property t_colon
| word t_colon
"""
p[0] = (p[1][0], '')
@@ -770,6 +772,7 @@ class LessParser(object):
| word
| id
| css_uri
+ | css_user_property
| '='
| fcall
"""
@@ -871,7 +874,7 @@ class LessParser(object):
""" variable : less_variable
| less_variable t_ws
"""
-# p[0] = p[1]
+ # p[0] = p[1]
p[0] = tuple(list(p)[1:])
def p_color(self, p):
@@ -883,8 +886,8 @@ class LessParser(object):
if len(p) > 2:
p[0] = [p[0], p[2]]
except ValueError:
- self.handle_error(
- 'Illegal color value `%s`' % p[1], p.lineno(1), 'W')
+ self.handle_error('Illegal color value `%s`' % p[1], p.lineno(1),
+ 'W')
p[0] = p[1]
def p_number(self, p):
@@ -966,6 +969,12 @@ class LessParser(object):
"""
p[0] = tuple(list(p)[1:])
+ def p_user_property(self, p):
+ """ user_property : css_user_property
+ | css_user_property t_ws
+ """
+ p[0] = tuple(list(p)[1:])
+
def p_media_type(self, p):
""" media_type : css_media_type
| css_media_type t_ws
@@ -1023,6 +1032,7 @@ class LessParser(object):
'empty :'
pass
+
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
diff --git a/lesscpy/lessc/scope.py b/lesscpy/lessc/scope.py
index 6f973d3..05d8ec2 100644
--- a/lesscpy/lessc/scope.py
+++ b/lesscpy/lessc/scope.py
@@ -7,13 +7,12 @@
See LICENSE for details.
.. moduleauthor:: Johann T. Mariusson <jtm@robot.is>
"""
-import six
+from six import string_types
from . import utility
class Scope(list):
-
""" Scope class. A stack implementation.
"""
@@ -53,9 +52,7 @@ class Scope(list):
Returns:
list
"""
- return [r['__current__']
- for r in self
- if r['__current__']]
+ return [r['__current__'] for r in self if r['__current__']]
def add_block(self, block):
"""Add block element to scope
@@ -125,9 +122,7 @@ class Scope(list):
def _smixins(self, name):
"""Inner wrapper to search for mixins by name.
"""
- return (self._mixins[name]
- if name in self._mixins
- else False)
+ return self._mixins[name] if name in self._mixins else False
def blocks(self, name):
"""
@@ -195,7 +190,7 @@ class Scope(list):
var = self.variables('@' + name[2:-1])
if var is False:
raise SyntaxError('Unknown escaped variable %s' % name)
- if isinstance(var.value[0], six.string_types):
+ if isinstance(var.value[0], string_types):
var.value[0] = utility.destring(var.value[0])
else:
var = self.variables(name)
diff --git a/lesscpy/lessc/utility.py b/lesscpy/lessc/utility.py
index 2f61e9c..9c23d29 100644
--- a/lesscpy/lessc/utility.py
+++ b/lesscpy/lessc/utility.py
@@ -10,13 +10,18 @@
from __future__ import print_function
-import collections
import itertools
import math
import re
import sys
from six import string_types
+try:
+ from collections.abc import Iterable
+except ImportError:
+ from collections import Iterable
+
+
def flatten(lst):
"""Flatten list.
Args:
@@ -25,7 +30,8 @@ def flatten(lst):
generator
"""
for elm in lst:
- if isinstance(elm, collections.Iterable) and not isinstance(elm, string_types):
+ if isinstance(elm, Iterable) and not isinstance(
+ elm, string_types):
for sub in flatten(elm):
yield sub
else:
@@ -74,8 +80,8 @@ def blocksearch(block, name):
"""
if hasattr(block, 'tokens'):
for b in block.tokens[1]:
- b = (b if hasattr(b, 'raw') and b.raw() == name
- else blocksearch(b, name))
+ b = (b if hasattr(b, 'raw') and b.raw() == name else blocksearch(
+ b, name))
if b:
return b
return False
@@ -89,12 +95,7 @@ def reverse_guard(lst):
returns:
list
"""
- rev = {
- '<': '>=',
- '>': '=<',
- '>=': '<',
- '=<': '>'
- }
+ rev = {'<': '>=', '>': '=<', '>=': '<', '=<': '>'}
return [rev[l] if l in rev else l for l in lst]
@@ -138,16 +139,16 @@ def analyze_number(var, err=''):
"""
n, u = split_unit(var)
if not isinstance(var, string_types):
- return (var, u)
+ return var, u
if is_color(var):
- return (var, 'color')
+ return var, 'color'
if is_int(n):
n = int(n)
elif is_float(n):
n = float(n)
else:
raise SyntaxError('%s ´%s´' % (err, var))
- return (n, u)
+ return n, u
def with_unit(number, unit=None):
@@ -196,10 +197,10 @@ def is_variable(value):
bool
"""
if isinstance(value, string_types):
- return (value.startswith('@') or value.startswith('-@'))
+ return value.startswith('@') or value.startswith('-@')
elif isinstance(value, tuple):
value = ''.join(value)
- return (value.startswith('@') or value.startswith('-@'))
+ return value.startswith('@') or value.startswith('-@')
return False
@@ -278,6 +279,7 @@ def convergent_round(value, ndigits=0):
return math.ceil(nearest_even)
return round(value, ndigits)
+
def pc_or_float(s):
""" Utility function to process strings that contain either percentiles or floats
args:
@@ -289,6 +291,7 @@ def pc_or_float(s):
return float(s.strip('%')) / 100.0
return float(s)
+
def permutations_with_replacement(iterable, r=None):
"""Return successive r length permutations of elements in the iterable.
diff --git a/lesscpy/lib/css.py b/lesscpy/lib/css.py
index 4404687..b411764 100644
--- a/lesscpy/lib/css.py
+++ b/lesscpy/lib/css.py
@@ -334,7 +334,7 @@ vendor_ugly = [
'behavior',
'zoom',
]
-propertys = css2 + css3 + svg + vendor_ugly
+properties = css2 + css3 + svg + vendor_ugly
# CSS-2(.1) media types: http://www.w3.org/TR/CSS2/media.html#media-types
# Include media types as defined in HTML4: http://www.w3.org/TR/1999/REC-html401-19991224/types.html#h-6.13
@@ -374,7 +374,6 @@ css3_media_features = [
'min-aspect-ratio',
'max-aspect-ratio',
'device-aspect-ratio',
-
'min-device-aspect-ratio',
'max-device-aspect-ratio',
'color',
diff --git a/lesscpy/lib/dom.py b/lesscpy/lib/dom.py
index 7057992..d20cdff 100644
--- a/lesscpy/lib/dom.py
+++ b/lesscpy/lib/dom.py
@@ -135,7 +135,6 @@ html5 = [
'track',
'video',
'wbr',
-
'only', # TODO/FIXME: What is this?!?
]
diff --git a/lesscpy/lib/reserved.py b/lesscpy/lib/reserved.py
index 1c0df47..fbf8957 100644
--- a/lesscpy/lib/reserved.py
+++ b/lesscpy/lib/reserved.py
@@ -18,9 +18,7 @@ tokens = {
'@-webkit-keyframes': 'css_keyframes',
'@-ms-keyframes': 'css_keyframes',
'@-o-keyframes': 'css_keyframes',
-
'@viewport': 'css_viewport',
'@-ms-viewport': 'css_viewport',
-
'@arguments': 'less_arguments',
}
diff --git a/lesscpy/plib/block.py b/lesscpy/plib/block.py
index d6a0bcb..d92b534 100644
--- a/lesscpy/plib/block.py
+++ b/lesscpy/plib/block.py
@@ -13,7 +13,6 @@ from lesscpy.plib.identifier import Identifier
class Block(Node):
-
""" Block node. Represents one parse-block.
Can contain property nodes or other block nodes.
identifier {
@@ -29,7 +28,7 @@ class Block(Node):
raises:
SyntaxError
returns:
- self
+ self, or a list of new blocks if media queries need to be rotated
"""
if not self.parsed:
scope.push()
@@ -43,89 +42,81 @@ class Block(Node):
inner = list(utility.flatten([p.parse(scope) for p in inner if p]))
self.parsed = []
self.inner = []
- if not hasattr(self, "inner_media_queries"):
- self.inner_media_queries = []
+ # Because media queries need to be at the root in CSS, we rotate them
+ # up the parse tree. More specifically, in a situation like this where
+ # the current node is .foo:
+ #
+ # .foo {
+ # @media print {
+ # /* ... */
+ # }
+ # .bar {
+ # /* ... */
+ # }
+ # }
+ #
+ # The media query is rotated out after splitting the node, resulting in
+ # this tree:
+ #
+ # @media print {
+ # .foo {
+ # /* ... */
+ # }
+ # }
+ # .foo {
+ # .bar {
+ # /* ... */
+ # }
+ # }
+ #
+ # New media queries are returned as siblings of self. If .foo is a
+ # media query itself, conditions are merged. This process is recursive
+ # so that media queries bubble up and split nodes along the way.
+ inner_media_queries = []
+ sibling_media_queries = []
for p in inner:
- if p is not None:
- if isinstance(p, Block):
- if (len(scope) == 2 and p.tokens[1] is not None):
- p_is_mediaquery = p.name.tokens[0] == '@media'
- # Inner block @media ... { ... } is a nested media
- # query. But double-nested media queries have to be
- # removed and marked as well. While parsing ".foo",
- # both nested "@media print" and double-nested
- # "@media all" will be handled as we have to
- # re-arrange the scope and block layout quite a bit:
- #
- # .foo {
- # @media print {
- # color: blue;
- # @media screen { font-size: 12em; }
- # }
- # }
- #
- # Expected result:
- #
- # @media print {
- # .foo { color: blue; }
- # }
- # @media print and screen {
- # .foo { font-size: 12 em; }
- # }
- append_list = []
- reparse_p = False
- for child in p.tokens[1]:
- if isinstance(child, Block) and child.name.raw().startswith("@media"):
- # Remove child from the nested media query, it will be re-added to
- # the parent with 'merged' media query (see above example).
- p.tokens[1].remove(child)
- if p_is_mediaquery: # Media query inside a & block
- # Double-nested media query found. We remove it from 'p' and add
- # it to this block with a new 'name'.
- reparse_p = True
- part_a = p.name.tokens[2:][0][0][0]
- part_b = child.name.tokens[2:][0][0]
- new_ident_tokens = ['@media', ' ', [part_a, (' ', 'and', ' '), part_b]]
- # Parse child again with new @media $BLA {} part
- child.tokens[0] = Identifier(new_ident_tokens)
- child.parsed = None
- child = child.parse(scope)
- else:
- child.block_name = p.name
- append_list.append(child)
- if reparse_p:
- p.parsed = None
- p = p.parse(scope)
- if not p_is_mediaquery and not append_list:
- self.inner.append(p)
- else:
- append_list.insert(0, p) # This media query should occur before it's children
- for media_query in append_list:
- self.inner_media_queries.append(media_query)
- # NOTE(saschpe): The code is not recursive but we hope that people
- # wont use triple-nested media queries.
- else:
- self.inner.append(p)
- else:
- self.parsed.append(p)
- if self.inner_media_queries:
- # Nested media queries, we have to remove self from scope and
- # push all nested @media ... {} blocks.
- scope.remove_block(self, index=-2)
- for mb in self.inner_media_queries:
- # New inner block with current name and media block contents
- if hasattr(mb, 'block_name'):
- cb_name = mb.block_name
- else:
- cb_name = self.tokens[0]
- cb = Block([cb_name, mb.tokens[1]]).parse(scope)
- # Replace inner block contents with new block
- new_mb = Block([mb.tokens[0], [cb]]).parse(scope)
- self.inner.append(new_mb)
- scope.add_block(new_mb)
+ if not isinstance(p, Block):
+ self.parsed.append(p)
+ # TODO: By separating in two lists (inner and inner_media_queries),
+ # we change the final order of declarations.
+ elif p.tokens[1] is not None and p.name.tokens[0] == '@media':
+ inner_media_queries.append(p)
+ else:
+ self.inner.append(p)
+ for mb in inner_media_queries:
+ # If the current node is also a media query, create a merged media
+ # query for each inner media query.
+ if self.name.tokens[0] == '@media':
+ part_a = self.name.tokens[2:][0][0][0]
+ part_b = mb.name.tokens[2:][0]
+ cond = [
+ '@media', ' ', [
+ part_a, (' ', 'and', ' '),
+ part_b
+ ]
+ ]
+ # TODO: mb.parsed + mb.inner reorders things again
+ mb = Block([Identifier(cond), mb.parsed + mb.inner]).parse(scope)
+ sibling_media_queries += mb
+ for block in mb:
+ scope.add_block(block)
+ # Otherwise, rotate inner media queries out of self.
+ else:
+ cbs = Block([self.tokens[0], mb.parsed + mb.inner]).parse(scope)
+ for cb in cbs:
+ # Replace inner block contents with new block
+ new_mb = Block([mb.tokens[0], [cb]]).parse(scope)
+ sibling_media_queries += new_mb
+ for block in new_mb:
+ scope.add_block(block)
scope.real.pop()
scope.pop()
- return self
+ if self.inner or self.parsed:
+ return [self] + sibling_media_queries
+ else:
+ return sibling_media_queries
+ else:
+ return [self]
def raw(self, clean=False):
"""Raw block name
@@ -149,17 +140,22 @@ class Block(Node):
f = "%(identifier)s%(ws)s{%(nl)s%(proplist)s}%(eb)s"
out = []
name = self.name.fmt(fills)
- if self.parsed and any(p for p in self.parsed if str(type(p)) != "<class 'lesscpy.plib.variable.Variable'>"):
+ if self.parsed and any(
+ p for p in self.parsed
+ if str(type(p)) != "<class 'lesscpy.plib.variable.Variable'>"):
fills.update({
- 'identifier': name,
- 'proplist': ''.join([p.fmt(fills) for p in self.parsed if p]),
+ 'identifier':
+ name,
+ 'proplist':
+ ''.join([p.fmt(fills) for p in self.parsed if p]),
})
out.append(f % fills)
if hasattr(self, 'inner'):
if self.name.subparse and len(self.inner) > 0: # @media
inner = ''.join([p.fmt(fills) for p in self.inner])
inner = inner.replace(fills['nl'],
- fills['nl'] + fills['tab']).rstrip(fills['tab'])
+ fills['nl'] + fills['tab']).rstrip(
+ fills['tab'])
if not fills['nl']:
inner = inner.strip()
fills.update({
@@ -177,8 +173,7 @@ class Block(Node):
"""
name, inner = self.tokens
if inner:
- inner = [u.copy() if u else u
- for u in inner]
+ inner = [u.copy() if u else u for u in inner]
if name:
name = name.copy()
return Block([name, inner], 0)
@@ -193,8 +188,7 @@ class Block(Node):
list (block contents)
"""
if self.tokens[1]:
- tokens = [u.copy() if u else u
- for u in self.tokens[1]]
+ tokens = [u.copy() if u else u for u in self.tokens[1]]
out = [p for p in tokens if p]
utility.rename(out, scope, Block)
return out
diff --git a/lesscpy/plib/call.py b/lesscpy/plib/call.py
index 2842a3d..f45d04b 100644
--- a/lesscpy/plib/call.py
+++ b/lesscpy/plib/call.py
@@ -13,7 +13,7 @@ try:
from urllib.parse import quote as urlquote
except ImportError:
from urllib import quote as urlquote
-import six
+from six import string_types
from .node import Node
import lesscpy.lessc.utility as utility
import lesscpy.lessc.color as Color
@@ -21,7 +21,6 @@ from lesscpy.lib.colors import lessColors
class Call(Node):
-
"""Call node. Node represents a function call.
All builtin none-color functions are in this node.
This node attempts calls on built-ins and lets non-builtins
@@ -45,8 +44,10 @@ class Call(Node):
elif name in ('~', 'e'):
name = 'escape'
color = Color.Color()
- args = [t for t in parsed
- if not isinstance(t, six.string_types) or t not in '(),']
+ args = [
+ t for t in parsed
+ if not isinstance(t, string_types) or t not in '(),'
+ ]
if hasattr(self, name):
try:
return getattr(self, name)(*args)
@@ -118,7 +119,7 @@ class Call(Node):
returns:
bool
"""
- return (string in lessColors)
+ return string in lessColors
def isurl(self, string, *args):
"""Is url
@@ -128,15 +129,17 @@ class Call(Node):
bool
"""
arg = utility.destring(string)
- regex = re.compile(r'^(?:http|ftp)s?://' # http:// or https://
- r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+'
- r'(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain...
- # localhost...
- r'localhost|'
- r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
- # optional port
- r'(?::\d+)?'
- r'(?:/?|[/?]\S+)$', re.IGNORECASE)
+ regex = re.compile(
+ r'^(?:http|ftp)s?://' # http:// or https://
+ r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+'
+ r'(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain...
+ # localhost...
+ r'localhost|'
+ r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
+ # optional port
+ r'(?::\d+)?'
+ r'(?:/?|[/?]\S+)$',
+ re.IGNORECASE)
return regex.match(arg)
def isstring(self, string, *args):
@@ -156,7 +159,7 @@ class Call(Node):
returns:
bool
"""
- return (string in ('when', 'and', 'not'))
+ return string in ('when', 'and', 'not')
def increment(self, value, *args):
""" Increment function
@@ -185,7 +188,7 @@ class Call(Node):
returns:
str
"""
- if(len(args) <= 1):
+ if len(args) <= 1:
return 0
return sum([int(v) for v in args])
@@ -197,7 +200,8 @@ class Call(Node):
str
"""
n, u = utility.analyze_number(value)
- return utility.with_unit(int(utility.away_from_zero_round(float(n))), u)
+ return utility.with_unit(
+ int(utility.away_from_zero_round(float(n))), u)
def ceil(self, value, *args):
""" Ceil number
diff --git a/lesscpy/plib/deferred.py b/lesscpy/plib/deferred.py
index 8ccbbdb..2eb94c8 100644
--- a/lesscpy/plib/deferred.py
+++ b/lesscpy/plib/deferred.py
@@ -11,7 +11,6 @@ from .node import Node
class Deferred(Node):
-
def __init__(self, mixin, args, lineno=0):
"""This node represents mixin calls. The calls
to these mixins are deferred until the second
@@ -91,8 +90,8 @@ class Deferred(Node):
break
if res:
- store = [t for t in scope.deferred.parsed[
- -1]] if scope.deferred else False
+ store = [t for t in scope.deferred.parsed[-1]
+ ] if scope.deferred else False
tmp_res = []
for p in res:
if p:
@@ -102,7 +101,7 @@ class Deferred(Node):
tmp_res.append(p.parse(scope))
res = tmp_res
#res = [p.parse(scope, depth=depth+1) for p in res if p]
- while(any(t for t in res if isinstance(t, Deferred))):
+ while (any(t for t in res if isinstance(t, Deferred))):
res = [p.parse(scope) for p in res if p]
if store:
scope.deferred.parsed[-1] = store
diff --git a/lesscpy/plib/expression.py b/lesscpy/plib/expression.py
index 5e5e2ee..f91c5c6 100644
--- a/lesscpy/plib/expression.py
+++ b/lesscpy/plib/expression.py
@@ -16,7 +16,6 @@ from lesscpy.lessc import color
class Expression(Node):
-
"""Expression node. Parses all expression except
color expressions (handled in the color class)
and unary negation (handled in the NegatedExpression class).
@@ -31,19 +30,18 @@ class Expression(Node):
returns:
str
"""
- assert(len(self.tokens) == 3)
+ assert (len(self.tokens) == 3)
expr = self.process(self.tokens, scope)
- A, O, B = [e[0]
- if isinstance(e, tuple)
- else e
- for e in expr
- if str(e).strip()]
+ A, O, B = [
+ e[0] if isinstance(e, tuple) else e for e in expr
+ if str(e).strip()
+ ]
try:
a, ua = utility.analyze_number(A, 'Illegal element in expression')
b, ub = utility.analyze_number(B, 'Illegal element in expression')
except SyntaxError:
return ' '.join([str(A), str(O), str(B)])
- if(a is False or b is False):
+ if (a is False or b is False):
return ' '.join([str(A), str(O), str(B)])
if ua == 'color' or ub == 'color':
return color.Color().process((A, O, B))
diff --git a/lesscpy/plib/identifier.py b/lesscpy/plib/identifier.py
index 68c55c7..c2263d7 100644
--- a/lesscpy/plib/identifier.py
+++ b/lesscpy/plib/identifier.py
@@ -14,7 +14,6 @@ from lesscpy.lib import reserved
class Identifier(Node):
-
"""Identifier node. Represents block identifier.
"""
@@ -30,14 +29,14 @@ class Identifier(Node):
"""
names = []
name = []
- self._subp = (
- '@media', '@keyframes',
- '@-moz-keyframes', '@-webkit-keyframes',
- '@-ms-keyframes'
- )
+ self._subp = ('@media', '@keyframes', '@-moz-keyframes',
+ '@-webkit-keyframes', '@-ms-keyframes')
if self.tokens and hasattr(self.tokens, 'parse'):
- self.tokens = list(utility.flatten([id.split() + [',']
- for id in self.tokens.parse(scope).split(',')]))
+ self.tokens = list(
+ utility.flatten([
+ id.split() + [',']
+ for id in self.tokens.parse(scope).split(',')
+ ]))
self.tokens.pop()
if self.tokens and any(hasattr(t, 'parse') for t in self.tokens):
tmp_tokens = []
@@ -73,15 +72,21 @@ class Identifier(Node):
# But: '@media print' results in [['@media', ' ', 'print']]
#
def replace_variables(tokens, scope):
- return [scope.swap(t)
- if (utility.is_variable(t) and not t in reserved.tokens)
- else t
- for t in tokens]
- parsed = [list(utility.flatten(replace_variables(part, scope))) for part in parsed]
+ return [
+ scope.swap(t)
+ if (utility.is_variable(t) and not t in reserved.tokens) else t
+ for t in tokens
+ ]
+
+ parsed = [
+ list(utility.flatten(replace_variables(part, scope)))
+ for part in parsed
+ ]
- self.parsed = [[i for i, j in utility.pairwise(part)
- if i != ' ' or (j and '?' not in j)]
- for part in parsed]
+ self.parsed = [[
+ i for i, j in utility.pairwise(part)
+ if i != ' ' or (j and '?' not in j)
+ ] for part in parsed]
return self
def root(self, scope, names):
@@ -104,7 +109,9 @@ class Identifier(Node):
for part in parent.parsed:
if part and part[0] not in self._subp:
filtered_parts.append(part)
- permutations = list(utility.permutations_with_replacement(filtered_parts, ampersand_count))
+ permutations = list(
+ utility.permutations_with_replacement(
+ filtered_parts, ampersand_count))
for permutation in permutations:
parsed = []
for name_part in name:
@@ -153,8 +160,7 @@ class Identifier(Node):
Identifier object
"""
tokens = ([t for t in self.tokens]
- if isinstance(self.tokens, list)
- else self.tokens)
+ if isinstance(self.tokens, list) else self.tokens)
return Identifier(tokens, 0)
def fmt(self, fills):
@@ -164,7 +170,6 @@ class Identifier(Node):
returns:
str (CSS)
"""
- name = ',$$'.join(''.join(p).strip()
- for p in self.parsed)
+ name = ',$$'.join(''.join(p).strip() for p in self.parsed)
name = re.sub('\?(.)\?', '%(ws)s\\1%(ws)s', name) % fills
return name.replace('$$', fills['nl']).replace(' ', ' ')
diff --git a/lesscpy/plib/import_.py b/lesscpy/plib/import_.py
index f1a6d15..749b6b9 100644
--- a/lesscpy/plib/import_.py
+++ b/lesscpy/plib/import_.py
@@ -11,7 +11,6 @@ from .node import Node
class Import(Node):
-
"""Represents CSS property declaration.
"""
diff --git a/lesscpy/plib/keyframe_selector.py b/lesscpy/plib/keyframe_selector.py
index c4677ab..cf2b516 100644
--- a/lesscpy/plib/keyframe_selector.py
+++ b/lesscpy/plib/keyframe_selector.py
@@ -27,8 +27,10 @@ class KeyframeSelector(Node):
returns:
self
"""
- self.keyframe, = [e[0] if isinstance(e, tuple) else e
- for e in self.tokens if str(e).strip()]
+ self.keyframe, = [
+ e[0] if isinstance(e, tuple) else e for e in self.tokens
+ if str(e).strip()
+ ]
self.subparse = False
return self
diff --git a/lesscpy/plib/mixin.py b/lesscpy/plib/mixin.py
index 75ead7e..ffe7e89 100644
--- a/lesscpy/plib/mixin.py
+++ b/lesscpy/plib/mixin.py
@@ -18,7 +18,6 @@ from lesscpy.lessc import utility
class Mixin(Node):
-
""" Mixin Node. Represents callable mixin types.
"""
@@ -34,9 +33,10 @@ class Mixin(Node):
self.name, args, self.guards = self.tokens[0]
self.args = [a for a in utility.flatten(args) if a]
self.body = Block([None, self.tokens[1]], 0)
- self.vars = list(utility.flatten([list(v.values())
- for v in [s['__variables__']
- for s in scope]]))
+ self.vars = list(
+ utility.flatten([
+ list(v.values()) for v in [s['__variables__'] for s in scope]
+ ]))
return self
def raw(self):
@@ -56,15 +56,19 @@ class Mixin(Node):
raises:
SyntaxError
"""
- arguments = list(zip(args, [' '] * len(args))) if args and args[0] else None
+ arguments = list(zip(args,
+ [' '] * len(args))) if args and args[0] else None
zl = itertools.zip_longest if sys.version_info[
0] == 3 else itertools.izip_longest
if self.args:
- parsed = [v if hasattr(v, 'parse') else v
- for v in copy.copy(self.args)]
+ parsed = [
+ v if hasattr(v, 'parse') else v for v in copy.copy(self.args)
+ ]
args = args if isinstance(args, list) else [args]
- vars = [self._parse_arg(var, arg, scope)
- for arg, var in zl([a for a in args], parsed)]
+ vars = [
+ self._parse_arg(var, arg, scope)
+ for arg, var in zl([a for a in args], parsed)
+ ]
for var in vars:
if var:
var.parse(scope)
@@ -125,8 +129,7 @@ class Mixin(Node):
for g in self.guards:
if isinstance(g, list):
res = (g[0].parse(scope)
- if len(g) == 1
- else Expression(g).parse(scope))
+ if len(g) == 1 else Expression(g).parse(scope))
if cor:
if res:
return True
@@ -147,11 +150,9 @@ class Mixin(Node):
"""
ret = False
if args:
- args = [[a.parse(scope)
- if isinstance(a, Expression)
- else a for a in arg]
- if arg else arg
- for arg in args]
+ args = [[
+ a.parse(scope) if isinstance(a, Expression) else a for a in arg
+ ] if arg else arg for arg in args]
try:
self.parse_args(args, scope)
except SyntaxError:
diff --git a/lesscpy/plib/negated_expression.py b/lesscpy/plib/negated_expression.py
index 6140354..60d35d6 100644
--- a/lesscpy/plib/negated_expression.py
+++ b/lesscpy/plib/negated_expression.py
@@ -7,17 +7,16 @@
See LICENSE for details.
"""
-import six
+from six import string_types
from .node import Node
class NegatedExpression(Node):
-
"""Expressions preceded by unary negation."""
def parse(self, scope):
val, = self.process(self.tokens, scope)
- if isinstance(val, six.string_types):
+ if isinstance(val, string_types):
return '-' + val
return -val
diff --git a/lesscpy/plib/node.py b/lesscpy/plib/node.py
index 7a9714f..313a683 100644
--- a/lesscpy/plib/node.py
+++ b/lesscpy/plib/node.py
@@ -11,7 +11,6 @@ from lesscpy.lessc import utility
class Node(object):
-
def __init__(self, tokens, lineno=0):
""" Base Node
args:
@@ -43,12 +42,15 @@ class Node(object):
tokens = list(utility.flatten(tokens))
done = True
if any(t for t in tokens if hasattr(t, 'parse')):
- tokens = [t.parse(scope)
- if hasattr(t, 'parse')
- else t
- for t in tokens]
+ tokens = [
+ t.parse(scope) if hasattr(t, 'parse') else t
+ for t in tokens
+ ]
done = False
- if any(t for t in tokens if (utility.is_variable(t)) or str(type(t)) == "<class 'lesscpy.plib.variable.Variable'>"):
+ if any(
+ t for t in tokens
+ if (utility.is_variable(t)) or str(type(t)) ==
+ "<class 'lesscpy.plib.variable.Variable'>"):
tokens = self.replace_variables(tokens, scope)
done = False
if done:
diff --git a/lesscpy/plib/property.py b/lesscpy/plib/property.py
index a303877..388f991 100644
--- a/lesscpy/plib/property.py
+++ b/lesscpy/plib/property.py
@@ -12,7 +12,6 @@ from .node import Node
class Property(Node):
-
"""Represents CSS property declaration.
"""
@@ -48,14 +47,12 @@ class Property(Node):
list
"""
if self.property == 'font':
- style = [''.join(u.expression())
- if hasattr(u, 'expression')
- else u
- for u in style]
+ style = [
+ ''.join(u.expression()) if hasattr(u, 'expression') else u
+ for u in style
+ ]
else:
- style = [(u, ' ')
- if hasattr(u, 'expression')
- else u
+ style = [(u, ' ') if hasattr(u, 'expression') else u
for u in style]
return style
@@ -69,14 +66,12 @@ class Property(Node):
f = "%(tab)s%(property)s:%(ws)s%(style)s%(important)s;%(nl)s"
imp = ' !important' if self.important else ''
if fills['nl']:
- self.parsed = [',%s' % fills['ws']
- if p == ','
- else p
- for p in self.parsed]
- style = ''.join([p.fmt(fills)
- if hasattr(p, 'fmt')
- else str(p)
- for p in self.parsed])
+ self.parsed = [
+ ',%s' % fills['ws'] if p == ',' else p for p in self.parsed
+ ]
+ style = ''.join([
+ p.fmt(fills) if hasattr(p, 'fmt') else str(p) for p in self.parsed
+ ])
# IE cannot handle no space after url()
style = re.sub("(url\([^\)]*\))([^\s,])", "\\1 \\2", style)
fills.update({
diff --git a/lesscpy/plib/statement.py b/lesscpy/plib/statement.py
index dcb67e9..06540b9 100644
--- a/lesscpy/plib/statement.py
+++ b/lesscpy/plib/statement.py
@@ -12,7 +12,6 @@ from lesscpy.lessc import utility
class Statement(Node):
-
"""Represents CSS statement (@import, @charset...)
"""
diff --git a/lesscpy/plib/variable.py b/lesscpy/plib/variable.py
index 2ddda8d..7e596a0 100644
--- a/lesscpy/plib/variable.py
+++ b/lesscpy/plib/variable.py
@@ -11,7 +11,6 @@ from .node import Node
class Variable(Node):
-
def parse(self, scope):
""" Parse function
args:
diff --git a/lesscpy/scripts/compiler.py b/lesscpy/scripts/compiler.py
index 2d8f890..090011c 100644
--- a/lesscpy/scripts/compiler.py
+++ b/lesscpy/scripts/compiler.py
@@ -55,12 +55,13 @@ def ldirectory(inpath, outpath, args, scope):
recompile = True
if recompile:
print('%s -> %s' % (lf, outf))
- p = parser.LessParser(yacc_debug=(args.debug),
- lex_optimize=True,
- yacc_optimize=(not args.debug),
- scope=scope,
- tabfile=yacctab,
- verbose=args.verbose)
+ p = parser.LessParser(
+ yacc_debug=(args.debug),
+ lex_optimize=True,
+ yacc_optimize=(not args.debug),
+ scope=scope,
+ tabfile=yacctab,
+ verbose=args.verbose)
p.parse(filename=lf, debuglevel=0)
css = f.format(p)
if not args.dry_run:
@@ -70,54 +71,110 @@ def ldirectory(inpath, outpath, args, scope):
print('skipping %s, not modified' % lf, file=sys.stderr)
sys.stdout.flush()
if args.recurse:
- [ldirectory(os.path.join(inpath, name), os.path.join(outpath, name), args, scope)
- for name in os.listdir(inpath)
- if os.path.isdir(os.path.join(inpath, name)) and
- not name.startswith('.') and not name == outpath]
+ [
+ ldirectory(
+ os.path.join(inpath, name), os.path.join(outpath, name), args,
+ scope) for name in os.listdir(inpath)
+ if os.path.isdir(os.path.join(inpath, name))
+ and not name.startswith('.') and not name == outpath
+ ]
def run():
"""Run compiler
"""
- aparse = argparse.ArgumentParser(description='LessCss Compiler',
- epilog='<< jtm@robot.is @_o >>')
- aparse.add_argument('-v', '--version', action='version',
- version=VERSION_STR)
- aparse.add_argument('-I', '--include', action="store", type=str,
- help="Included less-files (comma separated)")
- aparse.add_argument('-V', '--verbose', action="store_true",
- default=False, help="Verbose mode")
- aparse.add_argument('-C', '--dont_create_dirs', action="store_true",
- default=False, help="Creates directories when outputing files (lessc non-compatible)")
+ aparse = argparse.ArgumentParser(
+ description='LessCss Compiler', epilog='<< jtm@robot.is @_o >>')
+ aparse.add_argument(
+ '-v', '--version', action='version', version=VERSION_STR)
+ aparse.add_argument(
+ '-I',
+ '--include',
+ action="store",
+ type=str,
+ help="Included less-files (comma separated)")
+ aparse.add_argument(
+ '-V',
+ '--verbose',
+ action="store_true",
+ default=False,
+ help="Verbose mode")
+ aparse.add_argument(
+ '-C',
+ '--dont_create_dirs',
+ action="store_true",
+ default=False,
+ help="Creates directories when outputing files (lessc non-compatible)")
fgroup = aparse.add_argument_group('Formatting options')
- fgroup.add_argument('-x', '--minify', action="store_true",
- default=False, help="Minify output")
- fgroup.add_argument('-X', '--xminify', action="store_true",
- default=False, help="Minify output, no end of block newlines")
+ fgroup.add_argument(
+ '-x',
+ '--minify',
+ action="store_true",
+ default=False,
+ help="Minify output")
+ fgroup.add_argument(
+ '-X',
+ '--xminify',
+ action="store_true",
+ default=False,
+ help="Minify output, no end of block newlines")
fgroup.add_argument('-t', '--tabs', help="Use tabs", action="store_true")
fgroup.add_argument(
- '-s', '--spaces', help="Number of startline spaces (default 2)", default=2)
- dgroup = aparse.add_argument_group('Directory options',
- 'Compiles all *.less files in directory that '
- 'have a newer timestamp than it\'s css file.')
+ '-s',
+ '--spaces',
+ help="Number of startline spaces (default 2)",
+ default=2)
+ dgroup = aparse.add_argument_group(
+ 'Directory options', 'Compiles all *.less files in directory that '
+ 'have a newer timestamp than it\'s css file.')
dgroup.add_argument('-o', '--out', action="store", help="Output directory")
dgroup.add_argument(
- '-r', '--recurse', action="store_true", help="Recursive into subdirectorys")
+ '-r',
+ '--recurse',
+ action="store_true",
+ help="Recursive into subdirectorys")
+ dgroup.add_argument(
+ '-f',
+ '--force',
+ action="store_true",
+ help="Force recompile on all files")
+ dgroup.add_argument(
+ '-m',
+ '--min-ending',
+ action="store_true",
+ default=False,
+ help="Add '.min' into output filename. eg, name.min.css")
dgroup.add_argument(
- '-f', '--force', action="store_true", help="Force recompile on all files")
- dgroup.add_argument('-m', '--min-ending', action="store_true",
- default=False, help="Add '.min' into output filename. eg, name.min.css")
- dgroup.add_argument('-D', '--dry-run', action="store_true",
- default=False, help="Dry run, do not write files")
+ '-D',
+ '--dry-run',
+ action="store_true",
+ default=False,
+ help="Dry run, do not write files")
group = aparse.add_argument_group('Debugging')
- group.add_argument('-g', '--debug', action="store_true",
- default=False, help="Debugging information")
- group.add_argument('-S', '--scopemap', action="store_true",
- default=False, help="Scopemap")
- group.add_argument('-L', '--lex-only', action="store_true",
- default=False, help="Run lexer on target")
- group.add_argument('-N', '--no-css', action="store_true",
- default=False, help="No css output")
+ group.add_argument(
+ '-g',
+ '--debug',
+ action="store_true",
+ default=False,
+ help="Debugging information")
+ group.add_argument(
+ '-S',
+ '--scopemap',
+ action="store_true",
+ default=False,
+ help="Scopemap")
+ group.add_argument(
+ '-L',
+ '--lex-only',
+ action="store_true",
+ default=False,
+ help="Run lexer on target")
+ group.add_argument(
+ '-N',
+ '--no-css',
+ action="store_true",
+ default=False,
+ help="No css output")
aparse.add_argument('target', help="less file or directory")
aparse.add_argument('output', nargs='?', help="output file path")
args = aparse.parse_args()
@@ -132,7 +189,8 @@ def run():
tok = ll.token()
if not tok:
break
- if hasattr(tok, "lexer"): # literals don't have the lexer attribute
+ if hasattr(tok,
+ "lexer"): # literals don't have the lexer attribute
print(tok, "State:", tok.lexer.lexstate)
else:
print(tok)
@@ -146,11 +204,12 @@ def run():
if args.include:
for u in args.include.split(','):
if os.path.exists(u):
- p = parser.LessParser(yacc_debug=(args.debug),
- lex_optimize=True,
- yacc_optimize=(not args.debug),
- tabfile=yacctab,
- verbose=args.verbose)
+ p = parser.LessParser(
+ yacc_debug=(args.debug),
+ lex_optimize=True,
+ yacc_optimize=(not args.debug),
+ tabfile=yacctab,
+ verbose=args.verbose)
p.parse(filename=u, debuglevel=args.debug)
if not scope:
scope = p.scope
@@ -168,11 +227,12 @@ def run():
if args.dry_run:
print('Dry run, nothing done.', file=sys.stderr)
else:
- p = parser.LessParser(yacc_debug=(args.debug),
- lex_optimize=True,
- yacc_optimize=(not args.debug),
- scope=copy.deepcopy(scope),
- verbose=args.verbose)
+ p = parser.LessParser(
+ yacc_debug=(args.debug),
+ lex_optimize=True,
+ yacc_optimize=(not args.debug),
+ scope=copy.deepcopy(scope),
+ verbose=args.verbose)
p.parse(filename=args.target, debuglevel=args.debug)
if args.scopemap:
args.no_css = True
@@ -180,7 +240,8 @@ def run():
if not args.no_css and p:
out = f.format(p)
if args.output:
- if not args.dont_create_dirs and not os.path.exists(os.path.dirname(args.output)):
+ if not args.dont_create_dirs and not os.path.exists(
+ os.path.dirname(args.output)):
try:
os.makedirs(os.path.dirname(args.output))
except OSError as exc: # Guard against race condition
diff --git a/requirements.txt b/requirements.txt
index 4efa1dc..90412f0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1 @@
ply
-six
diff --git a/setup.py b/setup.py
index cd3cc63..583e36e 100755
--- a/setup.py
+++ b/setup.py
@@ -8,12 +8,13 @@ import codecs
import lesscpy
-
with codecs.open('README.rst', encoding='utf-8') as f:
long_description = f.read()
with open("requirements.txt", "r") as f:
- install_requires = [str(req) for req in pkg_resources.parse_requirements(f)]
+ install_requires = [
+ str(req) for req in pkg_resources.parse_requirements(f)
+ ]
with open("test-requirements.txt", "r") as f:
test_requires = []
for line in f.readlines():
@@ -32,10 +33,8 @@ setup(
url='https://github.com/lesscpy/lesscpy',
packages=find_packages(exclude=['*test*']),
package_data={'': ['LICENSE']},
- entry_points = {
- 'console_scripts' : [
- 'lesscpy = lesscpy.scripts.compiler:run'
- ]
+ entry_points={
+ 'console_scripts': ['lesscpy = lesscpy.scripts.compiler:run']
},
install_requires=install_requires,
tests_require=test_requires,
@@ -49,11 +48,15 @@ setup(
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
- 'Programming Language :: Python :: 2.6',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3.3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
+ 'Programming Language :: Python :: 3.8',
+ 'Programming Language :: Python :: 3.9',
+ 'Programming Language :: Python :: 3.10',
+ 'Programming Language :: Python :: 3.11',
+ 'Programming Language :: Python :: Implementation :: CPython',
+ 'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Software Development :: Code Generators',
'Topic :: Software Development :: Pre-processors',
],
diff --git a/test-requirements.txt b/test-requirements.txt
index a7052aa..6cce76e 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -2,3 +2,4 @@
coverage
flake8
nose
+tox
\ No newline at end of file
diff --git a/test/__init__.py b/test/__init__.py
index 8922b7b..454593c 100644
--- a/test/__init__.py
+++ b/test/__init__.py
@@ -6,7 +6,6 @@ import re
import sys
import unittest
-
here = os.path.dirname(__file__)
path = os.path.abspath(here)
while os.path.dirname(path) != path:
diff --git a/test/core.py b/test/core.py
index 438ec54..a88333a 100644
--- a/test/core.py
+++ b/test/core.py
@@ -15,7 +15,11 @@ class Opt(object):
self.tabs = True
-def find_and_load_cases(cls, less_dir, css_dir, less_files=None, css_minimized=True):
+def find_and_load_cases(cls,
+ less_dir,
+ css_dir,
+ less_files=None,
+ css_minimized=True):
_less_path = os.path.join(os.path.dirname(__file__), less_dir)
_css_path = os.path.join(os.path.dirname(__file__), css_dir)
@@ -24,14 +28,15 @@ def find_and_load_cases(cls, less_dir, css_dir, less_files=None, css_minimized=T
else:
LESS = glob.glob(os.path.join(_less_path, '*.less'))
for less in LESS:
- lessf = less.split('.')[0].split('/')[-1]
+ lessf = less.rpartition('.')[0].split('/')[-1]
css = os.path.join(_css_path, lessf + '.css')
if css_minimized:
mincss = os.path.join(_css_path, lessf + '.min.css')
test_method = create_case((less, css, mincss))
else:
test_method = create_case((less, css, None))
- test_method.__name__ = 'test_%s' % "_".join(reversed(os.path.basename(less).split('.')))
+ test_method.__name__ = 'test_%s' % "_".join(
+ reversed(os.path.basename(less).split('.')))
setattr(cls, test_method.__name__, test_method)
@@ -48,13 +53,13 @@ def create_case(args):
with open(cssf) as cssf:
for line in cssf.readlines():
if i >= pl:
- self.fail(
- "%s: result has less lines (%d < %d)" % (cssf, i, pl))
+ self.fail("%s: result has less lines (%d < %d)" %
+ (cssf, i, pl))
line = line.rstrip()
if not line:
continue
- self.assertEqual(
- line, pout[i], '%s: Line %d' % (cssf, i + 1))
+ self.assertEqual(line, pout[i],
+ '%s: Line %d' % (cssf, i + 1))
i += 1
if pl > i and i:
self.fail(
@@ -74,14 +79,15 @@ def create_case(args):
with open(minf) as cssf:
for line in cssf.readlines():
if i >= ml:
- self.fail(
- "%s: result has less lines (%d < %d)" % (minf, i, ml))
- self.assertEqual(
- line.rstrip(), mout[i], '%s: Line %d' % (minf, i + 1))
+ self.fail("%s: result has less lines (%d < %d)" %
+ (minf, i, ml))
+ self.assertEqual(line.rstrip(), mout[i],
+ '%s: Line %d' % (minf, i + 1))
i += 1
if ml > i and i:
self.fail(
"%s: result has more lines (%d > %d)" % (minf, i, ml))
else:
self.fail("%s not found..." % minf)
+
return do_case_expected
diff --git a/test/css/css-variables.css b/test/css/css-variables.css
new file mode 100644
index 0000000..9f91915
--- /dev/null
+++ b/test/css/css-variables.css
@@ -0,0 +1,17 @@
+:root {
+ --bg: #e0e0e0;
+}
+p {
+ --bg: white;
+}
+* {
+ background: var(--bg);
+ color: var(--fg,var(--text-fg,black));
+}
+div {
+ --bg: red;
+}
+div span {
+ --bg: purple;
+ background: var(--bg);
+}
diff --git a/test/css/css-variables.min.css b/test/css/css-variables.min.css
new file mode 100644
index 0000000..9e8fa7e
--- /dev/null
+++ b/test/css/css-variables.min.css
@@ -0,0 +1,5 @@
+:root{--bg:#e0e0e0;}
+p{--bg:white;}
+*{background:var(--bg);color:var(--fg,var(--text-fg,black));}
+div{--bg:red;}
+div span{--bg:purple;background:var(--bg);}
diff --git a/test/css/issues/issue61.css b/test/css/issues/issue61.css
new file mode 100644
index 0000000..5e4711c
--- /dev/null
+++ b/test/css/issues/issue61.css
@@ -0,0 +1,10 @@
+@media screen and (max-width:460px) {
+ h1.title {
+ background: url("image_460.jpg");
+ }
+}
+@media screen and (min-width:461px) {
+ h1.title {
+ background: url("image_740.jpg");
+ }
+}
diff --git a/test/css/issues/issue65.css b/test/css/issues/issue65.css
new file mode 100644
index 0000000..5c4a148
--- /dev/null
+++ b/test/css/issues/issue65.css
@@ -0,0 +1,10 @@
+@media (max-width:400px) {
+ body {
+ color: green;
+ }
+}
+@media (max-width:30px) {
+ body h3 .before {
+ color: red;
+ }
+}
diff --git a/test/css/media-nested-2.css b/test/css/media-nested-2.css
new file mode 100644
index 0000000..94417d2
--- /dev/null
+++ b/test/css/media-nested-2.css
@@ -0,0 +1,23 @@
+@media screen {
+ div span p {
+ color: auto;
+ }
+}
+@media screen and (max-width:100px) and (min-width:50px) and print {
+ p {
+ color: auto;
+ }
+}
+@media (width:400px) {
+ .one {
+ font-size: 1.2em;
+ }
+ .one .sub {
+ background: red;
+ }
+}
+@media (width:400px) and print and (color) {
+ .one {
+ color: blue;
+ }
+}
diff --git a/test/css/media-nested-2.min.css b/test/css/media-nested-2.min.css
new file mode 100644
index 0000000..538aa99
--- /dev/null
+++ b/test/css/media-nested-2.min.css
@@ -0,0 +1,5 @@
+@media screen{div span p{color:auto;}}
+@media screen and (max-width:100px) and (min-width:50px) and print{p{color:auto;}}
+@media (width:400px){.one{font-size:1.2em;}
+.one .sub{background:red;}}
+@media (width:400px) and print and (color){.one{color:blue;}}
diff --git a/test/css/media-nested.css b/test/css/media-nested.css
new file mode 100644
index 0000000..5472dc1
--- /dev/null
+++ b/test/css/media-nested.css
@@ -0,0 +1,43 @@
+@media (width:768px) {
+ .lead {
+ font-size: 21px;
+ }
+}
+@media (width:400px) {
+ .one {
+ font-size: 1.2em;
+ }
+}
+@media (width:400px) and print and (color) {
+ .one {
+ color: blue;
+ }
+}
+.two {
+ width: 100px;
+}
+@media (width:400px) {
+ .two {
+ font-size: 1.2em;
+ }
+}
+@media print and (color) {
+ .two {
+ color: blue;
+ }
+}
+@media print {
+ .visible {
+ color: green;
+ }
+}
+@media screen {
+ .visible.visible-sm {
+ color: green;
+ }
+}
+@media (max-width:10px) {
+ .navbar .form {
+ margin-bottom: 5px;
+ }
+}
diff --git a/test/css/media-nested.min.css b/test/css/media-nested.min.css
new file mode 100644
index 0000000..9f353b9
--- /dev/null
+++ b/test/css/media-nested.min.css
@@ -0,0 +1,9 @@
+@media (width:768px){.lead{font-size:21px;}}
+@media (width:400px){.one{font-size:1.2em;}}
+@media (width:400px) and print and (color){.one{color:blue;}}
+.two{width:100px;}
+@media (width:400px){.two{font-size:1.2em;}}
+@media print and (color){.two{color:blue;}}
+@media print{.visible{color:green;}}
+@media screen{.visible.visible-sm{color:green;}}
+@media (max-width:10px){.navbar .form{margin-bottom:5px;}}
diff --git a/test/css/media.css b/test/css/media.css
index afebe5e..f078401 100644
--- a/test/css/media.css
+++ b/test/css/media.css
@@ -53,46 +53,3 @@
@media only screen and (min--moz-device-pixel-ratio:2.5),only screen and (-o-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2),only screen and (min-resolution:192dpi),only screen and (min-resolution:2dppx) {
background-size: 10px;
}
-@media (width:768px) {
- .lead {
- font-size: 21px;
- }
-}
-@media (width:400px) {
- .one {
- font-size: 1.2em;
- }
-}
-@media (width:400px) and print and (color) {
- .one {
- color: blue;
- }
-}
-.two {
- width: 100px;
-}
-@media (width:400px) {
- .two {
- font-size: 1.2em;
- }
-}
-@media print and (color) {
- .two {
- color: blue;
- }
-}
-@media print {
- .visible {
- color: green;
- }
-}
-@media screen {
- .visible.visible-sm {
- color: green;
- }
-}
-@media (max-width:10px) {
- .navbar .form {
- margin-bottom: 5px;
- }
-}
diff --git a/test/css/media.min.css b/test/css/media.min.css
index aa94486..bd06df8 100644
--- a/test/css/media.min.css
+++ b/test/css/media.min.css
@@ -10,12 +10,3 @@ body{max-width:35em;margin:0 auto;}}
@media (min-width:12px){body{margin:0 auto;}}
@media (width:767px){.visible-xs{display:block;}}
@media only screen and (min--moz-device-pixel-ratio:2.5),only screen and (-o-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2),only screen and (min-resolution:192dpi),only screen and (min-resolution:2dppx){background-size:10px;}
-@media (width:768px){.lead{font-size:21px;}}
-@media (width:400px){.one{font-size:1.2em;}}
-@media (width:400px) and print and (color){.one{color:blue;}}
-.two{width:100px;}
-@media (width:400px){.two{font-size:1.2em;}}
-@media print and (color){.two{color:blue;}}
-@media print{.visible{color:green;}}
-@media screen{.visible.visible-sm{color:green;}}
-@media (max-width:10px){.navbar .form{margin-bottom:5px;}}
diff --git a/test/less/css-variables.less b/test/less/css-variables.less
new file mode 100644
index 0000000..dcd0c18
--- /dev/null
+++ b/test/less/css-variables.less
@@ -0,0 +1,21 @@
+:root {
+ --bg: #e0e0e0;
+}
+
+p {
+ --bg: white;
+}
+
+* {
+ background: var(--bg);
+ color: var(--fg, var(--text-fg, black));
+}
+
+div {
+ --bg: red;
+
+ span {
+ --bg: purple;
+ background: var(--bg);
+ }
+}
diff --git a/test/less/issues/issue61.less b/test/less/issues/issue61.less
new file mode 100644
index 0000000..e142fdc
--- /dev/null
+++ b/test/less/issues/issue61.less
@@ -0,0 +1,10 @@
+h1 {
+ &.title {
+ @media screen and (max-width: 460px) {
+ background: url("image_460.jpg");
+ }
+ @media screen and (min-width: 461px) {
+ background: url("image_740.jpg");
+ }
+ }
+}
diff --git a/test/less/issues/issue65.less b/test/less/issues/issue65.less
new file mode 100644
index 0000000..98c5fea
--- /dev/null
+++ b/test/less/issues/issue65.less
@@ -0,0 +1,12 @@
+body {
+ @media (max-width: 400px) {
+ color:green;
+ }
+ h3 {
+ .before {
+ @media (max-width: 30px) {
+ color: red;
+ }
+ }
+ }
+}
diff --git a/test/less/media-nested-2.less b/test/less/media-nested-2.less
new file mode 100644
index 0000000..4be2c6f
--- /dev/null
+++ b/test/less/media-nested-2.less
@@ -0,0 +1,35 @@
+/* Media queries nested deep in the tree */
+div {
+ span {
+ p {
+ @media screen {
+ color: auto;
+ }
+ }
+ }
+}
+/* Streak of media queries to be merged */
+@media screen {
+ @media (max-width: 100px) {
+ @media (min-width: 50px) {
+ @media print {
+ p {
+ color: auto;
+ }
+ }
+ }
+ }
+}
+/* Interaction of media queries with properties, smaller blocks, and other
+ media queries at once */
+.one {
+ @media (width: 400px){
+ font-size: 1.2em;
+ .sub {
+ background: red;
+ }
+ @media print and (color) {
+ color: blue;
+ }
+ }
+}
diff --git a/test/less/media-nested.less b/test/less/media-nested.less
new file mode 100644
index 0000000..08f0720
--- /dev/null
+++ b/test/less/media-nested.less
@@ -0,0 +1,42 @@
+/*
+ Nested media queries
+*/
+.lead {
+ @media (width: 768px) {
+ font-size: 21px;
+ }
+}
+.one {
+ @media (width: 400px){
+ font-size: 1.2em;
+ @media print and (color) {
+ color: blue;
+ }
+ }
+}
+.two {
+ @media (width: 400px){
+ font-size: 1.2em;
+ }
+ @media print and (color) {
+ color: blue;
+ }
+ width: 100px;
+}
+.visible {
+ @media print {
+ color: green;
+ }
+ &.visible-sm {
+ @media screen {
+ color: green;
+ }
+ }
+}
+.navbar {
+ .form {
+ @media (max-width: 10px) {
+ margin-bottom: 5px;
+ }
+ }
+}
diff --git a/test/less/media.less b/test/less/media.less
index 3e4c846..ea09670 100644
--- a/test/less/media.less
+++ b/test/less/media.less
@@ -70,45 +70,3 @@
only screen and ( min-resolution: 2dppx) {
background-size: 10px;
}
-/*
- Nested media queries
-*/
-.lead {
- @media (width: 768px) {
- font-size: 21px;
- }
-}
-.one {
- @media (width: 400px){
- font-size: 1.2em;
- @media print and (color) {
- color: blue;
- }
- }
-}
-.two {
- @media (width: 400px){
- font-size: 1.2em;
- }
- @media print and (color) {
- color: blue;
- }
- width: 100px;
-}
-.visible {
- @media print {
- color: green;
- }
- &.visible-sm {
- @media screen {
- color: green;
- }
- }
-}
-.navbar {
- .form {
- @media (max-width: 10px) {
- margin-bottom: 5px;
- }
- }
-}
diff --git a/test/test_bootstrap3.py b/test/test_bootstrap3.py
index 2451456..f153eb5 100644
--- a/test/test_bootstrap3.py
+++ b/test/test_bootstrap3.py
@@ -15,12 +15,14 @@ class Bootstrap3ThemeTestCase(unittest.TestCase):
pass
-find_and_load_cases(Bootstrap3TestCase,
- less_dir='bootstrap3/less',
- less_files=['bootstrap'],
- css_dir='bootstrap3/css')
-
-find_and_load_cases(Bootstrap3ThemeTestCase,
- less_dir='bootstrap3/less',
- less_files=['theme'],
- css_dir='bootstrap3/css')
+find_and_load_cases(
+ Bootstrap3TestCase,
+ less_dir='bootstrap3/less',
+ less_files=['bootstrap'],
+ css_dir='bootstrap3/css')
+
+find_and_load_cases(
+ Bootstrap3ThemeTestCase,
+ less_dir='bootstrap3/less',
+ less_files=['theme'],
+ css_dir='bootstrap3/css')
diff --git a/test/test_color.py b/test/test_color.py
index 4ae87bc..6daacb1 100644
--- a/test/test_color.py
+++ b/test/test_color.py
@@ -133,7 +133,6 @@ class TestLessColor(unittest.TestCase):
('#29332f', '40%', '#174533'),
('#29332f', '60%', '#0d4f35'),
('#29332f', '100%', '#005c37'),
-
]:
self.assertEqual(test(c, p), v, v)
@@ -156,7 +155,6 @@ class TestLessColor(unittest.TestCase):
('#29332f', '40%', '#2e2e2e'),
('#29332f', '60%', '#2e2e2e'),
('#29332f', '100%', '#2e2e2e'),
-
]:
self.assertEqual(test(c, p), v, v)
@@ -179,6 +177,5 @@ class TestLessColor(unittest.TestCase):
('#29332f', '40%', '#293033'),
('#29332f', '60%', '#292d33'),
('#29332f', '100%', '#2c2933'),
-
]:
self.assertEqual(test(c, p), v, v)
diff --git a/test/test_expression.py b/test/test_expression.py
index be70bf7..3434b7c 100644
--- a/test/test_expression.py
+++ b/test/test_expression.py
@@ -14,8 +14,8 @@ class TestExpression(unittest.TestCase):
['2.0', '+', '2', '4'],
['2', '+', '2.0', '4'],
['2.0', '+', '2.0', '4'],
- [('2.0',), '+', '2.0', '4'],
- [('2.0',), '+', ('2.0',), '4'],
+ [('2.0', ), '+', '2.0', '4'],
+ [('2.0', ), '+', ('2.0', ), '4'],
['0px', '+', '0', '0'],
['2px', '+', '2', '4px'],
['2.0px', '+', '2', '4px'],
diff --git a/test/test_identifier.py b/test/test_identifier.py
index 14d5157..d729cc4 100644
--- a/test/test_identifier.py
+++ b/test/test_identifier.py
@@ -5,7 +5,6 @@ from lesscpy.plib.identifier import Identifier
class TestIdentifier(unittest.TestCase):
-
def test_basic(self):
fl = {'ws': ' ', 'nl': '\n'}
for i in [
@@ -77,21 +76,23 @@ class TestIdentifier(unittest.TestCase):
sc.push()
sc.current = Identifier(['.c', ',', '.d'], 0).parse(sc)
id = Identifier(['.deep'], 0)
- self.assertEqual(id.parse(sc).fmt(fl), '.a .next .c .deep,\n'
- '.b .next .c .deep,\n'
- '.a .next .d .deep,\n'
- '.b .next .d .deep')
- self.assertEqual(id.raw(), '.a% %.next% %.c% %.deep%'
- '.b% %.next% %.c% %.deep%'
- '.a% %.next% %.d% %.deep%'
- '.b% %.next% %.d% %.deep')
+ self.assertEqual(
+ id.parse(sc).fmt(fl), '.a .next .c .deep,\n'
+ '.b .next .c .deep,\n'
+ '.a .next .d .deep,\n'
+ '.b .next .d .deep')
+ self.assertEqual(
+ id.raw(), '.a% %.next% %.c% %.deep%'
+ '.b% %.next% %.c% %.deep%'
+ '.a% %.next% %.d% %.deep%'
+ '.b% %.next% %.d% %.deep')
def test_media(self):
fl = {'ws': ' ', 'nl': '\n'}
sc = Scope()
sc.push()
- sc.current = Identifier(
- ['@media', ' ', 'screen', ',', 'projection'], 0).parse(sc)
+ sc.current = Identifier(['@media', ' ', 'screen', ',', 'projection'],
+ 0).parse(sc)
self.assertEqual(sc.current.fmt(fl), '@media screen,projection')
for i in [
(['html'], 'html'),
diff --git a/test/test_issues.py b/test/test_issues.py
index 46e51e7..91719f8 100644
--- a/test/test_issues.py
+++ b/test/test_issues.py
@@ -10,7 +10,8 @@ class IssuesTestCase(unittest.TestCase):
pass
-find_and_load_cases(IssuesTestCase,
- less_dir='less/issues',
- css_dir='css/issues',
- css_minimized=False)
+find_and_load_cases(
+ IssuesTestCase,
+ less_dir='less/issues',
+ css_dir='css/issues',
+ css_minimized=False)
diff --git a/test/test_less.py b/test/test_less.py
index b5dcc1c..ef7dfc0 100644
--- a/test/test_less.py
+++ b/test/test_less.py
@@ -10,6 +10,4 @@ class LessTestCase(unittest.TestCase):
pass
-find_and_load_cases(LessTestCase,
- less_dir='less',
- css_dir='css')
+find_and_load_cases(LessTestCase, less_dir='less', css_dir='css')
diff --git a/test/test_pycompile.py b/test/test_pycompile.py
index 195b86c..2ed54aa 100644
--- a/test/test_pycompile.py
+++ b/test/test_pycompile.py
@@ -42,4 +42,5 @@ class TestCompileFunction(unittest.TestCase):
def fail_func():
compile(StringIO("a }"), minify=True)
+
self.assertRaises(CompilationError, fail_func)
diff --git a/test/test_utility.py b/test/test_utility.py
index 5c9d673..3ca4bcb 100644
--- a/test/test_utility.py
+++ b/test/test_utility.py
@@ -88,7 +88,7 @@ class TestUtility(unittest.TestCase):
self.assertEqual('0.6px', test(.6, 'px'))
self.assertEqual('1', test(1))
self.assertEqual('1', test(1, None))
- self.assertEqual('1', test(1,))
+ self.assertEqual('1', test(1, ))
def test_convergent_round(self):
test = utility.convergent_round
diff --git a/tox.ini b/tox.ini
index 8c9b39f..c4c5627 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,6 @@
[tox]
-envlist = py27,py33,p36
+envlist = py37,py38,py39,py3.10,py3.11,pypy3
+skip_missing_interpreters = True
[testenv]
deps = -r{toxinidir}/test-requirements.txt
Debdiff
[The following lists of changes regard files as different if they have different names, permissions or owners.]
Files in second set of .debs but not in first
-rw-r--r-- root/root /usr/lib/python3/dist-packages/lesscpy-0.15.1.egg-info/PKG-INFO -rw-r--r-- root/root /usr/lib/python3/dist-packages/lesscpy-0.15.1.egg-info/dependency_links.txt -rw-r--r-- root/root /usr/lib/python3/dist-packages/lesscpy-0.15.1.egg-info/entry_points.txt -rw-r--r-- root/root /usr/lib/python3/dist-packages/lesscpy-0.15.1.egg-info/requires.txt -rw-r--r-- root/root /usr/lib/python3/dist-packages/lesscpy-0.15.1.egg-info/top_level.txt
Files in first set of .debs but not in second
-rw-r--r-- root/root /usr/lib/python3/dist-packages/lesscpy-0.13.0.egg-info/PKG-INFO -rw-r--r-- root/root /usr/lib/python3/dist-packages/lesscpy-0.13.0.egg-info/dependency_links.txt -rw-r--r-- root/root /usr/lib/python3/dist-packages/lesscpy-0.13.0.egg-info/entry_points.txt -rw-r--r-- root/root /usr/lib/python3/dist-packages/lesscpy-0.13.0.egg-info/requires.txt -rw-r--r-- root/root /usr/lib/python3/dist-packages/lesscpy-0.13.0.egg-info/top_level.txt
Control files: lines which differ (wdiff format)
Depends: python3-ply, python3-six, python3:any