1. Python'ı Başka Bir Uygulamaya Gömme
**************************************

Önceki bölümlerde Python'un nasıl genişletileceği, yani Python'a bir C
fonksiyonları kitaplığı ekleyerek Python'un işlevselliğinin nasıl
genişletileceği tartışıldı. Bunu ayrıca tam tersi şekilde de yapmak
mümkündür: Python'u içine gömerek C/C++ uygulamanızı zenginleştirin.
Gömme işlemi, uygulamanıza bazı işlevlerini C veya C++ yerine
Python'da uygulama yeteneği sağlar. Bu birçok amaç için
kullanılabilir; bir örnek olarak kullanıcıların Python'da bazı komut
dosyaları yazarak uygulamayı ihtiyaçlarına göre uyarlamalarına izin
vermek olabilir. Bazı işlevler Python'da daha kolay yazılabilecekse
kendiniz de kullanabilirsiniz.

Python'u gömmek, onu genişletmeye benzer, ancak tam olarak değil.
Aralarındaki farksa Python'u genişlettiğinizde, uygulamanın ana
programının hala Python yorumlayıcısı olması; Python'u gömerseniz, ana
programın Python ile hiçbir ilgisi olmayabilmesidir --- bunun yerine,
uygulamanın bazı bölümleri bazı Python kodlarını çalıştırmak için
zaman zaman Python yorumlayıcısını çağırır.

Yani Python'u gömüyorsanız, kendi ana programınızı sağlıyorsunuz
demektir. Bu ana programın yapması gereken şeylerden biri Python
yorumlayıcısını başlatmaktır. En azından "Py_Initialize()"
fonksiyonunu çağırmalısınız. Python'a komut satırı argümanlarını
iletmek için opsiyonel çağrılar vardır. Daha sonra uygulamanın
herhangi bir yerinden yorumlayıcıyı çağırabilirsiniz.

Yorumlayıcıyı çağırmanın birkaç farklı yolu vardır: Python deyimlerini
içeren bir dizeyi "PyRun_SimpleString()" öğesine veya bir stdio dosya
işaretçisini ve bir dosya adını (yalnızca hata mesajlarında tanımlama
için) "PyRun_SimpleFile()" 'a iletebilirsiniz. Python nesnelerini
oluşturmak ve kullanmak için önceki bölümlerde açıklanan alt düzey
işlemleri de çağırabilirsiniz.

Ayrıca bakınız:

  Python/C API Referans Kılavuzu
     Python'un C arayüzünün detayları bu kılavuzda verilmiştir. Çok
     sayıda gerekli bilgi burada bulunabilir.


1.1. Çok Üst Düzey Gömme
========================

Python'u gömmenin en basit şekli, çok yüksek seviyeli arayüzün
kullanılmasıdır. Bu arabirim, uygulamayla doğrudan etkileşime girmeye
gerek kalmadan bir Python betiği yürütmeyi amaçlamaktadır. Bu örnek
olarak bir dosya üzerinde bazı işlemler gerçekleştirmek için
kullanılabilir.

   #define PY_SSIZE_T_CLEAN
   #include <Python.h>

   int
   main(int argc, char *argv[])
   {
       wchar_t *program = Py_DecodeLocale(argv[0], NULL);
       if (program == NULL) {
           fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
           exit(1);
       }
       Py_SetProgramName(program);  /* optional but recommended */
       Py_Initialize();
       PyRun_SimpleString("from time import time,ctime\n"
                          "print('Today is', ctime(time()))\n");
       if (Py_FinalizeEx() < 0) {
           exit(120);
       }
       PyMem_RawFree(program);
       return 0;
   }

"Py_SetProgramName()" işlevi, yorumlayıcıyı Python çalışma zamanı
kütüphanelerine giden yollar hakkında bilgilendirmek için
"Py_Initialize()" öncesinde çağrılmalıdır. Ardından, Python
yorumlayıcısı "Py_Initialize()" ile başlatılır, ardından tarih ve
saati yazdıran sabit kodlanmış bir Python betiği yürütülür. Daha
sonra, "Py_FinalizeEx()" çağrısı yorumlayıcıyı kapatır ve ardından
programın sonu gelir. Gerçek bir programda, Python betiğini başka bir
kaynaktan, belki bir metin düzenleyici rutininden, bir dosyadan veya
bir veri tabanından almak isteyebilirsiniz. Python kodunu bir dosyadan
almak, sizi bellek alanı ayırma ve dosya içeriğini yükleme zahmetinden
kurtaran "PyRun_SimpleFile()" işlevi kullanılarak daha iyi
yapılabilir.


1.2. Çok Yüksek Düzeyde Gömmenin Ötesinde: Genel Bir Bakış
==========================================================

Yüksek seviyeli arayüz size uygulamanızdan rastgele Python kodu
parçalarını yürütme yeteneği verir, ancak veri değerleri alışverişi en
hafif tabirle oldukça zahmetlidir. Bunu istiyorsanız, daha düşük
seviyeli aramalar kullanmalısınız. Daha fazla C kodu yazmak zorunda
kalma pahasına neredeyse her şeyi başarabilirsiniz.

Farklı amaçlara rağmen Python'u genişletmek ve Python'u gömmek tamamen
aynı aktivitedir. Önceki bölümlerde tartışılan konuların çoğu hala
geçerlidir. Bunu göstermek için Python'dan C'ye uzantı kodunun
gerçekten ne yaptığını düşünün:

1. Veri değerlerini Python'dan C'ye çevirin,

2. Çevrilen değerleri kullanarak bir C rutinine bir fonksiyon çağrısı
   yapın, ve

3. Çağrıdaki veri değerlerini C'den Python'a çevirin.

Python'u yerleştirirken, arayüz kodu şunları yapar:

1. Veri değerlerini C'den Python'a çevirin,

2. Çevrilen değerleri kullanarak bir Python arabirim rutinine bir
   fonksiyon çağrısı gerçekleştirin ve

3. Çağrıdaki veri değerlerini C'den Python'a dönüştürün.

Gördüğünüz gibi, veri dönüştürme adımları, diller arası aktarımın
farklı yönüne uyum sağlamak için basitçe değiştirilir. Tek fark, her
iki veri dönüşümü arasında çağırdığınız rutindir. Uzatırken C rutini
çağırırsınız, gömerken Python rutini çağırırsınız.

Bu bölüm, verilerin Python'dan C'ye nasıl dönüştürüleceğini ve bunun
tam tersini tartışmayacaktır. Ayrıca, referansların doğru kullanımı ve
hataların ele alınmasının anlaşıldığı varsayılmaktadır. Bu hususlar,
yorumlayıcının genişletilmesinden farklı olmadığı için, gerekli
bilgiler için önceki bölümlere başvurabilirsiniz.


1.3. Saf Gömme
==============

İlk program, bir Python betiğinde bir fonksiyonu çalıştırmayı amaçlar.
Çok yüksek seviyeli arayüzle ilgili bölümde olduğu gibi, Python
yorumlayıcısı uygulama ile doğrudan etkileşime girmez (ancak bu bir
sonraki bölümde değişecektir).

Python betiğinde tanımlanan bir işlevi çalıştırma kodu:

   #define PY_SSIZE_T_CLEAN
   #include <Python.h>

   int
   main(int argc, char *argv[])
   {
       PyObject *pName, *pModule, *pFunc;
       PyObject *pArgs, *pValue;
       int i;

       if (argc < 3) {
           fprintf(stderr,"Usage: call pythonfile funcname [args]\n");
           return 1;
       }

       Py_Initialize();
       pName = PyUnicode_DecodeFSDefault(argv[1]);
       /* Error checking of pName left out */

       pModule = PyImport_Import(pName);
       Py_DECREF(pName);

       if (pModule != NULL) {
           pFunc = PyObject_GetAttrString(pModule, argv[2]);
           /* pFunc is a new reference */

           if (pFunc && PyCallable_Check(pFunc)) {
               pArgs = PyTuple_New(argc - 3);
               for (i = 0; i < argc - 3; ++i) {
                   pValue = PyLong_FromLong(atoi(argv[i + 3]));
                   if (!pValue) {
                       Py_DECREF(pArgs);
                       Py_DECREF(pModule);
                       fprintf(stderr, "Cannot convert argument\n");
                       return 1;
                   }
                   /* pValue reference stolen here: */
                   PyTuple_SetItem(pArgs, i, pValue);
               }
               pValue = PyObject_CallObject(pFunc, pArgs);
               Py_DECREF(pArgs);
               if (pValue != NULL) {
                   printf("Result of call: %ld\n", PyLong_AsLong(pValue));
                   Py_DECREF(pValue);
               }
               else {
                   Py_DECREF(pFunc);
                   Py_DECREF(pModule);
                   PyErr_Print();
                   fprintf(stderr,"Call failed\n");
                   return 1;
               }
           }
           else {
               if (PyErr_Occurred())
                   PyErr_Print();
               fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
           }
           Py_XDECREF(pFunc);
           Py_DECREF(pModule);
       }
       else {
           PyErr_Print();
           fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
           return 1;
       }
       if (Py_FinalizeEx() < 0) {
           return 120;
       }
       return 0;
   }

Bu kod, "argv[1]" kullanarak bir Python betiği yükler ve "argv[2]"
içinde adlandırılan fonksiyonu çağırır. Tamsayı argümanları, "argv"
dizisinin diğer değerleridir. Bu programı derler,  ve bağlarsanız
(bitmiş yürütülebilir dosyayı **call** olarak adlandıralım) ve onu
aşağıdaki gibi bir Python betiğini çalıştırmak için kullanırsanız:

   def multiply(a,b):
       print("Will compute", a, "times", b)
       c = 0
       for i in range(0, a):
           c = c + b
       return c

o zaman sonuç olmalıdır:

   $ call multiply multiply 3 2
   Will compute 3 times 2
   Result of call: 6

Program işlevselliği açısından oldukça büyük olmasına rağmen, kodun
çoğu Python ve C arasında veri dönüştürme ve hata raporlama içindir.
Python'u gömmekle ilgili ilginç kısım şununla başlar

   Py_Initialize();
   pName = PyUnicode_DecodeFSDefault(argv[1]);
   /* Error checking of pName left out */
   pModule = PyImport_Import(pName);

Yorumlayıcıyı başlattıktan sonra komut dosyası "PyImport_Import()"
kullanılarak yüklenir. Bu rutin, argümanı olarak
"PyUnicode_FromString()" veri dönüştürme rutini kullanılarak
oluşturulan bir Python dizesine ihtiyaç duyar.

   pFunc = PyObject_GetAttrString(pModule, argv[2]);
   /* pFunc is a new reference */

   if (pFunc && PyCallable_Check(pFunc)) {
       ...
   }
   Py_XDECREF(pFunc);

Komut dosyası yüklendikten sonra, aradığımız ad
"PyObject_GetAttrString()" kullanılarak alınır. Ad varsa ve döndürülen
nesne çağrılabilirse bunun bir fonksiyon olduğunu güvenle
varsayabilirsiniz. Program daha sonra normal olarak bir dizi argüman
oluşturarak devam eder. Python işlevine yapılan çağrı şu şekilde
yapılır:

   pValue = PyObject_CallObject(pFunc, pArgs);

Fonksiyon döndürüldüğünde, "pValue" ya "NULL" olur ya da fonksiyonun
dönüş değerine bir başvuru içerir. Değeri inceledikten sonra referans
bıraktığınızdan emin olun.


1.4. Gömülü Python'u Genişletme
===============================

Şimdiye kadar, gömülü Python yorumlayıcısının uygulamanın kendisinden
işlevselliğe erişimi yoktu. Python API, gömülü yorumlayıcıyı
genişleterek buna izin verir. Yani, gömülü yorumlayıcı, uygulama
tarafından sağlanan rutinlerle genişletilir. Kulağa karmaşık gelse de,
o kadar da kötü değil. Uygulamanın Python yorumlayıcısını başlattığını
bir süreliğine unutun. Bunun yerine, uygulamayı bir dizi altyordam
olarak düşünün ve tıpkı normal bir Python uzantısı yazacağınız gibi,
Python'un bu rutinlere erişmesini sağlayan bir tutkal kodu yazın.
Örneğin:

   static int numargs=0;

   /* Return the number of arguments of the application command line */
   static PyObject*
   emb_numargs(PyObject *self, PyObject *args)
   {
       if(!PyArg_ParseTuple(args, ":numargs"))
           return NULL;
       return PyLong_FromLong(numargs);
   }

   static PyMethodDef EmbMethods[] = {
       {"numargs", emb_numargs, METH_VARARGS,
        "Return the number of arguments received by the process."},
       {NULL, NULL, 0, NULL}
   };

   static PyModuleDef EmbModule = {
       PyModuleDef_HEAD_INIT, "emb", NULL, -1, EmbMethods,
       NULL, NULL, NULL, NULL
   };

   static PyObject*
   PyInit_emb(void)
   {
       return PyModule_Create(&EmbModule);
   }

Yukarıdaki kodu "main()" fonksiyonunun hemen üstüne ekleyin. Ayrıca,
"Py_Initialize()": çağrısından önce aşağıdaki iki ifadeyi ekleyin:

   numargs = argc;
   PyImport_AppendInittab("emb", &PyInit_emb);

These two lines initialize the "numargs" variable, and make the
"emb.numargs()" function accessible to the embedded Python
interpreter. With these extensions, the Python script can do things
like

   import emb
   print("Number of arguments", emb.numargs())

Gerçek bir uygulamada, yöntemler uygulamanın API'sini Python'a
gösterir.


1.5. Python'u C++'a Gömmek
==========================

Python'u bir C++ programına yerleştirmek de mümkündür; bunun tam
olarak nasıl yapılacağı, kullanılan C++ sisteminin ayrıntılarına bağlı
olacaktır; genel olarak ana programı C++ ile yazmanız ve programınızı
derlemek ve bağlamak için C++ derleyicisini kullanmanız gerekecektir.
Python'un kendisini C++ kullanarak yeniden derlemeye gerek yoktur.


1.6. Unix benzeri sistemler altında Derleme ve Bağlama
======================================================

Python yorumlayıcısını uygulamanıza gömmek için derleyicinize (ve
bağlayıcınıza) iletilecek doğru bayrakları bulmak mutlaka önemli
değildir, özellikle Python'un C dinamik uzantıları buna karşı
bağlatılı olan (".so"  dosyaları) olarak uygulanan kütüphane
modüllerini yüklemesi gerektiğinden.

Gerekli derleyici ve bağlayıcı bayraklarını bulmak için, yükleme
işleminin bir parçası olarak oluşturulan "python*X.Y*-config" betiğini
çalıştırabilirsiniz (bir "python3-config" betiği de mevcut olabilir).
Bu komut dosyası, size doğrudan yardımcı olacak birkaç seçeneğe
sahiptir:

* "pythonX.Y-config --cflags" derleme sırasında size önerilen
  bayrakları verecektir:

     $ /opt/bin/python3.11-config --cflags
     -I/opt/include/python3.11 -I/opt/include/python3.11 -Wsign-compare  -DNDEBUG -g -fwrapv -O3 -Wall

* "pythonX.Y-config --ldflags --embed", bağlantı kurarken size
  önerilen bayrakları verecektir:

     $ /opt/bin/python3.11-config --ldflags --embed
     -L/opt/lib/python3.11/config-3.11-x86_64-linux-gnu -L/opt/lib -lpython3.11 -lpthread -ldl  -lutil -lm

Not:

  Birkaç Python kurulumu arasında (ve özellikle sistem Python ile
  kendi derlenmiş Python'unuz arasında) karışıklığı önlemek için,
  yukarıdaki örnekte olduğu gibi mutlak "python*X.Y*-config" yolunu
  kullanmanız önerilir.

Bu prosedür sizin için işe yaramazsa (tüm Unix benzeri platformlar
için çalışması garanti edilmez; ancak, memnuniyetle karşılıyoruz: hata
raporları) dinamik hakkında sisteminizin belgelerini okumanız
gerekecektir ve/veya  Python'un "Makefile" (konumunu bulmak için
"sysconfig.get_makefile_filename()" kullanın) ve derleme seçeneklerini
bağlamayı inceleyin. Bu durumda, "sysconfig" modülü, birleştirmek
isteyeceğiniz konfigürasyon değerlerini programlı olarak çıkarmak için
kullanışlı bir araçtır. Örneğin:

   >>> import sysconfig
   >>> sysconfig.get_config_var('LIBS')
   '-lpthread -ldl  -lutil'
   >>> sysconfig.get_config_var('LINKFORSHARED')
   '-Xlinker -export-dynamic'
