Как я могу получить версию, определенную в setup.py (setuptools) в моем пакете?

Как я могу получить версию, определенную в setup.py, из моего пакета (для --version или других целей)?

139
задан Acumenus 20 February 2017 в 19:40
поделиться

5 ответов

Внутренняя строка версии уже установленного распределения

Чтобы получить версию изнутри вашу пакет во время выполнения (каким появляется ваш вопрос, на самом деле спрашивать), вы можете использовать:

import pkg_resources  # part of setuptools
version = pkg_resources.require("MyProject")[0].version

Строка версии хранилища для использования во время установки

Если вы хотите пойти на другую сторону «раунд (который, кажется, каким другим авторами ответа здесь, кажется, думал, что вы спрашиваете), поставьте строку версии в отдельный файл и прочитайте содержимое файла в . py .

Вы можете сделать Version.py в вашей пакете с помощью линии __ __, а затем прочитайте его от Setup.py Использование Execfile ('mypackage / version.py') , так Это устанавливает __ версию __ в пространстве имен SETUP.PY.

Если вы хотите гораздо более простое способом, который будет работать со всеми версиями Python и даже неискристыми языками, которые могут понадобиться доступа к строке версии:

хранить строку версии в качестве единственного содержимого простого текстового файла, названного например версия и прочитайте этот файл во время Setup.py .

version_file = open(os.path.join(mypackage_root_dir, 'VERSION'))
version = version_file.read().strip()

То же самое версия файл будет работать именно в любой другой программе, даже без Python, и вам нужно только изменить строку версии в одном месте для всех программ.

Предупреждение о состоянии гонки во время установки

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

228
ответ дан 23 November 2019 в 23:19
поделиться

Ваш вопрос немного расплывчат, но я думаю, что вы спрашиваете, как его уточнить.

Вы должны определить __ версия __ как так:

__version__ = '1.4.4'

И затем вы можете подтвердить, что setup.py знает о версии, которую вы просто определили:

% ./setup.py --version
1.4.4
14
ответ дан 23 November 2019 в 23:19
поделиться

Лучший способ - определить __версию__ в коде вашего продукта, а затем импортировать его оттуда в setup.py. Это даст вам значение, которое вы сможете прочитать в работающем модуле, и у вас будет только одно место для его определения.

Значения в файле setup.py не устанавливаются, и файл setup.py не остается после установки.

То, что я сделал (например) в coverage.py:

# coverage/__init__.py
__version__ = "3.2"


# setup.py
from coverage import __version__

setup(
    name = 'coverage',
    version = __version__,
    ...
    )

UPDATE (2017): coverage.py больше не импортирует себя, чтобы получить версию. Импорт собственного кода может сделать его деинсталляционным, потому что код продукта будет пытаться импортировать зависимости, которые еще не установлены, потому что setup.py - это то, что их устанавливает.

14
ответ дан 23 November 2019 в 23:19
поделиться

Чтобы избежать импорта файла (и, следовательно, выполнения его кода), его можно проанализировать и восстановить атрибут версия из синтаксического дерева:

# assuming 'path' holds the path to the file

import ast

with open(path, 'rU') as file:
    t = compile(file.read(), path, 'exec', ast.PyCF_ONLY_AST)
    for node in (n for n in t.body if isinstance(n, ast.Assign)):
        if len(node.targets) == 1:
            name = node.targets[0]
            if isinstance(name, ast.Name) and \
                    name.id in ('__version__', '__version_info__', 'VERSION'):
                v = node.value
                if isinstance(v, ast.Str):
                    version = v.s
                    break
                if isinstance(v, ast.Tuple):
                    r = []
                    for e in v.elts:
                        if isinstance(e, ast.Str):
                            r.append(e.s)
                        elif isinstance(e, ast.Num):
                            r.append(str(e.n))
                    version = '.'.join(r)
                    break

Этот код пытается найти __ версия __ ] или ВЕРСИЯ присвоение на верхнем уровне модуля возвращает строковое значение. Правая часть может быть строкой или кортежем.

2
ответ дан 23 November 2019 в 23:19
поделиться

Создайте файл в исходном дереве, например. в yourbasedir/yourpackage/_version.py. Пусть этот файл содержит только одну строку кода, например:

__version__ = "1.1.0-r4704"

Затем в setup.py откройте этот файл и проанализируйте номер версии следующим образом:

verstr = "unknown"
try:
    verstrline = open('yourpackage/_version.py', "rt").read()
except EnvironmentError:
    pass # Okay, there is no version file.
else:
    VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
    mo = re.search(VSRE, verstrline, re.M)
    if mo:
        verstr = mo.group(1)
    else:
        raise RuntimeError("unable to find version in yourpackage/_version.py")

] Наконец, в yourbasedir/yourpackage/__init__.py импортируйте _version следующим образом:

__version__ = "unknown"
try:
    from _version import __version__
except ImportError:
    # We're running in a tree that doesn't have a _version.py, so we don't know what our version is.
    pass

Примером кода, который делает это, является пакет "pyutil", который я поддерживаю. (См. PyPI или поиск в Google — stackoverflow запрещает мне включать гиперссылку на него в этот ответ.)

@pjeby прав, что вам не следует импортировать свой пакет из его собственного setup.py. Это будет работать, когда вы протестируете его, создав новый интерпретатор Python и выполнив в нем setup.py первым делом: python setup.py, но бывают случаи, когда это не сработает. Это потому, что import youpackage не означает чтение текущего рабочего каталога для каталога с именем «yourpackage», это означает поиск в текущем sys.modules ключа «yourpackage» а затем делать разные вещи, если его там нет. Таким образом, это всегда работает, когда вы делаете python setup.py, потому что у вас есть свежий, пустой sys.modules, но в целом это не работает.

Например, что, если py2exe выполняет ваш setup.py как часть процесса упаковки приложения? Я видел подобный случай, когда py2exe помещал неправильный номер версии в пакет, потому что пакет получал свой номер версии из import myownthing в своей настройке.py, но во время запуска py2exe ранее была импортирована другая версия этого пакета. Аналогично, что, если setuptools, easy_install, Distribute или distutils2 пытаются собрать ваш пакет как часть процесса установки другого пакета, который зависит от вашего? Затем можно ли импортировать ваш пакет во время оценки его setup.py, или уже существует версия вашего пакета, которая была импортирована во время жизни этого интерпретатора Python, или требуется ли для импорта вашего пакета сначала установить другие пакеты. , или имеет побочные эффекты, может изменить результаты. У меня было несколько проблем с повторным использованием пакетов Python, которые вызывали проблемы для таких инструментов, как py2exe и setuptools, потому что их setup.py импортирует сам пакет, чтобы найти его номер версии.

Между прочим, этот метод прекрасно сочетается с инструментами для автоматического создания файла yourpackage/_version.py для вас, например, путем чтения вашей истории контроля версий и записи номера версии на основе наиболее последний тег в истории контроля версий. Вот инструмент, который делает это для darcs: http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst и вот фрагмент кода, который делает то же самое для git : http://github.com/warner/python-ecdsa/blob/0ed702a9d4057ecf33eea969b8cf280eaccd89a1/setup.py#L34

4
ответ дан 23 November 2019 в 23:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: