Это одобренный способ доступа к данным, смежным с / упакованным с помощью скрипта Python? - PullRequest
7 голосов
/ 08 апреля 2019

У меня есть скрипт Python, которому нужны данные, которые хранятся в файле, который всегда будет находиться в том же месте, что и скрипт.У меня есть setup.py для скрипта, и я хочу убедиться, что его можно установить в pip в самых разных средах и при необходимости можно превратить в автономный исполняемый файл.

В настоящее время скрипт работает с Python 2.7.и Python 3.3 или выше (хотя у меня нет тестовой среды для 3.3, поэтому я не уверен в этом).

Я придумал этот метод для получения данных.Этот сценарий не является частью каталога модуля с __init__.py или чем-либо еще, это просто автономный файл, который будет работать, если просто запустить с python напрямую, но также имеет точку входа, определенную в файле setup.py.Это все один файл.Это правильный путь?

def fetch_wordlist():
    wordlist = 'wordlist.txt'
    try:
        import importlib.resources as res
        return res.read_binary(__file__, wordlist)
    except ImportError:
        pass
    try:
        import pkg_resources as resources
        req = resources.Requirement.parse('makepw')
        wordlist = resources.resource_filename(req, wordlist)
    except ImportError:
        import os.path
        wordlist = os.path.join(os.path.dirname(__file__), wordlist)
    with open(wordlist, 'rb') as f:
        return f.read()

Это кажется нелепо сложным.Кроме того, кажется, что я полагаюсь на систему управления пакетами так, как мне неудобно.Сценарий больше не работает, если он не был установлен в pip, и это также не кажется желательным.

Ответы [ 3 ]

7 голосов
/ 13 апреля 2019

Ресурсы, живущие в файловой системе

Стандартный способ чтения файла, смежного с вашим скриптом python:

a) Если у вас есть python> = 3.4, я бы предложилвы используете модуль pathlib , например:

from pathlib import Path


def fetch_wordlist(filename="wordlist.txt"):
    return (Path(__file__).parent / filename).read_text()


if __name__ == '__main__':
    print(fetch_wordlist())

b) И если вы все еще используете версию Python <3.4 или все еще хотите использовать старый добрый <a href="https://docs.python.org/3/library/os.path.html" rel="noreferrer">os.path модуль, вы должны сделать что-то вроде этого:

import os


def fetch_wordlist(filename="wordlist.txt"):
    with open(os.path.join(os.path.dirname(__file__), filename)) as f:
        return f.read()


if __name__ == '__main__':
    print(fetch_wordlist())

Кроме того, я бы предложил вам захватывать исключения во внешних вызывающих, вышеупомянутые методы являются стандартным способом чтения файлов в Python, так что выне нужно заключать их в такие функции, как fetch_wordlist, в противном случае чтение файлов в python является «атомарной» операцией.

Теперь может случиться так, что вы заморозили свою программу, используя какой-то морозильник, напримеркак cx_freeze, pyinstaller или подобные ... в этом случае вам нужно это обнаружить, вот простой способ проверить это:

a) с помощью os.path:

if getattr(sys, 'frozen', False):
    app_path = os.path.dirname(sys.executable)
elif __file__:
    app_path = os.path.dirname(__file__)

b) с использованием pathlib:

if getattr(sys, 'frozen', False):
    app_path = Path(sys.executable).parent
elif __file__:
    app_path = Path(__file__).parent

Ресурсы, находящиеся в zip-файле

Вышеприведенные решения будут работать, если код находится в файловой системе, но не будут работать, если пакет находится внутри zip-файла. Когда это происходит, вы можете использовать либо importlib.resources (new inверсия 3.7) или pkg_resources combo, как вы уже показали в вопросе (или вы можете заключить в некоторых помощниках), или вы можете использовать хорошую стороннюю библиотеку под названием importlib_resources, которая должна работать со старыми и современнымиВерсии Python:

Специально для вашей конкретной проблемы я бы посоветовал вам взглянуть на этот https://importlib -resources.readthedocs.io / en / latest / using.html # file-system-or-zip-file .

Если вы хотите знать, что эта библиотека делает за кулисами, потому что вы не желаете устанавливать какую-либо стороннюю библиотеку, вы можете найти код для py2 здесь и py3 здесь на случай, если вы хотите получить соответствующуюбиты для вашей конкретной проблемы

5 голосов
/ 16 апреля 2019

Я собираюсь выйти на конечность и сделать предположение, потому что это может существенно упростить вашу проблему.Единственный способ представить, что вы можете утверждать, что эти данные «хранятся в файле, который всегда будет находиться в том же месте, что и сценарий», состоит в том, что вы создали эти данные один раз и поместили их в файл в исходном коде.каталог.Несмотря на то, что эти данные являются двоичными, рассматривали ли вы возможность сделать эти данные литеральной байтовой строкой в ​​файле Python, а затем просто импортировать их, как если бы это было что-то еще? *

0 голосов
/ 13 апреля 2019

Вы правы в том, что ваш метод чтения файла немного излишне сложен.Если у вас нет действительно конкретной причины использовать модули importlib и pkg_resources, это довольно просто.

import os

def fetch_wordlist():
    if not os.path.exists('wordlist.txt'):
        raise FileNotFoundError

    with open('wordlist.txt', 'rb') as wordlist:
        return wordlist.read()

Вы не предоставили много информации о вашем скрипте, поэтому я не могу комментировать почемуон не работает, если не установлен с помощью pip.Мое лучшее предположение: ваш скрипт, вероятно, упакован в пакет python.

...