2025, Dec 12 09:02

Запуск Python в Flutter через Chaquopy: как исправить AttributeError из‑за script.py

Как исправить AttributeError при запуске Python из Flutter через Chaquopy: используйте корректный bootstrap-файл script.py, верное расположение и отступы.

Запуск Python из Flutter через Chaquopy в целом несложен, но на мелочах интеграции легко споткнуться. Частая проблема — использовать не тот bootstrap‑файл или переименовать его, из‑за чего при выполнении возникают ошибки вроде AttributeError при обращении к атрибутам модуля. Ниже — краткое объяснение проблемы и точное решение по инструкции плагина.

Кратко о проблеме

Приложение вызывает Python‑скрипт для генерации случайного числа. Сначала импорт модуля не удавался, пока файл не переместили в каталог Python. После этого запуск привёл к ошибке AttributeError: module 'script' has no attribute 'mainTextCode'. Код на Dart загружает Python‑файл из assets и передаёт его Chaquopy для выполнения; сам Python‑файл просто возвращает число.

Проблемный пример кода

Future<void> _runProbe() async {
  showDialog(
    context: context,
    barrierDismissible: false,
    builder: (ctx) => const AlertDialog(
      content: Row(
        children: [
          CircularProgressIndicator(),
          SizedBox(width: 20),
          Text("Running analysis..."),
        ],
      ),
    ),
  );
  try {
    final pySource = await rootBundle.loadString('assets/python/placeholder.py');
    final pyResult = await Chaquopy.executeCode(pySource);
    Navigator.pop(context);
    String displayText = '';
    if (pyResult is Map<String, dynamic>) {
      displayText = pyResult['textOutput']?.toString() ?? 'No output';
    } else {
      displayText = pyResult?.toString() ?? 'No result';
    }
    showDialog(
      context: context,
      builder: (ctx) => AlertDialog(
        title: Row(
          children: [
            Icon(Icons.check_circle, color: Colors.green),
            SizedBox(width: 8),
            Text('Analysis Result'),
          ],
        ),
        content: Text(
          displayText,
          style: const TextStyle(fontSize: 16),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('OK'),
          ),
        ],
      ),
    );
  } catch (err) {
    if (Navigator.canPop(context)) {
      Navigator.pop(context);
    }
    showDialog(
      context: context,
      builder: (ctx) => AlertDialog(
        title: Row(
          children: [
            Icon(Icons.error, color: Colors.red),
            SizedBox(width: 8),
            Text('Error'),
          ],
        ),
        content: Text('Failed to run analysis: ${err.toString()}'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('OK'),
          ),
        ],
      ),
    );
  }
}
import random
def rnd_val():
    return random.randint(1, 100)

В чём дело

Используемый вами плагин — неофициальная интеграция Flutter для Chaquopy. Его настройка прямо требует специальный bootstrap‑файл с именем script.py. Этот файл должен лежать в Python‑каталоге плагина и сохранять точное имя script.py. В документации плагина также отмечено, что в этом файле важны отступы. Если ожидаемого файла или его содержимого нет на месте, появляются ошибки вроде AttributeError при обращении к атрибутам, которые плагин пытается получить у модуля script.

Download script.py and put it in python directory. (Kindly note that this python file should not be renamed other than script.py and also if your code doesn’t work, check the intendations of the downloaded file.)

Копия этого script.py также доступна в репозитории плагина.

Решение

Действуйте строго по инструкции плагина: используйте предоставленный script.py, поместите его в ожидаемый каталог python и не переименовывайте. Если выполнение по‑прежнему падает, проверьте отступы в файле. Когда правильный script.py на месте, приложение сможет вызывать код Python как задумано.

Если сейчас ваш код загружает ассет с другим именем, переключитесь на script.py, чтобы запускался нужный bootstrap‑файл.

Исправленный пример кода

Future<void> _runProbe() async {
  showDialog(
    context: context,
    barrierDismissible: false,
    builder: (ctx) => const AlertDialog(
      content: Row(
        children: [
          CircularProgressIndicator(),
          SizedBox(width: 20),
          Text("Running analysis..."),
        ],
      ),
    ),
  );
  try {
    // Используйте требуемое имя bootstrap‑файла
    final pySource = await rootBundle.loadString('assets/python/script.py');
    final pyResult = await Chaquopy.executeCode(pySource);
    Navigator.pop(context);
    String displayText = '';
    if (pyResult is Map<String, dynamic>) {
      displayText = pyResult['textOutput']?.toString() ?? 'No output';
    } else {
      displayText = pyResult?.toString() ?? 'No result';
    }
    showDialog(
      context: context,
      builder: (ctx) => AlertDialog(
        title: Row(
          children: [
            Icon(Icons.check_circle, color: Colors.green),
            SizedBox(width: 8),
            Text('Analysis Result'),
          ],
        ),
        content: Text(
          displayText,
          style: const TextStyle(fontSize: 16),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('OK'),
          ),
        ],
      ),
    );
  } catch (err) {
    if (Navigator.canPop(context)) {
      Navigator.pop(context);
    }
    showDialog(
      context: context,
      builder: (ctx) => AlertDialog(
        title: Row(
          children: [
            Icon(Icons.error, color: Colors.red),
            SizedBox(width: 8),
            Text('Error'),
          ],
        ),
        content: Text('Failed to run analysis: ${err.toString()}'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('OK'),
          ),
        ],
      ),
    );
  }
}

После применения настроек из документации плагина интеграция работает, и Python‑код запускается. Если позже увидите сообщение вроде «the asset does not exist or has empty data», это уже другая проблема — с загрузкой ассетов и расположением файлов.

Почему это важно

Некоторые интеграции опираются на точку входа по соглашению, а не конфигурации. Имя файла, его расположение и даже структура bootstrap‑файла — часть контракта. Любое отступление от требований, включая переименование файла, выливается в AttributeError или ошибки отсутствия модуля. Следование документации сразу устраняет целый класс неисправностей, ещё до отладки логики приложения.

Что запомнить

Используйте предоставленный плагином script.py, не меняйте имя и положите его в правильный каталог python. Если что‑то всё ещё не работает, проверьте отступы в этом файле, как советует документация. При нерешённых вопросах или непонятном поведении поделитесь текущим кодом и полным текстом ошибки на странице Chaquopy в GitHub — так проще вычислить точную причину.