def _productions(self): """Return definition used for parsing.""" types = self._prods # rename! itemProd = Choice( _ColorProd(self), _DimensionProd(self), _URIProd(self), _ValueProd(self), _CalcValueProd(self), _CSSVariableProd(self), _CSSFunctionProd(self), ) funcProds = Sequence( Prod( name='FUNCTION', match=lambda t, v: t == types.FUNCTION, toSeq=lambda t, tokens: (t[0], normalize(t[1])), ), Choice( Sequence( itemProd, Sequence(PreDef.comma(optional=True), itemProd, minmax=lambda: (0, None)), PreDef.funcEnd(stop=True), ), PreDef.funcEnd(stop=True), ), ) return funcProds
def _setCssText(self, cssText): self._checkReadonly() types = self._prods # rename! _operator = Choice( Prod( name='Operator */', match=lambda t, v: v in '*/', toSeq=lambda t, tokens: (t[0], t[1]), ), Sequence( PreDef.S(), Choice( Sequence( Prod( name='Operator */', match=lambda t, v: v in '*/', toSeq=lambda t, tokens: (t[0], t[1]), ), PreDef.S(optional=True), ), Sequence( Prod( name='Operator +-', match=lambda t, v: v in '+-', toSeq=lambda t, tokens: (t[0], t[1]), ), PreDef.S(), ), PreDef.funcEnd(stop=True, mayEnd=True), ), ), ) _operant = lambda: Choice( # noqa:E731 _DimensionProd(self), _CalcValueProd(self), _CSSVariableProd(self)) prods = Sequence( Prod( name='CALC', match=lambda t, v: t == types.FUNCTION and normalize(v) == 'calc(', ), PreDef.S(optional=True), _operant(), Sequence(_operator, _operant(), minmax=lambda: (0, None)), PreDef.funcEnd(stop=True), ) # store: name of variable ok, seq, store, unused = ProdParser().parse(cssText, 'CSSCalc', prods, checkS=True) self.wellformed = ok if ok: self._setSeq(seq)
def _productiondefinition(self): """Return definition used for parsing.""" types = self._prods # rename! value = Sequence( PreDef.unary(), Prod( name='PrimitiveValue', match=lambda t, v: t in ( types.DIMENSION, types.HASH, types.IDENT, types.NUMBER, types.PERCENTAGE, types.STRING, ), toSeq=lambda t, tokens: (t[0], CSSPrimitiveValue(t[1])), ), ) valueOrFunc = Choice( value, # FUNC is actually not in spec but used in e.g. Prince PreDef.function(toSeq=lambda t, tokens: ( 'FUNCTION', CSSFunction(cssutils.helper.pushtoken(t, tokens)), )), ) funcProds = Sequence( Prod( name='FUNC', match=lambda t, v: t == types.FUNCTION, toSeq=lambda t, tokens: (t[0], cssutils.helper.normalize(t[1])), ), Choice( Sequence( valueOrFunc, # more values starting with Comma # should use store where colorType is saved to # define min and may, closure? Sequence(PreDef.comma(), valueOrFunc, minmax=lambda: (0, None)), PreDef.funcEnd(stop=True), ), PreDef.funcEnd(stop=True), ), ) return funcProds
def _setCssText(self, cssText): self._checkReadonly() prods = Sequence( # PreDef.unary(), Choice( PreDef.dimension(stop=True), PreDef.number(stop=True), PreDef.percentage(stop=True), )) ok, seq, store, unused = ProdParser().parse(cssText, 'DimensionValue', prods) self.wellformed = ok if ok: item = seq[0] sign, v, d = self.__reUnNumDim.findall(normalize(item.value))[0] if '.' in v: val = float(sign + v) else: val = int(sign + v) dim = None if d: dim = d self._sign = sign self._value = val self._dimension = dim self._type = item.type self._setSeq(seq)
def _productiondefinition(self): """Return defintion used for parsing.""" types = self._prods # rename! def toSeq(t, tokens): "Do not normalize function name!" return t[0], t[1] funcProds = Sequence( Prod(name='expression', match=lambda t, v: t == types.FUNCTION, toSeq=toSeq), Sequence( Choice( Prod( name='nested function', match=lambda t, v: t == self._prods.FUNCTION, toSeq=lambda t, tokens: ( ExpressionValue._functionName, ExpressionValue( cssutils.helper.pushtoken(t, tokens)), ), ), Prod( name='part', match=lambda t, v: v != ')', toSeq=lambda t, tokens: (t[0], t[1]), ), ), minmax=lambda: (0, None), ), PreDef.funcEnd(stop=True), ) return funcProds
def _setCssText(self, cssText): self._checkReadonly() types = self._prods # rename! valueProd = Prod( name='value', match=lambda t, v: t in (types.NUMBER, types.PERCENTAGE), toSeq=lambda t, v: (CSSPrimitiveValue, CSSPrimitiveValue(v)), toStore='parts', ) # COLOR PRODUCTION funccolor = Sequence( Prod( name='FUNC', match=lambda t, v: t == types.FUNCTION and cssutils.helper. normalize(v) in ('rgb(', 'rgba(', 'hsl(', 'hsla('), toSeq=lambda t, v: (t, v), # cssutils.helper.normalize(v)), toStore='colorType', ), PreDef.unary(), valueProd, # 2 or 3 more values starting with Comma Sequence(PreDef.comma(), PreDef.unary(), valueProd, minmax=lambda: (2, 3)), PreDef.funcEnd(), ) colorprods = Choice( funccolor, PreDef.hexcolor('colorType'), Prod( name='named color', match=lambda t, v: t == types.IDENT, toStore='colorType', ), ) # store: colorType, parts wellformed, seq, store, unusedtokens = ProdParser().parse( cssText, 'RGBColor', colorprods, keepS=True, store={'parts': []}) if wellformed: self.wellformed = True if store['colorType'].type == self._prods.HASH: self._colorType = 'HEX' elif store['colorType'].type == self._prods.IDENT: self._colorType = 'Named Color' else: self._colorType = store['colorType'].value[:-1] # self._colorType = \ # cssutils.helper.normalize(store['colorType'].value)[:-1] self._setSeq(seq)
def test_matches(self): "Choice.matches()" p1 = Prod('p1', lambda t, v: t == 1) p2 = Prod('p2', lambda t, v: t == 2, optional=True) t1 = (1, 0, 0, 0) t2 = (2, 0, 0, 0) t3 = (3, 0, 0, 0) c = Choice(p1, p2) self.assertEqual(True, c.matches(t1)) self.assertEqual(True, c.matches(t2)) self.assertEqual(False, c.matches(t3)) c = Choice(Sequence(p1), Sequence(p2)) self.assertEqual(True, c.matches(t1)) self.assertEqual(True, c.matches(t2)) self.assertEqual(False, c.matches(t3))
def _setCssText(self, cssText): self._checkReadonly() prods = Choice( PreDef.hexcolor(stop=True), PreDef.ident(stop=True), PreDef.string(stop=True), PreDef.unicode_range(stop=True), ) ok, seq, store, unused = ProdParser().parse(cssText, 'Value', prods) self.wellformed = ok if ok: # only 1 value anyway! self._type = seq[0].type self._value = seq[0].value self._setSeq(seq)
def test_reset(self): "Choice.reset()" p1 = Prod('p1', lambda t, v: t == 1) p2 = Prod('p2', lambda t, v: t == 2) t1 = (1, 0, 0, 0) t2 = (2, 0, 0, 0) ch = Choice(p1, p2) self.assertEqual(p1, ch.nextProd(t1)) self.assertRaises(Exhausted, ch.nextProd, t1) ch.reset() self.assertEqual(p2, ch.nextProd(t2))
def _productions(self): """Return definition used for parsing.""" types = self._prods # rename! func = Prod( name='MSValue-Sub', match=lambda t, v: t == self._prods.FUNCTION, toSeq=lambda t, tokens: ( MSValue._functionName, MSValue(pushtoken(t, tokens), parent=self), ), ) funcProds = Sequence( Prod( name='FUNCTION', match=lambda t, v: t == types.FUNCTION, toSeq=lambda t, tokens: (t[0], t[1]), ), Sequence( Choice( _ColorProd(self), _DimensionProd(self), _URIProd(self), _ValueProd(self), _MSValueProd(self), # _CalcValueProd(self), _CSSVariableProd(self), func, # _CSSFunctionProd(self), Prod( name='MSValuePart', match=lambda t, v: v != ')', toSeq=lambda t, tokens: (t[0], t[1]), ), ), minmax=lambda: (0, None), ), PreDef.funcEnd(stop=True), ) return funcProds
def _setCssText(self, cssText): self._checkReadonly() types = self._prods # rename! prods = Sequence( Prod( name='var', match=lambda t, v: t == types.FUNCTION and normalize(v) == 'var(', ), PreDef.ident(toStore='ident'), Sequence( PreDef.comma(), Choice( _ColorProd(self, toStore='fallback'), _DimensionProd(self, toStore='fallback'), _URIProd(self, toStore='fallback'), _ValueProd(self, toStore='fallback'), _CalcValueProd(self, toStore='fallback'), _CSSVariableProd(self, toStore='fallback'), _CSSFunctionProd(self, toStore='fallback'), ), minmax=lambda: (0, 1), ), PreDef.funcEnd(stop=True), ) # store: name of variable store = {'ident': None, 'fallback': None} ok, seq, store, unused = ProdParser().parse(cssText, 'CSSVariable', prods) self.wellformed = ok if ok: self._name = store['ident'].value try: self._fallback = store['fallback'].value except KeyError: self._fallback = None self._setSeq(seq)
def test_nested(self): "Choice with nested Sequence" p1 = Prod('p1', lambda t, v: t == 1) p2 = Prod('p2', lambda t, v: t == 2) s1 = Sequence(p1, p1) s2 = Sequence(p2, p2) t0 = (0, 0, 0, 0) t1 = (1, 0, 0, 0) t2 = (2, 0, 0, 0) ch = Choice(s1, s2) self.assertRaisesMsg( ParseError, 'No match for (0, 0, 0, 0) in Choice(Sequence(p1, p1), Sequence(p2, p2))', ch.nextProd, t0, ) self.assertEqual(s1, ch.nextProd(t1)) self.assertRaisesMsg(Exhausted, 'Extra token', ch.nextProd, t1) ch = Choice(s1, s2) self.assertEqual(s2, ch.nextProd(t2)) self.assertRaisesMsg(Exhausted, 'Extra token', ch.nextProd, t1)
def test_combi(self): "ProdParser.parse() 2" p1 = Prod('p1', lambda t, v: v == '1') p2 = Prod('p2', lambda t, v: v == '2') p3 = Prod('p3', lambda t, v: v == '3') tests = { '1 2': True, '1 2 1 2': True, '3': True, # '': 'No match in Choice(Sequence(p1, p2), p3)', '1': 'Missing token for production p2', '1 2 1': 'Missing token for production p2', '1 2 1 2 x': "No match: ('IDENT', 'x', 1, 9)", '1 2 1 2 1': "No match: ('NUMBER', '1', 1, 9)", '3 x': "No match: ('IDENT', 'x', 1, 3)", '3 3': "No match: ('NUMBER', '3', 1, 3)", } for text, exp in list(tests.items()): prods = Choice(Sequence(p1, p2, minmax=lambda: (1, 2)), p3) if exp is True: wellformed, seq, store, unused = ProdParser().parse( text, 'T', prods) self.assertEqual(wellformed, exp) else: self.assertRaisesMsg( xml.dom.SyntaxErr, 'T: %s' % exp, ProdParser().parse, text, 'T', prods, ) tests = { '1 3': True, '1 1 3': True, '2 3': True, '1': 'Missing token for production p3', '1 1': 'Missing token for production p3', '1 3 3': "No match: ('NUMBER', '3', 1, 5)", '1 1 3 3': "No match: ('NUMBER', '3', 1, 7)", '2 3 3': "No match: ('NUMBER', '3', 1, 5)", '2': 'Missing token for production p3', '3': "Missing token for production Choice(Sequence(p1), p2): " "('NUMBER', '3', 1, 1)", } for text, exp in list(tests.items()): prods = Sequence(Choice(Sequence(p1, minmax=lambda: (1, 2)), p2), p3) if exp is True: wellformed, seq, store, unused = ProdParser().parse( text, 'T', prods) self.assertEqual(wellformed, exp) else: self.assertRaisesMsg( xml.dom.SyntaxErr, 'T: %s' % exp, ProdParser().parse, text, 'T', prods, )
def _setCssText(self, cssText): # noqa: C901 """ Format:: unary_operator : '-' | '+' ; operator : '/' S* | ',' S* | /* empty */ ; expr : term [ operator term ]* ; term : unary_operator? [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* ] | STRING S* | IDENT S* | URI S* | hexcolor | function | UNICODE-RANGE S* ; function : FUNCTION S* expr ')' S* ; /* * There is a constraint on the color that it must * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F]) * after the "#"; e.g., "#000" is OK, but "#abcd" is not. */ hexcolor : HASH S* ; :exceptions: - :exc:`~xml.dom.SyntaxErr`: Raised if the specified CSS string value has a syntax error (according to the attached property) or is unparsable. - :exc:`~xml.dom.InvalidModificationErr`: TODO: Raised if the specified CSS string value represents a different type of values than the values allowed by the CSS property. - :exc:`~xml.dom.NoModificationAllowedErr`: Raised if this value is readonly. """ self._checkReadonly() # used as operator is , / or S nextSor = ',/' term = Choice( Sequence( PreDef.unary(), Choice( PreDef.number(nextSor=nextSor), PreDef.percentage(nextSor=nextSor), PreDef.dimension(nextSor=nextSor), ), ), PreDef.string(nextSor=nextSor), PreDef.ident(nextSor=nextSor), PreDef.uri(nextSor=nextSor), PreDef.hexcolor(nextSor=nextSor), PreDef.unicode_range(nextSor=nextSor), # special case IE only expression Prod( name='expression', match=lambda t, v: t == self._prods.FUNCTION and (cssutils.helper.normalize(v) in ( 'expression(', 'alpha(', 'blur(', 'chroma(', 'dropshadow(', 'fliph(', 'flipv(', 'glow(', 'gray(', 'invert(', 'mask(', 'shadow(', 'wave(', 'xray(', ) or v.startswith('progid:DXImageTransform.Microsoft.')), nextSor=nextSor, toSeq=lambda t, tokens: ( ExpressionValue._functionName, ExpressionValue(cssutils.helper.pushtoken(t, tokens), parent=self), ), ), # CSS Variable var( PreDef.variable( nextSor=nextSor, toSeq=lambda t, tokens: ( 'CSSVariable', CSSVariable(cssutils.helper.pushtoken(t, tokens), parent=self), ), ), # calc( PreDef.calc( nextSor=nextSor, toSeq=lambda t, tokens: ( CalcValue._functionName, CalcValue(cssutils.helper.pushtoken(t, tokens), parent=self), ), ), # TODO: # # rgb/rgba( # Prod(name='RGBColor', # match=lambda t, v: t == self._prods.FUNCTION and ( # cssutils.helper.normalize(v) in (u'rgb(', # u'rgba(' # ) # ), # nextSor=nextSor, # toSeq=lambda t, tokens: (RGBColor._functionName, # RGBColor( # cssutils.helper.pushtoken(t, tokens), # parent=self) # ) # ), # other functions like rgb( etc PreDef.function( nextSor=nextSor, toSeq=lambda t, tokens: ( 'FUNCTION', CSSFunction(cssutils.helper.pushtoken(t, tokens), parent=self), ), ), ) operator = Choice( PreDef.S(), PreDef.char('comma', ',', toSeq=lambda t, tokens: ('operator', t[1])), PreDef.char('slash', '/', toSeq=lambda t, tokens: ('operator', t[1])), optional=True, ) # CSSValue PRODUCTIONS valueprods = Sequence( term, Sequence( operator, # mayEnd this Sequence if whitespace # TODO: only when setting via other class # used by variabledeclaration currently PreDef.char('END', ';', stopAndKeep=True, optional=True), term, minmax=lambda: (0, None), ), ) # parse wellformed, seq, store, notused = ProdParser().parse(cssText, 'CSSValue', valueprods, keepS=True) if wellformed: # - count actual values and set firstvalue which is used later on # - combine comma separated list, e.g. font-family to a single item # - remove S which should be an operator but is no needed count, firstvalue = 0, () newseq = self._tempSeq() i, end = 0, len(seq) while i < end: item = seq[i] if item.type == self._prods.S: pass elif (item.value, item.type) == (',', 'operator'): # , separared counts as a single STRING for now # URI or STRING value might be a single CHAR too! newseq.appendItem(item) count -= 1 if firstvalue: # list of IDENTs is handled as STRING! if firstvalue[1] == self._prods.IDENT: firstvalue = firstvalue[0], 'STRING' elif item.value == '/': # / separated items count as one newseq.appendItem(item) elif item.value == '-' or item.value == '+': # combine +- and following number or other i += 1 try: next = seq[i] except IndexError: firstvalue = () # raised later break newval = item.value + next.value newseq.append(newval, next.type, item.line, item.col) if not firstvalue: firstvalue = (newval, next.type) count += 1 elif item.type != cssutils.css.CSSComment: newseq.appendItem(item) if not firstvalue: firstvalue = (item.value, item.type) count += 1 else: newseq.appendItem(item) i += 1 if not firstvalue: self._log.error('CSSValue: Unknown syntax or no value: %r.' % self._valuestr(cssText)) else: # ok and set self._setSeq(newseq) self.wellformed = wellformed if hasattr(self, '_value'): # only in case of CSSPrimitiveValue, else remove! del self._value if count == 1: # inherit, primitive or variable if isinstance( firstvalue[0], str) and 'inherit' == cssutils.helper.normalize( firstvalue[0]): self.__class__ = CSSValue self._cssValueType = CSSValue.CSS_INHERIT elif 'CSSVariable' == firstvalue[1]: self.__class__ = CSSVariable self._value = firstvalue # TODO: remove major hack! self._name = firstvalue[0]._name else: self.__class__ = CSSPrimitiveValue self._value = firstvalue elif count > 1: # valuelist self.__class__ = CSSValueList # change items in list to specific type (primitive etc) newseq = self._tempSeq() commalist = [] nexttocommalist = False def itemValue(item): "Reserialized simple item.value" if self._prods.STRING == item.type: return cssutils.helper.string(item.value) elif self._prods.URI == item.type: return cssutils.helper.uri(item.value) elif (self._prods.FUNCTION == item.type or 'CSSVariable' == item.type): return item.value.cssText else: return item.value def saveifcommalist(commalist, newseq): """ saves items in commalist to seq and items if anything in there """ if commalist: newseq.replace( -1, CSSPrimitiveValue(cssText=''.join(commalist)), CSSPrimitiveValue, newseq[-1].line, newseq[-1].col, ) del commalist[:] for i, item in enumerate(self._seq): if issubclass(type(item.value), CSSValue): # set parent of CSSValueList items to the lists # parent item.value.parent = self.parent if item.type in ( self._prods.DIMENSION, self._prods.FUNCTION, self._prods.HASH, self._prods.IDENT, self._prods.NUMBER, self._prods.PERCENTAGE, self._prods.STRING, self._prods.URI, self._prods.UNICODE_RANGE, 'CSSVariable', ): if nexttocommalist: # wait until complete commalist.append(itemValue(item)) else: saveifcommalist(commalist, newseq) # append new item if hasattr(item.value, 'cssText'): newseq.append( item.value, item.value.__class__, item.line, item.col, ) else: newseq.append( CSSPrimitiveValue(itemValue(item)), CSSPrimitiveValue, item.line, item.col, ) nexttocommalist = False elif ',' == item.value: if not commalist: # save last item to commalist commalist.append(itemValue(self._seq[i - 1])) commalist.append(',') nexttocommalist = True else: if nexttocommalist: commalist.append(item.value.cssText) else: newseq.appendItem(item) saveifcommalist(commalist, newseq) self._setSeq(newseq) else: # should not happen... self.__class__ = CSSValue self._cssValueType = CSSValue.CSS_CUSTOM
def _setMediaText(self, mediaText): """ :param mediaText: a single media query string, e.g. ``print and (min-width: 25cm)`` :exceptions: - :exc:`~xml.dom.SyntaxErr`: Raised if the specified string value has a syntax error and is unparsable. - :exc:`~xml.dom.InvalidCharacterErr`: Raised if the given mediaType is unknown. - :exc:`~xml.dom.NoModificationAllowedErr`: Raised if this media query is readonly. media_query : [ONLY | NOT]? S* media_type S* [ AND S* expression ]* | expression [ AND S* expression ]* ; media_type : IDENT ; expression : '(' S* media_feature S* [ ':' S* expr ]? ')' S* ; media_feature : IDENT ; """ self._checkReadonly() expression = lambda: Sequence( # noqa PreDef.char(name='expression', char='('), Prod(name='media_feature', match=lambda t, v: t == PreDef.types.IDENT), Sequence( PreDef.char(name='colon', char=':'), cssutils.css.value.MediaQueryValueProd(self), minmax=lambda: (0, 1), # optional ), PreDef.char( name='expression END', char=')', stopIfNoMoreMatch=self._partof ), ) prods = Choice( Sequence( Prod( name='ONLY|NOT', # media_query match=lambda t, v: t == PreDef.types.IDENT and normalize(v) in ('only', 'not'), optional=True, toStore='not simple', ), Prod( name='media_type', match=lambda t, v: t == PreDef.types.IDENT and normalize(v) in self.MEDIA_TYPES, stopIfNoMoreMatch=True, toStore='media_type', ), Sequence( Prod( name='AND', match=lambda t, v: t == PreDef.types.IDENT and normalize(v) == 'and', toStore='not simple', ), expression(), minmax=lambda: (0, None), ), ), Sequence( expression(), Sequence( Prod( name='AND', match=lambda t, v: t == PreDef.types.IDENT and normalize(v) == 'and', ), expression(), minmax=lambda: (0, None), ), ), ) # parse ok, seq, store, unused = ProdParser().parse(mediaText, 'MediaQuery', prods) self._wellformed = ok if ok: try: media_type = store['media_type'] except KeyError: pass else: if 'not simple' not in store: self.mediaType = media_type.value # TODO: filter doubles! self._setSeq(seq)
def _setCssText(self, cssText): # noqa: C901 self._checkReadonly() types = self._prods # rename! component = Choice( PreDef.unary(toSeq=lambda t, tokens: ( t[0], DimensionValue(pushtoken(t, tokens), parent=self), )), PreDef.number(toSeq=lambda t, tokens: ( t[0], DimensionValue(pushtoken(t, tokens), parent=self), )), PreDef.percentage(toSeq=lambda t, tokens: ( t[0], DimensionValue(pushtoken(t, tokens), parent=self), )), ) noalp = Sequence( Prod( name='FUNCTION', match=lambda t, v: t == types.FUNCTION and v in ('rgb(', 'hsl('), toSeq=lambda t, tokens: (t[0], normalize(t[1])), ), component, Sequence(PreDef.comma(optional=True), component, minmax=lambda: (2, 2)), PreDef.funcEnd(stop=True), ) witha = Sequence( Prod( name='FUNCTION', match=lambda t, v: t == types.FUNCTION and v in ('rgba(', 'hsla('), toSeq=lambda t, tokens: (t[0], normalize(t[1])), ), component, Sequence(PreDef.comma(optional=True), component, minmax=lambda: (3, 3)), PreDef.funcEnd(stop=True), ) namedcolor = Prod( name='Named Color', match=lambda t, v: t == 'IDENT' and (normalize(v) in list(self.COLORS.keys())), stop=True, ) prods = Choice(PreDef.hexcolor(stop=True), namedcolor, noalp, witha) ok, seq, store, unused = ProdParser().parse(cssText, self.type, prods) self.wellformed = ok if ok: t, v = seq[0].type, seq[0].value if 'IDENT' == t: rgba = self.COLORS[normalize(v)] if 'HASH' == t: if len(v) == 4: # HASH #rgb rgba = ( int(2 * v[1], 16), int(2 * v[2], 16), int(2 * v[3], 16), 1.0, ) else: # HASH #rrggbb rgba = (int(v[1:3], 16), int(v[3:5], 16), int(v[5:7], 16), 1.0) elif 'FUNCTION' == t: functiontype, raw, check = None, [], '' HSL = False for item in seq: try: type_ = item.value.type except AttributeError: # type of function, e.g. rgb( if item.type == 'FUNCTION': functiontype = item.value HSL = functiontype in ('hsl(', 'hsla(') continue # save components if type_ == Value.NUMBER: raw.append(item.value.value) check += 'N' elif type_ == Value.PERCENTAGE: if HSL: # save as percentage fraction raw.append(item.value.value / 100.0) else: # save as real value of percentage of 255 raw.append(int(255 * item.value.value / 100)) check += 'P' if HSL: # convert to rgb # h is 360 based (circle) h, s, l_ = raw[0] / 360.0, raw[1], raw[2] # ORDER h l s !!! r, g, b = colorsys.hls_to_rgb(h, l_, s) # back to 255 based rgba = [ int(round(r * 255)), int(round(g * 255)), int(round(b * 255)), ] if len(raw) > 3: rgba.append(raw[3]) else: # rgb, rgba rgba = raw if len(rgba) < 4: rgba.append(1.0) # validate checks = { 'rgb(': ('NNN', 'PPP'), 'rgba(': ('NNNN', 'PPPN'), 'hsl(': ('NPP', ), 'hsla(': ('NPPN', ), } if check not in checks[functiontype]: self._log.error('ColorValue has invalid %s) parameters: ' '%s (N=Number, P=Percentage)' % (functiontype, check)) self._colorType = t self._red, self._green, self._blue, self._alpha = tuple(rgba) self._setSeq(seq)
def test_init(self): "Choice.__init__()" p1 = Prod('p1', lambda t, v: t == 1) p2 = Prod('p2', lambda t, v: t == 2) t0 = (0, 0, 0, 0) t1 = (1, 0, 0, 0) t2 = (2, 0, 0, 0) ch = Choice(p1, p2) self.assertRaisesMsg(ParseError, 'No match for (0, 0, 0, 0) in Choice(p1, p2)', ch.nextProd, t0) self.assertEqual(p1, ch.nextProd(t1)) self.assertRaisesMsg(Exhausted, 'Extra token', ch.nextProd, t1) ch = Choice(p1, p2) self.assertEqual(p2, ch.nextProd(t2)) self.assertRaisesMsg(Exhausted, 'Extra token', ch.nextProd, t2) ch = Choice(p2, p1) self.assertRaisesMsg(ParseError, 'No match for (0, 0, 0, 0) in Choice(p2, p1)', ch.nextProd, t0) self.assertEqual(p1, ch.nextProd(t1)) self.assertRaisesMsg(Exhausted, 'Extra token', ch.nextProd, t1) ch = Choice(p2, p1) self.assertEqual(p2, ch.nextProd(t2)) self.assertRaisesMsg(Exhausted, 'Extra token', ch.nextProd, t2)
def MediaQueryValueProd(parent): return Choice( _ColorProd(parent), _DimensionProd(parent), _ValueProd(parent), )
def _setCssText(self, cssText): if isinstance(cssText, (int, float)): cssText = str(cssText) # if it is a number """ Format:: unary_operator : '-' | '+' ; operator : '/' S* | ',' S* | /* empty */ ; expr : term [ operator term ]* ; term : unary_operator? [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* ] | STRING S* | IDENT S* | URI S* | hexcolor | function | UNICODE-RANGE S* ; function : FUNCTION S* expr ')' S* ; /* * There is a constraint on the color that it must * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F]) * after the "#"; e.g., "#000" is OK, but "#abcd" is not. */ hexcolor : HASH S* ; :exceptions: - :exc:`~xml.dom.SyntaxErr`: Raised if the specified CSS string value has a syntax error (according to the attached property) or is unparsable. - :exc:`~xml.dom.InvalidModificationErr`: TODO: Raised if the specified CSS string value represents a different type of values than the values allowed by the CSS property. - :exc:`~xml.dom.NoModificationAllowedErr`: Raised if this value is readonly. """ self._checkReadonly() # used as operator is , / or S nextSor = ',/' term = Choice( _ColorProd(self, nextSor), _DimensionProd(self, nextSor), _URIProd(self, nextSor), _ValueProd(self, nextSor), # _Rect(self, nextSor), # all other functions _CSSVariableProd(self, nextSor), _MSValueProd(self, nextSor), _CalcValueProd(self, nextSor), _CSSFunctionProd(self, nextSor), ) operator = Choice( PreDef.S(toSeq=False), PreDef.char('comma', ',', toSeq=lambda t, tokens: ('operator', t[1]), optional=True), PreDef.char('slash', '/', toSeq=lambda t, tokens: ('operator', t[1]), optional=True), optional=True, ) prods = Sequence( term, Sequence( # mayEnd this Sequence if whitespace operator, # TODO: only when setting via other class # used by variabledeclaration currently PreDef.char('END', ';', stopAndKeep=True, optional=True), # TODO: } and !important ends too! term, minmax=lambda: (0, None), ), ) # parse ok, seq, store, unused = ProdParser().parse(cssText, 'PropertyValue', prods) # must be at least one value! ok = ok and len(list(self.__items(seq))) > 0 for item in seq: if hasattr(item.value, 'wellformed') and not item.value.wellformed: ok = False break self.wellformed = ok if ok: self._setSeq(seq) else: self._log.error('PropertyValue: Unknown syntax or no value: %s' % self._valuestr(cssText))
def _setCssText(self, cssText): """ :exceptions: - :exc:`~xml.dom.SyntaxErr`: Raised if the specified CSS string value has a syntax error and is unparsable. - :exc:`~xml.dom.InvalidModificationErr`: Raised if the specified CSS string value represents a different type of rule than the current one. - :exc:`~xml.dom.HierarchyRequestErr`: Raised if the rule cannot be inserted at this point in the style sheet. - :exc:`~xml.dom.NoModificationAllowedErr`: Raised if the rule is readonly. """ super(MarginRule, self)._setCssText(cssText) # TEMP: all style tokens are saved in store to fill styledeclaration # TODO: resolve when all generators styletokens = Prod( name='styletokens', match=lambda t, v: v != '}', # toSeq=False, toStore='styletokens', storeToken=True, ) prods = Sequence( Prod(name='@ margin', match=lambda t, v: t == 'ATKEYWORD' and self._normalize(v) in MarginRule.margins, toStore='margin' # TODO? # , exception=xml.dom.InvalidModificationErr ), PreDef.char('OPEN', '{'), Sequence( Choice(PreDef.unknownrule(toStore='@'), styletokens), minmax=lambda: (0, None), ), PreDef.char('CLOSE', '}', stopAndKeep=True), ) # parse ok, seq, store, unused = ProdParser().parse(cssText, 'MarginRule', prods) if ok: # TODO: use seq for serializing instead of fixed stuff? self._setSeq(seq) if 'margin' in store: # may raise: self.margin = store['margin'].value else: self._log.error( 'No margin @keyword for this %s rule' % self.margin, error=xml.dom.InvalidModificationErr, ) # new empty style self.style = CSSStyleDeclaration(parentRule=self) if 'styletokens' in store: # may raise: self.style.cssText = store['styletokens']