diff options
author | Eevee (Lexy Munroe) <eevee.git@veekun.com> | 2016-06-08 17:24:31 -0700 |
---|---|---|
committer | Eevee (Lexy Munroe) <eevee.git@veekun.com> | 2016-06-08 17:24:31 -0700 |
commit | 3edf81a3ebfbc913c1dceeba7add454e7ce6c1c9 (patch) | |
tree | 41b0653ff9db0c346057e735e12fd827f8249b22 | |
parent | 466a68ce0278105828d41c36d2341e18aed89348 (diff) | |
download | pyscss-3edf81a3ebfbc913c1dceeba7add454e7ce6c1c9.tar.gz |
Add the Sass 3.4 *-exists functions. Fixes #226, #229, #353
-rw-r--r-- | scss/ast.py | 11 | ||||
-rw-r--r-- | scss/compiler.py | 1 | ||||
-rw-r--r-- | scss/extension/core.py | 71 | ||||
-rw-r--r-- | scss/namespace.py | 14 | ||||
-rw-r--r-- | scss/tests/files/general/global-variable-exists.css | 5 | ||||
-rw-r--r-- | scss/tests/files/general/global-variable-exists.scss | 27 |
6 files changed, 116 insertions, 13 deletions
diff --git a/scss/ast.py b/scss/ast.py index a8ab69b..122fbf2 100644 --- a/scss/ast.py +++ b/scss/ast.py @@ -212,10 +212,6 @@ class CallOp(Expression): funct = None try: funct = calculator.namespace.function(func_name, argspec_len) - # @functions take a ns as first arg. TODO: Python functions possibly - # should too - if getattr(funct, '__name__', None) == '__call': - funct = partial(funct, calculator.namespace) except KeyError: try: # DEVIATION: Fall back to single parameter @@ -226,7 +222,12 @@ class CallOp(Expression): log.error("Function not found: %s:%s", func_name, argspec_len, extra={'stack': True}) if funct: - ret = funct(*args, **kwargs) + if getattr(funct, '_pyscss_needs_namespace', False): + # @functions and some Python functions take the namespace as an + # extra first argument + ret = funct(calculator.namespace, *args, **kwargs) + else: + ret = funct(*args, **kwargs) if not isinstance(ret, Value): raise TypeError("Expected Sass type as return value, got %r" % (ret,)) return ret diff --git a/scss/compiler.py b/scss/compiler.py index e0ca8d5..08c5638 100644 --- a/scss/compiler.py +++ b/scss/compiler.py @@ -684,6 +684,7 @@ class Compilation(object): return e.retval else: return Null() + __call._pyscss_needs_namespace = True return __call _mixin = _call(mixin) _mixin.mixin = mixin diff --git a/scss/extension/core.py b/scss/extension/core.py index 7967429..b939d1c 100644 --- a/scss/extension/core.py +++ b/scss/extension/core.py @@ -840,8 +840,71 @@ def map_merge_deep(*maps): return Map(pairs) +@ns.declare +def keywords(value): + """Extract named arguments, as a map, from an argument list.""" + expect_type(value, Arglist) + return value.extract_keywords() + + # ------------------------------------------------------------------------------ -# Meta functions +# Introspection functions + +# TODO feature-exists + +@ns.declare_internal +def variable_exists(namespace, name): + expect_type(name, String) + try: + namespace.variable('$' + name.value) + except KeyError: + return Boolean(False) + else: + return Boolean(True) + + +@ns.declare_internal +def global_variable_exists(namespace, name): + expect_type(name, String) + + # TODO this is... imperfect and invasive, but should be a good + # approximation + scope = namespace._variables + while len(scope.maps) > 1: + scope = scope.maps[-1] + + try: + scope['$' + name.value] + except KeyError: + return Boolean(False) + else: + return Boolean(True) + + +@ns.declare_internal +def function_exists(namespace, name): + expect_type(name, String) + # TODO invasive, but there's no other way to ask for this at the moment + for fname, arity in namespace._functions.keys(): + if name.value == fname: + return Boolean(True) + return Boolean(False) + + +@ns.declare_internal +def mixin_exists(namespace, name): + expect_type(name, String) + # TODO invasive, but there's no other way to ask for this at the moment + for fname, arity in namespace._mixins.keys(): + if name.value == fname: + return Boolean(True) + return Boolean(False) + + +@ns.declare +def inspect(value): + return String.unquoted(value.render()) + @ns.declare def type_of(obj): # -> bool, number, string, color, list @@ -877,11 +940,7 @@ def comparable(number1, number2): and left.unit_denom == right.unit_denom) -@ns.declare -def keywords(value): - """Extract named arguments, as a map, from an argument list.""" - expect_type(value, Arglist) - return value.extract_keywords() +# TODO call # ------------------------------------------------------------------------------ diff --git a/scss/namespace.py b/scss/namespace.py index 8076bca..939cde5 100644 --- a/scss/namespace.py +++ b/scss/namespace.py @@ -157,7 +157,17 @@ class Namespace(object): return decorator - def _auto_register_function(self, function, name): + def declare_internal(self, function): + """Like declare(), but the registered function will also receive the + current namespace as its first argument. Useful for functions that + inspect the state of the compilation, like ``variable-exists()``. + Probably not so useful for anything else. + """ + function._pyscss_needs_namespace = True + self._auto_register_function(function, function.__name__, 1) + return function + + def _auto_register_function(self, function, name, ignore_args=0): name = name.replace('_', '-').rstrip('-') argspec = inspect.getargspec(function) @@ -170,7 +180,7 @@ class Namespace(object): num_optional = len(argspec.defaults) else: num_optional = 0 - num_args = len(argspec.args) + num_args = len(argspec.args) - ignore_args arities = range(num_args - num_optional, num_args + 1) for arity in arities: diff --git a/scss/tests/files/general/global-variable-exists.css b/scss/tests/files/general/global-variable-exists.css new file mode 100644 index 0000000..a614018 --- /dev/null +++ b/scss/tests/files/general/global-variable-exists.css @@ -0,0 +1,5 @@ +blockquote { + background: lime; + color: red; + float: right; +} diff --git a/scss/tests/files/general/global-variable-exists.scss b/scss/tests/files/general/global-variable-exists.scss new file mode 100644 index 0000000..4404ed0 --- /dev/null +++ b/scss/tests/files/general/global-variable-exists.scss @@ -0,0 +1,27 @@ +@mixin myblockquote { + blockquote { + @if global-variable-exists(blockquote-color) { + background: $blockquote-color; + } + @else { + background: lime; + } + + @if mixin-exists(myblockquote) { + color: red; + } + @else { + color: blue; + } + + @if variable-exists(foo) { + float: left; + } + $foo: 1; + @if variable-exists(foo) { + float: right; + } + } +} + +@include myblockquote; |