Normalmente el conocimiento base de todo desarrollador llega hasta la solución puntual de un problema, pero cuando este problema resulta siendo bastante común en proyectos propios, del equipo, empresa o ecosistema, entonces es un buen momento para crear una librería. En esta entrada vamos a dar un ejemplo de cómo crear un proyecto con ciertos criterios de calidad, pero al mismo tiempo básico.
En este tutorial vamos a crear una clase Snake
en un módulo llamado animals
, la cual va a estar documentada con Sphinx y con tests unitarios usando unittest
, para ser publicada en pypi
.
La clase Snake
para este ejemplo va a ser
# snake.py
class Snake:
def eat(self, animal):
return 'Delicious!!!' if animal is 'mouse' else 'No thanks'
Estructura de archivos
A continuación vamos a mostrar la estructura propuesta, vamos a ir explicando la finalidad de los demás archivos.
.
├ animals
| ├ __init__.py
| └ snake.py
├ setup.py
├ requirements.txt
├ README.md
└ .gitignore
En el módulo de animals
está la clase en el archivo snake.py
y un archivo para configurar el módulo, en donde se importan los archivos y clases a usar, como se muestra en el ejemplo
# __init__.py
from .snake import Snake
__name__ = 'animals'
__version__ = '0.0.0'
__all__ = ['Snake']
En el archivo requirements.txt
se agregan las dependencias del proyecto, especificando versiones si es necesario, como por ejemplo
# requirements.txt
pandas==0.25.0
sklearn
Este archivo es tomado por el archivo de configuración del módulo, el cual es setup.py
, para la configuración de dependencias. Este archivo puede ser de la siguiente forma
# setup.py
import setuptools
import animals
with open('README.md', 'r') as fh:
long_description = fh.read()
setuptools.setup(
name='animals',
version=animals.__version__,
author='Santa Claus',
author_email='santa@claus.com',
description='Please describe this',
long_description=long_description,
long_description_content_type='text/markdown',
url='https://github.com/resuelve/animals',
packages=setuptools.find_packages(exclude=['sphinx_docs', 'docs', 'tests']),
python_requires='~=3.5',
install_requires=[
i.replace('\n', '')
for i in open('requirements.txt', 'r').readlines()
],
extras_require={
'dev': ['setuptools', 'wheel', 'twine', 'Sphinx'],
},
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Topic :: Software Development',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
],
)
Para terminar, el archivo .gitignore
puede ser el mismo generado por github, solo recomendamos que al menos estén estas reglas
# .gitignore
*.egg-info/
build/
dist/
__pycache__/
Pruebas unitarias
Se agregan unas pruebas unitarias usando unittesting
. La idea es que sean lo más concisas posibles. Este archivo para este caso va a estar dentro de la carpeta tests
.
# test_snake.py
import unittest
from animals.snake import Snake
class TestSnake(unittest.TestCase):
def test_eat(self):
snake = Snake()
self.assertEqual(snake.eat('mouse'), 'Delicious!!!')
self.assertEqual(snake.eat('elephant'), 'No thanks')
if __name__ == '__main__':
unittest.main()
La forma de revisar que estén pasando todos los tests es corriendo en consola, el cual solo funciona si los tests están ubicados dentro de la carpeta de tests y comienzan con la palabra test_
, como por ejemplo, test_snake.py
python3 -m unittest tests/test_*
Documentación
Para generar una página web con los comentarios en python nosotros usamos Sphinx. Un ejemplo de la forma de documentar usando la estructura de sphinx se puede ver en la siguiente forma
# snake.py
class Snake:
''' Esta es la clase snake, de tener unos parámetros de constructor, acá irían'''
def eat(self, animal):
''' Toda serpiente necesita comer
:param animal: Hoy que animal va a comer
:type animal: str
:return: Si la serpiente está dispuesta a comer o no
:rtype: str
'''
return 'Delicious!!!' if animal is 'mouse' else 'No thanks'
Para esto se agrega una carpeta donde se guarde la estructura de los archivos a generar. En este punto la estructura de carpetas debería estar de la siguiente manera
.
├ animals
| ├ __init__.py
| └ snake.py
├ tests
| └ test_snake.py
├ sphinx_docs
| ├ conf.py
| ├ index.rst
| └ snake.rst
├ setup.py
├ requirements.txt
├ MANIFEST.in
├ README.md
└ .gitignore
En conf.py
está la configuración y demás formas de revisar el código para generar el HTML, en index.rst
está la entrada inicial del proyecto y cómo se va a ver representada y en snake.rst
están las directivas de cómo leer el archivo de snake.py
# conf.py
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
sys.path.insert(0, os.path.abspath('..'))
# -- Project information -----------------------------------------------------
project = 'animals'
copyright = '2019, Resuelve'
author = 'Santa Claus'
# The full version, including alpha/beta/rc tags
release = '0.0.0'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.viewcode',
'sphinx.ext.intersphinx',
'sphinx.ext.autosummary',
]
# Include Python objects as they appear in source files
# Default: alphabetically ('alphabetical')
autodoc_member_order = 'bysource'
# Default flags used by autodoc directives
autodoc_default_flags = ['members', 'show-inheritance']
# This value contains a list of modules to be mocked up
autodoc_mock_imports = [
# all the dependencies that you want to ignore
]
# Generate autodoc stubs with summaries from code
autosummary_generate = True
# Add any paths that contain templates here, relative to this directory.
# templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# alabater theme opitons
html_theme_options = {
'github_button': True,
'github_type': 'star&v=2',
'github_user': 'resuelve',
'github_repo': 'silk-ml',
}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named 'default.css' will overwrite the builtin 'default.css'.
html_static_path = ['_static']
# Sidebars configuration for alabaster theme
html_sidebars = {
'**': [
'about.html',
'navigation.html',
'searchbox.html',
]
}
# I don't like links to page reST sources
html_show_sourcelink = True
# Add Python version number to the default address to correctly reference
# the Python standard library
intersphinx_mapping = {'https://docs.python.org/3.7': None}
.. index.rst
*******
animals
*******
.. image:: https://img.shields.io/pypi/v/animals.svg
:target: https://pypi.python.org/pypi/animals
:alt: PyPI Version
.. image:: https://img.shields.io/pypi/pyversions/animals.svg
:target: https://pypi.python.org/pypi/animals
:alt: PyPI python Version
Simple Intelligent Learning Kit (SILK) for Machine learning
Welcome to silk_ml's documentation!
===================================
:Source code: `github.com project <https://github.com/resuelve/animals>`_
:Bug tracker: `github.com issues <https://github.com/resuelve/animals/issues>`_
:Generated: |today|
:License: MIT
:Version: |release|
Project Modules
===============
List of project's modules
.. toctree::
:maxdepth: 2
_autosummary/snake.rst
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
.. snake.rst
snake
========
.. automodule:: animals.snake
:members:
Generación de binarios
Primero se necesita instalar las herramientas necesarias para poder realizar la compilación del código
python3 -m pip install --upgrade setuptools wheel twine
Ya teniendo esto instalado, primero se compilan y crean los binarios usando el mismo archivo de configuración setup.py
, y se suben a pypi usando twine. Vale destacar que primero es necesario haber creado una cuenta en pypi.
python3 setup.py sdist bdist_wheel
python3 -m twine upload dist/*
Síguenos