wheel easy python setuptools

python - easy - setuptools windows 10



¿Cómo puedo obtener la versión definida en setup.py(setuptools) en mi paquete? (11)

estudio de ejemplo: mymodule

Imagina esta configuración:

setup.py mymodule/ / __init__.py / version.py / myclasses.py

Luego imagina un escenario habitual donde tengas dependencias y setup.py ve así:

setup(... install_requires=[''dep1'',''dep2'', ...] ...)

Y un ejemplo __init__.py :

from mymodule.myclasses import * from mymodule.version import __version__

Y por ejemplo myclasses.py :

# these are not installed on your system. # importing mymodule.myclasses would give ImportError import dep1 import dep2

problema # 1: importación de mymodule durante la instalación

Si su setup.py importa mymodule entonces durante la instalación es muy probable que obtenga un ImportError . Este es un error muy común cuando su paquete tiene dependencias. Si su paquete no tiene otras dependencias que las compiladas, puede estar seguro; sin embargo, esta no es una buena práctica. La razón de esto es que no es a prueba de futuro; digamos que mañana tu código necesita consumir alguna otra dependencia.

problema # 2: ¿dónde está mi __version__ ?

Si codifica __version__ en setup.py , es posible que no coincida con la versión que setup.py en su módulo. Para ser coherente, lo pondría en un lugar y lo leería desde el mismo lugar cuando lo necesitara. Usando import , puede obtener el problema # 1.

solución: à la setuptools

Utilizaría una combinación de open , exec y proporcionaría un dict para que exec agregue variables:

# setup.py from setuptools import setup, find_packages from distutils.util import convert_path main_ns = {} ver_path = convert_path(''mymodule/version.py'') with open(ver_path) as ver_file: exec(ver_file.read(), main_ns) setup(..., version=main_ns[''__version__''], ...)

Y en mymodule/version.py exponer la versión:

__version__ = ''some.semantic.version''

De esta forma, la versión se envía con el módulo, y no tiene problemas durante la configuración al intentar importar un módulo que tiene dependencias faltantes (aún por instalar).

¿Cómo podría obtener la versión definida en setup.py de mi paquete (para --version , u otros propósitos)?


Versión Interrogate cadena de distribución ya instalada

Para recuperar la versión desde el interior de su paquete en tiempo de ejecución (lo que parece que su pregunta realmente es), puede usar:

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

Almacene cadena de versión para usar durante la instalación

Si quiere ir al revés (que parece ser lo que otros autores de la respuesta parecen haber pensado que estaba haciendo), coloque la cadena de la versión en un archivo separado y lea los contenidos de ese archivo en setup.py .

Puede hacer un version.py en su paquete con una línea __version__ , luego leerlo desde setup.py utilizando execfile(''mypackage/version.py'') , de modo que establezca __version__ en el espacio de nombres setup.py.

Si desea una forma mucho más simple, que funcionará con todas las versiones de Python e incluso con lenguajes que no sean de Python que puedan necesitar acceso a la cadena de versión:

Almacene la cadena de versión como único contenido de un archivo de texto sin formato, llamado, por ejemplo VERSION , y lea ese archivo durante setup.py .

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

El mismo archivo VERSION funcionará exactamente igual en cualquier otro programa, incluso en los que no sean de Python, y solo necesitará cambiar la cadena de versión en un lugar para todos los programas.

Advertencia sobre la condición de carrera durante la instalación

Por cierto, NO importe su paquete desde su setup.py como se sugiere en otra respuesta aquí: parecerá que funciona para usted (porque ya tiene instaladas las dependencias de su paquete), pero causará estragos en los nuevos usuarios de su paquete , ya que no podrán instalar su paquete sin instalar manualmente las dependencias primero.


Ahora esto es asqueroso y necesita algo de refinamiento (incluso puede haber una llamada de miembro descubierta en pkg_resources que me perdí), pero simplemente no veo por qué esto no funciona, ni por qué nadie lo ha sugerido hasta la fecha (Google ha funcionado no se volvió a esto) ... tenga en cuenta que este es Python 2.x, y requeriría requerir pkg_resources (suspiro):

import pkg_resources version_string = None try: if pkg_resources.working_set is not None: disto_obj = pkg_resources.working_set.by_key.get(''<my pkg name>'', None) # (I like adding ", None" to gets) if disto_obj is not None: version_string = disto_obj.version except Exception: # Do something pass


Cree un archivo en su árbol fuente, por ejemplo, en subasedir / yourpackage / _version.py. Deje que el archivo contenga solo una línea de código, como esta:

__version__ = "1.1.0-r4704"

Luego, en su setup.py, abra ese archivo y analice el número de versión de esta manera:

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")

Finalmente, en su base de yourbasedir/yourpackage/__init__.py import_version como este:

__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

Un ejemplo de código que hace esto es el paquete "pyutil" que mantengo. (Consulte la búsqueda de PyPI o de google: me impide incluir un hipervínculo en esta respuesta).

@pjeby tiene razón al decir que no debe importar su paquete desde su propio setup.py. Eso funcionará cuando lo pruebes creando un nuevo intérprete de Python y ejecutando setup.py primero: python setup.py , pero hay casos en que no funcionará. Eso es porque import youpackage no significa leer el directorio de trabajo actual para un directorio llamado "tu paquete", significa buscar en el sys.modules actual una clave "tu paquete" y luego hacer varias cosas si no está allí . Por lo tanto, siempre funciona cuando realiza python setup.py porque tiene sys.modules frescos y vacíos, pero esto no funciona en general.

Por ejemplo, ¿qué pasa si py2exe está ejecutando su setup.py como parte del proceso de empaquetado de una aplicación? He visto un caso como este en el que py2exe pondría el número de versión incorrecto en un paquete porque el paquete import myownthing su número de versión import myownthing en su setup.py, pero una versión diferente de ese paquete había sido previamente importada durante el py2exe correr. Del mismo modo, ¿qué pasa si setuptools, easy_install, distribute o distutils2 está intentando construir su paquete como parte de un proceso de instalación de un paquete diferente que depende del suyo? Luego, si su paquete es importable en el momento en que se evalúa su setup.py, si ya se ha importado una versión de su paquete durante la vida del intérprete de Python, o si la importación de su paquete requiere la instalación de otros paquetes primero. o tiene efectos secundarios, puede cambiar los resultados. He tenido varias dificultades para tratar de volver a utilizar los paquetes de Python que causaron problemas para herramientas como py2exe y setuptools porque setup.py importa el paquete para encontrar su número de versión.

Por cierto, esta técnica funciona muy bien con herramientas para crear automáticamente el archivo yourpackage/_version.py para usted, por ejemplo, leyendo su historial de control de revisión y escribiendo un número de versión basado en la etiqueta más reciente en el historial de control de revisiones. Aquí hay una herramienta que hace eso para darcs: http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst y aquí hay un fragmento de código que hace lo mismo para git: http://github.com/warner/python-ecdsa/blob/0ed702a9d4057ecf33eea969b8cf280eaccd89a1/setup.py#L34


Esto también debería funcionar, usando expresiones regulares y dependiendo de los campos de metadatos para tener un formato como este:

__fieldname__ = ''value''

Use lo siguiente al comienzo de su setup.py:

import re main_py = open(''yourmodule.py'').read() metadata = dict(re.findall("__([a-z]+)__ = ''([^'']+)''", main_py))

Después de eso, puede usar los metadatos en su script de esta manera:

print ''Author is:'', metadata[''author''] print ''Version is:'', metadata[''version'']


Hay mil maneras de despellejar a un gato: aquí está el mío:

# Copied from (and hacked): # https://github.com/pypa/virtualenv/blob/develop/setup.py#L42 def get_version(filename): import os import re here = os.path.dirname(os.path.abspath(__file__)) f = open(os.path.join(here, filename)) version_file = f.read() f.close() version_match = re.search(r"^__version__ = [''/"]([^''/"]*)[''/"]", version_file, re.M) if version_match: return version_match.group(1) raise RuntimeError("Unable to find version string.")


La mejor técnica es definir __version__ en su código de producto, luego importarlo en setup.py desde allí. Esto le da un valor que puede leer en su módulo en ejecución y solo tiene un lugar para definirlo.

Los valores en setup.py no están instalados, y setup.py no se queda después de la instalación.

Lo que hice (por ejemplo) en coverage.py:

# coverage/__init__.py __version__ = "3.2" # setup.py from coverage import __version__ setup( name = ''coverage'', version = __version__, ... )

ACTUALIZACIÓN (2017): coverage.py ya no se importa para obtener la versión. Importar su propio código puede hacerlo desinstalable, porque su código de producto intentará importar dependencias, que aún no están instaladas, porque setup.py es lo que las instala.


Limpiar share de @ gringo-suave:

from itertools import ifilter from os import path from ast import parse with open(path.join(''package_name'', ''__init__.py'')) as f: __version__ = parse(next(ifilter(lambda line: line.startswith(''__version__''), f))).body[0].value.s


No estaba contento con estas respuestas ... no quería requerir setuptools, ni crear un módulo completo para una sola variable, así que se me ocurrió esto.

Para cuando esté seguro de que el módulo principal está en el estilo pep8 y seguirá así:

version = ''0.30.unknown'' with file(''mypkg/mymod.py'') as f: for line in f: if line.startswith(''__version__''): _, _, version = line.replace("''", '''').split() break

Si quieres ser más cuidadoso y usar un analizador real:

import ast version = ''0.30.unknown2'' with file(''mypkg/mymod.py'') as f: for line in f: if line.startswith(''__version__''): version = ast.parse(line).body[0].value.s break

setup.py es un módulo desechable, por lo que no es un problema si es un poco feo.

print `version`


Para evitar importar un archivo (y así ejecutar su código) uno podría analizarlo y recuperar el atributo de version del árbol de sintaxis:

# 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

Este código intenta encontrar la __version__ o la asignación de VERSION en el nivel superior del retorno del módulo es el valor de la cadena. El lado derecho puede ser una cuerda o una tupla.


Su pregunta es un poco vaga, pero creo que lo que está preguntando es cómo especificarlo.

Necesitas definir __version__ como tal:

__version__ = ''1.4.4''

Y luego puede confirmar que setup.py conoce la versión que acaba de especificar:

% ./setup.py --version 1.4.4