Caixa de ferramentas Python hipermoderna

Apr 03 2023
Bibliotecas Python estabelecendo o padrão em 2023. Todo desenvolvedor Python é desafiado pelo tamanho e velocidade do grande e dinâmico ecossistema Python. o novo e o útil em Python.

Bibliotecas Python estabelecendo o padrão em 2023.

Um computador no estilo de MC Escher — criado com Stable Diffusion.

Todo desenvolvedor Python é desafiado pelo tamanho e velocidade do grande e dinâmico ecossistema Python

De novatos encontrando seu primeiro fluxo de trabalho a desenvolvedores seniores acompanhando novos pacotes, todos nós lutamos para acompanhar a interseção do novo e do útil em Python.

Esta postagem fornece clareza com uma caixa de ferramentas Python hipermoderna - ferramentas que estão definindo o padrão para Python em 2023

Python 3.10

O Python 3.10 adicionou mensagens de erro melhores — é mais importante do que você imagina.

O código abaixo contém um erro Tentamos atribuir um valor ao primeiro elemento de data, referindo-nos erroneamente ao inexistente datas:

#  mistake.py

data = [1, 4, 8]
#  datas does not exist!
datas[0] = 2

$ python --version
3.8.13

$ python mistake.py
Traceback (most recent call last):
  File "mistake.py", line 2, in <module>
    datas[0] = 2
NameError: name 'datas' is not defined

$ python --version
3.10.6

$ python mistake.py
Traceback (most recent call last):
  File "/Users/adam/hypermodern-python-2022/mistake.py", line 2, in <module>
    datas[0] = 2
NameError: name 'datas' is not defined. Did you mean: 'data'?

pyenv & pyenv-virtualenv

A parte mais difícil de aprender Python é aprender a instalar e gerenciar Python.

Mesmo os desenvolvedores seniores podem ter dificuldades com o gerenciamento do Python, especialmente quando não é seu idioma principal.

Trabalhar com Python requer ser capaz de trabalhar com diferentes versões de Python e com diferentes ambientes virtuais Python.

O clássico comentário xkcd sobre o ecossistema Python.

pyenv é uma ferramenta para gerenciar diferentes versões do Python .

É uma alternativa ao uso do miniconda ou instalação do Python a partir de um instalador baixado.

O pyenv pode ser usado para gerenciar muitas versões do Python — abaixo de três versões do Python instaladas e gerenciadas pelo pyenv:

$ pyenv versions
3.7.9
3.8.13
3.10.5

$ pyenv install 3.10.6
python-build: use [email protected] from homebrew
python-build: use readline from homebrew
Installing Python-3.10.6...
python-build: use tcl-tk from homebrew
python-build: use readline from homebrew
python-build: use zlib from xcode sdk
Installed Python-3.10.6 to /Users/adam/.pyenv/versions/3.10.6

Depois de instalar esta versão do Python, agora podemos criar um ambiente virtual usando esta versão do Python.

pyenv-virtualenv é uma ferramenta para gerenciar ambientes virtuais em Python — é uma alternativa ao venv ou miniconda.

pyenv-virtualenv funciona bem com nossas instalações pyenv do Python. Podemos criar um ambiente virtual pyenv usando $ pyenv virtualenv {version} {name}:

$ pyenv virtualenv 3.10.6 default

$ pyenv versions
3.7.9
3.8.13
3.10.5
3.10.6
3.10.6/envs/default

Poesia

Depois de ter uma nova configuração do Python em um ambiente virtual, muitas vezes você desejará trabalhar com pacotes Python externos (como numpy ou pandas) e criar seu próprio pacote Python para organizar seu próprio código-fonte.

Poetry é uma ferramenta para gerenciar dependências e pacotes do Python. É uma alternativa ao pip. Tanto o pip quanto o Poetry são usados ​​para instalar e atualizar pacotes de terceiros.

O Pip usa dois arquivos para gerenciar um pacote Python ✅:

  • requirements.txt- uma lista de dependências do Python,
  • setup.py- um script Python que descreve nosso pacote.
  • pyproject.tomlpara descrever nosso pacote Python,
  • poetry.lockpara definir e bloquear todas as dependências - semelhante à saída de $ pip freeze.

A poesia tem duas formas de iniciar um novo projeto :

  • $ poetry new- iniciar um novo projeto (irá criar uma pasta com arquivos de Poesia, README e pasta de pacotes),
  • $ poetry init- em um projeto existente - somente arquivos de Poesia.

$ pip install -q poetry; poetry init
This command will guide you through creating your pyproject.toml config.

Package name [general]:  general
Version [0.1.0]:
Description []:

#  pyproject.toml

[tool.poetry]
name = "general"
version = "0.1.0"
authors = ["Adam Green <[email protected]>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10"
mypy = "^0.971"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

$ poetry install
Updating dependencies
Resolving dependencies... (0.1s)

Writing lock file

Package operations: 4 installs, 0 updates, 0 removals

  • Installing mypy-extensions (0.4.3)
  • Installing tomli (2.0.1)
  • Installing typing-extensions (4.3.0)
  • Installing mypy (0.971)

$ head -n 12 poetry.lock
[[package]]
name = "mypy"
version = "0.971"
description = "Optional static typing for Python"
category = "main"
optional = false
python-versions = ">=3.6"

[package.dependencies]
mypy-extensions = ">=0.4.3"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
typing-extensions = ">=3.10"

Podemos exportar nossas dependências para um pip compatível requirements.txt:

$ poetry export -f requirements.txt > requirements.txt

preto e isort

Black & isort são ferramentas que formatam código Python — são alternativas a ferramentas como autopep8.

O código abaixo bad_format.pyestá mal formatado

Não temos espaços em branco ao redor de =:

#  bad_format.py

data=[1, 4, 8]
datas[0]=2


$ black bad_format.py
reformatted test.py

All done! ✨  ✨
1 file reformatted.

#  bad_format.py

data = [1, 4, 8]
datas[0] = 2

#  bad_imports.py

import pandas as pd
import random
impport collections
data = [1, 4, 8]
datas[0] = 2

$ isort bad_imports.py 
Fixing /Users/adam/dss/notes/content/ideas/temp/test.py

#  bad_imports.py

import collections
import random

import pandas as pd

data = [1, 4, 8]
datas[0] = 2

rufo

Ruff é um linter do Python — é uma alternativa ao Flake8. O Ruff verificará o código com base nas regras - não formatará o código como Black e isort.

A grande coisa de Ruff está sendo escrita em Rust - isso torna mais rápido. Quando usado com Black para garantir um estilo de código consistente, o Ruff cobre grande parte do conjunto de regras do Flake8, juntamente com outras regras, como isort.

Uma ótima maneira de usar o Ruff é com os padrões e verificar tudo.

O código abaixo tem três problemas — usamos uma variável indefinida datasat, tem imports no lugar errado e importa algo que não usamos:

data = datas[0]
import collections

$ ruff check .
ruff.py:1:8: F821 Undefined name `datas`
ruff.py:2:1: E402 Module level import not at top of file
ruff.py:2:8: F401 [*] `collections` imported but unused
Found 3 errors.
[*] 1 potentially fixable with the - fix option.

Verificação de tipo estático com mypy

mypy é uma ferramenta para impor segurança de tipo em Python — é uma alternativa para declarações de tipo que permanecem apenas como documentação não executada.

Recentemente, o Python passou por uma transição semelhante à transição de Javascript para Typescript, com a digitação estática sendo aprimorada na biblioteca padrão e com ferramentas de terceiros. Python com tipagem estática é o padrão para muitas equipes que desenvolvem Python em 2023.

O código abaixo contém mypy_error.pyum erro Tentamos dividir uma string por 10:

#  mypy_error.py

def process(user):
    user['name'] / 10

user = {'name': 'alpha'}
process(user)

$ mypy --strict mypy_error.py
mypy_error.py:1: error: Function is missing a type annotation
mypy_error.py:5: error: Call to untyped function "process" in typed context
Found 2 errors in 1 file (checked 1 source file)

  1. user: dict[str,str]- useré um dicionário com strings como chaves e valores,
  2. -> None:- a processfunção retorna Nenhum.
  3. #  mypy_intermediate.py
    
    def process(user: dict[str,str]) -> None:
        user['name'] / 10
    
    user = {'name': 'alpha'}
    process(user)
    

    $ mypy --strict mypy_intermediate.py 
    mypy_fixed.py:2: error: Unsupported operand types for / ("str" and "int") Found 1 error in 1 file (checked 1 source file)
    

Dica - adicione mypy como uma camada adicional de teste ao seu conjunto de testes.

Organize dados com pydantic

pydantic é uma ferramenta para organizar e validar dados em Python — é uma alternativa ao uso de dicionários ou dataclasses.

O pydantic faz parte da revolução de digitação do Python — a capacidade do pydantic de criar tipos personalizados torna a escrita em Python digitada uma alegria.

pydantic usa dicas de tipo Python para definir tipos de dados. Vamos modelar a usercom a namee id:

import uuid

users = [    {'name': 'alpha', 'id': str(uuid.uuid4())},    {'name': 'beta'},    {'name': 'omega', 'id': 'invalid'}]

import uuid
import pydantic

class User(pydantic.BaseModel):
    name: str
    id: str = None

users = [
    User(name='alpha', 'id'= str(uuid.uuid4())),
    User(name='beta'),
    User(name='omega', id='invalid'),
]

import uuid
import pydantic

class User(pydantic.BaseModel):
    name: str
    id: str = None

    @pydantic.validator('id')
    def validate_id(cls, user_id):
        try:
            user_id = uuid.UUID(user_id, version=4)
            print(f"{user_id} is valid")
            return user_id
        except ValueError:
            print(f"{user_id} is invalid")
            return None

users = [
    User(name='alpha', id= str(uuid.uuid4())),
    User(name='beta'),
    User(name='omega', id='invalid'),
]
[print(user) for user in users]

$ python pydantic_eg.py
45f3c126-1f50-48bf-933f-cfb268dca39a is valid
invalid is invalid
name='alpha' id=UUID('45f3c126-1f50-48bf-933f-cfb268dca39a')
name='beta' id=None
name='omega' id=None

Criar CLIs com o Typer

Typer é uma ferramenta para construir interfaces de linha de comando (CLIs) usando dicas de tipo em Python — é uma alternativa para sys.argv e argparse.

Podemos construir uma CLI Python com Poetry e Typer criando primeiro um pacote Python com Poetry, adicionando typercomo uma dependência).

Aqui usamos poetry new, que criará mais arquivos e pastas do que poetry init:

$ poetry new general
$ tree
.
└── general
    ├── README.md
    ├── general
    │   └── __init__.py
    ├── pyproject.toml
    └── tests
        └── __init__.py

#  general/cli.py

import typer

def main(name: str) -> None:
    print(f"Hello {name}")

if __name__ == "__main__":
    typer.run(main)

$ python general/cli.py omega
Hello omega

$ python general/cli.py --help
Usage: cli.py [OPTIONS] NAME

Arguments:
  NAME  [required]

Options:
  --install-completion  Install completion for the current shell.
  --show-completion     Show completion for the current shell, to copy it or
                        customize the installation.
  --help                Show this message and exit.

#  pyproject.toml

[tool.poetry.scripts]
general-cli = "general.cli:main"

$ poetry run general-cli zeta
hello zeta

Execute comandos Shell em Python com zxpy

zxpy é uma ferramenta para executar comandos shell dentro do Python . Usaremos a CLI do Github como uma fonte de comandos shell — é uma boa maneira de obter dados sobre seu código no Github.

Abaixo temos todos os problemas para o repositório mypy no Github:

$ gh search issues --repo python/mypy --json title | jq > issues.json
$ head -n 7 issues.json
[
  {
    "title": "Not evaluating Union[X, Y] from Type[Union[X, Y]] over (Type[T]) -> T function"
  },
  {
    "title": "Detect `Any` used as a metaclass"
  },

Com o zxpy, podemos executar o comando shell diretamente no Python — usando a ~"shell-command"sintaxe

#  zxpy_eg.py

import json

issues = json.loads(~"gh search issues --repo python/mypy --json title")
print(f"{len(issues)} issues")
print(f" first {issues[0]}")
print(" last {issues[-1]}")

$ zxpy zxpy_eg.py
30 issues
 first {'title': 'Cannot infer type of generic attributes in `match` statements when inheritance is involved'}
 last {'title': 'Parent modules are added as a dependency'}

Impressão bonita com rico

Rich é uma ferramenta para imprimir texto bonito em um terminal — é uma alternativa à saída de terminal monótona da maioria dos programas Python.

Um dos recursos mais úteis do Rich é a bela impressão de cores e emojis :

import rich

user = {'name': 'omega', 'id': 'invalid'}
print(f" normal printing\nuser {user}\n")
rich.print(f" :wave: [bold blue]rich[/] [green]printing[/]\nuser {user}\n")

      
                

from rich import print
print('this will be printed with rich :clap:')

      
                

Resumo

Aqui está nossa caixa de ferramentas Hypermodern Python em toda a sua glória :

  • Python 3.10 para melhores mensagens de erro,
  • pyenv & pyenv-virtualenv para gerenciar versões do Python e ambientes virtuais,
  • Poesia para gerenciar pacotes e dependências do Python,
  • Preto e isort para formatar o código Python,
  • Ruff por linting código Python,
  • mypy para verificação de tipo estático,
  • pydantic para organizar e validar dados,
  • Digitador para CLIs,
  • zxpy para executar comandos shell dentro do Python,
  • Rico para impressão bonita no terminal.

Obrigado por ler

Originalmente publicado em https://datasciencesouth.com .

Codificação de nível

Obrigado por fazer parte da nossa comunidade! Antes de você ir:

  • Bata palmas para a história e siga o autor
  • Veja mais conteúdo na publicação Level Up Coding
  • Curso gratuito de entrevista de codificação ⇒ Veja o curso
  • Siga-nos: Twitter | Linkedin | Boletim de Notícias