Documentation
This commit is contained in:
parent
b050925ba0
commit
ba4fe2ea62
lesscpy
@ -0,0 +1,4 @@
|
|||||||
|
"""
|
||||||
|
Main lesscss parse library. Contains lexer and parser, along with
|
||||||
|
utility classes
|
||||||
|
"""
|
@ -1,9 +1,11 @@
|
|||||||
|
# -*- coding: utf8 -*-
|
||||||
"""
|
"""
|
||||||
LESSCPY Color functions
|
.. module:: lesscpy.lessc.color
|
||||||
|
:synopsis: Lesscpy Color functions
|
||||||
|
|
||||||
Copyright (c)
|
Copyright (c)
|
||||||
See LICENSE for details.
|
See LICENSE for details.
|
||||||
<jtm@robot.is>
|
.. moduleauthor:: Jóhann T. Maríusson <jtm@robot.is>
|
||||||
"""
|
"""
|
||||||
import colorsys
|
import colorsys
|
||||||
from . import utility
|
from . import utility
|
||||||
@ -11,8 +13,10 @@ from . import utility
|
|||||||
class Color():
|
class Color():
|
||||||
def process(self, expression):
|
def process(self, expression):
|
||||||
""" Process color expression
|
""" Process color expression
|
||||||
@param tuple: color expression
|
args:
|
||||||
@return: string
|
expression (tuple): color expression
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
a, o, b = expression
|
a, o, b = expression
|
||||||
c1 = self._hextorgb(a)
|
c1 = self._hextorgb(a)
|
||||||
@ -25,23 +29,29 @@ class Color():
|
|||||||
r.append("%02x" % v)
|
r.append("%02x" % v)
|
||||||
return ''.join(r)
|
return ''.join(r)
|
||||||
|
|
||||||
def operate(self, a, b, o):
|
def operate(self, left, right, operation):
|
||||||
""" Do operation on colors
|
""" Do operation on colors
|
||||||
@param string: color
|
args:
|
||||||
@param string: color
|
left (str): left side
|
||||||
@param string: operator
|
right (str): right side
|
||||||
|
operation (str): Operation
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
operation = {
|
operation = {
|
||||||
'+': '__add__',
|
'+': '__add__',
|
||||||
'-': '__sub__',
|
'-': '__sub__',
|
||||||
'*': '__mul__',
|
'*': '__mul__',
|
||||||
'/': '__truediv__'
|
'/': '__truediv__'
|
||||||
}.get(o)
|
}.get(operation)
|
||||||
v = getattr(a, operation)(b)
|
return getattr(left, operation)(right)
|
||||||
return v
|
|
||||||
|
|
||||||
def rgb(self, *args):
|
def rgb(self, *args):
|
||||||
"""
|
""" Translate rgb(...) to color string
|
||||||
|
raises:
|
||||||
|
ValueError
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
if len(args) == 4:
|
if len(args) == 4:
|
||||||
return self.rgba(*args)
|
return self.rgba(*args)
|
||||||
@ -57,7 +67,11 @@ class Color():
|
|||||||
raise ValueError('Illegal color values')
|
raise ValueError('Illegal color values')
|
||||||
|
|
||||||
def rgba(self, *args):
|
def rgba(self, *args):
|
||||||
"""
|
""" Translate rgba(...) to color string
|
||||||
|
raises:
|
||||||
|
ValueError
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
if len(args) == 4:
|
if len(args) == 4:
|
||||||
try:
|
try:
|
||||||
@ -71,7 +85,11 @@ class Color():
|
|||||||
raise ValueError('Illegal color values')
|
raise ValueError('Illegal color values')
|
||||||
|
|
||||||
def hsl(self, *args):
|
def hsl(self, *args):
|
||||||
"""
|
""" Translate hsl(...) to color string
|
||||||
|
raises:
|
||||||
|
ValueError
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
if len(args) == 4:
|
if len(args) == 4:
|
||||||
return self.hsla(*args)
|
return self.hsla(*args)
|
||||||
@ -85,7 +103,11 @@ class Color():
|
|||||||
raise ValueError('Illegal color values')
|
raise ValueError('Illegal color values')
|
||||||
|
|
||||||
def hsla(self, *args):
|
def hsla(self, *args):
|
||||||
"""
|
""" Translate hsla(...) to color string
|
||||||
|
raises:
|
||||||
|
ValueError
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
if len(args) == 4:
|
if len(args) == 4:
|
||||||
h, s, l, a = args
|
h, s, l, a = args
|
||||||
@ -97,28 +119,46 @@ class Color():
|
|||||||
return "rgba(%s,%s,%s,%s)" % tuple(color)
|
return "rgba(%s,%s,%s,%s)" % tuple(color)
|
||||||
raise ValueError('Illegal color values')
|
raise ValueError('Illegal color values')
|
||||||
|
|
||||||
def hue(self, *args):
|
def hue(self, color, *args):
|
||||||
|
""" Return the hue value of a color
|
||||||
|
args:
|
||||||
|
color (str): color
|
||||||
|
raises:
|
||||||
|
ValueError
|
||||||
|
returns:
|
||||||
|
float
|
||||||
"""
|
"""
|
||||||
"""
|
if color:
|
||||||
if args:
|
h, l, s = self._hextohls(color)
|
||||||
h, l, s = self._hextohls(args[0])
|
return round(h * 360.0, 3)
|
||||||
return round(h * 360, 3)
|
|
||||||
raise ValueError('Illegal color values')
|
raise ValueError('Illegal color values')
|
||||||
|
|
||||||
def saturation(self, *args):
|
def saturation(self, color, *args):
|
||||||
|
""" Return the saturation value of a color
|
||||||
|
args:
|
||||||
|
color (str): color
|
||||||
|
raises:
|
||||||
|
ValueError
|
||||||
|
returns:
|
||||||
|
float
|
||||||
"""
|
"""
|
||||||
"""
|
if color:
|
||||||
if args:
|
h, l, s = self._hextohls(color)
|
||||||
h, l, s = self._hextohls(args[0])
|
return s * 100.0
|
||||||
return s * 100
|
|
||||||
raise ValueError('Illegal color values')
|
raise ValueError('Illegal color values')
|
||||||
|
|
||||||
def lightness(self, *args):
|
def lightness(self, color, *args):
|
||||||
|
""" Return the lightness value of a color
|
||||||
|
args:
|
||||||
|
color (str): color
|
||||||
|
raises:
|
||||||
|
ValueError
|
||||||
|
returns:
|
||||||
|
float
|
||||||
"""
|
"""
|
||||||
"""
|
if color:
|
||||||
if args:
|
h, l, s = self._hextohls(color)
|
||||||
h, l, s = self._hextohls(args[0])
|
return l * 100.0
|
||||||
return l * 100
|
|
||||||
raise ValueError('Illegal color values')
|
raise ValueError('Illegal color values')
|
||||||
|
|
||||||
def opacity(self, *args):
|
def opacity(self, *args):
|
||||||
@ -126,74 +166,97 @@ class Color():
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def lighten(self, *args):
|
def lighten(self, color, diff, *args):
|
||||||
|
""" Lighten a color
|
||||||
|
args:
|
||||||
|
color (str): color
|
||||||
|
diff (str): percentage
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
"""
|
if color and diff:
|
||||||
if len(args) == 2:
|
|
||||||
color, diff = args
|
|
||||||
return self._ophsl(color, diff, 1, '__add__')
|
return self._ophsl(color, diff, 1, '__add__')
|
||||||
raise ValueError('Illegal color values')
|
raise ValueError('Illegal color values')
|
||||||
|
|
||||||
def darken(self, *args):
|
def darken(self, color, diff, *args):
|
||||||
|
""" Darken a color
|
||||||
|
args:
|
||||||
|
color (str): color
|
||||||
|
diff (str): percentage
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
"""
|
if color and diff:
|
||||||
if len(args) == 2:
|
|
||||||
color, diff = args
|
|
||||||
return self._ophsl(color, diff, 1, '__sub__')
|
return self._ophsl(color, diff, 1, '__sub__')
|
||||||
raise ValueError('Illegal color values')
|
raise ValueError('Illegal color values')
|
||||||
|
|
||||||
def saturate(self, *args):
|
def saturate(self, color, diff, *args):
|
||||||
|
""" Saturate a color
|
||||||
|
args:
|
||||||
|
color (str): color
|
||||||
|
diff (str): percentage
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
"""
|
if color and diff:
|
||||||
if len(args) == 2:
|
|
||||||
color, diff = args
|
|
||||||
return self._ophsl(color, diff, 2, '__add__')
|
return self._ophsl(color, diff, 2, '__add__')
|
||||||
raise ValueError('Illegal color values')
|
raise ValueError('Illegal color values')
|
||||||
|
|
||||||
def desaturate(self, *args):
|
def desaturate(self, color, diff, *args):
|
||||||
|
""" Desaturate a color
|
||||||
|
args:
|
||||||
|
color (str): color
|
||||||
|
diff (str): percentage
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
"""
|
if color and diff:
|
||||||
if len(args) == 2:
|
|
||||||
color, diff = args
|
|
||||||
return self._ophsl(color, diff, 2, '__sub__')
|
return self._ophsl(color, diff, 2, '__sub__')
|
||||||
raise ValueError('Illegal color values')
|
raise ValueError('Illegal color values')
|
||||||
|
|
||||||
def clamp(self, v):
|
def _clamp(self, value):
|
||||||
"""
|
# Clamp value
|
||||||
"""
|
return min(1, max(0, value))
|
||||||
return min(1, max(0, v))
|
|
||||||
|
|
||||||
def grayscale(self, *args):
|
def grayscale(self, color, *args):
|
||||||
|
""" Simply 100% desaturate.
|
||||||
|
args:
|
||||||
|
color (str): color
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
Simply 100% desaturate.
|
if color:
|
||||||
"""
|
return self.desaturate(color, 100.0)
|
||||||
if len(args) == 2:
|
|
||||||
return self.desaturate(args[0], 100)
|
|
||||||
raise ValueError('Illegal color values')
|
raise ValueError('Illegal color values')
|
||||||
|
|
||||||
def greyscale(self, *args):
|
def greyscale(self, color, *args):
|
||||||
|
"""Wrapper for grayscale, other spelling
|
||||||
"""
|
"""
|
||||||
Wrapper for grayscale
|
return self.grayscale(color, *args)
|
||||||
"""
|
|
||||||
return self.grayscale(*args)
|
|
||||||
|
|
||||||
def spin(self, *args):
|
def spin(self, color, degree, *args):
|
||||||
|
""" Spin color by degree. (Increase / decrease hue)
|
||||||
|
args:
|
||||||
|
color (str): color
|
||||||
|
degree (str): percentage
|
||||||
|
raises:
|
||||||
|
ValueError
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
"""
|
if color and degree:
|
||||||
if len(args) == 2:
|
if type(degree) == str:
|
||||||
color, deg = args
|
degree = int(degree.strip('%'))
|
||||||
if type(deg) == str: deg = int(deg.strip('%'))
|
|
||||||
h, l, s = self._hextohls(color)
|
h, l, s = self._hextohls(color)
|
||||||
h = ((h * 360) + deg) % 360
|
h = ((h * 360.0) + degree) % 360.0
|
||||||
h = 360 + h if h < 0 else h
|
h = 360.0 + h if h < 0 else h
|
||||||
rgb = colorsys.hls_to_rgb(h / 360, l, s)
|
rgb = colorsys.hls_to_rgb(h / 360.0, l, s)
|
||||||
color = (round(c * 255) for c in rgb)
|
color = (round(c * 255) for c in rgb)
|
||||||
return self._rgbatohex(color)
|
return self._rgbatohex(color)
|
||||||
raise ValueError('Illegal color values')
|
raise ValueError('Illegal color values')
|
||||||
|
|
||||||
def mix(self, *args):
|
def mix(self, color1, color2, weight=50, *args):
|
||||||
"""
|
"""This algorithm factors in both the user-provided weight
|
||||||
This algorithm factors in both the user-provided weight
|
|
||||||
and the difference between the alpha values of the two colors
|
and the difference between the alpha values of the two colors
|
||||||
to decide how to perform the weighted average of the two RGB values.
|
to decide how to perform the weighted average of the two RGB values.
|
||||||
|
|
||||||
@ -216,19 +279,24 @@ class Color():
|
|||||||
|
|
||||||
Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
|
Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
|
||||||
http://sass-lang.com
|
http://sass-lang.com
|
||||||
|
args:
|
||||||
|
color1 (str): first color
|
||||||
|
color2 (str): second color
|
||||||
|
weight (int/str): weight
|
||||||
|
raises:
|
||||||
|
ValueError
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
if len(args) >= 2:
|
if color1 and color2:
|
||||||
try:
|
if type(weight) == str:
|
||||||
c1, c2, w = args
|
weight = int(weight.strip('%'))
|
||||||
except ValueError:
|
weight = ((weight / 100.0) * 2) - 1
|
||||||
c1, c2 = args
|
rgb1 = self._hextorgb(color1)
|
||||||
w = 50
|
rgb2 = self._hextorgb(color2)
|
||||||
if type(w) == str: w = int(w.strip('%'))
|
alpha = 0
|
||||||
w = ((w / 100.0) * 2) - 1
|
w1 = (((weight if weight * alpha == -1
|
||||||
rgb1 = self._hextorgb(c1)
|
else weight + alpha) / (1 + weight * alpha)) + 1)
|
||||||
rgb2 = self._hextorgb(c2)
|
|
||||||
a = 0
|
|
||||||
w1 = (((w if w * a == -1 else w + a) / (1 + w * a)) + 1)
|
|
||||||
w1 = w1 / 2.0
|
w1 = w1 / 2.0
|
||||||
w2 = 1 - w1
|
w2 = 1 - w1
|
||||||
rgb = [
|
rgb = [
|
||||||
@ -240,10 +308,14 @@ class Color():
|
|||||||
raise ValueError('Illegal color values')
|
raise ValueError('Illegal color values')
|
||||||
|
|
||||||
def fmt(self, color):
|
def fmt(self, color):
|
||||||
"""
|
""" Format CSS Hex color code.
|
||||||
Format CSS Hex color code.
|
uppercase becomes lowercase, 3 digit codes expand to 6 digit.
|
||||||
uppercase becomes lowercase, 3 digit codes expand to 6 digit.
|
args:
|
||||||
@param string: color
|
color (str): color
|
||||||
|
raises:
|
||||||
|
ValueError
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
if utility.is_color(color):
|
if utility.is_color(color):
|
||||||
color = color.lower().strip('#')
|
color = color.lower().strip('#')
|
||||||
@ -253,8 +325,6 @@ class Color():
|
|||||||
raise ValueError('Cannot format non-color')
|
raise ValueError('Cannot format non-color')
|
||||||
|
|
||||||
def _rgbatohex(self, rgba):
|
def _rgbatohex(self, rgba):
|
||||||
"""
|
|
||||||
"""
|
|
||||||
return '#%s' % ''.join(["%02x" % v for v in
|
return '#%s' % ''.join(["%02x" % v for v in
|
||||||
[0xff
|
[0xff
|
||||||
if h > 0xff else
|
if h > 0xff else
|
||||||
@ -262,8 +332,6 @@ class Color():
|
|||||||
for h in rgba]
|
for h in rgba]
|
||||||
])
|
])
|
||||||
def _hextorgb(self, hex):
|
def _hextorgb(self, hex):
|
||||||
"""
|
|
||||||
"""
|
|
||||||
hex = hex.strip()
|
hex = hex.strip()
|
||||||
if hex[0] == '#':
|
if hex[0] == '#':
|
||||||
hex = hex.strip('#').strip(';')
|
hex = hex.strip('#').strip(';')
|
||||||
@ -275,17 +343,13 @@ class Color():
|
|||||||
return [int(hex, 16)] * 3
|
return [int(hex, 16)] * 3
|
||||||
|
|
||||||
def _hextohls(self, hex):
|
def _hextohls(self, hex):
|
||||||
"""
|
|
||||||
"""
|
|
||||||
rgb = self._hextorgb(hex)
|
rgb = self._hextorgb(hex)
|
||||||
return colorsys.rgb_to_hls(*[c / 255.0 for c in rgb])
|
return colorsys.rgb_to_hls(*[c / 255.0 for c in rgb])
|
||||||
|
|
||||||
def _ophsl(self, color, diff, idx, op):
|
def _ophsl(self, color, diff, idx, op):
|
||||||
"""
|
|
||||||
"""
|
|
||||||
if type(diff) == str: diff = int(diff.strip('%'))
|
if type(diff) == str: diff = int(diff.strip('%'))
|
||||||
hls = list(self._hextohls(color))
|
hls = list(self._hextohls(color))
|
||||||
hls[idx] = self.clamp(getattr(hls[idx], op)(diff / 100))
|
hls[idx] = self._clamp(getattr(hls[idx], op)(diff / 100))
|
||||||
rgb = colorsys.hls_to_rgb(*hls)
|
rgb = colorsys.hls_to_rgb(*hls)
|
||||||
color = (round(c * 255) for c in rgb)
|
color = (round(c * 255) for c in rgb)
|
||||||
return self._rgbatohex(color)
|
return self._rgbatohex(color)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- coding: utf8 -*-
|
# -*- coding: utf8 -*-
|
||||||
"""
|
"""
|
||||||
.. module:: parser
|
.. module:: lesscpy.lessc.parser
|
||||||
:synopsis: Lesscss parser.
|
:synopsis: Lesscss parser.
|
||||||
|
|
||||||
http://www.dabeaz.com/ply/ply.html
|
http://www.dabeaz.com/ply/ply.html
|
||||||
http://www.w3.org/TR/CSS21/grammar.html#scanner
|
http://www.w3.org/TR/CSS21/grammar.html#scanner
|
||||||
http://lesscss.org/#docs
|
http://lesscss.org/#docs
|
||||||
@ -33,7 +33,8 @@ class LessParser(object):
|
|||||||
scope=None,
|
scope=None,
|
||||||
outputdir='/tmp',
|
outputdir='/tmp',
|
||||||
importlvl=0,
|
importlvl=0,
|
||||||
verbose=False):
|
verbose=False
|
||||||
|
):
|
||||||
""" Parser object
|
""" Parser object
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
@ -46,9 +47,9 @@ class LessParser(object):
|
|||||||
importlvl (int): Import depth
|
importlvl (int): Import depth
|
||||||
verbose (bool): Verbose mode
|
verbose (bool): Verbose mode
|
||||||
"""
|
"""
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
self.importlvl = importlvl
|
self.importlvl = importlvl
|
||||||
self.lex = lexer.LessLexer()
|
self.lex = lexer.LessLexer()
|
||||||
if not tabfile:
|
if not tabfile:
|
||||||
tabfile = 'yacctab'
|
tabfile = 'yacctab'
|
||||||
|
|
||||||
|
@ -1,16 +1,29 @@
|
|||||||
"""
|
"""
|
||||||
|
.. module:: lesscpy.lessc.scope
|
||||||
|
:synopsis: Scope class.
|
||||||
|
|
||||||
|
|
||||||
|
Copyright (c)
|
||||||
|
See LICENSE for details.
|
||||||
|
.. moduleauthor:: Jóhann T. Maríusson <jtm@robot.is>
|
||||||
"""
|
"""
|
||||||
from . import utility
|
from . import utility
|
||||||
|
|
||||||
class Scope(list):
|
class Scope(list):
|
||||||
|
""" Scope class. A stack implementation.
|
||||||
|
"""
|
||||||
def __init__(self, init=False):
|
def __init__(self, init=False):
|
||||||
|
"""Scope
|
||||||
|
Args:
|
||||||
|
init (bool): Initiate scope
|
||||||
|
"""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._mixins = {}
|
self._mixins = {}
|
||||||
if init: self.push()
|
if init: self.push()
|
||||||
self.in_mixin = False
|
self.in_mixin = False
|
||||||
|
|
||||||
def push(self):
|
def push(self):
|
||||||
"""
|
"""Push level on scope
|
||||||
"""
|
"""
|
||||||
self.append({
|
self.append({
|
||||||
'__variables__' : {},
|
'__variables__' : {},
|
||||||
@ -29,7 +42,9 @@ class Scope(list):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def scopename(self):
|
def scopename(self):
|
||||||
"""
|
"""Current scope name as list
|
||||||
|
Returns:
|
||||||
|
list
|
||||||
"""
|
"""
|
||||||
return [r['__current__']
|
return [r['__current__']
|
||||||
for r in self
|
for r in self
|
||||||
@ -37,13 +52,17 @@ class Scope(list):
|
|||||||
|
|
||||||
|
|
||||||
def add_block(self, block):
|
def add_block(self, block):
|
||||||
"""
|
"""Add block element to scope
|
||||||
|
Args:
|
||||||
|
block (Block): Block object
|
||||||
"""
|
"""
|
||||||
self[-1]['__blocks__'].append(block)
|
self[-1]['__blocks__'].append(block)
|
||||||
self[-1]['__names__'].append(block.raw())
|
self[-1]['__names__'].append(block.raw())
|
||||||
|
|
||||||
def add_mixin(self, mixin):
|
def add_mixin(self, mixin):
|
||||||
"""
|
"""Add mixin to scope
|
||||||
|
Args:
|
||||||
|
mixin (Mixin): Mixin object
|
||||||
"""
|
"""
|
||||||
raw = mixin.name.raw()
|
raw = mixin.name.raw()
|
||||||
if raw in self._mixins:
|
if raw in self._mixins:
|
||||||
@ -52,13 +71,18 @@ class Scope(list):
|
|||||||
self._mixins[raw] = [mixin]
|
self._mixins[raw] = [mixin]
|
||||||
|
|
||||||
def add_variable(self, variable):
|
def add_variable(self, variable):
|
||||||
"""
|
"""Add variable to scope
|
||||||
|
Args:
|
||||||
|
variable (Variable): Variable object
|
||||||
"""
|
"""
|
||||||
self[-1]['__variables__'][variable.name] = variable
|
self[-1]['__variables__'][variable.name] = variable
|
||||||
|
|
||||||
def variables(self, name):
|
def variables(self, name):
|
||||||
"""
|
"""Search for variable by name. Searches scope top down
|
||||||
Search for variable by name
|
Args:
|
||||||
|
name (string): Search term
|
||||||
|
Returns:
|
||||||
|
Variable object OR False
|
||||||
"""
|
"""
|
||||||
if type(name) is tuple:
|
if type(name) is tuple:
|
||||||
name = name[0]
|
name = name[0]
|
||||||
@ -70,17 +94,19 @@ class Scope(list):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def mixins(self, name):
|
def mixins(self, name):
|
||||||
"""
|
""" Search mixins for name.
|
||||||
Search mixins for name.
|
Allow '>' to be ignored. '.a .b()' == '.a > .b()'
|
||||||
Allow '>' to be ignored.
|
Args:
|
||||||
|
name (string): Search term
|
||||||
|
Returns:
|
||||||
|
Mixin object list OR False
|
||||||
"""
|
"""
|
||||||
m = self._smixins(name)
|
m = self._smixins(name)
|
||||||
if m: return m
|
if m: return m
|
||||||
return self._smixins(name.replace('?>?', ' '))
|
return self._smixins(name.replace('?>?', ' '))
|
||||||
|
|
||||||
def _smixins(self, name):
|
def _smixins(self, name):
|
||||||
"""
|
"""Inner wrapper to search for mixins by name.
|
||||||
Inner wrapper to search for mixins by name.
|
|
||||||
"""
|
"""
|
||||||
return (self._mixins[name]
|
return (self._mixins[name]
|
||||||
if name in self._mixins
|
if name in self._mixins
|
||||||
@ -89,15 +115,18 @@ class Scope(list):
|
|||||||
def blocks(self, name):
|
def blocks(self, name):
|
||||||
"""
|
"""
|
||||||
Search for defined blocks recursively.
|
Search for defined blocks recursively.
|
||||||
Allow '>' to be ignored.
|
Allow '>' to be ignored. '.a .b' == '.a > .b'
|
||||||
|
Args:
|
||||||
|
name (string): Search term
|
||||||
|
Returns:
|
||||||
|
Block object OR False
|
||||||
"""
|
"""
|
||||||
b = self._blocks(name)
|
b = self._blocks(name)
|
||||||
if b: return b
|
if b: return b
|
||||||
return self._blocks(name.replace('?>?', ' '))
|
return self._blocks(name.replace('?>?', ' '))
|
||||||
|
|
||||||
def _blocks(self, name):
|
def _blocks(self, name):
|
||||||
"""
|
"""Inner wrapper to search for blocks by name.
|
||||||
Inner wrapper to search for blocks by name.
|
|
||||||
"""
|
"""
|
||||||
i = len(self)
|
i = len(self)
|
||||||
while i >= 0:
|
while i >= 0:
|
||||||
@ -116,7 +145,11 @@ class Scope(list):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def update(self, scope, at=0):
|
def update(self, scope, at=0):
|
||||||
"""
|
"""Update scope. Add another scope to this one.
|
||||||
|
Args:
|
||||||
|
scope (Scope): Scope object
|
||||||
|
Kwargs:
|
||||||
|
at (int): Level to update
|
||||||
"""
|
"""
|
||||||
if hasattr(scope, '_mixins') and not at:
|
if hasattr(scope, '_mixins') and not at:
|
||||||
self._mixins.update(scope._mixins)
|
self._mixins.update(scope._mixins)
|
||||||
@ -125,13 +158,19 @@ class Scope(list):
|
|||||||
self[at]['__names__'].extend(scope[at]['__names__'])
|
self[at]['__names__'].extend(scope[at]['__names__'])
|
||||||
|
|
||||||
def swap(self, name):
|
def swap(self, name):
|
||||||
"""
|
""" Swap variable name for variable value
|
||||||
|
Args:
|
||||||
|
name (str): Variable name
|
||||||
|
Returns:
|
||||||
|
Variable value (Mixed)
|
||||||
"""
|
"""
|
||||||
if name.startswith('@@'):
|
if name.startswith('@@'):
|
||||||
var = self.variables(name[1:])
|
var = self.variables(name[1:])
|
||||||
if var is False: raise SyntaxError('Unknown variable %s' % name)
|
if var is False:
|
||||||
|
raise SyntaxError('Unknown variable %s' % name)
|
||||||
name = '@' + utility.destring(var.value[0])
|
name = '@' + utility.destring(var.value[0])
|
||||||
var = self.variables(name)
|
var = self.variables(name)
|
||||||
if var is False: raise SyntaxError('Unknown variable %s' % name)
|
if var is False:
|
||||||
|
raise SyntaxError('Unknown variable %s' % name)
|
||||||
return var.value
|
return var.value
|
||||||
|
|
@ -1,41 +1,52 @@
|
|||||||
|
# -*- coding: utf8 -*-
|
||||||
"""
|
"""
|
||||||
Utility functions
|
.. module:: lesscpy.lessc.utility
|
||||||
|
:synopsis: various utility functions
|
||||||
|
|
||||||
Copyright (c)
|
Copyright (c)
|
||||||
See LICENSE for details.
|
See LICENSE for details.
|
||||||
<jtm@robot.is>
|
.. moduleauthor:: Jóhann T. Maríusson <jtm@robot.is>
|
||||||
"""
|
"""
|
||||||
import collections
|
import collections
|
||||||
import re
|
import re
|
||||||
|
|
||||||
def flatten(ll):
|
def flatten(lst):
|
||||||
|
"""Flatten list.
|
||||||
|
Args:
|
||||||
|
lst (list): List to flatten
|
||||||
|
Returns:
|
||||||
|
generator
|
||||||
"""
|
"""
|
||||||
Flatten list.
|
for elm in lst:
|
||||||
@param ll: list
|
if isinstance(elm, collections.Iterable) and not isinstance(elm, str):
|
||||||
@return: generator
|
for sub in flatten(elm):
|
||||||
"""
|
|
||||||
for el in ll:
|
|
||||||
if isinstance(el, collections.Iterable) and not isinstance(el, str):
|
|
||||||
for sub in flatten(el):
|
|
||||||
yield sub
|
yield sub
|
||||||
else:
|
else:
|
||||||
yield el
|
yield elm
|
||||||
|
|
||||||
def pairwise(lst):
|
def pairwise(lst):
|
||||||
""" yield item i and item i+1 in lst. e.g.
|
""" yield item i and item i+1 in lst. e.g.
|
||||||
(lst[0], lst[1]), (lst[1], lst[2]), ..., (lst[-1], None)
|
(lst[0], lst[1]), (lst[1], lst[2]), ..., (lst[-1], None)
|
||||||
|
Args:
|
||||||
|
lst (list): List to process
|
||||||
|
Returns:
|
||||||
|
list
|
||||||
"""
|
"""
|
||||||
if not lst: return
|
if not lst:
|
||||||
l = len(lst)
|
return
|
||||||
for i in range(l-1):
|
length = len(lst)
|
||||||
|
for i in range(length-1):
|
||||||
yield lst[i], lst[i+1]
|
yield lst[i], lst[i+1]
|
||||||
yield lst[-1], None
|
yield lst[-1], None
|
||||||
|
|
||||||
def rename(ll, scope):
|
def rename(blocks, scope):
|
||||||
""" Rename all sub-blocks moved under another
|
""" Rename all sub-blocks moved under another
|
||||||
block. (mixins)
|
block. (mixins)
|
||||||
|
Args:
|
||||||
|
lst (list): block list
|
||||||
|
scope (object): Scope object
|
||||||
"""
|
"""
|
||||||
for p in ll:
|
for p in blocks:
|
||||||
if hasattr(p, 'inner'):
|
if hasattr(p, 'inner'):
|
||||||
p.name.parse(scope)
|
p.name.parse(scope)
|
||||||
if p.inner:
|
if p.inner:
|
||||||
@ -45,7 +56,11 @@ def rename(ll, scope):
|
|||||||
scope.pop()
|
scope.pop()
|
||||||
|
|
||||||
def blocksearch(block, name):
|
def blocksearch(block, name):
|
||||||
""" Recursive search for name in block
|
""" Recursive search for name in block (inner blocks)
|
||||||
|
Args:
|
||||||
|
name (str): search term
|
||||||
|
Returns:
|
||||||
|
Block OR False
|
||||||
"""
|
"""
|
||||||
for b in block.inner:
|
for b in block.inner:
|
||||||
b = (b if b.raw() == name
|
b = (b if b.raw() == name
|
||||||
@ -53,43 +68,59 @@ def blocksearch(block, name):
|
|||||||
if b: return b
|
if b: return b
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def reverse_guard(ll):
|
def reverse_guard(lst):
|
||||||
"""
|
""" Reverse guard expression. not
|
||||||
|
(@a > 5) -> (@a <= 5)
|
||||||
|
Args:
|
||||||
|
lst (list): Expression
|
||||||
|
returns:
|
||||||
|
list
|
||||||
"""
|
"""
|
||||||
rev = {
|
rev = {
|
||||||
'<': '>',
|
'<': '>=',
|
||||||
'>': '<',
|
'>': '<=',
|
||||||
'=': '!=',
|
'=': '!=',
|
||||||
'!=': '=',
|
'!=': '=',
|
||||||
'>=': '<=',
|
'>=': '<',
|
||||||
'<=': '>='
|
'<=': '>'
|
||||||
}
|
}
|
||||||
return [rev[l] if l in rev else l for l in ll]
|
return [rev[l] if l in rev else l for l in lst]
|
||||||
|
|
||||||
def debug_print(ll, lvl=0):
|
def debug_print(lst, lvl=0):
|
||||||
"""
|
""" Print scope tree
|
||||||
|
args:
|
||||||
|
lst (list): parse result
|
||||||
|
lvl (int): current nesting level
|
||||||
"""
|
"""
|
||||||
pad = ''.join(['\t.'] * lvl)
|
pad = ''.join(['\t.'] * lvl)
|
||||||
t = type(ll)
|
t = type(lst)
|
||||||
if t is list:
|
if t is list:
|
||||||
for p in ll:
|
for p in lst:
|
||||||
debug_print(p, lvl)
|
debug_print(p, lvl)
|
||||||
elif hasattr(ll, 'tokens'):
|
elif hasattr(lst, 'tokens'):
|
||||||
print(pad, t)
|
print(pad, t)
|
||||||
debug_print(list(flatten(ll.tokens)), lvl+1)
|
debug_print(list(flatten(lst.tokens)), lvl+1)
|
||||||
|
|
||||||
def destring(v):
|
def destring(value):
|
||||||
""" Strip quotes
|
""" Strip quotes from string
|
||||||
@param string: value
|
args:
|
||||||
@return: string
|
value (str)
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
return v.strip('"\'')
|
return value.strip('"\'')
|
||||||
|
|
||||||
def analyze_number(var, err=''):
|
def analyze_number(var, err=''):
|
||||||
""" Analyse number for type and split from unit
|
""" Analyse number for type and split from unit
|
||||||
@param str: value
|
1px -> (q, 'px')
|
||||||
@raises: SyntaxError
|
args:
|
||||||
@return: tuple (number, unit)
|
var (str): number string
|
||||||
|
kwargs:
|
||||||
|
err (str): Error message
|
||||||
|
raises:
|
||||||
|
SyntaxError
|
||||||
|
returns:
|
||||||
|
tuple
|
||||||
"""
|
"""
|
||||||
n, u = split_unit(var)
|
n, u = split_unit(var)
|
||||||
if type(var) is not str:
|
if type(var) is not str:
|
||||||
@ -104,74 +135,94 @@ def analyze_number(var, err=''):
|
|||||||
raise SyntaxError('%s ´%s´' % (err, var))
|
raise SyntaxError('%s ´%s´' % (err, var))
|
||||||
return (n, u)
|
return (n, u)
|
||||||
|
|
||||||
def with_unit(n, u=None):
|
def with_unit(number, unit=None):
|
||||||
""" Return number with unit
|
""" Return number with unit
|
||||||
@param int/float: value
|
args:
|
||||||
@param str: unit
|
number (mixed): Number
|
||||||
@return: mixed
|
unit (str): Unit
|
||||||
|
returns:
|
||||||
|
str
|
||||||
"""
|
"""
|
||||||
if type(n) is tuple:
|
if type(number) is tuple:
|
||||||
n, u = n
|
number, unit = number
|
||||||
if n == 0: return 0
|
if number == 0:
|
||||||
if u:
|
return '0'
|
||||||
n = str(n)
|
if unit:
|
||||||
if n.startswith('.'):
|
number = str(number)
|
||||||
n = '0' + n
|
if number.startswith('.'):
|
||||||
return "%s%s" % (n, u)
|
number = '0' + number
|
||||||
return n
|
return "%s%s" % (number, unit)
|
||||||
|
return number if type(number) is str else str(number)
|
||||||
|
|
||||||
def is_color(v):
|
def is_color(value):
|
||||||
""" Is CSS color
|
""" Is string CSS color
|
||||||
@param mixed: value
|
args:
|
||||||
@return: bool
|
value (str): string
|
||||||
|
returns:
|
||||||
|
bool
|
||||||
"""
|
"""
|
||||||
if not v or type(v) is not str:
|
if not value or type(value) is not str:
|
||||||
return False
|
return False
|
||||||
if v[0] == '#' and len(v) in [4, 5, 7, 9]:
|
if value[0] == '#' and len(value) in [4, 5, 7, 9]:
|
||||||
try:
|
try:
|
||||||
int(v[1:], 16)
|
int(value[1:], 16)
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_variable(v):
|
def is_variable(value):
|
||||||
""" Check if string is LESS variable
|
""" Check if string is LESS variable
|
||||||
@param string: check
|
args:
|
||||||
@return: bool
|
value (str): string
|
||||||
|
returns:
|
||||||
|
bool
|
||||||
"""
|
"""
|
||||||
if type(v) is str:
|
if type(value) is str:
|
||||||
return (v.startswith('@') or v.startswith('-@'))
|
return (value.startswith('@') or value.startswith('-@'))
|
||||||
elif type(v) is tuple:
|
elif type(value) is tuple:
|
||||||
v = ''.join(v)
|
value = ''.join(value)
|
||||||
return (v.startswith('@') or v.startswith('-@'))
|
return (value.startswith('@') or value.startswith('-@'))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_int(v):
|
def is_int(value):
|
||||||
""" Is value integer
|
""" Is value integer
|
||||||
|
args:
|
||||||
|
value (str): string
|
||||||
|
returns:
|
||||||
|
bool
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
int(str(v))
|
int(str(value))
|
||||||
return True
|
return True
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_float(v):
|
def is_float(value):
|
||||||
""" Is value float
|
""" Is value float
|
||||||
|
args:
|
||||||
|
value (str): string
|
||||||
|
returns:
|
||||||
|
bool
|
||||||
"""
|
"""
|
||||||
if not is_int(v):
|
if not is_int(value):
|
||||||
try:
|
try:
|
||||||
float(str(v))
|
float(str(value))
|
||||||
return True
|
return True
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def split_unit(v):
|
def split_unit(value):
|
||||||
""" Split a number from its unit
|
""" Split a number from its unit
|
||||||
|
1px -> (q, 'px')
|
||||||
|
Args:
|
||||||
|
value (str): input
|
||||||
|
returns:
|
||||||
|
tuple
|
||||||
"""
|
"""
|
||||||
r = re.search('^(\-?[\d\.]+)(.*)$', str(v))
|
r = re.search('^(\-?[\d\.]+)(.*)$', str(value))
|
||||||
return r.groups() if r else ('','')
|
return r.groups() if r else ('','')
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,9 +86,9 @@ class TestUtility(unittest.TestCase):
|
|||||||
self.assertEqual('1.0px', test(1.0, 'px'))
|
self.assertEqual('1.0px', test(1.0, 'px'))
|
||||||
self.assertEqual('0.0px', test('.0', 'px'))
|
self.assertEqual('0.0px', test('.0', 'px'))
|
||||||
self.assertEqual('0.6px', test(.6, 'px'))
|
self.assertEqual('0.6px', test(.6, 'px'))
|
||||||
self.assertEqual(1, test(1))
|
self.assertEqual('1', test(1))
|
||||||
self.assertEqual(1, test(1, None))
|
self.assertEqual('1', test(1, None))
|
||||||
self.assertEqual(1, test(1,))
|
self.assertEqual('1', test(1,))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user