forked from jmgaguilera/inmersionenpython3
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cap16.tex
604 lines (481 loc) · 37.4 KB
/
cap16.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
% ch16.tex
% This work is licensed under the Creative Commons Attribution-Noncommercial-Share Alike 3.0 License.
% To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/nz
% or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
\chapter{Empaquetando librerías en Python}\label{ch:emplib}
\noindent Nivel de dificultad:\difllll
\begin{citaCap}
``Descubrirás que la vergüenza es como el dolor; solo lo sientes una vez.'' \\
---\emph{Marquesa de Merteuil, Las amistades peligrosas\footnote{\href{http://www.imdb.com/title/tt0094947/quotes}{http://www.imdb.com/title/tt0094947/quotes}}}
\end{citaCap}
\section{Inmersión}
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 \codigo{Distutils}, que sirve para muchas cosas:
\begin{itemize}
\item Una herramienta de construcción (para ti).
\item Una herramienta de instalación (para tus usuarios).
\item Un formato de metadatos para los paquetes (para las herramientas de búsqueda).
\item Y mas\ldots
\end{itemize}
Se integra con el Índice de Paquetes de Python ``(\codigo{PyPI})''\footnote{\href{http://pypi.python.org/}{http://pypi.python.org/}}, un repositorio central para las librerías de Python de código abierto.
Todos estos aspectos de \codigo{Distutils} giran en torno al \emph{script de setup}, que tradicionalmente se ha denominado \codigo{setup.py}. De hecho, ya has visto varios scripts de setup (configuración) a lo largo de este libro. Utilizaste \codigo{Distutils} para instalar \codigo{httplib2} en el apartado \ref{sec:httplib2}, Introducción a \codigo{httplib2}. También lo utilizaste para instalar \codigo{chardet} en el capítulo \ref{ch:chardet}, 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 \codigo{chardet} y \codigo{httplib2}, y recorrerás los pasos necesarios para generar una versión de tu propio software en Python.
\begin{lstlisting}[language=Python,breaklines=true]
# 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.
"""
)
\end{lstlisting}
\cajaTextoAncho{\codigo{chardet} y \codigo{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.}
\section{Cosas que \codigo{Distutils} no puede hacer por ti}
Distribuir tu primer paquete Python es un proceso intimidante (la segunda es un poco más sencillo). \codigo{Distutils} intenta automatizarlo al máximo, pero hay algunas cosas que tienes que hacer por ti mismo.
\begin{itemize}
\item \textbf{Elige la licencia}. Este es un tema complejo, cargado de política y peligro. Si deseas liberar tu software como código abierto, te ofrezco humildemente cinco consejos:
\begin{enumerate}
\item No escribas tu propia licencia.
\item No escribas tu propia licencia.
\item No escribas tu propia licencia.
\item No tiene que ser \codigo{GPL} pero tiene que ser compatible\footnote{\href{http://www.dwheeler.com/essays/gpl-compatible.html}{http://www.dwheeler.com/essays/gpl-compatible.html}}.
\item No escribas tu propia licencia.
\end{enumerate}
\item \textbf{Clasifica tu software} utilizando el sistema de clasificación de PyPI. Explicaré lo que significa más adelante en este capítulo.
\item \textbf{Escribe un fichero ``readme'' (léeme)}. No te saltes esto. Como mínimo, debería dar a tus usuarios una idea de lo que hace tu software y cómo instalarlo.
\end{itemize}
\section{La estructura de directorios}
Para comenzar a empaquetar un software Python necesitas colocar tus ficheros y directores en el orden adecuado. La estructura de directorios de \codigo{httplib2} es la siguiente:
\begin{lstlisting}[breaklines=true]
httplib2/
|
+--README.txt
|
+--setup.py
|
+--httplib2/
|
+--__init__.py
|
+--iri2uri.py
\end{lstlisting}
\begin{enumerate}
\item \emph{Línea 1:} Crea un directorio raíz para contenerlo todo. Dale el mismo nombre que el de tu módulo Python.
\item \emph{Línea 3:} Para acomodar a tus usuarios de windows, el fichero ``README'' debería incluir la extensión \codigo{.txt}, y debería utilizar el estilo de retorno de caracteres de Windows. Solo porque utilices un moderno editor de texto que se ejecuta desde la línea de comando e incluya su propio lenguaje de macros, eso no significa que tengas que hacerle la vida más difícil a tus usuarios (Tus usuarios usan el cuaderno de notas de Windows, triste pero cierto). Incluso aunque estés en Linux o en Mac OS X, tu editor de textos seguro que tiene una opción para grabar los ficheros con retornos de carro al estilo de Windows.
\item \emph{Línea 5:} Tu script de configuración de \codigo{Distutils} debería llamarse \codigo{setup.py}, a menos que tengas una buena razón para no hacerlo así. Pero no la tienes.
\item \emph{Línea 7:} Si tu paquete Python es un único fichero \codigo{.py}, deberías ponerlo en el directorio ``raíz'' junto al fichero ``README'' y tu script de configuración. Pero la librería \codigo{httplib2} no es un único fichero; es un módulo multifichero. Simplemente añade el directorio \codigo{httplib2} dentro del directorio raíz. De este modo tendrás el fichero \codigo{\_\_init\_\_.py} dentro de un directorio \codigo{httplib2} que, a su vez, está dentro del directorio raíz \codigo{httplib2}. Esto no es un problema; de hecho, simplifica el proceso de empaquetado.
\end{enumerate}
La estructura de directorios de \codigo{chardet} es un poco diferente. Como \codigo{httplib2} es un módulo multifichero, así que hay un directorio \codigo{chardet} dentro del directorio ``raíz'' \codigo{chardet}. Además del fichero \codigo{README.txt}, \codigo{chardet} dispone de una documentación formateada en HTML, en el directorio \codigo{docs}. Este contiene varios ficheros \codigo{.html} y \codigo{css} y un subdirectorio \codigo{images} que contiene varios ficheros \codigo{.png} y \codigo{.gif} (esto será importante después). También, manteniendo la convención del software liberado bajo licencia \codigo{gpl}, tiene un fichero separado denominado \codigo{COPYING.txt} que contiene el texto completo de la licencia \codigo{GPL}.
\begin{lstlisting}[breaklines=true]
chardet/
|
+--COPYING.txt
|
+--setup.py
|
+--README.txt
|
+--docs/
| |
| +--index.html
| |
| +--usage.html
| |
| +--images/ ...
|
+--chardet/
|
+--__init__.py
|
+--big5freq.py
|
+--...
\end{lstlisting}
\section{Escribiendo el script de configuración}
El script de configuración de \codigo{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:
\begin{lstlisting}[language=Python,breaklines=true]
form distutils.core import setup
\end{lstlisting}
Esta línea importa la función \codigo{setup()}, que es el punto de entrada principal a \codigo{Distutils}. El 95\% de los ficheros de configuración de \codigo{Distutils} constan simplemente de una llamada al método \codigo{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 \codigo{setup()} puede tomar docenas de parámetros\footnote{\href{http://docs.python.org/3.1/distutils/apiref.html\#distutils.core.setup}{http://docs.python.org/3.1/distutils/apiref.html\#distutils.core.setup}}. 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:
\begin{itemize}
\item \textbf{name}, es el nombre del paquete.
\item \textbf{version}, el número de versión del paquete.
\item \textbf{author\_email}, tu dirección de correo.
\item \textbf{url}, la página web de tu proyecto. Esta puede ser la página del paquete en \codigo{PyPI} si no tienes una web separada para tu proyecto.
\end{itemize}
Aunque no se requieren, recomiendo que también incluyas los siguientes parámetros en tu script de configuración:
\begin{itemize}
\item \textbf{description}, un resumen de una línea sobre el proyecto.
\item \textbf{long\_description}, una cadena de caracteres de varias líneas en formato \codigo{reStructuredFormat}\footnote{\href{http://docutils.sourceforge.net/rst.html}{http://docutils.sourceforge.net/rst.html}}. \codigo{PyPI} convierte este formato a \codigo{HTML} y lo muestra en la página de tu paquete.
\item \textbf{classifiers}, una lista de cadenas de caracteres formateadas de manera especial como se describe en la sección siguiente.
\end{itemize}
\cajaTextoAncho{Los metadatos del script de configuración están definidos en la PEP 314 \href{http://www.python.org/dev/peps/pep-0314/}{http://www.python.org/dev/peps/pep-0314/}}
Ahora veamos el script de configuración de \codigo{chardet}. Contiene todos los parámetros requeridos y los recomendados, más uno que no he mencionado aún: \codigo{packages}.
\begin{lstlisting}[language=Python,breaklines=true]
from distutils.core import setup
setup(
name = 'chardet',
packages = ['chardet'],
version = '1.0.2',
description = 'Universal encoding detector',
author='Mark Pilgrim',
...
)
\end{lstlisting}
El parámetro \codigo{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 \codigo{packages} quiere expresar. Se refiere al hecho de que el módulo \codigo{chardet} es un módulo multifichero, algunas veces conocido como\ldots un ``paquete''. El parámetro \codigo{packages} le indica a \codigo{Distutils} que incluya el directorio \codigo{chardet}, su fichero \codigo{\_\_init\_\_.py} y todos los ficheros \codigo{.py} que constituyen el módulo \codigo{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.
\section{Clasificando tu paquete}
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 clasificador\footnote{\href{http://pypi.python.org/pypi?:action=browse}{http://pypi.python.org/pypi?:action=browse}}. 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 \codigo{classifiers} a la función \codigo{setup} de \codigo{Distutils}. Este parámetro es una lista de cadenas de caracteres que \emph{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 \href{http://pypi.python.org/pypi?:action=list\_classifiers}{http://pypi.python.org/pypi?:action=list\_classifiers}.
Aquí puedes ver una parte de ella:
\begin{lstlisting}[language=Python,breaklines=true]
...
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
...
\end{lstlisting}
Los clasificadores son opcionales. Puedes escribir un script de configuración de Distutils sin ellos. \textbf{No lo hagas}. Siempre deberías incluir, al menos, los siguientes clasificadores:
\begin{itemize}
\item \emph{Programming language}. En particular, deberías incluir ambos ``\codigo{Programming Language :: Python}'' u ``\codigo{Programming Language :: Python :: 3}''. Si no los incluyes, tu paquete no se mostrará en la lista de librerías compatibles con Python 3\footnote{\href{http://pypi.python.org/pypi?:action=browse\&c=533\&show=all}{http://pypi.python.org/pypi?:action=browse\&c=533\&show=all}}, que se enlace desde el menú lateral de todas las páginas de \codigo{pypi.python.org}.
\item \emph{License}. \emph{Esto es lo primero que miro} cuando estoy evaluando librerías de terceros que quiero utilizar. No me hagas buscar esta información vital. No incluyas más que un clasificador de licencia a menos que tu software se encuentre disponible explícitamente bajo varias licencias. Y no liberes software bajo varias licencias a menos que te veas obligado a ello. Y no obligues a otras personas a hacerlo. El licenciamiento es ya de por sí un dolor de cabeza, no lo hagas peor.
\item \emph{Operating System}. Si tu software solamente funciona bajo Windows, o Mac OS X, o Linux, quiero saberlo lo más pronto posible. Si tu software funciona en cualquier parte sin código específico de plataforma, utiliza el clasificador ``\codigo{Operating System :: OS Independent}''. Solo son necesarios múltiples clasificadores de este tipo si tu software necesita soporte específico en los diferentes sistemas operativos, lo que no es muy común.
\end{itemize}
También te recomiendo que incluyas los siguientes:
\begin{itemize}
\item \emph{Development Status}. ¿Tu software tiene la calidad de una beta? ¿Calidad de una versión alpha? ¿Pre-alpha? Elige una. Sé honesto.
\item \emph{Intended Audience}. ¿Quién podría necesitar tu software? Las elecciones habituales son: desarrolladores, usuarios finales/puesto de trabajo, ciencia/investigación, y administradores de sistemas\footnote{Los clasificadores on, respectivamente: \codigo{Developers}, \codigo{End Users/Desktop}, \codigo{Science/Research}, y \codigo{System Administrators}}.
\end{itemize}
\subsection{Ejemplos de unos buenos clasificadores de paquete}
Como ejemplo, aquí están los clasificadores de \codigo{Django}\footnote{\href{http://pypi.python.org/pypi/Django/}{http://pypi.python.org/pypi/Django/}}, un framework para el desarrollo de aplicaciones web bajo licencia BSD, multiplataforma y listo para producción\footnote{En el momento de escribir este libro, \codigo{Django} no es compatible con Python 3, de ahí que no se utilice el clasificador \codigo{Programming Language :: Python :: 3}}.
\begin{lstlisting}[language=Python,breaklines=true]
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
\end{lstlisting}
A continuación se muestran los clasificadores de \codigo{chardet}, la librería de detección de la codificación de caracteres que se muestra en el capítulo \ref{ch:chardet}. \codigo{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.
\begin{lstlisting}[language=Python,breaklines=true]
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
\end{lstlisting}
Y estos son los clasificadores de \codigo{httplib2}, la librería que se mostraba en el capítulo \ref{ch:servicios_web}. \codigo{httplib2} es calidad beta, multiplataforma, con licencia MIT, y su audiencia es la de los desarrolladores Python.
\begin{lstlisting}[language=Python,breaklines=true]
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
\end{lstlisting}
\section{Especificando ficheros adicionales con un ``Manifiesto''}
Por defecto, Distutils incluirá los siguientes ficheros en tu paquete de distribución:
\begin{itemize}
\item \codigo{README.txt}
\item \codigo{setup.py}
\item Los ficheros \codigo{.py} necesarios incluidos de los módulos multificheros, que se encuentren listados en el parámetro \codigo{packages}.
\item Los ficheros \codigo{.py} individuales que se listen en el parámetro \codigo{py\_modules}.
\end{itemize}
Eso es suficiente para los ficheros del proyecto \codigo{httplib2}. Pero para el proyecto \codigo{chardet} necesitamos incluir también el fichero de licencia \codigo{COPYING.txt} y el directorio \codigo{docs/} completo, que contiene las imágenes y los ficheros \codigo{HTML}. Para decirle a Distutils que incluya estos ficheros y directorios adicionales cuando construya el paquete de distribución, necesitas un fichero \codigo{manifest}.
Un fichero de manifiesto es un fichero de texto denominado \codigo{MANIFEST.in}. Debe encontrarse en el directorio raíz del proyecto, junto al fichero \codigo{README.txt} y \codigo{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 \codigo{chardet}:
\begin{lstlisting}[language=Python,breaklines=true]
include COPYING.txt
recursive-include docs *.html *.css *.png *.gif
\end{lstlisting}
{\begin{enumerate}
\item \emph{Línea 1:} La primera línea es autoexplicativa: incluir el fichero \codigo{COPYING.txt} del directorio raíz del proyecto.
\item \emph{Línea 2:} La segunda línea es un poco más complicada. El comando \codigo{recursive-include} toma el nombre de un directorio y de un o más ficheros. Los ficheros pueden incluir caracteres comodín. Esta línea significa ``¿Ves el directorio \codigo{docs/} en el directorio raíz del proyecto? Mira dentro, recursivamente, y busca ficheros \codigo{.html}, \codigo{.css}, \codigo{.png}, y \codigo{.gif}. Inclúyelos a todos en la distribución de mi paquete''.
\end{enumerate}
Todos los comandos de manifiesto conservan la estructura de directorios que tengas en el directorio de tu proyecto. Este comando \codigo{recursive-include} no va a poner un puñado de ficheros \codigo{.html} y \codigo{.png} en el directorio raíz del paquete. Va a mantener la estructura de directorios de \codigo{docs/}, pero solamente incluirá los ficheros que coincidan con los comodines especificados\footnote{No lo he mencionado antes, pero la documentación de \codigo{chardet} está escrita en \codigo{XML} y se convierte a \codigo{HTML} posteriormente mediante un script. No quiero incluir los ficheros \codigo{XML} en el paquete de distribución, solo el código \codigo{HTML} y las imágenes}.
\cajaTextoAncho{Los ficheros de manifiesto tienen su propio formato. Puedes consultar ``Especificando los ficheros a distribuir'' \\ (\href{http://docs.python.org/3.1/distutils/sourcedist.html\#manifest}{http://docs.python.org/3.1/distutils/sourcedist.html\#manifest}) \\ y ``los comandos de la plantilla de manifiesto'' \\ (\href{http://docs.python.org/3.1/distutils/commandref.html\#sdist-cmd}{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.
\section{Revisando errores en tu script de configuración}
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 \codigo{version}, Distutils te lo recordará:
\begin{lstlisting}[language=Python,breaklines=true]
c:\Users\pilgrim\chardet> c:\python31\python.exe setup.py check
running check
warning: check: missing required meta-data: version
\end{lstlisting}
Una vez incluyas el parámetro \codigo{version} (y los restantes metadatos obligatorios), el comando \codigo{check} se ejecutará así:
\begin{lstlisting}[language=Python,breaklines=true]
c:\Users\pilgrim\chardet> c:\python31\python.exe setup.py check
running check
\end{lstlisting}
\section{Creando un distribución de código fuente}
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 \codigo{sdist} a tu script de configuración de Distutils.
\begin{lstlisting}[language=Python,breaklines=true]
c:\Users\pilgrim\chardet> c:\python31\python.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\chardet
creating chardet-1.0.2\docs
creating chardet-1.0.2\docs\images
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\chardet
copying chardet\big5freq.py -> chardet-1.0.2\chardet
...
copying chardet\universaldetector.py -> chardet-1.0.2\chardet
copying chardet\utf8prober.py -> chardet-1.0.2\chardet
copying docs\faq.html -> chardet-1.0.2\docs
copying docs\history.html -> chardet-1.0.2\docs
copying docs\how-it-works.html -> chardet-1.0.2\docs
copying docs\index.html -> chardet-1.0.2\docs
copying docs\license.html -> chardet-1.0.2\docs
copying docs\supported-encodings.html -> chardet-1.0.2\docs
copying docs\usage.html -> chardet-1.0.2\docs
copying docs\images\caution.png -> chardet-1.0.2\docs\images
copying docs\images\important.png -> chardet-1.0.2\docs\images
copying docs\images\note.png -> chardet-1.0.2\docs\images
copying docs\images\permalink.gif -> chardet-1.0.2\docs\images
copying docs\images\tip.png -> chardet-1.0.2\docs\images
copying docs\images\warning.png -> chardet-1.0.2\docs\images
creating dist
creating 'dist\chardet-1.0.2.zip' and adding 'chardet-1.0.2' to it
adding 'chardet-1.0.2\COPYING'
adding 'chardet-1.0.2\PKG-INFO'
adding 'chardet-1.0.2\README.txt'
adding 'chardet-1.0.2\setup.py'
adding 'chardet-1.0.2\chardet\big5freq.py'
adding 'chardet-1.0.2\chardet\big5prober.py'
...
adding 'chardet-1.0.2\chardet\universaldetector.py'
adding 'chardet-1.0.2\chardet\utf8prober.py'
adding 'chardet-1.0.2\chardet\__init__.py'
adding 'chardet-1.0.2\docs\faq.html'
adding 'chardet-1.0.2\docs\history.html'
adding 'chardet-1.0.2\docs\how-it-works.html'
adding 'chardet-1.0.2\docs\index.html'
adding 'chardet-1.0.2\docs\license.html'
adding 'chardet-1.0.2\docs\supported-encodings.html'
adding 'chardet-1.0.2\docs\usage.html'
adding 'chardet-1.0.2\docs\images\caution.png'
adding 'chardet-1.0.2\docs\images\important.png'
adding 'chardet-1.0.2\docs\images\note.png'
adding 'chardet-1.0.2\docs\images\permalink.gif'
adding 'chardet-1.0.2\docs\images\tip.png'
adding 'chardet-1.0.2\docs\images\warning.png'
removing 'chardet-1.0.2' (and everything under it)
\end{lstlisting}
Hay varias cosas a destacar aquí:
\begin{itemize}
\item Distutils tiene en cuenta el fichero de manifiesto (\codigo{MANIFES.in}).
\item Distutils procesa satisfactoriamente el fichero de manifiesto y añade los ficheros adicionales que queríamos.
\item Si miras en el directorio de tu proyecto, verás que Distutils ha creado un directorio \codigo{dist/}. Dentro de él, existe un fichero \codigo{.zip} que puedes distribuir.
\end{itemize}
\begin{lstlisting}[language=Python,breaklines=true]
c:\Users\pilgrim\chardet> dir dist
Volume in drive C has no label.
Volume Serial Number is DED5-B4F8
Directory of c:\Users\pilgrim\chardet\dist
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
\end{lstlisting}
\section{Creando un instalador gráfico}
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 ti\footnote{\href{http://docs.python.org/3.1/distutils/builtdist.html\#creating-windows-installers}{http://docs.python.org/3.1/distutils/builtdist.html\#creating-windows-installers}}, si le pasas el comando \codigo{bdist\_wininst} a tu script de configuración de Distutils.
\begin{lstlisting}[language=Python,breaklines=true]
c:\Users\pilgrim\chardet> c:\python31\python.exe setup.py bdist_wininst
running bdist_wininst
running build
running build_py
creating build
creating build\lib
creating build\lib\chardet
copying chardet\big5freq.py -> build\lib\chardet
copying chardet\big5prober.py -> build\lib\chardet
...
copying chardet\universaldetector.py -> build\lib\chardet
copying chardet\utf8prober.py -> build\lib\chardet
copying chardet\__init__.py -> build\lib\chardet
installing to build\bdist.win32\wininst
running install_lib
creating build\bdist.win32
creating build\bdist.win32\wininst
creating build\bdist.win32\wininst\PURELIB
creating build\bdist.win32\wininst\PURELIB\chardet
copying build\lib\chardet\big5freq.py -> build\bdist.win32\wininst\PURELIB\chardet
copying build\lib\chardet\big5prober.py -> build\bdist.win32\wininst\PURELIB\chardet
...
copying build\lib\chardet\universaldetector.py -> build\bdist.win32\wininst\PURELIB\chardet
copying build\lib\chardet\utf8prober.py -> build\bdist.win32\wininst\PURELIB\chardet
copying build\lib\chardet\__init__.py -> build\bdist.win32\wininst\PURELIB\chardet
running install_egg_info
Writing build\bdist.win32\wininst\PURELIB\chardet-1.0.2-py3.1.egg-info
creating 'c:\users\pilgrim\appdata\local\temp\tmp2f4h7e.zip' and adding '.' to it
adding 'PURELIB\chardet-1.0.2-py3.1.egg-info'
adding 'PURELIB\chardet\big5freq.py'
adding 'PURELIB\chardet\big5prober.py'
...
adding 'PURELIB\chardet\universaldetector.py'
adding 'PURELIB\chardet\utf8prober.py'
adding 'PURELIB\chardet\__init__.py'
removing 'build\bdist.win32\wininst' (and everything under it)
c:\Users\pilgrim\chardet> dir dist
c:\Users\pilgrim\chardet>dir dist
Volume in drive C has no label.
Volume Serial Number is AADE-E29F
Directory of c:\Users\pilgrim\chardet\dist
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
\end{lstlisting}
\subsection{Construyendo paquetes de instalación para otros sistemas operativos}
Distutils de puede ayudar a construir paquetes de instalación para usuarios linux\footnote{\href{http://docs.python.org/3.1/distutils/builtdist.html\#creating-rpm-packages}{http://docs.python.org/3.1/distutils/builtdist.html\#creating-rpm-packages}}. 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 \codigo{chardet} está en los repositorios de Debian GNU/linux\footnote{\href{http://packages.debian.org/python-chardet}{http://packages.debian.org/python-chardet}} (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 Python\footnote{\href{http://www.debian.org/doc/packaging-manuals/python-policy/}{http://www.debian.org/doc/packaging-manuals/python-policy/}}, y el paquete \codigo{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.
\section{Añadiendo tu software al Índice de Paquetes de Python}
Subir software al Índice de Paquetes de Python es un proceso de tres fases:
\begin{enumerate}
\item Primero, te debes registrar.
\item Luego, debes registrar tu software.
\item Por último, debes subir los paquetes que creaste con \codigo{setup.py sdist} y \codigo{setup.py bdist\_*}.
\end{enumerate}
Para registrarte tienes que ir a la página de registro de usuarios de PyPI\footnote{\href{http://pypi.python.org/pypi?:action=register\_form}{http://pypi.python.org/pypi?:action=register\_form}}. Debes introducir el nombre de usuario y clave deseados, proporcionar una dirección de correo electrónico válida, y pulsar el botón \codigo{Register} (Si dispones de una clave \codigo{PGP} o \codigo{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:
\begin{lstlisting}[language=Python,breaklines=true]
c:\Users\pilgrim\chardet> c:\python31\python.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\chardet-1.0.2.zip to http://pypi.python.org/pypi
Server response (200): OK
Submitting dist\chardet-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:\home\.pypirc)
Save your login (y/N)?n
\end{lstlisting}
\begin{enumerate}
\item \emph{Línea 1:} Cuando distribuyes tu proyecto por vez primera, Distutils lo añade al Índice de Paquetes de Python y le da su propia \codigo{URL}. A partir de ese momento, solo actualizará los metadatos del proyecto con los cambios que hayas introducido en tus parámetros de \codigo{setup.py}. Después, construye una distribución de código fuente (\codigo{sdist}) y un instalador para Windows (\codigo{bdist\_wininst}). Luego los sube a PyPI (\codigo{upload}).
\item \emph{Línea 8:} Teclea 1 o pulsa \codigo{ENTER} para seleccionar que quieres usar un usuario existente.
\item \emph{Línea 9:} Teclea el usuario y la clave que seleccionaste en la página de registro. La clave no se mostrará en pantalla; ni siquiera con asteriscos en lugar de los caracteres. Solamente teclea la clave y pulsa \codigo{ENTER}.
\item \emph{Línea 11:} Distutils registra tu paquete en PyPI\ldots
\item \emph{Línea 13:} \ldots construye tu distribución de código fuente\ldots
\item \emph{Línea 15:} \ldots construye el instalador para Windows\ldots
\item \emph{Línea 17:} \ldots y los sube al Índice de Paquetes de Python.
\item \emph{Línea 24:} Si quieres automatizar el proceso de distribución de nuevas versiones, necesitas salvar tus credenciales en un fichero local. Esto es inseguro y totalmente opcional.
\end{enumerate}
¡Enhorabuena! Ya tienes su própia página en el Índice de Paquetes de Python. La dirección es \codigo{http://pypi.python.org/pypi/NAME}, en donde \codigo{NAME} es la cadena de caracteres que utilizaste en el parámetro \codigo{name} del fichero \codigo{setup.py}.
Si quieres distribuir una nueva versión, solo tienes que actualizar el fichero \codigo{setup.py} con el nuevo número de versión y volver a ejecutar el comando anterior:
\begin{lstlisting}[language=Python,breaklines=true]
c:\Users\pilgrim\chardet> c:\python31\python.exe setup.py register sdist bdist_wininst upload
\end{lstlisting}
\section{Los muchos futuro posibles del empaquetado de Python}
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:
\begin{itemize}
\item Setuptools - \href{http://pypi.python.org/pypi/setuptools}{http://pypi.python.org/pypi/setuptools}
\item Pip - \href{http://pypi.python.org/pypi/pip}{http://pypi.python.org/pypi/pip}
\item Distribute - \href{http://bitbucket.org/tarek/distribute/}{http://bitbucket.org/tarek/distribute/}
\end{itemize}
Los siguientes se enfocan en las pruebas y distribución:
\begin{itemize}
\item virtualenv - \href{http://pypi.python.org/pypi/virtualenv}{http://pypi.python.org/pypi/virtualenv}
\item zc.buildout - \href{http://pypi.python.org/pypi/zc.buildout}{http://pypi.python.org/pypi/zc.buildout}
\item Paver - \href{http://www.blueskyonmars.com/projects/paver/}{http://www.blueskyonmars.com/projects/paver/}
\item Fabric - \href{http://fabfile.org/}{http://fabfile.org/}
\item py2exe - \href{http://www.py2exe.org/}{http://www.py2exe.org/}
\end{itemize}
\section{Lecturas complementarias}
Sobre Distutils:
\begin{itemize}
\item \href{http://docs.python.org/3.1/distutils/}{Ditribuyendo módulos de Python con Distutils}
\item \href{http://docs.python.org/3.1/distutils/apiref.html#module-distutils.core}{Funcionalidad básica de Distutils}, con todos los parámetros posibles a la función \codigo{setup()}.
\item \href{http://wiki.python.org/moin/Distutils/Cookbook}{Libro de ``cocina'' de Distutils}
\item \href{http://www.python.org/dev/peps/pep-0370/}{PEP 370: Directorios \codigo{site-packages} por usuario}.
\item \href{http://jessenoller.com/2009/07/19/pep-370-per-user-site-packages-and-environment-stew/}{PEP 370 y el ``estofado'' de entornos}
\end{itemize}
Sobre otros sistemas de empaquetado:
\begin{itemize}
\item \href{http://groups.google.com/group/django-developers/msg/5407cdb400157259}{El ecosistema de empaquetado de Python}.
\item \href{http://www.b-list.org/weblog/2008/dec/14/packaging/}{Sobre el empaquetado}.
\item \href{http://blog.ianbicking.org/2008/12/14/a-few-corrections-to-on-packaging/}{Unas cuantas correcciones a ``sobre el empaquetado''}.
\item \href{http://www.b-list.org/weblog/2008/dec/15/pip/}{Porqué me gusta Pip}.
\item \href{http://cournape.wordpress.com/2009/04/01/python-packaging-a-few-observations-cabal-for-a-solution/}{Empaquetado en Python: unas cuantas observaciones}.
\item \href{http://jacobian.org/writing/nobody-expects-python-packaging/}{¡Nadie espera el empaquetado de Python!}
\end{itemize}