def adjust(color, prop, amount, evaluator=None): assert_color(color, "color") assert_string(prop, "prop") assert_type(amount, "unit", "amount") hsl = color.hsla().clone() if not hasattr(hsl, prop.string): raise StilusError("Invalid adjustment property.") value = amount.value if amount.type == "%": if prop.string == "lightness" and value > 0: value = (100 - hsl.lightness) * value / 100 else: value = getattr(hsl, prop.string) * (value / 100) setattr(hsl, prop.string, getattr(hsl, prop.string) + value) return hsl.rgba()
def range_function(start, stop, step=None, evaluator=None): assert_type(start, "unit", "start") assert_type(stop, "unit", "stop") if step: assert_type(step, "unit", "step") if step.value == 0: raise StilusError('ArgumentError: "step" argument ' "must not be zero") else: step = Unit(1) lst = Expression() i = start.value while i <= stop.value: lst.append(Unit(i, start.type)) i += step.value return lst
def image_size(img: str, ignore_errors=False, evaluator=None): assert_type(img, "string", "img") p = Path(img.string) path = lookup(p, evaluator.paths) if p.suffix == ".svg": return _x_and_y_from_svg(path) if path: with Image.open(path) as image: x, y = image.size expression = Expression() expression.nodes = [Unit(x, "px"), Unit(y, "px")] return expression elif ignore_errors: expression = Expression() expression.nodes = [Unit(0), Unit(0)] return expression else: raise StilusError("Could not find image.")
def visit(self, node): try: return super().visit(node) except StilusError as se: if not se.filename: raise se try: with open(str(se.filename)) as f: input = f.read() except (AttributeError, FileNotFoundError): pass raise StilusError( self.stack, filename=node.filename, lineno=node.lineno, column=node.column, input=input, )
def opposite_position(positions, evaluator=None): expr = [] nodes = utils.unwrap(positions) for i, node in enumerate(nodes): utils.assert_string(node, f"position {i}") if node.string == "top": expr.append(Literal("bottom")) elif node.string == "bottom": expr.append(Literal("top")) elif node.string == "left": expr.append(Literal("right")) elif node.string == "right": expr.append(Literal("left")) elif node.string == "center": expr.append(Literal("center")) else: raise StilusError(f"invalid position {i}") return expr
def visit_group(self, group: Group): if self.keyframe: stack = [] else: stack = self.stack if self.compress: comma = "," else: comma = ",\n" stack.append(group.nodes) # selectors if group.block.has_properties(): selectors = utils.compile_selectors( stack, leave_hidden=False, indent=self.indent() ) if selectors: if self.keyframe: if self.compress: comma = "," else: comma = ", " for i, selector in enumerate(selectors): last = i == len(selectors) - 1 # keyframe bocks (10%, 20% { ... }) if self.keyframe: selector = selector.strip() if i else selector try: self.buf += self.out( selector + ("" if last else comma) ) except BaseException: raise StilusError() else: group.block.lacks_rendered_selectors = True self.visit(group.block) stack.pop()
def json_function(path, local=None, name_prefix=None, evaluator=None): assert_string(path, "path") def convert(content, options): ret = ObjectNode() leave_strings = options.get("leave-strings").to_boolean() for key in content: val = content[key] if isinstance(val, dict): ret.set(key, convert(val, options)) else: val = coerce(val, raw=False) if (val and val.node_name == "string" and leave_strings.is_false()): val = parse_string(val.string) ret.set(key, val) return ret # lookup path = path.string found = lookup(path, evaluator.paths, evaluator.filename) options = None if (local and hasattr(local, "node_name") and local.node_name == "objectnode"): options = local if not found: if options: optional = options.get("optional") if optional and optional.first().is_true(): return null raise StilusError(f"failed to locate .json file {path}") # read with open(found, "r") as reader: content = json.load(reader) if options: return convert(content, options) else: old_json_function(content, local, name_prefix, evaluator)
def error(msg, evaluator=None): assert_type(msg, "string", "msg") err = StilusError(msg.value) err.from_stilus = True raise err
def visit_import(self, imported): literal = False self.result += 1 path = self.visit(imported.path).first() node_name = "require" if imported.once else "import" self.result -= 1 log.debug(f"import {path}") # url() passed if hasattr(path, "function_name") and path.function_name == "url": if hasattr(imported, "once") and imported.once: raise StilusError("You cannot @require a url") return imported # ensure string if not hasattr(path, "string"): raise StilusError(f"@{node_name} string expected") name = path = path.string # absolute URL or hash m = re.match(r"(?:url\s*\(\s*)?[\'\"]?(?:#|(?:https?:)?\/\/)", path, re.I) if m: if imported.once: raise StilusError("You cannot @require a url") return imported # literal if path.endswith(".css") or '.css"' in path: literal = True if not imported.once and not self.include_css: return imported # support optional .styl if not literal and not path.endswith(".styl"): path += ".styl" # lookup found = utils.find(path, self.paths, self.filename) if not found: found = utils.lookup_index(name, self.paths, self.filename, parser=self.parser) # throw if import failed if not found: raise TypeError(f"failed to locate @{node_name} file in {path}" f" {self.paths}") block = Block(None, None, lineno=self.parser.lineno, column=self.parser.column) for f in found: block.append( self.import_file( imported, f, literal, lineno=self.parser.lineno, column=self.parser.column, )) return block
def operate( # noqa: C901 self, op: str, right: Type["Node"], value=None ) -> "Node": """Operate on ``right`` with the given ``op``.""" from .boolean import Boolean if op == "is a": if "string" == right.first().node_name: return Boolean(self.node_name == right.value) else: raise Exception( f'"is a" expects a string, ' f"got {right.toString}" ) elif op == "==": from stilus import utils if utils.is_number(self) and utils.is_number(right): return Boolean(utils.get_value(self) == utils.get_value(right)) return Boolean(self.hash() == right.hash()) elif op == "!=": return Boolean(self.hash() != right.hash()) elif op == ">=": return Boolean(self.hash() >= right.hash()) elif op == "<=": return Boolean(self.hash() <= right.hash()) elif op == ">": if self.one_is_unit(right): return Boolean(float(self.hash()) > float(right.hash())) return Boolean(self.hash() > right.hash()) elif op == "<": return Boolean(self.hash() < right.hash()) elif op == "||": return self if self.to_boolean().value is True else right elif op == "in": from stilus import utils values = utils.unwrap(right).nodes if not values: raise StilusError( '"in" given invalid right-hand operand, ' "expecting an expression" ) # 'prop' in object if len(values) == 1 and values[0].node_name == "objectnode": return Boolean(values[0].has(self.hash())) for value in values: if str(value.hash()) == str(self.hash()): return Boolean(True) return Boolean(False) elif op == "&&": a = self.to_boolean() b = right.to_boolean() if a.value is True and b.value is True: return right elif a.value is False: return self return right elif op == "[]": raise StilusError(f"cannot perform {self}[{right}]") else: raise StilusError(f"cannot perform {self} {op} {right}")