sábado, 5 de septiembre de 2015

Desempolvando Django (1)

Hace como 3 años que no toco una línea de código Python, y, teniendo en cuenta que estuve una buena temporada trabajando como desarrollador Django, se me cae un poco la cara de vergüenza de estar tan oxidado.

Como estoy un poco cansado del ecosistema PHP, voy a ponerme de deberes para este curso que empieza volver a ser competente en Django.

Empezamos con lo más básico, la instalación. La forma fácil (y poco práctica en un entorno profesional) es bajarse el zip o tgz y darle al setup.py, o, peor todavía, usar la versión que nuestro gestor de paquetes nos proporciona (por ejemplo, apt-get install django).

En el momento de escribir esto, la versión en curso de Django es 1.8.4, y la forma recomendada de instalación es con pip.

Personalmente, recuerdo que es muy cómodo usar virtualenv para desarrollar Python, nos libramos de problemas con diferentes versiones, podemos probar actualizaciones en cada virtualenv, etc.

Suponiendo que tenemos ya instaladas las utilidades virtualenv y pip, allá vamos:

  1. Creamos un virtualenv con Python 3:
    yo@home:~/Desarrollo$ virtualenv --python=python3 django1.8.4

    Esto crea un virtualenv con Python3 en el directorio "django1.8.4"

  2. A continuación, nos metemos en el directorio recién creado y activamos el virtualenv:
    yo@home:~/Desarrollo$ cd django1.8.4/
    yo@home:~/Desarrollo/django1.8.4$ source bin/activate


  3. Instalamos Django ya con el virtualenv activado:
    (django1.8.4)yo@home:~/Desarrollo/django1.8.4$ pip install Django==1.8.4

    Vemos que nos queda Django sólo instalado para este entorno:
    (django1.8.4)yo@home:~/Desarrollo/django1.8.4$ ls lib/python3.4/site-packages/
    django Django-1.8.4.dist-info ...

    Con esto ya tenemos montado el virtualenv con una instalación propia de Python 3 y Django 1.8.4, sin afectar a la instalación que haya en el sistema.

  4. Con este entorno activado ya podemos salir del directorio donde hemos instalado el virtualenv y crear el proyecto Django en cualquier otro directorio. Procedemos de la forma habitual, con un startproject y comprobando que se crea el manage.py y el directorio para el código del proyecto:
    (django1.8.4)yo@home:~/Desarrollo/django1.8.4$ cd ..
    (django1.8.4)yo@home:~/Desarrollo$ django-admin startproject marcadores
    (django1.8.4)yo@home:~/Desarrollo$ cd marcadores/
    (django1.8.4)yo@home:~/Desarrollo/marcadores$ ls
    manage.py  marcadores


  5. Nos puede surgir la duda... pero si yo ahora invoco Python... ¿cuál estaré usando? Respuesta: si estamos con el virtualenv activado, será el que tenemos en nuestro directorio virtualenv:
    (django1.8.4)yo@home:~/Desarrollo/marcadores$ which python 
    /home/yo/Desarrollo/django1.8.4/bin/python

    Si desactivamos el virtualenv, volvemos al intérprete por defecto instalado en el sistema:
    (django1.8.4)yo@home:~/Desarrollo/marcadores$ deactivate
    yo@home:~/Desarrollo/marcadores$ which python
    /usr/bin/python



Por ahora ya hemos preparado el entorno. En la siguiente sesión, empezaremos a programar.

 

lunes, 6 de julio de 2015

Guarreando con los límites de un array

Como no puede ser de otra forma, en el día a día, por muchos años que lleve uno desarrollando, siempre nos encontramos con el típico error latoso provocado por acceder fuera de los límites de memoria de una estructura de datos. Algunos lenguajes son totalmente permisivos y no avisan, otros sacan "warning" y siguen adelante, otros mueren ante estas situaciones. Me entró el otro día la curiosidad de ver cómo reaccionan diferentes lenguajes antes estos errores, así que me preparé unas líneas de código triviales en diferentes lenguajes para comprobarlo. Se trata de acceder fuera de los límites de un array. En Perl:
#!/usr/bin/perl
use strict;
my @input = ("This", "is", "an", "array");
my $max = 4;
for (my $x=0; $x<=$max; $x++) {
printf("Index: %d // Value: %sn", $x, $input[$x]);
}
printf "Goodbye, I'm Perl!n";

En PHP:
<?php
$input = array("This", "is", "an", "array");
$max = 4;
for ($x=0; $x<=$max; $x++) {
echo sprintf("Index: %d // Value: %sn", $x, $input[$x]);
}
echo "Goodbye, I'm PHP!n";

En Python:
#!/usr/bin/env python

input = ["This", "is", "an", "array"];
max = 4;
for x in range(max+1):
print "Index: %d // Value: %s" % (x, input[x]);
print "Goodbye, I'm Python";

En C:
#include 

int main() {
char *input[] = {"This", "is", "an", "array"};
int max = 4;
for (int x=0; x<=max; x++) {
printf("Index: %d // Value: %sn", x, input[x]);
}
printf("Goodbye, I'm C!n");

}

En Java:
class out {
public static void main(String[] args) {
String[] input = {"This", "is", "an", "array"};
int max = 4;
for (int x=0; x<=max; x++) {
System.out.printf("Index: %d // Value: %sn", x, input[x]);
}
System.out.println("Goodbye, I'm Java");
}
}

En Pascal:
program Out;
Uses sysutils;

Var
max : Integer = 4;
x : Integer;
input : Array [0..3] of string[20] = ('This', 'is', 'an', 'array');
begin
for x := 0 to max do
begin
Writeln('Index: ' + IntToStr(x) + ' // Value: ' + input[x]);
end;
end.

He ignorado deliberadamente los modismos de cada lenguaje para hacer algo similar en todos (jamás recorrería una lista en Python de esta forma). Al ejecutarlo, es curioso ver cómo reacciona cada lenguaje en cada plataforma. Por ejemplo, en un MacOSX:




Veamos en un Linux:



Es curioso el comportamiento de la versión en C. En Mac OSX devuelve un valor sin fallar (accede a un área de memoria que no le pertenece) y en Linux hace un "segfault". La línea de compilación es la misma en ambos casos: gcc -std=c99 -o outc out.c, pero el ejecutable resultante no se comporta igual. Tendría que mirarlo más a fondo ;)

lunes, 15 de junio de 2015

Adiós, Sourceforge, que te zurzan

Durante muchos años, Sourceforge fue uno de los principales repositorios de software "open source". GitHub le quitó el puesto, pero me temo que son ellos mismos (SF) los que se acaban de poner el último clavo al ataúd.

Desde hace un tiempo, empezaron a poner adware en los instaladores de los desarrolladores, llegando al límite de quedarse con la cuenta de algunos proyectos emblemáticos, como GIMP, VLC, Nmap o Notepad++, al más puro estilo Softonic (parece que estos últimos ya se han reformado, menos mal).

Todos estamos de acuerdo en que hay que buscar vías de financiación, pero apoderarse del código e instalar crapware a los usuarios finales... no puede ser.

Les deseo una pronta desaparición y que se busquen la vida de una forma más honrada y respetuosa con los usuarios y desarrolladores.

Actualización: parece que también han llenado de crapware un instalador de Firefox. Morir matando, lo llaman.

viernes, 8 de mayo de 2015

El venerable cowsay

Lo bien que me lo pasaba "tuneando" la shell con cowsay... Lo divertido es que se lo enseñas a gente joven y se queda alucinada ;-)

pantallazo-2015-05-08_091358

martes, 1 de octubre de 2013

Probabilidades y palabrotas

Situación típica: escribimos un fragmento de código para generar contraseñas más o menos aleatorias:

$src = 'abcdefghijkmlnopqrstuvwxyz0123456789';
$new = "";
$x = 0;
while($x < 8){
  $new .= substr($src, rand(0, strlen($src)-1), 1);
  $x++;
}

Estas líneas generan una cadena aleatoria de 8 caracteres alfanumérico. ¿Cuál es la probabilidad de que salga alguna palabrota? Tengo que recordar mis rudimentos de combinatoria, pero podemos asegurar que es una probabilidad bastante baja.

Pues bien, por baja que sea, no es cero. Caso verídico: he tenido una queja de una persona que se quejaba de que en la contraseña que le enviamos ponía "puta".

Vamos a comprobarlo mediante fuerza bruta:

<?php

function generarCadena($src, $len) {
    $x = 0;
    $new = "";
    while($x < $len) {
      $new .= substr($src, rand(0, strlen($src)-1), 1);
      $x++;
    }
    return $new;
}

ini_set('display_errors', FALSE);
$opts = getopt('s:l:w:');
if(!$opts['w']) die("-w");
$word = $opts['w'];
$src = isset($opts['s']) ? $opts['s'] : 'abcdefghijkmlnopqrstuvwxyz0123456789';
$len = $opts['l'] && is_numeric($opts['l']) ? (int)$opts['l'] : 8;
$count = 0;

while(true) {
    $new = generarCadena($src, $len);
    if(strpos($new, $word) !== FALSE) {
        die("Encontramos '$word' en la cadena '$new' tras ".number_format($count, 0, ",", ".")." iteraciones.n");
    }
    $count++;
}

?>

En este fragmento de código pedimos una palabra y empezamos a generar cadenas con una longitud de $len a partir de la semilla $src hasta que la encontramos.  Veamos cuánto tarda en aparecer la palabra "puta":

david@localhost:~/dev$ while(true); do php testRandomString.php -w puta  -l 8; done
Encontramos 'puta' en la cadena 'putarzd7' tras 113.588 iteraciones.
Encontramos 'puta' en la cadena 'tputag94' tras 745.111 iteraciones.
Encontramos 'puta' en la cadena 'oputa6in' tras 540.359 iteraciones.
Encontramos 'puta' en la cadena 'vputazze' tras 321.592 iteraciones.
Encontramos 'puta' en la cadena 'putarx1t' tras 331.196 iteraciones.
Encontramos 'puta' en la cadena 'puta75cs' tras 782.809 iteraciones.
Encontramos 'puta' en la cadena 'crputazr' tras 180.502 iteraciones.
Encontramos 'puta' en la cadena '13qqputa' tras 56.523 iteraciones.
Encontramos 'puta' en la cadena 'g1puta3n' tras 12.595 iteraciones.
Encontramos 'puta' en la cadena '6fg6puta' tras 190.036 iteraciones.
Encontramos 'puta' en la cadena 'oputayon' tras 644.111 iteraciones.
Encontramos 'puta' en la cadena 'ckputako' tras 170.121 iteraciones.
Encontramos 'puta' en la cadena 'jyputaob' tras 285.211 iteraciones.
Encontramos 'puta' en la cadena 'avputao5' tras 864.466 iteraciones.
Encontramos 'puta' en la cadena '88putaqa' tras 605.127 iteraciones.
Encontramos 'puta' en la cadena '9putawoi' tras 101.390 iteraciones.
Encontramos 'puta' en la cadena 'oputaik1' tras 15.688 iteraciones.
Encontramos 'puta' en la cadena '7puta940' tras 19.865 iteraciones.
Encontramos 'puta' en la cadena 'fjputajw' tras 355.393 iteraciones.
Encontramos 'puta' en la cadena 'putafpkn' tras 273.997 iteraciones.
Encontramos 'puta' en la cadena '7asputa5' tras 4.631 iteraciones.
Encontramos 'puta' en la cadena 'eputartt' tras 484.259 iteraciones.
Encontramos 'puta' en la cadena 'gz5putax' tras 266.215 iteraciones.
Encontramos 'puta' en la cadena '4ftpputa' tras 834.145 iteraciones.
Encontramos 'puta' en la cadena 'ryoputas' tras 318.049 iteraciones.
^C

No es tan improbable como pensábamos. En la línea marcada se ve cómo ha habido un ciclo de ejecuciones en las que sólo han sido necesarias 4.631 iteraciones para que aparezca la palabra que buscamos.

Conclusión: ojo con los generadores de palabras "aleatorios". Pueden generar patrones reconocibles que pueden ofender a algún usuario.

Por curiosidad, también lo he probado con palabras más largas:

david@localhost:~/dev$ while(true); do php testRandomString.php -w zorro -l 8; done
Encontramos 'zorro' en la cadena 'kozorro7' tras 14.560.786 iteraciones.
Encontramos 'zorro' en la cadena 'zorro9v7' tras 1.676.779 iteraciones.
Encontramos 'zorro' en la cadena 'zorro6i3' tras 6.871.325 iteraciones.
^C

david@localhost:~/dev$ while(true); do php testRandomString.php -w cabron -l 8; done
Encontramos 'cabron' en la cadena '0hcabron' tras 935.995.307 interaciones.
^C