Pregunta

Hay una manera estándar de asociado de la cadena de la versión con un paquete de python, de tal manera que yo podría hacer lo siguiente?

import foo
print foo.version

Me imagino que hay alguna manera de recuperar los datos sin ningún tipo de extra codificar, ya que los pequeños/grandes cadenas se especifican en setup.py ya.La solución alternativa que he encontrado para tener import __version__ en mi foo/__init__.py y luego han __version__.py generado por setup.py.

¿Fue útil?

Solución

No es directamente una respuesta a su pregunta, pero debería considerar nombrarla __version__, no version.

Esto es casi un cuasi-estándar. Muchos módulos en la biblioteca estándar usan <=>, y esto también se usa en lotes de módulos de terceros , entonces es el cuasi-estándar.

Por lo general, <=> es una cadena, pero a veces también es un flotador o una tupla.

Editar: según lo mencionado por S.Lott (¡Gracias!), PEP 8 lo dice explícitamente:

  

Contabilidad de versiones

     

Si tiene que tener Subversion, CVS o RCS crud en su archivo fuente,   hazlo de la siguiente manera.

    __version__ = "$Revision: 63990 $"
    # $Source$
     

Estas líneas deben incluirse después de la cadena de documentación del módulo, antes   cualquier otro código, separado por una línea en blanco arriba y abajo.

También debe asegurarse de que el número de versión se ajuste al formato descrito en PEP 440 ( PEP 386 una versión anterior de esta norma).

Otros consejos

Utilizo un solo archivo _version.py como " una vez canónico lugar " para almacenar información de la versión:

  1. Proporciona un atributo __version__.

  2. Proporciona la versión estándar de metadatos. Por lo tanto, será detectado por pkg_resources u otras herramientas que analizan los metadatos del paquete (EGG-INFO y / o PKG-INFO, PEP 0345).

  3. No importa su paquete (o cualquier otra cosa) al compilar su paquete, lo que puede causar problemas en algunas situaciones. (Consulte los comentarios a continuación sobre los problemas que esto puede causar).

  4. Solo hay un lugar donde se escribe el número de versión, por lo que solo hay un lugar para cambiarlo cuando cambia el número de versión, y hay menos posibilidades de versiones inconsistentes.

Así es como funciona: el " un lugar canónico " para almacenar el número de versión es un archivo .py, llamado " _version.py " que está en su paquete de Python, por ejemplo en myniftyapp/_version.py. Este archivo es un módulo de Python, ¡pero tu setup.py no lo importa! (Eso anularía la función 3.) En cambio, setup.py sabe que el contenido de este archivo es muy simple, algo así como:

__version__ = "3.6.5"

Y entonces tu setup.py abre el archivo y lo analiza, con un código como:

import re
VERSIONFILE="myniftyapp/_version.py"
verstrline = open(VERSIONFILE, "rt").read()
VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
mo = re.search(VSRE, verstrline, re.M)
if mo:
    verstr = mo.group(1)
else:
    raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,))

Entonces su setup.py pasa esa cadena como el valor de " version " argumento para setup(), satisfaciendo así la característica 2.

Para satisfacer la función 1, puede hacer que su paquete (en tiempo de ejecución, no en el momento de la configuración) importe el archivo _version desde myniftyapp/__init__.py de esta manera:

from _version import __version__

Aquí está un ejemplo de esta técnica que Lo he estado usando durante años.

El código en ese ejemplo es un poco más complicado, pero el ejemplo simplificado que escribí en este comentario debería ser una implementación completa.

Aquí está código de ejemplo de importación la versión .

Si ve algún problema con este enfoque, hágamelo saber.

Reescrito 2017-05

Después de más de diez años de escribir código Python y administrar varios paquetes, llegué a la conclusión de que el bricolaje quizás no sea el mejor enfoque.

Empecé a usar el paquete pbr para tratar el control de versiones en mis paquetes. Si está utilizando git como su SCM, esto se ajustará a su flujo de trabajo como magia, ahorrando sus semanas de trabajo (se sorprenderá de lo complejo que puede ser el problema).

A partir de hoy pbr está clasificado como el paquete de python más utilizado en el puesto número 11 y alcanzó este nivel no No incluye ningún truco sucio: era solo uno: solucionar un problema de empaquetado común de una manera muy simple.

yourpackage/version.py puede hacer más de la carga de mantenimiento del paquete, no se limita al control de versiones, pero no lo obliga a adoptar todos sus beneficios.

Entonces, para darle una idea de cómo se ve adoptar pbr en una confirmación, eche un vistazo swiching embalaje a pbr

Probablemente haya observado que la versión no está almacenada en absoluto en el repositorio. PBR lo detecta desde las ramas y etiquetas de Git.

No es necesario preocuparse por lo que sucede cuando no tiene un repositorio git porque pbr hace " compila " y guarde en caché la versión cuando empaquete o instale las aplicaciones, para que no haya dependencia del tiempo de ejecución en git.

Solución anterior

Aquí está la mejor solución que he visto hasta ahora y también explica por qué:

Dentro de yourpackage/__init__.py:

# Store the version here so:
# 1) we don't load dependencies by storing it in __init__.py
# 2) we can import it in setup.py for the same reason
# 3) we can import it into your module module
__version__ = '0.12'

Dentro de setup.py:

from .version import __version__

Dentro de <=>:

exec(open('yourpackage/version.py').read())
setup(
    ...
    version=__version__,
    ...

Si conoce otro enfoque que parece ser mejor, hágamelo saber.

Según el PEP 396 (números de versión del módulo) diferido, hay Una forma propuesta de hacer esto. Describe, con justificación, un estándar (ciertamente opcional) para los módulos a seguir. Aquí hay un fragmento:

  

3) Cuando un módulo (o paquete) incluye un número de versión, la versión DEBE estar disponible en el atributo __version__.

     

4)   Para los módulos que viven dentro de un paquete de espacio de nombres, el módulo DEBE incluir el atributo <=>. El paquete de espacio de nombres en sí NO DEBE incluir su propio atributo <=>.

     

5)   El valor del atributo <=> DEBE ser una cadena.

A pesar de que este es, probablemente, demasiado tarde, hay un poco más simple alternativa a la respuesta anterior:

__version_info__ = ('1', '2', '3')
__version__ = '.'.join(__version_info__)

(Y sería bastante simple para convertir incremento automático de las porciones de los números de versión a una cadena con str().)

Por supuesto, de lo que he visto, la gente tiende a usar algo como la ya mencionada versión cuando se utiliza __version_info__, y como este tipo de almacén es como una tupla de enteros;sin embargo, no acabo de ver el punto de hacerlo, como me cabe duda de que hay situaciones donde se iba a realizar operaciones matemáticas de suma y resta en las porciones de los números de versión para cualquier propósito, además de la curiosidad o de auto-incremento (e incluso entonces, int() y str() puede ser utilizado con bastante facilidad).(Por otro lado, existe la posibilidad de que el código de otra persona esperando un numérica tupla en lugar de una cadena de tupla y no pudiendo por tanto.)

Este es, por supuesto, mi propio punto de vista, y yo con mucho gusto a los demás' entrada sobre el uso de un número de tupla.


Como shezi me recordó, (léxico) las comparaciones de cadenas de número no necesariamente tienen el mismo resultado directo comparaciones numéricas;ceros a la izquierda / a para que.Así que al final, almacenando __version_info__ (o lo que sería llamado) como una tupla de valores enteros permitiría más eficiente de la versión comparaciones.

Uso un archivo JSON en el directorio del paquete. Esto se ajusta a los requisitos de Zooko.

Dentro de pkg_dir/pkg_info.json:

{"version": "0.1.0"}

Dentro de setup.py:

from distutils.core import setup
import json

with open('pkg_dir/pkg_info.json') as fp:
    _info = json.load(fp)

setup(
    version=_info['version'],
    ...
    )

Dentro de pkg_dir/__init__.py:

import json
from os.path import dirname

with open(dirname(__file__) + '/pkg_info.json') as fp:
    _info = json.load(fp)

__version__ = _info['version']

También pongo otra información en pkg_info.json, como autor. yo me gusta usar JSON porque puedo automatizar la administración de metadatos.

Muchas de estas soluciones, aquí ignorar git etiquetas de versión que todavía significa que tienen que seguir la pista de la versión en varios lugares (malo).Me acerqué a este con los siguientes objetivos:

  • Derivan todas de la versión de python referencias de un tag en el git repo
  • Automatizar git tag/push y setup.py upload pasos con un solo comando, que no toma los insumos.

Cómo funciona:

  1. A partir de una make release comando, la última versión de etiquetado en el repositorio git que se encuentra y se incrementa.La etiqueta es empujado de nuevo a origin.

  2. El Makefile almacena la versión en src/_version.py donde va a ser leído por setup.py y también se incluyó en la liberación. No comprobar _version.py en el control de código fuente!

  3. setup.py comando lee la nueva versión de la cadena de package.__version__.

Detalles:

Makefile

# remove optional 'v' and trailing hash "v1.0-N-HASH" -> "v1.0-N"
git_describe_ver = $(shell git describe --tags | sed -E -e 's/^v//' -e 's/(.*)-.*/\1/')
git_tag_ver      = $(shell git describe --abbrev=0)
next_patch_ver = $(shell python versionbump.py --patch $(call git_tag_ver))
next_minor_ver = $(shell python versionbump.py --minor $(call git_tag_ver))
next_major_ver = $(shell python versionbump.py --major $(call git_tag_ver))

.PHONY: ${MODULE}/_version.py
${MODULE}/_version.py:
    echo '__version__ = "$(call git_describe_ver)"' > $@

.PHONY: release
release: test lint mypy
    git tag -a $(call next_patch_ver)
    $(MAKE) ${MODULE}/_version.py
    python setup.py check sdist upload # (legacy "upload" method)
    # twine upload dist/*  (preferred method)
    git push origin master --tags

El release destino siempre incrementos de la versión 3 dígitos, pero se puede utilizar el next_minor_ver o next_major_ver el incremento de los otros dígitos.Los comandos se basan en la versionbump.py secuencia de comandos que se registró en la raíz de la repo

versionbump.py

"""An auto-increment tool for version strings."""

import sys
import unittest

import click
from click.testing import CliRunner  # type: ignore

__version__ = '0.1'

MIN_DIGITS = 2
MAX_DIGITS = 3


@click.command()
@click.argument('version')
@click.option('--major', 'bump_idx', flag_value=0, help='Increment major number.')
@click.option('--minor', 'bump_idx', flag_value=1, help='Increment minor number.')
@click.option('--patch', 'bump_idx', flag_value=2, default=True, help='Increment patch number.')
def cli(version: str, bump_idx: int) -> None:
    """Bumps a MAJOR.MINOR.PATCH version string at the specified index location or 'patch' digit. An
    optional 'v' prefix is allowed and will be included in the output if found."""
    prefix = version[0] if version[0].isalpha() else ''
    digits = version.lower().lstrip('v').split('.')

    if len(digits) > MAX_DIGITS:
        click.secho('ERROR: Too many digits', fg='red', err=True)
        sys.exit(1)

    digits = (digits + ['0'] * MAX_DIGITS)[:MAX_DIGITS]  # Extend total digits to max.
    digits[bump_idx] = str(int(digits[bump_idx]) + 1)  # Increment the desired digit.

    # Zero rightmost digits after bump position.
    for i in range(bump_idx + 1, MAX_DIGITS):
        digits[i] = '0'
    digits = digits[:max(MIN_DIGITS, bump_idx + 1)]  # Trim rightmost digits.
    click.echo(prefix + '.'.join(digits), nl=False)


if __name__ == '__main__':
    cli()  # pylint: disable=no-value-for-parameter

Esto hace el trabajo pesado de cómo procesar y incrementar el número de versión de git.

__init__.py

El my_module/_version.py archivo se importa en my_module/__init__.py.Poner estática instalar config aquí que desea distribuir con su módulo.

from ._version import __version__
__author__ = ''
__email__ = ''

setup.py

El último paso es leer la información de la versión de la my_module el módulo.

from setuptools import setup, find_packages

pkg_vars  = {}

with open("{MODULE}/_version.py") as fp:
    exec(fp.read(), pkg_vars)

setup(
    version=pkg_vars['__version__'],
    ...
    ...
)

Por supuesto, para que todo esto deberás tener al menos una versión de la etiqueta en la repo para empezar.

git tag -a v0.0.1

No parece ser una forma estándar para integrar una cadena de versión en un paquete de python.La mayoría de los paquetes que he visto usar algunas variantes de su solución, es decir,eitner

  1. Incrustar la versión en setup.py y han setup.py generar un módulo (por ejemplo, version.py) que contiene sólo información de la versión, que es importado por el paquete, o

  2. A la inversa:poner la información de la versión en su propio paquete, y de importación que para establecer la versión en setup.py

También vale la pena señalar que además de __version__ ser un semi-estándar. en Python también lo es __version_info__ que es una tupla, en los casos simples puedes hacer algo como:

__version__ = '1.2.3'
__version_info__ = tuple([ int(num) for num in __version__.split('.')])

... y puede obtener la cadena <=> de un archivo, o lo que sea.

También vi otro estilo:

>>> django.VERSION
(1, 1, 0, 'final', 0)

arrow lo maneja de una manera interesante.

En arrow/__init__.py:

__version__ = '0.8.0'
VERSION = __version__

En setup.py:

def grep(attrname):
    pattern = r"{0}\W*=\W*'([^']+)'".format(attrname)
    strval, = re.findall(pattern, file_text)
    return strval

setup(
    name='arrow',
    version=grep('__version__'),
    # [...]
)

Para lo que vale, si está usando NumPy distutils, numpy.distutils.misc_util.Configuration tiene un make_svn_version_py() método que incorpora el número de revisión dentro de package.__svn_version__ en la variable version.

  1. El uso de un version.py archivo sólo con __version__ = <VERSION> param en el archivo.En el setup.py importación de archivos de la __version__ param y poner su valor en el setup.py archivo como este:version=__version__
  2. Otra forma es utilizar un setup.py archivo con version=<CURRENT_VERSION> - el CURRENT_VERSION está codificado.

Ya no queremos cambiar manualmente la versión en el archivo cada vez que creamos una nueva etiqueta (listo para lanzar una nueva versión de paquete), podemos utilizar la siguiente..

Recomiendo altamente bumpversion paquete.He estado usando durante años a golpe de una versión.

comience por agregar version=<VERSION> a su setup.py archivo si no lo tiene ya.

Usted debe usar un breve script como este cada vez que das una versión:

bumpversion (patch|minor|major) - choose only one option
git push
git push --tags

A continuación, agregue un archivo por repo llamado: .bumpversion.cfg:

[bumpversion]
current_version = <CURRENT_TAG>
commit = True
tag = True
tag_name = {new_version}
[bumpversion:file:<RELATIVE_PATH_TO_SETUP_FILE>]

Nota:

  • Puede utilizar __version__ parámetro bajo version.py archivo de como se sugirió en otros posts y actualizaciones de la bumpversion archivo como este:[bumpversion:file:<RELATIVE_PATH_TO_VERSION_FILE>]
  • Usted debe git commit o git reset todo en su repo, de lo contrario obtendrá un sucio repo de error.
  • Asegúrese de que su entorno virtual incluye el paquete de bumpversion, sin que esto no va a funcionar.

Si usa CVS (o RCS) y desea una solución rápida, puede usar:

__version__ = "$Revision: 1.1 $"[11:-2]
__version_info__ = tuple([int(s) for s in __version__.split(".")])

(Por supuesto, el número de revisión será sustituido por CVS)

Esto le proporciona una versión para imprimir y una información de versión que puede usar para verificar que el módulo que está importando tiene al menos la versión esperada:

import my_module
assert my_module.__version_info__ >= (1, 1)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top