Estou trabalhando em uma ferramenta de automação de avaliação para tarefas de programação. Cada submissão do aluno é executada em seu próprio ambiente virtual isolado (venv), e as dependências são instaladas a partir de um arquivo requirements.txt localizado na pasta de cada submissão.
Usei subprocess.run([sys.executable, "-m", "venv", "submission_[studentID]/venv"])
para cada envio de aluno. É seguro e funciona como esperado, mas é muito lento ao processar mais de 200 envios. Também utilizei o multiprocessamento para criar um ambiente virtual em paralelo, mas também demora muito para concluir.
Existe uma maneira segura e rápida de configurar um ambiente virtual por aluno (possivelmente sem modificar o ambiente base original)?
É assim que estou criando venv
por enquanto:
class VenvManager:
## Initialise the folder and virtual environment path
def __init__(self, folderPath: Path):
self._folderPath = Path(folderPath).resolve()
self._envPath = self._folderPath / "venv"
self.requirements_path = self._folderPath / "requirements.txt"
## Create virtual environment in the submission directory as 'venv'
def create_venv(self):
if not self._envPath.exists():
result = subprocess.run(
[sys.executable, "-m", "venv", str(self._envPath)],
capture_output= True,
text=True
)
if result.returncode != 0:
return False
return True
else:
...
no script principal:
def setup_virtualenvs(submissions_root: Path):
submissions = [s for s in submissions_root.iterdir() if s.is_dir() and s.name.startswith("Portfolio")]
def setup(sub):
v = VenvManager(sub)
v.create_venv()
v.install_requirements()
v.save_log()
with ThreadPoolExecutor(max_workers=12) as executor:
futures = {executor.submit(setup, sub): sub.name for sub in submissions}
for future in tqdm(as_completed(futures), total=len(futures), desc="Setting up venvs", unit="student"):
student_name = futures[future]
try:
future.result()
print(f"Finished setup for {student_name}")
except Exception as e:
print(f" Error processing {student_name}: {e}")
Minha recomendação é usar . uv
uv
em vez devenv
. uv, que é cerca de uma ordem de magnitude mais rápido na criação de envs e na instalação de pacotes.A título de comparação, isto é usar
venv
e regularpip
:python -m venv with-venv
source with-venv/Scripts/activate
pip install numpy
deactivate
python -v venv with-venv2
source with-venv2/Scripts/activate
pip install numpy
Fazer o equivalente com
uv
é muito mais rápido:uv venv with-uv
source with-uv/Scripts/activate
uv pip install numpy
deactivate
uv venv with-uv2
source with-uv/Scripts/activate
uv pip install numpy
Executei cada sequência duas vezes para demonstrar os benefícios do cache. Com o pip normal, há alguma economia, mas com o uv, todas as operações são super rápidas, exceto o download pela rede.
Escolhi o NumPy como um exemplo arbitrário de instalação, mas tenho um projeto com muitas dependências bem grandes, e o uv leva 30 segundos para instalá-lo na primeira vez, 0,6 s quando encontra tudo em seus caches para o segundo venv. O Pip, por outro lado, levou 69 segundos no primeiro venv e ainda precisou de 27 segundos no segundo venv.
Se você usar
uv
, acredito que não sentirá necessidade de multithread, e talvez seja melhor não fazer isso de qualquer maneira, porque imaginouv pip install
que já esteja usando paralelismo internamente.uv
Repositório oficial : https://github.com/astral-sh/uv e documentação: https://docs.astral.sh/uv/