An Obsidian IDE for Python
You did what?!#
Let me explain: You write your notes in your Vault. Then, you run a command, and the code inside becomes a script and executes! And it does so with the environment of your choice already activated.
Oh, I see now. This is a notebook!#
No, it’s not. It transforms every code block—including comments—into an independent script.py
inside your .obsidian\scripts\python
folder. From there, you can grab it and start deploying or packaging it.
But the Python Plugin doesn’t work for me here…#
No problem! You can also run it directly from your CLI/Cmd/PowerShell/Terminal—whatever you’re used to. When you do so, it will:
- Find the most recently updated note in your vault.
- Open it and extract every code block marked as Python. For example:
# path_venv = D:\some_path\venv look_a_python some_code = "over here
- Use the first comment in the first Python block to determine the virtual environment. If none is specified, it will use your default environment.
- Save all extracted code blocks, in order, inside a
.py
file in.obsidian\scripts\python
, named after the first snake_case word found in the first comment. - Create a batch file that activates the environment, runs the script, and waits for user input before closing.
Show this the code#
To make this work, save the following script inside .obsidian\scripts\python
:
# IDE_py_Obsidian.py
import os
import re
from datetime import datetime
import subprocess
import glob
import sys
def normalizar_caminho(caminho):
"""Normaliza o caminho para usar uma única barra, independente de como foi escrito"""
# Primeiro substitui barras duplas por barras simples
caminho = caminho.replace('\\\\', '\\')
# Substitui barras normais por barras invertidas
caminho = caminho.replace('/', '\\')
# Remove barras duplas que possam ter sido criadas
while '\\\\' in caminho:
caminho = caminho.replace('\\\\', '\\')
return caminho
def encontrar_arquivo_mais_recente(vault_path):
"""Encontra o arquivo markdown mais recentemente modificado no vault"""
arquivos = glob.glob(os.path.join(vault_path, '**/*.md'), recursive=True)
return max(arquivos, key=os.path.getmtime)
def extrair_blocos_python(arquivo_md):
"""Extrai blocos de código Python e informações do ambiente virtual"""
with open(arquivo_md, 'r', encoding='utf-8') as f:
conteudo = f.read()
# Encontra blocos de código Python
padrao = r'```python\s*(.*?)\s*```'
blocos = re.findall(padrao, conteudo, re.DOTALL)
print(f"Blocos encontrados: {len(blocos)}")
# Procura pelo path_venv nos comentários
path_venv = None
nome_script = None
codigo_completo = []
for bloco in blocos:
print(f"Analisando bloco:\n{bloco}")
match = re.search(r'#\s*path_venv\s*=\s*(.*)\s+(\w+)\s*$', bloco, re.MULTILINE)
if match:
path_venv = normalizar_caminho(match.group(1).strip())
nome_script = match.group(2).strip()
print(f"Path encontrado: {path_venv}")
print(f"Nome do script encontrado: {nome_script}")
codigo_completo.append(bloco)
return '\n'.join(codigo_completo), path_venv, nome_script
def salvar_script_python(codigo, nome_script, pasta_scripts):
"""Salva o código Python extraído em um arquivo .py"""
if not os.path.exists(pasta_scripts):
os.makedirs(pasta_scripts)
caminho_script = os.path.join(pasta_scripts, f"{nome_script}.py")
with open(caminho_script, 'w', encoding='utf-8') as f:
f.write(codigo)
return caminho_script
def criar_batch_script(path_venv, caminho_script):
"""Cria um arquivo batch temporário para ativar o ambiente e executar o script"""
# Garantir que os caminhos estejam corretos
path_venv = normalizar_caminho(path_venv)
caminho_script = normalizar_caminho(caminho_script)
conteudo_batch = f"""@echo off
chcp 65001 > nul
echo Ativando ambiente virtual em {path_venv}...
call "{path_venv}\\Scripts\\activate.bat"
echo.
echo Executando script {caminho_script}...
echo.
python "{caminho_script}"
echo.
echo Execucao finalizada!
echo.
:PROMPT
set /p CONFIRM="Digite OK para fechar: "
if /i "%CONFIRM%" neq "OK" goto PROMPT
exit
"""
batch_path = caminho_script.replace('.py', '.bat')
print(f"Criando batch em: {batch_path}")
print(f"Conteúdo do batch:\n{conteudo_batch}")
with open(batch_path, 'w', encoding='utf-8') as f:
f.write(conteudo_batch)
return batch_path
def main(vault_path):
# Configurações
pasta_scripts = os.path.join(vault_path, '.obsidian', 'scripts', 'python')
# Encontra o arquivo mais recente
arquivo_recente = encontrar_arquivo_mais_recente(vault_path)
print(f"Arquivo mais recente encontrado: {arquivo_recente}")
# Extrai o código Python
codigo, path_venv, nome_script = extrair_blocos_python(arquivo_recente)
if not codigo or not path_venv or not nome_script:
print("Não foi possível encontrar código Python válido ou informações do ambiente virtual")
print(f"Código: {bool(codigo)}")
print(f"Path_venv: {path_venv}")
print(f"Nome_script: {nome_script}")
return
# Salva o script Python
caminho_script = salvar_script_python(codigo, nome_script, pasta_scripts)
print(f"Script salvo em: {caminho_script}")
# Cria e executa o batch script
batch_path = criar_batch_script(path_venv, caminho_script)
print(f"Executando batch: {batch_path}")
try:
subprocess.run(batch_path, shell=True)
# Apaga o arquivo batch após a execução
if os.path.exists(batch_path):
os.remove(batch_path)
except Exception as e:
print(f"Erro ao executar batch: {e}")
if __name__ == "__main__":
try:
vault_path = sys.argv[1]
except IndexError:
vault_path = "D:\\Trabalho\\obsidian_vaults\\Vault"
main(vault_path)
Let’s code some more!#
This was a very satisfying exercise that has now become part of my daily workflow. I hope this helps you as well in the projects you have in mind!