summaryrefslogtreecommitdiff
path: root/scss/extension/api.py
blob: 6c77704f0b2f371c74d8178524ff5440ea937444 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
"""Support for extending the Sass compiler."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function


_no_default = object()


class Cache(object):
    """Serves as a local memory cache storage for extensions usage.
    """
    _cache = {}

    def __init__(self, prefix=None):
        self.prefix = prefix

    def get(self, key, default=None):
        try:
            return self.__class__._cache[self.prefix][key]
        except KeyError:
            if default is _no_default:
                raise
            return default

    def set(self, key, value):
        self.__class__._cache.setdefault(self.prefix, {})[key] = value

    def clear_cache(self, key=None):
        if key:
            try:
                del self.__class__._cache[self.prefix][key]
            except KeyError:
                pass
        else:
            self.__class__._cache.clear()

    def __len__(self):
        return len(self.__class__._cache.setdefault(self.prefix, {}))

    def __iter__(self):
        return iter(self.__class__._cache.setdefault(self.prefix, {}))

    def __getitem__(self, key):
        return self.get(key, _no_default)

    def __setitem__(self, key, value):
        return self.set(key, value)

    def __delitem__(self, key):
        self.clear_cache(key)


class Extension(object):
    """An extension to the Sass compile process.  Subclass to add your own
    behavior.

    Methods are hooks, called by the compiler at certain points.  Each
    extension is considered in the order it's provided.
    """

    # TODO unsure how this could work given that we'd have to load modules for
    # it to be available
    name = None
    """A unique name for this extension, which will allow it to be referenced
    from the command line.
    """

    namespace = None
    """An optional :class:`scss.namespace.Namespace` that will be injected into
    the compiler.
    """

    def __init__(self):
        pass

    def __repr__(self):
        return "<{0}>".format(type(self).__name__)

    def handle_import(self, name, compilation, rule):
        """Attempt to resolve an import.  Called once for every Sass string
        listed in an ``@import`` statement.  Imports that Sass dictates should
        be converted to plain CSS imports do NOT trigger this hook.

        So this::

            @import url(foo), "bar", "baz";

        would call `handle_import` twice: once with "bar", once with "baz".

        Return a :class:`scss.source.SourceFile` if you want to handle the
        import, or None if you don't.  (This method returns None by default, so
        if you don't care about hooking imports, just don't implement it.)
        This method is tried on every registered `Extension` in order, until
        one of them returns successfully.

        A good example is the core Sass import machinery, which is implemented
        with this hook; see the source code of the core extension.
        """
        pass


class NamespaceAdapterExtension(Extension):
    """Trivial wrapper that adapts a bare :class:`scss.namespace.Namespace`
    into a full extension.
    """

    def __init__(self, namespace):
        self.namespace = namespace