Chapter 8, Environment
Which tool to choose, editor and project structure
Five tools do similar things. I compare them with objective criteria and say what I would choose in each situation.
Code for this chapter: ambiente/cap08_estrutura_projeto.py
Comparison
| Criterion | pip and venv | pyenv | pipx | Poetry | uv |
|---|---|---|---|---|---|
| Installs Python versions | No | Yes (compiles) | No | No | Yes (pre-built binaries) |
| Creates virtual environment | Yes (venv) | No | One per tool | Yes | Yes |
| Resolves indirect dependencies | Basic | No | No | Yes | Yes |
| Generates lockfile | No | No | No | Yes | Yes |
| Installs CLI tools | No | No | Yes | No | Yes (uv tool) |
| Publishes packages | No | No | No | Yes | Yes (uv publish) |
| Learning curve | Low | Average | Low | Average | Low |
My recommendation
I would choose uv for new projects because it covers the entire cycle with a single tool. I would choose Poetry if the team is already using it and the poetry.lock is in production: switching managers for trendiness is a cost without return. I would choose pip and venv to learn and for restricted environments where only the system's Python is available: what you learn there applies to everything else. I would choose pyenv if I needed a specific build of Python. I would choose pipx for global tools on machines where uv is not available.
Editor
VS Code with the Microsoft Python extension solves most cases. After opening the project folder, choose the interpreter with the command Python: Select Interpreter (the command palette opens with Ctrl+Shift+P, or Cmd+Shift+P on macOS) and point to the .venv of the project. PyCharm is a complete alternative if you prefer an IDE.
Be careful with the Code Runner extension
By default, Code Runner executes the file in the Output panel of VS Code, which does not accept input. Examples with
input()get stuck or fail there. Run in the integrated terminal (python3 arquivo.py) or use the Run Python File button from the official extension.
The minimum structure of a project
meu-projeto/
pyproject.toml
README.md
.python-version
.gitignore
src/
meu_projeto/
__init__.py
__main__.py
tests/
test_basico.py
Every entry point should be importable without executing anything. The default is a main() protected:
def main() -> None:
print("projeto funcionando")
if __name__ == "__main__":
main()
projeto funcionando
I explain the reason for this protection in the modules chapter.
Exercise 1
Set up your working environment
Create a folder meu-primeiro-projeto, fix Python 3.13, create the environment, install requests and confirm within Python that the environment is active.