Iniciando com o ORM Pony no Python III - Erros e Exceções
Seguindo o trilha com o ORM Pony, neste terceiro texto, agora começaremos a gerar situações de erro e ver o que os objetos retornam ou que exceções são geradas.
No primeiro texto desta série, demos os primeiros passos com o ORM Pony, usando o SQLite como banco de dados.
Já no segundo texto desta coleção, criamos um pequeno conjunto de dados a partir de uma requisição à API do Github, e usamos em três bancos distintos: MySQL, Postgres e o bom e velho SQLite, usando docker.
Agora vamos fazer um monte de coisa errada para entender as situações de erro e estarmos preparados para programarmos essas situações.
O Laboratório
Partamos do laboratório construído no segundo texto acima referenciado. Por gosto pessoal, escolho o Postgres para conectar, mas os exemplos devem funcionar com qualquer um dos bancos, basta trocar a conexão (método bind
, conforme visto no segundo texto).
Em um terminal execute o comando docker-compose up --build
para subir os bancos e em outro terminal entremos na imagem Alpine, que contém o Python:
In [1]: import bombril
In [2]: import models
In [3]: db = models.db
In [4]: data = bombril.read_csv()
In [5]: db.bind(provider='postgres', user='postgres', password='top123', host='postgres', database='github')
In [6]: db.generate_mapping(create_tables=True)
Como vimos, tudo funcionando, que tal fazer algumas coisas erradas?
Começando pelo driver:
In [10]: db.bind(provider='postgress', user='postgres', password='top123', host='postgres', database='github')
... muitas linhas depois
ModuleNotFoundError: No module named 'pony.orm.dbproviders.postgress'
Errando o usuário:
In [11]: db.bind(provider='postgres', user='postgress', password='top123', host='postgres', database='github')
... muitas linhas depois
OperationalError: FATAL: password authentication failed for user "postgress"
Que por acaso gera o mesmo erro para uma senha errada:
In [12]: db.bind(provider='postgres', user='postgres', password='top123....', host='postgres', database='github')
... muitas linhas depois
OperationalError: FATAL: password authentication failed for user "postgres"
Errando o host:
In [13]: db.bind(provider='postgres', user='postgres', password='top123', host='postgress', database='github')
... muitas linhas depois
OperationalError: could not translate host name "postgress" to address: Name does not resolve
Finalmente, errando o nome do banco:
In [14]: db.bind(provider='postgres', user='postgres', password='top123', host='postgres', database='github..')
... muitas linhas depois
OperationalError: FATAL: database "github.." does not exist
O que nos leva a poder programar algo assim:
In [15]: from pony import orm
In [16]: try:
...: db.bind(provider='postgres', user='postgres', password='top123', host='postgres', database='github..')
...: except orm.OperationalError as e:
...: print(e)
...:
FATAL: database "github.." does not exist
Vamos arrumas as coisas:
In [17]: db.bind(provider='postgres', user='postgres', password='top123', host='postgres', database='github')
In [18]: db.generate_mapping(create_tables=True)
Pegar um registro pela chave primária:
In [19]: models.Github[1]
Out[19]: Github[1]
In [20]: models.Github[1].to_dict()
Out[20]: {'id': 1, 'key': 'current_user_url', 'value': 'https://api.github.com/user'}
E tentar pegar um registro que não existe:
In [21]: models.Github[99].to_dict()
... muitas linhas depois
ObjectNotFound: Github[99]
Temos mais uma exceção para nossa coleção. Sempre lembrando, elas "moram" em pony.orm
.
Outras formas de pesquisa
Uma forma de não gerar exceção e ficar no ifizinho é usar o get
:
In [22]: response = models.Github.get(id=99)
In [23]: response
In [24]: type(response)
Out[24]: NoneType
In [25]: response is None
Out[25]: True
Porém, caso haja mais que uma resposta, teremos o belo estouro de uma exceção:
In [26]: response = models.Github.get(lambda p: 'a' in p.key)
... muitas linhas depois
MultipleObjectsFoundError: Multiple objects were found. Use select(...) to retrieve them
Interessante que o erro já vem com um conselho, então sigamo-lo:
In [27]: response = models.Github.select(lambda p: 'a' in p.key)
In [28]: response.count()
Out[28]: 17
Ou uma busca mais real:
In [29]: response = models.Github.select(lambda p: 'user' in p.key)
In [30]: response.show()
id|key |value
--+------------------------------------+----------------------------------------
1 |current_user_url |https://api.github.com/user
2 |current_user_authorizations_html_url|https://github.com/settings/connectio...
26|current_user_repositories_url |https://api.github.com/user/repos{?ty...
29|user_url |https://api.github.com/users/{user}
30|user_organizations_url |https://api.github.com/user/orgs
31|user_repositories_url |https://api.github.com/users/{user}/r...
32|user_search_url |https://api.github.com/search/users?q...
O show()
é muito útil em casos de debug, e poderíamos ter feito essa consulta em uma só linha com:
models.Github.select(lambda p: 'user' in p.key).show()
O select
retorna um iterável, então, pode-se transformá-lo em uma lista, ou, antes verificar pela contagem do próprio Pony
:
In [31]: response = models.Github.select(lambda p: 'non exist' in p.key)
In [32]: response.count()
Out[32]: 0
E para fechar nosso guia básico de erros, que tal tentar incluir um registro com chave primária existente? Aqui o experimento:
In [33]: with orm.db_session:
...: models.Github(id=1,key='bla', value='ble')
... muitas linhas depois
CacheIndexError: Cannot create Github: instance with primary key 1 already exists
Acredito que com essa enfadonha e nada exaustiva lista com situações de erro, eu possa ter pego e passado o espírito do tratamento de erros no Pony
. O importante é ter a documentação em mãos e um terminal com seu laboratório para testar as diversas situações e poder defender-se desse mundo hostil que é a comunicação em rede.
Nunca nos esqueçamos desses princípios:
The Eight Fallacies of
Distributed Computing
Peter Deutsch
Essentially everyone,
when they first build a distributed application,
makes the following eight assumptions.
All prove to be false in the long run and all
cause big trouble and painful learning experiences.
1. The network is reliable
2. Latency is zero
3. Bandwidth is infinite
4. The network is secure
5. Topology doesn't change
6. There is one administrator
7. Transport cost is zero
8. The network is homogeneous
https://www.researchgate.net/publication/322500050FallaciesofDistributedComputing_Explained
Então, sempre na defesa!