Środowisko pytigon posiada dołączony kompilator, pozwalający na tworzenie modułów rozszerzających możliwości systemu w językach C i ZIG. Są 3 głowne metody rozszerzania możliwości systemu Pytigon w oparciu o dołączony kompilator:
Jest to polecany sposób, powinien być łatwy w tworzeniu a następnie łatwy w instalacji na komputerach użytkowników.
Przykład takiego rozszerzenia znajduje się w programie demonstracyjnym, projekt schpytigondemo, aplikacja interfaces. W folderze applib tworzymy folder biblioteki cffi, w przykładzie demonstracyjnym jest to cffi_add. W folderze tym umieszczamy plik build.py, w wersji demo wygląda on tak:
import os
import sys
import shutil
from cffi import FFI
os.environ["CC"] = sys.executable + " -m ziglang cc"
ffibuilder = FFI()
ffibuilder.cdef("int add(int a, int b);")
ffibuilder.set_source(
"cffi_add",
'#include "add.h"',
sources=["./add.c"],
)
ffibuilder.compile(tmpdir="./src")
l = os.listdir("./src")
for item in l:
if item.endswith(".so") or item.endswith(".dll"):
shutil.copy(os.path.join("./src", item), "..")
Przykładowy projekt zawiera jeden plik z kodem źródłowym w języku c: add.c oraz plik nagłówkowy. Szczegóły tworzenia modułów CFFI znajdziesz w dokumentacji tej biblioteki. Aby utworzone przez ciebie rozszerzenie prawidłowo zintegrowało się z Pytigonem musisz jedynie trzymać się pewnych zasad:
a) na początku pliku build.py wskazujesz nazwę dołączonego kompilatora i przypisujesz ją do zmiennej środowiskowej "CC" os.environ["CC"] = sys.executable + " -m ziglang cc" b) na końcu pliku build.py kopiujesz skompilowaną bibliotekę dynamiczną do folderu nadrzędnego, w przykładzie odpowiada za to blok kodu:
l = os.listdir("./src")
for item in l:
if item.endswith(".so") or item.endswith(".dll"):
shutil.copy(os.path.join("./src", item), "..")
Aby skorzystać z tak utworzonego rozszerzenia używasz biblioteki zgodnie z regułami CFFI, przykładowy kod dla wersji demo wygląda tak:
from interfaces.applib.cffi_add import lib as cffitestlib result1 = cffitestlib.add(2,2)
Kod rozszerzenia kompilowany jest w momencie generowania projektu w programie schdevtools. Kompilacja odbywa się także na maszynie użytkownika w chwili instalowania paczki projektu *.ptig.
Jeżeli twoje rozszerzenie ma być bardziej rozbudowanym projektem, przemyśl użycie języka ZIG. Jest to tak zwane lepsze C, z jednej strony język ZIG pozwala na tworzenie programów zbliżonych do C, jeżeli chodzi o wielkość i szybkość działania, z drugiej strony jest językiem zapewniającym o wiele większe bezpieczeństwo, błędy popełnia każdy, ZIG dba o to aby nie miały one tak poważnych konsekwencji jak w C.
Integracja Pytigon z ZIG odbywa się w 2 warstwach: a) kompilacja - Pytigon szuka w folderze applib tworzonej aplikacji plików projetu kompilatora ZIG. Plik ten ma nazwę build.zig. Jeżeli taki plik zostaje znaleziony dołączony do Pytigona kompilator ZIG'a próbuję cały projekt oparty na build.zig skompilować. Przykladowy plik build.zig z wersji demo (schpytigondemo, aplikacja: interfaces) może wyglądać tak:
const std = @import("std");
pub fn build(b: *std.build.Builder) void {
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
const lib = b.addSharedLibrary("zig_add", "src/main.zig", b.version(0, 0, 1));
lib.setBuildMode(mode);
lib.install();
}
Skompilowany moduł ZIG, pytigon umieści w folderze nadrzędnym do folderu build.zig, powinien to być folder applib w twojej aplikacji.
b) aby skorzystać z tak utworzonego modułu zaleca się użycie biblioteki ctypes (standardowa biblioteka python'a) przykładowy kod, który wywołuje uwtorzoną bibliotekę wygląda tak:
import ctypes
lib = ctypes.CDLL('interfaces/applib/libzig_add.so')
result2 = lib.add(2,2)
Nie jest to zalecana metoda, ale czasami może okazać się najlepsza. Problem pojawia się przy instalacji powstałego przy użyciu tej metody modułu, we wcześniejszych wersjach działanie modułu nie powinno zależeć od wersji python'a anie od wersji systemu operacyjnego. Ta metoda wymaga dostępu do plików nagłówkowych pythona (Python.h). Są one zależne od wersji pythona i w różny sposób dystrybuowane w różnych wersjach systemu Linux. Jeżeli jednak tworzysz oprograkmowanie działające tylko na twoim komputerze lub na jednej wersji systemu operacyjnego i tej samej wersji pythona, nie ma przeciwskazań abś skorzystal z tej metody. Daje ona maksymalne możliwości integracji kodu C z kodem w pythonie.
System pytigon szuka w folderze applib twojej aplikacji plików setup.py. Jeżeli je znajdzie, próbuje taki plik zainstalować używając do tego dołączonego kompilatora ZIG. Do ciebie należy zapewnienie dostępu do plików nagłowkowych systemu python. Przykładowy program znajduje się w demo Pytigona (schpytigondemo, aplikacja: interfaces). Przykłądowy plik setup.py
from setuptools import Extension
from setuptools import setup
setup(
name="c_sum",
version="1.0.0",
python_requires=">=3.7.15",
build_zig=True,
ext_modules=[
Extension(
"c_sum",
[
"sum.c",
],
include_dirs = [ "/usr/lib/python3.10/", ],
)
],
setup_requires=["setuptools-zig"],
)
Szczegóły tworzenia bibliotek znadziesz w dokukmentacji setuptools, ważne abyś uwzględnił dwa tematy:
a) musisz dodać:
setup_requires=["setuptools-zig"],
to zapewni ci użycie dołączonego do systemu Pytigon kompilatora a nie domyślnego używanego przez setuptools. W przypadku systemów Linux nie ma to większego znaczenia, natomiast dla systemów Windows ma dramatyczne znaczenie. Standardowo system Windows nie posiada wbudowanego kompilatora C, pobieranie i instalowanie kompilatora zgodnego z twoją wersją pythona to zadanie skomplikowane i czasochlonne.
b) Dodajesz linijkę
include_dirs = [ "/usr/lib/python3.10/", ],
oczywiście ścieżka musi prowadzić do twoich plików nagłówkowych systemu python.
Tak utworzona biblioteka kompiluje się automatycznie podczas generowania projektu w schdevtools oraz przy instalacji plików *.ptig
Aby użyć tak utworzonych biblitek robisz to w najbardziej standardowy dla pythona sposób:
import c_sum result3 = c_sum.sum(2,2)