Взаимодействие с внешними приложениями#
Исполнение программ#
Запуск с ожиданием завершения#
Простейший метод запуска программы метод os.system
:
import os
ret = os.system("dir")
# ret - код завершения программы
# 0 - успешно, иначе ошибка
basics1.ipynb calc2.ipynb data2.ipynb _images locale plot.ipynb
basics2.ipynb conf.py ext.ipynb index.rst mt.ipynb _static
calc1.ipynb data1.ipynb gph.ipynb intro.md net.ipynb _templates
Вызов блокирующий - программа ждет завершения команды вызванной через os.system
.
Запуск с получением вывода#
import subprocess
output = subprocess.check_output('ping 127.0.0.1 -n 6', encoding="cp866")
print(output)
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
Cell In[2], line 3
1 import subprocess
----> 3 output = subprocess.check_output('ping 127.0.0.1 -n 6', encoding="cp866")
5 print(output)
File /usr/lib/python3.12/subprocess.py:466, in check_output(timeout, *popenargs, **kwargs)
463 empty = b''
464 kwargs['input'] = empty
--> 466 return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
467 **kwargs).stdout
File /usr/lib/python3.12/subprocess.py:548, in run(input, capture_output, timeout, check, *popenargs, **kwargs)
545 kwargs['stdout'] = PIPE
546 kwargs['stderr'] = PIPE
--> 548 with Popen(*popenargs, **kwargs) as process:
549 try:
550 stdout, stderr = process.communicate(input, timeout=timeout)
File /usr/lib/python3.12/subprocess.py:1026, in Popen.__init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, user, group, extra_groups, encoding, errors, text, umask, pipesize, process_group)
1022 if self.text_mode:
1023 self.stderr = io.TextIOWrapper(self.stderr,
1024 encoding=encoding, errors=errors)
-> 1026 self._execute_child(args, executable, preexec_fn, close_fds,
1027 pass_fds, cwd, env,
1028 startupinfo, creationflags, shell,
1029 p2cread, p2cwrite,
1030 c2pread, c2pwrite,
1031 errread, errwrite,
1032 restore_signals,
1033 gid, gids, uid, umask,
1034 start_new_session, process_group)
1035 except:
1036 # Cleanup if the child failed starting.
1037 for f in filter(None, (self.stdin, self.stdout, self.stderr)):
File /usr/lib/python3.12/subprocess.py:1955, in Popen._execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, gid, gids, uid, umask, start_new_session, process_group)
1953 err_msg = os.strerror(errno_num)
1954 if err_filename is not None:
-> 1955 raise child_exception_type(errno_num, err_msg, err_filename)
1956 else:
1957 raise child_exception_type(errno_num, err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'ping 127.0.0.1 -n 6'
Вызов блокирующий - программа ждет завершения команды вызванной через subprocess.check_output
.
Запуск без ожидания завершения#
Метод subprocess.Popen
по умолчанию запускает процесс без ожидания завершения:
import subprocess
q = subprocess.Popen("ping 127.0.0.1 -n 6", shell=True)
while q.poll() is None:
print('waiting...')
sleep(1)
print("Finished with code", q.poll())
Что еще нужно знать о запуске внешних программ#
Если вы нажимаете Ctrl-C пока внешняя программа запущена через os.system, то этот сигнал уйдет ей, а не Python.
Можно взаимодействовать с интерактивными консольными приложениями пока те запущены через
Popen
. В частности их можно принудительно завершить или прочитать вывод до завершения самой программы.Большинство приложений с графическим интерфейсом в качестве первого и единственного аргумента принимают имя файла, который должен быть в них открыт.
Загрузка С библиотек#
Python может загружать библиотеки на С и вызывать функции из них. Для этого можно воспользоваться модулем стандартной библиотеки ctypes
или модулем cffi
.
Модуль ctypes требует явного описания сигнатуры (списка аргументов и возвращаемого значения) вызываемой функции на Python, cffi, же включает синтаксический анализатор C, благодаря которому может разбирать заголовочные файлы .h
и получать оттуда информацию о структурах.
Загрузка библиотеки в ctypes#
import ctypes
# В *nix разделяемые библиотеки - файлы .so
lib = ctypes.CDLL('./mylib.so')
# В Windows - файлы .dll
lib = ctypes.CDLL('./mylib.dll')
Вызов функции в ctypes#
foo = lib.foo
foo.restype = ctypes.c_double
foo.argtypes = [ctypes.c_int, ctypes.c_double]
# Теперь функцию foo можно вызывать, как обычную функцию Python.
ret = foo(5, 3.2)
print(ret)
Некоторые популярные типы ctypes#
Тип ctypes |
Тип C |
Тип Python |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Так как строки в Python являются константами, для создания изменяемого массива типа c_char_p
используется метод create_string_buffer
. Подробнее в документации.
Загрузка библиотеки и вызов функции в cffi#
import cffi
# Создаем объект пространства имен cffi
ffi = cffi.FFI()
# Загружаем заголовочный файл
with open("mylib_header.h") as f:
ffi.cdef(f.read())
# Загружаем библиотеку
lib = ffi.dlopen("mylib.so")
# Вызываем функцию
result = lib.foo(5, 3.2)
# Создаем объект - структуру данных объявленную в заголовочном файле
my_data_struct = self.ffi.new("DataStruct *")
my_data_struct.a = 15 # Работаем с полями структуры на Python
ffi.buffer(my_data_struct) # Получаем bytes представление структуры
Что еще нужно знать о вызове С функций#
Написание критичных с точки зрения производительности участков кода на C — один из стандартных способов разогнать Python.
Ошибка в С библиотеке не может быть обработана на уровне Python и приведет к аварийному завершению интерпретатора.
Анализатор С pycparser который использует
cffi
поддерживает не все возможности языка С, а также не умеет обрабатывать директивы препроцессора (#include
,#ifdef
и т.п.).Для CPython можно писать модули С — CPython Extensions.