<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>Código Comentado</title>
    <link>https://blog.kirbylife.dev</link>
    <description>Blog personal de kirbylife</description>
    <language>es-mx</language>
    <webMaster>hola@kirbylife.dev (kirbylife)</webMaster>
    <managingEditor>hola@kirbylife.dev (kirbylife)</managingEditor>
    <generator>Rocket</generator>
      <item>
        <title>Creando un motor de plantillas ligero (y malo) en Python</title>
        <pubDate>Thu, 26 Dec 2024 21:35:35 GMT</pubDate>
        <link>https://blog.kirbylife.dev/post/creando-un-motor-de-plantillas-ligero-y-malo-en-python-10</link>
        <guid isPermaLink="true">https://blog.kirbylife.dev/post/10</guid>
        <description>&lt;p&gt;TL;DR:
Todo el código de un motor de plantillas muy malo está en mi &lt;a href=&quot;https:&#x2F;&#x2F;git.kirbylife.dev&#x2F;kirbylife&#x2F;jinja_lowcost&#x2F;src&#x2F;branch&#x2F;main&#x2F;jinja_lowcost&#x2F;main.py&quot;&gt;git&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Hace ya algunos años necesitaba en mi trabajo un motor de plantillas para hacerme la vida mas sencilla al momento de generar unos reportes en HTML que posteriormente serían enviados por e-mail, pero por políticas de la empresa era algo complicado y burocrático que aceptaran alguna dependencias de PyPI, así que preferí aventarme la tarea de hacer mi propio motor de plantillas sin utilizar dependencias, y... salió un monstruo horrible parecido al que voy a recrear en este post.&lt;&#x2F;p&gt;
&lt;p&gt;Para llevar acabo esa tarea decidí extender el motor de plantillas que incluye Python por defecto. el que se utiliza cuando se llama la función &lt;code&gt;format&lt;&#x2F;code&gt; a un string. Dicha funcionalidad se esconde detrás de la clase &lt;code&gt;Formatter&lt;&#x2F;code&gt; dentro del paquete &lt;code&gt;string&lt;&#x2F;code&gt; de la biblioteca estándar.&lt;&#x2F;p&gt;
&lt;h2&gt;sintaxis&lt;&#x2F;h2&gt;
&lt;p&gt;Vamos comenzar viendo la sintaxis que tendrán los valores y como se van a manipular dentro nuestro motor de plantillas para después irle agregando funcionalidades.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;&amp;quot;{variable:operador parametro?:plantilla?}&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;La sintaxis tendrá 4 partes:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;La variable con la que se va a trabajar.&lt;&#x2F;li&gt;
&lt;li&gt;El operador que se le aplicará.&lt;&#x2F;li&gt;
&lt;li&gt;El parámetro que puede recibir el operador (opcional).&lt;&#x2F;li&gt;
&lt;li&gt;Una plantilla interna que reaccionará al operador (opcional).&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;La elección de esa sintaxis se ve un poco forzada por como el Formatter funciona por defecto, ya que recordemos que se le pueden aplicar pequeños modificadores al momento de formatear valores dentro de un string, ya sea mostrar un número en hexadecimal o limitar el número de decimales haciendo uso de un &lt;a href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;string.html#format-specification-mini-language&quot;&gt;mini-lenguaje&lt;&#x2F;a&gt;, así que básicamente lo vamos a extender para poder hacer cosas como estas:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;&amp;gt;&amp;gt;&amp;gt; template = &amp;quot;{name:upper}&amp;quot;
&amp;gt;&amp;gt;&amp;gt; print(format(template, name=&amp;quot;CódigoComentado&amp;quot;))
CÓDIGOCOMENTADO

&amp;gt;&amp;gt;&amp;gt; template = &amp;quot;{langs:join &#x27;, &#x27;}&amp;quot;
&amp;gt;&amp;gt;&amp;gt; print(format(template, langs=[&amp;quot;Python&amp;quot;, &amp;quot;JavaScript&amp;quot;, &amp;quot;Rust&amp;quot;]))
Python, JavaScript, Rust

&amp;gt;&amp;gt;&amp;gt; template = &amp;quot;{numbers:repeat num:- {{num}}\n}&amp;quot;
&amp;gt;&amp;gt;&amp;gt; print(format(template, numbers=[10, 20, 30]))
- 10
- 20
- 30
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2&gt;Esqueleto del programa&lt;&#x2F;h2&gt;
&lt;p&gt;Para comenzar vamos a importar la clase &lt;code&gt;Formatter&lt;&#x2F;code&gt; que se encuentra dentro del paquete &lt;code&gt;string&lt;&#x2F;code&gt;, esto para posteriormente crear una clase que herede de ella y sobrescribiremos el método &lt;code&gt;format_field&lt;&#x2F;code&gt; que es el encargado de procesar el &amp;quot;mini-lenguaje&amp;quot; del que hablábamos antes.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;from typing import Any
from string import Formatter

class JinjaLowCost(Formatter):
    def format_field(self, value: Any, format_spec: str) -&amp;gt; Any:
        print(f&amp;quot;{value=}&amp;quot;)
        print(f&amp;quot;{format_spec=}&amp;quot;)
        return super().format_field(value, format_spec)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Sí creamos una instancia de esa clase así como está e intentamos formatear algo de la forma habitual, veremos lo que nos llega por los parámetros.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class JinjaLowCost(Formatter):
    def format_field(self, value: Any, format_spec: str) -&amp;gt; Any:
        ...                  ^              ^
                             ║              ║
&amp;gt;&amp;gt;&amp;gt; jl = JinjaLowCost()      ║              ║
                     ╔═══════╩╗             ║
&amp;gt;&amp;gt;&amp;gt; jl.format(&amp;quot;hola {a:0x}&amp;quot;, a=10)          ║
value=10               ╚╩═══════════════════╝
format_spec=&#x27;0x&#x27;       
&#x27;hola a&#x27;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como se puede apreciar, el valor de la variable &lt;code&gt;a&lt;&#x2F;code&gt; llega directamente al campo &lt;code&gt;value&lt;&#x2F;code&gt; con todo y su tipo (&lt;code&gt;int&lt;&#x2F;code&gt; en este caso) y la 2da parte de la expresión llega en forma de string al campo &lt;code&gt;format_spec&lt;&#x2F;code&gt;. Esas 2 partes serán los cimientos en lo que construiremos todo lo demás, así que vamos a hacerlo.&lt;&#x2F;p&gt;
&lt;p&gt;Crearemos un Enum con las instrucciones que queremos que tenga nuestro motor de plantillas, esto con el fin de evitar tener strings mágicos por todo el código.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;from enum import Enum

class Instruction(Enum):
    LOWER = &amp;quot;lower&amp;quot;
    UPPER = &amp;quot;upper&amp;quot;
    STRIP = &amp;quot;strip&amp;quot;
    JOIN = &amp;quot;join&amp;quot;
    REPEAT = &amp;quot;repeat&amp;quot;
    IF = &amp;quot;if&amp;quot;
    NOP = &amp;quot;nop&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Aparte incluí una opción NOP (No Operation), para indicar cuando a un valor no se le va a aplicar ninguna transformación nuestra, ya sea por que la operación escrita no es correcta o bien por que está utilizando alguna de las que ya vienen por defecto.&lt;&#x2F;p&gt;
&lt;h2&gt;Implementación de instrucciones simples&lt;&#x2F;h2&gt;
&lt;p&gt;Comenzaremos con las instrucciones que hacen las transformaciones de strings, esto para explicar mas o menos la idea detrás de todo esto ya que son las mas sencillas de implementar.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class JinjaLowCost(Formatter):
    def format_field(self, value: Any, format_spec: str) -&amp;gt; Any:
        instruction = Instruction(format_spec)
        
        match instruction:
            case Instruction.LOWER:
                return value.lower()
            case Instruction.UPPER:
                return value.upper()
            case _:
                return super().format_field(value, format_spec)

&amp;gt;&amp;gt;&amp;gt; jl = JinjaLowCost()
&amp;gt;&amp;gt;&amp;gt; jl.format(&amp;quot;{a:upper} {b:lower}&amp;quot;, a=&amp;quot;Código&amp;quot;, b=&amp;quot;Comentado&amp;quot;)
&#x27;CÓDIGO comentado&#x27;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;¡¡Funciona!!, ya hemos introducido instrucciones personalizadas al motor de plantillas por defecto.&lt;&#x2F;p&gt;
&lt;p&gt;Ahora bien, este primer prototipo echa aguas por todos lados, ya que para empezar, sí la instrucción que le pasamos no es ninguna de la que definimos en el Enum, nos dará una excepción, así que habría que envolver esa linea en un try-except o hacer una comprobación. Lo segundo es que &lt;code&gt;value&lt;&#x2F;code&gt; puede ser de cualquier tipo de dato, no solo &lt;code&gt;str&lt;&#x2F;code&gt;, así que, o convertimos lo que nos llegue a &lt;code&gt;str&lt;&#x2F;code&gt; o lanzamos una excepción sí recibimos algo que no sea &lt;code&gt;str&lt;&#x2F;code&gt;. En mi caso optaré por ser permisivo y haré una conversión a lo bruto.&lt;br &#x2F;&gt;
Con todo eso implementado queda algo así:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class JinjaLowCost(Formatter):
    def format_field(self, value: Any, format_spec: str) -&amp;gt; Any:
        try:
            instruction = Instruction(format_spec)
        except ValueError:
            # Si no se encuentra en el Enum, podría ser una instrucción
            # heredada del mini-lenguaje, así que la convertimos en un NOP
            instruction = Instrution.NOP
        
        match instruction:
            case Instruction.LOWER:
                return str(value).lower()
            case Instruction.UPPER:
                return str(value).upper()
            case _:
                return super().format_field(value, format_spec)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2&gt;Implementación de instrucciones con parámetros&lt;&#x2F;h2&gt;
&lt;p&gt;Parsear parámetros siempre es un dolor de cabeza y siempre hay que pensar en los casos que pueden romper nuestro programa.
Para hacer la tarea un poco mas sencilla, haré que las instrucciones y los parámetros estén separados por espacios, ya que el split de shlex hace la tarea bastante mas sencilla y justo hace uso del espacio como separador.&lt;br &#x2F;&gt;
Viendo la estructura de la sintaxis, vemos que la variable, el operador junto con sus parámetros y la plantilla están separados por dobles puntos (:).&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;&amp;quot;{variable:operador parametro?:plantilla?}&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Así que podríamos pensar que con hacer un &lt;code&gt;split(&amp;quot;:&amp;quot;)&lt;&#x2F;code&gt; bastaría, pero ¿que tal sí queremos utilizar como parámetro un string que contenga justo &amp;quot;:&amp;quot;?, ese método va a separar por el delimitador no importándole sí está dentro de una comillas o no, así que hay que pensar en otra forma de hacerlo, con RegEx por ejemplo (Ya se, intentar resolver un problema con RegEx es igual a tener 2 problemas)...&lt;&#x2F;p&gt;
&lt;p&gt;Vamos a tener de ejemplo este format_spec:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;if equal &amp;quot;:&amp;quot;: lo que sea
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Vemos que los dobles puntos dentro de las comillas deben conservarse, así que diseñamos esta RegEx:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;           ╔════ Carácter que usaremos para hacer split
           v
(?![&#x27;|&amp;quot;].*):(?!.*[&#x27;|&amp;quot;])
 ^^          ^^
 ╚╩══════════╩╩═══ Operador de negación
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;No la voy a explicar del todo (para eso está ChatGPT), pero básicamente tiene un operador de negación a cada lado junto con las comillas simples y dobles para que haga un split por los dobles puntos solo cuando &lt;strong&gt;NO&lt;&#x2F;strong&gt; tiene ninguna de esas cosas.&lt;&#x2F;p&gt;
&lt;p&gt;Con la parte del operador y sus parámetros por un lado y la plantilla por otro, ya podemos pasarle al split de shlex todo el operador y el solito debería ser capaz de hacer la separación respetando las comillas, quedando algo como:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;import re
import shlex

INSTRUCTION_PATTERN = re.compile(&amp;quot;&amp;quot;&amp;quot;(?![&#x27;|&amp;quot;].*):(?!.*[&#x27;|&amp;quot;])&amp;quot;&amp;quot;&amp;quot;)

class JinjaLowCost(Formatter):
    @staticmethod
    def parse_instruction(raw_instruction: str) -&amp;gt; tuple[Instruction, list[Any], str]:
        # Separar la instrucción con sus parámetros y la template
        binding = iter(re.split(INSTRUCTION_PATTERN, raw_instruction, maxsplit=1))
        pre_instruction = next(binding, None)

        # Sí no tiene ninguna instrucción, se retorna NOP
        if not pre_instruction:
            return Instruction.NOP, [], &amp;quot;&amp;quot;

        template = next(binding, None)
    
        # Separar la instrucción de sus parámetros respetando las comillas
        instruction, *params = shlex.split(pre_instruction)
    
        try:
            # Retornar todas las partes sí la instrucción está en el Enum
            return Instruction(instruction), params, template
        except ValueError:
            # Si no, retornar NOP
            return Instruction.NOP, [], &amp;quot;&amp;quot;

    def format_field(self, value: Any, format_spec: str) -&amp;gt; Any:
        instruction, params, template = self.parse_instruction(format_spec)
        ...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Con la partes aisladas, ya podemos implementar casi todas las instrucciones restantes.
Vamos a comenzar con  &lt;code&gt;strip&lt;&#x2F;code&gt; y &lt;code&gt;join&lt;&#x2F;code&gt; para ver como se haría:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;from typing import Iterable

class JinjaLowCost(Formatter):
    def parse_instruction(raw_instruction: str) -&amp;gt; tuple[Instruction, list[Any], str]:
        ...

    def format_field(self, value: Any, format_spec: str) -&amp;gt; Any:
        instruction, params, template = self.parse_instruction(format_spec)

        match instruction:
            ...
            case Instruction.STRIP:
                param = next(iter(params), None)

                return str(value).strip(param)
            case Instruction.JOIN:
                if not isinstance(value, Iterable):
                    raise Exception(f&amp;quot;&#x27;{value}&#x27; is not iterable&amp;quot;)
                param = next(iter(params), &amp;quot;&amp;quot;)

                param.join(value)
            ...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;En este caso sí le puse una validación de tipo a la instrucción de &lt;code&gt;join&lt;&#x2F;code&gt;, ya que sí intentamos hacerlo con cualquier tipo de dato, daría un error mas complejo de depurar.&lt;&#x2F;p&gt;
&lt;p&gt;Lo probamos y vemos que funciona bien:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;&amp;gt;&amp;gt;&amp;gt; jl.format(&amp;quot;{x:join &#x27;, &#x27;}&amp;quot;, x=[1, 2, 3, 4])
&#x27;1, 2, 3, 4&#x27;

&amp;gt;&amp;gt;&amp;gt; f.format(&amp;quot;{x:strip &#x27;-&#x27;}&amp;quot;, x=&amp;quot;----prueba----&amp;quot;)
&#x27;prueba&#x27;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2&gt;Implementación de instrucciones con template&lt;&#x2F;h2&gt;
&lt;p&gt;Ya casi terminamos, solo nos faltan las instrucciones que utilizan parámetros y además utilizan la template, que son el &lt;code&gt;if&lt;&#x2F;code&gt; y el &lt;code&gt;repeat&lt;&#x2F;code&gt;. Comenzaremos con este último, ya que es el más sencillo de implementar.&lt;&#x2F;p&gt;
&lt;h3&gt;repeat&lt;&#x2F;h3&gt;
&lt;p&gt;Antes de empezar tengo que comentar una característica algo curiosa del Formatter, y es que, como ya sabemos, sí envolvemos una variable en un solo par de corchetes, esta será renderizada.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &amp;quot;{a}&amp;quot;.format(a=10)
&amp;quot;10&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Pero sí la envolvemos en 2 pares, esta no será renderizada, sino que le quitará un juego de corchetes:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &amp;quot;{{a}}&amp;quot;.format(a=10)
&amp;quot;{a}&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Lo cual es convenientemente útil para poder marcar el scope de las variables, ya que a ese format le podemos aplicar otro y ahora si debería renderizar la variable:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &amp;quot;{{a}}&amp;quot;.format().format(a=10)
&amp;quot;10&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Al &lt;code&gt;repeat&lt;&#x2F;code&gt; le vamos poder pasar un parámetro opcional y será el nombre de la nueva variable que crearemos para poder utilizar dentro de su plantilla, y en caso de que no le pasemos ningún parámetro el nombre por defecto será &amp;quot;item&amp;quot;. Vamos a implementar todo eso.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class JinjaLowCost(Formatter):
    def format_field(self, value: Any, format_spec: str) -&amp;gt; Any:
        instruction, params, template = self.parse_instruction(format_spec)

        match instruction:
            ...
            case Instruction.REPEAT:
                if not isinstance(value, Iterable):
                    raise Exception(f&amp;quot;&#x27;{value}&#x27; is not iterable&amp;quot;)
                if template is None:
                    raise Exception(&amp;quot;A template is needed&amp;quot;)
                
                name = next(iter(params), &amp;quot;item&amp;quot;)
                return &amp;quot;&amp;quot;.join(self.format(template, **{name: item}) for item in value)
            ...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ya así como lo tenemos debería funcionar:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;&amp;gt;&amp;gt;&amp;gt; jl.format(&amp;quot;{x:repeat n:-&amp;gt;{{n}}&amp;lt;-\n}&amp;quot;, x=[10, 20, 30])
-&amp;gt;10&amp;lt;-
-&amp;gt;20&amp;lt;-
-&amp;gt;30&amp;lt;-
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Pero tiene varias falencias, como por ejemplo que no puede iterar sobre los elementos de un diccionario directamente o tampoco podemos acceder al índice de cada elemento, y también me gustaría que al pasarle un &lt;code&gt;int&lt;&#x2F;code&gt; tenga un comportamiento similar a a un rango, así que vamos a implementar todas esas opciones:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class JinjaLowCost(Formatter):
    def format_field(self, value: Any, format_spec: str) -&amp;gt; Any:
        match instruction:
            ...
            case Instruction.REPEAT:
                if isinstance(value, dict):
                    elements = value.items()
                elif isinstance(value, int):
                    elements = range(value)
                elif isinstance(value, Iterable):
                    elements = value
                else:
                    raise Exception(f&amp;quot;&#x27;{value}&#x27; is not iterable&amp;quot;)
                if template is None:
                    raise Exception(&amp;quot;A template is needed&amp;quot;)

                var_name = next(iter(params), &amp;quot;item&amp;quot;)

                processed_elements = []
                for i, item in enumerate(elements):
                    processed_element = self.format(rest, **{var_name: item, &amp;quot;#&amp;quot;: i})
                    processed_elements.append(processed_element)
                return &amp;quot;&amp;quot;.join(processed_elements)
            ...

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Para poder acceder al índice me pareció buena idea utilizar el carácter &amp;quot;#&amp;quot;, así que ya podemos hacer cosas como estas:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;&amp;gt;&amp;gt;&amp;gt; jl.format(&amp;quot;{tacos: repeat taco:{{#}}. {{taco:upper}}\n}&amp;quot;, tacos=[&amp;quot;suadero&amp;quot;, &amp;quot;pasTor&amp;quot;, &amp;quot;tripa&amp;quot;])
0. SUADERO
1. PASTOR
2. TRIPA
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y podemos hasta tener loops anidados... eso si, la legibilidad de la plantilla se comienza a ver comprometida:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;&amp;gt;&amp;gt;&amp;gt; jl.format(
        &amp;quot;{nums:repeat num:{{num: repeat n:{{{{n:02}}}} }}\n}&amp;quot;,
        nums=[range(3), range(3,6), range(6, 9), range(9, 12)]
    )
00 01 02
03 04 05
06 07 08
09 10 11

&amp;gt;&amp;gt;&amp;gt; jl.format(&amp;quot;{n:repeat:{{#:repeat:* }}\n}&amp;quot;, n=7)

*
* *
* * *
* * * *
* * * * *
* * * * * *
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3&gt;Final Boss: if&lt;&#x2F;h3&gt;
&lt;p&gt;Ya casi lo tenemos terminado, solo nos falta el &lt;code&gt;if&lt;&#x2F;code&gt; y oficialmente hemos terminado de hacer nuestro propio motor de plantillas, vamos a ello.&lt;br &#x2F;&gt;
Vamos a comenzar declarando otro Enum, pero esta vez con todas las posibles operaciones que puede ejecutar nuestro &lt;code&gt;if&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class ConditionalOp(Enum):
    NOT = &amp;quot;not&amp;quot;
    EQUAL = &amp;quot;equal&amp;quot;
    NOTEQUAL = &amp;quot;notequal&amp;quot;
    EVEN = &amp;quot;even&amp;quot;
    ODD = &amp;quot;odd&amp;quot;
    CONTAINS = &amp;quot;contains&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Omití el &amp;quot;mayor que&amp;quot;, &amp;quot;menor igual que&amp;quot;, etc... para hacer la explicación mas sencilla, pero puede ser un buen ejercicio para que lo intentes implementar por tu cuenta.&lt;&#x2F;p&gt;
&lt;p&gt;Vamos primero a implementar únicamente el &amp;quot;igual&amp;quot; para que se vea mejor la lógica detrás del if y posteriormente implementaré el resto:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class JinjaLowCost(Formatter):
    def format_field(self, value: Any, format_spec: str) -&amp;gt; Any:
        match instruction:
            ...
            case Instruction.IF:
                try:
                    conditional = ConditionalOp(next(iter_params))
                except ValueError:
                    raise Exception(f&amp;quot;&#x27;{params[0]}&#x27; is not a valid if conditional&amp;quot;)
                criteria = next(iter_params, None)
                match conditional:
                    case ConditionalOp.EQUAL:
                        if criteria is None:
                            raise Exception(&amp;quot;Equal operator requires a parameter&amp;quot;)
                        func = lambda: value == type(value)(criteria)
            ...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Básicamente se repite la lógica de verificar sí el operador que se colocó pertenece al Enum con un try-except, después intentamos sacar un criterio el cual utilizaremos saber con que comparar el valor que recibimos.&lt;br &#x2F;&gt;
La parte quizás mas &lt;em&gt;hacky&lt;&#x2F;em&gt; es en donde declaramos una variable a la cual le asignamos una lambda y hacemos una conversión de tipos algo rara, pero es que realmente el criterio es un string y siempre va a ser un string, en cambio &lt;code&gt;value&lt;&#x2F;code&gt; puede ser de cualquier tipo de datos que no sabemos.&lt;br &#x2F;&gt;
Entonces, debemos tomar el tipo de dato de &lt;code&gt;value&lt;&#x2F;code&gt; y le pasamos el string del criterio para que lo convierta, esto casi siempre funciona jaja.&lt;br &#x2F;&gt;
Ahora ya tenemos una función que podemos llamar y nos dirá si los valores son iguales y deberemos generar una función así para cada operación del if.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class JinjaLowCost(Formatter):
    def format_field(self, value: Any, format_spec: str) -&amp;gt; Any:
        match instruction:
            ...
            case Instruction.IF:
                try:
                    conditional = ConditionalOp(next(iter_params))
                except ValueError:
                    raise Exception(f&amp;quot;&#x27;{params[0]}&#x27; is not a valid if conditional&amp;quot;)
                criteria = next(iter_params, None)
                match conditional:
                    case ConditionalOp.EQUAL:
                        if criteria is None:
                            raise Exception(&amp;quot;Equal operator requires a parameter&amp;quot;)
                        func = lambda: value == type(value)(criteria)
                    case ConditionalOp.NOTEQUAL:
                        if criteria is None:
                            raise Exception(&amp;quot;Equal operator requires a parameter&amp;quot;)
                        func = lambda: value != type(value)(criteria)
                    case ConditionalOp.CONTAINS:
                        if criteria is None:
                        raise Exception(&amp;quot;Contains operator requires a parameter&amp;quot;)
                        
                        func = lambda: value and type(value[0])(criteria) in value
                    case ConditionalOp.EVEN:
                            func = lambda: value % 2 == 0
                        case ConditionalOp.ODD:
                            func = lambda: value % 2 != 0
                        case ConditionalOp.NOT:
                            func = lambda: not value
            ...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;El &amp;quot;contains&amp;quot; tiene una limitación y es que solo va a tomar el tipo de datos del primer elemento, así que sí le pasas una lista con números, booleanos, texto, etc... solo va a intentar convertir el valor al primero elemento de la lista.&lt;&#x2F;p&gt;
&lt;p&gt;Y listo, ya solo falta colocar hasta abajo la llamada a la función y si es &lt;code&gt;True&lt;&#x2F;code&gt;, devolver el template y sí es &lt;code&gt;False&lt;&#x2F;code&gt;, retornar un string vacío.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class JinjaLowCost(Formatter):
    def format_field(self, value: Any, format_spec: str) -&amp;gt; Any:
        match instruction:
            ...
            case Instruction.IF:
                try:
                    conditional = ConditionalOp(next(iter_params))
                except ValueError:
                    raise Exception(f&amp;quot;&#x27;{params[0]}&#x27; is not a valid if conditional&amp;quot;)
                case Instruction.IF:
                    ...
            return template if func() else &amp;quot;&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y como ultima cosita, quiero que el &lt;code&gt;if&lt;&#x2F;code&gt; se pueda utilizar así solo, para que evalúe el truthy&#x2F;falsy del propio &lt;code&gt;value&lt;&#x2F;code&gt;. Ese caso lo podemos resolver hasta arriba, sin necesidad de crear otra función:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class JinjaLowCost(Formatter):
    def format_field(self, value: Any, format_spec: str) -&amp;gt; Any:
        match instruction:
            ...
            case Instruction.IF:
                if not params:
                    return template if value else &amp;quot;&amp;quot;

                iter_params = iter(params)
                try:
                    conditional = ConditionalOp(next(iter_params))
                except ValueError:
                    raise Exception(f&amp;quot;&#x27;{params[0]}&#x27; is not a valid if conditional&amp;quot;)

                criteria = next(iter_params, None)
                match conditional:
                    case ConditionalOp.EQUAL:
                        if criteria is None:
                            raise Exception(&amp;quot;Equal operator requires a parameter&amp;quot;)
                        func = lambda: value == type(value)(criteria)
                    case ConditionalOp.NOTEQUAL:
                        if criteria is None:
                            raise Exception(&amp;quot;NotEqual operator requires a parameter&amp;quot;)
                        func = lambda: value != type(value)(criteria)
                    case ConditionalOp.CONTAINS:
                        if criteria is None:
                            raise Exception(&amp;quot;Contains operator requires a parameter&amp;quot;)
                        func = lambda: value and type(value[0])(criteria) in value
                    case ConditionalOp.EVEN:
                        func = lambda: value % 2 == 0
                    case ConditionalOp.ODD:
                        func = lambda: value % 2 != 0
                    case ConditionalOp.NOT:
                        func = lambda: not value
                return template if func() else &amp;quot;&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ahora si lo podemos ver en acción:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;&amp;gt;&amp;gt;&amp;gt; template = &amp;quot;&amp;quot;&amp;quot;
&amp;lt;ul&amp;gt;{cities:repeat city:
  &amp;lt;li style=&amp;quot;background-color: {{#:if even:gray}}{{#if odd:lightgray}}&amp;quot;&amp;gt;{{city}}&amp;lt;&#x2F;li&amp;gt;}
&amp;lt;&#x2F;ul&amp;gt;
&amp;quot;&amp;quot;&amp;quot;
&amp;gt;&amp;gt;&amp;gt; jl.format(template, cities=[&amp;quot;New York&amp;quot;, &amp;quot;New Delhi&amp;quot;, &amp;quot;Tokio&amp;quot;, &amp;quot;Monterrey&amp;quot;])
&amp;lt;ul&amp;gt;
  &amp;lt;li style=&amp;quot;background-color: gray&amp;quot;&amp;gt;New York&amp;lt;&#x2F;li&amp;gt;
  &amp;lt;li style=&amp;quot;background-color: lightgray&amp;quot;&amp;gt;New Delhi&amp;lt;&#x2F;li&amp;gt;
  &amp;lt;li style=&amp;quot;background-color: gray&amp;quot;&amp;gt;Tokio&amp;lt;&#x2F;li&amp;gt;
  &amp;lt;li style=&amp;quot;background-color: lightgray&amp;quot;&amp;gt;Monterrey&amp;lt;&#x2F;li&amp;gt;
&amp;lt;&#x2F;ul&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y listo, ya hemos terminado :).&lt;&#x2F;p&gt;
&lt;p&gt;Sí quieren ver el código completo, lo puedes encontrar en mi &lt;a href=&quot;https:&#x2F;&#x2F;git.kirbylife.dev&#x2F;kirbylife&#x2F;jinja_lowcost&quot;&gt;git&lt;&#x2F;a&gt;.&lt;br &#x2F;&gt;
Como conclusión les puedo decir que, aunque podamos hacer las cosas por nuestra cuenta, no significa que las debamos hacer... así que háganme un favor a mi y a ustedes y no utilicen este monstruo de código en producción, utilicen &lt;a href=&quot;https:&#x2F;&#x2F;jinja.palletsprojects.com&#x2F;en&#x2F;stable&#x2F;&quot;&gt;jinja2&lt;&#x2F;a&gt; y ya.&lt;&#x2F;p&gt;
&lt;h2&gt;Bonus&lt;&#x2F;h2&gt;
&lt;p&gt;En el código de Git hay una instrucción extra que es &amp;quot;call&amp;quot;, por que desde la plantilla no podemos mandar llamar a una función, así que si queremos acceder a todos métodos de un string (title, center, etc...) tendríamos que implementar una a una, lo cual puede ser algo molesto, así que podemos incluir una opción mas al Enum e implementarlo:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Instruction(Enum):
    ...
    NOP = &amp;quot;nop&amp;quot;

class JinjaLowCost(Formatter):
    def format_field(self, value: Any, format_spec: str) -&amp;gt; Any:
        instruction, params, template = self.parse_instruction(format_spec)

        match instruction:
            ...
            case Instruction.CALL:
                if not isinstance(value, Callable):
                    raise Exception(f&amp;quot;{value} is not callable&amp;quot;)
                return str(value(*params))
            ...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y listo, ya podemos hacer:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;&amp;gt;&amp;gt;&amp;gt; jl.format(&amp;quot;{name.title:call}&amp;quot;, name=&amp;quot;JiNjA LoW cOsT&amp;quot;)
&amp;quot;Jinja Low Cost&amp;quot;

&amp;gt;&amp;gt;&amp;gt; jl.format(&amp;quot;{exchange.get:call {currency}} {currency}&amp;quot;, exchange={&amp;quot;USD&amp;quot;: 1, &amp;quot;MXN&amp;quot;: 19.7}, currency=&amp;quot;MXN&amp;quot;)
&amp;quot;19.7 MXN&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y ya, ahora sí eso es todo, adiós.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
        <title>funcionamiento de la generación de claves TOTP</title>
        <pubDate>Tue, 26 Dec 2023 05:43:42 GMT</pubDate>
        <link>https://blog.kirbylife.dev/post/funcionamiento-de-la-generacion-de-claves-totp-9</link>
        <guid isPermaLink="true">https://blog.kirbylife.dev/post/9</guid>
        <description>&lt;p&gt;Cada vez es mas común que la mayoría de sitios web te pidan activar la verificación de dos factores mediante el uso de claves OTP (Contraseñas de un solo uso).&lt;br &#x2F;&gt;
Su uso es realmente simple: el servicio en donde quieres activar dicha característica te proporciona una clave alfanumérica, ya sea en texto plano o mediante un código QR, y tu la introduces en un software que te sirva como llavero de ese tipo de claves (Authy, 1Password, Aegis, Vualtwarden, etc...), y listo. Cada que quieres iniciar sesión en la web, a parte del usuario y la contraseña, también deberás introducir el código de 6 dígitos que te genera en tiempo real tu llavero.&lt;&#x2F;p&gt;
&lt;p&gt;El algoritmo detrás de TOTP es bastante simple ya que solo necesita 2 datos para generarte la clave de 6 dígitos y es:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Una llave alfanumérica&lt;&#x2F;li&gt;
&lt;li&gt;La fecha y hora actual en UTC&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Teniendo esos 2 datos realmente podrías calcular en un papel tu contraseña de un solo uso (aunque para cuando termines, ya habrán pasado 30 segundos y ya no funcionaría jeje).&lt;&#x2F;p&gt;
&lt;p&gt;Voy a dividir el proceso en varios pasos para que se pueda entender mejor.&lt;&#x2F;p&gt;
&lt;h2&gt;Preprocesar los datos&lt;&#x2F;h2&gt;
&lt;p&gt;Primero que nada la fecha debemos pasarla de un formato como &lt;code&gt;12-Dic-2023 14:04:03&lt;&#x2F;code&gt; a su &lt;a href=&quot;https:&#x2F;&#x2F;es.wikipedia.org&#x2F;wiki&#x2F;Tiempo_Unix&quot;&gt;Unix Epoch&lt;&#x2F;a&gt;, que es la cantidad de segundos que han pasado desde el 1&#x2F;Enero&#x2F;1970 hasta la fecha seleccionada. Así pues, el Unix Epoch de la fecha anterior luce algo como &lt;code&gt;1702411443&lt;&#x2F;code&gt;.&lt;br &#x2F;&gt;
Una vez que ya tienes la fecha en ese formato, ahora debes dividirlo por el intervalo de tiempo que necesites que se refresque la clave. Lo estándar suele ser 30 segundos.&lt;br &#x2F;&gt;
Así que tomando el ejemplo anterior quedaría de la siguiente manera &lt;code&gt;1702411443 &#x2F; 30 = 56747048&lt;&#x2F;code&gt;. Solo nos interesa la parte de los enteros, los decimales los puedes descartar.&lt;br &#x2F;&gt;
Teniendo ya ese número, el último paso es sacar los bytes en formato &lt;a href=&quot;https:&#x2F;&#x2F;es.m.wikipedia.org&#x2F;wiki&#x2F;Endianness&quot;&gt;Big Endian&lt;&#x2F;a&gt;. En Python se puede hacer con el método &lt;code&gt;int.to_bytes&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;msg = 56747048
bytes_msg = msg.to_bytes(8, byteorder=&amp;quot;big&amp;quot;)
print(list(bytes_msg))
# [3, 97, 228, 40]
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como primer parámetro le pasamos la longitud de bytes del número y como segundo parámetro en que &lt;a href=&quot;https:&#x2F;&#x2F;es.m.wikipedia.org&#x2F;wiki&#x2F;Endianness&quot;&gt;Endianidad&lt;&#x2F;a&gt; los queremos.&lt;&#x2F;p&gt;
&lt;p&gt;La siguiente tarea es decodificar la clave que nos dieron en la web, ya que todas las páginas la dan en formato &lt;a href=&quot;https:&#x2F;&#x2F;es.wikipedia.org&#x2F;wiki&#x2F;Base32&quot;&gt;Base32&lt;&#x2F;a&gt; para que sea mas sencillo copiar en texto plano o dictarla.&lt;br &#x2F;&gt;
Esto con ayuda de alguna web o biblioteca de Python se puede hacer fácil.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;from base64 import b32decode
TOTP_KEY = &amp;quot;BOMY6BV5BWVJEWJ4ZLDYKN3LSIS736B3&amp;quot; # Clave de ejemplo
key_decoded = b32decode(TOTP_KEY, casefold=True)
print(list(key_decoded))
# [11, 153, 143, 6, 189, ..., 248, 59]
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A partir de este punto a la fecha ya procesada le voy a llamar &amp;quot;mensaje&amp;quot; y a la clave ya decodificada le voy a llamar &amp;quot;llave&amp;quot;, para evitar confusiones futuras.&lt;br &#x2F;&gt;
Teniendo ya nuestros datos procesados, ahora si vamos a la generación de la clave como tal.&lt;&#x2F;p&gt;
&lt;h2&gt;HMAC-SHA1&lt;&#x2F;h2&gt;
&lt;p&gt;HMAC es un algoritmo que fue pensado con el propósito de tener un método robusto y flexible para validar mensajes en combinación con un llave secreta.&lt;br &#x2F;&gt;
HMAC puede utilizarse con una multitud de algoritmos &lt;a href=&quot;https:&#x2F;&#x2F;es.wikipedia.org&#x2F;wiki&#x2F;Funci%C3%B3n_hash&quot;&gt;Hash&lt;&#x2F;a&gt; y TOTP utilizar SHA1 como algoritmo de Hasheo. Esto en principio podría parecer inseguro, ya que SHA1 es un algoritmo que se podría considerar obsoleto para guardar contraseñas, pero TOTP no utiliza a SHA1 para generar un hash para almacenarlo, solo lo usa para generar una clave que a lo mucho servirá por un intervalo corto de tiempo y luego será inútil. Además, SHA1 es bastante mas barato de computar que otros competidores como SHA-3, SHA-256, Tiger, Etc...&lt;br &#x2F;&gt;
La forma de calcularlo es un poco compleja de entender, pero por fortuna no tiene tantos pasos.&lt;&#x2F;p&gt;
&lt;p&gt;Los pasos que vamos a seguir son los siguientes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Primero generamos un Array de 64 elementos, primero irán los elementos de nuestra llave y, en caso de que no se llenen todos los campos, rellenamos con ceros a la derecha. Lo llamaremos &lt;code&gt;key_pad&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;# Indice   0    1    2   3   4        19   20  21 22      62 63
key_pad = [11, 153, 143, 6, 189, ..., 248, 59, 0, 0, ..., 0, 0]
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;A partir de ese Array, creamos otro con todos los elementos del anterior pero aplicando un &lt;a href=&quot;https:&#x2F;&#x2F;es.wikipedia.org&#x2F;wiki&#x2F;Puerta_XOR&quot;&gt;XOR&lt;&#x2F;a&gt; con el número &lt;code&gt;0x36&lt;&#x2F;code&gt;. A este nuevo grupo de bytes lo llamamos &lt;code&gt;i_key_pad&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;i_key_pad = bytes(x ^ 0x36 for x in key_pad)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Repetimos el paso anterior pero aplicando el XOR con el número &lt;code&gt;0x5C&lt;&#x2F;code&gt; y a este otro grupo de bytes será el &lt;code&gt;o_key_pad&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;o_key_pad = bytes(x ^ 0x5C for x in key_pad)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Concatenamos el &lt;code&gt;i_key_pad&lt;&#x2F;code&gt; con los valores de nuestro mensaje.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;i_key_pad_msg = i_key_pad + msg
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Lo que resulte lo pasamos por nuestra función de Hasheo (SHA1 en nuestro caso).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;i_key_pad_msg_hashed = hashlib.sha1(i_key_pad_msg).digest()
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Concatenamos lo que resulte del paso anterior con lo que tenemos en &lt;code&gt;o_key_pad&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;o_key_pad_msg = o_key_pad + i_key_pad_msg_hashed
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Y como último paso, tambien hasheamos lo que haya resultado del paso anterior.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;hmac_result = hashlib.sha1(o_key_pad_msg).digest()
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;La razón del uso de los números &lt;code&gt;0x5C&lt;&#x2F;code&gt; y &lt;code&gt;0x36&lt;&#x2F;code&gt; no está del todo clara para mi. Según vi que era por que esos números son especialmente buenos para ofuscar y generan un patrón único que mejora la seguridad... esto no lo puedo asegurar, pero apuesto a que las personas que trabajaron en el diseño del algoritmo saben infinitas veces mas que yo sobre criptografía, así que solo nos queda confiar.&lt;&#x2F;p&gt;
&lt;p&gt;Una vez sacado el resultado del HMAC, ahora si vamos a generar nuestra bonita clave TOTP.&lt;&#x2F;p&gt;
&lt;h2&gt;Algoritmo TOTP&lt;&#x2F;h2&gt;
&lt;p&gt;Esta última parte también es una locura de cosas que parecen meramente elegidas al azar pero ya son los últimos pasos antes de tener nuestra preciada clave TOTP, así que vamos a continuar.&lt;br &#x2F;&gt;
Los pasos a seguir son:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Tomar el valor en la posición 20 de los bytes que obtuvimos con HMAC y de ese valor, obtener los primeros 4 bits. Esto último lo podemos hacer utilizando el operador &lt;a href=&quot;https:&#x2F;&#x2F;es.wikipedia.org&#x2F;wiki&#x2F;Puerta_AND&quot;&gt;AND&lt;&#x2F;a&gt; con el número &lt;code&gt;0xF&lt;&#x2F;code&gt; (una explicación mas detallada acerca se encuentra al pie de página).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;hmac_result = ...
start = hmac_result[19] &amp;amp; 0xF
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Ahora utilizamos ese número como índice inicial y tomamos los siguientes 4 valores.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;raw_bytes = hmac_result[start:start + 4]
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Ahora generamos un número entero utilizando esos 4 bytes en formato Big Endian.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;raw_token = int.from_bytes(raw_bytes, byteorder=&amp;quot;big&amp;quot;)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Ahora a ese valor le aplicamos la máscara &lt;code&gt;0x7FFFFFFF&lt;&#x2F;code&gt; de vuelta con el operador AND.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;raw_token = raw_token &amp;amp; 0x7F_FF_FF_FF
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Y como último paso debemos saber cual es el número de dígitos que debe tener nuestro token TOTP. Lo mas común es que sean 6 pero podría variar dependiendo del servicio web.&lt;br &#x2F;&gt;
Conociendo eso ahora le aplicamos el operador &lt;a href=&quot;https:&#x2F;&#x2F;es.wikipedia.org&#x2F;wiki&#x2F;Operaci%C3%B3n_m%C3%B3dulo&quot;&gt;MOD&lt;&#x2F;a&gt; con un número con esa cantidad de 0&#x27;s en la izquierda y un 1 a la derecha. Por ejemplo con 6 dígitos sería &lt;code&gt;1_000_000&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;token = raw_token % 1_000_000
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y ¡¡listo!!, ese token ya lo podríamos introducir en el campo donde nos pide la clave de doble autenticación.&lt;&#x2F;p&gt;
&lt;p&gt;Parece un mecanismo algo retorcido, pero es de hecho bastante ingenioso ya que la generación de clave se hace utilizando únicamente la fecha y hora actual y la llave que nos da la web en donde queremos autenticarnos y este proceso es completamente offline y totalmente en local.&lt;br &#x2F;&gt;
Una prueba de ello es el proyecto en el que cree un llavero de claves TOTP con un Arduino, una pantalla LCD y un RTC. En &lt;a href=&quot;https:&#x2F;&#x2F;mstdn.mx&#x2F;@kirbylife&#x2F;110272946339632120&quot;&gt;mi Mastodon&lt;&#x2F;a&gt; esta el proyecto, por si gustan verlo.&lt;&#x2F;p&gt;
&lt;p&gt;También aquí dejo una implementación, tanto en &lt;a href=&quot;https:&#x2F;&#x2F;git.kirbylife.dev&#x2F;kirbylife&#x2F;snippets&#x2F;src&#x2F;branch&#x2F;main&#x2F;TOTP-algorithm&#x2F;Python-TOTP-algorithm.md&quot;&gt;Python&lt;&#x2F;a&gt; como en &lt;a href=&quot;https:&#x2F;&#x2F;git.kirbylife.dev&#x2F;kirbylife&#x2F;snippets&#x2F;src&#x2F;branch&#x2F;main&#x2F;TOTP-algorithm&#x2F;Rust-TOTP-algorithm.md&quot;&gt;Rust&lt;&#x2F;a&gt; de lo presentado en este artículo.&lt;&#x2F;p&gt;
&lt;h3&gt;Explicación sobre filtrar bits con el operador AND&lt;&#x2F;h3&gt;
&lt;p&gt;El operador AND se emplea para filtrar bits de un número aplicándole una máscara.&lt;br &#x2F;&gt;
Por ejemplo, consideremos el número &lt;code&gt;219 = 0b11011011&lt;&#x2F;code&gt;, y si solo nos interesan los primeros 4 bits, aplicamos una máscara con 4 bits activos &lt;code&gt;0b1111 = 0xF&lt;&#x2F;code&gt;. Al utilizar el operador AND, obtenemos &lt;code&gt;0b11011011 &amp;amp; 0xF = 0b1011&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Para comprender mejor la operación, observemos la tabla de verdad del operador AND:&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;A&lt;&#x2F;th&gt;
&lt;th&gt;B&lt;&#x2F;th&gt;
&lt;th&gt;&amp;amp;&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Y viendo la operación en una tabla se puede apreciar mejor como, efectivamente, solo se están conservando los bits del número inicial en donde la máscara tiene 1&#x27;s.&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;&#x2F;th&gt;
&lt;th&gt;7&lt;&#x2F;th&gt;
&lt;th&gt;6&lt;&#x2F;th&gt;
&lt;th&gt;5&lt;&#x2F;th&gt;
&lt;th&gt;4&lt;&#x2F;th&gt;
&lt;th&gt;3&lt;&#x2F;th&gt;
&lt;th&gt;2&lt;&#x2F;th&gt;
&lt;th&gt;1&lt;&#x2F;th&gt;
&lt;th&gt;0&lt;&#x2F;th&gt;
&lt;th&gt;Decimal&lt;&#x2F;th&gt;
&lt;th&gt;Hexadecimal&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Valor&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;219&lt;&#x2F;td&gt;
&lt;td&gt;0xDB&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;Máscara&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;15&lt;&#x2F;td&gt;
&lt;td&gt;0xF&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;Resultado&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;11&lt;&#x2F;td&gt;
&lt;td&gt;0xB&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;h3&gt;Notas&lt;&#x2F;h3&gt;
&lt;p&gt;En este artículo expliqué una versión simplificada de HMAC-SHA1. La versión completa del algoritmo HMAC toma en cuenta la longitud de la llave y, sí es mayor a 64, le aplica un SHA1 y utiliza ese resultado como nueva llave. Sin embargo, dado que para generar claves TOTP las llaves raramente superan esa longitud, decidí omitir ese detalle para simplificar la explicación.&lt;&#x2F;p&gt;
&lt;h3&gt;Fuentes y enlaces de interés&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.ietf.org&#x2F;rfc&#x2F;rfc2104.txt&quot;&gt;Especificación de HMAC RFC-2104&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;datatracker.ietf.org&#x2F;doc&#x2F;html&#x2F;rfc6238&quot;&gt;Especificación de TOTP RFC-6238&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;es.wikipedia.org&#x2F;wiki&#x2F;HMAC&quot;&gt;https:&#x2F;&#x2F;es.wikipedia.org&#x2F;wiki&#x2F;HMAC&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jedisct1&#x2F;rust-hmac-sha1&#x2F;blob&#x2F;master&#x2F;src&#x2F;lib.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;jedisct1&#x2F;rust-hmac-sha1&#x2F;blob&#x2F;master&#x2F;src&#x2F;lib.rs&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pyauth&#x2F;pyotp&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;pyauth&#x2F;pyotp&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</description>
      </item>
      <item>
        <title>Rust en Arduino - La manera difícil</title>
        <pubDate>Thu, 22 Dec 2022 06:44:14 GMT</pubDate>
        <link>https://blog.kirbylife.dev/post/rust-en-arduino-la-manera-dificil-8</link>
        <guid isPermaLink="true">https://blog.kirbylife.dev/post/8</guid>
        <description>&lt;p&gt;Vamos a realizar el ejemplo mas sencillo que puede existir en Arduino, como el “hola mundo” para el área de embebidos, un led que parpadea.&lt;&#x2F;p&gt;
&lt;p&gt;Antes de empezar necesitamos adecuar nuestro ambiente de trabajo y para ello se necesitarán 3 cosas:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;avr-gcc
&lt;ul&gt;
&lt;li&gt;Herramientas de compilación para la arquitectura del procesador&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;avr-binutils
&lt;ul&gt;
&lt;li&gt;Herramientas para manipulación del resultado de compilación&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;avrdude
&lt;ul&gt;
&lt;li&gt;Para subir al Arduino el resultado de nuestra compilación desde linea de comandos&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Rahix&#x2F;avr-hal&#x2F;blob&#x2F;main&#x2F;avr-specs&#x2F;avr-atmega328p.json&quot;&gt;avr-atmel328p.json&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;Es un archivo que contiene info relevante para la compilación, como la ubicación del linker, del compilador, el &lt;a href=&quot;https:&#x2F;&#x2F;es.wikipedia.org&#x2F;wiki&#x2F;Endianness&quot;&gt;endianness&lt;&#x2F;a&gt;, etc...&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Para este texto yo estaré utilizando mi distro de Arch con los paquetes &lt;code&gt;avr-gcc avr-binutils avr-libc avrdude&lt;&#x2F;code&gt; instalados, ustedes pueden ver como instalarlos en su distro o en el sistema operativo de su elección... Desconozco cual es el procedimiento para hacerlo en Windows, pero siempre se puede utilizar el WSL y yo digo que debería ir bien, pero no lo he probado.&lt;&#x2F;p&gt;
&lt;h2&gt;Notación numérica&lt;&#x2F;h2&gt;
&lt;p&gt;Antes de comenzar voy a explicar la notación numérica que estaré utilizando.&lt;br &#x2F;&gt;
Sí un número comienza con &lt;strong&gt;0x&lt;&#x2F;strong&gt; Significa que ese número está siendo expresado en hexadecimal.&lt;br &#x2F;&gt;
De otra forma, si comienza con &lt;strong&gt;0b&lt;&#x2F;strong&gt; indica que ese numero está siendo escrito en binario.&lt;br &#x2F;&gt;
Sí de lo contrario, no contiene ningún prefijo, es que ese número está en decimal.&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prefijo&lt;&#x2F;th&gt;
&lt;th&gt;Sistema&lt;&#x2F;th&gt;
&lt;th&gt;Ejemplo&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;0x&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;
&lt;td&gt;Hexadecimal&lt;&#x2F;td&gt;
&lt;td&gt;&lt;strong&gt;0x&lt;&#x2F;strong&gt;25 = 37&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;strong&gt;0x&lt;&#x2F;strong&gt;FF = 255&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;0b&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;
&lt;td&gt;Binario&lt;&#x2F;td&gt;
&lt;td&gt;&lt;strong&gt;0b&lt;&#x2F;strong&gt;101 = 5&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;strong&gt;0b&lt;&#x2F;strong&gt;1010101 = 85 = 0x55&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;strong&gt;0b&lt;&#x2F;strong&gt;11111111 = 255 = 0xFF&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;«Nada»&lt;&#x2F;td&gt;
&lt;td&gt;Decimal&lt;&#x2F;td&gt;
&lt;td&gt;10, 5, 240&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Si quieres convertir entre los distintos sistemas puedes utilizar a Python con las funciones &lt;code&gt;bin&lt;&#x2F;code&gt; y &lt;code&gt;hex&lt;&#x2F;code&gt;, que reciben un número y lo convierten a binario o hexadecimal respectivamente.&lt;&#x2F;p&gt;
&lt;h2&gt;Registros&lt;&#x2F;h2&gt;
&lt;p&gt;Lo primero que deben de hacer es… olvidar todo lo que saben hasta ahorita de Arduino, por lo menos sí solo haz programado Arduinos con Processing, que es el lenguaje por defecto del Arduino IDE.&lt;&#x2F;p&gt;
&lt;p&gt;Recuerdan esos numeritos a los lados de los pines en tu Arduino?&lt;&#x2F;p&gt;
&lt;pre&gt;
                               +-----+
  +----[PWR]-------------------| USB |--+
  |                            +-----+  |
  |         GND&#x2F;RST2  [ ][ ]            |
  |       MOSI2&#x2F;SCK2  [ ][ ]  A5&#x2F;SCL[ ] |
  |          5V&#x2F;MISO2 [ ][ ]  A4&#x2F;SDA[ ] |
  |                             AREF[ ] |
  |                              GND[ ] |
  | [ ]N&#x2F;C                    SCK&#x2F;&lt;b&gt;13&lt;&#x2F;b&gt;[ ] |
  | [ ]IOREF                 MISO&#x2F;&lt;b&gt;12&lt;&#x2F;b&gt;[ ] |
  | [ ]RST                   MOSI&#x2F;&lt;b&gt;11&lt;&#x2F;b&gt;[ ]~|
  | [ ]3V3    +---+               &lt;b&gt;10&lt;&#x2F;b&gt;[ ]~|
  | [ ]5v    -| A |-               &lt;b&gt;9&lt;&#x2F;b&gt;[ ]~|
  | [ ]GND   -| R |-               &lt;b&gt;8&lt;&#x2F;b&gt;[ ] |
  | [ ]GND   -| D |-                    |
  | [ ]Vin   -| U |-               &lt;b&gt;7&lt;&#x2F;b&gt;[ ] |
  |          -| I |-               &lt;b&gt;6&lt;&#x2F;b&gt;[ ]~|
  | [ ]&lt;b&gt;A0&lt;&#x2F;b&gt;    -| N |-               &lt;b&gt;5&lt;&#x2F;b&gt;[ ]~|
  | [ ]&lt;b&gt;A1&lt;&#x2F;b&gt;    -| O |-               &lt;b&gt;4&lt;&#x2F;b&gt;[ ] |
  | [ ]&lt;b&gt;A2&lt;&#x2F;b&gt;     +---+           INT1&#x2F;&lt;b&gt;3&lt;&#x2F;b&gt;[ ]~|
  | [ ]&lt;b&gt;A3&lt;&#x2F;b&gt;                     INT0&#x2F;&lt;b&gt;2&lt;&#x2F;b&gt;[ ] |
  | [ ]&lt;b&gt;A4&lt;&#x2F;b&gt;&#x2F;SDA  RST SCK MISO     TX►&lt;b&gt;1&lt;&#x2F;b&gt;[ ] |
  | [ ]&lt;b&gt;A5&lt;&#x2F;b&gt;&#x2F;SCL  [ ] [ ] [ ]      RX◄&lt;b&gt;0&lt;&#x2F;b&gt;[ ] |
  |            [ ] [ ] [ ]              |
  |  UNO_R3    GND MOSI 5V  ____________&#x2F;
   \_______________________&#x2F;
&lt;&#x2F;pre&gt;
&lt;p&gt;Pues olvídense de ellos, ya que ahora les presentare los registros.&lt;&#x2F;p&gt;
&lt;p&gt;Los registros son áreas de la memoria de un procesador y su función es guardar o leer información y cada registro tiene un identificador numérico único, comúnmente expresado en hexadecimal.&lt;&#x2F;p&gt;
&lt;p&gt;Lo curioso es que la información no solo puede ser escrita por el procesador, sino que, sensores y otros chips externos al circuito principal también pueden leer y modificar esos registros. En términos prácticos, es lo que modificamos cuando escribimos un digitalWrite y es de donde obtenemos la información cuando ejecutamos un digitalRead en processing.&lt;&#x2F;p&gt;
&lt;p&gt;Dicho esto, en el Arduino UNO podemos identificar físicamente, por así decirlo, 4 secciones distintas de pines:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;La primera, es la sección que tiene todo lo relacionado a la alimentación de la placa como tal.&lt;&#x2F;li&gt;
&lt;li&gt;La segunda es la sección donde se encuentran los pines compatibles con salidas analógicas, son los que tienen grabado antes del nombre del Pin una letra “A”.&lt;&#x2F;li&gt;
&lt;li&gt;La tercera podríamos llamarla “pines generales: primera sección”, que comprenden los pines marcados en el PCB desde el 0 hasta el 7.&lt;&#x2F;li&gt;
&lt;li&gt;y la cuarta y ultima es la sección “pines generales: segunda sección”, que comprende desde el Pin marcado como 8 hasta el 13.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code&gt;                                     +-----+
        +----[PWR]-------------------| USB |--+
        |                            +-----+  |
        |         GND&#x2F;RST2  [ ][ ]            |
        |       MOSI2&#x2F;SCK2  [ ][ ]  A5&#x2F;SCL[ ] | ═══╗
        |          5V&#x2F;MISO2 [ ][ ]  A4&#x2F;SDA[ ] |    ║
        |                             AREF[ ] |    ║
        |                              GND[ ] |    ║
   ╔═══ | [ ]N&#x2F;C                    SCK&#x2F;13[ ] | Sección
   ║    | [ ]IOREF                 MISO&#x2F;12[ ] |    4
   ║    | [ ]RST                   MOSI&#x2F;11[ ]~|    ║
Sección | [ ]3V3    +---+               10[ ]~|    ║
   1    | [ ]5v    -| A |-               9[ ]~|    ║
   ║    | [ ]GND   -| R |-               8[ ] | ═══╝
   ║    | [ ]GND   -| D |-                    |
   ╚═══ | [ ]Vin   -| U |-               7[ ] | ═══╗
        |          -| I |-               6[ ]~|    ║
   ╔═══ | [ ]A0    -| N |-               5[ ]~|    ║
   ║    | [ ]A1    -| O |-               4[ ] | Sección
Sección | [ ]A2     +---+           INT1&#x2F;3[ ]~|    3
   2    | [ ]A3                     INT0&#x2F;2[ ] |    ║
   ║    | [ ]A4&#x2F;SDA  RST SCK MISO     TX►1[ ] |    ║
   ╚═══ | [ ]A5&#x2F;SCL  [ ] [ ] [ ]      RX◄0[ ] | ═══╝
        |            [ ] [ ] [ ]              |
        |  UNO_R3    GND MOSI 5V  ____________&#x2F;
         \_______________________&#x2F;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Las secciones en donde nos vamos a centrar aquí serán las 3 ultimas.&lt;&#x2F;p&gt;
&lt;p&gt;Cada sección de pines tiene asignado 3 registros, uno que contendrá el modo en el que se encuentra cada Pin, otro contiene los valores de salida y otro con los valores de entrada.&lt;&#x2F;p&gt;
&lt;p&gt;Cada registro en el Arduino UNO es de 8 bits y cada bit en ese entero &lt;code&gt;u8&lt;&#x2F;code&gt; va a ser capaz de manipular independientemente un Pin. Creo que con un ejemplo lo vamos a entender mejor.&lt;&#x2F;p&gt;
&lt;p&gt;Para mayor facilidad en este ejemplo voy a hacer este ejercicio con el 3er grupo de pines, los marcados desde el 0 hasta el 7.&lt;&#x2F;p&gt;
&lt;p&gt;Como había dicho anteriormente, cada grupo de pines tiene 3 registros asignados, uno que indica si el pin es de lectura o escritura y otro que trae la info. Bien, entonces digamos que el grupo 3 tiene asignados los siguientes registros:&lt;br &#x2F;&gt;
AA - para controlar el modo&lt;br &#x2F;&gt;
AB - para escribir valores de salida&lt;br &#x2F;&gt;
AC - para leer los valores de entrada&lt;&#x2F;p&gt;
&lt;p&gt;Cada bit del u8 corresponde a un Pin físico de nuestro Arduino.&lt;br &#x2F;&gt;
Comencemos con el registro AA, que es el que controla los modos.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;      8   7   6   5   4   3   2   1
AA = [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]
--------------------------------------
     [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]  \
      7   6   5   4   3   2   1   0   |
                              TX  RX  |
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Digamos que si le escribimos un 1 en algún bit del registro AA (el de modos), pondremos ese pin en modo de OUTPUT y por lo contrario, si le colocamos un 0, se pondrá en modo INPUT.&lt;br &#x2F;&gt;
entonces haciendo un:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;      8   7   6   5   4   3   2   1
AA = [0] [0] [0] [0] [1] [1] [1] [1] = 0x0F = 15
--------------------------------------
     [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]  \
      7   6   5   4   3   2   1   0   |
                              TX  RX  |
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Eso colocaría al PIN 1,2,3,4 en modo OUTPUT y el 5,6,7,8 en modo INPUT.&lt;&#x2F;p&gt;
&lt;p&gt;Ya que controlamos mas o menos esta información, ahora sigue el siguiente registro.&lt;br &#x2F;&gt;
Nuestro registro hipotético AB sería el encargado de almacenar la información de salida de los pines.&lt;br &#x2F;&gt;
Sí el Pin está en modo OUTPUT y de manera interna colocas un 1, el Pin mandaría voltaje alto (5v).&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;      8   7   6   5   4   3   2   1
AA = [0] [0] [0] [0] [0] [0] [0] [1] = 0x01 = 1
--------------------------------------
     [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]  \
      7   6   5   4   3   2   1   0   |
                              TX  RX  |
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Si colocamos el valor 0b00000001 = 0x01 = 1, esto haría que el actuador que tengamos conectado al Pin marcado como 0 en el PCB, se active.&lt;&#x2F;p&gt;
&lt;p&gt;Y el último registro, el AC, se puede intuir mas o menos como se va a comportar, sí el pin está en modo INPUT, y con algún sensor se le mande voltaje alto, este colocaría el bit correspondiente al Pin en 1 (Al final del post hay una nota con respecto a esto).&lt;&#x2F;p&gt;
&lt;p&gt;Ok, perfecto, ya sabemos como gestionar el modo y la info de los pines desde los registros... pero como se manipulan o leen esos registros desde Rust?&lt;&#x2F;p&gt;
&lt;h2&gt;Esqueleto del main.rs&lt;&#x2F;h2&gt;
&lt;p&gt;Para hacer que Rust pueda compilar a un Arduino se necesita un archivo &lt;code&gt;main.rs&lt;&#x2F;code&gt; algo parecido a esto:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Rust&quot;&gt;#![no_std]
#![no_main]

#[no_mangle]
pub extern &amp;quot;C&amp;quot; fn main() {}

#[panic_handler]
fn panic(_info: &amp;amp;::core::panic::PanicInfo) -&amp;gt; ! {
    loop {}
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y lo iré explicando linea a linea.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Rust&quot;&gt;#![no_std]
#![no_main]
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Estas primeras lineas contienen unos macros que le indican al compilar de Rust 2 cosas:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;No incluyan la biblioteca estándar a este proyecto&lt;&#x2F;li&gt;
&lt;li&gt;Este proyecto no tendrá una función main &amp;quot;rustificada&amp;quot;.&lt;br &#x2F;&gt;
Este ultimo punto significa que vas a deslindar al compilador de asignar un punto de entrada a tu programa, esa chamba te la estás delegando a ti y sí tu programa no sabe por donde arrancar, el compilador se lavará las manos.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-Rust&quot;&gt;#[no_mangle]
pub fn extern &amp;quot;C&amp;quot; main() {
} 
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;El macro encima de la función &lt;code&gt;main&lt;&#x2F;code&gt; con muchas cositas extra, le está indicando al compilador que no haga optimizaciones de nombres.
Y es que Rust (y muchos otros lenguajes como C++, D, etc...), al momento de compilar, les da igual el nombre que tu le hayas colocado a tu función, internamente se llamará de otra forma.&lt;br &#x2F;&gt;
En programas clásicos eso da igual, mientras todo funcione como tenga que funcionar, a tu realmente te da igual que la función se llame &amp;quot;main&amp;quot; o &lt;code&gt;punto_de_entrada_asbdauhsd123&lt;&#x2F;code&gt;, mientras el programa comience por ahí, está todo bien.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Sin &lt;code&gt;#[no_mangle]&lt;&#x2F;code&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;godbolt.org&#x2F;z&#x2F;boT6aKobf&quot;&gt;https:&#x2F;&#x2F;godbolt.org&#x2F;z&#x2F;boT6aKobf&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rust&lt;&#x2F;th&gt;
&lt;th&gt;Ensamblador&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;pre&gt;pub fn square(num: i32) -&amp;gt; i32 {&lt;br&gt;    num * num&lt;br&gt;}&lt;&#x2F;pre&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;pre&gt;&lt;b&gt;_ZN7example6square17h6dfd88b0be9f1c7eE:&lt;&#x2F;b&gt;&lt;br&gt;    push rax&lt;br&gt;    imul edi, edi&lt;br&gt;    mov dword ptr [rsp + 4], edi&lt;br&gt;...&lt;&#x2F;pre&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;ul&gt;
&lt;li&gt;Con &lt;code&gt;#[no_mangle]&lt;&#x2F;code&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;godbolt.org&#x2F;z&#x2F;W8oe95nar&quot;&gt;https:&#x2F;&#x2F;godbolt.org&#x2F;z&#x2F;W8oe95nar&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rust&lt;&#x2F;th&gt;
&lt;th&gt;Ensamblador&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;pre&gt;#[no_mangle]&lt;br&gt;pub fn &lt;b&gt;square&lt;&#x2F;b&gt;(num: i32) -&amp;gt; i32 {&lt;br&gt;    num * num&lt;br&gt;}&lt;&#x2F;pre&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;pre&gt;&lt;b&gt;square:&lt;&#x2F;b&gt;&lt;br&gt;    push rax&lt;br&gt;    imul edi, edi&lt;br&gt;    mov dword ptr [rsp + 4], edi&lt;br&gt;...&lt;&#x2F;pre&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;(En estos ejemplos se está utilizando el &amp;quot;pub&amp;quot; para que jale en godbolt, pueden ignorarlos sin problemas)&lt;&#x2F;p&gt;
&lt;p&gt;Peeeeero, recuerden que el trabajo de asignar un punto de entrada al programa se la quitamos al compilador, así que la persona que está programando es la que se debe aventar esa chamba y es justo lo que haremos a continuación.&lt;br &#x2F;&gt;
La segunda linea, esa que tiene un montón de cositas extras en la función &lt;code&gt;main&lt;&#x2F;code&gt; significan:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pub&lt;&#x2F;code&gt;: Le estás indicando al compilador que la función &lt;code&gt;main&lt;&#x2F;code&gt; puede ser llamada por fuera del archivo &lt;code&gt;main.rs&lt;&#x2F;code&gt; que la contiene, ya que de otra manera, esa función solo podría ser accedida desde el propio archivo.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;extern &amp;quot;C&amp;quot; main&lt;&#x2F;code&gt;: Le estamos indicando al compilador que el ABI que utilizará esa función es el de &amp;quot;C&amp;quot;.&lt;br &#x2F;&gt;
Esto es un poco mas complejo de explicar, pero digamos que existen varias formas de llamar a una función y cada lenguaje tiene la suya. Pero para comunicarse entre lenguajes necesitan un mediador o &amp;quot;especificación en común&amp;quot; y ahí es donde entra el ABI de C y básicamente es la forma en la que esa función debe ser llamada (ir a la lista de enlaces para mas información acerca de esto, por sí les interesa).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Rust&quot;&gt;#[panic_handler]
fn panic(_info: &amp;amp;::core::panic::PanicInfo) -&amp;gt; ! {
    loop {}
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Visto lo anterior, esto es muuucho mas sencillo de entender. Vamos línea por línea:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;El macro &lt;code&gt;panic_handler&lt;&#x2F;code&gt; le indica al compilar que deberá llamar a la función que se encuentra debajo cada que ocurra un &lt;code&gt;panic&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;fn panic&lt;&#x2F;code&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;core::panic::PanicInfo&lt;&#x2F;code&gt;: Contiene la información del &lt;code&gt;panic&lt;&#x2F;code&gt;, la linea que lo causó, el mensaje de error, el traza del error, etc...&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-&amp;gt; !&lt;&#x2F;code&gt;: Esto le está indicando al compilador que esa función nunca va a terminar ya que tiene un loop infinito sin condición de salida. Se puede prescindir de ello, pero ya que existe la función de indicar algo tan especifico al compilador, pues se lo ponemos.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;loop {}&lt;&#x2F;code&gt;: Es un loop infinito sin condición de salida.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Y a grandes rasgos significa qué cuando nuestro programa tenga un &lt;code&gt;panic!&lt;&#x2F;code&gt;, lo que hará será ejecutar un loop infinito y tendremos que reiniciar a mano nuestro Arduino.&lt;br &#x2F;&gt;
Ya existen crates que tienen implementadas varias entradas en pánico (Consultar las referencias la final del documento), pero quería enseñarles que también se pueden implementar las propias.&lt;&#x2F;p&gt;
&lt;p&gt;También necesitaremos tunear un poco el Cargo.toml agregando esto al final:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;[profile.release]
lto = true
panic = &amp;quot;abort&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y es básicamente un workaround para que todo el conjunto compile, ya que la arquitectura AVR no está soportada oficialmente por el equipo de Rust, puede que algunos cambios rompan la compatibilidad.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;El primero es la optimización del linker... esto no sería necesario, pero actualmente existe un bug con ciertas versiones de LLVM que impiden que el programa compile, y se puede evitar habilitando esa optimización.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;panic = &amp;quot;abort&amp;quot;&lt;&#x2F;code&gt;: Aquí vamos con un concepto algo complicado... Cuando tu programa en Rust tiene un &lt;code&gt;panic&lt;&#x2F;code&gt;, por defecto comienza a desalojar la información que existía en el stack de atrás hacía adelante, el &amp;quot;cómo&amp;quot; suele venir en la biblioteca estándar, ya que puede variar entre arquitecturas y entre OS&#x27;s. Pero aquí como no tenemos std, pues nos tocaría hacer a mano esa liberación de memoria, peeero como pues nuestro panic es un maldito loop vacío, pues nos da igual sí el stack se queda con cosas o no. el &amp;quot;abort&amp;quot; indica que deje todo como está y vaya al &lt;code&gt;panic_handler&lt;&#x2F;code&gt; directamente (ver el lang_item = &amp;quot;eh_personality&amp;quot; para mas info al respecto).&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Y ¡¡Listo!!, ya con ese &lt;code&gt;main.rs&lt;&#x2F;code&gt; y &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt; está todo listo para compilar tu hermoso primer ejemplo con la siguiente linea:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cargo build -Z build-std=core --target avr-atmega328p.json --release
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Cada parámetro significa lo siguiente:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;-Z build-std=core&lt;&#x2F;code&gt;: Sin esta linea, Cargo no agregará el crate &amp;quot;core&amp;quot;. Este crate es necesario para el &lt;code&gt;panic_handler&lt;&#x2F;code&gt; y también incluye varias funciones que utilizaremos mas adelante.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--target avr-atmega328p.json&lt;&#x2F;code&gt;: Le indica al compilador cual es el target custom que utilizaremos, en este caso es un json que ya está tuneado para el micro del Arduino.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--release&lt;&#x2F;code&gt;: Para que se tomen en cuenta los workarounds que agregamos al &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt; es necesario compilar en modo release.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Una acotación mas, y es que para poder compilar todo nuestro desmadrito, será necesario hacerlo en la versión nocturna del compilador, ya que utilizamos características como el &lt;code&gt;build-std=core&lt;&#x2F;code&gt;, que no es posible utilizarlo en la rama estable.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;&amp;gt;&amp;gt; cargo build -Z build-std=core --target avr-atmega328p.json --release
 Compiling compiler_builtins v0.1.85
 Compiling core v0.0.0 (~&#x2F;.rustup&#x2F;toolchains&#x2F;nightly-x86_64-unknown-linux-gnu&#x2F;lib&#x2F;rustlib&#x2F;src&#x2F;rust&#x2F;library&#x2F;core)
 Compiling rustc-std-workspace-core v1.99.0 (~&#x2F;.rustup&#x2F;toolchains&#x2F;nightly-x86_64-unknown-linux-gnu&#x2F;lib&#x2F;rustlib&#x2F;src&#x2F;rust&#x2F;library&#x2F;rustc-std-workspace-core)
 Compiling arduino_blinkrs v0.1.0 (~&#x2F;Proyectos&#x2F;arduino_blinkrs)
  Finished release [optimized] target(s) in 16.59s

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;La compilación se realizó con éxito :) oficialmente hemos programado un firmware para el Arduino con Rust.&lt;br &#x2F;&gt;
Pero aún faltan un montón de cosas, ya que de momento solo tenemos un firmware que no hace absolutamente nada.&lt;br &#x2F;&gt;
Lo siguiente que toca es aprender a manipular los registros que vimos anteriormente.&lt;&#x2F;p&gt;
&lt;h2&gt;Hola hoja de especificaciones&lt;&#x2F;h2&gt;
&lt;p&gt;Pero antes tenemos que saber qué registros modificar y para ello no hay nada como consultar la propia &lt;a href=&quot;http:&#x2F;&#x2F;ww1.microchip.com&#x2F;downloads&#x2F;en&#x2F;DeviceDoc&#x2F;ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf&quot;&gt;hoja de especificaciones del ATmega328p&lt;&#x2F;a&gt;, pero como leer eso puede ser un poco abrumador, aquí la voy a resumir un poco:&lt;br &#x2F;&gt;
Los grupos de pines tienen asignada una letra que los identifica:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;                                    +-----+
       +----[PWR]-------------------| USB |--+
       |                            +-----+  |
       |         GND&#x2F;RST2  [ ][ ]            |
       |       MOSI2&#x2F;SCK2  [ ][ ]  A5&#x2F;SCL[ ] | ══╗
       |          5V&#x2F;MISO2 [ ][ ]  A4&#x2F;SDA[ ] |   ║
       |                             AREF[ ] |   ║
       |                              GND[ ] |   ║
       | [ ]N&#x2F;C                    SCK&#x2F;13[ ] | Grupo
       | [ ]IOREF                 MISO&#x2F;12[ ] |   B
       | [ ]RST                   MOSI&#x2F;11[ ]~|   ║
       | [ ]3V3    +---+               10[ ]~|   ║
       | [ ]5v    -| A |-               9[ ]~|   ║
       | [ ]GND   -| R |-               8[ ] | ══╝
       | [ ]GND   -| D |-                    |
       | [ ]Vin   -| U |-               7[ ] | ══╗
       |          -| I |-               6[ ]~|   ║
   ╔══ | [ ]A0    -| N |-               5[ ]~|   ║
   ║   | [ ]A1    -| O |-               4[ ] | Grupo
 Grupo | [ ]A2     +---+           INT1&#x2F;3[ ]~|   D
   C   | [ ]A3                     INT0&#x2F;2[ ] |   ║
   ║   | [ ]A4&#x2F;SDA  RST SCK MISO     TX►1[ ] |   ║
   ╚══ | [ ]A5&#x2F;SCL  [ ] [ ] [ ]      RX◄0[ ] | ══╝
       |            [ ] [ ] [ ]              |
       |  UNO_R3    GND MOSI 5V  ____________&#x2F;
        \_______________________&#x2F;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y como había dicho al inicio de la presentación, existen 3 registros para cada grupo de pines:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;El registro para controlar en que modo van a estar los pines lo llamaremos &amp;quot;DDR&amp;quot;, que significa &amp;quot;Data Direction Register&amp;quot;.&lt;&#x2F;li&gt;
&lt;li&gt;El registro que contiene la información de salida, lo llamaremos &amp;quot;PORT&amp;quot;, que significa... &amp;quot;PORT&amp;quot;.&lt;&#x2F;li&gt;
&lt;li&gt;El registro que contiene la información de entrada, y su nombre es &amp;quot;PIN&amp;quot;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Y cada Pin tiene un bit asignado y se enumeran desde el 0 hasta el 7.&lt;br &#x2F;&gt;
Así que para construir una dirección a un Pin en concreto lo haremos de la siguiente manera:&lt;br &#x2F;&gt;
{Nombre del registro}{Letra identificativa}{número del bit}&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ejemplo&lt;&#x2F;th&gt;
&lt;th&gt;Descripción&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PORTC5&lt;&#x2F;td&gt;
&lt;td&gt;6to Pin del registro &amp;quot;PORT&amp;quot; grupo &amp;quot;C&amp;quot;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;PINB&lt;&#x2F;td&gt;
&lt;td&gt;Todos los pines del registro &amp;quot;PIN&amp;quot; del grupo B&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;D2&lt;&#x2F;td&gt;
&lt;td&gt;3er Pin del grupo &amp;quot;D&amp;quot; Sin determinar ningún registro en específico&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;DDRB6&lt;&#x2F;td&gt;
&lt;td&gt;7mo Pin del registro &amp;quot;DDR&amp;quot; del grupo &amp;quot;B&amp;quot;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;PINC9&lt;&#x2F;td&gt;
&lt;td&gt;No existe, recuerda que sólo hay 8 pines&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Como dato relevante, el LED incrustado en la placa es el B5, solo para que lo sepan, ya que ese justamente será el que haremos parpadear.&lt;&#x2F;p&gt;
&lt;p&gt;Sabiendo eso, nos vamos a la tabla de especificamos, en el apartado en donde están los números de registros para saber cual es el DDR, PORT y PIN de la sección B.&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Address&lt;&#x2F;th&gt;
&lt;th&gt;Name&lt;&#x2F;th&gt;
&lt;th&gt;Bit 7&lt;&#x2F;th&gt;
&lt;th&gt;Bit 6&lt;&#x2F;th&gt;
&lt;th&gt;Bit 5&lt;&#x2F;th&gt;
&lt;th&gt;. . .&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;. . .&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;0x06(0x26)&lt;&#x2F;td&gt;
&lt;td&gt;PINC&lt;&#x2F;td&gt;
&lt;td&gt;-&lt;&#x2F;td&gt;
&lt;td&gt;PINC6&lt;&#x2F;td&gt;
&lt;td&gt;PINC5&lt;&#x2F;td&gt;
&lt;td&gt;. . .&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;0x05(&lt;strong&gt;0x25&lt;&#x2F;strong&gt;)&lt;&#x2F;td&gt;
&lt;td&gt;&lt;strong&gt;PORTB&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;
&lt;td&gt;PORTB7&lt;&#x2F;td&gt;
&lt;td&gt;PORTB6&lt;&#x2F;td&gt;
&lt;td&gt;PORTB5&lt;&#x2F;td&gt;
&lt;td&gt;. . .&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;0x04(&lt;strong&gt;0x24&lt;&#x2F;strong&gt;)&lt;&#x2F;td&gt;
&lt;td&gt;&lt;strong&gt;DDRB&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;
&lt;td&gt;DDRB7&lt;&#x2F;td&gt;
&lt;td&gt;DDRB6&lt;&#x2F;td&gt;
&lt;td&gt;DDRB5&lt;&#x2F;td&gt;
&lt;td&gt;. . .&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;0x03(&lt;strong&gt;0x23&lt;&#x2F;strong&gt;)&lt;&#x2F;td&gt;
&lt;td&gt;&lt;strong&gt;PINB&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;
&lt;td&gt;PINB7&lt;&#x2F;td&gt;
&lt;td&gt;PINB6&lt;&#x2F;td&gt;
&lt;td&gt;PINB5&lt;&#x2F;td&gt;
&lt;td&gt;. . .&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;0x02(0x22)&lt;&#x2F;td&gt;
&lt;td&gt;Reserved&lt;&#x2F;td&gt;
&lt;td&gt;-&lt;&#x2F;td&gt;
&lt;td&gt;-&lt;&#x2F;td&gt;
&lt;td&gt;-&lt;&#x2F;td&gt;
&lt;td&gt;-&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;. . .&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;(Ver sección &amp;quot;Register summary&amp;quot; de la &lt;a href=&quot;http:&#x2F;&#x2F;ww1.microchip.com&#x2F;downloads&#x2F;en&#x2F;DeviceDoc&#x2F;ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf#G3.2063043&quot;&gt;Hoja de especificaciones&lt;&#x2F;a&gt;)&lt;br &#x2F;&gt;
Solo vamos a tomar en cuenta el número que se encuentre entre paréntesis.&lt;&#x2F;p&gt;
&lt;p&gt;Y ¡bingo!, finalmente dimos con que la dirección de los registros Siendo 0x25 para el &amp;quot;PORTB&amp;quot;, 0x24 para el &amp;quot;DDRB&amp;quot; y finalmente el 0x23 para el &amp;quot;PIN&amp;quot;, recordando que están expresados en hexadecimal.&lt;br &#x2F;&gt;
Una vez teniendo esto, ya tenemos todo para ir a nuestro editor.&lt;br &#x2F;&gt;
Para interactuar con los registros vamos a necesitar un par de funciones que se encuentran dentro del crate &lt;code&gt;core&lt;&#x2F;code&gt; y se llaman &lt;code&gt;read_volatile&lt;&#x2F;code&gt; y &lt;code&gt;write_volatile&lt;&#x2F;code&gt;.&lt;br &#x2F;&gt;
Y para mayor comodidad, pondremos en constantes las direcciones de los registros como punteros.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Rust&quot;&gt;...
use core::ptr::{read_volatile, write_volatile};

const PORTB: *mut u8 = 0x25 as *mut u8;
const DDRB: *mut u8 = 0x24 as *mut u8;
const PINB: *mut u8 = 0x23 as *mut u8;

#[no_mangle]
pub extern &amp;quot;C&amp;quot; fn main() {
    unsafe { write_volatile(DDRB, 0b11111111) };
    unsafe { write_volatile(PORTB, 0b00100000) };
}
...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Con la primer linea de &lt;code&gt;write_volatile&lt;&#x2F;code&gt; estamos colocando todo el grupo de pines de la sección &amp;quot;B&amp;quot; en modo OUTPUT (recuerden, 1 es OUTPUT y 0 es INPUT).&lt;br &#x2F;&gt;
Y con la segunda estamos colocando un valor de 1 (Voltaje alto) en PORTB5, que es justamente el que controla el Pin marcado como 13 en la placa, que a su vez es el que controla el LED naranja incluido en el Arduino.&lt;br &#x2F;&gt;
Con este código podemos apreciar 2 cosas:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;write_volatile&lt;&#x2F;code&gt; (&lt;code&gt;y read_volatile&lt;&#x2F;code&gt;) necesitan estar enjauladas en un bloque &lt;code&gt;unsafe&lt;&#x2F;code&gt;, ya que modificarán directamente una dirección de memoria evadiendo por completo el verificador de seguridad de Rust.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;write_volatile&lt;&#x2F;code&gt; recibe 2 parámetros, el registro que queremos modificar y el valor que vamos a asignar.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Compilamos para ver que todo esté funcionando correctamente y parece ser que si:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;&amp;gt;&amp;gt; cargo build -Z build-std=core --target avr-atmega328p.json --release
   Compiling arduino_blinkrs v0.1.0 (~&#x2F;Proyectos&#x2F;arduino_blinkrs)
warning: unused import: `read_volatile`
 --&amp;gt; src&#x2F;main.rs:4:17
  |
4 | use core::ptr::{read_volatile, write_volatile};
  |                 ^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: `arduino_blinkrs` (bin &amp;quot;arduino_blinkrs&amp;quot;) generated 1 warning (run `cargo fix --bin &amp;quot;arduino_blinkrs&amp;quot;` to apply 1 suggestion)
    Finished release [optimized] target(s) in 0.20s
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Nuestro pequeño monstruo parece no tener ningún error (solo un pequeño warning, pero se sabe que esos se ignoran), así que en principio ya podríamos subir este resultado a la placa y ver que, efectivamente, el led de nuestra plaquita se prende, pero... como subimos el código al Arduino?.&lt;&#x2F;p&gt;
&lt;h2&gt;Preparando el binario&lt;&#x2F;h2&gt;
&lt;p&gt;Lo primero es conectar por USB el Arduino a nuestra computadora, evidentemente.&lt;br &#x2F;&gt;
Despues, identificar cual puerto le asignó la PC a la placa. Esto lo podemos hacer entrando al Arduino IDE &amp;gt; Herramientas &amp;gt; Puerto y ver cual nos marca como &amp;quot;Arduino UNO&amp;quot;.&lt;br &#x2F;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;codigocomentado-imgs.us-southeast-1.linodeobjects.com&#x2F;arduino_rust_arduino_ide_screenshot.png&quot; alt=&quot;Captura de pantalla del Arduino IDE mostrando el puerto serie de un Arduino UNO conectado&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;En mi caso me asignó el &#x2F;dev&#x2F;ttyACM0. Ya identificado, podemos pasar a lo que sigue que es, la conversión del .elf a un .hex.&lt;br &#x2F;&gt;
Para hacer esa conversión vamos a necesitar una de las utilidades que instalamos al inicio llamada &amp;quot;avr-objcopy&amp;quot; y se utiliza de la siguiente manera:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;avr-objcopy -O ihex -R .eeprom path&#x2F;al&#x2F;archivo.elf &amp;lt;output&amp;gt;.hex
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Que en este caso sería:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;avr-objcopy -O ihex -R .eeprom target&#x2F;avr-atmega328p&#x2F;release&#x2F;arduino_blinkrs.elf output.hex
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Siendo &lt;code&gt;&#x2F;target&#x2F;avr-atmega328p&#x2F;release&#x2F;arduino_blinkrs.elf&lt;&#x2F;code&gt; nuestro binario de entrada y &lt;code&gt;output.hex&lt;&#x2F;code&gt;, el archivo en donde volcará el resultado de la conversión.&lt;br &#x2F;&gt;
Lo ejecutamos y sí vemos que no arroja ningún error, eso es buena señal.&lt;&#x2F;p&gt;
&lt;p&gt;Lo siguiente ahora si es subirlo a nuestra placa, y para ello se hará uso de otra herramienta llamada &lt;code&gt;avrdude&lt;&#x2F;code&gt;, y se utiliza de la siguiente manera:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;avrdude -p atmega328p -c arduino -P &amp;lt;puerto del arduino&amp;gt; -U flash:w:&amp;lt;&#x2F;path&#x2F;al&#x2F;archivo.hex&amp;gt;:i
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;que en este caso sería:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;avrdude -p atmega328p -c arduino -P &#x2F;dev&#x2F;ttyACM0 -U flash:w:output.hex:i
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Lo ejecutamos y nos arroja un output parecido a este:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;&amp;gt;&amp;gt; avrdude -p atmega328p -c arduino -P &#x2F;dev&#x2F;ttyACM0 -U flash:w:output.hex:i

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0xle950f (probably m328p)
avrdude: NOTE: &amp;quot;flash&amp;quot; memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file &amp;quot;output.hex&amp;quot;
avrdude: writing flash (178 bytes):

Writing | ################################################## | 100% 0.04s

avrdude: 178 bytes of flash written
avrdude: verifying flash memory against output.hex:

Reading | ################################################## | 100% 0.03s
avrdude: 178 bytes of flash verified
avrdude done. Thank you.
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;El comando nos dice que todo el archivo &lt;code&gt;output.hex&lt;&#x2F;code&gt; se volcó en el Arduino exitosamente.&lt;br &#x2F;&gt;
Y como podemos apreciar, el Led incorporado, se prende:&lt;br &#x2F;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;codigocomentado-imgs.us-southeast-1.linodeobjects.com&#x2F;arduino_led_encendido.png&quot; alt=&quot;Arduino UNO con el led embebido encendido&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Y colocando un 0 en PORTB5:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Rust&quot;&gt;pub extern &amp;quot;C&amp;quot; fn main() {
    unsafe { write_volatile(DDRB, 0b11111111) };
    unsafe { write_volatile(PORTB, 0b00000000) };
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Volvemos, a compilar, convertir el .elf a un .hex y sublo al Arduibo, veremos como el led se apaga.&lt;br &#x2F;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;codigocomentado-imgs.us-southeast-1.linodeobjects.com&#x2F;arduino_led_apagado.png&quot; alt=&quot;Arduino UNO con el led embebido apagado&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Pero yo les prometí al inicio un led parpadeante, no un led fijo, así que hay que hacer que parpadeé.&lt;&#x2F;p&gt;
&lt;p&gt;Como podrías intuirlo algunos, podemos simplemente meter el &lt;code&gt;write_volatile&lt;&#x2F;code&gt; de &lt;code&gt;PORTB&lt;&#x2F;code&gt; que hace prenda y se apague en un loop infinito y ya lo tendríamos, pero esto haría que se prenda y apague a una velocidad demasiada alta como para que fuera apreciable, así que tenemos que colocar un sleep de por medio.&lt;&#x2F;p&gt;
&lt;h2&gt;&lt;code&gt;std::thread\::sleep&lt;&#x2F;code&gt;?&lt;&#x2F;h2&gt;
&lt;p&gt;Por desgracia, el &lt;code&gt;thread::sleep&lt;&#x2F;code&gt; solo se encuentra en la biblioteca estándar, y recordemos que estamos en un proyecto sin eso, así que tendremos que hacerlo de otra forma, como por ejemplo, hacer que el microcontrolador esté ejecutando operaciones vacías durante X ciclos y para hacerlo tendremos que adentrarnos a un nivel muy bajo.&lt;br &#x2F;&gt;
Muchos de los procesadores, incluyendo los AVR, tienen en su set de instrucciones una que no hace nada, y normalmente se llama &amp;quot;nop&amp;quot; o &amp;quot;no operation&amp;quot;.
Esto lo podemos consultar de nueva en la hoja de especificaciones del microcontrolador, en el apartado de &lt;a href=&quot;http:&#x2F;&#x2F;ww1.microchip.com&#x2F;downloads&#x2F;en&#x2F;DeviceDoc&#x2F;ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf#G3.2600220&quot;&gt;&amp;quot;Instruction Set Summary&amp;quot;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mnemonics&lt;&#x2F;th&gt;
&lt;th&gt;Operands&lt;&#x2F;th&gt;
&lt;th&gt;Description&lt;&#x2F;th&gt;
&lt;th&gt;Operation&lt;&#x2F;th&gt;
&lt;th&gt;Flags&lt;&#x2F;th&gt;
&lt;th&gt;#Clocks&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;. . .&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;MCU CONTROL INSTRUCTIONS&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;NOP&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;No Operation&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;None&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;SLEEP&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;Sleep&lt;&#x2F;td&gt;
&lt;td&gt;(see specific descr. for Sleep function)&lt;&#x2F;td&gt;
&lt;td&gt;None&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;. . .&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Como se puede apreciar, efectivamente, el procesador AVR tiene una instrucción llamada &amp;quot;nop&amp;quot;, que no hace nada, no recibe operadores y que gasta un único ciclo del procesador. También hay otra llamada &amp;quot;sleep&amp;quot; que hace lo mismo, pero &amp;quot;nop&amp;quot; puede ser mas extrapolable a otras arquitecturas, así que es el que utilizaremos aquí.&lt;br &#x2F;&gt;
Así que solo tendremos que hacer una llamada con ensamblador a esa instrucción tantas veces como necesitemos y listo, nuestra simulación de &lt;code&gt;thread::sleep&lt;&#x2F;code&gt; quedaría lista.&lt;br &#x2F;&gt;
Ahora, el procesador del Arduino UNO va a 16 MHz o 16&#x27;000&#x27;000 Hz, eso significa que realiza 16 millones de instrucciones por segundo, así que sí queremos dormir al procesador durante un segundo, hay que ejecutar el &amp;quot;nop&amp;quot; 16&#x27;000&#x27;000 de veces, ya que, como vimos en la tabla anterior, solo gasta un ciclo.&lt;br &#x2F;&gt;
En Rust puedes hacer llamadas a ensamblador de manera cruda utilizando el macro &lt;code&gt;asm!&lt;&#x2F;code&gt; que se encuentra dentro de &lt;code&gt;core::arch&lt;&#x2F;code&gt; y recibe como parámetro el nombre de la instrucción.&lt;br &#x2F;&gt;
Para que sea mas sencillo de utilizar, haremos una función que reciba como parámetro el número de segundo que queramos que se &amp;quot;duerma&amp;quot; nuestro CPU y también pondremos en una constante la velocidad del CPU expresado en Hz, que en este caso es 16_000_000.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Rust&quot;&gt;#![feature(asm_experimental_arch)]

use core::arch::asm;

const CPU_SPEED: u32 = 16_000_000;

fn sleep(time: u32) {
    for _ in 0..(CPU_SPEED &#x2F; 10 * time) {
        unsafe { asm!(&amp;quot;nop&amp;quot;) }
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Dejando de lado la feature &lt;code&gt;asm_experimental_arch&lt;&#x2F;code&gt;, que es para habilitar el macro en las arquitectura donde aún es experimental, solo nos tocaría hacer un for que itere desde 0 hasta la velocidad del CPU multiplicado por el número de segundos y dentro del for llamar al &amp;quot;nop&amp;quot; y listo, el problema de la pausa está solucionado.&lt;br &#x2F;&gt;
Pero como pueden ver en el código, existe una división entre 10 en el número total de ciclos del procesador y es que realmente cada iteración del for se lleva mas de un ciclo.&lt;br &#x2F;&gt;
Esto ya es un poco mas bajo nivel, pero haré mi mejor esfuerzo para explicarlo y que se entienda.&lt;br &#x2F;&gt;
Si pegamos ese mismo código en &lt;a href=&quot;https:&#x2F;&#x2F;godbolt.org&#x2F;z&#x2F;Y9ncea8WW&quot;&gt;godbolt&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rust&lt;&#x2F;th&gt;
&lt;th&gt;Ensamblador&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;pre&gt;use std::arch::asm;&lt;br&gt;&lt;br&gt;#[no_mangle]&lt;br&gt;pub fn sleep(time: u32) {&lt;br&gt;    let cycles = 10_000_000 * time;&lt;br&gt;    for _ in 0..cycles {&lt;br&gt;        unsafe { asm!(&amp;quot;nop&amp;quot;) };&lt;br&gt;    }&lt;br&gt;}&lt;&#x2F;pre&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;pre&gt;sleep:&lt;br&gt;    sub     rsp, 40&lt;br&gt;    . . .&lt;br&gt;    jne     .LBB6_2&lt;br&gt;    &lt;br&gt;.LBB6_2:&lt;br&gt;    . . .&lt;br&gt;.LBB6_3:&lt;br&gt;    &lt;b&gt;mov&lt;&#x2F;b&gt;     rax, . . .       ═════════╗&lt;br&gt;    &lt;b&gt;lea&lt;&#x2F;b&gt;     rdi, [rsp + 24]           ║&lt;br&gt;    &lt;b&gt;call&lt;&#x2F;b&gt;    rax                       ║&lt;br&gt;    &lt;b&gt;mov&lt;&#x2F;b&gt;     dword ptr [rsp + 36], edx ║&lt;br&gt;    &lt;b&gt;mov&lt;&#x2F;b&gt;     dword ptr [rsp + 32], eax ║&lt;br&gt;    &lt;b&gt;mov&lt;&#x2F;b&gt;     eax, dword ptr [rsp + 32] ║&lt;br&gt;    &lt;b&gt;cmp&lt;&#x2F;b&gt;     rax, 0                    ║&lt;br&gt;    &lt;b&gt;jne&lt;&#x2F;b&gt;     .LBB6_5                   ║&lt;br&gt;    &lt;b&gt;add&lt;&#x2F;b&gt;     rsp, 40                   ║&lt;br&gt;    ret                               ║&lt;br&gt;.LBB6_5:                              ║&lt;br&gt;    &lt;b&gt;nop&lt;&#x2F;b&gt;                               ║&lt;br&gt;    &lt;b&gt;jmp&lt;&#x2F;b&gt;     .LBB6_3          ═════════╝&lt;&#x2F;pre&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Cada instrucción que ven a la derecha (&amp;quot;mov&amp;quot;, &amp;quot;lea&amp;quot;, &amp;quot;call&amp;quot;, etc...) digamos que se lleva un ciclo del procesador (esto no es del todo cierto, ya que algunas instrucciones puede gastar varios ciclos, pero para que la explicación sea sencilla podemos decir que cada una de ellas es un ciclo gastado del procesador).&lt;br &#x2F;&gt;
Y efectivamente, en la sección &lt;code&gt;LBB6_5&lt;&#x2F;code&gt; podemos ver como nuestro &amp;quot;nop&amp;quot; está ahí, gastando su respectivo ciclo, pero el iterador dentro del for se está llevamos como 10 mas o menos (todas las instrucciones de la sección &lt;code&gt;LBB6_3&lt;&#x2F;code&gt; son las que hacen que el iterador funcione), así que cada ciclo realmente nos está costando ~10+1.&lt;br &#x2F;&gt;
Esa es la razón por la que todo lo dividimos entre 10, no es lo mas elegante pero tampoco estamos programando un reloj suizo, con llegar a un tiempo estimado creo que podemos quedar conformes en este experimento.&lt;br &#x2F;&gt;
Utilizar un &lt;code&gt;while&lt;&#x2F;code&gt; con una variable mutable como contador utiliza mas o menos la misma cantidad de instrucciones, así que támbien utilizo el &lt;code&gt;for&lt;&#x2F;code&gt; por que, a mi parecer, es mas sencillo de leer.&lt;br &#x2F;&gt;
Ahora si, mezclamos todo lo aprendido hemos aprendido y el resultado final es esta belleza:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Rust&quot;&gt;#![feature(asm_experimental_arch)]
#![no_std]
#![no_main]

use core::arch::asm;
use core::ptr::{read_volatile, write_volatile};

const DDRB: *mut u8 = 0x24 as *mut u8;
const PORTB: *mut u8 = 0x25 as *mut u8;

const CPU_SPEED: u32 = 16_000_000;

#[no_mangle]
pub extern &amp;quot;C&amp;quot; fn main() -&amp;gt; ! {
    unsafe { write_volatile(DDRB, 0b11111111) };

    loop {
	    unsafe { write_volatile(PORTB, 0b00100000) };
        sleep(1);
	    unsafe { write_volatile(PORTB, 0b00000000) };
        sleep(1);
    }
}

fn sleep(time: u32) {
    for _ in 0..(CPU_SPEED &#x2F; 10 * time) {
        unsafe { asm!(&amp;quot;nop&amp;quot;) }
    }
}

#[panic_handler]
fn panic(_info: &amp;amp;::core::panic::PanicInfo) -&amp;gt; ! {
    loop {}
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Compilamos, convertimos y subimos y vemos como nuestro led, efectivamente, está parpadeando (mas o menos) cada segundo ✨&lt;code&gt;\._.\ \._.&#x2F; &#x2F;._.&#x2F;&lt;&#x2F;code&gt;✨.&lt;&#x2F;p&gt;
&lt;h2&gt;Mejorando un poco el código&lt;&#x2F;h2&gt;
&lt;p&gt;Peeeero, tener &lt;a href=&quot;https:&#x2F;&#x2F;es.wikipedia.org&#x2F;wiki&#x2F;Hard_code&quot;&gt;hardcodeado&lt;&#x2F;a&gt; el valor de PORTB en escritura puede que no sea lo mejor. Sí solo vamos a hacer que el led parpadeé pues no habría ningún problema, pero sí esto lo queremos extrapolar a una situación un poquito mas dinámica, creo que lo mejor sería leer el valor de PORTB, sea cual sea, y modificarlo para prender o apagar SOLO el bit que nos interesa.&lt;br &#x2F;&gt;
Para ello haremos uso de un par de operadores binario, resultando en este código:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Rust&quot;&gt;... 
loop {
    let mut portb_value = unsafe { read_volatile(PORTB) };
    portb_value = portb_value ^ ( 1 &amp;lt;&amp;lt; 5 );
    unsafe { write_volatile(PORTB, portb_value) };
    sleep(1);
}
...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3&gt;Operador XOR (^)&lt;&#x2F;h3&gt;
&lt;p&gt;Comenzaré explicando el operador &amp;quot;^&amp;quot;, támbien llamado &amp;quot;XOR&amp;quot;.&lt;br &#x2F;&gt;
XOR es un operador booleano que compara 2 valores binarios y devuelve 1 sí solo uno de ellos es 1, de lo contrario, devuelve 0.&lt;br &#x2F;&gt;
Otra forma de verlo es que XOR es un &amp;quot;detector de diferencias&amp;quot;, ya que devuelve 0 si los 2 valores que estás comparando son iguales y 1 si son diferentes.   Creo que consultando su tabla de verdades es mas sencillo de ver:&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Entrada A&lt;&#x2F;th&gt;
&lt;th&gt;Entrada B&lt;&#x2F;th&gt;
&lt;th&gt;Salida&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Aquí se puede apreciar mejor lo que comentaba, tanto en el primero como en el ultimo caso, los valores son iguales, por que XOR regresa 0, pero en el 2do y en el 3ro son distintos, por lo cual va a regresar 1.&lt;br &#x2F;&gt;
Ahora, sí se aplica el operador XOR a un valor entero, la operación se haría una vez por cada bit del número. Pongamos de ejemplo 2 valores u8:&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;7&lt;&#x2F;th&gt;
&lt;th&gt;6&lt;&#x2F;th&gt;
&lt;th&gt;5&lt;&#x2F;th&gt;
&lt;th&gt;4&lt;&#x2F;th&gt;
&lt;th&gt;3&lt;&#x2F;th&gt;
&lt;th&gt;2&lt;&#x2F;th&gt;
&lt;th&gt;1&lt;&#x2F;th&gt;
&lt;th&gt;0&lt;&#x2F;th&gt;
&lt;th&gt;Valor&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;185&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;^&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;157&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;&#x2F;td&gt;
&lt;td&gt;=&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;0&lt;&#x2F;td&gt;
&lt;td&gt;36&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Gracias a esta cualidad, resulta muy útil el operador XOR para invertir un bit en específico de un número sin modificar alterar nada mas.&lt;br &#x2F;&gt;
Supongamos que tenemos el número (0b11110000 = 140) y queremos invertir su 6to bit, lo que podemos es aplicarle una operación XOR contra otro que tenga todos sus bits en 0, excepto el que queremos invertir (0b00100000 = 32).&lt;br &#x2F;&gt;
La primera vez que lo hagamos veremos como, efectivamente, el bit se invierte:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;0b11110000 ^ 0b00100000 = 0b11010000 = 208
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Lo interesante, viene ahora. Ya que, al volver a hacer la misma operación XOR, pero ahora aplicandoselo al resultado que nos arrojó veremos como el bit vuelve a ser 1:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;0b11010000 ^ 0b00100000 = 0b11110000 = 240
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3&gt;Operadores de desplazamiento (&amp;lt;&amp;lt; y &amp;gt;&amp;gt;)&lt;&#x2F;h3&gt;
&lt;p&gt;Ahora que ya sabemos invertir un bit en un valor numérico, no les parece como muy de hueva estar escribiendo 0b00100000 en cada operación que hagamos?&lt;br &#x2F;&gt;
Pues no se preocupen, ya que existe otro operador binario que nos puede hacer la vida un poquito mas sencilla, funciona de la siguiente manera:&lt;br &#x2F;&gt;
Le damos un número X y una cantidad de 0&#x27;s que queramos que se agreguen a ese X a la izquierda y por arte de magia, lo recorre. Con un ejemplo creo que se entiende mejor.&lt;br &#x2F;&gt;
Supongamos que tenemos el valor 0b101 y queremos desplazarlo 4 bits a la izquierda para que convierta en 0b&lt;strong&gt;101&lt;&#x2F;strong&gt;0000, pues simplemente tendríamos que escribir:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;0b101 &amp;lt;&amp;lt; 4 = 0b1010000 = 80
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Sí queremos que se desplace a la derecha es igual unicamente intercambiando el operador &lt;code&gt;&amp;lt;&amp;lt;&lt;&#x2F;code&gt; por &lt;code&gt;&amp;gt;&amp;gt;&lt;&#x2F;code&gt;, y en lugar de agregar 0&#x27;s, va descartando bits:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;0b1011111 &amp;gt;&amp;gt; 3 = 0b1011 = 11
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Viendo de vuelta el código ahora si podemos ver que ocurre:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Rust&quot;&gt;... 
loop {
    let mut portb_value = unsafe { read_volatile(PORTB) };
    portb_value = portb_value ^ ( 1 &amp;lt;&amp;lt; 5 );
    unsafe { write_volatile(PORTB, portb_value) };
    sleep(1);
}
...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Cuando comienza el programa PORTB está en 0&#x27;s, así que leemos el valor utilizando la función &lt;code&gt;read_volatile&lt;&#x2F;code&gt;, a ese número le aplicamos una operación XOR para prender unicamente el bit 5 y lo escribimos de nuevo en el registro haciendo uso de &lt;code&gt;write_volatile&lt;&#x2F;code&gt; y dormimos 1 segundo.&lt;br &#x2F;&gt;
En el siguiente ciclo, ya con PORTB5 encendido hacemos lo mismo, volvemos a leer su valor, aplicamos de vuelta XOR para ahora apagar el bit 5, escribimos dicho valor en PORTB y volvemos a dormir por un segundo y así hasta el infinito.&lt;&#x2F;p&gt;
&lt;p&gt;YYYYY LISTO!!!, logramos hacer que un led parpadeara escribiendo y leyendo código ensamblador, midiendo tiempos en el CPU, modificando registros, etc... pues saben que, nada de eso es útil.&lt;br &#x2F;&gt;
Y es que el ecosistema para sistema embebidos en Rust nos permite hacer todo esto de una manera mas sofisticada.&lt;br &#x2F;&gt;
No me malentiendan, lo que aprendimos aquí es bastante útil, sobretodo para entender como funcionan las tripas de nuestro querido Arduino, pero programar para embebidos en Rust no suele ser así.&lt;br &#x2F;&gt;
Imagina que quieres hacer una biblioteca para, por ejemplo, controlar un display de 7 segmentos. Puedes programarla de la forma que acabamos de ver, pero esto es poco portable para otras placas o arquitecturas, ya que no todas tienen los registros en los mismo lugares o no todos tienen un CPU con la misma velocidad, así que nuestra pobre biblioteca solo serviría para las Arduino UNO.&lt;br &#x2F;&gt;
Pero que pasa si les digo que la comunidad de Rust hace uso de una técnica para que todas las bibliotecas sirvan para todas las placas que existen y las que están por existir, practicamente sin que la persona que mantenga la biblioteca haga ningún cambio.&lt;br &#x2F;&gt;
Estimado público, dejenme presentarle a las ✨HAL✨.&lt;&#x2F;p&gt;
&lt;h2&gt;Presentando a las HAL&lt;&#x2F;h2&gt;
&lt;p&gt;Las HAL (por sus siglas en inglés: Capa de abstracción del Hardware) es una técnica para hacer bibliotecas de manera genérica y completamente portables a otras plataformas.&lt;br &#x2F;&gt;
Funciona así:&lt;br &#x2F;&gt;
Existe un crate llamado &lt;code&gt;embedded_hal&lt;&#x2F;code&gt;🤖, que contiene muchísimos Traits para todo lo que una placa suele tener (Pines digitales, analogícos, SPI&#x27;s, UART&#x27;s, I2C, etc...), pero no contiene ninguna implementación de ellos, solo son Traits que indican un estándar de como deben funcionar, por ejemplo:&lt;br &#x2F;&gt;
Los pines digitales deben tener un método llamado &lt;code&gt;set_high&lt;&#x2F;code&gt; que seteé ese pin en voltaje alto, y otro llamado &lt;code&gt;set_low&lt;&#x2F;code&gt; para lo contrario. el cómo se implemente eso en cada placa a ese crate no le importa, solo marca un éstandar.&lt;br &#x2F;&gt;
La ventaja de tener eso es que tu, como mantenedor de una biblioteca, puedes hacer tus funciones utilizando los métodos genéricos de &lt;code&gt;embedded_hal&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Si estás escribiendo una biblioteca📜 para controlar un display de 7 segmentos, necesitas 7 Pines digitales, uno para cada led individual.&lt;br &#x2F;&gt;
Entonces puedes crear un Struct que reciba esos Pines que implementen el Trait &lt;code&gt;OutputPin&lt;&#x2F;code&gt; de &lt;code&gt;embedded_hal&lt;&#x2F;code&gt; y utilizar los métodos &lt;code&gt;set_low&lt;&#x2F;code&gt;, &lt;code&gt;set_high&lt;&#x2F;code&gt; o &lt;code&gt;toggle&lt;&#x2F;code&gt; dependiendo de que valor quieres que tome, todo eso sin saber siquiera en que arquitectura o placa va a ejecutarse, tu solo recibes Pines y los pones en high o low según lo requiera tu biblioteca.&lt;&#x2F;p&gt;
&lt;p&gt;Otra de las partes es el &lt;code&gt;arduino_hal&lt;&#x2F;code&gt;⚡, la implementación como tal de los Traits de &lt;code&gt;embedded_hal&lt;&#x2F;code&gt; para el Arduino UNO, ahí si es donde se podría aplicar lo aprendido en aquí. Así, la persona que implemente el &lt;code&gt;arduino_hal&lt;&#x2F;code&gt; es la encargada de que cuando se mande llamar al método &lt;code&gt;set_high&lt;&#x2F;code&gt; o &lt;code&gt;set_low&lt;&#x2F;code&gt; del Pin, este realice las asignaciones en los registros PORTXY y DDRXY para prender o apagar ese Pin.&lt;&#x2F;p&gt;
&lt;p&gt;Así por último están las persona que mezclará todo para hacer su firmware💻, solo tiene que importar la biblioteca que quiera utilizar junto con el HAL específico para su placa y comenzar a utilizarla de una forma mucho mas sencilla.&lt;&#x2F;p&gt;
&lt;p&gt;La interacción entre las distintas partes se vería algo así:&lt;&#x2F;p&gt;
&lt;pre&gt;
embedded_hal &lt;═════════════ Biblioteca
     🤖                         📜
     ^                          ^
     ║                          ║
     ║                          ║
     ║                          ║
     ║                          ║
     ║                          ║
 arduino_hal &lt;══════════════ Firmware
     ⚡                         💻
&lt;&#x2F;pre&gt;
&lt;p&gt;Una vez explicado esto, el mísmo código para hacer que el led parpadeante quedaría algo así utilizando un HAL llamado &lt;code&gt;avr-hal&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Rust&quot;&gt;#![no_std]
#![no_main]

use arduino_hal::delay_ms;
use panic_halt as _;

#[arduino_hal::entry]
fn main() -&amp;gt; ! {
    let peripherals = arduino_hal::Peripherals::take().unwrap();
    let pins = arduino_hal::pins!(peripherals);

    let mut led = pins.d13.into_output();
    led.set_high();

    loop {
        led.toggle();
        delay_ms(1000);
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Así ya se ve mucho mas sencillo, no? solo importamos el Pin asociado al led que queremos modificar, en este caso el B5 (este HAL no nombra los pines siguiendo la nomenclatura de la hoja de especificaciones, en su lugar hace uso de la nomenclatura impresa en la PCB, que para el led embebido sería el digital 13 o d13), támbien el HAL ya tiene funciones para hacer los delays, así que solo seteamos el pin como output y en el loop colocamos un &lt;code&gt;toggle&lt;&#x2F;code&gt; y un delay y listo :), sin ensamblador, sin saber de registros, sin nosotros utilizar &lt;code&gt;unsafe&lt;&#x2F;code&gt;, desde luego un API mucho mas amigable.&lt;&#x2F;p&gt;
&lt;h2&gt;FIN&lt;&#x2F;h2&gt;
&lt;p&gt;Al final todo el código de este escrito lo puedes encontra en mi &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;kirbylife&#x2F;arduino_blinkrs&quot;&gt;GitLab&lt;&#x2F;a&gt;, en el cual habrá 2 ramas, una llamada &amp;quot;low_level&amp;quot; que contiene el ejemplo manipulando los registros directamente y otra llamada &amp;quot;using_hal&amp;quot; con el ejemplo utilizando la HAL.&lt;br &#x2F;&gt;
Támbien pueden encontrar revisar la información de este mismo texto pero en formato charla en el canal de YouTube &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=JXPYTvYnk_U&quot;&gt;Software Guru&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Espero les haya gustado y hayan aprendido lo básico de como es que funciona un microcontrolador a un nivel un poco mas bajo y como implementar ese conocimiento en Rust :), hasta la próxima.&lt;&#x2F;p&gt;
&lt;h3&gt;Fuentes y enlaces de interés&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Hermoso asciiart del Arduino Uno &lt;a href=&quot;http:&#x2F;&#x2F;busyducks.com&#x2F;wp_4_1&#x2F;2015&#x2F;11&#x2F;16&#x2F;ascii-art-arduino-pinouts&#x2F;&quot;&gt;http:&#x2F;&#x2F;busyducks.com&#x2F;wp_4_1&#x2F;2015&#x2F;11&#x2F;16&#x2F;ascii-art-arduino-pinouts&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;ww1.microchip.com&#x2F;downloads&#x2F;en&#x2F;DeviceDoc&#x2F;ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf&quot;&gt;http:&#x2F;&#x2F;ww1.microchip.com&#x2F;downloads&#x2F;en&#x2F;DeviceDoc&#x2F;ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;items&#x2F;external-blocks.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;items&#x2F;external-blocks.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;os.phil-opp.com&#x2F;freestanding-rust-binary&#x2F;#panic-implementation&quot;&gt;https:&#x2F;&#x2F;os.phil-opp.com&#x2F;freestanding-rust-binary&#x2F;#panic-implementation&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;os.phil-opp.com&#x2F;freestanding-rust-binary&#x2F;#the-eh-personality-language-item&quot;&gt;https:&#x2F;&#x2F;os.phil-opp.com&#x2F;freestanding-rust-binary&#x2F;#the-eh-personality-language-item&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.rust-embedded.org&#x2F;book&#x2F;start&#x2F;panicking.html&quot;&gt;https:&#x2F;&#x2F;docs.rust-embedded.org&#x2F;book&#x2F;start&#x2F;panicking.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.rust-embedded.org&#x2F;embedonomicon&#x2F;custom-target.html&quot;&gt;https:&#x2F;&#x2F;docs.rust-embedded.org&#x2F;embedonomicon&#x2F;custom-target.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;avr-rust&#x2F;ruduino&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;avr-rust&#x2F;ruduino&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;arduino.stackexchange.com&#x2F;questions&#x2F;62488&#x2F;elf-and-hex-equivalency-code-eeprom-fuses-and-lock-bits-all-in-one&quot;&gt;https:&#x2F;&#x2F;arduino.stackexchange.com&#x2F;questions&#x2F;62488&#x2F;elf-and-hex-equivalency-code-eeprom-fuses-and-lock-bits-all-in-one&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
        <title>Crear función que retorne un array con N valores por defecto en Rust - Const Generics</title>
        <pubDate>Wed, 26 May 2021 02:28:43 GMT</pubDate>
        <link>https://blog.kirbylife.dev/post/crear-funcion-que-retorne-un-array-con-n-valores-por-defecto-en-rust-const-generics-7</link>
        <guid isPermaLink="true">https://blog.kirbylife.dev/post/7</guid>
        <description>&lt;p&gt;Hace unos días una persona en un grupo de Telegram preguntó el porqué solo se podían hacer arrays con valores por defecto de máximo 32 de longitud y la respuesta a ese predicamento realmente es un tanto absurda pero desde luego es muy interesante y radica en que... *redoble de tambores* ... las funciones para generar los arrays se debían escribir a mano, y de manera arbitraria decidieron que iban a escribir funciones para generar arrays de hasta 32 elementos.&lt;br &#x2F;&gt;
Ósea que tienen escritas las funciones mas o menos así [&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;default&#x2F;trait.Default.html#impl-Default-69&quot;&gt;1&lt;&#x2F;a&gt;]:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;impl&amp;lt;T&amp;gt; Default for [T; 0] where T: Default {
                        ^
                        ╚═══════ Array de longitud 0
    fn default() -&amp;gt; Self {  
        []
        ^
        ╚═══════ Retorna un array vacío
    }
}

impl&amp;lt;T&amp;gt; Default for [T; 1] where T: Default {
                        ^
                        ╚═══════ Array de longitud 1
    fn default() -&amp;gt; Self {
        [T::default()]
         ^ ^ ^ ^ ^ ^
         ╚═╩═╩═╩═╩═╩═════ Retorna un array con un elemento
    }
}

...
impl&amp;lt;T&amp;gt; Default for [T; 32] where T: Default {
                        ^
                        ╚═══════ Array de longitud 32
    fn default() -&amp;gt; Self {
        [T::default(), T::default(), T::default(), T::default(), ...]
         ^ ^ ^ ^ ^ ^
         ╚═╩═╩═╩═╩═╩═════ Retorna un array con 32 elementos
                          ESCRITOS A MANO
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;y solo había de dos sopas, o ponían a una persona becada a implementar manualmente el resto de casos desde 33 hasta el máximo de &lt;code&gt;usize&lt;&#x2F;code&gt; o encontraban una forma de resolverlo de una manera inteligente, y el equipo de Rust se puso a hacer lo 2do y agregaron en esta nueva release una cosa maravillosa llamada &lt;code&gt;const generics&lt;&#x2F;code&gt;.&lt;br &#x2F;&gt;
Los &lt;code&gt;const generics&lt;&#x2F;code&gt;, como su nombre lo deja entrever, es la posibilidad de utilizar valores constantes como datos genéricos dentro de una función y poder colocar esos constantes genéricos en el lugar en el que debe ir un valor constante.&lt;br &#x2F;&gt;
Explicado de esta manera puede quedar mas o menos confuso, pero veamos un ejemplo sin &lt;code&gt;const generics&lt;&#x2F;code&gt; y uno con para ver la enorme maravilla que esto supone.&lt;&#x2F;p&gt;
&lt;h2&gt;Entendiendo los Const Generics&lt;&#x2F;h2&gt;
&lt;p&gt;El ejemplo será el siguiente: Se tiene que hacer una función que retorne un array vacío de tipos unidad (suena feo en español &amp;quot;unit type&amp;quot;, ya me disculparán), la longitud de ese array puede ser cualquier valor de 0 hasta el máximo de &lt;code&gt;usize&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3&gt;Sin Const Generics&lt;&#x2F;h3&gt;
&lt;pre&gt;&lt;code&gt;trait Foo {
    fn zeros() -&amp;gt; Self;
}
impl Foo for [(); 0] {
    fn zeros() -&amp;gt; Self { [] }
}
impl Foo for [(); 1] {
    fn zeros() -&amp;gt; Self { [()] }
}
... &#x2F;&#x2F; Aquí irían las implementaciones para cada longitud de array
impl Foo for [(); usize::MAX] {
    fn zeros() -&amp;gt; Self { [(); usize::MAX] }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2018&amp;amp;gist=11044917040771b0957eb121aa027ab1&quot;&gt;&lt;code&gt;playground&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
Como se puede apreciar, se debe crear un trait que contenga la función que nos interesa que haga la acción y que retorne un &lt;code&gt;Self&lt;&#x2F;code&gt; y después implementar cada caso a mano y después poderla mandarla a llamar así:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;let x: [(); 10] = Foo::zeros();
println!(&amp;quot;{:?}&amp;quot;, x);
&#x2F;&#x2F; [(), (), (), (), (), (), (), (), (), ()]
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como pueden intuir este método deja muchas cosas que desear, principalmente lo de estar implementando a mano cada uno de los casos para los arrays... ahora les mostraré lo sencillo que es implementarlo gracias a los Const Generics.&lt;&#x2F;p&gt;
&lt;h3&gt;Con Const Generics&lt;&#x2F;h3&gt;
&lt;pre&gt;&lt;code&gt;fn zeros&amp;lt;const N: usize&amp;gt;() -&amp;gt; [(); N] {
         ^ ^ ^ ^  ^ ^ ^            ^
         ║ ║ ║ ║  ║ ║ ║            ╚═ Se puede colocar el const generic en el lugar de la longitud
         ║ ║ ║ ║  ║ ║ ║               (ahí solo pueden ir valores constantes).
         ║ ║ ║ ║  ╚═╩═╩═ El tipo del const generic.
         ║ ║ ║ ╚═ Nombre del const generic.
         ╚═╩═╩═ Palabra reservada para indicar que será un valor const generic.
    [(); N]
         ^
         ╚═ Se crea el array de valores unidad y se coloca el const generic en el tamaño del array.
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2018&amp;amp;gist=344dc12610471a108c3ba2a09fd1b400&quot;&gt;&lt;code&gt;playground&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
Como se ve, el ejemplo con const generic queda mucho mas limpio, se ve de manera clara lo que hace y no se necesita un trait que contenta a la función, puede estar suelta y ser independiente. Y para utilizarla es idéntico a la versión anterior:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;let x: [(); 10] = zeros();
println!(&amp;quot;{:?}&amp;quot;, x);
&#x2F;&#x2F; [(), (), (), (), (), (), (), (), (), ()]

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2&gt;Generando arrays con valores por defecto&lt;&#x2F;h2&gt;
&lt;p&gt;Tomando en cuenta lo aprendido anteriormente podríamos pensar que generar un array con valores por defecto puede ser tan simple como recibir un genérico que tenga implementado el trait &lt;code&gt;Default&lt;&#x2F;code&gt; y después generar el array tomando su valor por defecto y listo, pero veamos que no es tan sencillo:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;fn fill_default&amp;lt;T: Default, const N: usize&amp;gt;() -&amp;gt; [T; N] {
    [T::default(); N]
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2018&amp;amp;gist=083449942941218e8f330b88371a99da&quot;&gt;&lt;code&gt;playground (no compila)&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
Ya que si es implementado así y lo si intentamos compilar nos saldrá un error parecido a este:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;error[E0277]: the trait bound `T: Copy` is not satisfied
 --&amp;gt; src&#x2F;main.rs:2:5
  |
2 |     [T::default(); N]
  |     ^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T`
  |
  = note: the `Copy` trait is required because the repeated element will be copied
help: consider further restricting this bound
  |
1 | fn fill_default&amp;lt;T: Default + std::marker::Copy, const N: usize&amp;gt;() -&amp;gt; [T; N] {
  |                            ^^^^^^^^^^^^^^^^^^^

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Debido a que la manera de generar arrays con la sintaxis &lt;code&gt;[Valor; longitud]&lt;&#x2F;code&gt; necesita que el valor implemente el trait &lt;code&gt;Copy&lt;&#x2F;code&gt;, para que se genere todo de golpe, y el reto que se propone en esta publicación es necesitar unicamente que T implemente a Default y nada más, así que tendremos que encontrar otra forma de realizarlo.&lt;br &#x2F;&gt;
Luego de darle muchas vueltas decidí atacar el problema de la siguiente manera:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Hacer un iterador para que cada valor se vaya generando uno a uno y no sea necesario el trait &lt;code&gt;Copy&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Generar un &lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; gracias a ese iterador para después:&lt;&#x2F;li&gt;
&lt;li&gt;Utilizando el trait &lt;code&gt;TryInto&lt;&#x2F;code&gt; convertir el &lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; a un array.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Y el resultado fue el siguiente:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;use std::convert::TryInto;
                  ^ ^ ^ ^
                  ╚═╩═╩═╩═ Nos traemos el trait TryInto

fn fill_default&amp;lt;T: Default, const N: usize&amp;gt;() -&amp;gt; [T; N] {
    std::iter::repeat_with(|| T::default())
        .take(N)             &amp;lt;═╦═ iter
        .collect::&amp;lt;Vec&amp;lt;T&amp;gt;&amp;gt;() &amp;lt;═╬═ Vec&amp;lt;T&amp;gt;
        .try_into()          &amp;lt;═╬═ Result&amp;lt;[T; N], _&amp;gt;
        .map_err(|_| ())     &amp;lt;═╬═ Result&amp;lt;[T; N], ()&amp;gt;
        .unwrap()            &amp;lt;═╩═ [T; N]
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2018&amp;amp;gist=c771cf7f1b8f81acdadf4589f837eec9&quot;&gt;&lt;code&gt;Playground&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
Y ¡¡listo!! despues de pasear un poco los datos (que sí iter, luego Vec, luego Result, etc...) ya tenemos nuestra función que genera un array de tamaño N con valores por defecto sin depender de otro trait como Copy o Debug.&lt;&#x2F;p&gt;
&lt;h2&gt;Curiosidades encontradas&lt;&#x2F;h2&gt;
&lt;p&gt;Aunque el problema ya está resuelto no quiero dejar pasar un par de curiosidades con respecto a este problema ya que, como pueden ver en el pedazo de código anterior nos encontramos con una linea particularmente curiosa... y sí, me refiero a esta:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;.map_err(|_| ())
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ya que en un principio podría parecer que no es necesaria por que solo está generando un Result de otro Result, pero si nos deshacemos de ella nos encontraremos con este curioso error:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;error[E0277]: `T` doesn&#x27;t implement `Debug`
 --&amp;gt; src&#x2F;main.rs:8:10
  |
8 |         .unwrap()
  |          ^^^^^^ `T` cannot be formatted using `{:?}` because it doesn&#x27;t implement `Debug`
  |
  = note: required because of the requirements on the impl of `Debug` for `Vec&amp;lt;T&amp;gt;`
help: consider further restricting this bound
  |
3 | fn fill_default&amp;lt;T: Default + std::fmt::Debug, const N: usize&amp;gt;() -&amp;gt; [T; N] {
  |                            ^^^^^^^^^^^^^^^^^
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Así es, el compilar nos está pidiendo que T implemente también el trait &lt;code&gt;Debug&lt;&#x2F;code&gt; lo cual es bastante peculiar (o me lo pareció en un principio) pero después de analizarlo me hizo todo el sentido del mundo ya que, si llegase a fallar la conversión de &lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; a &lt;code&gt;[T; N]&lt;&#x2F;code&gt; el compilador deberá saber como mostrar a T y eso se logra con el trait &lt;code&gt;Debug&lt;&#x2F;code&gt;. Por lo tanto, la linea &lt;code&gt;.map_err(|_| ())&lt;&#x2F;code&gt; nos está ayudando a disfrazar el error reemplazandolo por una &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;primitive.unit.html&quot;&gt;unidad&lt;&#x2F;a&gt; (que si tiene implementado el trait Debug).&lt;br &#x2F;&gt;
Otra de las curiosidades es que, sí estás utlizando la versión noctura del compilador, podremos utilizar una función que aún no es estable del todo llamada &amp;quot;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;issues&#x2F;75243&quot;&gt;array map&lt;&#x2F;a&gt;&amp;quot; y con eso sí que reducimos bastante el código sin comprometer la legibilidad del mismo ya que bastará con generar un array del tamaño que queramos y al que después le mapearemos la función que le colocará los valores deseados, algo mas o menos así:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;#![feature(array_map)]

fn fill_default&amp;lt;T: Default, const N: usize&amp;gt;() -&amp;gt; [T; N] {
    [0; N].map(|_| T::default())
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=nightly&amp;amp;mode=debug&amp;amp;edition=2018&amp;amp;gist=cc37fb1d9bdf8023819144d28ed0cfb8&quot;&gt;&lt;code&gt;Playground (compila con la versión nocturna)&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
Y hace basicamente lo mismo que se trató en la parte principal de este texto pero de una manera directa y sin tanta parafernalia.&lt;&#x2F;p&gt;
&lt;h4&gt;Esta publicación no sería posible sin la inestimable ayuda de &lt;a href=&quot;https:&#x2F;&#x2F;categulario.xyz&#x2F;&quot;&gt;Categulario&lt;&#x2F;a&gt;.&lt;&#x2F;h4&gt;
&lt;h2&gt;Fuentes&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;default&#x2F;trait.Default.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;default&#x2F;trait.Default.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;issues&#x2F;75243&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;issues&#x2F;75243&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.joshmcguigan.com&#x2F;blog&#x2F;array-initialization-rust&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.joshmcguigan.com&#x2F;blog&#x2F;array-initialization-rust&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;67963788&#x2F;why-tryinto-needs-copy-trait-in-t&#x2F;67964077?noredirect=1#comment120159571_67964077&quot;&gt;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;67963788&#x2F;why-tryinto-needs-copy-trait-in-t&#x2F;67964077?noredirect=1#comment120159571_67964077&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;(manera utilizando &lt;code&gt;unsafe&lt;&#x2F;code&gt; de hacerlo, es una forma fea y por eso no la mostré arriba) &lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=nightly&amp;amp;mode=debug&amp;amp;edition=2018&amp;amp;gist=48f7b644bb823912aa4a904e3189a0bb&quot;&gt;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=nightly&amp;amp;mode=debug&amp;amp;edition=2018&amp;amp;gist=48f7b644bb823912aa4a904e3189a0bb&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</description>
      </item>
      <item>
        <title>Optimizando código en Python</title>
        <pubDate>Sat, 25 Jan 2020 04:55:20 GMT</pubDate>
        <link>https://blog.kirbylife.dev/post/optimizando-codigo-en-python-6</link>
        <guid isPermaLink="true">https://blog.kirbylife.dev/post/6</guid>
        <description>&lt;p&gt;No es una sorpresa para nadie que Python no es de los lenguajes más rápidos en cuanto a operaciones por segundo se refiere. Parte de la culpa es del lenguaje, ya que al ser interpretado no puede tener un rendimiento igual a un lenguaje compilado, PEEEEEEERO otra gran parte de la culpa es de las personas que programan en dicho lenguaje. Esta entrada no se tratará directamente de como optimizar el código en general (eso podría ser en otra ocasión), sino que se tratará de ciertas cuestiones a tener en cuenta al momento de estar programando &lt;strong&gt;exclusivamente&lt;&#x2F;strong&gt; en Python. Tampoco se tocará la concurrencia y como podemos mejorar el rendimiento de nuestro código con ella.&lt;&#x2F;p&gt;
&lt;h3&gt;* Anotaciones antes de comenzar&lt;&#x2F;h3&gt;
&lt;p&gt;Para medir el tiempo se utilizará el &lt;a href=&quot;https:&#x2F;&#x2F;git.kirbylife.dev&#x2F;kirbylife&#x2F;snippets&#x2F;src&#x2F;branch&#x2F;main&#x2F;Python-decorator-to-measure-execution-time.md&quot;&gt;siguiente decorador&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;from cProfile import Profile

def explain_time(f):
     def wrapper(*args, **kwargs):
         profile = Profile()
         profile.enable() #  Comenzar conteo de operaciones
         output = f(*args, **kwargs) #  Ejecutar función
         profile.disable() #  Terminar conteo de operaciones
         profile.print_stats()
         return output
     return wrapper
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;No entraré a explicarlo a profundidad, solo diré que es una manera mucho más detallada de saber cuanto tiempo le tomó a una función ejecutarse fijándose en cuanto tarda cada operación individualmente.&lt;&#x2F;p&gt;
&lt;h2&gt;El indexado es más rápido que el método &lt;code&gt;get&lt;&#x2F;code&gt; en los diccionarios&lt;&#x2F;h2&gt;
&lt;p&gt;Hay 2 maneras de acceder a la información que contiene un diccionario, indexando el valor utilizando la sintaxis de corchetes &lt;code&gt;[]&lt;&#x2F;code&gt; y utilizando el método &lt;code&gt;get&lt;&#x2F;code&gt;. Ambos ofrecen ventajas y desventajas.&lt;br &#x2F;&gt;
La ventaja de utilizar &lt;code&gt;get&lt;&#x2F;code&gt; es que el código se vuelve &amp;quot;tolerante&amp;quot; a fallas ya que, si no se encuentra registrado un valor con la llave que le hemos pasado, este no arrojará una excepción, sino que simplemente nos devolverá un &lt;code&gt;None&lt;&#x2F;code&gt; por defecto, u otro valor que le pasemos como segundo parámetros.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;d = { &amp;quot;a&amp;quot;: -1 }

&amp;gt;&amp;gt;&amp;gt; d.get(&amp;quot;a&amp;quot;)
-1

&amp;gt;&amp;gt;&amp;gt; d.get(&amp;quot;b&amp;quot;)
      ^ ^
      ╚═╩═ Usando el método &amp;quot;get&amp;quot;, si la llave no se encuentra en el diccionario
           Retornará simplemente un &amp;quot;None&amp;quot;
╔═╦══════════════════════════════════╩═╝
v v
None

&amp;gt;&amp;gt;&amp;gt; d[&amp;quot;a&amp;quot;]
-1

&amp;gt;&amp;gt;&amp;gt; d[&amp;quot;b&amp;quot;]
      ^ ^
      ╚═╩═ Usando un indexado tradicional, si la llave no se encuentra
           Arrojará una excepción
╔═╦═════════════════════╩═╩═╩═╩═╝
v v
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
&amp;lt;ipython-input-212-07abf0799e3f&amp;gt; in &amp;lt;module&amp;gt;
----&amp;gt; 1 d[&amp;quot;b&amp;quot;]

KeyError: &#x27;b&#x27;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Pero este post no se trata sobre código tolerante a fallas, sino que se trata de código rápido; Y en rapidez, el indexado tradicional le gana  al método &lt;code&gt;get&lt;&#x2F;code&gt;.&lt;br &#x2F;&gt;
Y es que, si pones un indexado a competir contra &lt;code&gt;get&lt;&#x2F;code&gt;, estos son los resultados:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# Prueba utilizando el indexado tradicional
@explain_time
def test_index():
    d = { &amp;quot;a&amp;quot;: -1 }
    for _ in range(100000):
        d[&amp;quot;a&amp;quot;]

&amp;gt;&amp;gt;&amp;gt; test_index():
         2 function calls in 0.007 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.007    0.007    0.007    0.007 &amp;lt;ipython-input-250-319a0e1f128d&amp;gt;:2(test_index)
        1    0.000    0.000    0.000    0.000 {method &#x27;disable&#x27; of &#x27;_lsprof.Profiler&#x27; objects}

# Prueba utilizando el método &amp;quot;get&amp;quot;
@explain_time
def test_get():
    d = { &amp;quot;a&amp;quot;: -1 }
    for _ in range(100000):
        d.get(&amp;quot;a&amp;quot;)

&amp;gt;&amp;gt;&amp;gt; test_get()
         100002 function calls in 0.023 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.012    0.012    0.023    0.023 &amp;lt;ipython-input-252-96210b127bf1&amp;gt;:1(test_get)
        1    0.000    0.000    0.000    0.000 {method &#x27;disable&#x27; of &#x27;_lsprof.Profiler&#x27; objects}
   100000    0.011    0.000    0.011    0.000 {method &#x27;get&#x27; of &#x27;dict&#x27; objects}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;la diferencia es clara, ¿no?, el indexado tarda de 0.007 segundos haciendo 100000 búsquedas, a diferencia de &lt;code&gt;get&lt;&#x2F;code&gt; que tarda 0.023 segundos en hacer la misma cantidad de búsquedas.&lt;br &#x2F;&gt;
Aunque la diferencia sea clara, soy una persona de ciencia y eso me obliga a repetir la tarea varias veces para ver si el tiempo promedio nos sorprende.&lt;br &#x2F;&gt;
Y los resultados para sorpresa de nadie son los siguientes:&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;N°&lt;&#x2F;th&gt;
&lt;th&gt;index&lt;&#x2F;th&gt;
&lt;th&gt;get&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;1&lt;&#x2F;td&gt;
&lt;td&gt;0.008&lt;&#x2F;td&gt;
&lt;td&gt;0.027&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;2&lt;&#x2F;td&gt;
&lt;td&gt;0.007&lt;&#x2F;td&gt;
&lt;td&gt;0.027&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;3&lt;&#x2F;td&gt;
&lt;td&gt;0.006&lt;&#x2F;td&gt;
&lt;td&gt;0.024&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;4&lt;&#x2F;td&gt;
&lt;td&gt;0.004&lt;&#x2F;td&gt;
&lt;td&gt;0.023&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;5&lt;&#x2F;td&gt;
&lt;td&gt;0.011&lt;&#x2F;td&gt;
&lt;td&gt;0.028&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;6&lt;&#x2F;td&gt;
&lt;td&gt;0.009&lt;&#x2F;td&gt;
&lt;td&gt;0.032&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;7&lt;&#x2F;td&gt;
&lt;td&gt;0.009&lt;&#x2F;td&gt;
&lt;td&gt;0.028&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;8&lt;&#x2F;td&gt;
&lt;td&gt;0.007&lt;&#x2F;td&gt;
&lt;td&gt;0.027&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;9&lt;&#x2F;td&gt;
&lt;td&gt;0.009&lt;&#x2F;td&gt;
&lt;td&gt;0.028&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;10&lt;&#x2F;td&gt;
&lt;td&gt;0.008&lt;&#x2F;td&gt;
&lt;td&gt;0.031&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;promedio&lt;&#x2F;td&gt;
&lt;td&gt;0.007&lt;&#x2F;td&gt;
&lt;td&gt;0.027&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;h2&gt;Los &lt;code&gt;print&lt;&#x2F;code&gt; son el infierno del rendimiento&lt;&#x2F;h2&gt;
&lt;p&gt;Cada vez que nosotros interactuamos con un buffer, este consume cierto tiempo de CPU, incluyendo al buffer de estándar de salida (&lt;code&gt;stdout&lt;&#x2F;code&gt; para los amigos). Osea, nuestros &lt;code&gt;print&lt;&#x2F;code&gt; están haciendo lento nuestro código.&lt;br &#x2F;&gt;
Justo por eso es una mala práctica hacer debuging a punta de &lt;code&gt;print&lt;&#x2F;code&gt;&#x27;s en lugar de herramientas dirigidas a ello, como &lt;code&gt;logging&lt;&#x2F;code&gt;. En sí &lt;code&gt;logging&lt;&#x2F;code&gt; no hará nuestro código más rápido, pero si que nos permite elegir hasta que grado de importancia hará o no la petición al buffer de salida.&lt;br &#x2F;&gt;
Para ver esto es bastante sencillo, aquí los resultados:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# Prueba utilizando un &amp;quot;print&amp;quot; dentro de un ciclo
@explain_time
def test_print():
    for _ in range(10000):
        result = 10-5
        print(f&amp;quot;{result}&amp;quot;, end=&amp;quot;&amp;quot;)

&amp;gt;&amp;gt;&amp;gt; test_print()
55555...555
         10002 function calls in 0.022 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.004    0.004    0.022    0.022 &amp;lt;ipython-input-293-35e61664f972&amp;gt;:1(test_print)
    10000    0.017    0.000    0.017    0.000 {built-in method builtins.print}
        1    0.000    0.000    0.000    0.000 {method &#x27;disable&#x27; of &#x27;_lsprof.Profiler&#x27; objects}

# Prueba de ciclo sin llamadas a &amp;quot;print&amp;quot; dentro
@explain_time
def test_no_print():
    for _ in range(10000):
        result = 10-5

&amp;gt;&amp;gt;&amp;gt; test_no_print()
         2 function calls in 0.000 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 &amp;lt;ipython-input-295-e65a75e6f856&amp;gt;:1(test_no_print)
        1    0.000    0.000    0.000    0.000 {method &#x27;disable&#x27; of &#x27;_lsprof.Profiler&#x27; objects}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Aquí la diferencia es tan clara que ni siquiera haré tabla comparativa. El mismo &lt;code&gt;for&lt;&#x2F;code&gt; con la misma operación matemática, tarda 0.022 segundos mostrando el resultado y 0.000 no mostrando el resultado.&lt;br &#x2F;&gt;
Así que si están desarrollando el backend de una página web, &lt;strong&gt;quiten los prints&lt;&#x2F;strong&gt; de su código.&lt;&#x2F;p&gt;
&lt;h2&gt;Concatenar strings de la manera incorrecta&lt;&#x2F;h2&gt;
&lt;p&gt;Unir 2 strings es una tarea que debemos hacer prácticamente en todos los programas que hagamos... pero, ¿lo estamos haciendo de la manera más óptima?.&lt;br &#x2F;&gt;
Existen múltiples formas para hacer esa labor, ya sea desde utilizando el operador &lt;code&gt;+&lt;&#x2F;code&gt;, utlizar el método &lt;code&gt;join&lt;&#x2F;code&gt; de un string, utilizando la manera de formatear un texto como en C con el verbo &lt;code&gt;%s&lt;&#x2F;code&gt; o métodos de formateo más propias de Python como el método &lt;code&gt;format&lt;&#x2F;code&gt; o los f-strings.&lt;br &#x2F;&gt;
Bien, cada una de esas técnicas consumen más o menos recursos.&lt;br &#x2F;&gt;
Como aquí iba a quedar muy largo todo el código que utilicé para medir los tiempos, dejo un &lt;a href=&quot;https:&#x2F;&#x2F;git.kirbylife.dev&#x2F;kirbylife&#x2F;snippets&#x2F;src&#x2F;branch&#x2F;main&#x2F;Python-benchmark-different-ways-to-join-strings.md&quot;&gt;enlace a un snippet&lt;&#x2F;a&gt; por si les interesa verlo. De igual forma, aquí les dejo los resultados ordenados de peor a mejor:&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;técnica&lt;&#x2F;th&gt;
&lt;th align=&quot;center&quot;&gt;tiempo&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;método &amp;quot;format&amp;quot;&lt;&#x2F;td&gt;
&lt;td align=&quot;center&quot;&gt;0.066&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;método &amp;quot;join&amp;quot;&lt;&#x2F;td&gt;
&lt;td align=&quot;center&quot;&gt;0.049&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;verbos C-like&lt;&#x2F;td&gt;
&lt;td align=&quot;center&quot;&gt;0.037&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;operador +&lt;&#x2F;td&gt;
&lt;td align=&quot;center&quot;&gt;0.029&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;f-strings&lt;&#x2F;td&gt;
&lt;td align=&quot;center&quot;&gt;0.024&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Como podemos apreciar, los &lt;code&gt;f-strings&lt;&#x2F;code&gt; son la manera más rápida para concatenar 2 strings... pero tienen un pequeño defecto, y es que no podemos declarar f-strings &amp;quot;al vuelo&amp;quot; ya que su poder radica justo en el hecho de que el interprete ya sabe de antemano que justo ahí tendrá que hacer un concatenado de strings. Así que si queremos unir 2 strings sobre la marcha, nuestra segunda mejor opción es usar el operador +.&lt;&#x2F;p&gt;
&lt;h2&gt;Cachear resultados de funciones&lt;&#x2F;h2&gt;
&lt;p&gt;Una de las bases que definen la programación funcional es &amp;quot;siempre que mandes llamar una función con los mismos parámetros, está te devolverá el mismo resultado&amp;quot;, y en base a ella se creó el &lt;code&gt;lru_cache&lt;&#x2F;code&gt; (least recently used).&lt;br &#x2F;&gt;
El concepto es muy simple de entender. Sí nosotros mandamos llamar por primera vez una función, esta tiene que calcular el resultado para posteriormente retornarlo, si el resultado de la segunda vez que la mandemos llamar el resultado también debe ser el mismo, entonces, ¿Para qué volver a calcular el resultado si ya esa tarea la hicimos anteriormente?. Creo que con un ejemplo se entiende mejor.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;from time import sleep
from functools import lru_cache
                      ^ ^ ^ ^ ^
                      ╚═╩═╩═╩═╩═ Se importa el decorador del paquete &amp;quot;functools&amp;quot;
           ╔═╦═╦═╦═╦═╦═ Le indicamos que puede guardar hasta un máximo de 100 resultados
           v v v v v v
@lru_cache(maxsize=100)
def is_even(x):
    sleep(2) &amp;lt;═ Simulando una tarea muy tardada
    return x%2 == 0

@explain_time
def test_100_numbers():
    for n in range(1, 101):
        is_even(n)

&amp;gt;&amp;gt;&amp;gt; test_100_numbers()
         202 function calls in 200.195 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      100    0.001    0.000  200.193    2.002 &amp;lt;ipython-input-334-f1ecb61062cb&amp;gt;:1(is_even)
        1    0.002    0.002  200.195  200.195 &amp;lt;ipython-input-335-c237fa081463&amp;gt;:2(test_100_numbers)
      100  200.192    2.002  200.192    2.002 {built-in method time.sleep}
      ^ ^
      ╚═╩═ Hace 100 llamadas a la función &amp;quot;sleep&amp;quot;
        1    0.000    0.000    0.000    0.000 {method &#x27;disable&#x27; of &#x27;_lsprof.Profiler&#x27; objects}

&amp;gt;&amp;gt;&amp;gt; test_100_numbers()                                                                       
         2 function calls in 0.000 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 &amp;lt;ipython-input-335-c237fa081463&amp;gt;:2(test_100_numbers)
        1    0.000    0.000    0.000    0.000 {method &#x27;disable&#x27; of &#x27;_lsprof.Profiler&#x27; objects}
      ^ ^
      ╚═╩═ Las llamadas a &amp;quot;sleep&amp;quot; desaparecen
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como podemos apreciar, la primera vez que corremos el test que medirá si un número es par o no, a este le tomará 200 segundos (gracias al delay enorme que le pusimos a la función &lt;code&gt;is_even&lt;&#x2F;code&gt;), pero la segunda vez que corremos el test esté es terminado de inmediato, esto gracias a que los resultados de la función quedaron cacheados en memoria y cuando se volvió a llamar la función, esta ya no se ejecutó como tal librándonos del delay.&lt;br &#x2F;&gt;
Aunque esto pueda ser realmente bueno, hay que tener cuidado y pensar muy bien cual función puede ser cacheada y cual no, pero si utilizamos el caché a conciencia, nos traerá muchas bondades a nuestro código.&lt;&#x2F;p&gt;
&lt;h2&gt;Conclusiones&lt;&#x2F;h2&gt;
&lt;p&gt;Aunque Python no sea el lenguaje más rápido que pueda existir, su rendimiento en general no está nada mal y conociendo estos pequeños consejos, pueden escribir código con mejor rendimiento.&lt;br &#x2F;&gt;
Por otra parte, el rendimiento no lo es todo en un lenguaje y si sacrificamos un poco de rendimiento por tener un código más limpio o un código más tolerante a fallas, pues es un sacrificio que vale la pena.&lt;&#x2F;p&gt;
&lt;h2&gt;Fuentes&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3.8&#x2F;library&#x2F;functools.html&quot;&gt;https:&#x2F;&#x2F;docs.python.org&#x2F;3.8&#x2F;library&#x2F;functools.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3.8&#x2F;library&#x2F;profile.html&quot;&gt;https:&#x2F;&#x2F;docs.python.org&#x2F;3.8&#x2F;library&#x2F;profile.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;raymondh&#x2F;status&#x2F;1205969258800275456&quot;&gt;https:&#x2F;&#x2F;twitter.com&#x2F;raymondh&#x2F;status&#x2F;1205969258800275456&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;martinheinz.dev&#x2F;blog&#x2F;13&quot;&gt;https:&#x2F;&#x2F;martinheinz.dev&#x2F;blog&#x2F;13&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h4&gt;Actualizaciones&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;strong&gt;28-02-2020&lt;&#x2F;strong&gt;: Arreglados algunos typos. Gracias VMS.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
        <title>Poetry: un gran gestor de proyectos para Python</title>
        <pubDate>Fri, 17 Jan 2020 04:26:46 GMT</pubDate>
        <link>https://blog.kirbylife.dev/post/poetry-un-gran-gestor-de-proyectos-para-python-5</link>
        <guid isPermaLink="true">https://blog.kirbylife.dev/post/5</guid>
        <description>&lt;p&gt;Dos de las cosas que peor están resueltas en Python es le definición de dependencias y el aislamiento de proyectos.&lt;br &#x2F;&gt;
Seamos sinceros, almacenar las dependencias de un proyecto en &lt;code&gt;requierements.txt&lt;&#x2F;code&gt; de forma manual o con el comando:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; pip freeze &amp;gt; requierements.txt
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;es una manera bastante mala de llevar un continuo seguimiento de todas las dependencias, ya que es muy probable que no siempre ese archivo se mantenga actualizado, ya que se debe escribir de manera manual y separada del proyecto en si.&lt;br &#x2F;&gt;
Los entornos virtuales son otro tema que está resuelto a medias porque, de nuevo, se deben activar de manera manual y eso al final solo está agregando complejidad innecesaria para poder arrancar el proyecto.&lt;br &#x2F;&gt;
Maneras de resolver esto hay muchas y hoy les vengo a platicar de mi preferida, Poetry.&lt;&#x2F;p&gt;
&lt;p&gt;Poetry es una manera de tener correctamente controladas las dependencias del proyecto y poderlo aislar de una manera transparente para nosotros.&lt;br &#x2F;&gt;
Su manera de funcionar no dista demasiado de gestores de proyectos de otros lenguajes como npm, yarn, cargo o composer. En esencia, es un archivo que contiene toda la información y requerimientos que nuestro proyecto necesita para funcionar.&lt;&#x2F;p&gt;
&lt;h2&gt;Instalación&lt;&#x2F;h2&gt;
&lt;p&gt;La instalación es bastante sencilla y como única dependencia, debemos ya tener instalado Python en nuestro equipo.&lt;&#x2F;p&gt;
&lt;p&gt;Si nuestro equipo tiene alguna distribución de GNU&#x2F;Linux, MacOS o es windows con bashonwindows, basta con ejecutar el comando:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; curl -sSL https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;python-poetry&#x2F;poetry&#x2F;master&#x2F;get-poetry.py | python
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;O si nada mas estamos en Windows y tenemos powershell, se instala con el siguiente comando (Todo este blog fue escrito enteramente en GNU&#x2F;Linux y no fue probado en Windows):&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; (Invoke-WebRequest -Uri https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;python-poetry&#x2F;poetry&#x2F;master&#x2F;get-poetry.py -UseBasicParsing).Content | python
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Para verificar que se instaló correctamente se ejecuta el comando:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; poetry --version
Poetry 0.12.17
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Mas información acerca de la instalación en la &lt;a href=&quot;https:&#x2F;&#x2F;python-poetry.org&#x2F;docs&#x2F;&quot;&gt;web oficial&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2&gt;Creación de un nuevo proyecto&lt;&#x2F;h2&gt;
&lt;p&gt;Ahora si, dejando la parte mas tediosa a un lado, comencemos ahora si con lo interesante.
Para crear un nuevo proyecto de Poetry debes escribir el siguiente comando:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; poetry new «nombre del proyecto»
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Lo cual creará los siguientes archivos y carpetas&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;example&#x2F;
├── example
│   └── __init__.py
├── pyproject.toml
├── README.rst
└── tests
    ├── __init__.py
    └── test_example.py
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;y si queremos hacer compatible un proyecto ya existente con Poetry, solo debemos escribir:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; poetry init
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Nos hará una serie de preguntas como, ¿Cuál es el nombre del proyecto?, ¿En qué versión va?, ¿Quién lo creó? entre otras cosas.&lt;br &#x2F;&gt;
Ya sea que lo hayamos creado con &lt;code&gt;new&lt;&#x2F;code&gt; o con &lt;code&gt;init&lt;&#x2F;code&gt;, en ambos casos nos creará un archivo &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt; el cual es el responsable de que la magia ocurra. Dicho archivo contiene la siguiente información:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# pyproject.toml
[tool.poetry]
name = &amp;quot;example&amp;quot;
        ^ ^ ^ ^
        ╚═╩═╩═╩═ Nombre del proyecto
version = &amp;quot;0.1.0&amp;quot;
           ^ ^ ^
           ╚═╩═╩═ Versión actual del proyecto
description = &amp;quot;Proyecto de prueba&amp;quot;
               ^ ^ ^ ^ ^ ^ ^ ^ ^ 
               ╚═╩═╩═╩═╩═╩═╩═╩═╩═ Descripción del proyecto
                                  (útil si se quiere subir posteriormente a PyPI)
authors = [&amp;quot;kirbylife&amp;quot;]
            ^ ^ ^ ^ ^
            ╚═╩═╩═╩═╩═ La o las personas que mantienen el proyecto

[tool.poetry.dependencies]
python = &amp;quot;^3.6&amp;quot;
          ^ ^ 
          ╚═╩═ Versión de Python que usará el proyecto
^ ^ ^ ^ ^
╚═╩═╩═╩═╩═ En esta parte del documento irán las demás dependencias del proyecto

[tool.poetry.dev-dependencies]
pytest = &amp;quot;^3.0&amp;quot;

^ ^ ^ ^ ^
╚═╩═╩═╩═╩═ Aquí irán las dependencias que solo serán necesarias para pruebas o depuración

[build-system]
requires = [&amp;quot;poetry&amp;gt;=0.12&amp;quot;]
             ^ ^ ^ ^ ^ ^
             ╚═╩═╩═╩═╩═╩═ Versión de Poetry que se está utilizando
build-backend = &amp;quot;poetry.masonry.api&amp;quot;
                 ^ ^ ^ ^ ^ ^ ^ ^ ^
                 ╚═╩═╩═╩═╩═╩═╩═╩═╩═ Compositor del proyecto
                                    (solo tocar en caso de saber lo que estás haciendo)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como podemos ver, es un resumen bastante completo del proyecto en si, ya que incluye desde meta-información como el nombre, la descripción y las personas que los mantienen, hasta la dependencias que el proyecto necesita.&lt;&#x2F;p&gt;
&lt;h2&gt;Manejo de dependencias&lt;&#x2F;h2&gt;
&lt;p&gt;Una de las cosas que mas me gusta de Poetry es su manejo de dependencias, y es que, tenerlas todas recopiladas y que no se puedan utilizar dentro del proyecto si no está listada es un gran alivio para evitar &amp;quot;sorpresas&amp;quot; cuando se quiera subir a producción.&lt;br &#x2F;&gt;
Para añadir una nueva dependencia es bastante sencillo y lo podemos hacer desde la linea de comando con la instrucción &lt;code&gt;poetry add&lt;&#x2F;code&gt; como en este ejemplo:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; poetry add requests
Creating virtualenv example-py3.6 in &#x2F;home&#x2F;kirbylife&#x2F;.cache&#x2F;pypoetry&#x2F;virtualenvs
Using version ^2.22 for requests
...
  - Installing requests (2.22.0)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Una vez completado todo el proceso de instalación podemos fijarnos que se agregó el paquete &lt;code&gt;requests&lt;&#x2F;code&gt; al apartado de dependencias dentro de nuestro archivo &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# pyproject.toml
...
[tool.poetry.dependencies]
python = &amp;quot;^3.6&amp;quot;
requests = &amp;quot;^2.22&amp;quot;
^ ^ ^ ^ ^ ^ ^ ^ ^
╚═╩═╩═╩═╩═╩═╩═╩═╩═ Paquete &amp;quot;requests&amp;quot; añadido a la lista de dependencias
                   con todo y su versión

...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Otra forma de añadir dependencias es escribirla manualmente en el archivo &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt; y posteriormente actualizar el listado de dependencias. Aquí un ejemplo:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# pyproject.toml
...
[tool.poetry.dependencies]
python = &amp;quot;^3.6&amp;quot;
requests = &amp;quot;^2.22&amp;quot;
beautifulsoup4 = &amp;quot;4.8.2&amp;quot;
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
╚═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═ Paquete &amp;quot;beautifulsoup4&amp;quot; escrito manualmente con todo y versión
...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;La versión del paquete se puede consultar directamente en la &lt;a href=&quot;https:&#x2F;&#x2F;pypi.org&#x2F;&quot;&gt;web de PyPI&lt;&#x2F;a&gt;.&lt;br &#x2F;&gt;
Y una vez modificado solo basta ejecutar la instrucción &lt;code&gt;update&lt;&#x2F;code&gt; de Poetry para efectuar los cambios:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; poetry update
Updating dependencies
...
  - Installing beautifulsoup4 (4.8.2)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Lo mismo aplica para las dependencias de pruebas y depuración. Basta con agregar la bandera &lt;code&gt;--dev&lt;&#x2F;code&gt; o &lt;code&gt;-D&lt;&#x2F;code&gt; al comando &lt;code&gt;add&lt;&#x2F;code&gt; de Poetry para indicar que coloque esa dependencia en el apartado de &amp;quot;dev&amp;quot;, o la podemos escribir manualmente en su correspondiente apartado y ejecutar la instrucción &lt;code&gt;update&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; poetry add ipython --dev
Using version ^7.11 for ipython
...
  - Installing ipython (7.11.1)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Agregando así la dependencia a nuestro listado de paquetes para depuración.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# pyproject.toml
...
[tool.poetry.dev-dependencies]
pytest = &amp;quot;^3.0&amp;quot;
ipython = &amp;quot;^7.11&amp;quot;
...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;¡Y listo! dependencias añadidas de una manera controlada, simple ¿verdad?.&lt;&#x2F;p&gt;
&lt;h2&gt;Escribiendo código&lt;&#x2F;h2&gt;
&lt;p&gt;Bien, ya sabemos como agregar dependencias, pero ahora... ¿Cómo y en donde escribimos nuestro código?.&lt;br &#x2F;&gt;
Pues bien, aunque el código realmente lo podemos escribir y añadir en la raíz del proyecto, lo mas adecuado sería crear una carpeta con el mismo nombre de nuestro proyecto dentro de la carpeta raíz con su correspondiente archivo &lt;code&gt;__init__.py&lt;&#x2F;code&gt; y ahí colocar el código.&lt;br &#x2F;&gt;
Esto se hace de manera automática si el proyecto lo creamos de 0 con la herramienta de linea de comandos de Poetry, pero lo tendremos que hacer a mano si el proyecto lo creamos con la instrucción &lt;code&gt;init&lt;&#x2F;code&gt;.&lt;br &#x2F;&gt;
Aquí un ejemplo de como quedaría un proyecto simple con esta estructura.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;example&#x2F;
├── example
│   ├── __init__.py
│   └── main.py
│       ^ ^ ^ ^
│       ╚═╩═╩═╩══ Creé un nuevo archivo llamado &amp;quot;main.py&amp;quot;
├── poetry.lock
├── pyproject.toml
└── README.rst
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y dentro del archivo &lt;code&gt;main.py&lt;&#x2F;code&gt; está el siguiente código de ejemplo:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# example&#x2F;main.py

import re


def is_prime(n):
    return not re.match(r&amp;quot;-?$|^(--+?)\1$&amp;quot;, &amp;quot;-&amp;quot;*n)

def primes(a, b):
    return list(filter(is_prime, range(a, b)))

def main():
    print(&amp;quot;Calculo de números primos&amp;quot;)
    a = int(input(&amp;quot;Límite inferior: &amp;quot;))
    b = int(input(&amp;quot;Límite superior: &amp;quot;))

    result = primes(a, b)
    print(f&amp;quot;Todos los números primos que hay entre {a} y {b} son:&amp;quot;)
    print(&amp;quot;, &amp;quot;.join(map(str, result)))

if __name__ == &amp;quot;__main__&amp;quot;:
    main()
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como pueden ver, es un código sin nada demasiado destacable (no me maten por validar números primos con regex, es solo un código de ejemplo).
De esta manera tan simple y realmente cotidiana, se puede mantener una estructura poco caótica de nuestro proyecto lo cual, a la larga, será muy beneficioso.&lt;br &#x2F;&gt;
Pero ahora, ¿Cómo se corre el código?.&lt;&#x2F;p&gt;
&lt;h2&gt;Ejecución de comandos&lt;&#x2F;h2&gt;
&lt;p&gt;Bien, ya sabemos agregar dependencias y sabemos como debemos mantener la estructura de nuestro proyecto, pero ¿Cómo puedo ejecutar mi proyecto?&lt;br &#x2F;&gt;
Poetry incluye una instrucción llamada &lt;code&gt;run&lt;&#x2F;code&gt; que nos permite ejecutar comandos que se encuentran encapsulados dentro del entorno virtual de nuestro proyecto. Así ejecutaríamos el código anteriormente escrito:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; poetry run python example&#x2F;main.py 
Calculo de números primos
Límite inferior: 10
Límite superior: 50
Todos los números primos que hay entre 10 y 50 son:
11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como podemos apreciar, solo debemos escribir &lt;code&gt;poetry run&lt;&#x2F;code&gt; + el comando de toda la vida que queramos ejecutar.&lt;br &#x2F;&gt;
Aparte de tener esta opción, Poetry nos ofrece una manera de añadir atajos a al instrucción &lt;code&gt;run&lt;&#x2F;code&gt; para que sea mas sencillo ejecutar nuestro proyecto.&lt;br &#x2F;&gt;
Esto se hace añadiendo una nueva sección al archivo &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt; llamada &amp;quot;scripts&amp;quot;. Aquí un ejemplo de las partes de un atajo:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# pyproject.toml
...
[tool.poetry.dev-dependencies]
pytest = &amp;quot;^3.0&amp;quot;
ipython = &amp;quot;^7.11&amp;quot;

[tool.poetry.scripts]
start = &amp;quot;example.main:main&amp;quot;
^ ^ ^    ^ ^ ^ ^ ^ ^  ^ ^
║ ║ ║    ║ ║ ║ ║ ║ ║  ╚═╩═ Nombre de la función a ejecutar
║ ║ ║    ║ ║ ║ ║ ╚═╩═ Nombre del archivo que contiene la función
║ ║ ║    ╚═╩═╩═╩═ Nombre de la carpeta que contiene el archivo
╚═╩═╩═ Nombre del atajo

[build-system]
requires = [&amp;quot;poetry&amp;gt;=0.12&amp;quot;]
build-backend = &amp;quot;poetry.masonry.api&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Una vez añadida esa opción a nuestro archivo todopoderoso &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt;, solo bastará con ejecutar el comando:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;$ poetry run start
              ^ ^ ^
              ╚═╩═╩═ Nombre del atajo
Calculo de números primos
Límite inferior: 10
Límite superior: 50
Todos los números primos que hay entre 10 y 50 son:
11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y listo, eso simplifica bastante las cosas, ¿no? ahora es mucho mas fácil recordar el comando para cumplir con la acción.&lt;br &#x2F;&gt;
En el ejemplo anterior ejecuté el código que había escrito, pero no solo sirve para eso, ¿Recuerdan que instalé &lt;code&gt;ipython&lt;&#x2F;code&gt; como una dependencia de depuración? pues bien, para abrir el shell de ipython solo debemos escribir en la terminal:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; poetry run ipython
Python 3.6.9 (default, Dec  6 2019, 13:53:25) 
Type &#x27;copyright&#x27;, &#x27;credits&#x27; or &#x27;license&#x27; for more information
IPython 7.11.1 -- An enhanced Interactive Python. Type &#x27;?&#x27; for help.

In [1]:
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Esto funciona con todos los demás paquetes que agreguen un binario que puede ser accesible desde la terminal.&lt;&#x2F;p&gt;
&lt;h2&gt;Pruebas unitarias&lt;&#x2F;h2&gt;
&lt;p&gt;Escribir pruebas unitarias es una parte que no podemos omitir NUNCA si queremos escribir código fiable y Poetry puede ser un buen aliado para llevar a cabo este objetivo.&lt;br &#x2F;&gt;
Para hacer esta tarea mucho mas sencilla, dentro del proyecto debe existir una carpeta llamada &amp;quot;test&amp;quot; y dentro de esa carpeta, todos los archivos en donde escribiremos nuestras pruebas unitarias. Si nuestro proyecto lo creamos desde 0 con Poetry, esto ya vendrá incluido, pero si lo iniciamos con la instrucción &lt;code&gt;init&lt;&#x2F;code&gt; tocará hacer la carpeta y los archivos a mano.&lt;br &#x2F;&gt;
&lt;strong&gt;importante&lt;&#x2F;strong&gt;, los archivos de pruebas deben tener al inicio de su nombre &amp;quot;test_&amp;quot;.&lt;br &#x2F;&gt;
Al final la estructura del proyecto queda así:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;example&#x2F;
├── example
│   ├── __init__.py
│   └── main.py
├── poetry.lock
├── pyproject.toml
├── README.rst
└── tests
    ├── __init__.py
    └── test_example.py
        ^ ^ ^ ^ ^ ^ ^ ^
        ╚═╩═╩═╩═╩═╩═╩═╩═ Archivo que contendrá nuestras pruebas unitarias.
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Aquí dejo de ejemplo unas cuantas pruebas unitarias.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# tests&#x2F;test_example.py

from example import __version__
from example.main import is_prime, primes

def test_version():
    assert __version__ == &#x27;0.1.0&#x27;

def test_2_is_prime():
    assert is_prime(2)

def test_4_is_not_prime():
    assert not is_prime(4)

def test_113_is_prime():
    assert is_prime(113)

def test__minus_1_is_not_prime():
    assert not is_prime(-1)

def test_range_from_10_to_30_of_prime_numbers():
    numbers = primes(10, 30)
    assert numbers == [11, 13, 15, 17, 19, 21, 23, 25, 27, 29]
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Para correr los tests basta con llamar al ejecutable &lt;code&gt;pytest&lt;&#x2F;code&gt; utilizando la instrucción &amp;quot;run&amp;quot;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; poetry run pytest
============================= test session starts =============================
platform linux -- Python 3.6.9, pytest-3.10.1, py-1.8.1, pluggy-0.13.1
rootdir: &#x2F;home&#x2F;kirbylife&#x2F;Temp&#x2F;example, inifile:
collected 6 items                                                                                 

tests&#x2F;test_example.py ......                                            [100%]

========================== 6 passed in 0.02 seconds ===========================
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y si queremos mapear un atajo por si en un futuro agregamos mas tests con diferentes herramientas, debemos modificar un poco el archivo &lt;code&gt;tests&#x2F;__init__.py&lt;&#x2F;code&gt; para que quede así:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# tests&#x2F;__init__.py

from pytest import main as pytest_main

def run_all_tests():
    pytest_main()

    ^ ^ ^ ^ ^ ^ ^
    ╚═╩═╩═╩═╩═╩═╩═ Aquí se podrán apilar los tests de todos los frameworks que necesitemos
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;y el atajo en el archivo &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt; quedaría así:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# pyproject.toml
...
[tool.poetry.scripts]
start = &amp;quot;example.main:main&amp;quot;
tests = &amp;quot;tests:run_all_tests&amp;quot;
^ ^ ^    ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
║ ║ ║    ║ ║ ║ ╚═╩═╩═╩═╩═╩═╩═ Nombre de la función que dispara todas las pruebas
║ ║ ║    ╚═╩═╩═ Nombre de la carpeta donde se encuentras todas las pruebas
╚═╩═╩═ Nombre del atajo
...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y como mas o menos lo pueden intuir, las pruebas se correrán con el comando:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;❯ poetry run tests
             ^ ^ ^
             ╚═╩═╩═ Nombre del atajo
============================= test session starts =============================
platform linux -- Python 3.6.9, pytest-3.10.1, py-1.8.1, pluggy-0.13.1
rootdir: &#x2F;home&#x2F;kirbylife&#x2F;Temp&#x2F;example, inifile:
collected 6 items                                                                                 

tests&#x2F;test_example.py ......                                            [100%]

========================== 6 passed in 0.02 seconds ===========================
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2&gt;Crear paquete wheels&lt;&#x2F;h2&gt;
&lt;p&gt;Poetry no solo nos permite mantener en control todo lo relacionado con nuestros proyectos, sino que también nos hace la vida mas sencilla en caso de que queramos distribuirlos ya que nos permite construir todo nuestro proyecto en un paquete de wheels.&lt;br &#x2F;&gt;
Hacerlo es súper sencillo, solo ejecutamos la instrucción &lt;code&gt;build&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; poetry build
Building example (0.1.0)
 - Building sdist
 - Built example-0.1.0.tar.gz

 - Building wheel
 - Built example-0.1.0-py3-none-any.whl
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Eso creará una carpeta llamada &amp;quot;dist&amp;quot; y dentro nuestro paquete wheels.  Estos paquetes, por si no lo sabes, es la manera en la que Python por defecto maneja los paquetes.  Si queremos que otra persona utilice nuestro hermoso código, no es necesario que tenga Poetry instalado, solo le compartimos el archivo &lt;code&gt;.whl&lt;&#x2F;code&gt; y lo puede instalar en su equipo utilizando &lt;code&gt;pip&lt;&#x2F;code&gt; de toda la vida:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; pip install example-0.1.0-py3-none-any.whl --user
Processing .&#x2F;example-0.1.0-py3-none-any.whl
...
Successfully installed beautifulsoup4-4.8.2 example-0.1.0 requests-2.22.0
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y ahora será capaz de utilizar nuestro código en su propio shell. Aquí una demo:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;In [1]: from example import main                                                                   

In [2]: main.main()                                                                                
Calculo de números primos
Límite inferior: 10
Límite superior: 30
Todos los números primos que hay entre 10 y 30 son:
11, 13, 15, 17, 19, 21, 23, 25, 27, 29

In [3]: main.is_prime(100)                                                                         
Out[3]: False

In [4]: main.is_prime(113)                                                                         
Out[4]: True
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2&gt;Conclusiones&lt;&#x2F;h2&gt;
&lt;p&gt;Bueno, a grandes rasgos eso es Poetry.
¿Qué les pareció? ¿Lo usarían en sus proyectos personales o profesionales?
Personalmente soy un gran fan de este gestor de proyectos y considero que es indispensable una herramienta como estas, tanto en proyecto pequeñitos como en los proyectos mas gigantescos.&lt;br &#x2F;&gt;
Si no te terminó de convencer Poetry, tambien puedes investigar acerca de otras herramientas que cumplen funciones parecidas como &lt;a href=&quot;https:&#x2F;&#x2F;pipenv-es.readthedocs.io&#x2F;es&#x2F;latest&#x2F;&quot;&gt;pipenv&lt;&#x2F;a&gt; o &lt;a href=&quot;https:&#x2F;&#x2F;virtualenvwrapper.readthedocs.io&#x2F;en&#x2F;latest&#x2F;&quot;&gt;virtualenvwrapper&lt;&#x2F;a&gt; pero lo importante es que usen alguno.&lt;br &#x2F;&gt;
Subí el proyecto que utilice para realizar este post a mi &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;kirbylife&#x2F;poetry-example&quot;&gt;gitlab&lt;&#x2F;a&gt;, por si quedaron dudas sobre la estructura o el código en general.&lt;&#x2F;p&gt;
&lt;h2&gt;Sección extra: Posibles problemas&lt;&#x2F;h2&gt;
&lt;h3&gt;Error [TooManyRedirects] al intentar añadir un nuevo paquete&lt;&#x2F;h3&gt;
&lt;p&gt;Para solucionarlo es muy sencillo, basta con limpiar el caché de Poetry así:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$&amp;gt; poetry cache:clear --all pypi

Delete 332 entries? (yes&#x2F;no) [no] yes
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2&gt;Fuentes&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;python-poetry.org&#x2F;docs&#x2F;&quot;&gt;https:&#x2F;&#x2F;python-poetry.org&#x2F;docs&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;python-poetry&#x2F;poetry&#x2F;issues&#x2F;728&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;python-poetry&#x2F;poetry&#x2F;issues&#x2F;728&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.pytest.org&#x2F;en&#x2F;latest&#x2F;usage.html&quot;&gt;https:&#x2F;&#x2F;docs.pytest.org&#x2F;en&#x2F;latest&#x2F;usage.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</description>
      </item>
      <item>
        <title>¿Por qué Python3 es objetivamente mejor que Python2?</title>
        <pubDate>Fri, 27 Dec 2019 08:41:20 GMT</pubDate>
        <link>https://blog.kirbylife.dev/post/por-que-python3-es-objetivamente-mejor-que-python2-4</link>
        <guid isPermaLink="true">https://blog.kirbylife.dev/post/4</guid>
        <description>&lt;p&gt;Ya está casi por concluir el año 2019 y con el se va uno de los monstruos de software que nos dio esta década, Python2.&lt;br &#x2F;&gt;
Python2 nos ha acompañado a la largo de todo este tiempo como uno de los lenguajes mas simples pero poderosos y para homenajear todo su legado, en este post listaré todas las cosas por las cuales se estaba quedando obsoleto y porque la &lt;em&gt;Python Software Foundation&lt;&#x2F;em&gt; tomó la excelente decisión de dejarlo morir, ahí, solo, desangrándose y pidiendo clemencia.&lt;br &#x2F;&gt;
Este será un repaso de las caracteristicas que hacen a Python3 &lt;strong&gt;OBJETIVAMENTE&lt;&#x2F;strong&gt; mejor que su antecesor al cual ya no le quedaba superficie para colocar parches.&lt;&#x2F;p&gt;
&lt;h2&gt;Optimización de memoria y CPU con iteradores&lt;&#x2F;h2&gt;
&lt;p&gt;¿Cuántos &lt;code&gt;range&lt;&#x2F;code&gt; o &lt;code&gt;map&lt;&#x2F;code&gt; no hemos escrito a lo largo de nuestro tiempo desarrollando en Python? pues si bien, estos métodos son muy bonitos y prácticos, también representaban un gran desperdicio de memoria ya que se ejecutaba todo el código de golpe ocupando demasiado espacio.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# Python2
&amp;gt;&amp;gt;&amp;gt; range(100)
[0, 1, 2, 3, 4, 5, ..., 97, 98, 99]

&amp;gt;&amp;gt;&amp;gt; type(range(100))
         ^ ^ ^
         ╚═╩═╩═ La función &amp;quot;range&amp;quot; retorna una lista
╔═╦════════════════════════════════════════════╩═╩═╝
v v
list


# Python3
&amp;gt;&amp;gt;&amp;gt; range(100)
range(0, 100)

&amp;gt;&amp;gt;&amp;gt; type(range(100))
         ^ ^ ^
         ╚═╩═╩═ La función &amp;quot;range&amp;quot; retorna un objeto de tipo &amp;quot;range&amp;quot;
╔═╦═╦═════════════════════════════════════════════════════════╩═╩═╝
v v v
range
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;La cosa está bastante clara, no? en Python2 cuando nosotros declarábamos un &lt;code&gt;range&lt;&#x2F;code&gt;, en memoria se guardaban todos los números de de inicio a fin en una lista. ¿Son necesarios todos los números de golpe? probablemente no, pero aún así Python2 los generaba todos al mismo tiempo. Otra forma de ver esto es con la función &lt;code&gt;map&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;import sys

# Python2
&amp;gt;&amp;gt;&amp;gt; map(sys.exit, [0, 1, 2])
    ^ ^
    ╚═╩═ La función &amp;quot;map&amp;quot; ejecuta la función &amp;quot;sys.exit&amp;quot;
         al momento de ser ejecutado y sale del shell
╔═══════════════════════════════════════════════╩═╩═╝
v
$&amp;gt; echo $?
0

# Python3
&amp;gt;&amp;gt;&amp;gt; map(sys.exit, [0, 1, 2])

&amp;gt;&amp;gt;&amp;gt; next(map(sys.exit, [0, 1, 2]))
    ^ ^ ^ ^
    ╚═╩═╩═╩═ La función &amp;quot;map&amp;quot; ejecuta la función &amp;quot;sys.exit&amp;quot;
             solo al pedir un ítem, y hasta entonces, sale del shell
╔══════════════════════════════════════════════════════════════╩═╩═╝
v
$&amp;gt; echo $?
0
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Aquí con map las cosas son claras, en Python2 la función se mapeaba para &lt;strong&gt;TOOOODOS&lt;&#x2F;strong&gt; los elementos de la lista al momento de declarar el &lt;code&gt;map&lt;&#x2F;code&gt;. A diferencia de Python3, en donde la función es mapeada a cada elemento solo al momento de pedir el siguiente valor, pudiendo evitar así utilizar tiempo de CPU en vano.&lt;br &#x2F;&gt;
La gran desventaja de &lt;code&gt;range&lt;&#x2F;code&gt; fue parcheada con otra función llamada &lt;code&gt;xrange&lt;&#x2F;code&gt; que hacía algo similar a la función &lt;code&gt;range&lt;&#x2F;code&gt; de Python3. También lo corrigieron usuarios con implementaciones propias de &lt;code&gt;range&lt;&#x2F;code&gt;, en &lt;a href=&quot;https:&#x2F;&#x2F;git.kirbylife.dev&#x2F;kirbylife&#x2F;snippets&#x2F;src&#x2F;branch&#x2F;main&#x2F;Python-range-function-improved-for-Python2.md&quot;&gt;este snippet&lt;&#x2F;a&gt; pueden ver la que yo creé en su momento.&lt;br &#x2F;&gt;
Aunque las funciones mencionadas en este apartado solo fueron &lt;code&gt;range&lt;&#x2F;code&gt; y &lt;code&gt;map&lt;&#x2F;code&gt;, esta optimización la recibieron también los &lt;code&gt;zip&lt;&#x2F;code&gt;, &lt;code&gt;filter&lt;&#x2F;code&gt; y los métodos &lt;code&gt;keys&lt;&#x2F;code&gt;, &lt;code&gt;values&lt;&#x2F;code&gt; e &lt;code&gt;items&lt;&#x2F;code&gt; de los diccionarios.&lt;&#x2F;p&gt;
&lt;h2&gt;La división retorna enteros&lt;&#x2F;h2&gt;
&lt;p&gt;No se si ustedes han tenido ese presentimiento de &amp;quot;creo que no estoy &lt;em&gt;casteando&lt;&#x2F;em&gt; lo suficiente mis variables&amp;quot;, pues justo ese sentimiento era el que presentaba al momento de intentar hacer una división en Python2 ya que si ambas variables eran &lt;code&gt;int&lt;&#x2F;code&gt;, el resultado sería otro &lt;code&gt;int&lt;&#x2F;code&gt; aunque la división tuviera residuo, y para evitar eso, se tenía que &lt;em&gt;castear&lt;&#x2F;em&gt; (perdónenme pero no encontré una traducción al español para &amp;quot;cast&amp;quot;) uno de los datos a &lt;code&gt;float&lt;&#x2F;code&gt; si queriamos el resultado completo, aunque fuera un &lt;code&gt;10 &#x2F; 3&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;num1 = 15

# Python2
&amp;gt;&amp;gt;&amp;gt; 10 &#x2F; 3
       ^
       ╚═ El operador de división nos devuelve unicamente la parte entera
╔═══════════════════════════════════════════════════════════════════╩═╩═╝
v
3

&amp;gt;&amp;gt;&amp;gt; 10. &#x2F; 3
      ^
      ╚═ Si la división involucra por lo menos a un número &amp;quot;float&amp;quot;,
         el resultado será el valor exacto con precisión en decimales
╔═══════════════════════════════════════════════════════════╩═╩═╩═╩═╝
v
3.3333333333333335
                 ^
                 ╚═ (¿Este 5?, pronto explicaré la precisión con decimales)

&amp;gt;&amp;gt;&amp;gt; num1 &#x2F; 4
3

&amp;gt;&amp;gt;&amp;gt; float(num1) &#x2F; 4
    ^ ^ ^
    ╚═╩═╩═ Era necesario castear la variable a float si queríamos el valor completo
╔═══════════════════════════════════════════════════════════════════════════╩═╩═╩═╝
v
3.75

# Python3
&amp;gt;&amp;gt;&amp;gt; 10 &#x2F; 3
       ^
       ╚═ Operador de división normal retorna el valor el valor preciso
╔═══════════════════════════════════════════════════════════════╩═╩═╩═╝
v
3.3333333333333335

&amp;gt;&amp;gt;&amp;gt; num1 &#x2F; 4
3.75

&amp;gt;&amp;gt;&amp;gt; num1 &#x2F;&#x2F; 4
         ^
         ╚═ Nuevo operador de división que retorna unicamente la parte entera
╔═══════════════════════════════════════════════════════════════════════╩═╩═╝
v
3

&amp;gt;&amp;gt;&amp;gt; num1 &#x2F;&#x2F; 4.0
             ^
             ╚═ Si con el nuevo operador uno de los 2 valores es &amp;quot;float&amp;quot;,
                el resultado será unicamente la parte entera pero en un float
 ╔══════════════════════════════════════════════════════════════════════╩═╩═╝
 v
3.0
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Pero es probable que en ocasiones nos interese unicamente la parte entera de la división en Python3 y los &lt;em&gt;casteos&lt;&#x2F;em&gt; del infierno volverían a aparecer (&lt;code&gt;int(10 &#x2F; 4)&lt;&#x2F;code&gt;), así que la gente de Python, muy inteligentemente, implementaron un nuevo operador, el &lt;code&gt;&#x2F;&#x2F;&lt;&#x2F;code&gt;. Este nuevo operador tiene un comportamiento parecido a la división de Python2, si la división es entre enteros, el resultado será un &lt;code&gt;int&lt;&#x2F;code&gt;, y si nuestra división involucra por lo menos &lt;code&gt;float&lt;&#x2F;code&gt;, el valor resultante será unicamente la parte entera de la división pero mostrada en un &lt;code&gt;float&lt;&#x2F;code&gt;. A pesar de esta implementación, muchos seguiremos &lt;em&gt;casteando&lt;&#x2F;em&gt; nuestras variables &amp;quot;por si las dudas&amp;quot;.&lt;&#x2F;p&gt;
&lt;h2&gt;Scope diferente para las listas de compresiones&lt;&#x2F;h2&gt;
&lt;p&gt;Este es muy sencillo de explicar pero cuantos quebraderos de cabeza no nos llegó a dar en su momento y es que antes las variables utilizadas en las listas de compresiones estaban con un scope al mismo nivel que nuestro bloque actual, con el ejemplo se verá mas claramente.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;count = -10
^ ^ ^ ^ ^
╚═╩═╩═╩═╩═ Variable &amp;quot;count&amp;quot; declarada en el bloque actual con un valor de -10
items = [count**2 for count in range(1, 10)]
         ^ ^ ^        ^ ^ ^
         ╚═╩═╩════════╩═╩═╩═ Variable &amp;quot;count&amp;quot; también es utilizada en una lista
                             de compresión

# Python2
&amp;gt;&amp;gt;&amp;gt; count
    ^ ^ ^
    ╚═╩═╩═ Al consultar el valor de &amp;quot;count&amp;quot;, este cambió al ultimo valor del for
╔════════════════════════════════════════════════════════════════════════════╩═╝
v
4

# Python3
&amp;gt;&amp;gt;&amp;gt; count
    ^ ^ ^
    ╚═╩═╩═ Al consultar el valor de &amp;quot;count&amp;quot;, este quedó intacto a pesar de for
╔══════════════════════════════════════════════════════════════════════════╩═╝
v
-10

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como se puede apreciar, las variables utilizadas para iterar dentro de una lista de compresión tiene reservado un espacio aparte en memoria para que no modifiquen nuestro scope actual. Aunque en el ejemplo solo se hizo con listas de compresión, este principio funciona identico en diccionarios, generadores y sets de compresión.&lt;&#x2F;p&gt;
&lt;h2&gt;Comparaciones de tipos de datos sin sentido (cof cof JS)&lt;&#x2F;h2&gt;
&lt;p&gt;O si, quizás una de las cosas que mas me molestan de Javascript estaba presente en Python2 y es la increíble comparación de tipos sin sentido, ya que, el lenguaje nos dejaba comparar una tupla con un str, o una lista con un diccionario, pero no estaba claro si era la longitud, la cantidad de memoria que necesitaba o simplemente la posición en el stack (por poder, podía ser cualquier cosa). las imágenes hablan por si mismas.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# python2
&amp;gt;&amp;gt;&amp;gt; (1, 2) &amp;gt; &amp;quot;123&amp;quot;
True

&amp;gt;&amp;gt;&amp;gt; [1, 2] &amp;gt; &amp;quot;123&amp;quot;

&amp;gt;&amp;gt;&amp;gt; {1, 2, 3} &amp;gt;= 666
True

&amp;gt;&amp;gt;&amp;gt; 123 &amp;gt; &amp;quot;¿Esto tiene algún sentido? .-.&amp;quot;
           ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
           ╚═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═ Pregunta
╔═╦═╦════════════════ Respuesta
v v v
False

# Python3
&amp;gt;&amp;gt;&amp;gt; (1, 2) &amp;gt; &amp;quot;123&amp;quot;
---------------------------------------------------------------------------
...
TypeError: &#x27;&amp;gt;&#x27; not supported between instances of &#x27;tuple&#x27; and &#x27;str&#x27;

&amp;gt;&amp;gt;&amp;gt; [1, 2] &amp;gt; &amp;quot;123&amp;quot;
---------------------------------------------------------------------------
...
TypeError: &#x27;&amp;gt;&#x27; not supported between instances of &#x27;list&#x27; and &#x27;str&#x27;

&amp;gt;&amp;gt;&amp;gt; &amp;quot;foo&amp;quot; &amp;gt; {&amp;quot;a&amp;quot;: 11, &amp;quot;b&amp;quot;: lambda x: x**2}
---------------------------------------------------------------------------
...
TypeError: &#x27;&amp;gt;&#x27; not supported between instances of &#x27;str&#x27; and &#x27;dict&#x27;

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Aquí ni pondré anotaciones en el código, creo que es bastante autodescriptivo... una autentica masacre a la razón.&lt;&#x2F;p&gt;
&lt;h2&gt;print - función vs palabra reservada&lt;&#x2F;h2&gt;
&lt;p&gt;La función &lt;code&gt;print&lt;&#x2F;code&gt; es quizás una de mas cosas que primero nos enseñamos a usar al momento de estar aprendiendo a utilizar Python. Y digo función solo refiriéndome a a Python3, ya que en Python2 &lt;code&gt;print&lt;&#x2F;code&gt; era en realidad una palabra reservada, esto a priori debería de ser muy irrelevante para muchas personas, pero implicaba toparse con pared tarde o temprano ya que las funciones otorgan características interesantes de las cuales Python2 se estaba perdiendo, tal como elegir con que caractér terminará nuestra impresión, o con que caractér unirá el conjunto de datos que le arrojemos a la función.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# Python2
&amp;gt;&amp;gt;&amp;gt; print 123
         ^   ^
         ╚═══╩═ Al ser una palabra reservada no necesita paréntesis
123

&amp;gt;&amp;gt;&amp;gt; print &amp;quot;CodigoComentado&amp;quot;
CodigoComentado

&amp;gt;&amp;gt;&amp;gt; print(&amp;quot;123&amp;quot;, &amp;quot;456&amp;quot;, sep=&amp;quot;&amp;quot;)
         ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
         ╚═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═ Si se intenta utilizar como función, dará error
  File &amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;, line 1
    print(&amp;quot;123&amp;quot;, &amp;quot;456&amp;quot;, sep=&amp;quot;&amp;quot;)

from __future__ import print_function
                       ^ ^ ^ ^ ^ ^ ^
                       ╚═╩═╩═╩═╩═╩═╩═ Se puede importar la función &amp;quot;print&amp;quot; moderna

&amp;gt;&amp;gt;&amp;gt; print(&amp;quot;123&amp;quot;, &amp;quot;456&amp;quot;, sep=&amp;quot;&amp;quot;)
123456

&amp;gt;&amp;gt;&amp;gt; print &amp;quot;Hola mundo&amp;quot;
         ^            ^
         ╚════════════╩═ Pero al hacerlo ya no podremos utilizar la sintaxis anterior
  File &amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;, line 1
    print 123

# Python3
&amp;gt;&amp;gt;&amp;gt; print(123)
123

&amp;gt;&amp;gt;&amp;gt; print(&amp;quot;CodigoComentado&amp;quot;)
Codigocomentado

&amp;gt;&amp;gt;&amp;gt; print(&amp;quot;Codigo&amp;quot;, &amp;quot;Comentado&amp;quot;, sep=&amp;quot;-&amp;quot;, end=&amp;quot;\n=============\n&amp;quot;)
                                 ^ ^      ^ ^
                                 ╚═╩══════╩═╩═ &amp;quot;print&amp;quot; al ser una función por defecto,
                                               se le pueden pasar parámetros como a cualquier otra función
Codigo-Comentado
=============
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;El equipo de Python incluyó en Python2 un parche como paquete llamado &lt;code&gt;__future__&lt;&#x2F;code&gt; en el que incluian un montón de caracteristicas de Python3 para llevarlas a Python2, entre ellas incluye a &lt;code&gt;print&lt;&#x2F;code&gt; como función. Un movimiento muy inteligente por parte del equipo. A pesar del incoveniente de tener que hacer el import en todos los archivos, era mucho mejor que tener chapuzas con el &lt;code&gt;sys.stdin&lt;&#x2F;code&gt; y nuestra implementación pobre del &lt;code&gt;print&lt;&#x2F;code&gt;, sin contar que además ahora a &lt;code&gt;print&lt;&#x2F;code&gt; la podiamos pasar como parámetro como cualquier otra función.&lt;&#x2F;p&gt;
&lt;h2&gt;Unicode por defecto&lt;&#x2F;h2&gt;
&lt;p&gt;Y ahora el mejor y mi cambio favorito de todos.&lt;br &#x2F;&gt;
¿Quién no llegó a sufrir por intentar meter una tilde al código de Python?, ¿O por leer un archivo que en su interior tuviera una &lt;strong&gt;ñ&lt;&#x2F;strong&gt;? que  yo desde luego si, y es que Python2 no era unicode por defecto, sino que utilizaba el antigüisimo &amp;quot;ascíi&amp;quot; como estándar de codificación. Y es que en la época que Python2 nació, era de la mas común encontrarse con &amp;quot;ascii&amp;quot; por todos lados, pero esa época ya pasó y era necesario para todos que este hermoso lenguaje estuviera adaptado a las necesidad de un mundo globalizado.&lt;br &#x2F;&gt;
Python en realidad nunca se ha llevado demasiado bien con los tipos de datos que tienen que almacenar textos, pero por lo menos en Python3 esta tarea se hizo menos tortuosa teniendo unicamente 2 tipos de datos con tareas muy claras, el &lt;code&gt;str&lt;&#x2F;code&gt; y los &lt;code&gt;bytes&lt;&#x2F;code&gt;, a diferencia de python2 que teníamos el &lt;code&gt;str&lt;&#x2F;code&gt;, el &lt;code&gt;unicode&lt;&#x2F;code&gt; y el &lt;code&gt;bytes&lt;&#x2F;code&gt; que en realidad era solo un apodo cutre para &lt;code&gt;str&lt;&#x2F;code&gt;. ¿Todo muy confuso cierto? y es que en Python si queríamos manipular un &lt;code&gt;str&lt;&#x2F;code&gt; que almacenara caracteres que estaban fuera del dominio de &amp;quot;ascii&amp;quot;, debíamos decodificarlo a &amp;quot;utf-8&amp;quot; para convertirlo de &lt;code&gt;str&lt;&#x2F;code&gt; a &lt;code&gt;unicode&lt;&#x2F;code&gt;, pero aparte se debía poner al inicio de nuestro archivo &lt;code&gt;.py&lt;&#x2F;code&gt; el &lt;em&gt;shebang&lt;&#x2F;em&gt; &lt;code&gt;# -*- coding: utf-8 -*-&lt;&#x2F;code&gt; para así manipular textos no-anglosajones con libertad.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# Python2
# -*- coding: utf-8 -*-
      ^ ^ ^
      ╚═╩═╩═ Shebang indicando que manipularemos caracteres utf-8, no lo olvides
a = &amp;quot;ñ&amp;quot;
&amp;gt;&amp;gt;&amp;gt; a
    ^
    ╚═ La inofensiva &amp;quot;ñ&amp;quot; es en realidad &amp;quot;\xc3\xb1&amp;quot; en ascii.
╔═════════════════════════════════════════╩═╩═╩═╝
v
&#x27;\xc3\xb1&#x27;

&amp;gt;&amp;gt;&amp;gt; type(a)
    ^ ^ ^ ^
    ╚═╩═╩═╩═ La variable es de tipo &amp;quot;str&amp;quot;
       ╔═╦═══════════════════════════╩═╝
       v v
&amp;lt;type &#x27;str&#x27;&amp;gt;

a = a.decode(&amp;quot;utf-8&amp;quot;)
              ^ ^ ^
              ╚═╩═╩═ Se decodifica a &amp;quot;utf-8&amp;quot;

&amp;gt;&amp;gt;&amp;gt; a
    ^
    ╚═ Y ahora la variable si guarda el valor correcto de la &amp;quot;ñ&amp;quot; en unicode
╔═══════════════════════════════════════════════════════════════════╩═╩═╩═╝
v
u&#x27;\xf1&#x27;

&amp;gt;&amp;gt;&amp;gt; type(a)
&amp;lt;type &#x27;unicode&#x27;&amp;gt;
       ^ ^ ^ ^
       ╚═╩═╩═╩═ Otro tipo de dato para los strings con caracteres fuera de &amp;quot;ascii&amp;quot;

&amp;gt;&amp;gt;&amp;gt; bytes == str
True
^ ^
╚═╩═ El tipo &amp;quot;bytes&amp;quot; es solo una broma de mal gusto en python2 ya que es solo
     un alias para el tipo &amp;quot;str&amp;quot;

# Python3
a = &amp;quot;ñ&amp;quot;

&amp;gt;&amp;gt;&amp;gt; a
    ^
    ╚═ La &amp;quot;ñ&amp;quot; es una &amp;quot;ñ&amp;quot;, correcto
 ╔════════════════════╝
 v
&#x27;ñ&#x27;

&amp;gt;&amp;gt;&amp;gt; type(a)
    ^ ^ ^ ^
    ╚═╩═╩═╩═ El tipo de dato es &amp;quot;str&amp;quot;, correcto 
 ╔═╦═════════════════════════════╩═╝
 v v
&#x27;str&#x27;

a = a.encode(&amp;quot;utf-8&amp;quot;)
              ^ ^ ^
              ╚═╩═╩═ Se codifica con el estandar &amp;quot;utf-8&amp;quot; para utilizar a &amp;quot;ascíi&amp;quot;
&amp;gt;&amp;gt;&amp;gt; a
    ^
    ╚═ Valor real en ascíi, correcto 
  ╔═╦═╦═╦════════════╩═╩═╝
  v v v v
b&#x27;\xc3\xb1&#x27;

&amp;gt;&amp;gt;&amp;gt; type(a)
    ^ ^ ^ ^
    ╚═╩═╩═╩═ Ahora el tipo de dato es uno llamado &amp;quot;bytes&amp;quot; que solo almacena
             textos con caracteres en ascii        ║ ║ ║
╔═╦═╦══════════════════════════════════════════════╩═╩═╝
v v v
bytes

&amp;gt;&amp;gt;&amp;gt; bytes == str
False
^ ^ ^
╚═╩═╩═ El tipo &amp;quot;bytes&amp;quot; ahora si es distinto a &amp;quot;str&amp;quot; y no solo una fachada
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como podemos darnos cuenta, en Python2 todo era mucho mas caótico al momento de manipular textos. Por suerte en Python3 todo quedó en solo 2 tipos con funciones bastante claras: El tipo &lt;code&gt;str&lt;&#x2F;code&gt; será el encargado de almacenar todo tipo de textos y es compatible por defecto con el estandar &lt;code&gt;utf-8&lt;&#x2F;code&gt; (utf-8 = emojis[😂, 😅, 🤔, 🔥, 💯, 🦆]) y el tipo &lt;code&gt;bytes&lt;&#x2F;code&gt; será el encargado de almacenar cadenas de textos que solo &lt;strong&gt;deban&lt;&#x2F;strong&gt; almacenar caracteres ascíi, es decir, unicamente binarios.&lt;br &#x2F;&gt;
Como anecdota, recuerdo en cada archivo de Python3 que creaba, debía escribir al inicio:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;try:
    unicode
except:
    unicode = str
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;para que el archivo no tuviera problemas para correr tanto en Python2 como en Python3 y poder utilizar a &lt;code&gt;unicode&lt;&#x2F;code&gt; como tipo de dato para compararlo con &lt;code&gt;isinstance&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2&gt;Conclusión&lt;&#x2F;h2&gt;
&lt;p&gt;Si bien Python2 fue una de las mejores cosas que le pasaron a mi vida en esta década y con la que aprendí realmente a programar y todo lo que envolvía al mundo de las ciencias de la computación, puedo decir con completa convicción... que bueno que vas a morir, no te extrañaré. Hasta nunca, Python2.&lt;&#x2F;p&gt;
&lt;h2&gt;Fuentes&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;howto&#x2F;pyporting.html&quot;&gt;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;howto&#x2F;pyporting.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;python-future.org&#x2F;compatible_idioms.html&quot;&gt;https:&#x2F;&#x2F;python-future.org&#x2F;compatible_idioms.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;sebastianraschka.com&#x2F;Articles&#x2F;2014_python_2_3_key_diff.html&quot;&gt;https:&#x2F;&#x2F;sebastianraschka.com&#x2F;Articles&#x2F;2014_python_2_3_key_diff.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Experiencia personal&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h4&gt;Actualizaciones&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;strong&gt;27-12-2019&lt;&#x2F;strong&gt;: Al parecer el EOL de Python2 no será en Enero, sino &lt;a href=&quot;https:&#x2F;&#x2F;pyfound.blogspot.com&#x2F;2019&#x2F;12&#x2F;python-2-sunset.html&quot;&gt;hasta Abril&lt;&#x2F;a&gt;. creo que quieren hacer coincidir la muerte de Python2 con la PyCon 2020 para hacerle un funeral con todos los asistentes o yo que se... de verdad, déjenlo morir, ya está agonizando. Otro tema diferente a tratar sería ver hasta cuando retirarán a Python2 de los repositorios de la N distribuciones *NIX... Gracias &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;irl_ry&quot;&gt;@irl_ry&lt;&#x2F;a&gt;.
&lt;strong&gt;28-02-2020&lt;&#x2F;strong&gt;: Arreglados algunos typos. Gracias VMS.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
        <title>Manejo de excepciones en Rust</title>
        <pubDate>Fri, 20 Dec 2019 09:05:15 GMT</pubDate>
        <link>https://blog.kirbylife.dev/post/manejo-de-excepciones-en-rust-3</link>
        <guid isPermaLink="true">https://blog.kirbylife.dev/post/3</guid>
        <description>&lt;p&gt;Uno de los temas que mas me ha costado aprender en Rust ha sido la manera de manejar los posibles errores que se puedan tener en tiempo de ejecución ya que, a diferencia de los demás lenguajes que ya había manejado (Python, Ruby o Java) ya que el concepto de &amp;quot;excepción&amp;quot; no existe como tal, sino que todo se trabaja mediante &amp;quot;panic!&amp;quot;, &amp;quot;Option&amp;quot; y &amp;quot;Result&amp;quot; y unos métodos &lt;code&gt;unwrap&lt;&#x2F;code&gt; o &lt;code&gt;unwrap_or&lt;&#x2F;code&gt; y en este post explicaré cada uno de ellos.&lt;&#x2F;p&gt;
&lt;h2&gt;panic!&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;panic!&lt;&#x2F;code&gt; es un macro (no entraré en la definición de macro en este momento, esto será para una futura entrada. Pero para fines prácticos, es algo parecido a una función) que lo que hace es terminar con el hilo principal de ejecución de manera completamente abrupta y es imposible evitar su comportamiento. Así que nuestro objetivo es intentar tener el menor número de ejecuciones de &lt;code&gt;panic!&lt;&#x2F;code&gt;, en la medida de lo posible, y solo ejecutarlo cuando sea 100% necesario.&lt;br &#x2F;&gt;
Es imposible de detener porque a nivel de ensamblador se está mandando llamar a la instrucción &lt;code&gt;ud2&lt;&#x2F;code&gt; la cual genera un código de ejecución inválido y eso detiene en seco el hilo principal.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;&#x2F;&#x2F; Rust
fn main() {
    panic!();
}

; Ensamblador
...
example::main:
    push    rax
    lea     rdi, [rip + .L__unnamed_9]
    lea     rdx, [rip + .L__unnamed_10]
    mov     rax, qword ptr [rip + std::panicking::begin_panic@GOTPCREL]
    mov     esi, 14
    call    rax
    ud2
    ^ ^
    ╚═╩═ Generación de un código inválido ejecución
...

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Su funcionamiento es de lo mas sencillo y tiene un cierto parecido a la función &lt;code&gt;sys.exit&lt;&#x2F;code&gt; de Python, solo que el macro &lt;code&gt;panic!&lt;&#x2F;code&gt; acepta un &lt;code&gt;str&lt;&#x2F;code&gt; como parámetro para indicarle al usuario cual fue el posible error y este es opcional.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;&#x2F;&#x2F; Código
fn main() {
    panic!();
}

&#x2F;&#x2F; Salida
...
thread &#x27;main&#x27; panicked at &#x27;explicit panic&#x27;, src&#x2F;main.rs:2:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.


&#x2F;&#x2F; Código
fn main() {
    panic!(&amp;quot;Error en la función &#x27;main&#x27;&amp;quot;);

}

&#x2F;&#x2F; Salida
...
thread &#x27;main&#x27; panicked at &#x27;Error en la función &#x27;main&#x27;&#x27;, src&#x2F;main.rs:2:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2&gt;Option&lt;&#x2F;h2&gt;
&lt;p&gt;Ahora si comenzamos con el manejo de excepciones como tal.&lt;br &#x2F;&gt;
Option es un &lt;code&gt;Enum&lt;&#x2F;code&gt; (Si no estás familiarizado con el término &amp;quot;Enum&amp;quot; pero si con el término &amp;quot;Tipo&amp;quot;, en tu mente reemplaza uno por otro, para este caso son parecidos) que puede contener 2 posibles valores mutuamente excluyentes, y son &amp;quot;algún valor&amp;quot; o &amp;quot;ninguno&amp;quot;, que traducido a lenguaje Rust sería un &lt;code&gt;Some&lt;&#x2F;code&gt; o un &lt;code&gt;None&lt;&#x2F;code&gt;.&lt;br &#x2F;&gt;
Este &lt;code&gt;Enum&lt;&#x2F;code&gt; se tendrá que usar cuando nuestra función es posible que devuelva un valor o que no devuelva nada.&lt;br &#x2F;&gt;
La declaración en el campo a retornar es muy simple, solo se debe indicar que la función retornará una instancia del Enum &lt;code&gt;Option&lt;&#x2F;code&gt;, un diamante (&lt;code&gt;&amp;lt;&amp;gt;&lt;&#x2F;code&gt;) y dentro del diamante el tipo de datos del posible valor a retornar.&lt;br &#x2F;&gt;
El valor a retornar debe estar &amp;quot;encapsulado&amp;quot; dentro de otro Enum llamado &lt;code&gt;Some&lt;&#x2F;code&gt; y el dato dentro de ella debe ser del mismo tipo que la declarada dentro del diamante en la cabecera de la función.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;fn factorial(n: i32) -&amp;gt; Option&amp;lt;i32&amp;gt; {
                        ^ ^ ^  ^ ^
                        ║ ║ ║  ╚═╩═ Tipo de dato del posible valor a retornar
                        ╚═╩═╩═ Nombre del Enum
    if n &amp;lt; 0 {
        None
        ^ ^
        ╚═╩═ Se retorna el Enum &amp;quot;None&amp;quot; cuando no se retorna un valor
    } else if n == 0 {
        Some(1)
        ^ ^  ^
        ║ ║  ╚═ Valor que se retornará
        ╚═╩═ Enum Some
    } else {
        let mut total = 1;
        for value in 1..n {
            total *= value;
        }
        Some(total)
        ^ ^  ^ ^ ^
        ║ ║  ╚═╩═╩═ Valor que se retornará
        ╚═╩═ Enum Some
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como se puede ver en el ejemplo, la función &lt;code&gt;factorial&lt;&#x2F;code&gt; puede recibir un parámetro del tipo &lt;code&gt;i32&lt;&#x2F;code&gt; y retornará un posible valor cuyo tipo de dato será también &lt;code&gt;i32&lt;&#x2F;code&gt; y dentro de la función se retornan el Enum &lt;code&gt;None&lt;&#x2F;code&gt; en caso de que no se deseé retornar ningún valor, o un Enum &lt;code&gt;Some&lt;&#x2F;code&gt; cuando la función deba retornar un valor. simple, eh? Ahora vamos a ver como lidiar con lo que retorne esa función.&lt;br &#x2F;&gt;
Una de las maneras mas sencillas de trabajar con los Option es con la estructura de control &lt;code&gt;match&lt;&#x2F;code&gt;, poniendo las posibles opciones como criterios de coincidencia dentro.&lt;br &#x2F;&gt;
Aquí un ejemplo:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;fn factorial(n: i32) -&amp;gt; Option&amp;lt;i32&amp;gt; { ... }

fn main() {
    let valor = factorial(10);
                ^ ^ ^ ^ ^ ^ ^
                ╚═╩═╩═╩═╩═╩═╩═ Llamada a la función &amp;quot;factorial&amp;quot;
    match valor {
        Some(1) =&amp;gt; println!(&amp;quot;Posible factorial de 0 o 1&amp;quot;),
        ^ ^  ^
        ║ ║  ╚═ Criterio de coincidencia exacto
        ╚═╩═ Enum Some
        Some(n) =&amp;gt; println!(&amp;quot;El factorial es: {}&amp;quot;, n),
        ^ ^  ^
        ║ ║  ╚═ Criterio de coincidencia con un identificador
        ║ ║     Se puede acceder a el desde el bloque de ejecución
        ╚═╩═ Enum Some
        None =&amp;gt; println!(&amp;quot;No se puede sacar el factorial de un número negativo&amp;quot;),
        ^ ^
        ╚═╩═ Enum None
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como se puede ver en el ejemplo, utilizando &lt;code&gt;match&lt;&#x2F;code&gt; se pueden declarar los posibles criterios de coincidencia, ya sea con valores (&lt;code&gt;Some(1)&lt;&#x2F;code&gt;) para que solo se ejecute el bloque si la función retorna explícitamente un &lt;code&gt;Some&lt;&#x2F;code&gt; con valor &amp;quot;1&amp;quot;. O también con una variable &lt;code&gt;Some(n)&lt;&#x2F;code&gt; y esa variable se puede utilizar dentro del bloque a ejecutar y su Scope no va mas allá. Y por último también se puede hacer la coincidencia con el Enum &lt;code&gt;None&lt;&#x2F;code&gt; cuando la función no deba retornar nada.&lt;br &#x2F;&gt;
Como podemos ver, la utilización del Enum &lt;code&gt;Option&lt;&#x2F;code&gt; es una manera mas elegante de hacer cosas &lt;em&gt;hacky&lt;&#x2F;em&gt; como devolver un &amp;quot;-1&amp;quot; cuando una función puede devolver un valor numérico.&lt;&#x2F;p&gt;
&lt;h2&gt;Result&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;Result&lt;&#x2F;code&gt; es otro Enum que tiene cierto parecido a &lt;code&gt;Option&lt;&#x2F;code&gt; pero con una ligera diferencia y es que se debe retornar un &lt;code&gt;Result&lt;&#x2F;code&gt; cuando la función deba retornar un error por culpa de alguno de los parámetros introducidos. Para indicar que una función retornará un &lt;code&gt;Result&lt;&#x2F;code&gt; se debe colocar en la cabecera de la función el nombre del Enum &lt;code&gt;Result&lt;&#x2F;code&gt;, un diamante y dentro del diamante 2 tipos de datos, el primero corresponderá al tipo de dato de una respuesta exitosa y el segundo corresponderá al tipo de dato de la respuesta errónea.&lt;br &#x2F;&gt;
Los valores a retornar deberán ser &amp;quot;encapsulados&amp;quot; en el Enum &lt;code&gt;Ok&lt;&#x2F;code&gt; y en el Enum &lt;code&gt;Err&lt;&#x2F;code&gt; y debe corresponder al tipo de dato correspondiente al colocado dentro del diamante.&lt;br &#x2F;&gt;
Como por ejemplo:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;fn div(num1: i32, num2: i32) -&amp;gt; Result&amp;lt;f64, &amp;amp;&#x27;static str&amp;gt; {
                                ^ ^ ^  ^ ^  ^ ^ ^ ^ ^ ^
                                ║ ║ ║  ║ ║  ╚═╩═╩═╩═╩═╩═ Tipo de dato del error
                                ║ ║ ║  ╚═╩═ Tipo de datos para el caso de éxito
                                ╚═╩═╩═ Nombre del Enum
    if num2 == 0 {
        Err(&amp;quot;No se puede dividir un número entre 0&amp;quot;)
        ^ ^  ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
        ║ ║  ╚═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═ Valor que retornará como error
        ╚═╩═ Nombre del Enum
    } else {
        Ok(num1 as f64 &#x2F; num2 as f64)
        ^  ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
        ║  ╚═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═ Valor que retornará en caso de éxito
        ╚═ Nombre del Enum
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como se puede ver en el ejemplo, en la cabecera de la función, en la parte donde se especifica que va a retornar, se debe colocar el Enum &lt;code&gt;Result&lt;&#x2F;code&gt; y dentro de un diamante los 2 tipos de datos, en este caso será un &lt;code&gt;f64&lt;&#x2F;code&gt; para los casos de éxito y un &lt;code&gt;&amp;amp;&#x27;static str&lt;&#x2F;code&gt; (str estático) para los casos erróneos y dentro de la propia función se &amp;quot;encapsulan&amp;quot; los posibles valores de retorno en los Enum &lt;code&gt;Ok&lt;&#x2F;code&gt; y &lt;code&gt;Err&lt;&#x2F;code&gt; para los casos de éxito y de error según correspondan.&lt;br &#x2F;&gt;
Ahora, la manera de lidiar con el valor de una función que retorna un &lt;code&gt;Result&lt;&#x2F;code&gt; es muy similar a la manera de tratar con los Enum &lt;code&gt;Option... con un &lt;&#x2F;code&gt;match` y dentro, todas los posibles criterios de coincidencia.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;fn div(num1: i32, num2: i32) -&amp;gt; Result&amp;lt;f64, &amp;amp;&#x27;static str&amp;gt; { ... }

fn main() {
    let valor = div(10, 2);
                ^ ^ ^ ^ ^
                ╚═╩═╩═╩═╩═ Llamada a la función &amp;quot;div&amp;quot;
    match valor {
        Ok(3.14159) =&amp;gt; println!(&amp;quot;wow, tu división retorna el número pi&amp;quot;),
        ^  ^ ^ ^ ^
        ║  ╚═╩═╩═╩═ Criterio de coincidencia exacto
        ╚═ Enum Ok
        Ok(n) =&amp;gt; println!(&amp;quot;El resultado es: {}&amp;quot;, n),
        ^  ^
        ║  ╚═ Criterio de coincidencia con un identificador
        ║     Se puede acceder a el desde el bloque de ejecución
        ╚═ Enum Ok
        Err(e) =&amp;gt; println!(&amp;quot;{}&amp;quot;, e),
        ^ ^ ^
        ║ ║ ╚═ Criterio de coincidencia con un identificador
        ║ ║    Se puede acceder a el desde el bloque de ejecución
        ╚═╩═ Enum Err
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Se manda a comparar el valor retornado por la función utilizando un &lt;code&gt;match&lt;&#x2F;code&gt; y los criterios deben ser los Enum &lt;code&gt;Ok&lt;&#x2F;code&gt; con un valor exacto, con una variable que se puede utilizar dentro del bloque de ejecución o el Enum &lt;code&gt;Err&lt;&#x2F;code&gt; que sigue las mismas reglas, se puede poner un valor exacto o una variable para utilizar dentro del bloque de ejecución.&lt;&#x2F;p&gt;
&lt;h4&gt;pro-tip 1:&lt;&#x2F;h4&gt;
&lt;p&gt;Si por algún motivo no te interesa el valor retornado dentro del &lt;code&gt;Ok&lt;&#x2F;code&gt;, &lt;code&gt;Err&lt;&#x2F;code&gt; o &lt;code&gt;Some&lt;&#x2F;code&gt; puede poner como identificador un &amp;quot;&lt;code&gt;_&lt;&#x2F;code&gt;&amp;quot;, así el compilador ignorará ese valor por completo y será un criterio de coincidencia válido, como por ejemplo:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;fn div(num1: i32, num2: i32) -&amp;gt; Result&amp;lt;f64, &amp;amp;&#x27;static str&amp;gt; { ... }

fn main() {
    match div(10, 0) {
        Ok(_) =&amp;gt; println!(&amp;quot;La división se ejecutó correctamente&amp;quot;),
        ^  ^
        ║  ╚═ Criterio de coincidencia con &amp;quot;_&amp;quot;
        ║     El identificador al ser un &amp;quot;_&amp;quot;, el valor nunca es guardado en memoria y es inaccesible
        ╚═ Enum Ok
        Err(_) =&amp;gt; println!(&amp;quot;Error ejecutando la división&amp;quot;),
        ^ ^ ^
        ║ ║ ╚═ Criterio de coincidencia con &amp;quot;_&amp;quot;
        ║ ║    El identificador al ser un &amp;quot;_&amp;quot;, el valor nunca es guardado en memoria y es inaccesible
        ╚═╩═ Enum Err
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4&gt;pro-tip 2:&lt;&#x2F;h4&gt;
&lt;p&gt;Para mantener mas elegante nuestro código, es recomendable no retornar str&#x27;s en los mensajes de error de los &lt;code&gt;Result&lt;&#x2F;code&gt;, es mucho mejor implementar Enum&#x27;s propios para cada posible error.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;enum MathError {
    ZeroDivision,
    Domain
}

fn div(num1: i32, num2: i32) -&amp;gt; Result&amp;lt;f64, MathError&amp;gt; {
                                            ^ ^ ^ ^ ^
                                            ╚═╩═╩═╩═╩═ Nombre de la estructura que contiene los posibles errores
    if num2 == 0 {
        Err(MathError::ZeroDivision)
            ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
            ╚═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═ Valor dentro del Enum que contiene nuestro error deseado
    } else {
        Ok(num1 as f64 &#x2F; num2 as f64)
    }
}

fn main() {
    match div(10, 0) {
        Ok(n) =&amp;gt; println!(&amp;quot;El resultado es: {}&amp;quot;, n),
        Err(MathError::ZeroDivision) =&amp;gt; println!(&amp;quot;Oh no!, División entre 0&amp;quot;),
            ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
            ╚═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═ Criterio de coincidencia más explícito para los errores
        _ =&amp;gt; println!(&amp;quot;Error desconocido&amp;quot;),
        ^
        ╚═ Como nuestro Enum de errores tiene mas de un valor aparte del &amp;quot;ZeroDivision&amp;quot;,
           se debe declarar una opción por defecto como criterio de coincidencia 
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2&gt;Los métodos unwrap&lt;&#x2F;h2&gt;
&lt;p&gt;Los &lt;code&gt;match&lt;&#x2F;code&gt; son maneras muy elegantes para manejar los &lt;code&gt;Option&lt;&#x2F;code&gt; o los &lt;code&gt;Result&lt;&#x2F;code&gt;, pero en algunas ocasiones quizás querremos lidiar con ellos de una manera menos verbosa y justo para ese motivo existe la gama de métodos &lt;code&gt;unwrap&lt;&#x2F;code&gt;.&lt;br &#x2F;&gt;
El comportamiento de los métodos &lt;code&gt;unwarp&lt;&#x2F;code&gt; dependerá si el &lt;code&gt;Option&lt;&#x2F;code&gt; o el &lt;code&gt;Result&lt;&#x2F;code&gt; tiene un resultado exitoso y son los siguientes:&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Nombre del método&lt;&#x2F;th&gt;
&lt;th&gt;Comportamiento&lt;&#x2F;th&gt;
&lt;th&gt;Ejemplo&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unwrap&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;Intenta obtener el valor encapsulado dentro de un &lt;code&gt;Some&lt;&#x2F;code&gt; o un &lt;code&gt;Ok&lt;&#x2F;code&gt;. Sí la función no devolvió alguno de ellos y se ejecutará el macro &lt;code&gt;panic!&lt;&#x2F;code&gt;. No recibe parámetros.&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;let valor = factorial(1).unwrap(0);&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unwrap_or&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;Intenta obtener el valor encapsulado dentro de un &lt;code&gt;Some&lt;&#x2F;code&gt; o un  &lt;code&gt;Ok&lt;&#x2F;code&gt;. Si la función devolvió un &lt;code&gt;None&lt;&#x2F;code&gt; o un &lt;code&gt;Err&lt;&#x2F;code&gt;, la función devolverá el valor pasado como parámetro que deberá ser del mismo tipo de dato declarado en el diamante para el caso de éxito. Recibe como parámetro el valor a devolver.&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;let valor = div(10, 0).unwrap_or(0f64);&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unwrap_or_else&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;Intenta obtener el valor encapsulado dentro de un &lt;code&gt;Some&lt;&#x2F;code&gt; o un &lt;code&gt;Ok&lt;&#x2F;code&gt;. En caso de que la función haya devuelto un &lt;code&gt;None&lt;&#x2F;code&gt; o un &lt;code&gt;Err&lt;&#x2F;code&gt; la función retornará el valor que devuelva el &lt;code&gt;closure&lt;&#x2F;code&gt; pasado como parámetro. Recibe como parámetro el &lt;code&gt;closue&lt;&#x2F;code&gt; a ejecutar, en caso de que la función retorne un &lt;code&gt;Result&lt;&#x2F;code&gt;, el closure deberá tener un argumento y si retorna un &lt;code&gt;Option&lt;&#x2F;code&gt; el closure no debe tener argumentos.&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;let valor = factorial(-1).unwrap_or_else(|| 0); let valor = div(10, 0).unwrap_or_else(|_| 0f64);&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unwrap_or_default&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;Intentará obtener el valor encapsulado dentro de un &lt;code&gt;Some&lt;&#x2F;code&gt; o un &lt;code&gt;Ok&lt;&#x2F;code&gt;. Si el valor fue ninguno o un error, el método retornará los valores por defecto de cada tipo de dato. No recibe ningún parámetro.&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;let valor = factorial(0).unwrap_or_default();&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;expect&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;Intenta obtener el valor encapsulado dentro de un &lt;code&gt;Some&lt;&#x2F;code&gt; o un &lt;code&gt;Ok&lt;&#x2F;code&gt;. Si la función retornó un &lt;code&gt;None&lt;&#x2F;code&gt; o un &lt;code&gt;Err&lt;&#x2F;code&gt;, el método mandará a llamar al macro &lt;code&gt;panic!&lt;&#x2F;code&gt; con el mensaje que se le pase como parámetro. Recibe un parámetro del tipo &amp;quot;str&amp;quot;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;let valor = div(0, 0).expect(&amp;quot;Oh no&amp;quot;);&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Espero que les haya gustado y que les haya servido la información aquí proporcionada. Pueden comentarme mediante mi twitter &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;kirbylife&quot;&gt;@kirbylife&lt;&#x2F;a&gt; si algo de lo aquí leído es incorrecto o confuso. Con gusto los leeré :).&lt;&#x2F;p&gt;
&lt;h2&gt;Fuentes&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;result&#x2F;enum.Result.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;result&#x2F;enum.Result.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;option&#x2F;enum.Option.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;option&#x2F;enum.Option.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;&quot;&gt;The Rust programming language. Por Steve Klabnik y Carol Nichols.&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;rust.godbolt.org&quot;&gt;https:&#x2F;&#x2F;rust.godbolt.org&lt;&#x2F;a&gt; (para traducción de código Rust a ensamblador).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;learning-rust.github.io&#x2F;docs&#x2F;e3.option_and_result.html&quot;&gt;https:&#x2F;&#x2F;learning-rust.github.io&#x2F;docs&#x2F;e3.option_and_result.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h4&gt;Actualizaciones&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;strong&gt;23-12-2019&lt;&#x2F;strong&gt;: Arreglados algunos typos. Gracias Nacho.&lt;br &#x2F;&gt;
&lt;strong&gt;28-02-2020&lt;&#x2F;strong&gt;: Arreglados algunos typos. Gracias VMS.&lt;br &#x2F;&gt;
&lt;strong&gt;17-06-2020&lt;&#x2F;strong&gt;: Arreglado un typo. Gracias &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;marc2332&quot;&gt;Marc&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
        <title>Nuevas características de python 3.8</title>
        <pubDate>Fri, 13 Dec 2019 08:10:07 GMT</pubDate>
        <link>https://blog.kirbylife.dev/post/nuevas-caracteristicas-de-python-3-8-2</link>
        <guid isPermaLink="true">https://blog.kirbylife.dev/post/2</guid>
        <description>&lt;p&gt;Hace un par de meses el equipo que está detrás de Python anunció una nueva subversión de Python3 llamada python3.8.
En esta nueva subversión de incluyeron unas cuantas características y algunas de ellas me parecieron especialmente interesantes entre las cuales se encuentran:&lt;&#x2F;p&gt;
&lt;h2&gt;1) Expresiones de asignación&lt;&#x2F;h2&gt;
&lt;p&gt;Esta fue, quizás, la característica que mas me hace ilusión ya que por fin tenemos una manera &lt;em&gt;elegante&lt;&#x2F;em&gt; de poder la operación la cual denomino &amp;quot;asignar y retornar&amp;quot;.
En una gran cantidad de lenguajes de scripting (PHP, JS, Ruby por poner un ejemplo) es común ver que la operación de asignación también retorna el valor que estamos asignando y en Python esto no ocurría.
Para ello, el equipo de Python decidió implementar un nuevo operador &lt;code&gt;:=&lt;&#x2F;code&gt;... Es idéntico al operador al operador de asignación de Go, pero su funcionamiento es muy distinto.
Observen que es lo que sucede cuando usamos el clásico &lt;code&gt;=&lt;&#x2F;code&gt; y el nuevo &lt;code&gt;:=&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; var1 = 10
    &amp;lt;══ La operación de asignación no retorna nada

&amp;gt; (var2 := 20)
        ^
        ╚══ Nuevo operador
20  &amp;lt;══ La nueva expresión de asignación retorna el valor asignado a la variable &amp;quot;var2&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como se puede ver en el ejemplo, su funcionamiento, en principio, es idéntico al operador de asignación de toda la vida, lo único que cambió es que ahora como resultado de la operación, el interprete retorna lo asignado en la variable.
Ahora que ya está todo claro seguramente se preguntarán &amp;quot;Y esto ¿qué uso práctico tiene?&amp;quot;, bueno, a juzgar por la documentación oficial[2] su implementación se debe a que ahorita en el lenguaje se escriben demasiados &lt;code&gt;while True:&lt;&#x2F;code&gt; e &lt;em&gt;if&#x27;s&lt;&#x2F;em&gt; con una variable justo arriba.
. Aquí algún ejemplo práctico que se me ocurre a mi y otro que se puede encontrar en la documentación:&lt;&#x2F;p&gt;
&lt;h3&gt;meter números a una lista y parar cuando se introduzca un 0&lt;&#x2F;h3&gt;
&lt;pre&gt;&lt;code&gt;# Ahora
lista = []
while x := int(input()):
      ^ ^
      ║ ╚══ Nuevo operador de asignación
      ╚════ Variable a la cual se le asignará el valor
    lista.append(x)

# Antes
lista = []
while True:
    x = int(input())
    if not x:
        break
    lista.append(x)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;En este primer ejercicio se puede ver claramente como se redujo el número de lineas pasando de 6 a unicamente 3 mejorando el rendimiento pues solo es necesario hacer una comparación (la del &lt;code&gt;while&lt;&#x2F;code&gt;) en lugar de 2 (la de &lt;code&gt;while&lt;&#x2F;code&gt; que siempre iba a dar &lt;code&gt;True&lt;&#x2F;code&gt; y la de &lt;code&gt;if&lt;&#x2F;code&gt; para romper el ciclo) sin perder legibilidad en el código.&lt;&#x2F;p&gt;
&lt;h3&gt;Sumar zona horaria (Ejemplo extraído de la documentación oficial)&lt;&#x2F;h3&gt;
&lt;pre&gt;&lt;code&gt;# Ahora
s = _format_time(self._hour, self._minute,
                 self._second, self._microsecond,
                 timespec)
if tz := self._tzstr():
   ^  ^
   ║  ╚══ Nuevo operador de asignación
   ╚════ Variable a la cual se le asignará el valor
    s += tz
return s

# Antes
s = _format_time(self._hour, self._minute,
                 self._second, self._microsecond,
                 timespec)
tz = self._tzstr()
if tz:
    s += tz
return s
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;En este otro ejemplo extraído de la documentación oficial se puede ver como se evita declarar variables antes del &lt;code&gt;if&lt;&#x2F;code&gt; para reducir la cantidad de lineas sin tener perjudicar a la legibilidad del código.&lt;&#x2F;p&gt;
&lt;h2&gt;2) Argumentos unicamente posicionales&lt;&#x2F;h2&gt;
&lt;p&gt;Este cambio, a diferencia del anterior, lo veo como una mejoría unicamente a nivel de &lt;em&gt;&amp;quot;belleza&amp;quot;&lt;&#x2F;em&gt; del código, ya que esto no se verá reflejado al momento de que el código esté siendo interpretado.
El cambio consiste en que ahora se podrá hacer que las funciones y métodos acepten los argumentos obligatoriamente en orden... ¿Esto con qué fin? unicamente estético a nivel de código y eso conlleve en una tortura mas ligera al momento de darle mantenimiento a código legacy (En el futuro claro, ahorita el código legacy seguramente estará escrito en python2).
Para ello unicamente tendremos que colocar un &lt;code&gt;&#x2F;&lt;&#x2F;code&gt; después de los argumentos que tendrán un orden obligatorio, aquí unos ejemplos:&lt;&#x2F;p&gt;
&lt;h3&gt;Función para elevar un número al cuadrado&lt;&#x2F;h3&gt;
&lt;pre&gt;&lt;code&gt;def pow(num, exp, &#x2F;):
                  ^
                  ╚══ Parámetro &amp;quot;&#x2F;&amp;quot; para indicar argumentos
                      Unicamente posicionales
    return num ** exp

# Uso correcto
&amp;gt;&amp;gt;&amp;gt; pow(5, 2)
25

&amp;gt;&amp;gt;&amp;gt; pow(exp=2, num=5)
Traceback (most recent call last):
...
TypeError: pow() got some positional-only arguments passed as keyword arguments: &#x27;num, exp&#x27;

&amp;gt;&amp;gt;&amp;gt; pow(num=5, exp=2)
Traceback (most recent call last):
...
TypeError: pow() got some positional-only arguments passed as keyword arguments: &#x27;num, exp&#x27;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como se puede apreciar en el ejemplo, arroja un error cuando mandamos llamar a la función utilizando los nombres de los parámetros, aunque intentemos pasarlos en el orden que fueron declarados en un principio.
Pero aquí no acaba la cosa, ya que posterior a la &lt;code&gt;&#x2F;&lt;&#x2F;code&gt; podremos declarar mas parámetros y estos volverán a tener el comportamiento habitual como en versiones anteriores de Python.&lt;&#x2F;p&gt;
&lt;h3&gt;Sacar la edad de una persona y saludarla&lt;&#x2F;h3&gt;
&lt;pre&gt;&lt;code&gt;from datetime import datetime
def say_hello(year, month, day, &#x2F;, first_name, last_name):
                                ^
                                ╚══ Parámetro &amp;quot;&#x2F;&amp;quot; para indicar argumentos
                                    Unicamente posicionales
    date = datetime(year, month, day)
    diference = datetime.now() - date
    years = int(diference.days &#x2F;&#x2F; 365.25)
    print(f&amp;quot;hello {first_name} {last_name} you&#x27;re {years} years old&amp;quot;)

&amp;gt;&amp;gt;&amp;gt; say_hello(1810, 11, 23, &amp;quot;foo&amp;quot;, &amp;quot;bar&amp;quot;)
hello foo bar you&#x27;re 208 years old

&amp;gt;&amp;gt;&amp;gt; say_hello(1910, 5, 4, last_name=&amp;quot;foo&amp;quot;, first_name=&amp;quot;bar&amp;quot;)
hello bar foo you&#x27;re 109 years old

&amp;gt;&amp;gt; say_hello(day=28, month=7, year=2013, first_name=&amp;quot;python&amp;quot;, last_name=&amp;quot;3.8&amp;quot;)
Traceback (most recent call last):
...
TypeError: say_hello() got some positional-only arguments passed as keyword arguments: &#x27;year, month, day&#x27;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como nos podemos dar cuenta, en la cabecera de la función los parámetros &lt;code&gt;first_name&lt;&#x2F;code&gt; y &lt;code&gt;last_name&lt;&#x2F;code&gt; están después de la &lt;code&gt;&#x2F;&lt;&#x2F;code&gt; y solo por eso ya se pueden utilizar de la manera antigua pudiendo asignarlos utilizando su nombre (ver caso 2).&lt;&#x2F;p&gt;
&lt;h2&gt;3) Especificador &amp;quot;=&amp;quot; en f-strings&lt;&#x2F;h2&gt;
&lt;p&gt;Este cambio me parece buenísimo y es muy fácil de explicar y rápido de entender, se trata de que ahora las operaciones que hagamos dentro de un f-string las podremos poner dentro del propio f-string sin tener que escribir 2 veces la misma operación.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# Ahora
&amp;gt;&amp;gt;&amp;gt; print(f&amp;quot;{10+5=}&amp;quot;)
                 ^
                 ╚══ Signo &amp;quot;=&amp;quot; para indicar que es una
                     expresión de debugeo
10+5=15

&amp;gt;&amp;gt;&amp;gt; print(f&amp;quot;{10+5-22&#x2F;34**55&#x2F;&#x2F;5=}&amp;quot;)
                              ^
                              ╚══ Signo &amp;quot;=&amp;quot; para indicar que es una
                              expresión de debugeo
10+5-22&#x2F;34**55&#x2F;&#x2F;5=15.0

&amp;gt;&amp;gt;&amp;gt; print(f&amp;quot;{(a:=10)=} \t {10*a=}&amp;quot;)
                    ^          ^
                    ╚══════════╩══ Signo &amp;quot;=&amp;quot; para indicar que es una
                                   expresión de debugeo
(a:=10)=10 	 10*a=100

# Antes
&amp;gt;&amp;gt;&amp;gt; print(f&amp;quot;10+5={10+5}&amp;quot;)
10+5=15

&amp;gt;&amp;gt;&amp;gt; print(f&amp;quot;10+5-22&#x2F;34**55&#x2F;&#x2F;5={10+5-22&#x2F;34**55&#x2F;&#x2F;5}&amp;quot;)
10+5-22&#x2F;34**55&#x2F;&#x2F;5=15.0

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como pueden apreciar en el caso 3, se pueden declarar variables &lt;em&gt;DENTRO&lt;&#x2F;em&gt; del propio f-string gracias a nuestro precioso operador &lt;code&gt;:=&lt;&#x2F;code&gt; y se visualizará exactamente como lo hayamos escrito. Para debugear con print&#x27;s esto será una genialidad.&lt;&#x2F;p&gt;
&lt;h2&gt;4) Nuevas validaciones para tipos de datos&lt;&#x2F;h2&gt;
&lt;p&gt;Como bien sabemos, en Python existen una buena cantidad de herramientas para &lt;em&gt;ESPECIFICAR&lt;&#x2F;em&gt; (mas no validar) algunas pautas que el programador debe seguir al momento de estar de estar escribiendo su código. Estas pautas realmente no le dicen nada al interprete cuando nuestro programa se está ejecutando, solo sirve para que el linter (si lo soporta) nos arroje ciertos errores cuando estamos escribiendo código.
En python3.8 incluyeron algunas nuevas características para marcar mas pautas y son las siguientes:&lt;&#x2F;p&gt;
&lt;h3&gt;4.1) Calificador &amp;quot;final&amp;quot;&lt;&#x2F;h3&gt;
&lt;p&gt;El paquete &lt;code&gt;typing&lt;&#x2F;code&gt; incluye una gran cantidad de herramientas para escribir unas pautas mas &amp;quot;estrictas&amp;quot; (estrictas entre comillas, ya que realmente al interprete le dan completamente igual). Entre esas características, en Python3.8 incluyeron un nuevo calificador (que así es como le llaman a las pautas) que permite decir de cual clase NO deberíamos heredar, cual método NO deberíamos sobrescribir, cual método SI deberíamos sobrescribir y cuales atributos NO deberíamos modificar.
Para ello incluye un decorador y un tipo de dato, su uso es el siguiente:
Las clases de las cuales no deberíamos heredar ahora se deberían decorar con &lt;code&gt;@final&lt;&#x2F;code&gt; como en el siguiente ejemplo:&lt;&#x2F;p&gt;
&lt;h4&gt;Decorando clases&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;from typing import final

@final &amp;lt;══ Decorando la clase con el decorador &amp;quot;final&amp;quot; del paquete &amp;quot;typing&amp;quot;
class Base:
    pass

class A(Base):
        ^ ^
        ╚═╩═ Utilizando la clase &amp;quot;Base&amp;quot; para heredar de ellas
    pass
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como he repetido anteriormente, esto solo es una validación a nivel de linter y para una auto-documentación mas explícita ya que al ejecutar el código anteriormente descrito, este no presentará error alguno a nivel de interprete. El linter debería arrojar un error.&lt;&#x2F;p&gt;
&lt;h4&gt;Decorando métodos&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;from typing import final

class Base:
    @final &amp;lt;══ Decorando el método con el decorador &amp;quot;final&amp;quot; del paquete &amp;quot;typing&amp;quot;
    def method_1(self):
        pass

class A(Base):
    def method_1(self):
        ^ ^ ^ ^
        ╚═╩═╩═╩══ Reescribiendo un método decorado con &amp;quot;final&amp;quot;
                  en la clase padre
        pass
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Esto también debería arrojar un error al momento de ser inspeccionado por un linter. Pero repito, el interprete nos dejará sobrescribir estos métodos sin ningún mensaje de error.&lt;&#x2F;p&gt;
&lt;h4&gt;Declarando atributos&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;from typing import Final
                   ^ ^ ^
                   ╚═╩═╩═ Importando el tipo &amp;quot;Final&amp;quot; (Con &amp;quot;F&amp;quot; mayúscula)
                          del paquete &amp;quot;typing&amp;quot;

var_1: Final = &amp;quot;Hello world&amp;quot;
       ^ ^ ^
       ╚═╩═╩╦═╦═╦══ Declarando las variables de tipo &amp;quot;Final&amp;quot;
            ║ ║ ║
class Base: v v v
    attr_1: Final = 10

var_1 = &amp;quot;Good bye world&amp;quot; &amp;lt;═╦══ Intentado asignarle nuevos valores a variables
Base.attr_1 = -1         &amp;lt;═╝   del tipo &amp;quot;Final&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;También esto puede aplicar para variables sueltas del programa o para atributos internos de una clase. Al declararlas del tipo &lt;code&gt;Final&lt;&#x2F;code&gt; el linter debería arrojar un error cuando se intenten asignar nuevos valores.&lt;&#x2F;p&gt;
&lt;p&gt;Si quieres saber mas sobre decoradores puedes pasarte por mi &lt;a href=&quot;https:&#x2F;&#x2F;codigocomentado.herokuapp.com&#x2F;post&#x2F;decoradores-de-python-una-herramienta-poderosa-y-desconocida-1&quot;&gt;entrada anterior&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2&gt;Conclusiones&lt;&#x2F;h2&gt;
&lt;p&gt;Estos no fueron todos los cambios, como dije al principio, solo fueron los que me parecieron mas útiles y que se podían explicar de una manera relativamente sencilla. Los otros eran mas cambios relacionados al multithreading y a la carga de información utilizando CPython... No digo que no sean irrelevantes esos cambios, solo que me quise centrar mas en cambios relacionados a cosas de sintaxis.
Sin duda alguna mis cambios favoritos en esta versión fue el operador de asignación y retorno ya que, en mi opinión, hará posible la escritura de un código mas limpio.
Puedes consultar todos los cambios en la sección de &amp;quot;Fuentes&amp;quot;. Muchas gracias por leerme otro día mas.&lt;&#x2F;p&gt;
&lt;h2&gt;Fuentes&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.python.org&#x2F;downloads&#x2F;release&#x2F;python-380&#x2F;&quot;&gt;Changelog&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.python.org&#x2F;dev&#x2F;peps&#x2F;pep-0572&#x2F;#examples-from-the-python-standard-library&quot;&gt;Ejemplos de implementación de la expresión de asignación&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3.8&#x2F;whatsnew&#x2F;3.8.html&quot;&gt;Artículo oficial de Python sobre esta versión&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</description>
      </item>
      <item>
        <title>Decoradores de python: una herramienta poderosa y desconocida</title>
        <pubDate>Fri, 22 Nov 2019 21:54:07 GMT</pubDate>
        <link>https://blog.kirbylife.dev/post/decoradores-de-python-una-herramienta-poderosa-y-desconocida-1</link>
        <guid isPermaLink="true">https://blog.kirbylife.dev/post/1</guid>
        <description>&lt;p&gt;Los decoradores en Python son una gran herramienta que hacen que el código sea más fácil de mantener y que desgraciadamente no se le suele dar tanta importancia. Son la manera en la cual puedes reutilizar un mismo código para alterar el funcionamiento de funciones cuando este se vuelve muy repetitivo.&lt;&#x2F;p&gt;
&lt;p&gt;Si lo quieres ver de esta forma, los decoradores son a las funciones lo que las interfaces son a los objetos. O si vienes de un ambiente mas &lt;em&gt;rubyista&lt;&#x2F;em&gt;, los decoradores son algo parecido a los bloques.&lt;&#x2F;p&gt;
&lt;p&gt;Los decoradores son básicamente la &amp;quot;trifecta&amp;quot; de las funciones ya que:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Son una función&lt;&#x2F;li&gt;
&lt;li&gt;Reciben una función&lt;&#x2F;li&gt;
&lt;li&gt;Retornan una función&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Los habrás visto y&#x2F;o usado seguramente si haz trabajado alguna vez con el framework web &lt;a href=&quot;https:&#x2F;&#x2F;www.palletsprojects.com&#x2F;p&#x2F;flask&#x2F;&quot;&gt;Flask&lt;&#x2F;a&gt; ya que es la manera recomendada que tiene este framework para crear rutas para tu servicio web.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;@app.route(&amp;quot;&#x2F;&amp;quot;)
def index():
    pass
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2&gt;Sintaxis de una decorador&lt;&#x2F;h2&gt;
&lt;h4&gt;Indicador de &amp;quot;decoración&amp;quot;&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;@decorador(params)
^
╚════ Caractér para indicar que decoraremos una función
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;La manera mas sencilla para detectar que una función está siendo &amp;quot;decorada&amp;quot; es por que justo arriba de su cabecera se encuentra algo parecido a una llamada a una función pero que comienza con &lt;code&gt;@&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4&gt;Nombre del decorador&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;@decorador(params)
 ^ ^ ^ ^ ^
 ╚═╩═╩═╩═╩══ Nombre del decorador
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Esto es bastante simple y no varía en lo absoluto con una llamada típica a una función... para saber cual decorador estamos usando, se usará su nombre.&lt;&#x2F;p&gt;
&lt;h4&gt;Argumentos del decorador&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;@decorador(params)
           ^ ^ ^
           ╚═╩═╩══ Parámetros para modificar el decorador (opcional)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Tampoco esto varia demasiado con las funciones de toda la vida. Si el decorador acepta parámetros, se le pasan colocándolos dentro de unos paréntesis.&lt;&#x2F;p&gt;
&lt;h4&gt;Cabecera de la función a decorar&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;@decorador(params)
def index():
^ ^ ^ ^ ^ ^
╚═╩═╩═╩═╩═╩══ Función a decorar
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Inmediatamente debajo del decorador se debe colocar la función que será decorada.&lt;&#x2F;p&gt;
&lt;h2&gt;Decoradores sin parámetros: Funcionamiento interno&lt;&#x2F;h2&gt;
&lt;p&gt;Como mencioné al inicio del artículo, los decoradores no son otra cosa más que simples funciones.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;def decorador(f):
    def wrap(*args, **kwargs):
        return f(*args, **kwargs)
    return wrap

@decorador
def funcion(x):
    return x**2
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Esta es el decorador mas simple que se puede escribir... YYYYY no hace absolutamente nada, pero nos puede dar una idea de como están compuestos.&lt;&#x2F;p&gt;
&lt;h3&gt;Estructura del decorador&lt;&#x2F;h3&gt;
&lt;h4&gt;Nombre del decorador&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;def decorador(f):
    ^ ^ ^ ^ ^
    ╚═╩═╩═╩═╩══ Nombre del decorador
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Es el nombre que recibirá el decorador y que luego utilizaremos con la sintaxis de &lt;code&gt;@nombre&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4&gt;Función a decorar&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;def decorador(f):
              ^
              ║
              ╚═╦═══ Función a decorar siendo
@decorador      ║    capturada por el decorador
def funcion(x):&amp;lt;╝
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Este parámetro que está recibiendo nuestra función&#x2F;decorador es una referencia a la función que posteriormente se colocará debajo del decorador.&lt;&#x2F;p&gt;
&lt;h4&gt;Función interna del decorador&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;def decorador(f):
    def wrap(*args, **kwargs):
        ^ ^
        ╚═╩════ Nombre provisional para uso interno del decorador
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Esta será la función que retornaremos, &lt;code&gt;wrap&lt;&#x2F;code&gt; es solo un nombre provisional que usaremos unicamente dentro del decorador y que jamás se podrá ver al momento de utilizar el decorador.&lt;&#x2F;p&gt;
&lt;h4&gt;Argumentos que fueron mandados a la función decorada&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;def decorador(f):
    def wrap(*args, **kwargs):
             ^ ^ ^ ^ ^ ^ ^ ^
             ╚═╩═╩═╩═╩═╩═╩═╩══ Nuevos argumentos que aceptará nuestra función
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como nosotros al momento de crear el decorador desconocemos la cantidad real de argumentos que aceptará la función decorada, simplemente debemos capturar absolutamente todos para luego &amp;quot;desenvolvernos&amp;quot; al momento de llamar a la función a decorar.&lt;&#x2F;p&gt;
&lt;h4&gt;llamada a la función decorada&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;def decorador(f):
    def wrap(*args, **kwargs):
        return f(*args, **kwargs)
        ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
        ╚═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═══ Llamada a la función decorada desenvolviendo
                                        los parámetros capturados en el wrap
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Recordemos que en &lt;code&gt;f&lt;&#x2F;code&gt; se encuentra una referencia a la función a la cual queremos decorar, eso significa que si mandamos llamar a la función &lt;code&gt;f&lt;&#x2F;code&gt; realmente estaremos llamando a la función que colocamos debajo de nuestro decorador, y como esto es un decorador simple, no hacemos otra cosa mas que retornar los que la función decorada nos retorne.&lt;&#x2F;p&gt;
&lt;h4&gt;Retorno de la nueva función generada&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;def decorador(f):
    def wrap(*args, **kwargs):
        return f(*args, **kwargs)
    return wrap
    ^ ^ ^ ^ ^ ^
    ╚═╩═╩═╩═╩═╩══ Se retorna una referencia a nuestra función provisional
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Aquí está el truco de todo esto ya que, como dije al principio, nuestro decorador está retornando una función, o mas bien, una referencia a nuestra función... así que cuando estamos llamando a una función decorada, realmente estamos mandando llamar a un impostor, a una función intermediaría entre nosotros y nuestra hermosa función.&lt;&#x2F;p&gt;
&lt;h4&gt;Decorador listo&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;def decorador(f):
    def wrap(*args, **kwargs):
        return f(*args, **kwargs)
    return wrap

@decorador
def funcion(x):
    return x**2
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;em&gt;y boilá&lt;&#x2F;em&gt;, nuestro primer decorador está listo para ser coronar a nuestras funciones y otorgarle cualidades especiales... ...o en este caso, ninguna cualidad.&lt;&#x2F;p&gt;
&lt;h2&gt;Decoradores con argumentos: Funcionamiento interno&lt;&#x2F;h2&gt;
&lt;p&gt;Pero si creíste que el viaje por este maravilloso mundo de decoradores, funciones retornando funciones e impostores había acabo, pues estás equivocado ya que ahora subiremos al siguiente nivel, hacer que nuestro decorador admita * Tambores dramáticos * parámetros.
Los decoradores que reciben parámetros no son muy distintos a los decoradores que NO reciben parámetros, solo que con una pequeña diferencia. Si los decoradores que NO reciben parámetros son una función que retorna una función, los decoradores que SI reciben parámetros son funciones que retornan una función que retorna una función * emoji impactado *.
Pero esto, por que es así? por que nosotros al estar pasándole parámetros a una función realmente estamos llamando a una función y como a nosotros lo que nos interesa es otra función, pues la función a la que llamamos nos debe retornar otra... Yo se que te estoy confundiendo mas de lo que te estoy ayudando, así que mejor vamos a ver un ejemplo.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;def decorador(*args, **kwargs):
    def decorator(f):
        def wrap(*args, **kwargs):
            return f(*args, **kwargs)
        return wrap
    return decorator
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Aquí notamos algo familiar, no? &lt;code&gt;def decorator&lt;&#x2F;code&gt; que recibe una función en su parámetro, un función cuyo nombres es &lt;code&gt;wrap&lt;&#x2F;code&gt; que es retornado, pues si. En pocas palabras, podríamos decir que un decorador que SI recibe parámetros es un generador de decoradores que NO reciben parámetros. Genial, no? vamos a explicarlo un poco.&lt;&#x2F;p&gt;
&lt;h4&gt;Cabecera del decorador recibiendo parámetros&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;def decorador(*args, **kwargs):
    ^ ^ ^ ^ ^
    ╚═╩═╩═╩═╩═══ Nombre de nuestro decorador
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Comenzamos nuevo, este será el nombre que usaremos en la sintaxis de &lt;code&gt;@decorador&lt;&#x2F;code&gt; con la diferencia de que ahora nuestro decorador no recibe una función en sus parámetros, sino que recibe sus propios parámetros. Aquí es donde el decorador que flask que vimos al principio tiene declarados los argumentos como la ruta o los métodos. Pueden ser recibimos como argumentos envueltos con &lt;code&gt;*args, **kwargs&lt;&#x2F;code&gt; o podemos recibirlo uno a uno como una función cualquiera, por ejemplo:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;def decorador(arg1, args2=&amp;quot;default value&amp;quot;, arg3=None)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;y cuanta fantasía se nos vaya ocurriendo durante el proceso de programación.
La otra parte del decorador es exactamente la misma que con los decoradores sin parámetros.&lt;&#x2F;p&gt;
&lt;p&gt;Como se puede apreciar, los decoradores no son mas que un juego entre recibir funciones y retornar funciones y esto se puede explotar hasta donde nuestra imaginación nos de. Pero la pregunta que quizá alguno de ustedes tengan es &amp;quot;¿Y eso que utilidad tiene?&amp;quot;.&lt;&#x2F;p&gt;
&lt;h2&gt;Utilidad&lt;&#x2F;h2&gt;
&lt;p&gt;Como pudimos ver en los 2 bloques anteriores, nosotros al crear un decorador tenemos total acceso tanto a las funciones, como a los argumentos que estas están recibiendo, lo cual puede dar juego a hacer toda clase de validaciones y registros.&lt;&#x2F;p&gt;
&lt;h2&gt;Ejemplos&lt;&#x2F;h2&gt;
&lt;p&gt;A lo largo de mi vida como programador en Python he escrito bastantes decoradores y quiero compartir (y explicar) con ustedes un par de ellos.&lt;&#x2F;p&gt;
&lt;h3&gt;Decorador para manejo de excepciones&lt;&#x2F;h3&gt;
&lt;pre&gt;&lt;code&gt;def try_catch(value=...):
    def decorator(f):
        def wrap(*args, **kwargs):
            try:
                return f(*args, **kwargs)
            except BaseException as e:
                if value is ...:
                    return e
                else:
                    return value
        return wrap
    return decorator

@try_catch(0)
def pow(num, exp):
    return num ** exp

&amp;gt; pow(10, 2)
100

&amp;gt; pow(5, 5)
3125

&amp;gt; pow(&amp;quot;foo&amp;quot;, 10)
0

@try_catch()
def pow(num, exp):
    return num ** exp

&amp;gt; pow(&amp;quot;foo&amp;quot;, 10)
TypeError(&amp;quot;unsupported operand type(s) for ** or pow(): &#x27;str&#x27; and &#x27;int&#x27;&amp;quot;)

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como se puede apreciar, este es un decorador que si recibe parámetros, para ser correcto, recibe un solo parámetro y es opcional y el característica que le otorga a las funciones es la de evitar que arrojen una excepción y si lo hace, retornar el valor que se le pasó al decorador como parámetro y si no se le pasa ningún parámetro, retornará (mas no lanzará) la excepción que arrojó la función.&lt;&#x2F;p&gt;
&lt;h3&gt;Decorador que revisa que todos los parámetros sean números&lt;&#x2F;h3&gt;
&lt;pre&gt;&lt;code&gt;def only_numbers(f):
    def wrap(*args, **kwargs):
        for arg in args:
            if not isinstance(arg, (int, float)):
                raise ValueError(f&amp;quot;{arg} is not an int or a float, is a {type(arg).__name__}&amp;quot;)
        return f(*args, **kwargs)
    return wrap

@only_numbers 
def suma(*args): 
    return sum(args)

&amp;gt; suma(1, 2, 3, 4, 5)
15

&amp;gt; suma(1, 2, 3, 4, &amp;quot;foo&amp;quot;)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
...
ValueError: a is not a int or a float, is a str


&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Este otro decorador no recibe parámetros y lo que hace es verificar que todos los parámetros que esté recibiendo nuestra función sean tipo &lt;code&gt;int&lt;&#x2F;code&gt; o tipo &lt;code&gt;float&lt;&#x2F;code&gt;, de lo contrarío arrojará una excepción advirtiendo que uno de los argumentos incumple esta regla.&lt;&#x2F;p&gt;
&lt;h2&gt;Bonus: Apilamiento de decoradores&lt;&#x2F;h2&gt;
&lt;p&gt;Una ultima cosa antes de terminar y como dato que hará que les vuele cabeza.&lt;br &#x2F;&gt;
Los decoradores también se pueden apilar al momento de ser utilizados decorando una función dando como resultado una herramienta verdaderamente poderosa para la reutilización de código. Los decoradores que puse de ejemplo no fueron al azar, pues los escogí exactamente por que se pueden apilar ya que uno es capaz de capturar excepciones y otro arroja una excepción, dando como resultado algo como:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;@try_catch(-1) 
@only_numbers 
def suma(*args): 
    return sum(args)

&amp;gt; suma(1, 2, 3)
6

&amp;gt; suma(1, 2, &amp;quot;foo&amp;quot;)
-1
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Muchas gracias por leer el primer post de mi blog, espero que les haya gustado. en mi twitter &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;kirbylife&quot;&gt;@kirbylife&lt;&#x2F;a&gt; pueden hacerme llegar las opiniones con respecto al post, que les pareció, en que puedo mejorar o si tengo algún error, tanto en el código como en la redacción.&lt;&#x2F;p&gt;
</description>
      </item>
  </channel>
</rss>
