{ "cells": [ { "cell_type": "markdown", "id": "f4e5c5bd-00a8-4878-895b-e45f261b7ea4", "metadata": {}, "source": [ "# Взаимодействие с внешними приложениями\n", "\n", "## Исполнение программ\n", "\n", "### Запуск с ожиданием завершения\n", "\n", "Простейший метод запуска программы метод `os.system`: " ] }, { "cell_type": "code", "execution_count": null, "id": "5a476ec2-99ac-443a-9638-bde2c6d2b11e", "metadata": {}, "outputs": [], "source": [ "import os\n", "\n", "ret = os.system(\"dir\")\n", "# ret - код завершения программы\n", "# 0 - успешно, иначе ошибка" ] }, { "cell_type": "markdown", "id": "1d18218f-3e07-4e61-99e0-24f3a62b4630", "metadata": {}, "source": [ "Вызов блокирующий - программа ждет завершения команды вызванной через `os.system`.\n", "\n", "### Запуск с получением вывода" ] }, { "cell_type": "code", "execution_count": null, "id": "7b9f0649-8ee6-47d8-8ea7-0612fea01522", "metadata": {}, "outputs": [], "source": [ "import subprocess\n", "\n", "output = subprocess.check_output('ping 127.0.0.1 -n 6', encoding=\"cp866\")\n", "\n", "print(output)" ] }, { "cell_type": "markdown", "id": "5137d44f-1ca4-4c59-b2a8-a8682dbabd09", "metadata": {}, "source": [ "Вызов блокирующий - программа ждет завершения команды вызванной через `subprocess.check_output`.\n", "\n", "### Запуск без ожидания завершения\n", "\n", "Метод `subprocess.Popen` по умолчанию запускает процесс без ожидания завершения: " ] }, { "cell_type": "code", "execution_count": null, "id": "0f845c92-fc9a-4f2b-98a3-191a819dc8ec", "metadata": {}, "outputs": [], "source": [ "import subprocess\n", "\n", "q = subprocess.Popen(\"ping 127.0.0.1 -n 6\", shell=True)\n", "\n", "while q.poll() is None:\n", " print('waiting...')\n", " sleep(1)\n", " \n", "print(\"Finished with code\", q.poll())" ] }, { "cell_type": "markdown", "id": "fc6a8beb-6350-46bd-b360-27ff0d705b29", "metadata": {}, "source": [ "### Что еще нужно знать о запуске внешних программ\n", "\n", "* Если вы нажимаете Ctrl-C пока внешняя программа запущена через os.system, то этот сигнал уйдет ей, а не Python.\n", "* Можно взаимодействовать с интерактивными консольными приложениями пока те запущены через `Popen`. В частности их можно принудительно завершить или прочитать вывод до завершения самой программы.\n", "* Большинство приложений с графическим интерфейсом в качестве первого и единственного аргумента принимают имя файла, который должен быть в них открыт.\n", "\n", "### Загрузка С библиотек\n", "\n", "Python может загружать библиотеки на С и вызывать функции из них. Для этого можно воспользоваться модулем стандартной библиотеки `ctypes` или модулем `cffi`.\n", "\n", "Модуль ctypes требует явного описания сигнатуры (списка аргументов и возвращаемого значения) вызываемой функции на Python, cffi, же включает синтаксический анализатор C, благодаря которому может разбирать заголовочные файлы `.h` и получать оттуда информацию о структурах.\n", "\n", "### Загрузка библиотеки в ctypes\n" ] }, { "cell_type": "code", "execution_count": null, "id": "f61e195e-68a6-456d-9936-fb03ddc94a32", "metadata": {}, "outputs": [], "source": [ "import ctypes\n", "\n", "# В *nix разделяемые библиотеки - файлы .so\n", "lib = ctypes.CDLL('./mylib.so')\n", "\n", "# В Windows - файлы .dll\n", "lib = ctypes.CDLL('./mylib.dll')" ] }, { "cell_type": "markdown", "id": "8b662236-4de2-40e4-997b-37a795ce2ffb", "metadata": {}, "source": [ "### Вызов функции в ctypes" ] }, { "cell_type": "code", "execution_count": null, "id": "3d0dc408-5d33-4032-9908-52a15c182722", "metadata": {}, "outputs": [], "source": [ "foo = lib.foo\n", "foo.restype = ctypes.c_double\n", "foo.argtypes = [ctypes.c_int, ctypes.c_double]\n", "\n", "# Теперь функцию foo можно вызывать, как обычную функцию Python.\n", "\n", "ret = foo(5, 3.2)\n", "print(ret)" ] }, { "cell_type": "markdown", "id": "dea0d01d-4d75-4f09-84d2-e39c765b0f65", "metadata": {}, "source": [ "#### Некоторые популярные типы ctypes\n", "\n", "| Тип ctypes | Тип C | Тип Python |\n", "| ---------- | ----- | ---------- |\n", "| `c_byte` | `char` | `int` |\n", "| `c_ubyte` | `unsigned char` | `int` |\n", "| `c_int` | `int` | `int` |\n", "| `c_double` | `double` | `float` |\n", "| `c_char_p` | `char *` | `str` или `None` |\n", "| `c_int * 10` | `int [10]` | `List[int]` |\n", "\n", "Так как строки в Python являются константами, для создания изменяемого массива типа `c_char_p` используется метод `create_string_buffer`. Подробнее в [документации](https://docs.python.org/3/library/ctypes.html).\n", "\n", "### Загрузка библиотеки и вызов функции в cffi" ] }, { "cell_type": "code", "execution_count": null, "id": "0db6c825-7fb1-4e48-958b-926066eb95b2", "metadata": {}, "outputs": [], "source": [ "import cffi\n", "\n", "# Создаем объект пространства имен cffi\n", "ffi = cffi.FFI()\n", "\n", "# Загружаем заголовочный файл\n", "with open(\"mylib_header.h\") as f:\n", " ffi.cdef(f.read())\n", "\n", "# Загружаем библиотеку\n", "lib = ffi.dlopen(\"mylib.so\")\n", "\n", "# Вызываем функцию\n", "result = lib.foo(5, 3.2)\n", "\n", "# Создаем объект - структуру данных объявленную в заголовочном файле\n", "my_data_struct = self.ffi.new(\"DataStruct *\")\n", "\n", "my_data_struct.a = 15 # Работаем с полями структуры на Python\n", "\n", "ffi.buffer(my_data_struct) # Получаем bytes представление структуры" ] }, { "cell_type": "markdown", "id": "97753178-a780-4676-82e0-7c4df90b0b01", "metadata": {}, "source": [ "### Что еще нужно знать о вызове С функций\n", "\n", "* Написание критичных с точки зрения производительности участков кода на C — один из стандартных способов разогнать Python.\n", "* Ошибка в С библиотеке не может быть обработана на уровне Python и приведет к аварийному завершению интерпретатора.\n", "* Анализатор С pycparser который использует `cffi` поддерживает не все возможности языка С, а также не умеет обрабатывать директивы препроцессора (`#include`, `#ifdef` и т.п.).\n", "* Для CPython можно писать модули С — [CPython Extensions](https://book.pythontips.com/en/latest/python_c_extension.html).\n", "\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.6" } }, "nbformat": 4, "nbformat_minor": 5 }