--Expliquer comment créer un package Python --Expliquez ce dont vous voulez être conscient lors de la création d'un package Python ――Cet article est Poème
Nous prévoyons d'avoir un stagiaire cet été, mais ils n'ont jamais fait de package.
Par contre, en tant qu'entreprise, si vous ne le conditionnez pas, il faudra du temps pour l'utiliser dans la pratique, ce qui est difficile.
Par conséquent, j'ai écrit un document "Comment créer un package Python" à usage interne. Laissez le stagiaire lire ceci et faites un joli paquet! C'est un plan pratique.
Cependant, je n'ai pas étudié correctement et systématiquement le savoir-faire en matière d'emballage, donc cela m'inquiète. Alors, j'ai décidé de publier un poème sur Qiita et de faire corriger le poème.
Veuillez pardonner que le japonais est étrange à certains endroits. Je ne suis pas pratique en japonais. [^ 1]
En d'autres termes, ce sont les connaissances préalables que possède l'étudiant stagiaire.
↓ Poème d'ici
En fait, il n'y a pas de structure de répertoires officielle dans le monde Python. Le code fonctionne même si vous avez une structure de répertoires bâclée. Cependant, cela rendrait le code très déroutant pour les autres développeurs. C'est pourquoi nous avons besoin d'une structure de répertoires facile à comprendre.
Je recommande donc de suivre la méthode sur ce blog. Ce blog clone le Github Repository. Encore une fois, clonons ce référentiel.
MacBook-Pro% git clone [email protected]:kennethreitz/samplemod.git
La convention de dénomination des packages de Python est __ tous les rangs inférieurs __ et __ est recommandé. Google's Great People et [People in Python](https://www.python.org/dev/peps/ pep-0008 / # package-and-module-names) le dit, il est donc plus sûr de suivre.
Cette fois, créons un paquet appelé hoge_hoge
. C'est une bonne idée de faire correspondre le répertoire cloné avec le nom du package.
De plus, le répertoire cloné contient le fichier git d'origine. Alors, supprimons d'abord le fichier git.
Après cela, définissez le répertoire dans lequel le script est placé comme le nom du package. En d'autres termes, c'est hoge_hoge
.
MacBook-Pro% mv samplemod hoge_hoge
MacBook-Pro% cd hoge_hoge
MacBook-Pro% rm -rf .git
MacBook-Pro% mv sample hoge_hoge
Après avoir exécuté la commande ci-dessus, la structure de votre répertoire devrait ressembler à ceci.
(Modifié en réponse à la suggestion de @ shn. Required.txt
a été supprimé.)
MacBook-Pro% tree
.
├── LICENSE
├── Makefile
├── README.rst
├── docs
│ ├── Makefile
│ ├── conf.py
│ ├── index.rst
│ └── make.bat
├── hoge_hoge
│ ├── __init__.py
│ ├── core.py
│ └── helpers.py
├── setup.py
└── tests
├── __init__.py
├── context.py
├── test_advanced.py
└── test_basic.py
Initialisons un nouveau git pour votre prochain paquet.
MacBook-Pro% git init
MacBook-Pro% git add .
MacBook-Pro% git commit -m 'first commit'
Vous êtes maintenant prêt.
Il existe plusieurs outils qui initialisent la structure de répertoires des packages python. cookiecutter. Mais je ne le recommande pas beaucoup. En effet, trop de fichiers supplémentaires sont créés.
setup.py
est un fichier requis pour le package. Dans ce fichier, écrivez les informations de dépendance, les informations de version et le nom du package à créer.
Ce poème ne décrit que les champs minimaux. Si vous êtes également intéressé par d'autres domaines, jetez un œil à Les Écritures des personnes en Python.
Le contenu de l'original setup.py
devrait ressembler à ceci.
setup(
name='sample',
version='0.0.1',
description='Sample package for Python-Guide.org',
long_description=readme,
author='Kenneth Reitz',
author_email='[email protected]',
url='https://github.com/kennethreitz/samplemod',
license=license,
packages=find_packages(exclude=('tests', 'docs'))
)
Nom de domaine | La description | Explication plus détaillée |
---|---|---|
name | Écrivez le nom du package | |
version | Écrire les informations sur la version actuelle | |
description | Écrivons brièvement "Que fait ce paquet?" | |
install_requires | Écrire les informations de package dépendant | cette |
dependency_links | Écrivez lorsque le package dépendant n'existe pas dans pypi. | cette |
__ En particulier, ce que vous devez absolument écrire __ est ʻinstall_requires. Les développeurs utilisant vos packages ne savent pas "De quel package dépendez-vous?" Sans ces informations, d'autres développeurs seront en difficulté. (
Dependency_links` sera supprimé par [pip update] vers 2019/1 (https://github.com/pypa/pip/pull/6060). Des méthodes alternatives seront décrites plus tard.)
Peut-être avez-vous déjà fait l'installation de python setup.py. Cette commande fonctionne car les informations de dépendance sont écrites correctement.
Répertoriez les packages dépendants dans le champ install_requires
. Par exemple, si vous avez besoin de «numpy», écrivez «numpy».
setup(
name='sample',
version='0.0.1',
description='Sample package for Python-Guide.org',
long_description=readme,
author='Kenneth Reitz',
author_email='[email protected]',
install_requires=['numpy'],
url='https://github.com/kennethreitz/samplemod',
license=license,
packages=find_packages(exclude=('tests', 'docs'))
)
Si vous êtes une entreprise générale, vous exploitez probablement un package que vous avez développé en interne. Il n'y a pas de tel package dans Pypi, vous devez donc indiquer à setup.py où se trouve le package. dependency_links
est nécessaire à de tels moments.
Vous ne voudrez peut-être pas utiliser dependency_links
à l'avenir. La raison est que dependency_links
est obsolète dans pip.
Une alternative est de l'écrire dans requirements.txt.
requirements.txt
a longtemps été utilisé par ʻinstall_requires` comme une description alternative dépendant du paquet.
C'est une opinion égoïste, mais je pense que c'est une méthode assez courante.
Décrivez les informations de dépendance dans requirements.txt
comme ceci.
#Vous pouvez écrire le nom du package qui existe dans pypi tel quel
numpy
#Utilisez une équation lorsque vous souhaitez spécifier la version
scipy == 1.2.2
#git pour les paquets qui n'existent pas dans pypi://URL du référentiel.git
git://[email protected]/foo/foo.git
#Pour les référentiels privés+Ajouter ssh git+ssh://[email protected]/foo/foo.git
git+ssh://[email protected]/foo/foo.git
Ensuite, écrivez le processus pour lire requirements.txt dans setup.py.
Ce code est juste.
import os, sys
from setuptools import setup, find_packages
def read_requirements():
"""Parse requirements from requirements.txt."""
reqs_path = os.path.join('.', 'requirements.txt')
with open(reqs_path, 'r') as f:
requirements = [line.rstrip() for line in f]
return requirements
setup(
..., # Other stuff here
install_requires=read_requirements(),
)
(Important) Cette histoire n'est plus disponible au moins sur pip. setuptools pourrait bientôt supprimer cet argument. Par conséquent, veuillez le considérer comme obsolète.
(Dans le cas de Hayashi) Puisque j'utilise Github, je n'écrirai que l'exemple de git. Pour les autres systèmes de gestion de version article StackOverFlow Voyons voir.
La formule de base est
git+ssh://git@URL-TO-THE-PACKAGE#egg=PACKAGE-NAME
est. Par exemple, supposons que vous vouliez écrire des informations de dépendance pour un package appelé ʻunko`. À ce moment-là
git+ssh://[email protected]/our-company-internal/unko.git#egg=unko
est. Si vous souhaitez le corriger dans une version spécifique, vous pouvez spécifier le nom de la branche ou le nom de la balise après «@».
git+ssh://[email protected]/our-company-internal/[email protected]#egg=unko-0.1
@ v0.1
est le nom de la branche ou le nom de la balise de github. -0.1
est la version du package.
Enfin, réécrivez les informations telles que «nom», «auteur». Si vous écrivez votre nom, vous y serez probablement attaché;)
Le dernier setup.py
ressemble à ceci.
import os, sys
from setuptools import setup, find_packages
def read_requirements():
"""Parse requirements from requirements.txt."""
reqs_path = os.path.join('.', 'requirements.txt')
with open(reqs_path, 'r') as f:
requirements = [line.rstrip() for line in f]
return requirements
setup(
name='hoge_hoge',
version='0.0.1',
description='Sample package for Python-Guide.org',
long_description=readme,
author='Kensuke Mitsuzawa',
author_email='[email protected]',
install_requires=read_requirements(),
url='https://github.com/kennethreitz/samplemod',
license=license,
packages=find_packages(exclude=('tests', 'docs'))
)
Il existe de nombreuses façons d'écrire du code, mais je n'entrerai pas dans les détails cette fois. Veuillez écrire comme avant. Mais [__ "Ce dont je veux que vous vous souveniez" __](http://qiita.com/Kensuke-Mitsuzawa/items/7717f823df5a30c27077#%E8%A6%9A%E3%81%88%E3%81%A6% E3% 81% 8A% E3% 81% 84% E3% 81% A6% E3% 81% BB% E3% 81% 97% E3% 81% 84% E3% 81% 93% E3% 81% A8) C'est pourquoi je le veux.
Assurez-vous de rédiger le test. Vous avez peut-être déjà écrit le code pour vérifier l'opération. Cependant, en plus de vérifier le fonctionnement, veuillez considérer le cas de test vous-même autant que possible et vérifier la sortie.
Ici, nous expliquerons seulement comment écrire un cas de test. En Python, il existe un cadre de test standard appelé ʻunit test`. Utilisons ce cadre. Je vais omettre les détails, mais voir Cet article-JP et Cet article-FR. Maîtrisons comment l'utiliser.
L'endroit où placer le code de test est sous le répertoire tests /
.
├── setup.py
└── tests
├── __init__.py
├── context.py
├── test_advanced.py
└── test_basic.py
Si vous créez un script de module pour le package, créons un script de test. De cette façon, vous pouvez voir clairement la correspondance et elle sera plus facile à maintenir. Supposons maintenant que vous ayez créé hoge_hoge / core.py
en tant que script de module.
├── hoge_hoge
│ ├── __init__.py
│ ├── core.py
Placez le script de test pour core.py
sous tests /
. Il sera plus facile à comprendre si vous ajoutez le préfixe test_
à test_core.py
.
└── tests
├── __init__.py
├── test_core.py
Il y a deux choses à garder à l'esprit lors de l'écriture d'un script de test.
--Le nom de la classe de test est Camel Case
--Le préfixe test_
est requis avant le nom de la méthode. Sans ce préfixe, le test serait ignoré.
Une fois testé, exécutons le script. python test_core.py
.
Ou s'il s'agit de pycharm, appuyez sur le bouton d'exécution normal et le test s'exécutera.
import unittest
class TestCore(unittest.TestCase):
@classmethod
def setUpClass(cls):
# procedures before tests are started. This code block is executed only once
pass
@classmethod
def tearDownClass(cls):
# procedures after tests are finished. This code block is executed only once
pass
def setUp(self):
# procedures before every tests are started. This code block is executed every time
pass
def tearDown(self):
# procedures after every tests are finished. This code block is executed every time
pass
def test_core(self):
# one test case. here.
# You must “test_” prefix always. Unless, unittest ignores
pass
if __name__ == '__main__':
unittest.main()
(Mis à jour en réponse au commentaire de @ podhmo. Merci!)
Une fois que vous avez écrit quelques scripts de test, rendons exécutable python setup.py test
.
Assurez-vous de prendre l'habitude d'exécuter cette commande si vous apportez un changement majeur. Je trouve souvent des problèmes qui me sortent de l’esprit. Quand je travaille sur l'amélioration du paquet, j'oublie souvent de lancer le test en le changeant en "Oh, change ça aussi".
C'est également très utile car d'autres développeurs peuvent facilement le tester dans leur propre environnement.
Le répertoire de test actuel a cette structure.
└── tests
├── __init__.py
├── test_all.py
├── test_advanced.py
└── test_basic.py
Spécifiez l'emplacement du répertoire de test dans setup.py. setup.py chargera automatiquement la classe de test et exécutera le test pour vous.
setup(
name='hoge_hoge',
version='0.0.1',
description='Sample package for Python-Guide.org',
long_description=readme,
author='Kensuke Mitsuzawa',
author_email='[email protected]',
install_requires=['numpy', 'unko'],
dependency_links=['git+ssh://[email protected]/our-company-internal/unko.git#egg=unko'],
url='https://github.com/kennethreitz/samplemod',
license=license,
packages=find_packages(exclude=('tests', 'docs')),
test_suite='tests'
)
Exécutez python setup.py test
. Vous pouvez voir comment tous les tests sont exécutés.
S'il y a une erreur, elle sera affichée à la fin.
L'algorithme étonnant Mechanko peut être implémenté dans votre package. Cependant, les autres développeurs n'ont pas le temps de vérifier le contenu un par un. (Malheureusement)
L'interface est nécessaire pour rendre votre algorithme génial Mechanko prêt pour les développeurs de singes.
Même avec une interface simple, les développeurs de singes se plaignent de ne pas savoir comment l'utiliser. Écrivons un exemple de code et apprenons à l'utiliser.
Je pense que l'endroit où écrire l'interface dépend de vos préférences. Si vous écrivez du code orienté objet, vous pouvez écrire des méthodes d'interface dans la classe. Si vous écrivez un objet fonction dans un fichier de script comme Python, vous pouvez préparer un fichier de script pour l'interface. (Au fait, je suis ce dernier.)
Dans tous les cas, veillez à clarifier __ entrée / sortie __. Prenez l'interface de package de requête (https://github.com/kennethreitz/requests/blob/fb014560611f6ebb97e7deb03ad8336c3c8f2db1/requests/api.py) comme exemple. [^ 2]
La chose importante dans l'exemple de code est de clarifier __ entrée / sortie __. Les développeurs Monkey copient et collent souvent des exemples de code. L'exemple de code est le meilleur car vous pouvez facilement comprendre l'entrée et la sortie même si vous utilisez le copier-coller. À titre d'exemple, l'utilisation de jaconv est mentionnée.
Si votre équipe a un style d'écriture README, assurez-vous de suivre ce style __. (Malheureusement, il n'y a pas de telle méthode à Heisha)
Le principe lors de l'écriture d'un README est que "les autres développeurs peuvent comprendre la méthode d'installation et la méthode d'exécution simplement en lisant le README".
Dans la plupart des cas, vous devriez avoir les informations suivantes.
python setup.py install
devrait convenir)
--Comment exécuter le test (généralement python setup.py test
devrait convenir)
--Comment l'utilisez-vous? (En général, "Regardez l'exemple de code" devrait convenir)S'il y a des éléments qui doivent être préparés à l'avance, ils doivent être écrits dans le README. Par exemple
--Mecab doit être installé à l'avance
Dans un tel cas, vous devez l'écrire dans le README. Si vous pouvez vous le permettre, écrivez un Créer un fichier. D'autres développeurs pleurent et se réjouissent.
Une chose à garder à l'esprit est que "les autres développeurs ne peuvent pas comprendre l'entrée et la sortie simplement en regardant le code." C'est pourquoi vous avez besoin d'un exemple de code ou vous devez vous assurer que vous avez des commentaires doc. Cependant, il est difficile d'écrire des commentaires polis sur toutes les méthodes et fonctions (le temps disponible pour un stagiaire est limité). À tout le moins, écrivez la fonction __ interface entrée / sortie __ avec une explication polie.
Utilisez github pour gérer votre code. Donc (au moins dans notre entreprise), votre package pourrait bientôt être utilisé par d'autres développeurs.
C'est pourquoi vous devriez chérir le sens de branche. Dans la plupart des cas, la branche «master» est la «version stable». La version de développement doit être poussée vers une autre branche.
A titre d'exemple, c'est mon style de développement.
--Poussez pour master jusqu'à la première version alpha (spécifiez toujours "Ceci est en cours de développement" au début du README) --Créez une autre branche de développement après la version alpha. Comment gérer les branches suit git-flow [^ 3].
git-flow
est une règle qui détermine comment gérer les branches. Cette règle donne aux équipes une flexibilité de développement.
Suivez git-flow, même si vous développez vous-même. En effet, d'autres développeurs peuvent participer au développement en douceur. Pour le flux de git-flow, voir This good article.
Faites de même pour les autres développeurs pour obtenir les mêmes résultats. Ne codez pas en dur des informations qui ne peuvent être exécutées que dans votre environnement. Informations codées en dur communes
Si ces informations sont codées en dur, elles ne peuvent pas être exécutées par d'autres développeurs. J'ai des problèmes. Par exemple, voici un mauvais exemple
def do_something():
path_to_external_unix_command = '/usr/local/bin/command'
path_to_input_file = '/Users/kensuke-mi/Desktop/codes/hoge_hoge/input.txt'
return call_some_function(path_to_input_file)
Le contenu de "input.txt" reste inconnu de personne. Dans ce cas, c'est bien.
--Si vous avez besoin d'un fichier externe, poussez-le dans le même référentiel. Utiliser le chemin relatif du script
Voici un bon exemple ci-dessus.
def do_something(path_to_external_unix_command, path_to_input_file):
return call_some_function(path_to_input_file)
path_to_input_file = './input.txt'
path_to_external_unix_command = load_from_setting_file()
do_something(path_to_external_unix_command, path_to_input_file)
J'ai écrit "clairement entrée et sortie" plusieurs fois, mais pour être honnête, c'est gênant. De plus, il est facile de l'oublier lorsque vous le modifiez. Dans ce cas, cela n'a pas de sens d'écrire un commentaire clair. Voici donc l'indice de type. C'est un mécanisme qui peut décrire le type d'un objet comme Java. Cependant, la différence avec Java est que "l'indication de type de Python n'a aucun effet sur l'exécution". [^ 4]
――La quantité de caractères à saisir augmente un peu
Je vais vous donner un exemple. Qui sont les «noms» et «personnes» dans cette implémentation? Ce n'est pas bien compris. Et qu'est-ce que "saluer" une fonction qui renvoie? Est également inconnu.
def greeting(names, persons):
target = decide_target_person(persons)
greet = generate(target, names)
return greet
Maintenant, ajoutons un indice de type ici.
def greeting(names: List[str], persons: Dict[str, str])->str:
target = decide_target_person(persons)
greet = generate(target, names)
return greet
Maintenant, les entrées et les sorties sont claires!
Pycharm prend entièrement en charge les indices de type.
--Suggestion de la méthode de l'objet --Avertissement en cas d'incompatibilité de type
L'image ci-dessous est l'écran de développement de Pycharm. Je reçois un avertissement parce que j'ai fait une erreur dans l'entrée et la sortie de target
.
Cette zone est un bon article.
Dans Python2, l'indication de type de l'expression Python3 ne peut pas être interprétée et une erreur se produit. Cependant, PEP offre un excellent moyen "d'écrire un indice de type sous forme de commentaire". Pycharm n'est encore que partiellement pris en charge, mais il devrait l'être dans un proche avenir.
↑ Jusqu'à présent Poème
C'est désordonné, mais est-ce que ça va? Est-il possible de créer un package avec uniquement ces informations? Je suis inquiet. Tsukkomi est le bienvenu.
[^ 1]: En fait, le document original lui-même est rédigé en anglais. Certains étudiants internationaux viennent chez le stagiaire.
[^ 2]: Le package de requêtes a la réputation d'être "un code très facile à lire".
[^ 3]: En fait, git-flow
est le nom de l'outil, mais il indique souvent le style de développement (seulement autour de moi ??)
[^ 4]: N'est-ce pas contraire à la philosophie de Python? J'entends quelques voix dire, mais c'est un élément qui est «accepté» dans le PEP où les gens en Python sont actifs. Par conséquent, il y a certaines choses que vous devriez garder. (En premier lieu, le membre qui a fait cette proposition est Guido van Rossum)
Recommended Posts