Package cssutils :: Module util |
[hide private]
[frames] | no frames]

# Source Code for Module cssutils.util

      1  """base classes for css and stylesheets packages
      2  """
      3  __all__ = []
      4  __docformat__ = 'restructuredtext'
      5  __author__ = '$LastChangedBy: cthedot $'
      6  __date__ = '$LastChangedDate: 2007-11-06 22:23:55 +0100 (Di, 06 Nov 2007) $'
      7  __version__ = '$LastChangedRevision: 653 $'
      8
      9  import re
     10  import types
     11  import xml.dom
     12  import cssutils
     13  from tokenize2 import Tokenizer
     14
     15 -class Seq(object):
     16      """
     17      (EXPERIMENTAL)
     18      a list like sequence of (value, type) used in almost all cssutils classes
     19
     20      behaves almost like a list but keeps extra attribute "type" for
     21      each value in the list
     22
     23      types are tokens types like e.g. "COMMENT" (value='/*...*/', all uppercase)
     24      or productions like e.g. "universal" (value='*', all lowercase)
     25      """
     26 -    def __init__(self):
     27          self.values = []
     28          self.types = []
     29
     30 -    def __contains__(self, item):
     31          return item in self.values
     32
     33 -    def __getitem__(self, index):
     34          return self.values[index]
     35
     36 -    def __setitem__(self, index, value_type):
     37          "might be set with tuple (value, type) or a single value"
     38          if type(value_type) == tuple:
     39              val = value_type[0]
     40              typ = value_type[1]
     41          else:
     42              val = value_type
     43              typ = None
     44          self.values[index] = val
     45          self.types[index] = typ
     46
     47 -    def __iter__(self):
     48          "returns an iterator of values only "
     49          return iter(self.values)
     50
     51 -    def __len__(self):
     52          "same as len(list)"
     53          return len(self.values)
     54
     55 -    def __repr__(self):
     56          "returns a repr same as a list of tuples of (value, type)"
     57          return u'[%s]' % u',\n '.join([u'(%r, %r)' % (value, self.types[i])
     58                                      for i, value in enumerate(self.values)])
     59 -    def __str__(self):
     60          "returns a concanated string of all values"
     61          items = []
     62          for i, value in enumerate(self.values):
     63              if self.types[i]:
     64                  if self.types[i] != 'COMMENT':
     65                      items.append(value)
     66              items.append(value)
     67          return u''.join(items)
     68
     69 -    def append(self, value, type=None):
     70          """
     71          same as list.append but not a simple value but a SeqItem is appended
     72          """
     73          self.values.append(value) # str(value)??? does not work if value is e.g. comment
     74          self.types.append(type)
     75
     76
     77 -class Base(object):
     78      """
     79      Base class for most CSS and StyleSheets classes
     80
     81      Contains helper methods for inheriting classes helping parsing
     82
     83      ``_normalize`` is static as used be Preferences.
     84      """
     85      __tokenizer2 = Tokenizer()
     86      _log = cssutils.log
     87      _prods = cssutils.tokenize2.CSSProductions
     88
     89      # for more on shorthand properties see
     90      # http://www.dustindiaz.com/css-shorthand/
     91      # format: shorthand: [(propname, mandatorycheck?)*]
     92      _SHORTHANDPROPERTIES = {
     93              u'background': [],
     94              u'border': [],
     95              u'border-left': [],
     96              u'border-right': [],
     97              u'border-top': [],
     98              u'border-bottom': [],
     99              u'border-color': [],
    100              u'border-style': [],
    101              u'border-width': [],
    102              u'cue': [],
    103              u'font': [('font-weight', True),
    104                        ('font-size', True),
    105                        ('line-height', False),
    106                        ('font-family', True)],
    107              u'list-style': [],
    108              u'margin': [],
    109              u'outline': [],
    110              u'padding': [],
    111              u'pause': []
    112              }
    113
    114      # simple escapes, all non unicodes
    115      __escapes = re.compile(ur'(\\[^0-9a-fA-F])').sub
    116      # all unicode (see cssproductions "unicode")
    117      __unicodes = re.compile(ur'\\[0-9a-fA-F]{1,6}[\t|\r|\n|\f|\x20]?').sub
    118
    119      @staticmethod
    120 -    def _normalize(x):
    121          """
    122          normalizes x, namely:
    123
    124          - remove any \ before non unicode sequences (0-9a-zA-Z) so for
    125            x=="c\olor\" return "color" (unicode escape sequences should have
    126            been resolved by the tokenizer already)
    127          - lowercase
    128          """
    129          if x:
    130              def removeescape(matchobj):
    131                  return matchobj.group(0)[1:]
    132              x = Base.__escapes(removeescape, x)
    133              return x.lower()
    134          else:
    135              return x
    136
    137 -    def _checkReadonly(self):
    138          "raises xml.dom.NoModificationAllowedErr if rule/... is readonly"
    139          if hasattr(self, '_readonly') and self._readonly:
    140              raise xml.dom.NoModificationAllowedErr(
    141                  u'%s is readonly.' % self.__class__)
    142              return True
    143          return False
    144
    145 -    def _newseq(self):
    146          # used by Selector but should be used by most classes?
    147          return Seq()
    148
    149 -    def _valuestr(self, t):
    150          """
    151          returns string value of t (t may be a string, a list of token tuples
    152          or a single tuple in format (type, value, line, col) or a
    153          tokenlist[old])
    154          """
    155          if not t:
    156              return u''
    157          elif isinstance(t, basestring):
    158              return t
    159          elif isinstance(t, list) and isinstance(t[0], tuple):
    160              return u''.join([x[1] for x in t])
    161          elif isinstance(t, tuple): # needed?
    162              return self._tokenvalue(t)
    163          else: # old
    164              return u''.join([x.value for x in t])
    165
    166 -    def _tokenize2(self, textortokens, aslist=False, fullsheet=False):
    167          """
    168          returns tokens of textortokens which may already be tokens in which
    169          case simply returns input
    170          """
    171          if not textortokens:
    172              return None
    173          if types.GeneratorType == type(textortokens) and not aslist:
    174              # already tokenized
    175              return textortokens
    176          if isinstance(textortokens, basestring):
    177              if aslist:
    178                  return [t for t in self.__tokenizer2.tokenize(
    179                       textortokens, fullsheet=fullsheet)]
    180              else:
    181                  return self.__tokenizer2.tokenize(
    182                       textortokens, fullsheet=fullsheet)
    183          elif isinstance(textortokens, tuple):
    184              # a single token (like a comment)
    185              return [textortokens]
    186          else:
    187              # already tokenized but return generator
    188              return (x for x in textortokens)
    189
    190 -    def _nexttoken(self, tokenizer, default=None):
    191          "returns next token in generator tokenizer or the default value"
    192          try:
    193              return tokenizer.next()
    194          except (StopIteration, AttributeError):
    195              return default
    196
    197 -    def _type(self, token):
    198          "type of Tokenizer token"
    199          if not token:
    200              return None
    201          else:
    202              return token[0]
    203
    204 -    def _tokenvalue(self, token, normalize=False):
    205          "value of Tokenizer token"
    206          if not token:
    207              return None
    208          elif normalize:
    209              return Base._normalize(token[1])
    210          else:
    211              return token[1]
    212
    213 -    def _tokensupto2(self,
    214                       tokenizer,
    215                       starttoken=None,
    216                       blockstartonly=False,
    217                       blockendonly=False,
    218                       mediaendonly=False,
    219                       semicolon=False,
    220                       propertynameendonly=False,
    221                       propertyvalueendonly=False,
    222                       propertypriorityendonly=False,
    223                       selectorattendonly=False,
    224                       funcendonly=False,
    225                       listseponly=False, # ,
    226                       keepEnd=True,
    227                       keepEOF=True):
    228          """
    229          returns tokens upto end of atrule and end index
    230          end is defined by parameters, might be ; } ) or other
    231
    232          default looks for ending "}" and ";"
    233          """
    234          ends = u';}'
    235          brace = bracket = parant = 0 # {}, [], ()
    236
    237          if blockstartonly: # {
    238              ends = u'{'
    239              brace = -1 # set to 0 with first {
    240          elif blockendonly: # }
    241              ends = u'}'
    242          elif mediaendonly: # }
    243              ends = u'}'
    244              brace = 1 # rules } and mediarules }
    245          elif semicolon:
    246              ends = u';'
    247          elif propertynameendonly: # : and ; in case of an error
    248              ends = u':;'
    249          elif propertyvalueendonly: # ; or !important
    250              ends = (u';', u'!')
    251          elif propertypriorityendonly: # ;
    252              ends = u';'
    253          elif selectorattendonly: # ]
    254              ends = u']'
    255              if starttoken and self._tokenvalue(starttoken) == u'[':
    256                  bracket = 1
    257          elif funcendonly: # )
    258              ends = u')'
    259              parant = 1
    260          elif listseponly: # ,
    261              ends = u','
    262
    263          resulttokens = []
    264
    265          # NEEDED?
    266          if starttoken:
    267              resulttokens.append(starttoken)
    268
    269          if not tokenizer:
    270              return resulttokens
    271          else:
    272              for token in tokenizer:
    273                  if self._type(token) == 'EOF':
    274                      if keepEOF and keepEnd:
    275                          resulttokens.append(token)
    276                      break
    277                  val = self._tokenvalue(token)
    278                  if u'{' == val: brace += 1
    279                  elif u'}' == val: brace -= 1
    280                  elif u'[' == val: bracket += 1
    281                  elif u']' == val: bracket -= 1
    282                  # function( or single (
    283                  elif u'(' == val or \
    284                     Base._prods.FUNCTION == self._type(token): parant += 1
    285                  elif u')' == val: parant -= 1
    286                  if val in ends and (brace == bracket == parant == 0):
    287                      if keepEnd:
    288                          resulttokens.append(token)
    289                      break
    290                  else:
    291                      resulttokens.append(token)
    292
    293              return resulttokens
    294
    295 -    def _getProductions(self, productions):
    296          """
    297          each production should return the next expected token
    298          normaly a name like "uri" or "EOF"
    299          some have no expectation like S or COMMENT, so simply return
    300          the current value of self.__expected
    301          """
    302          def ATKEYWORD(expected, seq, token, tokenizer=None):
    303              "TODO: add default impl for unexpected @rule"
    304              return expected
    305
    306          def COMMENT(expected, seq, token, tokenizer=None):
    307              "default implementation for COMMENT token"
    308              seq.append(cssutils.css.CSSComment([token]))
    309              return expected
    310
    311          def S(expected, seq, token, tokenizer=None):
    312              "default implementation for S token"
    313              return expected
    314
    315          def EOF(expected=None, seq=None, token=None, tokenizer=None):
    316              "default implementation for EOF token"
    317              return 'EOF'
    318
    319          p = {'COMMENT': COMMENT,
    320               'S': S,
    321               'ATKEYWORD': ATKEYWORD,
    322               'EOF': EOF # only available if fullsheet
    323               }
    324          p.update(productions)
    325          return p
    326
    327 -    def _parse(self, expected, seq, tokenizer, productions, default=None):
    328          """
    329          puts parsed tokens in seq by calling a production with
    330              (seq, tokenizer, token)
    331
    332          expected
    333              a name what token or value is expected next, e.g. 'uri'
    334          seq
    335              to add rules etc to
    336          tokenizer
    337              call tokenizer.next() to get next token
    338          productions
    339              callbacks {tokentype: callback}
    340          default
    341              default callback if tokentype not in productions
    342
    343          returns (wellformed, expected) which the last prod might have set
    344          """
    345          wellformed = True
    346
    347          if not tokenizer:
    348              return wellformed, expected
    349
    350          prods = self._getProductions(productions)
    351          for token in tokenizer:
    352              typ, val, lin, col = token
    353              p = prods.get(typ, default)
    354              if p:
    355                  expected = p(expected, seq, token, tokenizer)
    356              else:
    357                  wellformed = False
    358                  self._log.error(u'Unexpected token (%s, %s, %s, %s)' % token)
    359
    360          return wellformed, expected
    361
    362
    363 -class Deprecated(object):
    364      """This is a decorator which can be used to mark functions
    365      as deprecated. It will result in a warning being emitted
    366      when the function is used.
    367
    368      It accepts a single paramter ``msg`` which is shown with the warning.
    369      It should contain information which function or method to use instead.
    370      """
    371 -    def __init__(self, msg):
    372          self.msg = msg
    373
    374 -    def __call__(self, func):
    375          def newFunc(*args, **kwargs):
    376              import warnings
    377              warnings.warn("Call to deprecated method %r. %s" %
    378                              (func.__name__, self.msg),
    379                              category=DeprecationWarning,
    380                              stacklevel=2)
    381              return func(*args, **kwargs)
    382          newFunc.__name__ = func.__name__
    383          newFunc.__doc__ = func.__doc__
    384          newFunc.__dict__.update(func.__dict__)
    385          return newFunc
    386