Nivel de dificultad:4 sobre 5
Los verdaderos artistas lanzan productos. O eso dice Steve Jobs. ¿Quieres sacar una versión de un script, librería, framework o aplicación? Excelente. El mundo necesita más código en Python. Python 3 viene con un framework de empaquetado denominado Distutils, que sirve para muchas cosas:
Se integra con el Índice de Paquetes de Python ``(PyPI)''10.2, un repositorio central para las librerías de Python de código abierto.
Todos estos aspectos de Distutils giran en torno al script de setup, que tradicionalmente se ha denominado setup.py. De hecho, ya has visto varios scripts de setup (configuración) a lo largo de este libro. Utilizaste Distutils para instalar httplib2 en el apartado 14.5, Introducción a httplib2. También lo utilizaste para instalar chardet en el capítulo 15, Caso de estudio: migrar chardet a Python 3.
En este capítulo aprenderás cómo funcionan los scripts de configuración para las librerías chardet y httplib2, y recorrerás los pasos necesarios para generar una versión de tu propio software en Python.
# chardet's setup.py
from distutils.core import setup
setup(
name = "chardet",
packages = ["chardet"],
version = "1.0.2",
description = "Universal encoding detector",
author = "Mark Pilgrim",
author_email = "mark@diveintomark.org",
url = "http://chardet.feedparser.org/",
download_url = "http://chardet.feedparser.org/download/python3-chardet-1.0.1.tgz",
keywords = ["encoding", "i18n", "xml"],
classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Development Status :: 4 - Beta",
"Environment :: Other Environment",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Operating System :: OS Independent",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Text Processing :: Linguistic",
],
long_description = """ Universal character encoding detector
-------------------
Detects - ASCII, UTF-8, UTF-16 (2 variants), UTF-32 (4 variants) - Big5, GB2312, EUC-TW, HZ-GB-2312, ISO-2022-CN (Traditional and Simplified Chinese) - EUC-JP, SHIFT_JIS, ISO-2022-JP (Japanese) - EUC-KR, ISO-2022-KR (Korean) - KOI8-R, MacCyrillic, IBM855, IBM866, ISO-8859-5, windows-1251 (Cyrillic) - ISO-8859-2, windows-1250 (Hungarian) - ISO-8859-5, windows-1251 (Bulgarian) - windows-1252 (English) - ISO-8859-7, windows-1253 (Greek) - ISO-8859-8, windows-1255 (Visual and Logical Hebrew) - TIS-620 (Thai)
This version requires Python 3 or later; a Python 2 version is available separately. """ )
chardet y httplib2 son librerías de código abierto, pero no existe ningún requisito para que tú liberes tus propias librerías Python bajo la licencia que quieras. El proceso descrito en este capítulo funcionará para cualquier software Python, independientemente del tipo de licencia.
Distribuir tu primer paquete Python es un proceso intimidante (la segunda es un poco más sencillo). Distutils intenta automatizarlo al máximo, pero hay algunas cosas que tienes que hacer por ti mismo.
Para comenzar a empaquetar un software Python necesitas colocar tus ficheros y directores en el orden adecuado. La estructura de directorios de httplib2 es la siguiente:
httplib2/
|
+-README.txt
|
+-setup.py
|
+-httplib2/
|
+-__init__.py
|
+-iri2uri.py
La estructura de directorios de chardet es un poco diferente. Como httplib2 es un módulo multifichero, así que hay un directorio chardet dentro del directorio ``raíz'' chardet. Además del fichero README.txt, chardet dispone de una documentación formateada en HTML, en el directorio docs. Este contiene varios ficheros .html y css y un subdirectorio images que contiene varios ficheros .png y .gif (esto será importante después). También, manteniendo la convención del software liberado bajo licencia gpl, tiene un fichero separado denominado COPYING.txt que contiene el texto completo de la licencia GPL.
chardet/
|
+-COPYING.txt
|
+-setup.py
|
+-README.txt
|
+-docs/
| |
| +-index.html
| |
| +-usage.html
| |
| +-images/ ...
|
+-chardet/
|
+-__init__.py
|
+-big5freq.py
|
+-...
El script de configuración de Distutils es un script en Python. En teoría puede hacer cualquier cosa que se pueda hacer en Python. En la práctica, debería hacer lo mínimo posible. Debería ser un script ``aburrido''. Cuanto más exótico sea tu proceso de instalación, tanto más complejo serán los mensajes de error durante el mismo.
La primera línea de cualquier fichero de script de configuración es:
form distutils.core import setup
Esta línea importa la función setup(), que es el punto de entrada principal a Distutils. El 95% de los ficheros de configuración de Distutils constan simplemente de una llamada al método setup() y nada más (me he inventado la estadística, pero si tu script hace más cosas, deberías tener una buena razón. ¿La tienes? Seguramente no).
La función setup() puede tomar docenas de parámetros10.4. Para mantener la cordura de los que tengan que leerla, deberías usar argumentos con nombre para cada uno que uses. Esto no es solamente una convención; es un requisito. Tu script de configuración fallará si intentas llamar a la función con argumentos sin nombre.
Se requieren, al menos, los siguientes:
Aunque no se requieren, recomiendo que también incluyas los siguientes parámetros en tu script de configuración:
Los metadatos del script de configuración están definidos en la PEP 314 http://www.python.org/dev/peps/pep-0314/
Ahora veamos el script de configuración de chardet. Contiene todos los parámetros requeridos y los recomendados, más uno que no he mencionado aún: packages.
from distutils.core import setup
setup(
name = 'chardet',
packages = ['chardet'],
version = '1.0.2',
description = 'Universal encoding detector',
author='Mark Pilgrim',
...
)
El parámetro packages destaca un solapamiento desafortunado en el vocabulario del proceso de distribución. Hemos estado hablando del ``paquete'' como la cosa que estamos construyendo (y potencialmente, listándolo en el Índice de Paquetes de Python). Pero eso no es lo que el parámetro packages quiere expresar. Se refiere al hecho de que el módulo chardet es un módulo multifichero, algunas veces conocido como...un ``paquete''. El parámetro packages le indica a Distutils que incluya el directorio chardet, su fichero __init__.py y todos los ficheros .py que constituyen el módulo chardet. Esto es muy importante; toda esta parrafada sobre la documentación y los metadatos es irrelevante si te olvidas de incluir el código propiamente dicho.
El Índice de Paquetes Python (``PyPI'') contiene miles de librerías Python. Una correcta clasificación utilizando metadatos permite encontrarlas con más facilidad. PyPI te permite mostrar los paquetes filtrando por clasificador10.6. Puedes seleccionar varios para acotar la búsqueda. ¡Estos clasificadores no son metadatos invisibles que puedas ignorar!
Para clasificar tu software debes pasar un parámetro classifiers a la función setup de Distutils. Este parámetro es una lista de cadenas de caracteres que no son texto libre. Todas las cadenas de clasificación deben salir una lista que existe en PyPI que está accesible en el siguiente enlace http://pypi.python.org/pypi?:action=list_classifiers.
Aquí puedes ver una parte de ella:
...
Topic :: Terminals
Topic :: Terminals :: Serial
Topic :: Terminals :: Telnet
Topic :: Terminals :: Terminal Emulators/X Terminals
Topic :: Text Editors
Topic :: Text Editors :: Documentation
Topic :: Text Editors :: Emacs
Topic :: Text Editors :: Integrated Development Environments (IDE)
Topic :: Text Editors :: Text Processing
Topic :: Text Editors :: Word Processors
...
Los clasificadores son opcionales. Puedes escribir un script de configuración de Distutils sin ellos. No lo hagas. Siempre deberías incluir, al menos, los siguientes clasificadores:
También te recomiendo que incluyas los siguientes:
Como ejemplo, aquí están los clasificadores de Django10.9, un framework para el desarrollo de aplicaciones web bajo licencia BSD, multiplataforma y listo para producción10.10.
Programming Language :: Python
License :: OSI Approved :: BSD License
Operating System :: OS Independent
Development Status :: 5 - Production/Stable
Environment :: Web Environment
Framework :: Django
Intended Audience :: Developers
Topic :: Internet :: WWW/HTTP
Topic :: Internet :: WWW/HTTP :: Dynamic Content
Topic :: Internet :: WWW/HTTP :: WSGI
Topic :: Software Development :: Libraries :: Python Modules
A continuación se muestran los clasificadores de chardet, la librería de detección de la codificación de caracteres que se muestra en el capítulo 15. chardet es una beta, multiplataforma, compatible con Python 3, con licencia LGPL, y sus usuarios potenciales son los desarrolladores para uso en sus propios productos.
Programming Language :: Python
Programming Language :: Python :: 3
License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Operating System :: OS Independent
Development Status :: 4 - Beta
Environment :: Other Environment
Intended Audience :: Developers
Topic :: Text Processing :: Linguistic
Topic :: Software Development :: Libraries :: Python Modules
Y estos son los clasificadores de httplib2, la librería que se mostraba en el capítulo 14. httplib2 es calidad beta, multiplataforma, con licencia MIT, y su audiencia es la de los desarrolladores Python.
Programming Language :: Python
Programming Language :: Python :: 3
License :: OSI Approved :: MIT License
Operating System :: OS Independent
Development Status :: 4 - Beta
Environment :: Web Environment
Intended Audience :: Developers
Topic :: Internet :: WWW/HTTP
Topic :: Software Development :: Libraries :: Python Modules
Por defecto, Distutils incluirá los siguientes ficheros en tu paquete de distribución:
Eso es suficiente para los ficheros del proyecto httplib2. Pero para el proyecto chardet necesitamos incluir también el fichero de licencia COPYING.txt y el directorio docs/ completo, que contiene las imágenes y los ficheros HTML. Para decirle a Distutils que incluya estos ficheros y directorios adicionales cuando construya el paquete de distribución, necesitas un fichero manifest.
Un fichero de manifiesto es un fichero de texto denominado MANIFEST.in. Debe encontrarse en el directorio raíz del proyecto, junto al fichero README.txt y setup.py. Los ficheros de manifiesto no son scripts de Python: son ficheros de texto que contienen una lista de ``comandos'' en un formato definido por Distutils. Estos comandos del manifiesto te permiten incluir o excluir determinados ficheros y directorios.
Este es el fichero de manifiesto definido para el proyecto chardet:
include COPYING.txt
recursive-include docs *.html *.css *.png *.gif
Todos los comandos de manifiesto conservan la estructura de directorios que tengas en el directorio de tu proyecto. Este comando recursive-include no va a poner un puñado de ficheros .html y .png en el directorio raíz del paquete. Va a mantener la estructura de directorios de docs/, pero solamente incluirá los ficheros que coincidan con los comodines especificados10.11.
Los ficheros de manifiesto tienen su propio formato. Puedes consultar ``Especificando los ficheros a distribuir''
(http://docs.python.org/3.1/distutils/sourcedist.html#manifest)
y ``los comandos de la plantilla de manifiesto''
(http://docs.python.org/3.1/distutils/commandref.html#sdist-cmd),
para conocer los detalles del mismo.
Por insistir: solo necesitas incluir un fichero de manifiesto si quieres incluir ficheros que Distutils no incluye por defecto. Si necesitas el fichero, solamente deberías incluir los ficheros y directorios que Distutils no encontraría por sí mismo.
Hay mucho que tener en cuenta. Distutils viene con un comando de validación que permite revisar si todos los metadatos obligatorios están presentes en tu script de configuración. Por ejemplo, si te olvidas de incluir el parámetro version, Distutils te lo recordará:
c:> c:31.exe setup.py check
running check
warning: check: missing required meta-data: version
Una vez incluyas el parámetro version (y los restantes metadatos obligatorios), el comando check se ejecutará así:
c:> c:31.exe setup.py check
running check
Distutils permite la construcción de diversos tipos de paquetes de distribución. Como mínimo, deberías construir una ``distribución de código fuente'' que incluiría tu código fuente, el fichero de configuración de Distutils, el fichero README, y los ficheros adicionales que quieras incluir. Para construirla, pasa el comando sdist a tu script de configuración de Distutils.
c:> c:31.exe setup.py sdist
running sdist
running check
reading manifest template 'MANIFEST.in'
writing manifest file 'MANIFEST'
creating chardet-1.0.2
creating chardet-1.0.2
creating chardet-1.0.2
creating chardet-1.0.2
copying files to chardet-1.0.2...
copying COPYING -> chardet-1.0.2
copying README.txt -> chardet-1.0.2
copying setup.py -> chardet-1.0.2
copying chardet__init__.py -> chardet-1.0.2
copying chardet5freq.py -> chardet-1.0.2
...
copying chardet.py -> chardet-1.0.2
copying chardet8prober.py -> chardet-1.0.2
copying docs.html -> chardet-1.0.2
copying docs.html -> chardet-1.0.2
copying docs-it-works.html -> chardet-1.0.2
copying docshtml -> chardet-1.0.2
copying docs.html -> chardet-1.0.2
copying docs-encodings.html -> chardet-1.0.2
copying docs.html -> chardet-1.0.2
copying docs.png -> chardet-1.0.2
copying docs.png -> chardet-1.0.2
copying docs.png -> chardet-1.0.2
copying docs.gif -> chardet-1.0.2
copying docs.png -> chardet-1.0.2
copying docs.png -> chardet-1.0.2
creating dist
creating 'dist-1.0.2.zip' and adding 'chardet-1.0.2' to it
adding 'chardet-1.0.2'
adding 'chardet-1.0.2-INFO'
adding 'chardet-1.0.2.txt'
adding 'chardet-1.0.2.py'
adding 'chardet-1.0.25freq.py'
adding 'chardet-1.0.25prober.py'
...
adding 'chardet-1.0.2.py'
adding 'chardet-1.0.28prober.py'
adding 'chardet-1.0.2__init__.py'
adding 'chardet-1.0.2.html'
adding 'chardet-1.0.2.html'
adding 'chardet-1.0.2-it-works.html'
adding 'chardet-1.0.2html'
adding 'chardet-1.0.2.html'
adding 'chardet-1.0.2-encodings.html'
adding 'chardet-1.0.2.html'
adding 'chardet-1.0.2.png'
adding 'chardet-1.0.2.png'
adding 'chardet-1.0.2.png'
adding 'chardet-1.0.2.gif'
adding 'chardet-1.0.2.png'
adding 'chardet-1.0.2.png'
removing 'chardet-1.0.2' (and everything under it)
Hay varias cosas a destacar aquí:
c:> dir dist
Volume in drive C has no label.
Volume Serial Number is DED5-B4F8
Directory of c:
07/30/2009 06:29 PM <DIR> . 07/30/2009 06:29 PM <DIR> .. 07/30/2009 06:29 PM 206,440 chardet-1.0.2.zip 1 File(s) 206,440 bytes 2 Dir(s) 61,424,635,904 bytes free
En mi opinión, toda librería Python merece un instalador gráfico para usuarios de Windows. Es fácil de hacer (incluso aunque tú no trabajes en Windows), y los usuarios de Windows lo apreciarán.
Distutils puede crear el instalador gráfico por ti10.12, si le pasas el comando bdist_wininst a tu script de configuración de Distutils.
c:> c:31.exe setup.py bdist_wininst
running bdist_wininst
running build
running build_py
creating build
creating build
creating build
copying chardet5freq.py -> build
copying chardet5prober.py -> build
...
copying chardet.py -> build
copying chardet8prober.py -> build
copying chardet__init__.py -> build
installing to build.win32
running install_lib
creating build.win32
creating build.win32
creating build.win32
creating build.win32
copying build5freq.py -> build.win32
copying build5prober.py -> build.win32
...
copying build.py -> build.win32
copying build8prober.py -> build.win32
copying build__init__.py -> build.win32
running install_egg_info
Writing build.win32-1.0.2-py3.1.egg-info
creating 'c:2f4h7e.zip' and adding '.' to it
adding 'PURELIB-1.0.2-py3.1.egg-info'
adding 'PURELIB5freq.py'
adding 'PURELIB5prober.py'
...
adding 'PURELIB.py'
adding 'PURELIB8prober.py'
adding 'PURELIB__init__.py'
removing 'build.win32' (and everything under it)
c:> dir dist
c:>dir dist
Volume in drive C has no label.
Volume Serial Number is AADE-E29F
Directory of c:
07/30/2009 10:14 PM <DIR> . 07/30/2009 10:14 PM <DIR> .. 07/30/2009 10:14 PM 371,236 chardet-1.0.2.win32.exe 07/30/2009 06:29 PM 206,440 chardet-1.0.2.zip 2 File(s) 577,676 bytes 2 Dir(s) 61,424,070,656 bytes free
Distutils de puede ayudar a construir paquetes de instalación para usuarios linux10.13. En mi opinión, probablemente no te merezca la pena. Si quieres distribuir software para Linux, puedes dedicar el tiempo con miembros de la comunidad que se han especializado en software de empaquetado para las distribuciones Linux mayoritarias.
Por ejemplo, mi librería chardet está en los repositorios de Debian GNU/linux10.14 (Y, por lo tanto, en los de Ubuntu). No tuve nada que ver con ello; los paquetes aparecieron allí un día. La comunidad de Debian tiene sus propias políticas de empaquetado de librerías Python10.15, y el paquete python-chardet está diseñado para cumplirlas. Y puesto que el paquete está en los repositorios de Debian, sus usuarios recibirán las actualizaciones de seguridad y las versiones nuevas, dependiendo de la configuración que hayan elegido en sus ordenadores.
Los paquetes de Linux que Distutils construye no ofrecen ninguna de estas ventajas. Puedes dedicar tu tiempo de mejor manera.
Subir software al Índice de Paquetes de Python es un proceso de tres fases:
Para registrarte tienes que ir a la página de registro de usuarios de PyPI10.16. Debes introducir el nombre de usuario y clave deseados, proporcionar una dirección de correo electrónico válida, y pulsar el botón Register (Si dispones de una clave PGP o GPG, también puedes incluirla. Si no tienes una, o no sabes lo que es, no te preocupes). Revisa tu correo; en unos minutos deberías recibir un mensaje de PyPI con un enlace de validación. Pulsa sobre él para completar el proceso de registro.
Ahora necesitas registrar tu software en PyPI y subirlo. Puedes hacerlo en un único paso:
c:> c:31.exe setup.py register sdist bdist_wininst upload
running register
We need to know who you are, so please choose either:
1. use your existing login,
2. register as a new user,
3. have the server generate a new password for you (and email it to you), or
4. quit
Your selection [default 1]: 1
Username: MarkPilgrim
Password:
Registering chardet to http://pypi.python.org/pypi
Server response (200): OK
running sdist
... output trimmed for brevity ...
running bdist_wininst
... output trimmed for brevity ...
running upload
Submitting dist-1.0.2.zip to http://pypi.python.org/pypi
Server response (200): OK
Submitting dist-1.0.2.win32.exe to http://pypi.python.org/pypi
Server response (200): OK
I can store your PyPI login so future submissions will be faster.
(the login will be stored in c:pypirc)
Save your login (y/N)?n
¡Enhorabuena! Ya tienes su própia página en el Índice de Paquetes de Python. La dirección es http://pypi.python.org/pypi/NAME, en donde NAME es la cadena de caracteres que utilizaste en el parámetro name del fichero setup.py.
Si quieres distribuir una nueva versión, solo tienes que actualizar el fichero setup.py con el nuevo número de versión y volver a ejecutar el comando anterior:
c:> c:31.exe setup.py register sdist bdist_wininst upload
Distutils no es el único sistema de empaquetado de software Python, pero en el momento de escritura de este libro (Agosto de 2009), es el único que funciona con Python 3. Hay otros sistemas que funcionan en Python 2; algunos se enfocan en la instalación, otros en las pruebas y distribución. Algunos de ellos, o todos, puede que acaben migrando a Python 3.
Los siguientes frameworks se enfocan en la instalación:
Los siguientes se enfocan en las pruebas y distribución:
Sobre Distutils:
Sobre otros sistemas de empaquetado:
José Miguel González Aguilera 2016-08-18