Existe-t-il un moyen standard d'associer une chaîne de version à un package python de telle manière que je puisse faire ce qui suit?
import foo
print foo.version
J'imagine qu'il existe un moyen de récupérer ces données sans aucun codage supplémentaire, car les chaînes mineures / majeures sont setup.py
déjà spécifiées dans . Une solution alternative que j'ai trouvée devait avoir import __version__
dans mon foo/__init__.py
et ensuite être __version__.py
générée par setup.py
.
setup.py
est suffisant. Voir cette question .Réponses:
Pas directement une réponse à votre question, mais vous devriez envisager de la nommer
__version__
, pasversion
.Il s'agit presque d'un quasi-standard. De nombreux modules dans la bibliothèque standard utilisent
__version__
, et cela est également utilisé en lots modules tiers, c'est donc le quasi-standard.Habituellement,
__version__
c'est une chaîne, mais parfois c'est aussi un flottant ou un tuple.Edit: comme mentionné par S.Lott (Merci!), PEP 8 le dit explicitement:
Vous devez également vous assurer que le numéro de version est conforme au format décrit dans PEP 440 ( PEP 386 une version précédente de cette norme).
la source
__version_info__
spécifiquement? (Qui "invente" votre propre double-soulignement.) [Lorsque James a commenté, les soulignés n'ont rien fait dans les commentaires, maintenant ils indiquent l'accent, donc James a vraiment écrit__version_info__
aussi. --- ed.]J'utilise un seul
_version.py
fichier comme "lieu une fois canonique" pour stocker les informations de version:Il fournit un
__version__
attribut.Il fournit la version standard des métadonnées. Par conséquent, il sera détecté par
pkg_resources
ou d'autres outils qui analysent les métadonnées du package (EGG-INFO et / ou PKG-INFO, PEP 0345).Il n'importe pas votre package (ou toute autre chose) lors de la construction de votre package, ce qui peut provoquer des problèmes dans certaines situations. (Voir les commentaires ci-dessous sur les problèmes que cela peut causer.)
Il n'y a qu'un seul endroit où le numéro de version est écrit, donc il n'y a qu'un seul endroit pour le changer lorsque le numéro de version change, et il y a moins de chance de versions incohérentes.
Voici comment cela fonctionne: le "seul endroit canonique" pour stocker le numéro de version est un fichier .py, nommé "_version.py" qui se trouve dans votre package Python, par exemple dans
myniftyapp/_version.py
. Ce fichier est un module Python, mais votre setup.py ne l'importe pas! (Cela annulerait la fonctionnalité 3.) Au lieu de cela, votre setup.py sait que le contenu de ce fichier est très simple, quelque chose comme:Et donc votre setup.py ouvre le fichier et l'analyse, avec du code comme:
Ensuite, votre setup.py transmet cette chaîne en tant que valeur de l'argument "version" à
setup()
, satisfaisant ainsi la fonctionnalité 2.Pour satisfaire la fonctionnalité 1, vous pouvez demander à votre package (au moment de l'exécution, pas au moment de l'installation!) D'importer le fichier _version
myniftyapp/__init__.py
comme ceci:Voici un exemple de cette technique que j'utilise depuis des années.
Le code de cet exemple est un peu plus compliqué, mais l'exemple simplifié que j'ai écrit dans ce commentaire devrait être une implémentation complète.
Voici un exemple de code d'importation de la version .
Si vous voyez quelque chose de mal avec cette approche, faites-le moi savoir.
la source
setup.py
. De plus, s'il essayait de faire la profondeur en premier et de faire tous les dépôts avant de faire celui-ci, il resterait coincé s'il y avait des dépôts circulaires. Mais s'il essaie de construire ce paquet avant d'installer les dépendances, alors si vous importez votre paquet depuis votresetup.py
, il ne pourra pas nécessairement importer ses deps, ou les bonnes versions de ses deps.execfile("myniftyapp/_version.py")
partir de setup.py, plutôt que d'essayer d'analyser le code de version manuellement. Suggéré dans stackoverflow.com/a/2073599/647002 - une discussion peut également être utile.Réécrit 2017-05
Après plus de dix ans d'écriture de code Python et de gestion de divers packages, je suis arrivé à la conclusion que le bricolage n'est peut-être pas la meilleure approche.
J'ai commencé à utiliser le
pbr
package pour gérer les versions dans mes packages. Si vous utilisez git comme SCM, cela s'intégrera à votre flux de travail comme par magie, économisant ainsi vos semaines de travail (vous serez surpris de la complexité du problème).À ce jour, pbr est classé n ° 11 des packages python les plus utilisés et atteindre ce niveau n'incluait aucun truc sale: n'en était qu'un: résoudre un problème de packaging commun de manière très simple.
pbr
peut faire plus de la charge de maintenance des packages, ne se limite pas au versioning mais ne vous oblige pas à adopter tous ses avantages.Donc, pour vous donner une idée de la façon dont il semble adopter pbr en un seul commit, jetez un œil à l' emballage pbr
Vous auriez probablement remarqué que la version n'est pas du tout stockée dans le référentiel. PBR le détecte à partir des branches et des balises Git.
Pas besoin de vous soucier de ce qui se passe lorsque vous n'avez pas de référentiel git car pbr "compile" et met en cache la version lorsque vous empaquetez ou installez les applications, il n'y a donc pas de dépendance d'exécution sur git.
Ancienne solution
Voici la meilleure solution que j'ai vue jusqu'à présent et elle explique également pourquoi:
À l'intérieur
yourpackage/version.py
:À l'intérieur
yourpackage/__init__.py
:À l'intérieur
setup.py
:Si vous connaissez une autre approche qui semble meilleure, faites-le moi savoir.
la source
from .version import __version__
dans setup.py aussi?setup.py
est en cours d'exécution - essayez-le, vous obtiendrez une erreur (ou du moins, je l'ai fait :-))Selon le PEP 396 différé (numéros de version de module) , il existe une méthode proposée pour le faire. Il décrit, avec justification, une norme (certes facultative) pour les modules à suivre. Voici un extrait:
la source
Bien que ce soit probablement beaucoup trop tard, il existe une alternative légèrement plus simple à la réponse précédente:
(Et il serait assez simple de convertir des portions de numéros de version à incrémentation automatique en une chaîne à l'aide
str()
.)Bien sûr, d'après ce que j'ai vu, les gens ont tendance à utiliser quelque chose comme la version mentionnée précédemment lors de l'utilisation
__version_info__
, et en tant que telle, à la stocker comme un tuple d'ints; cependant, je ne vois pas vraiment l'intérêt de le faire, car je doute qu'il y ait des situations où vous effectueriez des opérations mathématiques telles que l'addition et la soustraction sur des parties de numéros de version à des fins autres que la curiosité ou l'incrémentation automatique (et même alors,int()
etstr()
peut être utilisé assez facilement). (D'un autre côté, il est possible que le code de quelqu'un d'autre s'attende à un tuple numérique plutôt qu'à un tuple de chaîne et échoue donc.)Ceci est, bien sûr, mon propre point de vue, et j'aimerais volontiers que les autres me disent comment utiliser un tuple numérique.
Comme Shezi me l'a rappelé, les comparaisons (lexicales) de chaînes de nombres n'ont pas nécessairement le même résultat que les comparaisons numériques directes; les zéros en tête seraient tenus de le prévoir. Ainsi, au final, le stockage
__version_info__
(ou son nom) sous la forme d'un tuple de valeurs entières permettrait des comparaisons de versions plus efficaces.la source
__version_info__ = (1,2,3)
__version_info__ = (0, 1, 0) __version__ = '.'.join(map(str, __version_info__))
__version__ = '.'.join(__version_info__)
est que__version_info__ = ('1', '2', 'beta')
cela deviendra1.2.beta
, pas1.2beta
ou1.2 beta
Beaucoup de ces solutions ignorent les
git
balises de version, ce qui signifie que vous devez suivre la version à plusieurs endroits (mauvais). Je l'ai abordé avec les objectifs suivants:git
référentielgit tag
/push
et lessetup.py upload
étapes avec une seule commande qui ne prend aucune entrée.Comment ça fonctionne:
À partir d'une
make release
commande, la dernière version balisée du dépôt git est trouvée et incrémentée. La balise est repoussée àorigin
.Le
Makefile
stocke la version danssrc/_version.py
laquelle elle sera luesetup.py
et également incluse dans la version. Ne vérifiez pas le contrôle de_version.py
source!setup.py
commande lit la nouvelle chaîne de versionpackage.__version__
.Détails:
Makefile
La
release
cible incrémente toujours le 3e chiffre de la version, mais vous pouvez utiliser lenext_minor_ver
ounext_major_ver
pour incrémenter les autres chiffres. Les commandes reposent surversionbump.py
script qui est archivé à la racine du dépôtversionbump.py
Cela fait le gros du travail comment traiter et incrémenter le numéro de version de
git
.__init__.py
Le
my_module/_version.py
fichier est importé dansmy_module/__init__.py
. Mettez ici toute configuration d'installation statique que vous souhaitez distribuer avec votre module.setup.py
La dernière étape consiste à lire les informations de version du
my_module
module.Bien sûr, pour que tout cela fonctionne, vous devrez avoir au moins une balise de version dans votre référentiel pour commencer.
la source
versionbump.py
quand nous avons un package bumpversion génial pour python._version.py
être suivi avec le contrôle de version?J'utilise un fichier JSON dans le répertoire du package. Cela correspond aux exigences de Zooko.
À l'intérieur
pkg_dir/pkg_info.json
:À l'intérieur
setup.py
:À l'intérieur
pkg_dir/__init__.py
:J'ai également mis d'autres informations
pkg_info.json
, comme l'auteur. J'aime utiliser JSON car je peux automatiser la gestion des métadonnées.la source
Il convient également de noter que, en plus d'
__version__
être semi-standard. en python est donc__version_info__
ce qui est un tuple, dans les cas simples, vous pouvez simplement faire quelque chose comme:... et vous pouvez obtenir la
__version__
chaîne à partir d'un fichier, ou autre chose.la source
__version_info__
?__version_info__ = (1, 2, 3)
et__version__ = '.'.join(map(str, __version_info__))
).__version__ = '.'.join(str(i) for i in __version_info__)
- légèrement plus long mais plus pythonique.i
n'a pas de sens,version_num
est un peu long et ambigu…). Je prends même l'existence demap()
Python comme un indice fort qu'il devrait être utilisé ici, car ce que nous devons faire ici est le cas d'utilisation typique demap()
(utilisation avec une fonction existante) - je ne vois pas beaucoup d'autres utilisations raisonnables.Il ne semble pas exister de méthode standard pour incorporer une chaîne de version dans un package python. La plupart des packages que j'ai vus utilisent une variante de votre solution, c'est-à-dire eitner
Incorporez la version
setup.py
etsetup.py
générez un module (par exempleversion.py
) contenant uniquement les informations de version, qui sont importées par votre package, ouL'inverse: mettez les informations de version dans votre package lui-même, et importez -les pour définir la version dans
setup.py
la source
la flèche le gère d'une manière intéressante.
Maintenant (depuis 2e5031b )
Dans
arrow/__init__.py
:Dans
setup.py
:Avant
Dans
arrow/__init__.py
:Dans
setup.py
:la source
file_text
?pip install -e .
sur une branche de développement ou quelque chose lors des tests. setup.py ne doit absolument pas s'appuyer sur l'importation du package qu'il est en train d'installer pour déterminer les paramètres du déploiement. Oui.J'ai aussi vu un autre style:
la source
.VERSION
ne signifie pas que vous n'avez pas à l'implémenter__version__
.django
en œuvre dans le projet?En utilisant
setuptools
etpbr
Il n'y a pas de façon standard de gérer la version, mais la façon standard de gérer vos packages est
setuptools
.La meilleure solution que j'ai trouvée dans l'ensemble pour gérer la version est d'utiliser
setuptools
avec l'pbr
extension. C'est maintenant ma façon standard de gérer la version.La configuration de votre projet pour un emballage complet peut être exagérée pour des projets simples, mais si vous avez besoin de gérer la version, vous êtes probablement au bon niveau pour tout configurer. Cela rend également votre package libérable sur PyPi afin que tout le monde puisse le télécharger et l'utiliser avec Pip.
PBR déplace la plupart des métadonnées hors des
setup.py
outils et dans unsetup.cfg
fichier qui est ensuite utilisé comme source pour la plupart des métadonnées, qui peuvent inclure la version. Cela permet aux métadonnées d'être empaquetées dans un exécutable en utilisant quelque chose commepyinstaller
si nécessaire (si c'est le cas, vous aurez probablement besoin de ces informations ), et sépare les métadonnées des autres scripts de gestion / configuration de package. Vous pouvez directement mettre à jour la chaîne de version danssetup.cfg
manuellement, et elle sera tirée dans le*.egg-info
dossier lors de la création des versions de votre package. Vos scripts peuvent ensuite accéder à la version à partir des métadonnées en utilisant différentes méthodes (ces processus sont décrits dans les sections ci-dessous).Lorsque vous utilisez Git pour VCS / SCM, cette configuration est encore meilleure, car elle extrait de nombreuses métadonnées de Git afin que votre référentiel puisse être votre principale source de vérité pour certaines des métadonnées, y compris la version, les auteurs, les journaux des modifications, etc. Pour la version en particulier, il créera une chaîne de version pour la validation actuelle basée sur les balises git dans le référentiel.
setup.py
et unsetup.cfg
fichier avec les métadonnées.Comme PBR extraira la version, l'auteur, le journal des modifications et d'autres informations directement à partir de votre dépôt git, certaines des métadonnées
setup.cfg
peuvent être omises et générées automatiquement chaque fois qu'une distribution est créée pour votre package (en utilisantsetup.py
)Version actuelle en temps réel
setuptools
tirera les dernières informations en temps réel en utilisantsetup.py
:Cela extraira la dernière version du
setup.cfg
fichier ou du dépôt git, en fonction de la dernière validation effectuée et des balises qui existent dans le dépôt. Cette commande ne met cependant pas à jour la version dans une distribution.Mise à jour de la version
Lorsque vous créez une distribution avec
setup.py
(c'est-àpy setup.py sdist
- dire , par exemple), toutes les informations actuelles seront extraites et stockées dans la distribution. Cela exécute essentiellement lasetup.py --version
commande, puis stocke ces informations de version dans lepackage.egg-info
dossier dans un ensemble de fichiers qui stockent les métadonnées de distribution.Accéder à la version à partir d'un script
Vous pouvez accéder aux métadonnées à partir de la version actuelle dans les scripts Python du package lui-même. Pour la version, par exemple, il y a plusieurs façons de le faire jusqu'à présent:
Vous pouvez en mettre un directement dans votre
__init__.py
package pour extraire les informations de version comme suit, de la même manière que pour certaines autres réponses:la source
Après plusieurs heures à essayer de trouver la solution fiable la plus simple, voici les pièces:
créez un fichier version.py DANS le dossier de votre package "/ mypackage":
dans setup.py:
dans le dossier principal init .py:
La
exec()
fonction exécute le script en dehors de toute importation, car setup.py est exécuté avant que le module puisse être importé. Il vous suffit de gérer le numéro de version dans un fichier en un seul endroit, mais malheureusement ce n'est pas dans setup.py. (c'est l'inconvénient, mais l'absence de bogues d'importation est l'avantage)la source
Beaucoup de travail vers un versionnage uniforme et à l'appui des conventions a été accompli depuis la première question posée . Les options de goût sont maintenant détaillées dans le Guide de l'utilisateur de l'emballage Python . Il convient également de noter que les schémas de numéros de version sont relativement stricts en Python selon PEP 440 , et donc garder les choses saines est essentiel si votre package sera publié dans la boutique de fromage .
Voici une ventilation raccourcie des options de version:
setup.py
( setuptools ) et obtenez la version.__init__.py
ainsi que le contrôle des sources), par exemple bump2version , changes ou zest.releaser .__version__
variable globale dans un module spécifique.setup.py
version et utilisez importlib.metadata pour la récupérer lors de l'exécution. (Attention, il existe des versions pré-3.8 et post-3.8.)__version__
insample/__init__.py
et importez l'échantillon danssetup.py
.NOTEZ que (7) pourrait être l' approche la plus moderne (les métadonnées de construction sont indépendantes du code, publiées par l'automatisation). En outre NOTE que si l' installation est utilisée pour la libération du package simple
python3 setup.py --version
rapportera directement la version.la source
Pour ce que ça vaut, si vous utilisez des distutils NumPy,
numpy.distutils.misc_util.Configuration
a unemake_svn_version_py()
méthode qui incorpore le numéro de révision à l'intérieurpackage.__svn_version__
de la variableversion
.la source
version.py
fichier uniquement avec__version__ = <VERSION>
param dans le fichier. Dans lesetup.py
fichier, importez le__version__
paramètre et mettez sa valeur dans lesetup.py
fichier comme ceci:version=__version__
setup.py
fichier avecversion=<CURRENT_VERSION>
- la CURRENT_VERSION est codée en dur.Puisque nous ne voulons pas changer manuellement la version dans le fichier chaque fois que nous créons une nouvelle balise (prête à publier une nouvelle version de package), nous pouvons utiliser ce qui suit.
Je recommande fortement le package bumpversion . Je l'utilise depuis des années pour bump une version.
commencez par ajouter
version=<VERSION>
à votresetup.py
fichier si vous ne l'avez pas déjà.Vous devriez utiliser un court script comme celui-ci chaque fois que vous augmentez une version:
Ajoutez ensuite un fichier par dépôt appelé
.bumpversion.cfg
::Remarque:
__version__
paramètre sous leversion.py
fichier comme cela a été suggéré dans d'autres articles et mettre à jour le fichier bumpversion comme ceci:[bumpversion:file:<RELATIVE_PATH_TO_VERSION_FILE>]
git commit
ougit reset
tout dans votre repo, sinon vous obtiendrez une erreur de repo sale.la source
bumpversion
, sans cela cela ne fonctionnera pas. Utilisez la dernière version.version.py
ou le suivi avecbumpversion
?__version__
valeur param dans le fichier setup.py. Ma solution est utilisée en production et c'est une pratique courante. Remarque: juste pour être clair, utiliser bumpversion dans le cadre d'un script est la meilleure solution, mettez-le dans votre CI et ce sera un fonctionnement automatique.Si vous utilisez CVS (ou RCS) et souhaitez une solution rapide, vous pouvez utiliser:
(Bien sûr, le numéro de révision vous sera remplacé par CVS.)
Cela vous donne une version imprimable et des informations de version que vous pouvez utiliser pour vérifier que le module que vous importez a au moins la version attendue:
la source
__version__
? Comment incrémenter le numéro de version avec cette solution?