A linguagem Python possui muitas bibliotecas, e facilmente conseguimos instalar qualquer uma da internet ou do próprio www.pypi.python.org, que possuirão ‘N’ recursos para protocolos, comunicações, banco de dados, web, mineração de dados, matemática, Gui e entre muitas outras funções.
Vamos conhecer agora um recurso sensacional para usar no Python, de importar bibliotecas compartilhadas do sistema operacional, no Linux por exemplo as famosas lib*.so e poder usar as funções definidas nas mesmas.
Acessando o link do PyPi sobre ctypes https://docs.python.org/2/library/ctypes.html podemos ver que é bem direto sobre o que você pode fazer e o que ele faz.
“
ctypes is a foreign function library for Python. It provides C compatible data types, and allows calling functions in DLLs or shared libraries. It can be used to wrap these libraries in pure Python.
“
Uma breve descrição de ctypes, onde surgiu partir da versão 2.5, e com ele conseguimos utilizar uma função em uma biblioteca do sistema, podemos dizer ser um fork da libffi, lib esta que permite chamadas das funções C possam ser feitas em tempo de execução, e isso conseguimos apenas com o Python.
Básico do ctypes
Muito bacana isso, vamos fazer um teste com uma biblioteca que tenho certeza que se você possui em seu computador, a libc.so, vamos criar o script ex1_ctypes.py:
#! /usr/bin/python import ctypes # Modo 1 - Funcao printf() # Apenas com o nome da lib #_libc = ctypes.CDLL("libc.so.6") # Com o caminho absoluto _libc = ctypes.CDLL("/lib/x86_64-linux-gnu/libc.so.6") _libc.printf("Usando printf()\n") # Modo 2 - Funcao printf() _load_libc = ctypes.cdll.LoadLibrary("libc.so.6") _load_libc.printf("Usando printf()\n") # Modo 3 - Funcao time() print ctypes.cdll.LoadLibrary("libc.so.6").time(None) # Funcao get_nprocs() print "Cores: %d" % _libc.get_nprocs()
Executando:
$ python ex1_ctypes.py Usando printf() Usando printf() 1441760077 Cores: 4
Você viu como foi simples em poucas linhas usamos o printf() e o time() da libc, o que fizemos foi importar o modulo ctypes, e carregar nossa libc, usando apenas o nome libc.so.6, ou caminho absoluto da biblioteca e usando CDLL ou LoadLibrary, além da grande sacada de carregar e na mesma linha utilizar a função como na linha 17.
Pesquisando por funções
E como saber o nome correto das funções, muitas vezes você não esta familiarizado com a estrutura de libs do Linux, mas você pode usar o find_library do ctypes, então vamos ao script ex2_ctypes.py:
# /usr/bin/python import ctypes from ctypes.util import find_library as findlib # Tradicional print ctypes.util.find_library("c") # Um atalho :) print findlib("m") print findlib("udev") print findlib("lzma") print findlib("crypt") print findlib("abc") print findlib("crypto")
Executando:
$ python ex2_ctypes.py libc.so.6 libm.so.6 libudev.so.1 liblzma.so.5 libcrypt.so.1 None libcrypto.so.1.0.0
Que maravilha, não? E ficou claro que caso encontrar a biblioteca retornara com o nome da mesma, caso contrario retorna com None, conforme tentei pesquisar pela ‘abc‘ na linha14.
Criando nossa própria biblioteca
Vamos criar uma biblioteca em C básica com apenas duas funções, onde uma irá retornar o nome da função e a próxima um numero randômico, nada que não poderíamos fazer em Python facilmente, mas irei utilizar como exemplo.
Código da nossa biblioteca, mylib.c:
#include <stdio.h> #include <time.h> char *str_name = "Eu sou global :(\0"; void itself(void); int randomize(int); void itself() { printf("Arquivo %s\n",__FILE__); printf("Funcao %s\n",__func__); printf("Blog Cleiton Bueno\n"); } int randomize(int N) { srand(time(NULL)); int result = rand() % N; return result; }
Construindo nossa biblioteca compartilhada:
$ gcc -Wl,-soname,mylib -o mylib.so -shared -fPIC mylib.c
Criando nosso Python para usar as funções itself() e randomize(int), no ex3_ctypes.py:
# /usr/bin/python import ctypes mylib = ctypes.CDLL('./mylib.so') print mylib.itself() print print 'Random: %d' % mylib.randomize(ctypes.c_int(50))
Executando nossa aplicação:
$ python ex3_ctypes.py Arquivo mylib.c Funcao itself Blog Cleiton Bueno 19 Random: 28
Esta imaginando o poder que isso pode lhe oferecer?
Acessando variáveis de bibliotecas
Eu não comentei nada sobre a variável global str_name no exemplo anterior, porque iria abordar nesta etapa.
E como você esta pensando, sim, podemos acessar estas variáveis, vamos ao ex4_ctypes:
#! /usr/bin/python import ctypes mylib = ctypes.CDLL("./mylib.so") # Devo dizer que da minha lib "mylib" irei importar a variavel "str_name" # que eh um ponteiro de caracteres "c_char_p" my_str = ctypes.c_char_p.in_dll(mylib, 'str_name') print my_str.value
Executando nosso script:
$ python ex4_ctypes.py Eu sou global :(
Tipos de dados
No exemplo anterior eu deveria fornecer um numero qualquer para minha função randomize(int), e de onde tirei aquele ctype.c_int(), depois usei um tal de ctypes.c_char_p para importar um ponteiro de caracteres, pois é, esta função é uma das responsáveis por tornar compatível os tipos de dados entre C <-> Python, uma relação completa entre os tipos segue abaixo.
ctypes type | C type | Python type |
---|---|---|
c_bool | _Bool | bool (1) |
c_char | char | 1-character string |
c_wchar | wchar_t | 1-character unicode string |
c_byte | char | int/long |
c_ubyte | unsigned char | int/long |
c_short | short | int/long |
c_ushort | unsigned short | int/long |
c_int | int | int/long |
c_uint | unsigned int | int/long |
c_long | long | int/long |
c_ulong | unsigned long | int/long |
c_longlong | __int64 or long long | int/long |
c_ulonglong | unsigned __int64 or unsigned long long | int/long |
c_float | float | float |
c_double | double | float |
c_longdouble | long double | float |
c_char_p | char * (NUL terminated) | string or None |
c_wchar_p | wchar_t *(NUL terminated) | unicode or None |
c_void_p | void * | int/long or None |
Extras
São muitas as possibilidades e recursos, principalmente com a parte de ponteiros e que merecem e devem ter uma grande atenção.
Algumas dicas extra que posso dar é como exemplo a função create_string_buffer(), caso precisar criar um buffer mutável, onde nos parenteses você fornece o tamanho do buffer, e ele irá retornar um objeto que é um array de c_char, outro recurso é o ctypes.Structure, se você pensou em alguma semelhante com struct em C, pensou certo, onde você pode usar? Vamos ver.
Criando uma nova biblioteca, mystruct.c:
#include <stdio.h> #include <string.h> struct c2py { char indice; char buff[50]; int key; }; void printStruct(struct c2py *); void printStruct(struct c2py *my_struct) { my_struct->indice = 'A'; strncpy(my_struct->buff,"Cara, apareci no Python :)", 26); my_struct->key = 2015; }
Compilando a biblioteca:
$ gcc -Wl,-soname,mystruct -o mystruct.so -shared -fPIC mystruct.c
Código Python para usar nossa struct, ex5_ctypes.py:
#! /usr/bin/python import ctypes # Class com a estrutura do struct criado na lib em C class skeleton_mystruct(ctypes.Structure): _fields_ = [ ("indice", ctypes.c_char), ("buff", ctypes.c_char * 50), ("key", ctypes.c_int) ] # Carregando a nossa biblioteca lib_mystruct = ctypes.CDLL('./mystruct.so') # Criando um objeto da nossa estrutura s1_dev = skeleton_mystruct() # Acessando nossa funcao printStruct passando a nossa estrutura lib_mystruct.printStruct(ctypes.byref(s1_dev)) # Print da estrutura indexada print "Indice : %c " % s1_dev.indice print "Buff : %s " % s1_dev.buff print "Key : %d " % s1_dev.key # Explorando nosso objeto s1_dev print print type(s1_dev) print "\nObjeto s1_dev" print dir(s1_dev) print "\nObjeto s1_dev.buff" print dir(s1_dev.buff)
Executando nossa aplicação python:
$ python ex5_ctypes.py Indice : A Buff : Cara, apareci no Python :) Key : 2015 < class '__main__.skeleton_mystruct' > Objeto s1_dev ['__class__', '__ctypes_from_outparam__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_b_base_', '_b_needsfree_', '_fields_', '_objects', 'buff', 'indice', 'key'] Objeto s1_dev.buff ['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
Acho que ficou claro o que foi usado no Python para enquadrar com o que fizemos em C certo? Nas referências coloquei um link que aborda só a parte de Structure, vale a pena conferir. Mas e o create_string_buffer? Vamos adaptar um script python pra o buff deste exemplo, no ex6_ctypes.py:
#! /usr/bin/python import ctypes # Class com a estrutura do struct criado na lib em C class skeleton_mystruct(ctypes.Structure): _fields_ = [ ("indice", ctypes.c_char), ("buff", ctypes.c_char * 50), ("key", ctypes.c_int) ] # Carregando a nossa biblioteca lib_mystruct = ctypes.CDLL('./mystruct.so') # Criando um objeto da nossa estrutura s1_dev = skeleton_mystruct() # Acessando nossa funcao printStruct passando a nossa estrutura lib_mystruct.printStruct(ctypes.byref(s1_dev)) my_buff = ctypes.create_string_buffer(len(s1_dev.buff)) my_buff = s1_dev.buff print "Buffer : %s " % my_buff # Test: # Lembra que falei de que o create_string_buffer retorna # um array de c_char, vamos verificar print "\ncreate_string_buffer(10) == 10*c_char print type(ctypes.create_string_buffer(10)) == 10*ctypes.c_char
Executando a aplicação:
$ python ex6_ctypes.py Buffer : Cara, apareci o Python :) create_string_buffer(10) == 10*c_char True
Vimos um uso pratico do create_string_buffer e confirmamos realmente o que é seu tipo.
Demos uma boa pincelada nos principais recursos do ctypes, mas ele vai muito além.
Gostou de conhecer o ctypes? Incrível não? Caso pretenda usar recomendo fortemente acessar python ctypes, porque existem muitas opções para ser utilizadas e combinadas.
Espero que tenham gostado, até a próxima!
Referencias
https://docs.python.org/2/library/ctypes.html
https://docs.python.org/2/library/ctypes.html#structured-data-types
Python – Acessando bibliotecas do Linux com ctypes by Cleiton Bueno is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.