"""Lee una nota de Obsidian (.md) desde disco y la descompone en sus partes. Compone funciones puras del grupo obsidian: parse_obsidian_frontmatter y extract_obsidian_wikilinks. Funcion impura: hace I/O de lectura de archivo. """ import os from obsidian import extract_obsidian_wikilinks, parse_obsidian_frontmatter def _normalize_tags(raw) -> list: """Normaliza el campo tags del frontmatter a una lista de strings. Acepta None, un string CSV ("a, b, c") o una lista. Devuelve siempre una lista de strings sin espacios sobrantes y sin entradas vacias. """ if raw is None: return [] if isinstance(raw, str): return [t.strip() for t in raw.split(",") if t.strip()] if isinstance(raw, (list, tuple)): return [str(t).strip() for t in raw if str(t).strip()] # Cualquier otro tipo (int, etc.) -> representacion como unico tag. return [str(raw).strip()] if str(raw).strip() else [] def read_obsidian_note(path: str) -> dict: """Lee una nota Markdown de Obsidian y devuelve sus partes estructuradas. Args: path: ruta al archivo .md a leer. Returns: dict con las claves: - path: la ruta leida (tal cual fue pasada). - frontmatter: dict con el frontmatter YAML parseado. - body: str con el cuerpo Markdown sin el frontmatter. - wikilinks: list con los destinos de los wikilinks [[...]] del body. - tags: list normalizada a partir de frontmatter["tags"]. Raises: FileNotFoundError: si el archivo no existe. IsADirectoryError: si la ruta apunta a un directorio. OSError: si la lectura falla por otro motivo de I/O. """ if not os.path.exists(path): raise FileNotFoundError(f"obsidian note not found: {path}") if os.path.isdir(path): raise IsADirectoryError(f"expected a file, got a directory: {path}") with open(path, "r", encoding="utf-8") as f: content = f.read() parsed = parse_obsidian_frontmatter(content) frontmatter = parsed.get("frontmatter", {}) or {} body = parsed.get("body", "") or "" wikilinks = extract_obsidian_wikilinks(body) tags = _normalize_tags(frontmatter.get("tags")) return { "path": path, "frontmatter": frontmatter, "body": body, "wikilinks": wikilinks, "tags": tags, } if __name__ == "__main__": import tempfile sample = "---\ntitle: Demo\ntags: [a, b]\n---\n\nHola [[Otra Nota]] y [[Tercera]].\n" fd, tmp = tempfile.mkstemp(suffix=".md") os.close(fd) with open(tmp, "w", encoding="utf-8") as f: f.write(sample) note = read_obsidian_note(tmp) assert note["frontmatter"].get("title") == "Demo", note["frontmatter"] assert note["tags"] == ["a", "b"], note["tags"] assert "Otra Nota" in note["wikilinks"], note["wikilinks"] os.remove(tmp) print("read_obsidian_note smoke OK")