Cette librarie offre un client Constellation pour Python. Elle fonctionne en lançant un serveur ws Constellation local, avec lequel elle gère ensuite la communication par websocket.
Vous pouvez installer ConstellationPy avec poetry
:
poetry add constellationPy
... ou bien avec pip
pip install constellationPy
Si le serveur Constellation n'est pas déjà installé sur votre machine, ConstellationPy l'installera automatiquement pour vous. Pour ce faire, vous devrez au tout minimum avoir Node.js installé localement.
ConstellationPy est une libraries asynchrone basée sur trio. Étant donné que le serveur Constellation est fondamentalement asynchrone aussi, c'était la décision naturelle.
Cependant, nous comprenons bien que la grande majorité des utilisatrices et utilisateurs de Python n'ont aucune idée de ce qu'est la programmation asynchrone, ni aucun goût ou raison de l'apprendre. C'est pour cela que ConstellationPy vous offre également un IPA synchrone.
Vous ne savez pas ce que « synchrone » ou « asynchrone » veulent dire ? Ne vous en faites pas et utilisez l'IPA synchrone. « Synchrone » est le terme technique pour le style de code « normal » Python que vous connaissez bien. Si vous voulez en savoir plus, voici une belle présentation de la différence entre les deux (en JavaScript).
Attention ! L'IPA synchrone fonctionne bien pour des petites tâches (p. ex., récupérer un ou deux jeux de données), mais
l'IPA asynchrone est beaucoup plus efficace si vous traitez de grands nombres de données ou de requêtes à Constellation.
Si vous avez besoin d'accéder beaucoup de différentes bases de données Constellation, peut-être que ça vaudrait la
peine, après tout,
d'apprendre comment utiliser ces drôles de async
et await
en
Python.
En premier lieu, nous devons lancer le serveur Constellation. C'est absolument nécessaire, à moins que vous n'aviez déjà lancé un serveur Constellation manuellement, lorsque, par exemple, vous voulez exécuter plusieurs codes Python qui utilisent Constellation en parallèle sans dupliquer le serveur (oui, c'est bien possible) !
Donc, on commence. La façon la plus sure, c'est d'utiliser un bloc with
, car celui-ci fermera automatiquement le
serveur une fois que vous aurez terminé avec. Cette syntaxe permettra aussi au client Constellation de détecter
automatiquement le port auquel il devra se connecter.
from constellationPy import Serveur, ClientSync
with Serveur():
client = ClientSync()
données = client.appelerUneFonction()
...
Vous pouvez aussi lancer le client Constellation manuellement. Cette option est particulièrement utile si vous voulez obtenir les données les plus à jour du réseau. N'oubliez pas que Constellation est un réseau d'utilisatrices et d'utilisateurs comme vous ! Il n'y a donc pas de « serveur central » Constellation en tant que tel pour garder une copie des données. Si les données qui vous intéressent sont sur l'ordinateur ou le téléphone d'une autre participante au réseau, cela peut prendre un peu de temps pour que votre nœud local puisse se connecter à la nuée d'autres nœuds Constellation et reçoive les données les plus récentes. Comme règle générale, le plus longtemps le nœud reste en ligne, le plus de connexions et de données il obtiendra.
Vous pouvez donc lancer votre nœud local à l'aide de la ligne de commande. Vous pouvez utiliser n'importe quel port libre (ici 5001). Vous pouvez le laisser rouler aussi longtemps que vous voudrez, il y se synchronisera automatiquement avec le réseau Constellation. Tout client pyConstellation que vous lancerez en même temps obtiendra ainsi les données les plus à jour disponibles.
Note : pour installer Constellation pour la première fois, faites rouler le code suivant une seule fois sur votre ordinateur :
from constellationPy import mettre_constellation_à_jour
mettre_constellation_à_jour()
Vous pourrez ensuite invoquer le serveur Constellation ainsi :
constl lancer --port 5001 -b
Vous ne savez pas quel port mettre ? Lancez tout simplement constl lancer
et puis Constellation
vous donnera le numéro du port libre qu'elle aura trouvé.
Vu que vous avez déjà lancé votre propre serveur Constellation, vous devrez spécifier le port manuellement dans le client :
from constellationPy import ClientSync
client = ClientSync(port=5001)
...
Note : vous pouvez également spécifier le port du client sur Client
et ouvrir_client
(voir ci-dessous).
Toutes* les fonctions de l'IPA (Interface de programmation d'application) Constellation sont disponibles.
Note : vous pouvez appeler les fonctions Constellation en forme kebab (ma_fonction
, style Python)
ou bien chameau (maFonction
, style JavaScript). À titre d'exemple :
from constellationPy import ClientSync, Serveur
with Serveur():
client = ClientSync()
résultatChameau = client.obtIdDispositif()
résultat_kebab = client.obt_id_dispositif()
print(résultatChameau == résultat_kebab)
Vous pouvez également accéder les sous-objets de Constellation (profil
, bds
, tableaux
, et ainsi de suite) :
from constellationPy import ClientSync, Serveur
with Serveur():
client = ClientSync()
client.profil.sauvegarder_nom(langue="fr", nom="moi !")
client.bds.créer_bd(licence="ODbl-1.0")
L'IPA du client Python vous offre aussi quelques fonctions plus commodes qui n'existent pas dans l'IPA original de Constellation :
from constellationPy import ClientSync, Serveur
id_tableau = "/orbitdb/zdpu..."
with Serveur():
client = ClientSync()
données = client.obt_données_tableau(id_tableau=id_tableau)
Vous pouvez également obtenir les données en format Pandas, y compris avec les colonnes identifiées selon le nom des variables correspondantes dans la langue de votre choix, si elle est disponible :
from constellationPy import ClientSync, Serveur
id_tableau = "/orbitdb/zdpu..."
with Serveur():
client = ClientSync()
données = client.obt_données_tableau(id_tableau=id_tableau, langues=["fr", "es"], formatDonnées="pandas")
Quelques points importants
- Les fonctions plus obscures qui prennent plus qu'une autre fonction comme argument (p.
ex.
client.suivreBdDeFonction
) ne fonctionnent pas avec le client Python. Ne vous en faites pas. Elles sont obscures pour une raison. Laissez-les en paix. Vous avez amplement de quoi vous amuser avec le reste de l'IPA. - Vous devez utiliser des paramètres nommés (p. ex.,
client.bds.créerBd(licence="ODbl-1.0")
). Si vous ne le faites pas (client.bds.créerBd("ODbl-1.0")
), ça va vous créer des ennuis. Les noms des paramètres doivent être les mêmes que dans l'IPA Constellation JavaScript (p. ex., l'exemple précédent provient de la version JavaSCriptclient.bds.créerBd({ licence: "ODbl-1.0" })
). - Avec le client synchrone, les fonctions de suivi (voir ci-dessous) doivent être appelées avec une fonction vide (p.
ex.,
lambda: pass
ou bien tout simplementfais_rien
) à la place de la fonction de suivi. - Vous vous demandez où trouver tous ces drôles de « id tableau » pour les bases de données qui vous intéressent ? Il s'agit de l'identifiant unique d'un tableau ou d'une base de données, que vous pouvez récupérer lorsque vous créez la base de données, ou bien visuellement avec l'appli Constellation (recherchez l'icône lien 🔗).
Constellation, dans sa version asynchrone JavaScript, offre des fonctions qui, plutôt que de rendre le résultat
immédiatement, suivent le résultat à travers le temps et vous notifient (selon une fonction que vous choisissez)
chaque fois que le résultat change. La grande majorité des fonctions utiles de l'IPA de Constellation (p.
ex., client.tableaux.suivreDonnées
) sont de ce genre.
Évidemment, ce comportement n'est pas util dans un programme synchrone. Le client synchrone ClientSync
s'occupe donc de vous rendre le résultat, sans tracas. Il vous suffira de passer une fonction vide là où la fonction
originale s'attendait à avoir la fonction de suivi. Par exemple, si l'on appellerait la fonction comme suit dans
Constellation JavaScript,
const données = await client.tableaux.suivreDonnées({ idTableau: id_tableau, f: fSuivi });
Ici, en Python, nous ferons ainsi :
from constellationPy import ClientSync, Serveur, fais_rien
id_tableau = "/orbitdb/zdpu..."
with Serveur():
client = ClientSync()
mes_données = client.tableaux.suivre_données(id_tableau=id_tableau, f=fais_rien)
L'IPA asynchrone doit être utilisée avec trio. Il a les mêmes fonctions que l'IPA
synchrone, mais dois être invoqué dans un bloc async with ouvrir_client() as client:
import trio
from constellationPy import Serveur, ouvrir_client
id_tableau = "/orbitdb/zdpu..."
async def principale():
with Serveur():
async with ouvrir_client() as client:
données = await client.obt_données_tableau(id_tableau=id_tableau)
print(données)
...
trio.run(principale)
Tel que mentionné ci-dessus, la majorité des fonctions utiles de Constellation sont des fonctions de suivi. Nous devons les appeler avec une fonction qui sera invoquée à chaque fois que le résultat sera mis à jour.
import trio
from constellationPy import Serveur, ouvrir_client
id_tableau = "/orbitdb/zdpu..."
async def principale():
with Serveur():
async with ouvrir_client() as client:
# Suivre les données du réseau pour 15 secondes, et imprimer les résultats au fur et à mesure
# qu'ils nous parviennent du réseau
oublier_données = await client.tableaux.suivre_données(id_tableau=id_tableau, f=print)
await trio.sleep(15)
oublier_données() # Maintenant, on ne recevra plus les mises à jour des données
trio.run(principale)
Mais en Python, il est probable que, au lieu de vouloir suivre le résultat de la fonction à travers le temps, vous
préférerez obtenir les données présentes et puis poursuivre vos analyses. La fonction une_fois
vous permet de faire justement cela :
import trio
from constellationPy import Serveur, ouvrir_client, une_fois
id_tableau = "/orbitdb/zdpu..."
async def principale():
with Serveur():
async with ouvrir_client() as client:
# On doit définir une fonction auxiliaire qui ne prend que la fonction de suivi
# en tant qu'argument
async def f_données(f):
return await client.tableaux.suivre_données(id_tableau=id_tableau, f=f)
# La fonction `une_fois` appellera `f_données`, attendra le premier résultat,
# et nous renverra celui-ci.
données = await une_fois(f_données, client)
return données
mes_données = trio.run(principale)
print(mes_données)
Voici un exemple un peu plus avancé. Si vous avez plusieurs coroutines Python que vous voulez exécuter en parallèle avec
Constellation, vous pouvez créer une pouponnière trio
et la réutiliser pour les deux coroutines en invoquant Client
directement.
import trio
from constellationPy import Client
résultats = {}
async def coroutine1(client):
données = await client.appelerUneFonction()
résultats["1"] = données
async def coroutine2(client):
données = await client.appelerUneFonction()
résultats["2"] = données
async def principale():
async with trio.open_nursery() as pouponnière:
async with Client(pouponnière) as client:
await client.connecter() # À ne pas oublier ! Sinon je ne suis pas responsable.
pouponnière.start_soon(coroutine1, client)
pouponnière.start_soon(coroutine2, client)
trio.run(principale)
print(résultats)
Ceci peut aussi être utile avec
les canaux
de trio
pour communiquer entre les coroutines :
import trio
from constellationPy import Client
id_tableau = "/orbitdb/zdpu..."
async def coroutine_constellation(pouponnière, canal_envoie):
async with Client(pouponnière) as client:
await client.connecter() # À ne pas oublier ! Sinon je ne suis pas responsable.
données = await client.obt_données_tableau(id_tableau=id_tableau)
async with canal_envoie:
await canal_envoie.send(données)
async def une_autre_coroutine(canal_réception):
async with canal_réception:
async for message in canal_réception:
print(message) # En réalité, faire quelque chose d'asynchrone, comme écrire au disque
async def principale():
async with trio.open_nursery() as pouponnière:
canal_envoie, canal_réception = trio.open_memory_channel(0)
pouponnière.start_soon(coroutine_constellation, pouponnière, canal_envoie)
pouponnière.start_soon(une_autre_coroutine, canal_réception)
trio.run(principale)
Vous pouvez aussi initialiser Client
avec un canal trio
pour recevoir les erreurs. Si le client ou le serveur
encontre une erreur, celle-ci sera envoyée au canal erreur au lieu de soulever une erreur et d'arrêter exécution du
programme. Cette option peut être utile lorsque vous ne voulez pas qu'une erreur sur une requête arrête toute
l'exécution du logiciel.
import trio
from constellationPy import Serveur, Client
async def coroutine_client(pouponnière, canal_envoie_erreur):
async with canal_envoie_erreur:
async with Client(pouponnière) as client:
await client.connecter(canal_envoie_erreur) # À ne pas oublier ! Sinon je ne suis pas responsable.
# Faire quelque chose avec le client
async def coroutine_erreurs(canal_reçoie_erreurs):
async with canal_reçoie_erreurs:
async for erreur in canal_reçoie_erreurs:
print(erreur) # Ou écrire à un fichier journal sur le disque...
async def principale():
with Serveur():
async with trio.open_nursery() as pouponnière:
canal_envoie_erreur, canal_reçoie_erreur = trio.open_memory_channel(0)
pouponnière.start_soon(coroutine_client, pouponnière, canal_envoie_erreur)
pouponnière.start_soon(coroutine_erreurs, canal_reçoie_erreur)
trio.run(principale)