{ "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": 1, "id": "5a476ec2-99ac-443a-9638-bde2c6d2b11e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "basics1.ipynb calc2.ipynb data2.ipynb gph.ipynb intro.md net.ipynb _templates\n", "basics2.ipynb conf.py\t de.ipynb\t _images locale plot.ipynb\n", "calc1.ipynb data1.ipynb ext.ipynb\t index.rst mt.ipynb _static\n" ] } ], "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": 6, "id": "7b9f0649-8ee6-47d8-8ea7-0612fea01522", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.\n", "64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.044 ms\n", "64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.042 ms\n", "64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.043 ms\n", "\n", "--- 127.0.0.1 ping statistics ---\n", "3 packets transmitted, 3 received, 0% packet loss, time 2058ms\n", "rtt min/avg/max/mdev = 0.042/0.043/0.044/0.000 ms\n", "\n" ] } ], "source": [ "import subprocess\n", "\n", "output = subprocess.check_output(['ping', '127.0.0.1', '-c', '3'])\n", "\n", "print(output.decode())" ] }, { "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": 9, "id": "0f845c92-fc9a-4f2b-98a3-191a819dc8ec", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "waiting...\n", "PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.\n", "64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.037 ms\n", "waiting...\n", "64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.041 ms\n", "waiting...\n", "64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.048 ms\n", "\n", "--- 127.0.0.1 ping statistics ---\n", "3 packets transmitted, 3 received, 0% packet loss, time 2043ms\n", "rtt min/avg/max/mdev = 0.037/0.042/0.048/0.004 ms\n", "Finished with code 0\n" ] } ], "source": [ "import subprocess\n", "from time import sleep\n", "\n", "q = subprocess.Popen(\"ping 127.0.0.1 -c 3\", 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": "markdown", "id": "17c3a3b1-92af-4986-8d27-12dbcf616895", "metadata": {}, "source": [ "```python\n", "import ctypes\n", "\n", "# В *nix разделяемые библиотеки - файлы .so\n", "lib = ctypes.CDLL('./mylib.so')\n", " \n", "# В Windows - файлы .dll\n", "lib = ctypes.CDLL('./mylib.dll')\n", "\n", "# Вызов функции в ctypes\n", "\n", "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)\n", "```" ] }, { "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": "markdown", "id": "b137425f-4a66-4f84-9e05-e98f59fa776c", "metadata": {}, "source": [ "```python\n", "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 представление структуры\n", "```" ] }, { "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.13.7" } }, "nbformat": 4, "nbformat_minor": 5 }