Pony ORM yazarı burada.
Pony, Python oluşturucuyu üç adımda SQL sorgusuna çevirir:
- Oluşturucu bayt kodunun derlenmesi ve oluşturucu AST'nin yeniden oluşturulması (soyut sözdizimi ağacı)
- Python AST'nin "soyut SQL" e çevirisi - bir SQL sorgusunun evrensel liste tabanlı temsili
- Soyut SQL temsilini belirli bir veritabanına bağımlı SQL diyalektine dönüştürme
En karmaşık kısım, Pony'nin Python ifadelerinin "anlamını" anlaması gereken ikinci adımdır. Görünüşe göre en çok ilk adımla ilgileniyorsunuz, bu yüzden kod çözmenin nasıl çalıştığını açıklamama izin verin.
Bu sorguyu ele alalım:
>>> from pony.orm.examples.estore import *
>>> select(c for c in Customer if c.country == 'USA').show()
Hangisi aşağıdaki SQL'e çevrilecektir:
SELECT "c"."id", "c"."email", "c"."password", "c"."name", "c"."country", "c"."address"
FROM "Customer" "c"
WHERE "c"."country" = 'USA'
Aşağıda, yazdırılacak olan bu sorgunun sonucu yer almaktadır:
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
select()
Fonksiyon argüman olarak bir piton jeneratör kabul eder ve daha sonra onun baytkod analiz eder. Standart python dis
modülünü kullanarak bu jeneratörün bayt kodu talimatlarını alabiliriz :
>>> 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
Pony ORM, bayt kodundan bir AST'yi geri yükleyebilen decompile()
modül içinde işleve sahiptir pony.orm.decompiling
:
>>> from pony.orm.decompiling import decompile
>>> ast, external_names = decompile(gen)
Burada, AST düğümlerinin metinsel temsilini görebiliriz:
>>> ast
GenExpr(GenExprInner(Name('c'), [GenExprFor(AssName('c', 'OP_ASSIGN'), Name('.0'),
[GenExprIf(Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))]))])]))
Şimdi decompile()
fonksiyonun nasıl çalıştığını görelim .
decompile()
İşlevi oluşturur Decompiler
Ziyaretçi desen uygular nesneyi. Derleyici çözücü örneği, bayt kodu talimatlarını tek tek alır. Her komut için derleyici nesnesi kendi yöntemini çağırır. Bu yöntemin adı, mevcut bayt kodu talimatının ismine eşittir.
Python bir ifade hesapladığında, hesaplamanın ara sonucunu saklayan yığını kullanır. Derleyici nesnesinin de kendi yığını vardır, ancak bu yığın, ifade hesaplamasının sonucunu değil, ifade için AST düğümünü depolar.
Bir sonraki bayt kodu talimatı için derleyici çözme yöntemi çağrıldığında, AST düğümlerini yığından alır, bunları yeni bir AST düğümünde birleştirir ve ardından bu düğümü yığının en üstüne yerleştirir.
Örneğin, alt ifadenin nasıl c.country == 'USA'
hesaplandığını görelim . Karşılık gelen bayt kodu parçası:
9 LOAD_FAST 1 (c)
12 LOAD_ATTR 0 (country)
15 LOAD_CONST 0 ('USA')
18 COMPARE_OP 2 (==)
Dolayısıyla, derleyici nesnesi şunları yapar:
- Aramalar
decompiler.LOAD_FAST('c')
. Bu yöntem, Name('c')
düğümü decompiler yığınının en üstüne yerleştirir.
- Aramalar
decompiler.LOAD_ATTR('country')
. Bu yöntem, Name('c')
düğümü yığından alır, düğümü oluşturur Geattr(Name('c'), 'country')
ve onu yığının en üstüne yerleştirir.
- Aramalar
decompiler.LOAD_CONST('USA')
. Bu yöntem, Const('USA')
düğümü yığının en üstüne yerleştirir.
- Aramalar
decompiler.COMPARE_OP('==')
. Bu yöntem, yığından iki düğüm (Getattr ve Const) alır ve ardından yığının Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))])
en üstüne yerleştirir.
Tüm bayt kodu talimatları işlendikten sonra, derleme çözücü yığını, tüm jeneratör ifadesine karşılık gelen tek bir AST düğümü içerir.
Pony ORM'nin yalnızca jeneratörleri ve lambdaları yeniden derlemesi gerektiğinden, bu o kadar karmaşık değildir, çünkü bir jeneratör için komut akışı nispeten basittir - bu sadece bir grup iç içe döngüdür.
Şu anda Pony ORM, iki şey dışında tüm jeneratör talimat setini kapsar:
- Satır içi if ifadeler:
a if b else c
- Bileşik karşılaştırmaları:
a < b < c
Pony böyle bir ifadeyle karşılaşırsa, NotImplementedError
istisnayı ortaya çıkarır . Ancak bu durumda bile, üreteç ifadesini bir dizge olarak ileterek çalışmasını sağlayabilirsiniz. Bir üreteci dizge olarak ilettiğinizde Pony, kod çözücü modülünü kullanmaz. Bunun yerine standart Python compiler.parse
işlevini kullanarak AST alır .
Umarım bu sorunuzu yanıtlar.
p
nesne, Pony tarafından uygulanan ve üzerinde hangi yöntemlere / özelliklere erişildiğine (örneğinname
,startswith
) bakan ve bunları SQL'e dönüştüren türde bir nesnedir .