Poney ORM auteur est ici.
Poney traduit Python générateur dans la requête SQL en trois étapes:
- La décompilation de générateur de bytecode et la reconstruction de générateur AST
(arbre de syntaxe abstraite)
- Traduction de Python AST dans "résumé" SQL " - universal
de liste à la représentation d'une requête SQL
- La conversion résumé SQL représentation spécifique
base de données dépendant de dialecte SQL
La partie la plus complexe est la deuxième étape, où le Poney doit
comprendre le "sens" d'expressions Python. Semble que vous êtes plus
intéressé dans la première étape, alors laissez-moi vous expliquer comment la décompilation des œuvres.
Nous allons examiner cette requête:
>>> from pony.orm.examples.estore import *
>>> select(c for c in Customer if c.country == 'USA').show()
Qui sera traduit dans le code SQL suivant:
SELECT "c"."id", "c"."email", "c"."password", "c"."name", "c"."country", "c"."address"
FROM "Customer" "c"
WHERE "c"."country" = 'USA'
Et ci-dessous est le résultat de cette requête qui sera imprimé:
id|email |password|name |country|address
--+-------------------+--------+--------------+-------+---------
1 |john@example.com |*** |John Smith |USA |address 1
2 |matthew@example.com|*** |Matthew Reed |USA |address 2
4 |rebecca@example.com|*** |Rebecca Lawson|USA |address 4
L' select()
fonction accepte un python générateur comme argument, et analyse ensuite son bytecode.
Nous pouvons obtenir des instructions bytecode de ce générateur à l'aide de standards de python, dis
module de:
>>> gen = (c for c in Customer if c.country == 'USA')
>>> import dis
>>> dis.dis(gen.gi_frame.f_code)
1 0 LOAD_FAST 0 (.0)
>> 3 FOR_ITER 26 (to 32)
6 STORE_FAST 1 (c)
9 LOAD_FAST 1 (c)
12 LOAD_ATTR 0 (country)
15 LOAD_CONST 0 ('USA')
18 COMPARE_OP 2 (==)
21 POP_JUMP_IF_FALSE 3
24 LOAD_FAST 1 (c)
27 YIELD_VALUE
28 POP_TOP
29 JUMP_ABSOLUTE 3
>> 32 LOAD_CONST 1 (None)
35 RETURN_VALUE
Poney ORM a la fonction decompile()
dans le module pony.orm.decompiling
qui peut
la restauration d'un AST à partir du bytecode:
>>> from pony.orm.decompiling import decompile
>>> ast, external_names = decompile(gen)
Ici, on peut voir la représentation textuelle de l'AST nœuds:
>>> ast
GenExpr(GenExprInner(Name('c'), [GenExprFor(AssName('c', 'OP_ASSIGN'), Name('.0'),
[GenExprIf(Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))]))])]))
Nous allons maintenant voir comment l' decompile()
fonctionne.
L' decompile()
fonction crée un Decompiler
objet qui implémente le modèle Visiteur.
Le decompiler instance bénéficie d'un bytecode instructions une par une.
Pour chaque instruction de la decompiler objet d'appels de sa propre méthode.
Le nom de cette méthode est égal au nom de l'actuel code binaire de l'instruction.
Quand Python calcule une expression, il utilise la pile, qui stocke un intermédiaire
résultat de calcul. Le decompiler objet a également sa propre pile,
mais cette pile stocke pas le résultat de l'expression de calcul,
mais le nœud de l'AST pour l'expression.
Lorsque decompiler méthode pour la prochaine instruction bytecode est appelée,
il prend les nœuds de l'AST à partir de la pile, les combine
dans un nouveau nœud de l'AST, et met alors ce nœud sur le haut de la pile.
Par exemple, nous allons voir comment la sous-expression c.country == 'USA'
est calculé. L'
correspondant bytecode fragment est:
9 LOAD_FAST 1 (c)
12 LOAD_ATTR 0 (country)
15 LOAD_CONST 0 ('USA')
18 COMPARE_OP 2 (==)
Ainsi, le decompiler objet est le suivant:
- Appels
decompiler.LOAD_FAST('c')
.
Cette méthode met l' Name('c')
noeud sur le haut de la decompiler pile.
- Appels
decompiler.LOAD_ATTR('country')
.
Cette méthode prend l' Name('c')
nœud de la pile,
crée l' Geattr(Name('c'), 'country')
nœud et le pose sur le haut de la pile.
- Appels
decompiler.LOAD_CONST('USA')
.
Cette méthode met l' Const('USA')
noeud sur le dessus de la pile.
- Appels
decompiler.COMPARE_OP('==')
.
Cette méthode prend deux nœuds (Getattr et Const) à partir de la pile,
et puis met Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))])
sur le haut de la pile.
Après tout bytecode instructions sont traitées, le decompiler pile contient
un seul nœud de l'AST qui correspond à l'ensemble du générateur d'expression.
Depuis Poney ORM besoins de décompiler des générateurs
et les lambdas seulement, ce qui n'est pas complexe, parce que
l'instruction de flux pour un générateur est relativement simple
- c'est juste un tas de boucles imbriquées.
Actuellement Poney ORM couvre l'ensemble du générateur d'instructions à l'exception de deux choses:
- Inline si les expressions:
a if b else c
- Composé de comparaisons:
a < b < c
Si le Poney rencontre une telle expression, il soulève l' NotImplementedError
d'exception. Mais, même dans
ce cas, vous pouvez le faire fonctionner en passant par le générateur d'expression comme une chaîne de caractères.
Lorsque vous passez un générateur comme une chaîne de Poney ne pas utiliser le decompiler module. Au lieu de cela
il obtient l'AST en utilisant le standard de Python compiler.parse
fonction.
Espérons que cela répond à votre question.