paquetes modulos lista instalar python string package

modulos - paquetes de python



¿Manera estándar de incrustar la versión en el paquete de Python? (14)

¿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 forma de recuperar esos datos sin ninguna codificación adicional, ya que las cadenas menores / mayores ya están especificadas en setup.py . La solución alternativa que encontré fue tener import __version__ en mi foo/__init__.py y luego tener __version__.py generado por setup.py .


Reescrito 2017-05

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

Comencé a usar el paquete pbr para manejar el control de versiones en mis paquetes. Si está utilizando git como su SCM, esto encajará en 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 ocupa el puesto número 11 en el paquete de python más utilizado y, al llegar a este nivel, no incluía ningún truco: solo era 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 a las versiones, pero no le obliga a adoptar todos sus beneficios.

Así que para darle una idea de cómo se ve adoptar pbr en un solo compromiso, eche un vistazo al paquete de pbr

Probablemente usted observaría que la versión no está almacenada en absoluto en el repositorio. PBR lo detecta a partir de ramas y etiquetas de Git.

No tiene que 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 de tiempo de ejecución en git.

Antigua solucion

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

Dentro de tu yourpackage/version.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 su yourpackage/__init__.py :

from .version import __version__

Dentro de setup.py :

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

Si conoces otro enfoque que parece ser mejor házmelo saber.


  1. Use un archivo version.py solo con __version__ = <VERSION> param en el archivo. En el archivo setup.py , importe el __version__ y ponga su valor en el archivo setup.py esta manera: version=__version__
  2. Otra forma es usar solo un archivo setup.py con version=<CURRENT_VERSION> - la CURRENT_VERSION está codificada.

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 altamente el paquete bumpversion . Lo he estado utilizando durante años para golpear una versión.

Comience agregando version=<VERSION> a su archivo setup.py si aún no lo tiene.

Debe usar un script corto como este cada vez que golpee una versión:

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

Luego agregue un archivo por repositorio 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 usar el parámetro __version__ en el archivo version.py como se sugirió en otras publicaciones y actualizar el archivo bumpversion así: [bumpversion:file:<RELATIVE_PATH_TO_VERSION_FILE>]
  • Debe git commit o git reset todo en su repositorio, de lo contrario obtendrá un error de repo sucio.
  • Asegúrese de que su entorno virtual incluya el paquete de bumpversion, sin él no funcionará.

Aunque esto probablemente sea demasiado tarde, hay una alternativa un poco más simple a la respuesta anterior:

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

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

Por supuesto, por lo que he visto, las personas tienden a usar algo como la versión mencionada anteriormente cuando usan __version_info__ , y como tal la almacenan como una tupla de entradas; sin embargo, no veo el punto de hacerlo, ya que dudo que existan situaciones en las que pueda realizar operaciones matemáticas como la suma y la resta de partes de números de versión para cualquier propósito, además de la curiosidad o el incremento automático (e incluso entonces, int() y str() se pueden 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).

Este es, por supuesto, mi propio punto de vista, y me encantaría recibir comentarios de otros sobre el uso de una tupla numérica.

Como me recordó Shezi, las comparaciones (léxicas) de cadenas de números no tienen necesariamente el mismo resultado que las comparaciones numéricas directas; Los ceros iniciales serían requeridos para eso. Así que al final, almacenar __version_info__ (o como se llame) como una tupla de valores enteros permitiría comparaciones de versión más eficientes.


Muchas de estas soluciones aquí ignoran las etiquetas de versión de git , lo que aún significa que tienes que rastrear la versión en varios lugares (incorrecto). Me acerqué a esto con los siguientes objetivos:

  • Derive todas las referencias de la versión de Python de una etiqueta en el repositorio de git
  • Automatice git tag / push y setup.py upload pasos con un solo comando que no toma entradas.

Cómo funciona:

  1. Desde un comando make release , se encuentra y se incrementa la última versión etiquetada en el repositorio de git. La etiqueta es empujada de nuevo al origin .

  2. El Makefile almacena la versión en src/_version.py donde se leerá por setup.py y también se incluirá en la versión. ¡No compruebe _version.py en el control de código fuente!

  3. setup.py comando setup.py lee la nueva cadena de versión del 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 objetivo de release siempre incrementa el dígito de la tercera versión, pero puede usar el next_minor_ver o next_major_ver para incrementar los otros dígitos. Los comandos se basan en el script versionbump.py que está registrado en la raíz del repositorio

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 e incrementar el número de versión de git .

__init__.py

El archivo my_module/_version.py se importa a my_module/__init__.py . Ponga aquí cualquier configuración de instalación estática que desee distribuir con su módulo.

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

setup.py

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

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 funcione tendrá que tener al menos una etiqueta de versión en su repositorio para comenzar.

git tag -a v0.0.1


No directamente una respuesta a su pregunta, pero debe considerar nombrarla __version__ , no version .

Esto es casi un cuasi estándar. Muchos módulos en la biblioteca estándar usan __version__ , y esto también se usa en lots módulos de terceros, por lo que es el casi estándar.

Por lo general, __version__ es una cadena, pero a veces también es un float o una tupla.

Edit: como lo menciona S.Lott (¡Gracias!), PEP 8 lo dice explícitamente:

Versión de contabilidad

Si tiene que tener Subversion, CVS o RCS crud en su archivo fuente, hágalo 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 de cualquier otro código, separadas por una línea en blanco arriba y abajo.

También debe asegurarse de que el número de versión cumpla con el formato descrito en PEP 440 ( PEP 386 una versión anterior de este estándar).


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 utilizan alguna variante de su solución, es decir, eitner

  1. setup.py la versión en setup.py y setup.py que setup.py genere un módulo (por ejemplo, version.py ) que contenga solo información de la versión, que sea importada por su paquete, o

  2. Lo contrario: coloque la información de la versión en su paquete, e impórtela para configurar la versión en setup.py


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


Según el PEP diferido 396 (Números de versión de módulo) , hay una forma propuesta de hacerlo. Describe, con razón, un estándar (ciertamente opcional) para los módulos a seguir. Aquí hay un fragmento de código:

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 __version__ . El propio paquete de espacio de nombres NO DEBE incluir su propio atributo __version__ .

5) El valor del atributo __version__ DEBE ser una cadena.


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 usted por CVS).

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

import my_module assert my_module.__version_info__ >= (1, 1)


También vale la pena señalar que, además de que __version__ es 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 __version__ de un archivo, o lo que sea.


También vi otro estilo:

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


Utilizo un solo archivo _version.py como el "lugar antes canónico" para almacenar la 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 crear su paquete, lo que puede causar problemas en algunas situaciones. (Vea los comentarios a continuación sobre los problemas que esto puede causar).

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

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

__version__ = "3.6.5"

Y así, su 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,))

Luego, su setup.py pasa esa cadena como el valor del argumento de "versión" a setup() , por lo que satisface la característica 2.

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

from _version import __version__

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í está el código de ejemplo de importación de la versión .

Si ve algo malo con este enfoque, hágamelo saber.


Yo uso un archivo JSON en el directorio de paquetes. 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 puse otra información en pkg_info.json , como autor. Me gusta usar JSON porque puedo automatizar la administración de metadatos.


arrow maneja de una manera interesante.

En la 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__''), # [...] )