Comment créer un package Python (écrit pour un stagiaire)

Quel est cet article?

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

Synopsis jusqu'à présent

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]

Hypothèses pour les lecteurs de cet article

En d'autres termes, ce sont les connaissances préalables que possède l'étudiant stagiaire.


↓ Poème d'ici

Structure du répertoire

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

Convention de dénomination des packages

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.

Préparez-vous à faire un package

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.

Au fait ...

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.

Écrivez setup.py

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.

Au minimum, le champ que vous souhaitez écrire

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.

Écrire les informations de dépendance pour les packages qui existent dans Pypi

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

Décrivez les informations de dépendance pour le package interne

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.

Histoire après 2019/1

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(),
    )

Histoire avant 2019/1

(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.

Réécrire le nom, l'auteur, etc.

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

Ecrire le code

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.

Ecrire un test

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

Un script, un script de test

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

exemple de 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()

Ecrire tous les cas de test dans test_all

(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.

1: Modifier setup.py

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

2: Exécutez tous les 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.

Écrire une interface et un exemple de code

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.

interface

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]

Exemple de code

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.

Écrivez LISEZ-MOI

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.

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.

Choses dont il faut se rappeler

Clarifier l'entrée et la sortie

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.

Code stable et code en développement

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].

Suivez Git-flow

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.

Soyez conscient de la reproductibilité

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)

Autre

Utilisons l'indice de type!

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]

Principaux avantages

Mauvais points

――La quantité de caractères à saisir augmente un peu

Exemple

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!

À propos de l'assistance pycharm

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.

スクリーンショット 2016-07-29 2.49.55.png

Pour les packages python-3 uniquement

Cette zone est un bon article.

Pour les packages qui prennent en charge à la fois python-2 et python-3

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

Écrivez un poème et regardez en arrière

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

Comment créer un package Python (écrit pour un stagiaire)
Comment créer un package Python à l'aide de VS Code
[Python] Comment rendre une classe itérable
Comment transformer une chaîne en tableau ou un tableau en chaîne en Python
Comment créer un plug-in QGIS (génération de package)
Comment rendre le Python des débutants plus rapide [numpy]
Conseils aux débutants en Python pour utiliser l'exemple Scikit-image pour eux-mêmes 7 Comment créer un module
Comment faire une traduction japonais-anglais
Comment utiliser pip, un système de gestion de paquets indispensable pour utiliser Python
[Python] Comment créer une liste de chaînes de caractères caractère par caractère
[Python] Comment créer une matrice de contiguïté / liste de contiguïté [Théorie des graphes]
Comment créer un bot slack
Comment créer un package Conda
Expérimentez pour créer un PDF indépendant pour Kindle avec Python
Comment créer un robot - Avancé
Comment créer une fonction récursive
Comment définir plusieurs variables dans une instruction Python for
[Blender] Comment créer un plug-in Blender
Comment créer un robot - Basic
[Python] Comment générer une table pandas dans un fichier Excel
Un débutant en python a essayé de faire un stage dans une entreprise informatique
Premiers pas avec Python pour les non-ingénieurs
Comment créer une caméra de surveillance (caméra de sécurité) avec Opencv et Python
[Introduction à Python] Comment utiliser l'opérateur in dans l'instruction for?
Spigot (Paper) Introduction à la création d'un plug-in pour 2020 # 01 (Construction de l'environnement)
Comment utiliser un éditeur externe pour le développement Python avec Grasshopper
Comment faire un test unitaire Part.1 Modèle de conception pour l'introduction
[Python] Comment créer une matrice de motifs répétitifs (repmat / tile)
Comment empaqueter et distribuer des scripts Python
Comment ajouter un package avec PyCharm
python3 Comment installer un module externe
[Python] Comment convertir une liste bidimensionnelle en liste unidimensionnelle
Comment convertir Python en fichier exe
[Python] Comment inverser une chaîne de caractères
Comment installer un package à l'aide d'un référentiel
Comment obtenir stacktrace en python
[Python] Organisation de l'utilisation des instructions
Qt pour l'application de bureau de l'application Python
Comment utiliser "deque" pour les données Python
Une introduction à Python pour l'apprentissage automatique
Comment créer un indicateur personnalisé Backtrader
Comment créer un plan de site Pelican
Comment exécuter des scripts Maya Python
Une introduction à Python pour les programmeurs en langage C
Comment faire un modèle pour la détection d'objets avec YOLO en 3 heures
Comment installer le package python dans un environnement local en tant qu'utilisateur général
Une introduction aux applications Web Python auto-conçues pour un ingénieur Web de troisième année paresseux
Comment créer un système de dialogue dédié aux débutants
Comment lire un fichier CSV avec Python 2/3
Comment créer un pilote de périphérique Linux intégré (8)
Comment ouvrir un navigateur Web à partir de python
Comment créer un pilote de périphérique Linux intégré (1)
Comment effacer un taple dans une liste (Python)
Comment créer un pilote de périphérique Linux intégré (4)