Skip to main content

Ralsina.Me — Roberto Alsina's website

Extendiendo rst2pdf y sphinx

Sor­ry, span­ish on­ly post!


Es­ti­ma­do públi­co lec­tor, una cosa muy es­pe­cial: un post que no es­cribí yo! Dé­mosle una bi­en­veni­da al au­tor in­vi­ta­do, Juan BC!


Una de mis prome­sas en el año fue ter­mi­nar uno de los proyec­tos mas am­bi­ciosos que me había prop­uesto: ter­mi­nar mi am­bi­entación de rol uti­lizan­do el sis­tema matemáti­co que había dis­eña­do jun­to con un ami­go tiem­po atras lla­ma­do Cros aprovechan­do la bar­bari­dad de im­a­genes que tenía guarda­da de mi tesis de gra­do.

Co­mo no puede ser de otra man­era co­mo miem­bro de Python Ar­genti­na util­ice el pro­gra­ma uti­liza­do por rst2pdf crea­do por Rober­to Alsi­na el cual es el dueño de este blog

En un pun­to nece­sita­ba hac­er una es­pecie de plan­til­las de dibu­jos para repe­tir el es­ti­lo donde hay de­scrip­ciones de un per­son­aje pero el es­ti­lo se mantiene, co­mo por ejem­p­lo las tar­je­tas de este man­u­al de DC Uni­verse RPG :

/static/cards_dc.jpg

y me di­je a mi mis­mo

¿Por que de­mo­ni­os no puedo dibu­jar un svg_ de­jar es­pa­cios en blan­co y luego los lleno den­tro del svg?

En defini­ti­va, me imag­in­a­ba al­go así:

.. template_svg: path/a/mi/archivo/svg/que/tiene/las/variables/definidas/adentro.svg
    :nombre_variable_1: valor_de_variable_1
    :nombre_variable_2: valor_de_variable_2
    ...
    :nombre_variable_N: valor_de_variable_N

En­tonces me puse en cam­paña para poder pro­gra­mar el códi­go python que re­al­iza esa tarea co­mo una ex­ten­sión de rst2pdf.

AVI­SO!!! este tex­to lo ll­e­vara a ust­ed por ca­da una de las desi­ciones de dis­eño que in­volu­cran la creación de mi ex­ten­sión para rst2pdf

Definiendo sintaxis y comportamiento

Por em­pezar aprendí de las lim­ita­ciones de la di­rec­ti­va, de­cidí un nom­bre para el­la y definí cual era el com­por­tamien­to que hi­ba a ten­er.

  • Da­do que una di­rec­ti­va co­mo tem­plate_svg me son­a­ba a larga de­cidí que el nom­bre sería svgt (SVG tem­plate).

  • Las di­rec­ti­vas so­por­tan parámet­ros fi­jos, no puedo ten­er un número vari­able de ar­gu­men­tos para pasar­le: nom­bre_­vari­able_1, nom­bre_­vari­able_2, vari­able_N a al­go co­mo di­rec­ti­­va(**k­wargs).

    Mi solu­­ción fue pasar­le to­­do en un json.

  • Por úl­ti­mo definí que svgt gener­aría un no­do de tipo fig­ure con lo cual mi di­rec­ti­va acep­taría, además de mis parámet­ros, los ar­gu­men­tos que ya tenía in­cor­po­ra­dos en dicha di­rec­ti­va.

    Es­ta úl­ti­ma de­cisión me llevó tam­bién a con­tem­plar que el archi­vo svg de­bería con­ver­tirse en un png, y para es­to uti­lizaría la her­ramien­ta Inkscape con una lla­ma­da al sis­tema para re­alizar dicha con­ver­sión (la gran ven­ta­ja de inkscape es que puede cor­rer to­tal­mente head­less con el parámetro -z).

En defini­ti­va mi di­rec­ti­va tomaría un no­do asi:

.. svgt:: file.svg
    :vars:
        { "nombre_variable_1": "valor_de_variable_1",
          "nombre_variable_2": "valor_de_variable_2",
          ...,
          "nombre_variable_N": "valor_de_variable_N"}

     <figure parameters>

Y lo con­vert­ería en un no­do así

.. figure:: file_con_parametros_resueltos.png
    <figure parameters>

Creando el template

Yo uso para crear svg a Inkscape y no pens­a­ba en ningún mo­men­to de­jar de hac­er­lo para crear este proyec­to. Así que so­lo era cuestión de abrir el pro­gra­ma y pon­er en la parte de donde se ref­er­en­cia a una url de una im­a­gen o en un tex­to al­gún for­ma­to que in­dique que eso es una vari­able (recorde­mos que por den­tro el svg no de­ja de ser tex­to plano).

Para elegir el lenguaje de template decidí utilizar el formato que propone la clase Template que viene en la librería estandar de; la cual dispone que las declaraciones de hacen anteponiendo el símbolo $ al nombre de la variable y pudiendo o no este nombre estar encerrado entre { y }.

Por ejem­p­lo en el sigu­iente im­a­gen se ve co­mo declaro dos vari­ables con Inkscape

/static/making_template.jpeg

Por otra parte inkscape des­de con­so­la se eje­cu­ta de la sigu­iente man­era para con­ver­tir un svg a png

::

$ inkscape -z file.svg -e file.p­ng -d 300

Don­de:

  • inkscape es el co­­man­­do para cor­r­er inkscape.

  • -z sirve para de­sha­­bil­i­­tar el en­­torno grá­­fi­­co.

  • file.svg es el archi­­vo a con­ver­tir.

  • -e file.p­ng in­di­ca que se va a ex­por­tar el archi­vo file.svg al for­ma­to png y se guardara en el archi­vo file.p­ng.

  • -d 300 dice que el archi­vo file.p­ng se creara con 300 dpi de cal­i­dad.

Con es­to me gen­ero los sigu­ientes prob­le­mas:

  • ¿Qué sucede si otro me pasa un tem­­plate y no se cuales son las var­i­ables que tiene aden­tro?

  • ¿Y si inkscape no es­­tá en el path de eje­cu­­ción?

  • ¿Y si no me gus­­tan los 300 dpi de cal­i­­dad?

A este pun­to ust­ed lec­tor ya se dara cuen­ta que to­do se tra­ta de agre­gar­le mas vari­ables a nues­tra sin­taxis ya definida: con lo cual to­do este cachibache quedaría así:

.. svgt:: file.svg
    :vars:
        { "nombre_variable_1": "valor_de_variable_1",
          "nombre_variable_2": "valor_de_variable_2",
          ...,
          "nombre_variable_N": "valor_de_variable_N"}
    :dpi: 72
    :inkscape_dir: /usr/bin
    :list_vars_and_exit:

     <figure parameters>

Sien­do:

  • :d­pi: 72 dice que el archi­­vo gen­er­a­­do para la figu­ra re­­sul­­tante ten­­dra 72d­pi.

  • :inkscape_dir: /us­r/bin in­di­ca que el co­man­do inkscape vive den­tro de la car­pe­ta /us­r/bin

  • :list_­vars_and_ex­it: es­tablece que svgt so­lo lis­tara por std­out la lista de var­i­ales ex­is­tente den­tro de file.svg y luego el pro­gra­ma ter­mi­nara (notese que so­lo tiene mo­tivos de de­bug­ing).

En códi­go python só­lo hay que crear un string con es­os hue­cos y luego llenar­los co­mo en el sigu­iente ejem­plo:

import os
cmd = "{isp} {svg} -z -e {png} -d {dpi}"
print cmd.format(isp=os.path.join("/usr/bin/", "inkscape"),
                 svg="file.svg",
                 png="file.png",
                 dpi="72")

[out] /usr/bin/inkscape file.svg -z -e file.png -d 72

A los bifes

Aho­ra si leyen­do el man­u­al de co­mo crear di­rec­ti­vas para do­cu­tils uno se en­cuen­tra que el es­quele­to mín­i­mo para crear es­tos bi­chos es el sigu­ien­te:

# imports necesarios
from docutils import nodes
from docutils.parsers.rst import directives
from docutils.parsers.rst import Directive
from docutils.statemachine import StringList

# la directiva es una clase
class SVGTemplate(Directive):
    """ The svgt directive"""

    # cuantos argumentos obligatorios tenemos
    required_arguments = 0

    # cuantos argumentos opcionales
    optional_arguments = 0

    # si la directiva termina con una linea en blanco
    final_argument_whitespace = False

    # funciones de validación para los argumentos
    option_spec = {}

    # si puede tener mas rst adentro de la directiva
    has_content = True

    # método que hace el procesamiento
    def run(self):
        return []

# le pone nombre a la directiva y la registra
directives.register_directive("svgt", SVGTemplate)

Los parámetros

Primero em­pece­mos definien­do un dic­cionario que rela­ciona ca­da nom­bre de parámetro (los cuales yo defi­no que son TO­DOS op­cionales) con una fun­ción que los val­i­da. Por otra parte da­do la es­truc­tura que quer­e­mos gener­ar de fig­ure posee con­tenido y la nues­tra tam­bién debe poseer­lo.

import json

# docutils imports

SPECS = {

    # variables mías y funciones que la validan y convierten en datos utiles
    # para nuestro procesamiento

    # convierte vars a un json
    "vars": json.loads,
    # convierte dpi a entero positivo
    "dpi": directives.nonnegative_int,
    # preprocesa el directorio quitandole espacios finales e iniciales
    # en blanco
    "inkscape_dir": directives.path,
    # se fija que por ser una bandera no tenga ni un valor asignado
    # si eso sucede retorna None sino tira un ValueError
    "list_vars_and_exit": directives.flag,

    # variables de figure
    "alt": directives.unchanged,
    "height": directives.length_or_percentage_or_unitless,
    "width": directives.length_or_percentage_or_unitless,
    "scale": directives.percentage,
    "align": lambda align: directives.choice(align, ('left', 'center', 'right')),
    "target": directives.uri,
    "class": directives.unchanged,
    "name": directives.unchanged,
    "figwidth": directives.length_or_percentage_or_unitless,
    "figclass": directives.unchanged,
}

# la directiva es una clase
class SVGTemplate(Directive):
    """ The svgt directive"""

    required_arguments = 0

    # cuantos argumentos opcionales
    optional_arguments = len(SPECS)

    final_argument_whitespace = False

    # funciones de validacion para los argumentos
    option_spec = SPECS

    has_content = True

    def run(self):
        return []

directives.register_directive("svgt", SVGTemplate)

Todas las funciones de validación (menos json.load) estan muy bien explicadas en la documentación de docutils .

Entendiendo los Nodos

La creación de nodos se hace con funciones que habitan en el modulo from docutils import nodes y todos los nodos se crean así (tomando de ejemplo figure):

my_new_node = figure(self, rawsource='', *children, **attributes)

don­de:

  • raw­­source: es el codi­­go fuente del no­­do que sirve para mo­­tivos de de­bug­in­ng, si salta un er­ror te mues­­tra donde es­­­ta la fal­la basan­­dose en este string. Así que mien­­tras mas rep­re­sen­­ta al no­­do este ar­gu­­men­­to mejor.

  • *chil­­dren: son los no­­dos hi­jos.

  • **at­tribute: los atrib­u­­tos y op­­ciones del nue­­vo no­­do. si se pasa una op­­cion in­­­val­i­­da sim­­ple­­mente se ig­no­ra.

Entendienfo figure

Las fig­uras son un no­do que con­tiene 3 hi­jos:

  • im­age que con­­tiene la im­a­­gen propi­a­­mente dicha.

  • cap­­tion que es un pár­rafo que le da una eti­que­­ta a la im­a­­gen.

  • leg­end que con­­tiene to­­do los par­rafos so­brantes que no son el cap­­tion

Os­ea al­go asi:

+---------------------------+
|        figure             |
|                           |
|<------ figwidth --------->|
|                           |
|  +---------------------+  |
|  |     image           |  |
|  |                     |  |
|  |<--- width --------->|  |
|  +---------------------+  |
|                           |
|The figure's caption should|
|wrap at this width.        |
|                           |
|Legend                     |
+---------------------------+

Con es­to en mente y con el con­cep­to de que TO­DO NO­DO DEBE SER PROCE­SA­DO O DE­STRU­I­DO MAN­UAL­MENTE va­mos ya a en­car­ar el al­go­rit­mo.

Explicando el método run

La fun­ción run só­lo tiene una condi­ción: debe de­volver un lista que con­tiene no­dos de do­cu­tils a ser ren­der­iza­dos

Así que a mano alza­da run sería al­go mas o menos así:

def run(self):
    # sacamos la direccion donde se encuentra el svg
    uri = self.arguments[0]

    # extraemos nuestras variables y les asignamos un valor por defecto en
    # caso de no existir
    options = dict(self.options)
    svgt_vars = options.pop("vars") if "vars" in options else {}
    svgt_dpi = options.pop("dpi") if "dpi" in options else 72
    svgt_isd = options.pop("inkscape_dir") if "inkscape_dir" in options else ""
    svgt_lvae = options.pop("list_vars_and_exit") == None \
                if "list_vars_and_exit" in options else False

    # si tenemos seteado el flag list_vars_and_exit mostramos las variables
    # y salimos del programa
    if svgt_lvae:
        self._show_vars(uri)
        sys.exit(0)

    # por como esta diseñado figure hay que evitar que la propiedad align
    # le llege a su imagen interior.
    fig_align = options.pop("align") if "align" in options else None

    # pasamos a crear el archivo png
    png_path = self._render_svg(uri, svgt_isd, svgt_dpi, svgt_vars)

    # agremamos la uri del png a las opciones
    options["uri"] = png_path

    # creamos el nodo imagen
    image_node = nodes.image(self.block_text, **options)

    # el contenido de caption y legend viene todo mezclado
    # como un iterable llamado docutils.statemachine.StringList
    # hay que separarlo en dos partes para crear la figure.
    caption_content, legend_content = self._separate_content(self.content)

    # creamos el nodo caption procesando su contenido con
    # nested_parse
    caption_node = nodes.caption("\n".join(caption_content))
    self.state.nested_parse(caption_content, self.content_offset, caption_node)

    # creamos el nodo legend y procesamos su contenido
    legend_node = nodes.legend("\n".join(legend_content))
    self.state.nested_parse(legend_content, self.content_offset, legend_node)

    # restautamos la variable align para crear el nodo figure
    if fig_align != None:
        options["align"] = fig_align

    # creaamos el susodicho nodo figure pasandole sus hijos
    figure_node = nodes.figure(self.block_text, image_node,
                               caption_node, legend_node, **options)

    # retornamos una lista con el nodo resultante
    return [figure_node]

Explicando el método _render_svg

Este método utiliza otros dos, _resolve_render_name que sera explicado después y el método _call que debido a su extrema simplicidad se recomienda leer directamente la documentació del clase subprocess.Popen

CMD = "{isp} {svg} -z -e {png} -d {dpi}"

# donde:
#   - uri es la direccion donde esta el svg a renderizar
#   - svgt_isd es el lugar donde esta inkscape
#   - svgt_dpi son los dpi del png a renderizar
#   - svgt_vars es un diccionario que contiene los valores de las variables del svg
def _render_svg(self, uri, svgt_isd, svgt_dpi, svgt_vars):

    # abrimos el svg y lo cargamos en un Template
    with open(uri) as fp:
        svg_tplt = string.Template(fp.read())

    # reemplazamos las variables con sus valores
    svg_src = svg_tplt.safe_substitute(svgt_vars)

    # obtenemos paths donde guardar:
    #   - El svg con las variables reemplazadas (fname_svg)
    #   - El png resultante (fname_png)
    fname_svg, fname_png = self._resolve_render_name(uri)

    # guardamos el svg reemplazado en su lugar
    with open(fname_svg, "w") as fp:
        fp.write(svg_src)

    # Ponemos los parametros de ejecucion de inkscape
    cmd = CMD.format(isp=os.path.join(svgt_isd, "inkscape"),
                     svg=fname_svg,
                     png=fname_png,
                     dpi=svgt_dpi)

    # ejecutamos inkscape
    self._call(cmd)

    # retornamos la direccion del nuevo png
    return fname_png

El método _resolve_render_name

Este méto­do a primera vista es in­ece­sar­i­o, ya que po­dríamos gener­ar archivos tem­po­rales efi­cien­te­mente con el mod­u­lo _tem­plate, pero da­do co­mo tra­ba­ja sphinx es­to no es posi­ble de primera mano.

# uri es la direccioón del svg original
def _resolve_render_name(self, uri):

    # leemos el directorio temporal de la variable global _tempdir
    # que pudo haber sido modificada por sphinx o tomamos el dir temporal
    # del sistema operativo. Esto se debe a que sphinx corre en un sandbox
    # y todos los archivos deben estar en la misma carpeta donde se
    # encuentra el archivo de configuración conf.py
    tempdir = _tempdir if _tempdir != None else tempfile.gettempdir()

    # extraemos el nombre del archivo sin su extensión
    basename = os.path.basename(uri).rsplit(".", 1)[0]

    # agregamos al directorio temporal el nombre del archivo sin la
    # extensión
    render_name = os.path.join(tempdir, basename)

    # generamos un nuevo nombre para el archivo svg agregando un entero
    # al final para evitar coliciones
    idx = ""
    unique_svg = render_name + "{idx}" + ".svg"
    while os.path.exists(unique_svg.format(idx=idx)):
       idx = idx + 1 if isinstance(idx, int) else 1
    unique_svg = unique_svg.format(idx=idx)

    # lo mismo para el png
    idx = ""
    unique_png = render_name + "{idx}" + ".png"
    while os.path.exists(unique_png.format(idx=idx)):
       idx = idx + 1 if isinstance(idx, int) else 1
    unique_png = unique_png.format(idx=idx)

    # retornamos los dos valores
    return unique_svg, unique_png

El método _separate_content

Este es el último método que utiliza el método run y es lo último que necesitamos para hacer el trabajo de svgt

# recibe por parámetro un string list con todos los nodos de texto del
# contenido de svgt
def _separate_content(self, content):

    # primero creamos un nodo stringlist exclusivamente para el caption
    caption_cnt = StringList(parent=content.parent,
                           parent_offset=content.parent_offset)

    # ahora algo IMPORTANTE.. la leyenda va a ser el mismo nodo contenido
    # por que la otra alternativa es crear uno nuevo y borrar el viejo
    # por que TODO NODO TIENE QUE SER PROCESADO O ELIMINADO MANUALMENTE
    legend_cnt = content # all nodes need to be procesed

    # si tenemos contenido copiamos el primer elemento al caption y quitamos
    # ese elemento de la leyenda (que es lo mismo que el content)
    if content:
        caption_cnt.append(content[0], content.source(0), content.offset(0))
        content.pop(0)
    return caption_cnt, legend_cnt

Pensando en sphinx

Para que este bicho funcione en sphinx es necesario agregar a nivel de modulo una función setup que recibe un único parámetro que es la instancia de sphinx corriendo.

def setup(app):

    # toma el valor la referencia a la variable de módulo _tempdir
    global _tempdir

    # esta función reci be dos parámetros requeridos para usarse como
    # slot de la señal build-finished de sphinx y sirve para limpiar
    # el directorio temporal.
    #   - la aplicación que ejecuto la señal
    #   - la exception que genero el fin del procesamiento o None si
    #     finalizo normalmente.
    def reset(app, ex):
        if _tempdir != None and os.path.isdir(_tempdir):
            shutil.rmtree(_tempdir)
        if ex:
            raise ex

    # agregamos una configuracion mas a sphinx llamada tempdir y asignamos
    # como valor por defecto _temp
    app.add_config_value("tempdir", "_temp", "")

    # tomamos el valor de tempdir de la configuración
    _tempdir = app.config["tempdir"]

    # reiniciamos el directorio temporal
    reset(app, None)

    # lo volvemos a crear
    os.mkdir(_tempdir)

    # conectamos la señal build-finished con la función reset
    app.connect("build-finished", reset)

Si de­sean mas in­for­ma­ción lean la doc­u­mentación del api de ex­ten­siones de sphinx

Y como se usa todo esto

Bueno con rst2pdf o bi­en lo tiran en la car­pe­ta de ex­ten­siones, o en el path donde tienen su rst y luego eje­cu­tan

$ rst2pdf archivo.rst -e svgt

Tam­bién pueden in­sta­lar­lo des­de pypi con easy_in­stall o pip con los co­man­dos:

$ pip install docutils_ext

o

$ easy_install docutils_ext

En el ca­so de sphinx, tienen que agre­gar el path donde se en­cuen­tre a sys.­path y luego agre­gar­la a la lista de ex­ten­siones.

Y funciona?

Pueden ver el códi­go com­ple­to al mo­men­to de la pub­li­cación de este artícu­lo aca además de poder descar­gar la úl­ti­ma ver­sión es­table en la pes­tañi­ta de down­load­s.

Lo de aca aba­jo dice

.. svgt:: img/temp.svg
    :vars: {"name": "that's all folks", "url": "img/troll.png"}
troll

Going out

When we were not mar­ried, Rosario and I lived very far from each oth­er which meant that when we met, we stuck to­geth­er for 24, or 36 hours. Our 3rd date last­ed al­most 3 days.

A few days ago, we de­cid­ed to have one of those, even though we were with Tato for a part of it.

So, we went from home to the Museo Par­tic­i­pa­ti­vo de Cien­ci­as, in Reco­le­ta, a cool kids sci­ence mu­se­um, full of cool stuff.

First we went to Buenos Aires De­sign, place full of cool and very over­priced de­sign­er stuff. Bought two pen­s.

Since Georgina, a friend of ours who lives *very* far away from us works at the Cen­tro Cul­tur­al Reco­le­ta, we de­cid­ed to kid­nap her for a cup of cof­fee

IMAG0365

In the mean­time, Tato ac­quired two bal­loons and start­ed mak­ing in­ap­propi­ate things with them:

IMAG0366

But that was just a mo­men­t. Then... I'm an ele­phan­t!!!!

IMAG0369

We en­tered the Cen­tro Cul­tur­al Reco­le­ta, which is a very cool place...

IMAG0370

Tato and us per­formed a bunch of ex­per­i­ments. He even got to play with a Van­der­graaf gen­er­a­tor!!!!

IMAG0373

Then we wan­dered the scary ar­eas...

IMAG0374

When we got out, it was get­ting dark­...

IMAG0375

And Tato dis­cov­ered the sad truth about can­died ap­ples: they are crap...

IMAG0377

We left tato with his aun­ties Lau­ra and Agusti­na and start­ed the grownup part of the date. First, we saw Anony­mous protest­ing sci­en­tol­o­gy (and a troll­face!)...

IMAG0378

We had a pi­ca­da at a place called Ba­bieca. Great mor­tade­la! I saw an ex­am­ple of ram­pant cap­i­tal­is­m...

IMAG0380

Then our bank heist was foiled by a tell­er not giv­ing us the "s­mall de­nom­i­na­tion, un­marked, not-se­quen­tial" bills we want­ed...

IMAG0381

Turns out Rosario had nev­er been to the Ate­neo Grand Splen­did, the pret­ti­est book­store I have ev­er seen. So we went there to see some books and get a so­da or some­thing...

IMAG0384

The roof is pret­ty spe­cial...

IMAG0383

This is a re­stored teather, and the bar is in the stage...

IMAG0389

And you can see the an­cient lights board (luck­i­ly: dis­abled)...

IMAG0386

So, we walked on Cor­ri­entes av­enue and saw this (and yes, you can fake 7 sec­onds of tan­go by fol­low­ing the step­s, but it's very hard for the la­dy)...

IMAG0391

Then there was a guy, walk­ing in an ele­phan­t's trunk thong, while his friends (who had shorn him and cov­ered him in flour) whis­tled and shout­ed: a "de­s­pe­di­da de soltero", rit­u­al haz­ing for a guy get­ting mar­ried...

IMAG0392

He end­ed talk­ing with a 7-­foot-­tall trans­ves­tite in red se­quin­s. The trans­ves­tite shout­ing "y­ou are a healthy boy!"...

IMAG0393

So, af­ter a nice evening at a bor­rowed ap­part­men­t, next morn­ing we got Tato back!

IMAG0394

A hob­bit­sian 3-hour break­fast en­sued...

IMAG0395 IMAG0396 IMAG0397

Then tax­i, train home, and we got back al­most ex­act­ly 26 hours af­ter we left. Ev­ery­one had fun, and thanks to Agusti­na and Lau­ra (or Lau­ra and Agusti­na), be­cause with­out the mel­lis, this would have been im­pos­si­ble.

Comunismo Revolucionario en #pyar

Sor­ry, span­ish-on­ly post, be­cause I am so not trans­lat­ing this.


UP­DATE: Re­sul­ta que (al con­trario de lo que di­go aba­jo) PABLOOO no es python­ista7 si no un ami­go que lo ayu­da para que no pier­da el año en la fac­ul­tad.


Siem­pre en las lis­tas de tec­nología al­guien te tira al­go co­mo el viejo "Có­mo hac­er pre­gun­tas in­teligen­tes" y cosas así. Es útil porque, asom­brosa­mente, hay mu­chos que no son ca­paces (o no les sale) de pedir ayu­da de man­era que per­mi­ta que te ayu­den.

Los demás no tienen idea de qué es­tás hablan­do. En­tonces nece­sitás ex­plicar des­de cero. Si tirás un bodoque de códi­go de 400 líneas y decís "no an­da, ar­ré­glen­lo", nadie se va a mo­lestar siquiera en leer­lo.

To­dos es­tán ayu­dan­do gratis. Tenés que rec­om­pen­sar­los de al­gu­na man­er­a, aunque sea hacién­doles sen­tir que el prob­le­ma es in­tere­san­te, o que es­tán ayu­dan­do a al­guien que lo apre­cia y los es­cucha.

Tenés que dar­le pelota a lo que te di­cen, si no, es frus­trante. Si es frus­tan­te, y no te pa­gan, al cara­jo.

No podés pedir "ha­gan mi tra­ba­jo" o "ha­gan mi tarea" o "ha­gan este prác­ti­co". Eso lo tenés que hac­er vos. Co­mo mu­cho te va a ayu­dar al­guien con al­gu­na cosa que te trabes, co­mo te ayu­daría tu pro­fe­sor/­com­pañero de tra­ba­jo.

No tenés que eno­jarte si no obtenés lo que querés. Puede ser que sea porque es muy difí­cil y los demás tam­poco lo saben, o porque jus­to es­tá to­do el mun­do ocu­pado, o porque sos un hin­chapelotas que nadie quiere ayu­dar. Casi se­guro que no es que es­tán to­dos co­gien­do sec­re­tarias.

Sin más preám­bu­lo, les pre­sen­to a pabli­toooooo*, el python­ista número 7, co­mu­nista rev­olu­cionar­i­o, re­cu­per­ador de su plus­valía por el mecan­is­mo de pedir presta­da la de los demás. No se han cam­bi­a­do los nom­bres porque nadie ahí es in­ocente ;-)


Nota: PABLO* y pythonista7 son la misma persona, ok?

[20:45:47] <PABLOOO> holasssssssssss
[20:46:06] <PABLOOO> gentes...tengo q presentar mañana este juego
[20:46:09] <PABLOOO> http://pastebin.lugmen.org.ar/7304
[20:46:11] <lalita> PABLOOO: [#3225] pastebin.lugmen.org.ar - El pastebin m&aacute;s r&aacute;pido del oeste argentino.
[20:46:25] <PABLOOO> no va ni pa tras ni pa adelantre
[20:46:37] <PABLOOO> me podran dar una mano....plizzzzzzzzzz
[20:52:56] <PABLOOO> holasssssssss
[20:53:03] <PABLOOO> hay alguien????
[20:53:53] <alecu> PABLOOO, hola
[20:54:06] <PABLOOO> holasss...como va???
[20:54:12] <alecu> PABLOOO, lo lamento, pero mi religión no me permite hacer la tarea de otra gente
[20:54:22] <PABLOOO> nuuuuuuuu
[20:54:30] <PABLOOO> y si cambias de religion???
[20:54:52] <alecu> PABLOOO, y si cambiás la actitud? :-)
[20:55:04] <alecu> PABLOOO, si preguntás por cosas específicas, seguro que te dan bola
[20:55:04] <PABLOOO> ...no pego 1...
[20:55:27] <PABLOOO> lo puse aca a las 3 o 4 de la tarde...me digeron una par de cosas
[20:55:33] <PABLOOO> no saque nada
[20:55:43] <alecu> PABLOOO, empezá chiquito. Es decir, andá arreglando de a una cosa por vez.
[20:57:24] <PABLOOO> tengo ese while de...... no deja cargar al otro while...no toma datos...le mando print y la variable...y no sale nada
[20:57:47] <alecu> PABLOOO, aha.
[20:57:47] <PABLOOO> alguna sugerencia...
[20:57:49] <alecu> PABLOOO,  No entendí nada de lo que dijiste recién.
[20:57:59] <PABLOOO> el while...
[20:58:26] <alecu> PABLOOO, hay 13 whiles en tu código
[20:58:27] <PABLOOO> me quede ahi...tengo un juego ...tengo q ponerle vidas y tiempo...pongo el while
[20:58:58] <alecu> PABLOOO, en que curso tenés que entregarlo?
[20:59:22] <PABLOOO> fundamentos de programacion unaj
[20:59:35] <alecu> PABLOOO, en que universidad o carrera?
[20:59:43] <PABLOOO> sacamelo y me acuerdo de vos con mi profe
[20:59:47] <PABLOOO> XDDDDDD
[21:00:16] <alecu> PABLOOO, si yo fuera tu profe no podría aprobarte. :-)
[21:00:34] <ralsina> PABLOOO: decí una cosa sola que no anda, lo mas clara que puedas, y capaz que te podemos ayudar un poco
[21:00:37] <PABLOOO> naaaaaaaaaa....pero tengo la intencionnn
[21:00:44] <ralsina> PABLOOO: pero no podes esperar que te hagan los trabajos prácticos
[21:01:01] <PABLOOO> pero mañana es la final...o fui
[21:02:02] <PABLOOO> el problema es la def reglas...juego corria bien
[21:02:47] <ralsina> PABLOOO: esa funcion tiene casi 200 líneas.
[21:02:53] <PABLOOO> en def reglas...yo quiero meterle la opcion de q me carge difernetes cantidades de juegos y tiempos para realizarlas
[21:03:18] <PABLOOO> dame 1 minuto y lo pienso bien
[21:03:19] <ralsina> PABLOOO: no tengo idea ni de que juego se supone que es. Tenes que tratar de achicar el problema.
[21:03:32] <PABLOOO> ok...toy en eso
[21:04:12] <alecu> ok, me llaman a cenar...
[21:04:20] <alecu> PABLOOO, después contanos a la lista como te fue.
[21:04:45] <PABLOOO> okassssss
[21:05:47] <rbistolfi> PABLOOO: trata de identificar bien el problema, no sabemos que esperas que haga la funcion, ni que hace en lugar de lo que vos esperas
[21:18:52] <rbistolfi> Hau yottabit
[21:25:35] <rbistolfi> PABLOOO: che, esta dificil de leer ese codigo :[
[21:25:53] <PABLOOO> jajajajajaja....yo soy asi
[21:26:11] <rbistolfi> igual fijate que "fin" esta definido afuera de la funcion como global
[21:26:24] <rbistolfi> y despues la definis denuvo, pero en el scope local de la funcion
[21:26:29] <PABLOOO> creo q lo complique mucho...y esto en la recta final...del final final
[21:26:41] <rbistolfi> entendes la diferencia entre local y global?
[21:26:56] <PABLOOO> ....
[21:27:00] <rbistolfi> tambien fijate que los loops tienen que tener una condicion de salida clara
[21:27:41] <rbistolfi> no encuentro donde "f" se utiliza luego para salir del loop
[21:27:52] <rbistolfi> o no entiendo lo que queres hacer :)
[21:27:56] <PABLOOO> me anote para algoritmo...y no se q ...capaz q en un par de meses te sigo la conversacion
[21:28:04] <PABLOOO> por q hoy...
[21:29:14] <rbistolfi> PABLOOO: en la linea 181
[21:29:39] <PABLOOO> pera
[21:29:42] <rbistolfi> vos decis que vas a iterar hasta que f sea True
[21:30:38] <rbistolfi> bueno, me parece que f nunca se convierte en True
[21:30:38] <rbistolfi> despues usas "fin"
[21:30:38] <rbistolfi> asi que me parece te confundiste con "f" y "fin"
[21:32:26] <rbistolfi> yottabit: so, cuando venis a Argentina?
[21:34:14] <PABLOOO> f con fin
[21:34:18] <PABLOOO> toy en eso
[21:53:31] <pythonista7> lo podes terminar,es un trabajo muy important y lo teng q presentar mañana
[21:53:50] <rbistolfi> pythonista7: ?
[21:56:30] <pythonista7> http://pastebin.lugmen.org.ar/7304
[21:56:31] <lalita> [#3225] http://pastebin.lugmen.org.ar/7304 : pastebin.lugmen.org.ar - El pastebin m&aacute;s r&aacute;pido del oeste argentino. [by PABLOOO, 2012-02-29, 23:46:15]
[21:56:39] <yottabit> *toser*
[21:56:54] <pythonista7> este es el ejercicio,en realidad un jueguito simple
[21:57:46] <rbistolfi> yottabit: ya somos dos :P
[21:58:45] <pythonista7> ponganse media pila,lo sacan asi nomas o no???
[21:58:59] <rbistolfi> pythonista7: no
[21:59:02] <rbistolfi> sorry
[22:00:30] <rbistolfi> no creo que tengas suerte aca, pidiendo que te hagan la tarea
[22:01:31] <rbistolfi> mira
[22:02:11] <pythonista7> y no
[22:02:14] <pythonista7> creo q no
[22:02:21] <pythonista7> si se rascan a cuatro manos
[22:02:44] <pythonista7> o estan cogiendo con alguna secretaria.....:P
[22:02:58] <rbistolfi> si es tan importante, porque no estudias y lo haces bien
[22:04:00] <pythonista7> lo estudie,y lo se,solo kier una opinion
[22:04:06] <pythonista7> pero se ve q es al pedo
[22:04:21] <rbistolfi> ya te dije unas cosas que estaban mal
[22:04:33] <rbistolfi> lo del "fin" y "f"
[22:04:36] <pythonista7> x gent como esta el pais esta asi,cero solidaridad.......argentina tenia q ser
[22:04:38] <rbistolfi> lo arreglaste?
[22:04:48] <pythonista7> deja q busco x otro lado
[22:05:01] <pythonista7> no hay drama,fuck!!!
[22:05:05] <rbistolfi> gracias
[22:06:00] <rbistolfi> le estoy contando a la Vane sobre este pibe, y no me cree
[22:06:05] <TiN> juaz cero solidaridad xD
[22:10:49] <gatox> ahhh  bue

1 hora mas tarde...

[23:36:19] <PABLOOO> holasssssssssssss
[23:36:24] <PABLOOO> yo otra vezzzzzzzzz
[23:36:31] <PABLOOO> hay alguien?????
[23:39:09] <PABLOOO> holasssssssssssss
[23:39:17] <PABLOOO> queda alguien...
[23:39:23] <PABLOOO> todos durmiendo
[23:39:27] <PABLOOO> zzzzzzzzzzz
[23:43:47] <-- PABLOOO (ba8695e7@gateway/web/freenode/ip.186.134.149.231) has quit (Quit: Page closed)
[23:44:25] --> PABLOOO (ba8695e7@gateway/web/freenode/ip.186.134.149.231) has joined #pyar
[23:44:35] <PABLOOO> holaaaaaaaaaaassssssss
[23:44:46] <PABLOOO> hay alguien????
[23:46:47] <PABLOOO> holassssssss

The Killing League

Review:

Got it for 99 cents on Ama­zon.

The high­est se­ri­al killer per page den­si­ty you will find this year.


Contents © 2000-2020 Roberto Alsina