1. Victor Kotseruba
  2. python-sass

Source

python-sass / sass / css.py

# -*- coding: utf-8 -*-

import cStringIO
import weakref


class Sheet:
    
    """
    represents css stylesheet
    """
    
    def __init__(self):
        self.nodes = []
    
    def to_css(self):
        out = cStringIO.StringIO()
        for node in self.nodes:
            self.write_node(out, node)
        out.seek(0)
        return out.read()
    
    def write_selector(self, out, selector, level):
        line_length = 0
        out.write('  ' * level)
        for i in range(len(selector.sels)):
            if i:
                out.write(', ')
                line_length += 2
            sel = selector.sels[i]
            if line_length and line_length + len(sel) > 50:
                out.write('\n' + '  ' * level)
                line_length = 0
            out.write(sel)
            line_length += len(sel)
    
    def write_node(self, out, node):
        level = node.level
        self.write_selector(out, node.selector, level)
        out.write(' {')
        for prop in node.props:
            self.write_prop(out, prop, level)
        out.write(' }\n')
    
    def write_prop(self, out, prop, level):
        name, values = prop
        value = ' '.join([str(val) for val in values])
        out.write('\n' + '  ' * level + '  %s: %s;' % (name, value))


class Node:
    
    """
    represents one css rule (selector with properties)
    """
    
    def __init__(self, selector, parent, sheet):
        selector = Selector(selector)
        if parent:
            selector = parent.selector + selector
        self.selector = selector
        self.props = []
        self.parent = weakref.proxy(parent) if parent else None
        sheet.nodes.append(self)

    def set_prop(self, name, values):
        self.props.append((name, values))        

    @property
    def level(self):
        node = self
        level = 0
        while node.parent is not None:
            level += 1
            node = node.parent
        return level


class Selector:
    
    """
    css selector with support of "+" operator
    understands ">" and "&"
    """

    def __init__(self, sel):
        self.sels = map(str.strip, sel.split(','))

    def __add__(self, selector):
        _sels = []
        for sel in self.sels:
            for add_sel in selector.sels:
                if add_sel.startswith('>'):
                    _sels.append(sel + add_sel)
                elif add_sel.startswith('&'):
                    _sels.append(sel + add_sel.lstrip('&'))
                else:
                    _sels.append(sel + ' ' + add_sel)
        return Selector(', '.join(_sels))


class MathException(Exception):
    
    """
    happens when somebody make mistake with Numbers
    """
    
    pass


class Number:
    
    """
    number with unit supporting basic math operations
    """
    
    def __init__(self, number, unit):
        self.number = number
        self.unit = unit

    def __str__(self):
        return '%s%s' % (self.number, self.unit)

    def __add__(self, value):
        if isinstance(value, Number) and value.unit == self.unit:
            return Number(self.number+value.number, self.unit)
        if value.unit != self.unit:
            raise MathException('units not match %s + %s' % (self, value))
        raise MathException('not a number %s + %s' % (self, value))

    def __sub__(self, value):
        if isinstance(value, Number) and value.unit == self.unit:
            return Number(self.number-value.number, self.unit)
        raise MathException("%s - %s" % (self, value))

    def __mul__(self, value):
        if isinstance(value, (int, float)):
            return Number(self.number*value, self.unit)
        raise MathException("%s * %s" % (self, value))

    def __div__(self, value):
        if isinstance(value, int):
            return Number(self.number//value, self.unit)
        elif isinstance(value, float):
            return Number(self.number/value, self.unit)
        raise MathException("%s / %s" % (self, value))

    __rmul__ = __mul__
    __truediv__ = __div__
    __repr__ = __str__