Pyinstaller: пытается исключить все модули из подпапки, но pyinstaller все равно продолжает их компилировать

#python #python-3.x #pyinstaller

Вопрос:

В настоящее время я пытаюсь реализовать карточную игру на python, и мой план состоял в том, чтобы попытаться быть удобным для пользователя, скомпилировав основное приложение, чтобы не требовать от пользователя установки python, но также поддерживать пользовательские модификации разумно доступным способом, позволяя скомпилированному приложению загружать некомпилированные файлы python из определенных вложенных папок. Пока это нормально работает в некомпилированном приложении.

Однако я не могу заставить pyinstaller прекратить включение файлов python, которые должны быть отделены от скомпилированного приложения.

Файлы:

 main.py
gameobj.py
datagamedata.py
dataaisai.py
dataaisdummy.py
dataeffectseffect.py
dataeffectshaunt.py
datapowerspower.py
datapowerslore.py
dataspellsspell.py
dataspellsbottomlesspit.py
 

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

В идеале все, что находится во вложенной папке данных, не должно быть скомпилировано в исполняемый файл, а должно быть включено в виде файлов данных; gamedata.py обрабатывает загрузку материалов, включая дополнительные материалы, которые добавляет пользователь. В настоящее время с помощью следующей команды pyinstaller (или ее вариантов), gamedata.py и все, что он импортирует, компилируется, насколько я могу судить:

pyinstaller -n Game --exclude-module gamedata --add-data "res;res" --add-data "data;data" --noconfirm main.py

После выполнения этой команды я мог бы перейти в distGamedataaisdummy.py и измените значение, присвоенное имени ИИ, и он все равно напечатает предварительно скомпилированную версию.

Я попытался исключить данные, игровые данные, ии, эффекты, способности, заклинания. Я не уверен, исключая все отдельные файлы данных (такие файлы, как lore.py или преследовать.py) было бы жизнеспособно, так как я планирую создать еще много файлов, подобных тем, которые будут созданы позже, и до тех пор, пока gamedata.py похоже, что в любом случае компилируется, это вопрос спорный; пользователь не сможет указать больше вещей для загрузки. Если бы я мог заставить pyinstaller игнорировать это, у меня было бы (возможно, хакерское) решение для компиляции без большей части папки с данными, а затем копирования остальных в папку в разделе dist.


mnd.py contains mostly testing stuff at the moment.

 from gameobj import *
import data.gamedata as gamedata

# Testing power
lore_test = gamedata.lore.Lore()
print(lore_test.get_desc())

# Testing effect
haunt_test = gamedata.haunt.Haunt()
print(haunt_test.get_desc())

# Testing spell
bottomlesspit_test = gamedata.bottomlesspit.BottomlessPit()
print(bottomlesspit_test.name)

# testing AI
dummy_ai = gamedata.dummy.Dummy()
print(dummy_ai.name)
 

gamedata.py is where the user should be able to specify files to import from. For the above, I’m currently having it import this.

 import sys
sys.path.append(".data")

from powers import lore

from effects import haunt

from spells import bottomlesspit

from ais import dummy
 

As an example of one of the files it imports, they’re structured like this:

 from .ai import AI

class Dummy(AI):
    def __init__(self):
        super().__init__("DummyAI")

    def phase_setup(self):
        super().phase_setup()

    def phase_energize(self):
        super().phase_energize()

    def phase_prs_one(self):
        super().phase_prs_one()

    def phase_attack(self):
        super().phase_attack()

    def phase_creatures(self):
        super().phase_creatures()

    def phase_prs_two(self):
        super().phase_prs_two()

    def phase_draw(self):
        super().phase_draw()
 

Который получает ИИ от ai.py:

 from abc import ABC, abstractmethod

class AI(ABC):
    @abstractmethod
    def __init__(self, name):
        self.name = name
        self.player = None

    @abstractmethod
    def phase_setup(self):
        # draw up to 5 cards
        # intended to be called after AI chooses whether to take starter cards
        while len(self.player.hand) < 5:
            self.player.deck.draw()
        self.player.match.next_phase()

    @abstractmethod
    def phase_energize(self):
        if self.player.match.turn != 0:
            self.player.magi.energize()
        self.player.match.next_phase()

    @abstractmethod
    def phase_prs_one(self):
        self.player.match.next_phase()

    @abstractmethod
    def phase_attack(self):
        self.player.match.next_phase()

    @abstractmethod
    def phase_creatures(self):
        self.player.match.next_phase()

    @abstractmethod
    def phase_prs_two(self):
        self.player.match.next_phase()

    @abstractmethod
    def phase_draw(self):
        self.player.draw(2)
        self.player.match.next_phase()
 

gameobj.py содержит игровые объекты. Не уверен, что это актуально, ничего не импортирует из самого проекта. Однако вы можете отредактировать содержимое, если кто-то сочтет это уместным.

Комментарии:

1. PyInstaller следует за всеми imports , использование importlib может привести к скрытому импорту , который не позволяет объединять файлы в ваше приложение.

2. В конце концов, я заставил его работать. Я разделил папку с данными, включая дополнительные файлы .py, на их собственный проект, использовал pyinstaller для компиляции основных файлов, а затем скопировал папку с данными в папку dist. Файлы, скомпилированные pyinstaller, по-прежнему с радостью подхватывают gamedata.py и все, что он импортирует, внешнее по отношению к замороженному главному приложению, что означает, что скомпилированное ядро МОЖЕТ собирать внешние файлы .py.

3. Прости, что я был не прав.

Ответ №1:

В конечном счете, то, что мне нужно было сделать, чтобы заставить его работать, было:

  1. Переместите всю папку с данными в отдельный проект, не позволяя pyinstaller забрать ее. Pyinstaller имеет доступ только к main.py и gameobj.py.
  2. Изменить как main.py ищет импорт:
 import sys
sys.path.append(".data")
sys.path.append(".datapowers")
sys.path.append(".dataeffects")
sys.path.append(".dataspells")
sys.path.append(".dataais")

from gameobj import *
import data.gamedata as gamedata
 

Возможно, не все это строго необходимо. Все остальное осталось таким, каким было раньше.

  1. gamedata.py немного изменен на следующий:
 from data.powers import lore
from data.effects import haunt
from data.spells import bottomlesspit
from data.ais import dummy
 
  1. Запустите pyinstaller со следующей командой:
    pyinstaller -n Game --exclude-module gamedata --noconfirm main.py
  2. После запуска pyinstaller скопируйте папку с данными в distGame и запустите Game.exe. Хотя gamedata.py не застыла с main.py и gameobj.py, приложение по-прежнему будет проходить тесты, использующие данные из папки данных, и будет получать изменения, внесенные в эти файлы.