¿Existe una forma estándar de asociar la cadena de versión con un paquete de Python de manera que pueda hacer lo siguiente?
import foo
print foo.version
Me imagino que hay alguna manera de recuperar esos datos sin ningún tipo de codificación adicional, ya que las cadenas menores / mayores ya están especificadas setup.py
. La solución alternativa que encontré fue tener import __version__
en mi foo/__init__.py
y luego haber __version__.py
generado por setup.py
.
setup.py
es suficiente colocar solo la versión . Ver esta pregunta .Respuestas:
No es directamente una respuesta a su pregunta, pero debería considerar nombrarla
__version__
, noversion
.Esto es casi un cuasi-estándar. Muchos módulos en la biblioteca estándar usan
__version__
, y esto también se usa en muchos módulos de terceros, por lo que es el cuasi-estándar.Por lo general,
__version__
es una cadena, pero a veces también es un flotador o tupla.Editar: como lo menciona S.Lott (¡Gracias!), PEP 8 lo dice explícitamente:
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 este estándar).
fuente
__version_info__
específicamente? (Que "inventa" su propia palabra de doble guión bajo). [Cuando James comentó, los guiones bajos no hicieron nada en los comentarios, ahora indican énfasis, por lo que James realmente__version_info__
también escribió . --- ed.]Utilizo un solo
_version.py
archivo como "lugar canónico" para almacenar la información de la versión:Proporciona un
__version__
atributo.Proporciona la versión estándar de metadatos. Por lo tanto, será detectado por
pkg_resources
u otras herramientas que analicen los metadatos del paquete (EGG-INFO y / o PKG-INFO, PEP 0345).No importa su paquete (o cualquier otra cosa) al compilarlo, lo que puede causar problemas en algunas situaciones. (Vea los comentarios a continuación sobre los problemas que esto puede causar).
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 "único 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 como:Y entonces tu setup.py abre el archivo y lo analiza, con un código como:
Luego, su setup.py pasa esa cadena como el valor del argumento "versión"
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 de
myniftyapp/__init__.py
esta manera:Aquí hay un ejemplo de esta técnica que 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í hay un código de ejemplo para importar la versión .
Si ve algún problema con este enfoque, hágamelo saber.
fuente
setup.py
. Además, si intentara hacer profundidad completa primero y hacer todas las profundidades antes de hacerlo, se atascaría si hubiera deps circulares. Pero si intenta compilar este paquete antes de instalar las dependencias, si importa su paquete desde el suyosetup.py
, no necesariamente podrá importar sus deps o las versiones correctas de sus deps.execfile("myniftyapp/_version.py")
desde setup.py, en lugar de intentar analizar el código de versión manualmente. Sugerido en stackoverflow.com/a/2073599/647002 : la discusión allí también puede ser útil.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.
Comencé a usar el
pbr
paquete para lidiar con el control de versiones en mis paquetes. Si está utilizando git como su SCM, esto encajará en su flujo de trabajo como por arte de magia, ahorrando sus semanas de trabajo (se sorprenderá de lo complejo que puede ser el problema).A partir de hoy, pbr ocupa el puesto número 11 del paquete de python más utilizado y alcanzar este nivel no incluyó ningún truco sucio: era solo uno: solucionar un problema de empaquetado común de una manera muy simple.
pbr
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 al empaque de 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 necesita preocuparse por lo que sucede cuando no tiene un repositorio git porque pbr "compila" y almacena en caché la versión cuando empaqueta o instala las aplicaciones, por lo que no hay dependencia del tiempo de ejecución en git.
Vieja solución
Aquí está la mejor solución que he visto hasta ahora y también explica por qué:
Dentro
yourpackage/version.py
:Dentro
yourpackage/__init__.py
:Dentro
setup.py
:Si conoce otro enfoque que parece ser mejor, hágamelo saber.
fuente
from .version import __version__
en setup.py?setup.py
está ejecutando, pruébalo, obtendrás un error (o al menos lo hice :-))Según el PEP 396 diferido (números de versión del módulo) , hay una forma propuesta de hacerlo. Describe, con justificación, un estándar (ciertamente opcional) para los módulos a seguir. Aquí hay un fragmento:
fuente
Aunque esto probablemente sea demasiado tarde, hay una alternativa un poco más simple a la respuesta anterior:
(Y sería bastante simple convertir porciones de números de versión que se incrementan automáticamente a una cadena usando
str()
).Por supuesto, por lo que he visto, la gente tiende a usar algo como la versión mencionada anteriormente cuando la usa
__version_info__
, y como tal la almacena como una tupla de entradas; sin embargo, no veo el punto de hacerlo, ya que dudo que haya situaciones en las que realice operaciones matemáticas, como sumas y restas en porciones de números de versión para cualquier propósito además de curiosidad o auto-incremento (e incluso entonces,int()
ystr()
se puede usar con bastante facilidad). (Por otro lado, existe la posibilidad de que el código de otra persona espere una tupla numérica en lugar de una tupla de cadena y, por lo tanto, falle).Esta es, por supuesto, mi propio punto de vista, y con gusto me gustaría la opinión de otros sobre el uso de una tupla numérica.
Como me recordó Shezi, las comparaciones (léxicas) de cadenas de números no necesariamente tienen el mismo resultado que las comparaciones numéricas directas; se requerirían ceros iniciales para proporcionar eso. Entonces, al final, almacenar
__version_info__
(o como se llame) como una tupla de valores enteros permitiría comparaciones de versiones más eficientes.fuente
__version_info__ = (1,2,3)
__version_info__ = (0, 1, 0) __version__ = '.'.join(map(str, __version_info__))
__version__ = '.'.join(__version_info__)
es que__version_info__ = ('1', '2', 'beta')
se convertirá1.2.beta
, no1.2beta
o1.2 beta
Muchas de estas soluciones aquí ignoran las
git
etiquetas de versión, lo que aún significa que debe rastrear la versión en varios lugares (incorrecto). Abordé esto con los siguientes objetivos:git
repositoriogit tag
/push
ysetup.py upload
pasos con un solo comando que no toma entradas.Cómo funciona:
A partir de un
make release
comando, se encuentra e incrementa la última versión etiquetada en el repositorio de git. La etiqueta se devuelve aorigin
.Las
Makefile
tiendas de la versión ensrc/_version.py
donde será leído porsetup.py
e incluyó también en el comunicado. ¡No verifique el_version.py
control de la fuente!setup.py
El comando lee la nueva cadena de versiónpackage.__version__
.Detalles:
Makefile
El
release
objetivo siempre incrementa el dígito de la tercera versión, pero puede usarnext_minor_ver
onext_major_ver
para incrementar los otros dígitos. Los comandos se basan en elversionbump.py
script que se registra en la raíz del repositorioversionbump.py
Esto hace el trabajo pesado cómo procesar e incrementar el número de versión
git
.__init__.py
El
my_module/_version.py
archivo se importa amy_module/__init__.py
. Coloque aquí cualquier configuración de instalación estática que desee distribuir con su módulo.setup.py
El último paso es leer la información de la versión del
my_module
módulo.Por supuesto, para que todo esto funcione, tendrá que tener al menos una etiqueta de versión en su repositorio para comenzar.
fuente
versionbump.py
cuando tenemos un increíble paquete de bumpversion para python._version.py
debería rastrearse con el control de versiones?Yo uso un archivo JSON en el paquete dir. Esto se ajusta a los requisitos de Zooko.
Dentro
pkg_dir/pkg_info.json
:Dentro
setup.py
:Dentro
pkg_dir/__init__.py
:También pongo otra información en
pkg_info.json
, como autor. Me gusta usar JSON porque puedo automatizar la administración de metadatos.fuente
También vale la pena señalar que además de
__version__
ser un semi-estándar. en Python también lo__version_info__
que es una tupla, en los casos simples puedes hacer algo como:... y puedes obtener la
__version__
cadena de un archivo, o lo que sea.fuente
__version_info__
?__version_info__ = (1, 2, 3)
y__version__ = '.'.join(map(str, __version_info__))
).__version__ = '.'.join(str(i) for i in __version_info__)
- un poco más largo pero más pitónico.i
no tiene significado,version_num
es un poco largo y ambiguo ...). Incluso tomo la existencia demap()
Python como una fuerte pista de que debería usarse aquí, ya que lo que necesitamos hacer aquí es el caso de uso típico demap()
(uso con una función existente): no veo muchos otros usos razonables.No parece haber una forma estándar de incrustar una cadena de versión en un paquete de Python. La mayoría de los paquetes que he visto usan alguna variante de su solución, es decir, eitner
Incruste la versión
setup.py
ysetup.py
genere un módulo (pversion.py
. Ej. ) Que contenga solo información de la versión, importada por su paquete, oEn el reverso: poner la información de la versión en su propio envase, y la importación que para configurar la versión en
setup.py
fuente
La flecha lo maneja de una manera interesante.
Ahora (desde 2e5031b )
En
arrow/__init__.py
:En
setup.py
:antes de
En
arrow/__init__.py
:En
setup.py
:fuente
file_text
?pip install -e .
en una rama de desarrollo o algo así durante la prueba. setup.py no debe confiar en importar el paquete que está en proceso de instalación para determinar los parámetros para la implementación. YikesTambién vi otro estilo:
fuente
.VERSION
no significa que no tenga que implementar__version__
.django
en el proyecto?Usando
setuptools
ypbr
No hay una forma estándar de administrar la versión, pero la forma estándar de administrar sus paquetes es
setuptools
.La mejor solución que he encontrado en general para administrar la versión es usar
setuptools
con lapbr
extensión. Esta es ahora mi forma estándar de administrar la versión.Configurar su proyecto para el empaquetado completo puede ser excesivo para proyectos simples, pero si necesita administrar la versión, probablemente esté en el nivel correcto para configurar todo. Hacerlo también hace que su paquete sea liberable en PyPi para que todos puedan descargarlo y usarlo con Pip.
PBR mueve la mayoría de los metadatos de las
setup.py
herramientas a unsetup.cfg
archivo que luego se usa como fuente para la mayoría de los metadatos, que puede incluir la versión. Esto permite que los metadatos se empaqueten en un ejecutable usando algo comopyinstaller
si fuera necesario (si es así, probablemente necesitará esta información ), y separa los metadatos de los otros scripts de configuración / administración de paquetes. Puede actualizar directamente la cadena de versión de formasetup.cfg
manual, y se extraerá de la*.egg-info
carpeta al compilar las versiones de su paquete. Sus scripts pueden acceder a la versión de los metadatos utilizando varios métodos (estos procesos se describen en las secciones a continuación).Al usar Git para VCS / SCM, esta configuración es aún mejor, ya que extraerá muchos de los metadatos de Git para que su repositorio pueda ser su fuente principal de verdad para algunos de los metadatos, incluida la versión, los autores, los registros de cambios, etc. Para la versión específicamente, creará una cadena de versión para la confirmación actual basada en las etiquetas git en el repositorio.
setup.py
y unsetup.cfg
archivo con los metadatos.Como PBR extraerá la versión, el autor, el registro de cambios y otra información directamente de su repositorio de git, por lo que algunos de los metadatos
setup.cfg
pueden omitirse y generarse automáticamente cada vez que se crea una distribución para su paquete (usandosetup.py
)Versión actual en tiempo real
setuptools
extraerá la información más reciente en tiempo real usandosetup.py
:Esto extraerá la última versión del
setup.cfg
archivo o del repositorio de git, según la última confirmación realizada y las etiquetas que existen en el repositorio. Sin embargo, este comando no actualiza la versión en una distribución.Actualizando la versión
Cuando crea una distribución con
setup.py
(es decirpy setup.py sdist
, por ejemplo), toda la información actual se extraerá y almacenará en la distribución. Esto esencialmente ejecuta elsetup.py --version
comando y luego almacena esa información de versión en lapackage.egg-info
carpeta en un conjunto de archivos que almacenan metadatos de distribución.Accediendo a la versión desde un script
Puede acceder a los metadatos desde la compilación actual dentro de los scripts de Python en el paquete mismo. Para la versión, por ejemplo, hay varias formas de hacer esto que he encontrado hasta ahora:
Puede poner uno de estos directamente en su
__init__.py
paquete para extraer la información de la versión de la siguiente manera, similar a algunas otras respuestas:fuente
Después de varias horas de tratar de encontrar la solución confiable más simple, estas son las partes:
cree un archivo version.py DENTRO de la carpeta de su paquete "/ mypackage":
en setup.py:
en la carpeta principal init .py:
La
exec()
función ejecuta el script fuera de cualquier importación, ya que setup.py se ejecuta antes de que se pueda importar el módulo. Todavía solo necesita administrar el número de versión en un archivo en un lugar, pero desafortunadamente no está en setup.py. (Esa es la desventaja, pero no tener errores de importación es la ventaja)fuente
Se ha completado mucho trabajo hacia el versionado uniforme y en apoyo de las convenciones desde que se hizo esta pregunta por primera vez . Las opciones de palatable ahora se detallan en la Guía del usuario de Python Packaging . También es digno de mención que los esquemas de número de versión son relativamente estrictos en Python por PEP 440 , por lo que mantener las cosas sanas es fundamental si su paquete se lanzará a la tienda de quesos .
Aquí hay un desglose acortado de las opciones de versiones:
setup.py
( setuptools ) y obtenga la versión.__init__.py
como el control de origen), por ejemplo , bump2version , changes o zest.releaser .__version__
variable global en un módulo específico.setup.py
versión y use importlib.metadata para recogerlo en tiempo de ejecución. (Advertencia, hay versiones anteriores a 3.8 y posteriores a 3.8).__version__
ensample/__init__.py
e importe la muestra ensetup.py
.TENGA EN CUENTA que (7) podría ser el enfoque más moderno (los metadatos de compilación son independientes del código, publicado por automatización). También TENGA EN CUENTA que si se usa la configuración para la liberación del paquete, un simple
python3 setup.py --version
informará la versión directamente.fuente
Por lo que vale, si está usando NumPy distutils,
numpy.distutils.misc_util.Configuration
tiene unmake_svn_version_py()
método que incrusta el número de revisión dentropackage.__svn_version__
de la variableversion
.fuente
version.py
archivo solo con__version__ = <VERSION>
param en el archivo. En elsetup.py
archivo, importe el__version__
parámetro y coloque su valor en elsetup.py
archivo de esta manera:version=__version__
setup.py
archivo conversion=<CURRENT_VERSION>
: CURRENT_VERSION está codificado.Como no queremos cambiar manualmente la versión en el archivo cada vez que creamos una nueva etiqueta (lista para lanzar una nueva versión del paquete), podemos usar lo siguiente ...
Recomiendo mucho el paquete bumpversion . Lo he estado usando durante años para crear una versión.
comience agregando
version=<VERSION>
a susetup.py
archivo si aún no lo tiene.Deberías usar un script corto como este cada vez que topes una versión:
Luego agregue un archivo por repositorio llamado
.bumpversion.cfg
::Nota:
__version__
parámetro debajo delversion.py
archivo como se sugirió en otras publicaciones y actualizar el archivo de bumpversion de esta manera:[bumpversion:file:<RELATIVE_PATH_TO_VERSION_FILE>]
git commit
ogit reset
todo en su cesión temporal, de lo contrario obtendrá un error de recompra sucio.fuente
bumpversion
, sin él no funcionará. Utiliza la última versión.version.py
o con ellabumpversion
?__version__
valor param en el archivo setup.py. Mi solución se usa en producción y es una práctica común. Nota: para ser claros, usar bumpversion como parte de un script es la mejor solución, colóquelo en su CI y será una operación automática.Si usa CVS (o RCS) y desea una solución rápida, puede usar:
(Por supuesto, el número de revisión será sustituido por CVS).
Esto le brinda una versión fácil de 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:
fuente
__version__
? ¿Cómo se incrementaría el número de versión con esta solución?