def main(): global jinja, yaml, inp parser = argparse.ArgumentParser() parser.add_argument("input", help="Input directory", nargs='?') parser.add_argument("-o", "--output", help="Output file") args = parser.parse_args() inp = get_input_dir(args.input) outp = args.output or os.path.join(inp, '..', 'ui-lovelace.yaml') jinja = jinja2.Environment(loader=jinja2.FileSystemLoader(inp)) yaml = YAML(typ='safe') yaml.Constructor = SafeConstructor SafeConstructor.add_constructor("!include", include_statement) SafeConstructor.add_constructor("!file", file_statement) try: data = process_file(jinja, yaml, MAIN_FILE) except Exception as e: print("Processing of yaml failed.", file=sys.stderr) print(e) sys.exit(3) try: with open(outp, 'w') as fp: fp.write(GENERATOR_MESSAGE) yaml.dump(data, fp) except Exception as e: print("Writing ui-lovelace.yaml failed.", file=sys.stderr) print(e) sys.exit(4)
def process_file(path, args={}): global jinja template = jinja.get_template(path) yaml = YAML(typ='rt') yaml.preserve_quotes = True yaml.Constructor = RoundTripConstructor return yaml.load(template.render(args) + '\n')
def parse_yaml_preserve_spans(contents: str, filename: Optional[str]) -> YamlTree: """ parse yaml into a YamlTree object. The resulting spans are tracked in SourceTracker so they can be used later when constructing error messages or displaying context. :raise jsonschema.exceptions.SchemaError: if config is invalid """ source_hash = SourceTracker.add_source(contents) # this uses the `RoundTripConstructor` which inherits from `SafeConstructor` class SpanPreservingRuamelConstructor(RoundTripConstructor): def construct_object(self, node: Node, deep: bool = False) -> YamlTree: r = super().construct_object(node, deep) if r is None: from semgrep.error import InvalidRuleSchemaError Span.from_node(node, source_hash=source_hash, filename=filename) raise InvalidRuleSchemaError( short_msg="null values prohibited", long_msg= "In semgrep YAML configuration, null values are prohibited", spans=[ Span.from_node(node, source_hash=source_hash, filename=filename).with_context( before=1, after=1) ], ) if isinstance(r, dict): r = YamlMap(r) return YamlTree( r, Span.from_node(node, source_hash=source_hash, filename=filename)) yaml = YAML() yaml.Constructor = SpanPreservingRuamelConstructor data = yaml.load(StringIO(contents)) validate_yaml(data) if not isinstance(data, YamlTree): raise Exception( f"Something went wrong parsing Yaml (expected a YamlTree as output, but got {type(data).__name__}): {PLEASE_FILE_ISSUE_TEXT}" ) return data
def load_yaml(fname): """Load a YAML file.""" yaml = YAML(typ="safe") # Compat with HASS yaml.allow_duplicate_keys = True # Stub HASS constructors HassSafeConstructor.name = fname yaml.Constructor = HassSafeConstructor with open(fname, encoding="utf-8") as conf_file: # If configuration file is empty YAML returns None # We convert that to an empty dict return yaml.load(conf_file) or {}
def load_hass_config(path): """Load the HASS config.""" fname = os.path.join(path, 'configuration.yaml') yaml = YAML(typ='safe') # Compat with HASS yaml.allow_duplicate_keys = True # Stub HASS constructors HassSafeConstructor.name = fname yaml.Constructor = HassSafeConstructor with open(fname, encoding='utf-8') as conf_file: # If configuration file is empty YAML returns None # We convert that to an empty dict return yaml.load(conf_file) or {}
def load_yaml(fname: str, round_trip: bool = False) -> JSON_TYPE: """Load a YAML file.""" if round_trip: yaml = YAML(typ='rt') yaml.preserve_quotes = True else: if not hasattr(ExtSafeConstructor, 'name'): ExtSafeConstructor.name = fname yaml = YAML(typ='safe') yaml.Constructor = ExtSafeConstructor try: with open(fname, encoding='utf-8') as conf_file: # If configuration file is empty YAML returns None # We convert that to an empty dict return yaml.load(conf_file) or OrderedDict() except YAMLError as exc: _LOGGER.error("YAML error in %s: %s", fname, exc) raise HomeAssistantError(exc) except UnicodeDecodeError as exc: _LOGGER.error("Unable to read file %s: %s", fname, exc) raise HomeAssistantError(exc)
def load_yaml(fname: str, round_trip: bool = False) -> JSON_TYPE: """Load a YAML file.""" if round_trip: yaml = YAML(typ="rt") yaml.preserve_quotes = True # type: ignore[assignment] else: if ExtSafeConstructor.name is None: ExtSafeConstructor.name = fname yaml = YAML(typ="safe") yaml.Constructor = ExtSafeConstructor try: with open(fname, encoding="utf-8") as conf_file: # If configuration file is empty YAML returns None # We convert that to an empty dict return yaml.load(conf_file) or OrderedDict() except YAMLError as exc: _LOGGER.error("YAML error in %s: %s", fname, exc) raise OpenPeerPowerError(exc) from exc except UnicodeDecodeError as exc: _LOGGER.error("Unable to read file %s: %s", fname, exc) raise OpenPeerPowerError(exc) from exc
def load_yaml(fname: str, round_trip: bool = False) -> JSON_TYPE: """Load a YAML file.""" if round_trip: yaml = YAML(typ="rt") # type ignore: https://bitbucket.org/ruamel/yaml/pull-requests/42 yaml.preserve_quotes = True # type: ignore else: if ExtSafeConstructor.name is None: ExtSafeConstructor.name = fname yaml = YAML(typ="safe") yaml.Constructor = ExtSafeConstructor try: with open(fname, encoding="utf-8") as conf_file: # If configuration file is empty YAML returns None # We convert that to an empty dict return yaml.load(conf_file) or OrderedDict() except YAMLError as exc: _LOGGER.error("YAML error in %s: %s", fname, exc) raise HomeAssistantError(exc) except UnicodeDecodeError as exc: _LOGGER.error("Unable to read file %s: %s", fname, exc) raise HomeAssistantError(exc)
def parse_yaml_preserve_spans(contents: str, filename: Optional[str]) -> YamlTree: """ parse yaml into a YamlTree object. The resulting spans are tracked in SourceTracker so they can be used later when constructing error messages or displaying context. :raise jsonschema.exceptions.SchemaError: if config is invalid """ source_hash = SourceTracker.add_source(contents) # this uses the `RoundTripConstructor` which inherits from `SafeConstructor` class SpanPreservingRuamelConstructor(RoundTripConstructor): def construct_object(self, node: Node, deep: bool = False) -> YamlTree: r = super().construct_object(node, deep) # Check for duplicate mapping keys. # This -should- be caught and raised by ruamel.yaml. # However, resetting the constructor below, where the line # reads yaml.Constructor = SpanPreservingRuamelConstructor, # causes ruamel's DuplicateKeyError not to be raised. # This is a quick implementation that will check MappingNodes # if isinstance(node, MappingNode): from semgrep.error import InvalidRuleSchemaError kv_pairs: List[Tuple[Node, Node]] = [t for t in node.value] uniq_key_names: Set[str] = set(t[0].value for t in kv_pairs) # If the number of unique key names is less than the number # of key-value nodes, then there's a duplicate key if len(uniq_key_names) < len(kv_pairs): raise InvalidRuleSchemaError( short_msg="Detected duplicate key", long_msg= f"Detected duplicate key name, one of {list(sorted(uniq_key_names))}.", spans=[ Span.from_node(node, source_hash=source_hash, filename=filename).with_context( before=1, after=1) ], ) if r is None: from semgrep.error import InvalidRuleSchemaError Span.from_node(node, source_hash=source_hash, filename=filename) raise InvalidRuleSchemaError( short_msg="null values prohibited", long_msg= "In semgrep YAML configuration, null values are prohibited", spans=[ Span.from_node(node, source_hash=source_hash, filename=filename).with_context( before=1, after=1) ], ) if isinstance(r, dict): r = YamlMap(r) return YamlTree( r, Span.from_node(node, source_hash=source_hash, filename=filename)) yaml = YAML() yaml.Constructor = SpanPreservingRuamelConstructor data = yaml.load(StringIO(contents)) validate_yaml(data) if not isinstance(data, YamlTree): raise Exception( f"Something went wrong parsing Yaml (expected a YamlTree as output, but got {type(data).__name__}): {PLEASE_FILE_ISSUE_TEXT}" ) return data